Module: Megar::CryptoSupport
- Included in:
- FileDownloader, FileUploader, Session
- Defined in:
- lib/megar/crypto/support.rb
Overview
A straight-forward “quirks-mode” transcoding of core cryptographic methods required to talk to Mega. Some of this reeks a bit .. maybe more idiomatic ruby approaches are possible.
Generally we’re using signed 32-bit by default here … I don’t think it’s necessary, but it makes comparison with the javascript implementation easier.
Javascript reference implementations quoted here are taken from the Mega javascript source.
Instance Method Summary collapse
-
#a32_to_base64(a) ⇒ Object
Returns a base64-encoding given an array
a
of 32-bit integers. -
#a32_to_str(a) ⇒ Object
Returns a packed string given an array
a
of 32-bit signed integers. -
#accumulate_mac(chunk, progressive_mac, key, iv, signed = true) ⇒ Object
Calculates the accummulated MAC given new
chunk
. -
#aes_cbc_decrypt(data, key) ⇒ Object
Returns AES-128 CBC decrypted given
key
anddata
(String). -
#aes_cbc_decrypt_a32(data, key) ⇒ Object
Returns AES-128 CBC decrypted given
key
anddata
(arrays of 32-bit signed integers). -
#aes_cbc_encrypt(data, key) ⇒ Object
Returns AES-128 CBC encrypted given
key
anddata
(String). -
#aes_cbc_encrypt_a32(data, key, signed = true) ⇒ Object
Returns AES-128 CBC decrypted given
key
anddata
(arrays of 32-bit signed integers). -
#aes_encrypt_a32(data, key) ⇒ Object
Returns AES-128 encrypted given
key
anddata
(arrays of 32-bit signed integers). -
#base64_mpi_to_a32(s) ⇒ Object
Returns multiple precision integer (MPI) as an array of 32-bit signed integers decoded from base64 string
s
. -
#base64_mpi_to_bn(s) ⇒ Object
Returns multiple precision integer (MPI) as a big integers decoded from base64 string
s
. -
#base64_to_a32(s) ⇒ Object
Returns an array
a
of 32-bit integers given a base64-encodedb
(String). -
#base64urldecode(data) ⇒ Object
Returns a string given
data
(base64-encoded String). -
#base64urlencode(data) ⇒ Object
Returns a base64-encoding given
data
(String). -
#calculate_chunk_mac(chunk, key, iv, signed = false) ⇒ Object
Returns the
chunk
mac (array of unsigned int). -
#crypto_requirements_met? ⇒ Boolean
Verifies that the required crypto support is available from ruby/openssl.
-
#decompose_file_key(key) ⇒ Object
Returns a decomposed file
key
. -
#decompose_rsa_private_key(key) ⇒ Object
Returns the 4-part RSA key as array of big integers [d, p, q, u] given
key
(String). -
#decompose_rsa_private_key_a32(key) ⇒ Object
Returns the 4-part RSA key as 32-bit signed integers [d, p, q, u] given
key
(String). -
#decrypt_base64_to_a32(data, key) ⇒ Object
Returns decrypted array of 32-bit integers representation of base64
data
decrypted usingkey
. -
#decrypt_base64_to_str(data, key) ⇒ Object
Returns decrypted string representation of base64
data
decrypted usingkey
. -
#decrypt_file_attributes(attributes, key) ⇒ Object
Returns decrypted file attributes given encrypted
attributes
and decomposed filekey
. -
#decrypt_file_key(f) ⇒ Object
Returns a decrypted file key given
f
(API file response). -
#decrypt_key(a, key) ⇒ Object
Returns a decrypted given an array
a
of 32-bit integers andkey
. -
#decrypt_session_id(csid, rsa_private_key) ⇒ Object
Returns the decrypted session id given base64 MPI
csid
and RSArsa_private_key
as array of big integers [d, p, q, u]. -
#encrypt_file_attributes(attributes, key) ⇒ Object
Returns encrypted file attributes given encrypted
attributes
and decomposed filekey
. -
#encrypt_key(a, key) ⇒ Object
Javascript reference implementation: function encrypt_key(cipher,a).
-
#get_chunks(size) ⇒ Object
Returns an array of chunk sizes given total file
size
. -
#get_file_decrypter(key, iv) ⇒ Object
Returns AES CTR-mode decryption cipher given
key
andiv
as array of int. -
#get_file_encrypter(key, iv) ⇒ Object
Returns AES CTR-mode encryption cipher given
key
as array of int andiv
as binary string. -
#hexstr_to_bstr(h) ⇒ Object
Returns a binary string given a string
h
of hex digits. -
#mpi_to_a32(s) ⇒ Object
Returns multiple precision integer (MPI) as an array of 32-bit unsigned integers decoded from raw string
s
This first 16-bits of the MPI is the MPI length in bits. -
#openssl_rsa_cipher(pqdu) ⇒ Object
Returns an OpenSSL RSA cipher object initialised with
pqdu
(array of integer cipher components) p: The first factor of n, the RSA modulus q: The second factor of n d: The private exponent. -
#openssl_rsa_decrypt(m, pqdu) ⇒ Object
Returns the private key decryption of
m
givenpqdu
(array of integer cipher components) This implementation uses OpenSSL RSA public key feature. -
#prepare_key(a) ⇒ Object
Returns encrypted key given an array
a
of 32-bit integers. -
#prepare_key_pw(password) ⇒ Object
Returns encrypted key given the plain-text
password
string. -
#rsa_decrypt(m, pqdu) ⇒ Object
Returns the private key decryption of
m
givenpqdu
(array of integer cipher components). -
#str_to_a32(b, signed = true) ⇒ Object
Returns an array of 32-bit signed integers representing the string
b
. -
#stringhash(s, aeskey) ⇒ Object
Returns a base64-encoding of string
s
hashed withaeskey
key.
Instance Method Details
#a32_to_base64(a) ⇒ Object
Returns a base64-encoding given an array a
of 32-bit integers
Javascript reference implementation: function a32_to_base64(a)
163 164 165 |
# File 'lib/megar/crypto/support.rb', line 163 def a32_to_base64(a) base64urlencode(a32_to_str(a)) end |
#a32_to_str(a) ⇒ Object
Returns a packed string given an array a
of 32-bit signed integers
Javascript reference implementation: function a32_to_str(a)
141 142 143 144 145 |
# File 'lib/megar/crypto/support.rb', line 141 def a32_to_str(a) b = '' (a.size * 4).times { |i| b << ((a[i>>2] >> (24-(i & 3)*8)) & 255).chr } b end |
#accumulate_mac(chunk, progressive_mac, key, iv, signed = true) ⇒ Object
Calculates the accummulated MAC given new chunk
529 530 531 532 533 534 535 536 537 538 |
# File 'lib/megar/crypto/support.rb', line 529 def accumulate_mac(chunk,progressive_mac,key,iv,signed=true) chunk_mac = calculate_chunk_mac(chunk,key,iv,signed) combined_mac = [ progressive_mac[0] ^ chunk_mac[0], progressive_mac[1] ^ chunk_mac[1], progressive_mac[2] ^ chunk_mac[2], progressive_mac[3] ^ chunk_mac[3] ] aes_cbc_encrypt_a32(combined_mac, key, signed) end |
#aes_cbc_decrypt(data, key) ⇒ Object
Returns AES-128 CBC decrypted given key
and data
(String)
94 95 96 97 98 99 100 101 102 103 |
# File 'lib/megar/crypto/support.rb', line 94 def aes_cbc_decrypt(data, key) aes = OpenSSL::Cipher::Cipher.new('AES-128-CBC') aes.decrypt aes.padding = 0 aes.key = key aes.iv = "\0" * 16 d = aes.update(data) d = aes.final if d.empty? d end |
#aes_cbc_decrypt_a32(data, key) ⇒ Object
Returns AES-128 CBC decrypted given key
and data
(arrays of 32-bit signed integers)
89 90 91 |
# File 'lib/megar/crypto/support.rb', line 89 def aes_cbc_decrypt_a32(data, key) str_to_a32(aes_cbc_decrypt(a32_to_str(data), a32_to_str(key))) end |
#aes_cbc_encrypt(data, key) ⇒ Object
Returns AES-128 CBC encrypted given key
and data
(String)
111 112 113 114 115 116 117 118 119 120 |
# File 'lib/megar/crypto/support.rb', line 111 def aes_cbc_encrypt(data, key) aes = OpenSSL::Cipher::Cipher.new('AES-128-CBC') aes.encrypt aes.padding = 0 aes.key = key aes.iv = "\0" * 16 d = aes.update(data) d = aes.final if d.empty? d end |
#aes_cbc_encrypt_a32(data, key, signed = true) ⇒ Object
Returns AES-128 CBC decrypted given key
and data
(arrays of 32-bit signed integers)
106 107 108 |
# File 'lib/megar/crypto/support.rb', line 106 def aes_cbc_encrypt_a32(data, key, signed=true) str_to_a32(aes_cbc_encrypt(a32_to_str(data), a32_to_str(key)),signed) end |
#aes_encrypt_a32(data, key) ⇒ Object
Returns AES-128 encrypted given key
and data
(arrays of 32-bit signed integers)
77 78 79 80 81 82 83 84 85 86 |
# File 'lib/megar/crypto/support.rb', line 77 def aes_encrypt_a32(data, key) aes = OpenSSL::Cipher::Cipher.new('AES-128-ECB') aes.encrypt aes.padding = 0 aes.key = key.pack('l>*') aes.update(data.pack('l>*')).unpack('l>*') # e = aes.update(data.pack('l>*')).unpack('l>*') # e << aes.final # e.unpack('l>*') end |
#base64_mpi_to_a32(s) ⇒ Object
Returns multiple precision integer (MPI) as an array of 32-bit signed integers decoded from base64 string s
252 253 254 |
# File 'lib/megar/crypto/support.rb', line 252 def base64_mpi_to_a32(s) mpi_to_a32(base64urldecode(s)) end |
#base64_mpi_to_bn(s) ⇒ Object
Returns multiple precision integer (MPI) as a big integers decoded from base64 string s
258 259 260 261 262 |
# File 'lib/megar/crypto/support.rb', line 258 def base64_mpi_to_bn(s) data = base64urldecode(s) len = ((data[0].ord * 256 + data[1].ord + 7) / 8) + 2 data[2,len+2].unpack('H*').first.to_i(16) end |
#base64_to_a32(s) ⇒ Object
Returns an array a
of 32-bit integers given a base64-encoded b
(String)
Javascript reference implementation: function base64_to_a32(s)
171 172 173 |
# File 'lib/megar/crypto/support.rb', line 171 def base64_to_a32(s) str_to_a32(base64urldecode(s)) end |
#base64urldecode(data) ⇒ Object
Returns a string given data
(base64-encoded String)
Javascript reference implementation: function base64urldecode(data)
187 188 189 |
# File 'lib/megar/crypto/support.rb', line 187 def base64urldecode(data) Base64.urlsafe_decode64(data + '=' * ((4 - data.length % 4) % 4)) end |
#base64urlencode(data) ⇒ Object
Returns a base64-encoding given data
(String).
Javascript reference implementation: function base64urlencode(data)
179 180 181 |
# File 'lib/megar/crypto/support.rb', line 179 def base64urlencode(data) Base64.urlsafe_encode64(data).gsub(/=*$/,'') end |
#calculate_chunk_mac(chunk, key, iv, signed = false) ⇒ Object
Returns the chunk
mac (array of unsigned int)
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 |
# File 'lib/megar/crypto/support.rb', line 509 def calculate_chunk_mac(chunk,key,iv,signed=false) chunk_mac = iv (0..chunk.length-1).step(16).each do |i| block = chunk[i,16] if (mod = block.length % 16) > 0 block << "\x0" * (16 - mod) end block = str_to_a32(block,signed) chunk_mac = [ chunk_mac[0] ^ block[0], chunk_mac[1] ^ block[1], chunk_mac[2] ^ block[2], chunk_mac[3] ^ block[3] ] chunk_mac = aes_cbc_encrypt_a32(chunk_mac, key, signed) end chunk_mac end |
#crypto_requirements_met? ⇒ Boolean
Verifies that the required crypto support is available from ruby/openssl
15 16 17 |
# File 'lib/megar/crypto/support.rb', line 15 def crypto_requirements_met? OpenSSL::Cipher.ciphers.include?("AES-128-CTR") end |
#decompose_file_key(key) ⇒ Object
Returns a decomposed file key
Javascript reference implementation: function startdownload2(res,ctx)
446 447 448 449 450 451 452 453 |
# File 'lib/megar/crypto/support.rb', line 446 def decompose_file_key(key) [ key[0] ^ key[4], key[1] ^ key[5], key[2] ^ key[6], key[3] ^ key[7] ] end |
#decompose_rsa_private_key(key) ⇒ Object
Returns the 4-part RSA key as array of big integers [d, p, q, u] given key
(String)
result = p: The first factor of n, the RSA modulus result = q: The second factor of n result = d: The private exponent. result = u: The CRT coefficient, equals to (1/p) mod q.
Javascript reference implementation: function api_getsid2(res,ctx)
299 300 301 302 303 304 305 306 307 308 309 310 311 312 |
# File 'lib/megar/crypto/support.rb', line 299 def decompose_rsa_private_key(key) privk = key.dup decomposed_key = [] offset = 0 4.times do |i| len = ((privk[0].ord * 256 + privk[1].ord + 7) / 8) + 2 privk_part = privk[0,len] # puts "\nl: ", len # puts "decrypted rsa part hex: \n", privk_part.unpack('H*').first decomposed_key << privk_part[2,privk_part.length].unpack('H*').first.to_i(16) privk.slice!(0,len) end decomposed_key end |
#decompose_rsa_private_key_a32(key) ⇒ Object
Returns the 4-part RSA key as 32-bit signed integers [d, p, q, u] given key
(String)
result = p: The first factor of n, the RSA modulus result = q: The second factor of n result = d: The private exponent. result = u: The CRT coefficient, equals to (1/p) mod q.
Javascript reference implementation: function api_getsid2(res,ctx)
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/megar/crypto/support.rb', line 274 def decompose_rsa_private_key_a32(key) privk = key.dup decomposed_key = [] # puts "decomp: privk.len:#{privk.length}" 4.times do len = ((privk[0].ord * 256 + privk[1].ord + 7) / 8) + 2 privk_part = privk[0,len] # puts "\nprivk_part #{base64urlencode(privk_part)}" privk_part_a32 = mpi_to_a32(privk_part) decomposed_key << privk_part_a32 # puts "decomp: len:#{len} privk_part_a32:#{privk_part_a32.length} first:#{privk_part_a32.first} last:#{privk_part_a32.last}" privk.slice!(0,len) end decomposed_key end |
#decrypt_base64_to_a32(data, key) ⇒ Object
Returns decrypted array of 32-bit integers representation of base64 data
decrypted using key
66 67 68 |
# File 'lib/megar/crypto/support.rb', line 66 def decrypt_base64_to_a32(data,key) decrypt_key(base64_to_a32(data), key) end |
#decrypt_base64_to_str(data, key) ⇒ Object
Returns decrypted string representation of base64 data
decrypted using key
71 72 73 |
# File 'lib/megar/crypto/support.rb', line 71 def decrypt_base64_to_str(data,key) a32_to_str(decrypt_base64_to_a32(data, key)) end |
#decrypt_file_attributes(attributes, key) ⇒ Object
Returns decrypted file attributes given encrypted attributes
and decomposed file key
Javascript reference implementation: function dec_attr(attr,key)
425 426 427 428 |
# File 'lib/megar/crypto/support.rb', line 425 def decrypt_file_attributes(attributes,key) rstr = aes_cbc_decrypt(base64urldecode(attributes), a32_to_str(key)) JSON.parse( rstr.gsub("\x0",'').gsub(/^.*{/,'{')) end |
#decrypt_file_key(f) ⇒ Object
Returns a decrypted file key given f
(API file response)
416 417 418 419 |
# File 'lib/megar/crypto/support.rb', line 416 def decrypt_file_key(f) key = f['k'].split(':')[1] decrypt_key(base64_to_a32(key), self.master_key) end |
#decrypt_key(a, key) ⇒ Object
Returns a decrypted given an array a
of 32-bit integers and key
Javascript reference implementation: function decrypt_key(cipher,a)
47 48 49 50 51 52 53 |
# File 'lib/megar/crypto/support.rb', line 47 def decrypt_key(a, key) b=[] (0..(a.length-1)).step(4) do |i| b.concat aes_cbc_decrypt_a32(a[i,4], key) end b end |
#decrypt_session_id(csid, rsa_private_key) ⇒ Object
Returns the decrypted session id given base64 MPI csid
and RSA rsa_private_key
as array of big integers [d, p, q, u]
Javascript reference implementation: function api_getsid2(res,ctx)
318 319 320 321 322 323 324 325 |
# File 'lib/megar/crypto/support.rb', line 318 def decrypt_session_id(csid,rsa_private_key) csid_bn = base64_mpi_to_bn(csid) sid_bn = rsa_decrypt(csid_bn,rsa_private_key) sid_hs = sid_bn.to_s(16) sid_hs = '0' + sid_hs if sid_hs.length % 2 > 0 sid = hexstr_to_bstr(sid_hs)[0,43] base64urlencode(sid) end |
#encrypt_file_attributes(attributes, key) ⇒ Object
Returns encrypted file attributes given encrypted attributes
and decomposed file key
Javascript reference implementation: function enc_attr(attr,key)
434 435 436 437 438 439 440 |
# File 'lib/megar/crypto/support.rb', line 434 def encrypt_file_attributes(attributes, key) rstr = 'MEGA' + attributes.to_json if (mod = rstr.length % 16) > 0 rstr << "\x0" * (16 - mod) end aes_cbc_encrypt(rstr, a32_to_str(key)) end |
#encrypt_key(a, key) ⇒ Object
Javascript reference implementation: function encrypt_key(cipher,a)
57 58 59 60 61 62 63 |
# File 'lib/megar/crypto/support.rb', line 57 def encrypt_key(a, key) b=[] (0..(a.length-1)).step(4) do |i| b.concat aes_cbc_encrypt_a32(a[i,4], key) end b end |
#get_chunks(size) ⇒ Object
Returns an array of chunk sizes given total file size
Chunk boundaries are located at the following positions: 0 / 128K / 384K / 768K / 1280K / 1920K / 2688K / 3584K / 4608K / … (every 1024 KB) / EOF
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 |
# File 'lib/megar/crypto/support.rb', line 460 def get_chunks(size) chunks = [] p = pp = 0 i = 1 while i <= 8 and p < size - i * 0x20000 do chunk_size = i * 0x20000 chunks << [p, chunk_size] pp = p p += chunk_size i += 1 end while p < size - 0x100000 do chunk_size = 0x100000 chunks << [p, chunk_size] pp = p p += chunk_size end chunks << [p, size - p] if p < size chunks end |
#get_file_decrypter(key, iv) ⇒ Object
Returns AES CTR-mode decryption cipher given key
and iv
as array of int
487 488 489 490 491 492 493 494 |
# File 'lib/megar/crypto/support.rb', line 487 def get_file_decrypter(key,iv) aes = OpenSSL::Cipher::Cipher.new('AES-128-CTR') aes.decrypt aes.padding = 0 aes.key = a32_to_str(key) aes.iv = a32_to_str(iv) aes end |
#get_file_encrypter(key, iv) ⇒ Object
Returns AES CTR-mode encryption cipher given key
as array of int and iv
as binary string
498 499 500 501 502 503 504 505 |
# File 'lib/megar/crypto/support.rb', line 498 def get_file_encrypter(key,iv) aes = OpenSSL::Cipher::Cipher.new('AES-128-CTR') aes.encrypt aes.padding = 0 aes.key = a32_to_str(key) aes.iv = iv aes end |
#hexstr_to_bstr(h) ⇒ Object
Returns a binary string given a string h
of hex digits
409 410 411 412 413 |
# File 'lib/megar/crypto/support.rb', line 409 def hexstr_to_bstr(h) bstr = '' (0..h.length-1).step(2) {|n| bstr << h[n,2].to_i(16).chr } bstr end |
#mpi_to_a32(s) ⇒ Object
Returns multiple precision integer (MPI) as an array of 32-bit unsigned integers decoded from raw string s
This first 16-bits of the MPI is the MPI length in bits
Javascript reference implementation: function mpi2b(s)
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/megar/crypto/support.rb', line 196 def mpi_to_a32(s) bs=28 bx2=1<<bs bm=bx2-1 bn=1 r=[0] rn=0 sb=256 c = nil sn=s.length return 0 if(sn < 2) len=(sn-2)*8 bits=s[0].ord*256+s[1].ord return 0 if(bits > len || bits < len-8) len.times do |n| if ((sb<<=1) > 255) sb=1 sn -= 1 c=s[sn].ord end if(bn > bm) bn=1 rn += 1 r << 0 end if(c & sb != 0) r[rn]|=bn end bn<<=1 end r end |
#openssl_rsa_cipher(pqdu) ⇒ Object
Returns an OpenSSL RSA cipher object initialised with pqdu
(array of integer cipher components) p: The first factor of n, the RSA modulus q: The second factor of n d: The private exponent. u: The CRT coefficient, equals to (1/p) mod q.
NB: this hacks the RSA object creation n a way that should work, but can’t get this to work exactly right with Mega yet
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 |
# File 'lib/megar/crypto/support.rb', line 391 def openssl_rsa_cipher(pqdu) rsa = OpenSSL::PKey::RSA.new p, q, d, u = pqdu rsa.p, rsa.q, rsa.d = p, q, d rsa.n = rsa.p * rsa.q # # dmp1 = d mod (p-1) # rsa.dmp1 = rsa.d % (rsa.p - 1) # # dmq1 = d mod (q-1) # rsa.dmq1 = rsa.d % (rsa.q - 1) # # iqmp = q^-1 mod p? # rsa.iqmp = (rsa.q ** -1) % rsa.p # # ipmq = (rsa.p ** -1) % rsa.q # ipmq = rsa.p ** -1 % rsa.q rsa.e = 0 # 65537 rsa end |
#openssl_rsa_decrypt(m, pqdu) ⇒ Object
Returns the private key decryption of m
given pqdu
(array of integer cipher components) This implementation uses OpenSSL RSA public key feature.
NB: can’t get this to work exactly right with Mega yet
371 372 373 374 375 376 377 378 379 380 381 382 |
# File 'lib/megar/crypto/support.rb', line 371 def openssl_rsa_decrypt(m, pqdu) rsa = openssl_rsa_cipher(pqdu) chunk_size = 256 # hmm. need to figure out how to calc for "data greater than mod len" # number.size(self.n) - 1 : Return the maximum number of bits that can be handled by this key. decrypt_texts = [] (0..m.length - 1).step(chunk_size) do |i| pt_part = m[i,chunk_size] decrypt_texts << rsa.private_decrypt(pt_part,3) end decrypt_texts.join end |
#prepare_key(a) ⇒ Object
Returns encrypted key given an array a
of 32-bit integers
Javascript reference implementation: function prepare_key(a)
23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/megar/crypto/support.rb', line 23 def prepare_key(a) pkey = [0x93C467E3, 0x7DB0C7A4, 0xD1BE3F81, 0x0152CB56] 0x10000.times do (0..(a.length-1)).step(4) do |j| key = [0,0,0,0] 4.times {|i| key[i] = a[i+j] if (i+j < a.length) } pkey = aes_encrypt_a32(pkey,key) end end pkey end |
#prepare_key_pw(password) ⇒ Object
Returns encrypted key given the plain-text password
string
Javascript reference implementation: function prepare_key_pw(password)
39 40 41 |
# File 'lib/megar/crypto/support.rb', line 39 def prepare_key_pw(password) prepare_key(str_to_a32(password)) end |
#rsa_decrypt(m, pqdu) ⇒ Object
Returns the private key decryption of m
given pqdu
(array of integer cipher components). Computes m**d (mod n).
This implementation uses a Pure Ruby implementation of RSA private_decrypt
p: The first factor of n, the RSA modulus q: The second factor of n d: The private exponent. u: The CRT coefficient, equals to (1/p) mod q.
n = pq n is used as the modulus for both the public and private keys. Its length, usually expressed in bits, is the key length.
φ(n) = (p – 1)(q – 1), where φ is Euler’s totient function.
Choose an integer e such that 1 < e < φ(n) and gcd(e, φ(n)) = 1; i.e., e and φ(n) are coprime. e is released as the public key exponent
Determine d as d ≡ e−1 (mod φ(n)), i.e., d is the multiplicative inverse of e (modulo φ(n)). d is kept as the private key exponent.
More info: en.wikipedia.org/wiki/RSA_(algorithm)#Operation
Javascript reference implementation: function RSAdecrypt(m, d, p, q, u)
352 353 354 355 356 357 358 359 360 361 362 363 364 |
# File 'lib/megar/crypto/support.rb', line 352 def rsa_decrypt(m, pqdu) p, q, d, u = pqdu if p && q && u m1 = Math.powm(m, d % (p-1), p) m2 = Math.powm(m, d % (q-1), q) h = m2 - m1 h = h + q if h < 0 h = h*u % q h*p+m1 else Math.powm(m, d, p*q) end end |
#str_to_a32(b, signed = true) ⇒ Object
Returns an array of 32-bit signed integers representing the string b
Javascript reference implementation: function str_to_a32(b)
126 127 128 129 130 131 132 133 134 135 |
# File 'lib/megar/crypto/support.rb', line 126 def str_to_a32(b,signed=true) a = Array.new((b.length+3) >> 2,0) b.length.times { |i| a[i>>2] |= (b.getbyte(i) << (24-(i & 3)*8)) } if signed # hack to force to signed 32-bit ... I don't think we really need to do this, but it makes comparison with a.pack('l>*').unpack('l>*') else a end end |
#stringhash(s, aeskey) ⇒ Object
Returns a base64-encoding of string s
hashed with aeskey
key
Javascript reference implementation: function stringhash(s,aes)
151 152 153 154 155 156 157 |
# File 'lib/megar/crypto/support.rb', line 151 def stringhash(s,aeskey) s32 = str_to_a32(s) h32 = [0,0,0,0] s32.length.times {|i| h32[i&3] ^= s32[i] } 16384.times {|i| h32 = aes_encrypt_a32(h32, aeskey) } a32_to_base64([h32[0],h32[2]]) end |