Rails Encryption using AES, overcomplicated - ruby

I'm having trouble encrypting a value from a third party vendor I am using.
Their instructions are as follows:
1) Convert the encryption password to a byte array.
2) Convert the value to be encrypted to a byte array.
3) The entire length of the array is inserted as the first four bytes onto the front
of the first block of the resultant byte array before encryption.
4) Encrypt the value using AES with:
1. 256-bit key size,
2. 256-bit block size,
3. Encryption Mode ECB, and
4. an EMPTY initialization vector.
5) After encryption, you should now have a byte array that holds the encrypted value.
6) Convert each byte to a HEX format and string all the HEX values together.
7) The final result is a string of HEX values. This is the final encrypted value to be passed.
The length of the final value will always be an even number.
EXAMPLE:
Given the following input values:
plainText: 2017/02/07 22:46
secretKey: ABCD1234FGHI5678
The following string will be produced:
D6281D5BE6CD6E79BB41C039F4DD020FBEC9D290AD631B2598A6DFF55C68AD04
What I've tried so far...
plain_text = "2017/02/07 22:46"
secret_key = "ABCD1234FGHI5678"
plain_text_byte_array = plain_text.bytes
plain_text_byte_array.unshift(0).unshift(0).unshift(0).unshift(16) # I found a Java example in their documentation and this is what they do. They prepend their byte array with 16, 0, 0, 0
secret_byte_array = secret_key.bytes
secret_byte_array = secret_byte_array.concat([0, 0, 0,...]) # also from their java example, they append the secret_byte array with 16 0's in order to get its length to 32
cipher = OpenSSL::Cipher::AES256.new(:ECB)
cipher.key = secret_byte_array.pack("C*")
encrypted = cipher.update(plain_text_byte_array.pack("C*")) + cipher.final
p encrypted.unpack("H*").first.to_s.upcase
# Result is:
# "84A0E5DCA7D704C41332F86E707DDAC244A1A87C38A906145DE4060D2BC5C8F4"
As you can see my result is off from the actual result which should be
"D6281D5BE6CD6E79BB41C039F4DD020FBEC9D290AD631B2598A6DFF55C68AD04"
Does anyone know if I am missing something or doing something strange. Their instructions were difficult for me to parse through so maybe I'm missing something. Thank you for any help anyone can provide! (I've tried a ton of different variations on what you see above). I just need some guidance or at least someone to tell me I'm not crazy for not understanding their instructions.

I managed to reproduce their result - the process they've used is extremely convoluted and as far from elegant as it could possibly be. I've attached a far more descriptive explanation of the steps required to achieve their result, and the C# source code I used to do so.
Convert the password to a byte array. The byte array must be 32 bytes in length, and, if the password is not long enough, should be right-padded with 0 bytes. Thus their password, hex-encoded, becomes 4142434431323334464748493536373800000000000000000000000000000000.
Convert the value to be encrypted to a byte array. This one is simple enough, just encode with UTF-8.
The entire length of the array is inserted as the first four bytes onto the front of the first block of the resultant byte array before encryption. This is stupid and serves no purpose, but take the length of the byte array from step 2 as an unsigned 32-bit integer and convert to a little endian byte array. Prefix this to the array from step 2.
Encrypt the value using AES. Uhm. No don't do that. Encrypt the value with Rijndael, using a 256-bit block size, 256-bit key size, ECB mode and zero's for padding.
The rest is easy, just convert the result of encryption to hex.
The code I used to achieve this result is below, in C#. I don't know Ruby all that well sorry.
// 1. Convert the encryption password to a byte array.
byte[] passwordBytesOriginal = Encoding.UTF8.GetBytes("ABCD1234FGHI5678");
byte[] passwordBytes = new byte[32];
Array.Copy(passwordBytesOriginal, 0, passwordBytes, 0, passwordBytesOriginal.Length);
// 2. Convert the value to be encrypted to a byte array.
byte[] valueBytes = Encoding.UTF8.GetBytes("2017/02/07 22:46");
// 3. The entire length of the array is inserted as the first four bytes onto the front
// of the first block of the resultant byte array before encryption.
byte[] valueLengthAsBytes = BitConverter.GetBytes((uint)valueBytes.Length);
byte[] finalPlaintext = new byte[valueBytes.Length + valueLengthAsBytes.Length];
Array.Copy(valueLengthAsBytes, 0, finalPlaintext, 0, valueLengthAsBytes.Length);
Array.Copy(valueBytes, 0, finalPlaintext, valueLengthAsBytes.Length, valueBytes.Length);
// 4. Encrypt the value using AES...
byte[] ciphertext;
using (RijndaelManaged rijn = new RijndaelManaged())
{
rijn.BlockSize = 256;
rijn.KeySize = 256;
rijn.Key = passwordBytes;
rijn.Mode = CipherMode.ECB;
rijn.Padding = PaddingMode.Zeros;
var encryptor = rijn.CreateEncryptor();
ciphertext = encryptor.TransformFinalBlock(finalPlaintext, 0, finalPlaintext.Length);
}
// 5., 6., 7...
string result = BitConverter.ToString(ciphertext).Replace("-", "").ToUpper();
Console.WriteLine(result); // D6281D5BE6CD6E79BB41C039F4DD020FBEC9D290AD631B2598A6DFF55C68AD04

