module Ciri::Crypto
Constants
- ECIES_CIPHER_NAME
- SECP256K1N
- VERSION
Public Instance Methods
ecdsa_recover(msg, signature, return_raw_key: true)
click to toggle source
# File lib/ciri/crypto.rb, line 46 def ecdsa_recover(msg, signature, return_raw_key: true) signature = Signature.new(signature: signature) unless signature.is_a?(Signature) pk = Secp256k1::PrivateKey.new(flags: Secp256k1::ALL_FLAGS) sig, recid = signature.signature[0..-2], signature.v recsig = pk.ecdsa_recoverable_deserialize(sig, recid % 4) pubkey = pk.ecdsa_recover(msg, recsig, raw: true) key = Secp256k1::PublicKey.new(pubkey: pubkey) return_raw_key ? key.serialize(compressed: false) : key rescue Secp256k1::AssertError => e raise ECDSASignatureError.new(e) end
ecdsa_signature(key, data)
click to toggle source
# File lib/ciri/crypto.rb, line 40 def ecdsa_signature(key, data) secp256k1_key = ensure_secp256k1_key(privkey: key) signature, recid = secp256k1_key.ecdsa_recoverable_serialize(secp256k1_key.ecdsa_sign_recoverable(data, raw: true)) Signature.new(signature: signature + Ciri::Utils.big_endian_encode(recid, "\x00".b)) end
ecies_decrypt(data, priv_key, shared_mac_data = '')
click to toggle source
# File lib/ciri/crypto.rb, line 83 def ecies_decrypt(data, priv_key, shared_mac_data = '') raise ECIESDecryptionError.new('invalid header') if data[0] != "\x04" # compute shared_secret ephem_raw_pubkey = data[0..64] # add first byte tag ephem_pubkey = ec_pkey_from_raw(ephem_raw_pubkey) shared_secret = priv_key.dh_compute_key(ephem_pubkey.public_key) key = ecies_kdf(shared_secret, 32) key_enc, key_mac = key[0...16], key[16..-1] # verify data key_mac = Digest::SHA256.digest(key_mac) tag = data[-32..-1] unless Ciri::Utils.secret_compare(hmac_sha256(key_mac, data[65...-32] + shared_mac_data), tag) raise ECIESDecryptionError.new("Fail to verify data") end # decrypt data cipher = OpenSSL::Cipher.new(ECIES_CIPHER_NAME) iv_start = 65 iv_end = iv_start + cipher.iv_len iv = data[iv_start...iv_end] ciphertext = data[iv_end...-32] cipher.decrypt cipher.key = key_enc cipher.iv = iv cipher.update(ciphertext) + cipher.final end
ecies_encrypt(message, raw_pubkey, shared_mac_data = '')
click to toggle source
# File lib/ciri/crypto.rb, line 60 def ecies_encrypt(message, raw_pubkey, shared_mac_data = '') pubkey = raw_pubkey.is_a?(OpenSSL::PKey::EC) ? raw_pubkey : ec_pkey_from_raw(raw_pubkey) # compute keys ephem_key = OpenSSL::PKey::EC.new('secp256k1') ephem_key.generate_key shared_secret = ephem_key.dh_compute_key(pubkey.public_key) key = ecies_kdf(shared_secret, 32) key_enc, key_mac = key[0...16], key[16..-1] key_mac = Digest::SHA256.digest(key_mac) ephem_raw_pubkey = ephem_key.public_key.to_bn.to_s(2) cipher = OpenSSL::Cipher.new(ECIES_CIPHER_NAME) cipher.encrypt iv = cipher.random_iv cipher.key = key_enc cipher_text = cipher.update(message) + cipher.final msg = ephem_raw_pubkey + iv + cipher_text tag = hmac_sha256(key_mac, msg[ephem_raw_pubkey.size..-1] + shared_mac_data) msg + tag end
ensure_secp256k1_key(privkey:)
click to toggle source
# File lib/ciri/crypto.rb, line 116 def ensure_secp256k1_key(privkey:) privkey.is_a?(Secp256k1::BaseKey) ? privkey : Secp256k1::PrivateKey.new(privkey: privkey) end
Private Instance Methods
ec_pkey_from_raw(raw_pubkey, raw_privkey: nil)
click to toggle source
# File lib/ciri/crypto.rb, line 143 def ec_pkey_from_raw(raw_pubkey, raw_privkey: nil) Ciri::Utils.create_ec_pk(raw_pubkey: raw_pubkey, raw_privkey: raw_privkey) end
ecies_kdf(key_material, key_len)
click to toggle source
# File lib/ciri/crypto.rb, line 122 def ecies_kdf(key_material, key_len) s1 = ''.b key = ''.b hash_block_size = 64 reps = ((key_len + 7) * 8) / (hash_block_size * 8) counter = 0 while counter <= reps counter += 1 ctx = Digest::SHA256.new ctx.update([counter].pack("I>*")) ctx.update(key_material) ctx.update(s1) key += ctx.digest end key end
hmac_sha256(key, data)
click to toggle source
# File lib/ciri/crypto.rb, line 139 def hmac_sha256(key, data) OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), key, data) end