class Fabric::ECCryptoSuite

Elliptic-curve Crypto Suite using OpenSSL

@todo missing tests

Constants

CIPHER
DEFAULT_AES_KEY_SIZE
DEFAULT_DIGEST_ALGORITHM
DEFAULT_KEY_SIZE
EC_CURVES

Attributes

cipher[R]
curve[R]
digest_algorithm[R]
digest_instance[R]
key_size[R]

Public Class Methods

new(opts = {}) click to toggle source
# File lib/fabric/ec_crypto_suite.rb, line 23
def initialize(opts = {})
  @key_size = opts[:key_size] || DEFAULT_KEY_SIZE
  @digest_algorithm = opts[:digest_algorithm] || DEFAULT_DIGEST_ALGORITHM
  @digest_instance = OpenSSL::Digest.new digest_algorithm
  @curve = EC_CURVES[key_size]
  @cipher = opts[:cipher] || CIPHER
end

Public Instance Methods

address_from_public_key(public_key) click to toggle source
# File lib/fabric/ec_crypto_suite.rb, line 97
def address_from_public_key(public_key)
  bytes = decode_hex public_key
  address_bytes = digest(bytes[1..])[-20..]

  encode_hex address_bytes
end
build_shared_key(private_key, public_key) click to toggle source
# File lib/fabric/ec_crypto_suite.rb, line 104
def build_shared_key(private_key, public_key)
  pkey = pkey_from_private_key private_key
  public_bn = OpenSSL::BN.new public_key, 16
  group = OpenSSL::PKey::EC::Group.new curve
  public_point = OpenSSL::PKey::EC::Point.new group, public_bn

  encode_hex pkey.dh_compute_key(public_point)
end
decode_hex(string) click to toggle source
# File lib/fabric/ec_crypto_suite.rb, line 84
def decode_hex(string)
  [string].pack('H*')
end
decrypt(secret, data) click to toggle source
# File lib/fabric/ec_crypto_suite.rb, line 123
def decrypt(secret, data)
  return unless data

  encrypted_data = Base64.strict_decode64 data
  aes = OpenSSL::Cipher.new cipher
  aes.decrypt
  aes.key = decode_hex(secret)
  aes.iv = encrypted_data[0..15]
  encrypted_data = encrypted_data[16..]

  aes.update(encrypted_data) + aes.final
end
digest(message) click to toggle source
# File lib/fabric/ec_crypto_suite.rb, line 76
def digest(message)
  @digest_instance.digest message
end
encode_hex(bytes) click to toggle source
# File lib/fabric/ec_crypto_suite.rb, line 80
def encode_hex(bytes)
  bytes.unpack1('H*')
end
encrypt(secret, data) click to toggle source
# File lib/fabric/ec_crypto_suite.rb, line 113
def encrypt(secret, data)
  aes = OpenSSL::Cipher.new cipher
  aes.encrypt
  aes.key = decode_hex(secret)
  iv = aes.random_iv
  aes.iv = iv

  Base64.strict_encode64(iv + aes.update(data) + aes.final)
end
generate_csr(private_key, attrs = []) click to toggle source
# File lib/fabric/ec_crypto_suite.rb, line 57
def generate_csr(private_key, attrs = [])
  key = pkey_from_private_key private_key

  req = OpenSSL::X509::Request.new
  req.public_key = key
  req.subject = OpenSSL::X509::Name.new attrs
  req.sign key, @digest_instance

  req
end
generate_nonce(length = 24) click to toggle source
# File lib/fabric/ec_crypto_suite.rb, line 68
def generate_nonce(length = 24)
  OpenSSL::Random.random_bytes length
end
generate_private_key() click to toggle source
# File lib/fabric/ec_crypto_suite.rb, line 50
def generate_private_key
  key = OpenSSL::PKey::EC.new curve
  key.generate_key!

  key.private_key.to_s(16).downcase
end
hexdigest(message) click to toggle source
# File lib/fabric/ec_crypto_suite.rb, line 72
def hexdigest(message)
  @digest_instance.hexdigest message