Based on Luke's excellent answer here is the Ruby version. I had to use the ruby-mcrypt gem and install the mcrypt library locally using brew install libmcrypt.
It is as Luke's answer points out that the secret key should be right padded with 0's. Here is my code:
plain_text = "2017/02/07 22:46"
secret_text = "ABCD1234FGHI5678"
answer = "D6281D5BE6CD6E79BB41C039F4DD020FBEC9D290AD631B2598A6DFF55C68AD04"
def format_byte_arrays(plain, secret)
zero_byte_array = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
length_array = [16, 0, 0, 0]
plain_bytes = length_array.concat(plain.bytes)
secret_bytes = secret.bytes.concat(zero_byte_array)
[plain_bytes, secret_bytes]
end
plain_bytes, secret_bytes = format_byte_arrays(plain_text, secret_text)
final_plain, final_secret = [plain_bytes.pack("C*"), secret_bytes.pack("C*")]
cipher = Mcrypt.new("rijndael-256", :ecb, final_secret, nil, :zeros)
encrypted = cipher.encrypt(final_plain)
result = encrypted.unpack("H*").first.to_s.upcase
The result will be the correct answer.

Related

How to handle sequences longer than 512 tokens in layoutLMV3?

How to work with sequences longer than 512 tokens. I don't wanted to use truncates =True. But actually wanted to handle the longer sequences
You can use the stride with max length parameter to handle the larger documents.
encoding = processor(images, words, boxes=boxes, word_labels=word_labels, truncation=True,padding="max_length", max_length = 512, stride = 128, return_overflowing_tokens = True,return_offsets_mapping = True)
This would help to handle the larger files.
Let me know if this is useful.

Ruby OpenSSL AES-128-CTR

I can't figure out what I am doing wrong here trying to decrypt a string of hex values with a given key using ruby's OpenSSL cipher AES-128-CTR.
I am using the gem hex_string to convert my hex to bytes
ctrkey = "36f18357be4dbd77f050515c73fcf9f2"
ciphertext3 = "69dda8455c7dd4254bf353b773304eec0ec7702330098ce7f7520d1cbbb20fc3\
88d1b0adb5054dbd7370849dbf0b88d393f252e764f1f5f7ad97ef79d59ce29f5f51eeca32eabedd9afa9329"
cipher2 = OpenSSL::Cipher.new('AES-128-CTR')
cipher2.decrypt
ctrkey = ctrkey.to_byte_string
cipher2.key = ctrkey
iv = cipher2.random_iv
cipher2.iv = iv
ciphertext3 = ciphertext3.to_byte_string
plain = cipher2.update(ciphertext3) + cipher2.final
puts "plaintext of Q3: #{plain}"
I know I am missing something small because I have similar code implementing AES-128-CBC. Do I need to have a counter that increments the IV for each block of 128 bytes in the ciphertext?
No, you're not missing something small, you are missing something huge.
Instead of using the same IV as used for encryption, you are generating a new one. For CTR, if the IV is random then each counter value is different, resulting in random looking output.
Often the IV (or nonce in the case of CTR) is prefixed to the ciphertext. For CTR that may be fewer bytes than 16 - although that is still the most probable size to try.

Generate random Salt using securerandom

