FUNCTION bitXOR RETURNS INTEGER (INPUT X AS INTEGER, INPUT Y AS INTEGER):
   DEFINE VARIABLE b1 AS INTEGER NO-UNDO.
   DEFINE VARIABLE b2 AS INTEGER NO-UNDO.
   DEFINE VARIABLE n  AS INTEGER NO-UNDO.
   DEFINE VARIABLE Z  AS INTEGER NO-UNDO.
   DO n = 1 TO 32:
     ASSIGN
       b1 = GET-BITS(X, n, 1)
       b2 = GET-BITS(Y, n, 1)
       .
       IF b1 + b2 = 1 THEN PUT-BITS(Z, n, 1) = 1.
   END.
   RETURN Z.
END FUNCTION.
function RC4 RETURNS  character  (input pInput as character, input pKey as character):
  DEFINE VARIABLE cResult  AS CHARACTER   NO-UNDO.
  DEFINE VARIABLE i        AS INTEGER     NO-UNDO.
  DEFINE VARIABLE j        AS INTEGER     NO-UNDO.
  DEFINE VARIABLE x        AS INTEGER     NO-UNDO.
  DEFINE VARIABLE y        AS INTEGER     NO-UNDO.
  DEFINE VARIABLE box      AS INTEGER EXTENT 256 NO-UNDO.
  DEFINE VARIABLE keyLen   AS INTEGER     NO-UNDO.
  DEFINE VARIABLE inputLen AS INTEGER     NO-UNDO.
  DEFINE VARIABLE boxIndex AS INTEGER     NO-UNDO.
  cResult = "".
  keyLen = LENGTH(pKey).
  inputLen = LENGTH(pInput).
  /* Initialize the box */
  DO i = 1 TO 256:
  box[i ] = i.
  END.
  /* Key-scheduling algorithm */
  DO i = 1 TO 255:
  j = (ASC(SUBSTRING(pKey, (i MOD keyLen) + 1, 1)) + box[i + 1] + j) MOD 256.
  x = box[i + 1].
  box[i + 1] = box[j + 1].
  box[j + 1] = x.
  END.
  /* Pseudo-random generation algorithm */
  DO i = 0 TO inputLen - 1 :
  y = i MOD 256.
  j = (box[y + 1] + j) MOD 256.
  x = box[y + 1].
  box[y + 1] = box[j + 1].
  box[j + 1] = x.
  cResult = cResult + CHR(bitXOR( ASC(SUBSTRING(pInput, i + 1, 1)) , box[(box[y + 1 ] + box[j + 1]) MOD 256])).
  END.
  RETURN cResult.
end function.
def var encptString as character no-undo.
assign encptString = "Decryption in RC4 is essentially the same process as encryption".
message "Input:" encptString.
encptString = RC4(input encptString, input "1234").
message "Scrambled:" encptString.
encptString = RC4(input encptString, input "1234").
message "Unscrambled:" encptString.
FUNCTION bitXOR RETURNS INTEGER (INPUT X AS INTEGER, INPUT Y AS INTEGER):
    DEFINE VARIABLE b1 AS INTEGER NO-UNDO.
    DEFINE VARIABLE b2 AS INTEGER NO-UNDO.
    DEFINE VARIABLE n  AS INTEGER NO-UNDO.
    DEFINE VARIABLE Z  AS INTEGER NO-UNDO.
    DO n = 1 TO 32:
        ASSIGN
            b1 = GET-BITS(X, n, 1)
            b2 = GET-BITS(Y, n, 1)
            .
        IF b1 + b2 = 1 THEN PUT-BITS(Z, n, 1) = 1.
    END.
    RETURN Z.
END FUNCTION.
FUNCTION bitAnd returns integer ( input X as integer, input Y as integer):
    define variable b as integer no-undo.
    define variable n as integer no-undo.
    define variable Z as integer no-undo.
    do n = 1 to 32:
        if get-bits(X, n, 1) = 1 and get-bits(Y, n, 1) = 1 then
            b = 1.
        PUT-BITS(Z, n, 1) = b.
        b = 0.
    end.
    return Z.
end FUNCTION.
function RC4 RETURNS  character  (input pInput as character, input pKey as character):
    DEFINE VARIABLE cResult  AS CHARACTER NO-UNDO.
    DEFINE VARIABLE i        AS INTEGER   NO-UNDO.
    DEFINE VARIABLE j        AS INTEGER   NO-UNDO.
    DEFINE VARIABLE x        AS INTEGER   NO-UNDO.
    DEFINE VARIABLE y        AS INTEGER   NO-UNDO.
    DEFINE VARIABLE box      AS INTEGER   EXTENT 256 NO-UNDO.
    DEFINE VARIABLE keyLen   AS INTEGER   NO-UNDO.
    DEFINE VARIABLE inputLen AS INTEGER   NO-UNDO.
    DEFINE VARIABLE boxIndex AS INTEGER   NO-UNDO.
    DEFINE VARIABLE iByte AS INTEGER NO-UNDO.
    cResult = "".
    keyLen = LENGTH(pKey).
    inputLen = LENGTH(pInput).
    /* Initialize the box */
    DO i = 1 TO 256:
        box[i] = (i - 1).
    END.
    /* Key-scheduling algorithm */
    DO i = 0 TO 255:
        j = (ASC( SUBSTRING(pKey, ((i + 1) MOD keyLen) + 1, 1)) + box[i + 1] + j) MOD 256.
        x = box[i + 1].
        box[i + 1] = box[j + 1].
        box[j + 1] = x.
    END.
    
    
    /* Pseudo-random generation algorithm */
    DO i = 0 TO inputLen - 1 :
        y = i MOD 256.
        j = (box[y + 1] + j) MOD 256.
        x = box[y + 1].
        box[y + 1] = box[j + 1].
        box[j + 1] = x.
        
        iByte = bitXOR( ASC(SUBSTRING(pInput, i + 1, 1)) , box[(box[y + 1 ] + box[j + 1]) MOD 256]).
        
        cResult = cResult + CHR(bitAnd(iByte, 255)).
    end.
    
    RETURN cResult.
