Forum: Ruby Mimic AES_ENCRYPT and AES_DECRYPT functions in Ruby

C4ceadae4f61dac8ff3ababc3faa6c42?d=identicon&s=25 Felipe Coury (fcoury)
on 2009-03-24 04:25
Hello there!

I need to mimic what MySQL does when encrypting and decrypting strings
using built-in functions AES_ENCRYPT() and AES_DECRYPT().

Even though the application I am writing is a Rails application, I think
this question suited here better because, the encryption will take place
in Ruby and not necessarily depends on Rails.

I have read a couple of blog posts and apparently MySQL uses AES 128-bit
encryption for those functions. On top of that, since this encryption
requires a 16-bit key, MySQL pads the string with x0 chars (\0s) until
it's 16-bit in size.

The algorithm in C from MySQL source code is spotted here:
http://gtowey.blogspot.com/2009/01/mysql-aes-encry...

I have even tried to examine MySQL's C source code, but that didn't help
me much, since I can't really program in C. Maybe someone with a little
more experience can have some insights.

The source code that implements the encryption (rijndaelKeySetupEnc) and
decryption (rijndaelKeySetupDec) functions is here:

http://pastie.org/425070

And the actual AES_ENCRYPT (function my_aes_encrypt) and AES_DECRYPT
(my_aes_decrypt) source code is here:

http://pastie.org/425073

Please note that the necessity of using MySQL's compliancy was not my
call and is not a choice. I need that in order to communicate properly
with a legacy application, and I don't "own" this database. Please take
into consideration that security is definitely not the goal, talking to
that system properly is. The key length was not chosen by me and I know
it's a little peculiar, as you'll see below, on my replication "script".

Now I need to replicate what MySQL does in a Rails application, but
every single thing I tried, doesn't work.

Here's a way to replicate the behavior I am getting (in this case, using
Rails):

1) Create a new Rails app

rails encryption-test
cd encryption-test

2) Create a new scaffolding

script/generate scaffold user name:string password:binary

3) Edit your config/database.yml and add a test MySQL database

development:
  adapter: mysql
  host: localhost
  database: test
  user: <<user>>
  password: <<password>>

4) Run the migration

rake db:migrate

5) Enter console, create an user and update its password from MySQL
query

