ibmi-brunch-learn

Announcement

Collapse
No announcement yet.

trying to implement bcrypt

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • trying to implement bcrypt

    I need to implement 'bcrypt' on the i. I found a port (https://github.com/NattyNarwhal/bcrypt-ile) but couldn't get the 'make' file to work. I resorted to creating a CLLE to create the MODs and the SRVPGM which compiled but had warning errors when creating the MODs. I'm not getting a hash back from the SRVPGM so now I'm going back to the beginning to try and remove any potential issues.

    With the command:

    Code:
    CRTCMOD MODULE(QTEMP/BCRYPT) SRCSTMF(&SRCBASE *TCAT 'src/bcrypt.c') +
    DBGVIEW(*SOURCE) LANGLVL(*EXTENDED) LOCALETYPE(*LOCALEUTF) +
    TGTCCSID(1208)
    I'm getting this error:
    Message ID . . . . . . : CZS2118 Severity . . . . . . . : 10
    Message type . . . . . : Diagnostic
    Date sent . . . . . . : 07/22/21 Time sent . . . . . . : 08:52:44

    Message . . . . : Target CCSID is not compatible with
    LOCALETYPE(*LOCALEUTF).
    Cause . . . . . : You have specified LOCALETYPE(*LOCALEUTF) which can only
    be used in conjunction with 1208 target CCSID. Target CCSID will default to
    1208.
    Recovery . . . : This particular warning may be corrected by specifying
    TGTCCSID(1208) or removing LOCALETYPE(*LOCALEUTF).
    I don't understand why I'm getting the error when it appears I'm using the correct combination of parameters. Can I ignore this error or will it cause translation issues for me? (I'm using LOCALETYPE and TGTCCSID because they were using in the original 'make' file. I'm assuming they are needed since the program is processing *utf8 values.)

  • #2
    Can someone tell me how to define a RPG parameter to match a C field of type 'size_t'? Numerous search results tell me it needs to be an unsigned integer. I've tried uns(3), uns(5), uns(10) and uns(20). They all result with the value being changed from 128 to 1. Using debug to step into the C procedure I can force hashlen to 128 to get a result. I still don't know if the result is correct but this is more than I was getting yesterday.

    Here's my RPG code followed by the C prototype:
    Code:
    **free
    ctl-opt option(*srcstmt : *nodebugio) dftactgrp(*no) actgrp(*caller);
    ctl-opt bnddir('BCRYPT');
    
    dcl-pr crypt_newhash bindec(4) extproc(*dclcase);
    password char(10) ccsid(*utf8) options(*varsize) const;
    pref char(100) ccsid(*utf8) options(*varsize) const;
    hash char(128) ccsid(*utf8);
    hashsize uns(20);
    end-pr;
    
    dcl-pr sendMsg extpgm('QMHSNDPM');
    msgID char(7) const;
    msgFile char(20) const;
    msgDta char(80) const;
    
    msgDtaLen int(10:0) const;
    msgType char(10) const;
    msgQ char(10) const;
    msgQNbr int(10:0) const;
    msgKey char(4);
    errorDS char(16);
    end-pr;
    
    dcl-s passwd char(10) ccsid(*utf8) inz;
    dcl-s pref char(100) ccsid(*utf8) inz;
    dcl-s hash char(128) ccsid(*utf8) inz;
    dcl-s hashsize uns(20) inz;
    dcl-s result int(3) inz;
    dcl-s msgkey char(4) inz;
    dcl-s errcd char(16) inz;
    
    passwd = 'Password' + x'00';
    pref = 'bcrypt,8' + x'00';
    hash = x'00';
    hashsize = %len(hash);
    
    result = crypt_newhash(%trim(passwd):%trim(pref):hash:hashsize);
    
    //dsply %subst(hash:1:50);
    sendMsg ('CPF9898':'QCPFMSG QSYS':%trim(hash):
    %len(%trim(hash)):'*INFO':'*EXT':1:msgkey:errcd);
    
    *inlr = *on;
    return;
    Code:
    int
    crypt_newhash(const char *pass, const char *pref, char *hash, size_t hashlen)

    Comment


    • #3
      RPG UNS(10) matches C unsigned int. The C function has the hashLen parameter passed by value, so you need to code the VALUE keyword on the RPG prototype.

      Comment


      • #4
        How is the C function going to determine the length of the char* parameters? If it is using strlen, then it would be better to change those parameters in the RPG prototype to be POINTER VALUE OPTIONS(*STRING). If you pass a character string to that type of POINTER parameter, RPG will create a null-terminated string, and the C function will be able to safely use strlen to get the length.

        Comment


        • #5
          [Edited]
          Thanks, Barbara. As you would imagine, the 'uns(10) value' change worked.

          I didn't know a good way to pass a null-terminated string, so I was concatenating x'00' to those strings. I like your suggestion so I changed the prototype and updated the procedure call to
          Code:
          result = crypt_newhash(%addr(passwd):%addr(pref):hash:hashsize);
          This compiled fine (unlike what I posted in this response before editinged it). However when I call the function I don't see x'00' at the end of the value in debug and the function is failing. I assume the problem is with me not understanding what needs to be done. Your patience would be appreciated!

          Comment


          • #6
            I was wrong. Null is there but it's at the end of the full length of the field.

            I'm passing 'bcrypt,8' in the 'pref' field. The C code is comparing the full length of the parameter to a shorter value which results in not matching. In other words C is expecting a variable length field. I changed the field in RPG to be varchar() hoping some magic would happen to remove the field-length from the string but of course that didn't happen so the compare still fails even though the parameter has the null where it should be.

            Is there a trick I can use or do I need to go back to concatenating x'00' to my trimmed field?

            Comment


            • #7
              I've been able to get the crypt_newhash() function to work by changing the prototype back to this:
              Code:
              dcl-pr crypt_newhash int(3) extproc(*dclcase);
              password char(10) ccsid(*utf8) options(*varsize) const;
              pref char(20) ccsid(*utf8) options(*varsize) const;
              hash char(128) ccsid(*utf8);
              hashsize uns(20) value;
              end-pr;
              and calling it like this:
              Code:
              result = crypt_newhash(%trim(passwd)+ x'00' : %trim(pref)+ x'00' : hash : hashsize);
              I'd like to hear if there's a cleaner way to do it, but this seems to work.

              Thanks again, Barbara, for your help!

              Comment


              • #8
                Hi, I wrote that library.

                My guess on the CCSID errors is because they're now EBCDIC source files instead of UTF-8 stream files. What was the issue with the makefile? You should be able to just run "make" from a PASE shell. (I'd recommend using GNU make instead; you should be able to install make-gnu from yum.)

                size_t I think is 32-bit in ILE, which kinda sucks.

                I provided an untested RPG copybook that uses some 7.2+ functionality for i.e. CCSID(1208) params. That should be simpler, but I'm a C person, not an RPG one.

                Comment


                • #9
                  @calvinb: That's for the response. I'm actually using the IFS source files for the compile and the files are still ASCII. Between Barbara's help on matching the size_t parameter and sending the null on the end of the trimmed parameters - things are working.

                  I'm not sure what I had wrong on the make file and for now it's a moot point since the program is working.

                  The RPG copybook wasn't working because the parameters are incorrectly defined for RPG:
                  pref char(*) ccsid(*utf8) const;
                  isn't valid syntax. I was assuming the '*' was meant as a pointer. My prototype is defined as
                  pref char(20) ccsid(*utf8) options(*varsize) const;
                  It might be nice if I could use a pointer as Barbara suggested and still get the null immediately after my intended/trimmed value. I just don't know how to do it in a clean fashion.

                  Thanks for putting the package on github - without it I probably wouldn't have attempted this in the first place.

                  Comment


                  • #10
                    Sorry, I forgot to say that when you code the parameter as a POINTER VALUE OPTIONS(*STRING), RPG will only handle the null-terminator for you if you pass a character value. If you pass a pointer, you have to set up the null-terminated value yourself.

                    Unfortunately, OPTIONS(*STRING) doesn't work with UTF-8 data. So your OPTIONS(*VARSIZE) solution is good.

                    It's too bad that RPG doesn't support the CCSID keyword with OPTIONS(*STRING) ...

                    Comment


                    • #11
                      The Makefile works fine for me. I'm using the GNU Make package from yum, it can be installed into PASE with
                      Code:
                      yum install make-gnu
                      I suspect that xrunner may not be familiar with using PASE or installing the Make software... this is not common knowledge on IBM i like it would be on Unix/Linux.

                      Comment

                      Working...
                      X