end function.
def var encptString as character no-undo.
assign
    encptString = "Decryption in RC4 is essentially the same process as encryption. Decryption in RC4 is essentially the same process as encryption Decryption in RC4 is essentially the same process as encryption. Decryption in RC4 is essentially the same process as encryption".
message "Input:" encptString.
encptString = RC4(input encptString, input "1234").
message "Scrambled:" encptString.
encptString = RC4(input encptString, input "1234").
message "Unscrambled:" encptString./* RC4 Encryption/Decryption Function in Progress ABL */
FUNCTION bitXOR RETURNS INT64 (INPUT X AS INTEGER, INPUT Y AS INTEGER):
    DEFINE VARIABLE b1 AS INT64   NO-UNDO.
    DEFINE VARIABLE b2 AS INT64   NO-UNDO.
    DEFINE VARIABLE n  AS INTEGER NO-UNDO.
    DEFINE VARIABLE Z  AS INT64   NO-UNDO.
    DO n = 1 TO 64:
        ASSIGN
            b1 = GET-BITS(X, n, 1)
            b2 = GET-BITS(Y, n, 1)
            .
        IF b1 + b2 = 1 THEN PUT-BITS(Z, n, 1) = 1.
    END.
    RETURN Z.
END FUNCTION.
FUNCTION bitAnd RETURNS INT64 ( INPUT X AS INTEGER, INPUT Y AS INTEGER):
    DEFINE VARIABLE b AS INTEGER NO-UNDO.
    DEFINE VARIABLE n AS INTEGER NO-UNDO.
    DEFINE VARIABLE Z AS INT64   NO-UNDO.
    DO n = 1 TO 64:
        IF GET-BITS(X, n, 1) = 1 AND get-bits(Y, n, 1) = 1 THEN
            b = 1.
        PUT-BITS(Z, n, 1) = b.
        b = 0.
    END.
    RETURN Z.
END FUNCTION.
FUNCTION RC4 RETURNS MEMPTR
    (INPUT pwd AS MEMPTR, INPUT data AS MEMPTR):
    DEFINE VARIABLE a          AS INTEGER NO-UNDO.
    DEFINE VARIABLE i          AS INTEGER NO-UNDO.
    DEFINE VARIABLE j          AS INTEGER NO-UNDO.
    DEFINE VARIABLE k          AS INTEGER NO-UNDO.
    DEFINE VARIABLE tmp        AS INTEGER NO-UNDO.
    DEFINE VARIABLE key        AS INTEGER EXTENT 256 NO-UNDO.
    DEFINE VARIABLE box        AS INTEGER EXTENT 256 NO-UNDO.
    DEFINE VARIABLE dataLength AS INTEGER NO-UNDO.
    DEFINE VARIABLE pwdLength  AS INTEGER NO-UNDO.
    DEFINE VARIABLE cipher     AS MEMPTR  NO-UNDO.
    DEFINE VARIABLE cPtr       AS INTEGER NO-UNDO. /* Pointer for MEMPTR manipulation */
   
   
    /* Initialize lengths */
    ASSIGN
        dataLength = GET-SIZE(data)
        pwdLength  = GET-SIZE(pwd).
    /* Allocate memory for the cipher */
    SET-SIZE(cipher) = dataLength.
    cPtr = 1.
    /* Key-scheduling algorithm (KSA) */
    DO i = 1 TO 256:
        key[i] = GET-BYTE(pwd, (i - 1) MODULO pwdLength + 1).
        box[i] = i - 1.
    END.
    j = 1.
    DO i = 1 TO 256:
        j = (j + box[i] + key[i]) MODULO 256 + 1.
        /* Swap */
        tmp = box[i].
        box[i] = box[j].
        box[j] = tmp.
    END.
    /* Pseudo-random generation algorithm (PRGA) */
    a = 1.
    j = 1.
    DO i = 1 TO dataLength:
        a = a MODULO 256 + 1.
        j = (j + box[a]) MODULO 256 + 1.
       
        /* Swap */
        tmp = box[a].
        box[a] = box[j].
        box[j] = tmp.
        k = box[(box[a] + box[j]) MODULO 256 + 1].
        /* XOR the data with the generated key stream and store in cipher */
        tmp = bitXOR(GET-BYTE(data, i),  k).
       
        PUT-BYTE(cipher, cPtr) = bitAnd(tmp, 255).
       
        cPtr = cPtr + 1.
    END.
    RETURN cipher.