script/console
Loading development environment (Rails 2.2.2)
>> User.create(:name => "John Doe")
>> key = 
"82pjd12398JKBSDIGUSisahdoahOUASDHsdapdjqwjeASIduAsdh078asdASD087asdADSsdjhA7809asdajhADSs"
>> ActiveRecord::Base.connection.execute("UPDATE users SET password = 
AES_ENCRYPT('password', '#{key}') WHERE name='John Doe'")

That's where I got stuck. If I attempt to decrypt it, using MySQL it
works:

>> loaded_user = User.find_by_sql("SELECT AES_DECRYPT(password, '#{key}') AS password FROM 
users WHERE id=1").first
>> loaded_user['password']
=> "password"

However if I attempt to use OpenSSL library, there's no way I can make
it work:

cipher = OpenSSL::Cipher::Cipher.new("AES-128-ECB")
cipher.padding = 0
cipher.key = key
cipher.decrypt

user = User.find(1)
cipher.update(user.password) << cipher.final #=>
"########gf####\027\227"

I have tried padding the key:

desired_length = 16 * ((key.length / 16) + 1)
padded_key = key + "\0" * (desired_length - key.length)

cipher = OpenSSL::Cipher::Cipher.new("AES-128-ECB")
cipher.key = key
cipher.decrypt

user = User.find(1)
cipher.update(user.password) << cipher.final #=>
""|\e\261\205:\032s\273\242\030\261\272P##"

But it really doesn't work.

Does anyone have a clue on how can I properly mimic whatever MySQL is
doing in Ruby?

Thanks a lot for your help.

Cheers,

-- Felipe.
C4ceadae4f61dac8ff3ababc3faa6c42?d=identicon&s=25 Felipe Coury (fcoury)
on 2009-03-24 04:28
One additional information:

You may find weird that I tried padding the key, and even using \000
char to pad it. The reasoning behind it is the way MySQL documents those
two functions:

"AES_ENCRYPT() and AES_DECRYPT() allow encryption and decryption of data
using the official AES (Advanced Encryption Standard) algorithm,
previously known as “Rijndael.” Encoding with a 128-bit key length is
used, but you can extend it up to 256 bits by modifying the source. We
chose 128 bits because it is much faster and it is secure enough for
most purposes.

AES_ENCRYPT() encrypts a string and returns a binary string.
AES_DECRYPT() decrypts the encrypted string and returns the original
string. The input arguments may be any length. If either argument is
NULL, the result of this function is also NULL.

Because AES is a block-level algorithm, padding is used to encode uneven
length strings and so the result string length may be calculated using
this formula:

16 × (trunc(string_length / 16) + 1)

If AES_DECRYPT() detects invalid data or incorrect padding, it returns
NULL. However, it is possible for AES_DECRYPT() to return a non-NULL
value (possibly garbage) if the input data or the key is invalid."

Hope that also helps.

Thanks again.

-- Felipe
Ef3aa7f7e577ea8cd620462724ddf73b?d=identicon&s=25 Rob Biedenharn (Guest)
on 2009-03-24 04:47
(Received via mailing list)
On Mar 23, 2009, at 11:25 PM, Felipe Coury wrote:

> Because AES is a block-level algorithm, padding is used to encode
> uneven
> length strings and so the result string length may be calculated using
> this formula:
>
> 16 × (trunc(string_length / 16) + 1)


Do you mean to have:

16 * (string_length + 15)/16

If the string length is 32, what do you expect the result to be? Your
formula gives 48, (16*(trunc(32/16)+1))==(16*(2+1)), while mine gives
32, (16*(32+15)/16)==(16*(47/16))==(16*2) [integer division].

Here's a bit of code that I've lifted out of another project:

     # Encrypt the content of the document, block by block, in a manner
     # compatible with the original Python (so we can decrypt it and
     # remain backwardly compatible)
     rijndael = Crypt::Rijndael.new(self.key, 256, 256)
     encryptedData = ""
     data << 'X'            # a marker added to cope with partial block
     blocks, bytes = data.length.divmod(32)
     unless bytes.zero?
       data << "\0" * (32 - bytes)
       blocks += 1
     end
     (0...blocks).each do |block|
       encryptedData << rijndael.encrypt_block(data[block * 32, 32])
     end

The decrypting side was Java and I don't know why the 'X' was chosen
(seems that I recall something about there being a byte with the
number of extra bytes of padding... or your formula might hold a clue).

Anyway, you'd have to adjust it for 128-bit/16-byte keys (and blocks).

-Rob

Rob Biedenharn    http://agileconsultingllc.com
Rob@AgileConsultingLLC.com
E6330e273bee4650040ab3d4614e0915?d=identicon&s=25 gcristelli (Guest)
on 2009-03-24 05:35
(Received via mailing list)
On 24 Mar, 04:21, Felipe Coury <felipe.co...@gmail.com> wrote:
> cipher.key = key
> cipher.decrypt
>
> user = User.find(1)
> cipher.update(user.password) << cipher.final #=>
> "########gf####\027\227"
>
I use the following code for encrypt/decrypt:

@cipherAES256=OpenSSL::Cipher::AES256.new("CBC") if @cipherAES256.nil?
@cipherAES256.encrypt
@cipherAES256.key=key
ct = @cipherAES256.update(plainPassword) + @cipherAES256.final
password=ct.unpack("H*")[0]

@cipherAES256=OpenSSL::Cipher::AES256.new("CBC") if @cipherAES256.nil?
@cipherAES256.decrypt
@cipherAES256.key=key
ct = @cipherAES256.update([password].pack("H*")) + @cipherAES256.final

I don't know if it can helps you (it uses 256 and CBC), try changing
your code from

  cipher.update(user.password) << cipher.final
to
  cipher.update([user.password].pack("H*")) << cipher.final

Giovanni
C4ceadae4f61dac8ff3ababc3faa6c42?d=identicon&s=25 Felipe Coury (fcoury)
on 2009-03-24 06:34
Giovanni / Rob,

Thanks a lot for your responses, really.

Unfortunately, it it still doesn't work. I tried what you both
suggested, take a look at this console transcript:

>> def aes(m,k,t)
>>   (aes = OpenSSL::Cipher::AES128.new("ECB").send(m)).key = k
>>   aes.update(t) << aes.final
>> end
=> nil
>>
?> def encrypt(key, text)
>>   aes(:encrypt, key, text)
>> end
=> nil
>>
?> def decrypt(key, text)
>>   aes(:decrypt, key, text)
>> end
=> nil

>> key = 
"82pjd12398JKBSDIGUSisahdoahOUASDHsdapdjqwjeASIduAsdh078asdASD087asdADSsdjhA7809asdajhADSs"
=>
"82pjd12398JKBSDIGUSisahdoahOUASDHsdapdjqwjeASIduAsdh078asdASD087asdADSsdjhA7809asdajhADSs"

>> u = User.find(1)
=> #<User id: 1, name: "John Doe", password:
"###\270##\206ή5\202?\003\021###", created_at: "2009-03-23 20:31:43",
updated_at: "2009-03-23 20:31:43">
>> u.password.length
=> 16

>> decrypt(key, u.password)
OpenSSL::CipherError: bad decrypt
        from (irb):8:in `final'
        from (irb):8:in `aes'
        from (irb):16:in `decrypt'
        from (irb):19

>> decrypt(key, [u.password].pack("H*"))
OpenSSL::CipherError: wrong final block length
        from (irb):8:in `final'
        from (irb):8:in `aes'
        from (irb):16:in `decrypt'
        from (irb):32

>> [u.password].pack("H*").length
=> 8
>> card = ([u.password].pack("H*") + ("\0" * 8))
=> "9##n###\005\000\000\000\000\000\000\000\000"
>> decrypt(key, card)
OpenSSL::CipherError: bad decrypt
        from (irb):8:in `final'
        from (irb):8:in `aes'
        from (irb):16:in `decrypt'
        from (irb):43

Any other ideas?

Thanks,

-- Felipe
C4ceadae4f61dac8ff3ababc3faa6c42?d=identicon&s=25 Felipe Coury (fcoury)
on 2009-03-24 07:30
Some more discoveries...

According to the blog post I sent before, here's how MySQL works with
the key you provide AES_ENCRYPT / DECRYPT:

"The algorithm just creates a 16 byte buffer set to all zero, then loops
through all the characters of the string you provide and does an
assignment with bitwise OR between the two values. If we iterate until
we hit the end of the 16 byte buffer, we just start over from the
beginning doing ^=. For strings shorter than 16 characters, we stop at
the end of the string."

I don't know if you can read C, but here's the mentioned snippet:

http://pastie.org/425161

Specially this part:

  bzero((char*) rkey,AES_KEY_LENGTH/8);      /* Set initial key  */

  for (ptr= rkey, sptr= key; sptr < key_end; ptr++,sptr++)
  {
    if (ptr == rkey_end)
      ptr= rkey;  /*  Just loop over tmp_key until we used all key */
    *ptr^= (uint8) *sptr;
  }


So I came up with this method:

def mysql_key(key)
  # The algorithm just creates a 16 byte buffer set to all zero,
  final_key = "\0" * 16

  # Number of string "blocks"
  t = key.length / 16

  t.times do |i|
    # For each block
    key_block = key[i*16, 16]

    # Runs bitwise XOR for each char on string
    # and the same char on the block
    16.times do |j|
      final_key[j] ^= key_block[j]
    end
  end

  final_key
end

But it still fails:

>> key = 
"82pjd12398JKBSDIGUSisahdoahOUASDHsdapdjqwjeASIduAsdh078asdASD087asdADSsdjhA7809asdajhADSs"
=>
"82pjd12398JKBSDIGUSisahdoahOUASDHsdapdjqwjeASIduAsdh078asdASD087asdADSsdjhA7809asdajhADSs"
>> mkey = mysql_key(key)
=> "\027\024GK\023P{#8?G!8[r."
>> mkey.length
=> 16

>> decrypt(mkey, User.find(1).password)
  User Load (11.3ms)   SELECT * FROM `users` WHERE (`users`.`id` = 1)
OpenSSL::CipherError: bad decrypt
        from (irb):4:in `final'
        from (irb):4:in `aes'
        from (irb):12:in `decrypt'
        from (irb):42

>> decrypt(mkey, [User.find(1).password].pack("H*"))
  User Load (2.8ms)   SELECT * FROM `users` WHERE (`users`.`id` = 1)
OpenSSL::CipherError: wrong final block length
        from (irb):4:in `final'
        from (irb):4:in `aes'
        from (irb):12:in `decrypt'
        from (irb):43

Question is: did I miss something :) ?

I have a feeling I am *almost* there...

Thanks again!

-- Felipe
C4ceadae4f61dac8ff3ababc3faa6c42?d=identicon&s=25 Felipe Coury (fcoury)
on 2009-03-24 07:38
Just as a FYI, it works!!!

I forgot about the remains... Take a look at the final incarnation:

def mysql_key(key)
  # The algorithm just creates a 16 byte buffer set to all zero,
  final_key = "\0" * 16

  # Number of string "blocks"
  blocks, remain = key.length.divmod(16)

  blocks.times do |i|
    # For each block
    key_block = key[i*16, 16]

    # Runs bitwise XOR for each char on string
    # and the same char on the block
    16.times do |j|
      final_key[j] ^= key_block[j]
    end
  end

  if remain
    remain.times do |i|
      final_key[i] ^= key[(blocks * 16) + i]
    end
  end

  final_key
end

And:

>> mkey = mysql_key(key)
=> "dp&!{\021?pK?G!8[r."
>> decrypt(mkey, User.find(1).password)
  User Load (2.9ms)   SELECT * FROM `users` WHERE (`users`.`id` = 1)
=> "password"

Just BEATIFUL!

Thanks a lot everyone!
Ef3aa7f7e577ea8cd620462724ddf73b?d=identicon&s=25 Rob Biedenharn (Guest)
on 2009-03-24 08:56
(Received via mailing list)
On Mar 24, 2009, at 2:35 AM, Felipe Coury wrote:
>
>
>
> Posted via http://www.ruby-forum.com/.
>


I'm glad you got it.  Your key-building function doesn't need to be
quite so complex:

def mysql_key2(key)
   final_key = "\0" * 16
   key.length.times do |i|
     final_key[i%16] ^= key[i]
   end
   final_key
end

Hardly needs any comments now ;-)  Just a pointer to the MySQL doc
perhaps.

irb> mkey2 = mysql_key2(key)
=> "dp&!{\021?pK?G!8[r."
irb> mkey == mkey2
=> true

-Rob

Rob Biedenharn    http://agileconsultingllc.com
Rob@AgileConsultingLLC.com
C4ceadae4f61dac8ff3ababc3faa6c42?d=identicon&s=25 Felipe Coury (fcoury)
on 2009-03-24 14:36
Rob Biedenharn wrote:
> I'm glad you got it.  Your key-building function doesn't need to be
> quite so complex:

Rob, you nailed it. Thanks a lot!

Best regards,

-- Felipe
A4d50b0eaa77b44409f4dc9a02d88b7e?d=identicon&s=25 Joshua Mckinney (joshmckin)
on 2010-07-01 17:34
Having no luck with this. Getting the following error when generating
they key in 1.8.7 and 1.9.1. Any help would be much appreciated.



ruby-1.9.1-p378 > key = "test_key"
 => "test_key"
ruby-1.9.1-p378 >     final_key = "\0" * 16
 => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
ruby-1.9.1-p378 >     key.length.times do |i|
ruby-1.9.1-p378 >           final_key[i%16] ^= key[i]
ruby-1.9.1-p378 ?>      end
NoMethodError: undefined method `^' for "\x00":String
  from (irb):89:in `block in irb_binding'
  from (irb):88:in `times'
  from (irb):88

ruby-1.9.1-p378 >     final_key
 => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"





Rob Biedenharn wrote:
> On Mar 24, 2009, at 2:35 AM, Felipe Coury wrote:
>>
>>
>>
>> Posted via http://www.ruby-forum.com/.
>>
>
>
> I'm glad you got it.  Your key-building function doesn't need to be
> quite so complex:
>
> def mysql_key2(key)
>    final_key = "\0" * 16
>    key.length.times do |i|
>      final_key[i%16] ^= key[i]
>    end
>    final_key
> end
>
> Hardly needs any comments now ;-)  Just a pointer to the MySQL doc
> perhaps.
>
> irb> mkey2 = mysql_key2(key)
> => "dp&!{\021?pK?G!8[r."
> irb> mkey == mkey2
> => true
>
> -Rob
>
> Rob Biedenharn    http://agileconsultingllc.com
> Rob@AgileConsultingLLC.com
Ef3aa7f7e577ea8cd620462724ddf73b?d=identicon&s=25 Rob Biedenharn (Guest)
on 2010-07-01 18:30
(Received via mailing list)
On Jul 1, 2010, at 11:34 AM, Joshua Mckinney wrote:
>>     final_key[i%16] ^= key[i]
>> => true
> ruby-1.9.1-p378 > key = "test_key"
>
> ruby-1.9.1-p378 >     final_key
> => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"

Because in Ruby 1.8.6, "hello"[0] is 104, but in 1.8.7 and 1.9.x,
"hello"[0] is "h"

Change that line to:

final_key[i%16] = (final_key[i%16].ord ^ key[i].ord).chr

And you should get the right answer:

irb> x="hello"
=> "hello"
irb> x[0] = (x[0].ord ^ 0x20).chr
=> "H"
irb> x
=> "Hello"

String#ord gives the Fixnum value of a single-character string.
Fixnum#chr gives the single-character String whose #ord is the Fixnum

(I'm sure the actual docs say that better ;-)

-Rob

Rob Biedenharn
http://agileconsultingllc.com
  Rob@AgileConsultingLLC.com
http://gaslightsoftware.com
  rab@GaslightSoftware.com
A4d50b0eaa77b44409f4dc9a02d88b7e?d=identicon&s=25 Joshua Mckinney (joshmckin)
on 2010-07-01 18:55
Correction: in 1.8.7 no error is produced but the final_key is not
correct:

ruby-1.8.7-p174 > key = "test key"
 => "test key"
ruby-1.8.7-p174 >    final_key = "\0" * 16
 => "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"
ruby-1.8.7-p174 >     key.length.times do |i|
ruby-1.8.7-p174 >           final_key[i%16] ^= key[i]
ruby-1.8.7-p174 ?>      end
 => 8
ruby-1.8.7-p174 >     final_key
 => "test key\000\000\000\000\000\000\000\000"


Joshua Mckinney wrote:
> Having no luck with this. Getting the following error when generating
> they key in 1.8.7 and 1.9.1. Any help would be much appreciated.
>
>
>
> ruby-1.9.1-p378 > key = "test_key"
>  => "test_key"
> ruby-1.9.1-p378 >     final_key = "\0" * 16
>  => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
> ruby-1.9.1-p378 >     key.length.times do |i|
> ruby-1.9.1-p378 >           final_key[i%16] ^= key[i]
> ruby-1.9.1-p378 ?>      end
> NoMethodError: undefined method `^' for "\x00":String
>   from (irb):89:in `block in irb_binding'
>   from (irb):88:in `times'
>   from (irb):88
>
> ruby-1.9.1-p378 >     final_key
>  => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
>
>
>
>
>
A4d50b0eaa77b44409f4dc9a02d88b7e?d=identicon&s=25 Joshua Mckinney (joshmckin)
on 2010-07-01 19:01
Thanks for the reply, we got rid of the error in 1.9.1 but both 1.9.1
and 1.8.7 produce the wrong final_key

ruby-1.9.1-p378 >     final_key
 => "test key\x00\x00\x00\x00\x00\x00\x00\x00"

ruby-1.8.7-p174 >     final_key
 => "test key\000\000\000\000\000\000\000\000"

Rob Biedenharn wrote:
> On Jul 1, 2010, at 11:34 AM, Joshua Mckinney wrote:
>>>     final_key[i%16] ^= key[i]
>>> => true
>> ruby-1.9.1-p378 > key = "test_key"
>>
>> ruby-1.9.1-p378 >     final_key
>> => "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
>
> Because in Ruby 1.8.6, "hello"[0] is 104, but in 1.8.7 and 1.9.x,
> "hello"[0] is "h"
>
> Change that line to:
>
> final_key[i%16] = (final_key[i%16].ord ^ key[i].ord).chr
>
> And you should get the right answer:
>
> irb> x="hello"
> => "hello"
> irb> x[0] = (x[0].ord ^ 0x20).chr
> => "H"
> irb> x
> => "Hello"
>
> String#ord gives the Fixnum value of a single-character string.
> Fixnum#chr gives the single-character String whose #ord is the Fixnum
>
> (I'm sure the actual docs say that better ;-)
>
> -Rob
>
> Rob Biedenharn
> http://agileconsultingllc.com
>   Rob@AgileConsultingLLC.com
> http://gaslightsoftware.com
>   rab@GaslightSoftware.com
Ef3aa7f7e577ea8cd620462724ddf73b?d=identicon&s=25 Rob Biedenharn (Guest)
on 2010-07-01 20:40
(Received via mailing list)
On Jul 1, 2010, at 1:01 PM, Joshua Mckinney wrote:
>> Because in Ruby 1.8.6, "hello"[0] is 104, but in 1.8.7 and 1.9.x,
>> irb> x[0] = (x[0].ord ^ 0x20).chr
>>
>
> ruby-1.8.7-p174 >     final_key
> => "test key\000\000\000\000\000\000\000\000"
>
> --
> Posted via http://www.ruby-forum.com/.
>

What final_key do you expect?  You need to post the full code and
input along with the expected value (or what 1.8.6 gives?) in order to
help.

-Rob
A4d50b0eaa77b44409f4dc9a02d88b7e?d=identicon&s=25 Joshua Mckinney (joshmckin)
on 2010-07-01 22:46
I'm here are all the methods for encryption

def mysql_encrypt(s, key)
    encrypt(s, mysql_key(key))
  end

  def mysql_decrypt(s, key)
    puts s
    decrypt(s, mysql_key(key))
  end

  protected
  def aes(m,k,t)
    (aes = OpenSSL::Cipher::AES128.new("ECB").send(m)).key = k
    aes.update(t) << aes.final
  end

  def encrypt(text, key)
    aes(:encrypt, key, text)
  end

  def decrypt(text, key)
    aes(:decrypt, key, text)
  end

  def mysql_key(key)
    key = key.encode("UTF-8")
    final_key = "\0" * 16
    key.length.times do |i|
      final_key[i%16] = (final_key[i%16].ord ^ key[i].ord).chr
    end
    final_key

  end


When is use the key set to "test key" and string to be encrypted set to
"Some text for encryption". The returned encrypted string is:
"\x9E\xB6g\xB7\xF0\xF8\x9F
M\xC1\x82\xA0\xFC\xEF[hY\xEC]=\xE6U\xE8o\xFBN\xCD\x929\x9A\xF4\xB5"

But the encrypted string should be:
"9EB667B7F0F89F204DC182A0FCEF5B6859EC5D3DE655E86FFB4ECD92399AF4B5"
(generated from C# mysql_mimic and Mysql itself)

My encrypted string appears some hexadecimal values interlaced and I'm
not sure why or how "decode" it properly to match the desire encryption
results.

The above results were produced in ruby 1.9.1 (which is what we are
using for this project)

Thanks





Rob Biedenharn wrote:
>
> What final_key do you expect?  You need to post the full code and
> input along with the expected value (or what 1.8.6 gives?) in order to
> help.
>
> -Rob
A4d50b0eaa77b44409f4dc9a02d88b7e?d=identicon&s=25 Joshua Mckinney (joshmckin)
on 2010-07-01 23:26
Looks like was I given some skewed information from our C# fella.

"\x9E\xB6g\xB7\xF0\xF8\x9F
M\xC1\x82\xA0\xFC\xEF[hY\xEC]=\xE6U\xE8o\xFBN\xCD\x929\x9A\xF4\xB5".unpack("H*")
= ["9eb667b7f0f89f204dc182a0fcef5b6859ec5d3de655e86ffb4ecd92399af4b5"]

which means everything it is working :-)

My only issue now is taking
"9eb667b7f0f89f204dc182a0fcef5b6859ec5d3de655e86ffb4ecd92399af4b5" and
"packing" it back to "\x9E\xB6g\xB7\xF0\xF8\x9F
M\xC1\x82\xA0\xFC\xEF[hY\xEC]=\xE6U\xE8o\xFBN\xCD\x929\x9A\xF4\xB5"

Anyone know how to do that?

Thanks,
Ef3aa7f7e577ea8cd620462724ddf73b?d=identicon&s=25 Rob Biedenharn (Guest)
on 2010-07-02 00:11
(Received via mailing list)
On Jul 1, 2010, at 5:27 PM, Joshua Mckinney wrote:

> "9eb667b7f0f89f204dc182a0fcef5b6859ec5d3de655e86ffb4ecd92399af4b5" and
> "packing" it back to "\x9E\xB6g\xB7\xF0\xF8\x9F
> M\xC1\x82\xA0\xFC\xEF[hY\xEC]=\xE6U\xE8o\xFBN\xCD\x929\x9A\xF4\xB5"
>
> Anyone know how to do that?
>
> Thanks,
> --
> Posted via http://www.ruby-forum.com/.
>


irb>
["9eb667b7f0f89f204dc182a0fcef5b6859ec5d3de655e86ffb4ecd92399af4b5
"].pack("H*")
=> "\x9E\xB6g\xB7\xF0\xF8\x9F M\xC1\x82\xA0\xFC\xEF[hY\xEC]=\xE6U\xE8o
\xFBN\xCD\x929\x9A\xF4\xB5"

Rob Biedenharn
http://agileconsultingllc.com
  Rob@AgileConsultingLLC.com
http://gaslightsoftware.com
  rab@GaslightSoftware.com
A4d50b0eaa77b44409f4dc9a02d88b7e?d=identicon&s=25 Joshua Mckinney (joshmckin)
on 2010-07-02 00:38
Awesome, could not find .pack in the 1.9.1 documentation. Everything is
now working.

Here is all the code for 1.9.1:


  def mysql_encrypt(s, key=@key)
    encrypt(s, mysql_key(key))
  end

  def mysql_decrypt(s, key=@key)
    puts s
    decrypt(s, mysql_key(key))
  end

  protected
  def aes(m,k,t)
    c = OpenSSL::Cipher::Cipher.new('aes-128-ecb').send(m)
    c.key = k
    c.update(t) + c.final

  end

  def encrypt(text, key)
    aes(:encrypt, key, text).unpack("H*")
  end

  def decrypt(text, key)
    aes(:decrypt, key, [text].pack("H*"))
  end

  def mysql_key(key)

    final_key = "\0" * 16
    key.length.times do |i|
      final_key[i%16] = (final_key[i%16].ord ^ key[i].ord).chr
    end
   final_key
  end


Thanks for you help Rob



Rob Biedenharn wrote:
> ["9eb667b7f0f89f204dc182a0fcef5b6859ec5d3de655e86ffb4ecd92399af4b5
> "].pack("H*")
> => "\x9E\xB6g\xB7\xF0\xF8\x9F M\xC1\x82\xA0\xFC\xEF[hY\xEC]=\xE6U\xE8o
> \xFBN\xCD\x929\x9A\xF4\xB5"
>
> Rob Biedenharn
> http://agileconsultingllc.com
>   Rob@AgileConsultingLLC.com
> http://gaslightsoftware.com
>   rab@GaslightSoftware.com
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2010-07-02 13:14
Joshua Mckinney wrote:
> Awesome, could not find .pack in the 1.9.1 documentation

It's Array#pack, as opposed to String#unpack.
A4d50b0eaa77b44409f4dc9a02d88b7e?d=identicon&s=25 Joshua Mckinney (joshmckin)
on 2010-07-02 17:46
Ha..well that would make sense given unpack returns an array. Deductive
reasoning was on the back-burner yesterday.

Made a small change so decrypt returns the string only

The following worked in 1.8.7 and 1.9.1

 def mysql_encrypt(s, key)
    encrypt(s, mysql_key(key))
  end

  def mysql_decrypt(s, key)
    puts s
    decrypt(s, mysql_key(key))
  end

  protected
  def aes(m,k,t)
    c = OpenSSL::Cipher::Cipher.new('aes-128-ecb').send(m)
    c.key = k
    c.update(t) + c.final

  end

  def encrypt(text, key)
    aes(:encrypt, key, text).unpack("H*")[0]
  end

  def decrypt(text, key)
    aes(:decrypt, key, [text].pack("H*"))
  end

  def mysql_key(key)
    final_key = "\0" * 16
    key.length.times do |i|
      final_key[i%16] = (final_key[i%16].ord ^ key[i].ord).chr
    end
   final_key
  end

Thanks,

Josh

Brian Candler wrote:
> Joshua Mckinney wrote:
>> Awesome, could not find .pack in the 1.9.1 documentation
>
> It's Array#pack, as opposed to String#unpack.
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.