end
key_from_pem(pem) click to toggle source
# File lib/fabric/ec_crypto_suite.rb, line 149
def key_from_pem(pem)
  key = OpenSSL::PKey::EC.new(pem)
  key.private_key.to_s(16).downcase
end
openssl_pkey_from_public_key(public_key) click to toggle source
# File lib/fabric/ec_crypto_suite.rb, line 159
def openssl_pkey_from_public_key(public_key)
  pkey = OpenSSL::PKey::EC.new curve
  pkey.public_key = OpenSSL::PKey::EC::Point.new(pkey.group, OpenSSL::BN.new(public_key, 16))

  pkey
end
pkey_from_x509_certificate(certificate) click to toggle source
# File lib/fabric/ec_crypto_suite.rb, line 154
def pkey_from_x509_certificate(certificate)
  cert = OpenSSL::X509::Certificate.new(certificate)
  cert.public_key.public_key.to_bn.to_s(16).downcase
end
pkey_pem_from_private_key(private_key) click to toggle source
# File lib/fabric/ec_crypto_suite.rb, line 136
def pkey_pem_from_private_key(private_key)
  public_key = restore_public_key private_key
  key = OpenSSL::PKey::EC.new curve
  key.private_key = OpenSSL::BN.new private_key, 16
  key.public_key = OpenSSL::PKey::EC::Point.new key.group,
                                                OpenSSL::BN.new(public_key, 16)

  pkey = OpenSSL::PKey::EC.new(key.public_key.group)
  pkey.public_key = key.public_key

  pkey.to_pem
end
restore_public_key(private_key) click to toggle source
# File lib/fabric/ec_crypto_suite.rb, line 88
def restore_public_key(private_key)
  private_bn = OpenSSL::BN.new private_key, 16
  group = OpenSSL::PKey::EC::Group.new curve
  public_bn = group.generator.mul(private_bn).to_bn
  public_bn = OpenSSL::PKey::EC::Point.new(group, public_bn).to_bn

  public_bn.to_s(16).downcase
end
sign(private_key, message) click to toggle source
# File lib/fabric/ec_crypto_suite.rb, line 31
def sign(private_key, message)
  digest = digest message
  key = pkey_from_private_key private_key
  signature = key.dsa_sign_asn1 digest
  sequence = OpenSSL::ASN1.decode signature
  sequence = prevent_malleability sequence, key.group.order

  sequence.to_der
end
verify(public_key, message, signature) click to toggle source
# File lib/fabric/ec_crypto_suite.rb, line 41
def verify(public_key, message, signature)
  digest = digest message
  openssl_pkey = openssl_pkey_from_public_key public_key
  sequence = OpenSSL::ASN1.decode signature
  return false unless check_malleability sequence, openssl_pkey.group.order

  openssl_pkey.dsa_verify_asn1(digest, signature)
end

Private Instance Methods

check_malleability(sequence, order) click to toggle source

ported from python code, understanding extremely limited. from what I gather, sequence.value and sequence.value are the r and s values from the python implementation github.com/hyperledger/fabric-sdk-py/blob/25209f61518873da68d28313582607c29b5bae7d/hfc/util/crypto/crypto.py#L259

# File lib/fabric/ec_crypto_suite.rb, line 194
def check_malleability(sequence, order)
  half_order = order >> 1
  sequence.value[1].value <= half_order
end
pkey_from_private_key(private_key) click to toggle source
# File lib/fabric/ec_crypto_suite.rb, line 168
def pkey_from_private_key(private_key)
  public_key = restore_public_key private_key
  key = OpenSSL::PKey::EC.new curve
  key.private_key = OpenSSL::BN.new private_key, 16
  key.public_key = OpenSSL::PKey::EC::Point.new key.group,
                                                OpenSSL::BN.new(public_key, 16)

  key
end
prevent_malleability(sequence, order) click to toggle source

barely understand this code - this link provides a good explanation: coders-errand.com/malleability-ecdsa-signatures/

# File lib/fabric/ec_crypto_suite.rb, line 180
def prevent_malleability(sequence, order)
  half_order = order >> 1

  if (half_key = sequence.value[1].value) > half_order
    sequence.value[1].value = order - half_key
  end

  sequence
end