DLLs or other encryption options

LBaliao

Member
Hello,

Currently using ARC4 for encryption and want to upgrade. Moving to full 64-bit environment and using PROGRESS 12. I'd appreciate your help if you can share other options you know and use.

Thanks,
Liza
 
I've just written the algorithm ABL and tested it in ABL DOJO.
Use it at your own risk. I've done about 2 mins of testing in the ABL DOJO.
I think it also breaks when the string exceeds 112/113 characters.

Until today I've never head of RC4 or ARC4. Hopefully, you can fix it.

I do have a better XOR function but i'm on my lunch break, so I had to google search for the XOR function.

Code:
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.

1724726987183.png
 
Last edited:
Some Improvement to the code. Still has a bug where the source string can't exceed 261 characters.
Possibly someone cleverer than me can spot the bug.



C#:
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.
 
Thanks Cecil. I do appreciate your time and effort. In testing version 1 of the code, I found that using key other than "1234" can cause an array subscript error or return part of the un-encrypted text as garbage. I've used keys such as "MyKey" and "Hello". I will take a look at the code some more tomorrow.
 
Re-wrote using memptrs rather than concatenating character string values.
It seems to be working well with larger string values too.
It might possibly work with binary files aswell, but I have not tested that.

Link the ABL DOJO:

Also, ABL does have alternate built-in encryption which are more secure and possibly more portable across languages.

Implement at your own risk.

C#:
/* 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.
 
Last edited:
Cecil, thank you so much! So far the latest code is stable. I will be testing further today. Again, I can't thank you enough for your help and expertise.

-Liza
 
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.
 
Last edited:
You are so generous! Thank you for sharing your expertise. I appreciate your help very much. I'm doing some more testing and it's looking really promising. Thanks again.

- Liza
 
(Caveat: the above might be an academic exercise, with no intent to use this code in a production system. I wouldn't take issue with that. But as I re-read the thread, I don't think that is the case here.)

My two cents, as a non-expert:

Don't try to roll your own crypto libraries.
It may be fun but it's the wrong choice for security.

There are robust, well-tested implementations of industry-standard algorithms on all platforms. They are written by experts and attacked by other experts looking for vulnerabilities, to ensure robustness. These folks have far more domain expertise than any of us.

Don't use RC4 (or other vulnerable ciphers).
Ciphers don't live forever. Any cipher in widespread use is under widespread attack; by black hats, academics, government agencies, etc. Once meaningful vulnerabilities (i.e. exploitable in the real world) are found, the industry moves on, as evidenced by Progress removing RC4 support from TDE in OE 12.x.

Remember Kerckhoffs' Principle.
https://en.wikipedia.org/wiki/Kerckhoffs%27s_principle
Ultimately, the security of a cryptosystem resides in the keys (at least it should). If they are not managed properly, if they are exposed or even potentially exposed, then the other details of the system don't matter. Using a symmetric cipher implemented in software forces you to expose the key in software, e.g. in the client's memory, which can be dumped and inspected by an attacker. Think about how you are entering, storing, and transporting your keys, and who might have access to them. Can you use an HSM instead?

I don't know your use case, so I can't provide meaningful guidance on what you should do, but the above approach looks suspect to me (no disrespect intended).
 
Appreciate your input Rob. Still testing and evaluating if this encryption will do for us. HSM is probably out of our league given the high cost as our software is entering maintenance mode and is not web-based.
 
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.
Hi Cecil,

I've done extensive testing of the code.
1. Have a text file.
2. Read data line by line.
3. Output string to OutputFile1.txt.
4. Encrypt string then unencrypt string then output unencrypted string to OutputFile2.txt.
5. Compare original text file against OutputFile1.txt to make sure the data wasn't changed.
6. Compare original text file against OutputFile2.txt to test the encryption code.

For the most part it worked. However, results of the un-encrypted file (OutputFile2.txt) vary depending on key used or no key at all.
I tested the code (ABL-ARC42.p) that I downloaded from GitHub.

I picked up any output string with discrepancy and tested it outside of the big file to confirm if the output unencrypted string will vary based on key used.

Appreciate your time and effort for sure.

Liza
 
Hi Liza.

Just so that I understand you correctly, the code sort of works but not 100% of the time?
Is the ABL code working with the previously generated encrypted content?
Is the content that you are trying to encrypt (or decrypt) text based or binary?

When I wrote the first revision of the code I did not check to make sure that it was compatible with other RC4 based programmers.

I've only just crossed checked the encrypted output with some Python code and I found that it was not was not deciphering correctly. Whoops.

I'll have another look at the ABL code again to see where I've made a mistake. I think a lot of the issues is caused by ABL is not zero based indexed which is screwing up some modulo division calculations .
 
Cecillllllll,

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
 
Cecillllllll,

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
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) cipher

I've crossed check the output with other RC4 utilities and it encrypts/decrypts as expected.
 
Last edited:
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.

Liza
 
Back
Top