Forum Discussion
Kevin_Stewart
Employee
Mar 14, 2008AES functions
Hello Devcentral gurus,
Does anyone know the specifics of the AES functions and how they work. Specifically, look at the following code snippet:
set testkey "test"
set testdata "blah"
log local0. "aes output -> [AES::encrypt $testkey $testdata]"
I've searched and searched through the forum and perhaps I just don't understand it. The above code delivers a different result every time a new browser window is opened. I have to assume that internally AES is pulling some additional information to "seed" the result, but then why the key? I would have thought that since the AES::key function delivers a random key, everything else would be static, and entering a static key would give the same result every time. What am I missing? Say for example I am strongly encrypting something that is being sent to the user in a file-based cookie. When they come back to the site later, I would like to be able to decrypt that data. Again, am I missing something? As it stands, the AES functions don't allow that.
Thank you for your help.
Kevin
24 Replies
- hoolio
Cirrostratus
I think that the key you're testing with is invalid. For 128 bit encryption you need to use a key with 32 hexidecimal characters (Click here)
The AES key portion is composed of a number of characters specific to the bit value. The characters must be within the following hexadecimal range:
0123456789ABCDEF
The AES key must also contain the following number of characters to fulfill the bit requirement:
Note: One character is composed of 4 bits.
Bit Strength Number of Characters Required
128 32
192 48
256 64
Here is some sample code to test the key creation and encrypting/decrypting test strings:when RULE_INIT { Check if $::key is already defined and has a length if {[info exists ::key] and [string length $::key]}{ log local0. "using existing AES key: $::key" } else { The key didn't exist, so create a new 128 bit key set ::key [AES::key 128] } The key is actually a list of three elements: "AES" "key" "key_string" log local0. "list length of key: [llength $::key]" Test the encryption of a string, encoding it with base64 encoding, decode it and decrypt it. This verifies that the current version of software isn't susceptible to CR68290 / SOL7603. log local0. "Encrypt & encode test: [AES::decrypt $::key [b64decode [b64encode [AES::encrypt $::key "This should be logged in clear text"]]]]" Encrypt and encode a few test strings. The first two test strings are the same and result in the same encrypted/encoded string. set ::b64_encoded_encrypted_string_1 [b64encode [AES::encrypt $::key "test1"]] log local0. "\$::b64_encoded_encrypted_string_1: $::b64_encoded_encrypted_string_1" set ::b64_encoded_encrypted_string_2 [b64encode [AES::encrypt $::key "test1"]] log local0. "\$::b64_encoded_encrypted_string_2: $::b64_encoded_encrypted_string_2" set ::b64_encoded_encrypted_string_3 [b64encode [AES::encrypt $::key "test3"]] log local0. "\$::b64_encoded_encrypted_string_3: $::b64_encoded_encrypted_string_3" Decrypt the test strings set ::decrypted_1 [AES::decrypt $::key [b64decode $::b64_encoded_encrypted_string_1]] log local0. "\$::decrypted_1: $::decrypted_1" set ::decrypted_2 [AES::decrypt $::key [b64decode $::b64_encoded_encrypted_string_2]] log local0. "\$::decrypted_2: $::decrypted_2" set ::decrypted_3 [AES::decrypt $::key [b64decode $::b64_encoded_encrypted_string_3]] log local0. "\$::decrypted_3: $::decrypted_3" }
Log output:Rule : using existing AES key: AES 128 f97612f8dd24836405a9d9e7f69e7979 Rule : Encrypt & encode test: This should be logged in clear text Rule : list length of key: 3 Rule : $::b64_encoded_encrypted_string_1: P+SosLjL2NO1+souRy3zi0g2d3BZkd5Ire8LNveJTaQ= Rule : $::b64_encoded_encrypted_string_2: P+SosLjL2NO1+souRy3zi0g2d3BZkd5Ire8LNveJTaQ= Rule : $::b64_encoded_encrypted_string_3: P+SosLjL2NO1+souRy3ziSVAl60N6ojmK181jqBzqEw= Rule : $::decrypted_1: test1 Rule : $::decrypted_2: test1 Rule : $::decrypted_3: test3
If you're using the encrypted value in a cookie or in the URI, you should URI encode the output to conform to HTTP RFC's for character sets. You can use URI::encode and URI::decode to do this. Here is an example:when RULE_INIT { Cookie, which the application sets, to encrypt/decrypt set ::cookie_name "test_cookie" Log debug messages to /var/log/ltm? 1=yes, 0=no. set ::cookie_debug 1 Check if $::key is already defined and has a length if {[info exists ::key] and [string length $::key]}{ if {$::cookie_debug}{log local0. "Using existing AES key: $::key"} } else { The key didn't exist, so create a new 128 bit key set ::key [AES::key 128] if {$::cookie_debug}{log local0. "Created new AES key: $::key"} } } when HTTP_REQUEST { If the cookie exists with any value, for any requested object, try to decrypt it if {[string length [HTTP::cookie value $::cookie_name]]}{ if {$::cookie_debug}{log local0. "Original error cookie value: [HTTP::cookie value $::cookie_name]"} URI decode the value (catching any errors that occur when trying to URI decode the cookie value and save the output to cookie_uri_decoded) if {not ([catch {URI::decode [HTTP::cookie value $::cookie_name]} cookie_uri_decoded])}{ Log that the cookie was URI decoded if {$::cookie_debug}{log local0. "\$cookie_uri_decoded was set successfully"} Decrypt the value if {not ([catch {AES::decrypt $::aes_key $cookie_uri_decoded} cookie_decrypted])}{ Log the decrypted cookie value if {$::cookie_debug}{log local0. "\$cookie_decrypted: $cookie_decrypted"} } else { if {$::cookie_debug}{log local0. "Couldn't decrypt cookie: [HTTP::cookie value $::cookie_name"} } } else { if {$::cookie_debug}{log local0. "Couldn't URI decode cookie: [HTTP::cookie value $::cookie_name"} } } } when HTTP_RESPONSE { Check if response contains an error cookie with a value if {[string length [HTTP::cookie value $::cookie]] > 0}{ Log the original error cookie value from the app if {$::cookie_debug}{log local0. "Response from app contained error cookie: [HTTP::cookie value $::cookie_name]"} Encrypt the cookie value so the client can't change the value HTTP::cookie value $::cookie_name [URI::encode [AES::encrypt $::aes_key [HTTP::cookie value $::cookie_name]]] Log the encoded and encrypted error cookie value if {$::cookie_debug}{log local0. "Encrypted error cookie to: [URI::encode [AES::encrypt $::aes_key [HTTP::cookie value $::cookie_name]]]"} } }
There is an existing function which you could also use to encrypt/decrypt a cookie value: HTTP::cookie encrypt. You can check the HTTP::cookie wiki page (Click here) for more detail.
Aaron - Kevin_Stewart
Employee
Aaron,
thanks for that information. I hadn't seen this before. I am still confused though. Given the following:when RULE_INIT { set ::key [AES::key "128"] } when HTTP_REQUEST { set text "this is a test" log local0. "AES key: $::key" log local0. "Encrypted text: [b64encode [AES::encrypt $::key $text]] }
As long as the iRule isn't updated, I see that the key does not change, but the encrypted STATIC text changes on each new browser session (new browser window):
----------------------------
LTM capture from BIG-IP 9.4.2 Build 228.18 Final:: AES key: AES 128 eaa18b3c04c914f9e2fb8a6c9e479f4f: Encrypted test: TLkDvsXpttnYyJBMuQO+xem22djIkDLR5Xl32d9syl8l4t+LCI+WuG4VvVKQt/b4jSOtBA==: AES key: AES 128 eaa18b3c04c914f9e2fb8a6c9e479f4f: Encrypted test: hhNtFA/zVoJhJz6GE20UD/NWgmEnPof2Nalx2gy6lrsFUxRsiR+bA/ivrV+zjwANgc8Hrw==: AES key: AES 128 eaa18b3c04c914f9e2fb8a6c9e479f4f: Encrypted test: JzTGR0I2AL+G0dYnNMZHQjYAv4bR1mAUL1SMYguNEE29thMEzliMW74hgEFQ5iu5X9ctUw==
----------------------------
which leads me to believe there is a session-based subcomponent to the AES encryption function. I would like to be able to send the user an encrypted value, in a cookie, and retrieve that value at some later date. As for the strength of the encryption, vice basic cookie encryption, i'd like the value in the cookie to be unknown to the client.
Am I missing something, or is this an LTM version issue?
Thanks again.
Kevin - hoolio
Cirrostratus
Hi Kevin,
I'm not sure whether the encrypted string should always be the same if the source string is the same. I think the different outputs you're seeing could be tied to a salt or nonce, but I haven't delved into encryption enough to know.
Regardless of whether the encrypted value varies over time, I expect that every value would be decryptable using the key. Could you try decrypting the value on a subsequent request?
If I take the key you listed with the base64 encoded and encrypted strings you have, I get the correct decrypted values on a 9.4.3 unit:when RULE_INIT { set ::key [list AES 128 eaa18b3c04c914f9e2fb8a6c9e479f4f] log local0. "list length of key: [llength $::key]" log local0. "\$::key: $::key" set ::b64_encoded_encrypted_string "TLkDvsXpttnYyJBMuQO+xem22djIkDLR5Xl32d9syl8l4t+LCI+WuG4VvVKQt/b4jSOtBA==" log local0. "\$::b64_encoded_encrypted_string: $::b64_encoded_encrypted_string" log local0. "Decrypted string: [AES::decrypt $::key [b64decode $::b64_encoded_encrypted_string]]" set ::b64_encoded_encrypted_string "hhNtFA/zVoJhJz6GE20UD/NWgmEnPof2Nalx2gy6lrsFUxRsiR+bA/ivrV+zjwANgc8Hrw==" log local0. "\$::b64_encoded_encrypted_string: $::b64_encoded_encrypted_string" log local0. "Decrypted string: [AES::decrypt $::key [b64decode $::b64_encoded_encrypted_string]]" set ::b64_encoded_encrypted_string "JzTGR0I2AL+G0dYnNMZHQjYAv4bR1mAUL1SMYguNEE29thMEzliMW74hgEFQ5iu5X9ctUw==" log local0. "\$::b64_encoded_encrypted_string: $::b64_encoded_encrypted_string" log local0. "Decrypted string: [AES::decrypt $::key [b64decode $::b64_encoded_encrypted_string]]" }
Output:Rule : list length of key: 3 Rule : adh $::key: AES 128 eaa18b3c04c914f9e2fb8a6c9e479f4f Rule : $::b64_encoded_encrypted_string: TLkDvsXpttnYyJBMuQO+xem22djIkDLR5Xl32d9syl8l4t+LCI+WuG4VvVKQt/b4jSOtBA== Rule : Decrypted string: This is a test Rule : $::b64_encoded_encrypted_string: hhNtFA/zVoJhJz6GE20UD/NWgmEnPof2Nalx2gy6lrsFUxRsiR+bA/ivrV+zjwANgc8Hrw== Rule : Decrypted string: This is a test Rule : $::b64_encoded_encrypted_string: JzTGR0I2AL+G0dYnNMZHQjYAv4bR1mAUL1SMYguNEE29thMEzliMW74hgEFQ5iu5X9ctUw== Rule : Decrypted string: This is a test
Also, the HTTP::cookie encrypt/decrypt functions do encrypt the cookie value using a passphrase sent to the client to prevent it from being tampered with. Either method should work for the scenario you describe.
Aaron - Kevin_Stewart
Employee
Thanks again Aaron. I think you nailed it. I hadn't investigated the decrypt after your first response, but you're right, it does decrypt correctly despite the different encrypted message. I guess my next quest is to figure out why.
I looked at the TCL AES specifications and I'm assuming that the TMM uses CBC with an initialization vector. It didn't occur to me that different crypto messages would return the same plaintext.
Very cool.
Thanks again.
Kevin - hoolio
Cirrostratus
Yeah, I did some quick reading on AES and encryption in general, but didn't find anything that clearly spelled out what could be happening here. If you end up figuring this out, or if someone with more cryptographic knowledge is out there, I'd like to know.
Thanks,
Aaron - spark_86682Historic F5 AccountAES::encrypt (and ::decrypt, naturally) uses CWC mode with both a random IV and a random salt. So it is fully expected that the same plaintext could encrypt to multiple ciphertexts. I think that answers the original question, too.
- kuhn_52743
Nimbostratus
Hi,
This thread is old now, I'm hoping somebody is still interested in this topic. The previous post mentions that the AES encrypt/decrypt commands use CWC mode. Anybody know where this info comes from and how I can find out more detail?
My task is to make the F5 AES decrypt function interoperate with an external AES encoder. Ideally the encoder would be in java - but as a starting point, I've got some C code which can encrypt and decrypt in CWC mode.
I'd like to use the C code to encrypt something, and then use the F5 to decrypt it.
But I don't know exactly what data the F5 needs in the encrypted data. When I run a test and use AES::encrypt to encrypt something on the F5, I find that the amount of output data is around 34 bytes longer than the input plain text. (I don't always get the exact same size output, which is why I say "around" 34 bytes longer).
I'm guessing the extra bytes include a 16 byte "tag", an 11 byte "nonce" or init vector, and some extra bytes which I don't know what they are. I also don't know what order these fields have to go, in order to get the F5 to decrypt them correctly.
Anyone have any ideas about this, or anyone been able to get an external AES program to interoperate with the F5 LTM?
Regards,
Scott - spark_86682Historic F5 AccountWe've had several requests for the information needed to interact with the AES::encrypt format that we use, and will very likely be publishing this information. However, it will not be available for at least a month.
- kuhn_52743
Nimbostratus
Hi spark,
OK I'll look forward to any info you may be able to publish in the future.
In the meantime, I'm trying to figure some of it out the hard way, and get very strange results. Maybe I have some basic misunderstanding. Please consider this snippet of an irule - all I'm trying to do is generate a 128 bit key, then verify the length of the key is 128 bits, and print the key value out in hex:
set ::secret_key [AES::key 128]
log local0. " of chars of key is [string length $::secret_key]"
log local0. " byte Length of key is [string bytelength $::secret_key]"
set ::numconversions [binary scan $::secret_key H* ::keyhex]
log local0. "Key value in hex: $::keyhex ([string length $::keyhex])"
This results in the following output in the log:
Sep 23 17:58:50 tmm1 tmm1[2149]: Rule : of chars of key is 40
Sep 23 17:58:50 tmm1 tmm1[2149]: Rule : byte Length of key is 40
Sep 23 17:58:50 tmm1 tmm1[2149]: Rule : Key value in hex: 41455320313238206665623932383734373661666330326639353264636235346139386234386231 (80)
This seems totally wrong - I ask for a 128 bit key, and I get something 40 bytes long? The "hex" output is 80 hex characters, which would be equivalent to 40 bytes also. But non of the characters are a,b,c,d,e, or f - all of them are 0-9.
Any idea what's going on here? I'm either totally confused, or the key is not stored in a straightforward way?
Thanks,
Scott - habeebaslam_348
Nimbostratus
Hi,
Is it possible to configure the LTM to use CBC instead of CWC?
Thanks,
HA
Help guide the future of your DevCentral Community!
What tools do you use to collaborate? (1min - anonymous)Recent Discussions
Related Content
DevCentral Quicklinks
* Getting Started on DevCentral
* Community Guidelines
* Community Terms of Use / EULA
* Community Ranking Explained
* Community Resources
* Contact the DevCentral Team
* Update MFA on account.f5.com
Discover DevCentral Connects