END FUNCTION.
/* Test the RC4 function */
DEFINE VARIABLE pwd                  AS MEMPTR    NO-UNDO.
DEFINE VARIABLE data                 AS MEMPTR    NO-UNDO.
DEFINE VARIABLE encrypted            AS MEMPTR    NO-UNDO.
DEFINE VARIABLE decrypted            AS MEMPTR    NO-UNDO.
DEFINE VARIABLE tempString           AS CHARACTER NO-UNDO.
DEFINE VARIABLE passwordString       AS CHARACTER NO-UNDO.
DEFINE VARIABLE dataEncryptionString AS CHARACTER NO-UNDO.
/* Alway zero out your memptrs */
SET-SIZE(pwd) = 0.
SET-SIZE(data) = 0.
SET-SIZE(encrypted) = 0.
SET-SIZE(decrypted) = 0.
ASSIGN
    passwordString       = "This is the private key!"
    dataEncryptionString = "RC4 (also known as Rivest Cipher 4) is a form of stream cipher. It encrypts messages one byte at a time via an algorithm.~r~n~r~nPlenty of stream ciphers exist, but RC4 is among the most popular. It's simple to apply, and it works quickly, even on very large pieces of data. If you've ever used an application like TSL (transport layer security) or SSL (secure socket layer), you've probably encountered RC4 encryption.~r~n~r~nBut you may not know how it works.~r~n~r~nWe'll take a high-level approach and explain what the Rivest Cipher is in terms anyone can understand. We'll also explain why it's helpful, and we'll point out a few known limitations. EOL".
SET-SIZE(pwd) = LENGTH(passwordString).
SET-SIZE(data) = LENGTH(dataEncryptionString).
PUT-STRING(pwd, 1, LENGTH(passwordString)) = passwordString.
PUT-STRING(data, 1, LENGTH(dataEncryptionString)) = dataEncryptionString.
/* Encrypt the data */
encrypted = RC4(pwd, data).
tempString  = GET-STRING(encrypted, 1) .
MESSAGE "Encrypted (HEX): " STRING(HEX-ENCODE(encrypted)) SKIP(1)
    "Encrypted (TXT): " tempString
    VIEW-AS ALERT-BOX INFORMATION TITLE "ABL ARC4 Encryption (Encrypted)".
/* Decrypt the data (using the same RC4 function) */
ASSIGN
    decrypted = RC4(pwd, encrypted).
tempString  = GET-STRING(decrypted, 1).
MESSAGE "Decrypted: " tempString  VIEW-AS ALERT-BOX INFORMATION TITLE "ABL ARC4 Encryption (Decrypted)".
/* Clean up */
FINALLY:
    SET-SIZE(pwd) = 0.
    SET-SIZE(data) = 0.
    SET-SIZE(encrypted) = 0.
    SET-SIZE(decrypted) = 0.
END./*PUT-BYTE(cipher, cPtr) = bitAnd(tmp, 255).*/
PUT-BYTE(cipher, cPtr) = tmp.Hi Cecil,I've tested it with a binary file (1.2MB) and to my surprise it did work. However, it was very, very slow and that just because the ABL language is just not optimal for bitwise operations.
Recommendation is to use an alternative encryption method which is supported by OpenEdge.
If you do need to continue to use the CR4 cipher work with smaller files.
I've also converted the code to be a class object and uploaded it to GitHub if anyone else is interested. ABL-RC4/src at main · Jimbobnz/ABL-RC4
Update: I don't think there is a requirement to bitAnd operation. It seems to work well without it and so it improves performance.
Code:/*PUT-BYTE(cipher, cPtr) = bitAnd(tmp, 255).*/ PUT-BYTE(cipher, cPtr) = tmp.
I've updated the code on GitHub. I've altered things a bit and made it into a ABLOO static class object. GitHub - Jimbobnz/ABL-RC4: ABL RC4 (RC4) cipherCecillllllll,
I am beyond elated! I figured out what I was doing wrong. I was saving tempstring /** tempString = GET-STRING(encrypted, 1) . **/ to a file and then importing it as binary in order to decrypt it and get the original string.
I should have been using the IMPORT/EXPORT in order to save and retrieve MEMPTRs.
Now I need to clean up my code and run the same test using multiple files. I am so glad I re-read your code. I took a break from this task and re-read your code once I got notification that you replied. I think the break did me good. I will post an update about my testing. I don't think there is an issue in your code.
Thanks again!
Liza
Thank you so much! I was over my head making the implementation complex than it should be. I ran my revised code using simple string and converting files. The results are consistent now. I will get the latest from GitHub tomorrow and work on it again. Thanks again. I need to get some sleep.I've updated the code on GitHub. I've altered things a bit and made it into a ABLOO static class object. https://github.com/Jimbobnz/ABL-RC4/
I've crossed check the output with other RC4 utilities and it encrypts/decrypts as expected.