I want to generate a random salt...
I am doing this but get the same salt every time even if I restart my program .. i checked using println
SecureRandom random = SecureRandom.getInstance("NativePRNGBlocking");
byte[] salt = new byte[32];
random.nextBytes(salt);
System.out.println(salt);
return salt;
How to generate a random salt? I want a separate salt for each user.
The bytes in the salt array will in fact be different, at least unless the NativePRNGBlocking implementation is broken. I think the problem is in your check. The toString method for byte arrays does not print the values in the array, so printing the salt array in that way is useless. Try to print the individual values:
for(byte b: salt) {
System.out.print(b + " ");
}
Or you could just inspect them in a debugger.
SecureRandom class supports the “SHA1PRNG” pseudo random number generator algorithm.
Try the below code it works fine for me and getting always unique outptut.
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
//Create array for salt
byte[] salt = new byte[32];
//Get a random salt
sr.nextBytes(salt);
//return salt
System.out.println( salt.toString());
Hope it will resolve your issue.

How to find insertions/deletions by comparing hashes quickly?

I want to create a hash of a file such that if the file is changed I can determine what parts of the file changed. The problem is that if a byte is removed or added, all subsequent hashes change too, therefore I need to iterate per byte through all hashes. This however can be expensive so I am looking for a hash which doesnt require that I recompute the entire hash start to finish but rather lets me undo one byte and then add another byte.
Pseudocode:
string getFileDiffHash(file){
string result = "";
for each (512 bytes in file){
result += hash(bytes);
}
}
string getFileDiff(file, diffHash){
string result = "";
for each (hash size bytes in diffHash){ //yes this would be in a hash table ideally, but hey, this is pseudocode
string current_hash = "";
for (i = 0; i < file_size(file); i++){
if (current_hash.size > hash_size){
current_hash = undo_hash(current_hash, file[i-hash_size]);
}
current_hash = add_hash(current_hash, file[i]);
if (current_hash.size == hash_size && bytes == current_hash){
result += "+"+diffHash+":"+i;
}
}
}
return result;
}
Any idea on what sort of hash would be suited to 'undo_hash' and 'add_hash'?
If you can have a hash of length log2(N) bytes, you can use a Hamming code. If it must be shorter, then a Low-density parity-check code would do the job.
#Interjay's comment was correct, I need a rolling hash. Furthermore, the algorithm I describe here is similar to what rsync does (and Dropbox by extension).

How to compute SHA1 of an array in Linux kernel

I'm trying to compute SHA1 of an integer array in the Linux kernel. I have gone through crypto.c/crypto.h and security/integrity/ima/ima_crypto.c but I can't figure out how to init and then update the SHA1 computer. Can someone point me to a tutorial or guide on how to go about doing this?
There's a pretty good introduction to the linux cryptography api in Documentation/crypto/api-intro.txt. Also check out fs/ecryptfs/crypto.c for a real-life example of how the functions are used.
Here's a quick summary though to get you start:
Step 1: Declaration
Create some local variables:
struct scatterlist sg;
struct hash_desc desc;
char *plaintext = "plaintext goes here";
size_t len = strlen(plaintext);
u8 hashval[20];
A struct scatterlist is used to hold your plaintext in a format the crypto.h functions can understand, while a struct hash_desc is used to configure the hashing.
The variable plaintext holds our plaintext string, while hashval will hold the hash of our plaintext.
Finally, len holds the length the plaintext string.
Note that while I'm using ASCII plaintext in this example, you can pass an integer array as well -- just store the total memory size in len and replace every instance of plaintext with your integer array:
int myarr[4] = { 1, 3, 3, 7 };
size_t len = sizeof(myarr);
Be careful though: an int element generally has a size greater than a byte, so storing integer values in an int array won't have the same internal representation as a char array -- you may end up with null bytes as padding in between values.
Furthermore, if your intention is to hash the ASCII representation of your integers, you will have to first convert the values in your array to a string character sequence (perhaps using sprintf).
Step 2: Initialization
Initialize sg and desc:
sg_init_one(&sg, plaintext, len);
desc.tfm = crypto_alloc_hash("sha1", 0, CRYPTO_ALG_ASYNC);
Notice that "sha1" is passed to crypto_alloc_hash; this can be set to "md5" for MD5 hashing, or any other supported string in order to use the respective hashing method.
Step 3: Hashing
Now perform the hashing with three function calls:
crypto_hash_init(&desc);
crypto_hash_update(&desc, &sg, len);
crypto_hash_final(&desc, hashval);
crypto_hash_init configures the hashing engine according to the supplied struct hash_desc.
crypto_hash_update performs the actual hashing method on the plaintext.
Finally, crypto_hash_final copies the hash to a character array.
Step 4: Cleanup
Free allocated memory held by desc.tfm:
crypto_free_hash(desc.tfm);
See also
how to use CryptoAPI in the linux kernel 2.6

Resources