Class: Fabric::ECCryptoSuite

Inherits:
Object
  • Object
show all
Defined in:
lib/fabric/ec_crypto_suite.rb

Overview

TODO:

missing tests

Elliptic-curve Crypto Suite using OpenSSL

Constant Summary collapse

DEFAULT_KEY_SIZE =

rubocop:disable Metrics/ClassLength

256
DEFAULT_DIGEST_ALGORITHM =
'SHA256'
DEFAULT_AES_KEY_SIZE =
128
EC_CURVES =
{ 256 => 'prime256v1', 384 => 'secp384r1' }.freeze
CIPHER =
'aes-256-cbc'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ ECCryptoSuite

Returns a new instance of ECCryptoSuite.



23
24
25
26
27
28
29
# 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

Instance Attribute Details

#cipherObject (readonly)

Returns the value of attribute cipher.



21
22
23
# File 'lib/fabric/ec_crypto_suite.rb', line 21

def cipher
  @cipher
end

#curveObject (readonly)

Returns the value of attribute curve.



21
22
23
# File 'lib/fabric/ec_crypto_suite.rb', line 21

def curve
  @curve
end

#digest_algorithmObject (readonly)

Returns the value of attribute digest_algorithm.



21
22
23
# File 'lib/fabric/ec_crypto_suite.rb', line 21

def digest_algorithm
  @digest_algorithm
end

#digest_instanceObject (readonly)

Returns the value of attribute digest_instance.



21
22
23
# File 'lib/fabric/ec_crypto_suite.rb', line 21

def digest_instance
  @digest_instance
end

#key_sizeObject (readonly)

Returns the value of attribute key_size.



21
22
23
# File 'lib/fabric/ec_crypto_suite.rb', line 21

def key_size
  @key_size
end

Instance Method Details

#address_from_public_key(public_key) ⇒ Object



97
98
99
100
101
102
# 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) ⇒ Object



104
105
106
107
108
109
110
111
# 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) ⇒ Object



84
85
86
# File 'lib/fabric/ec_crypto_suite.rb', line 84

def decode_hex(string)
  [string].pack('H*')
end

#decrypt(secret, data) ⇒ Object



123
124
125
126
127
128
129
130
131
132
133
134
# 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) ⇒ Object



76
77
78
# File 'lib/fabric/ec_crypto_suite.rb', line 76

def digest(message)
  @digest_instance.digest message
end

#encode_hex(bytes) ⇒ Object



80
81
82
# File 'lib/fabric/ec_crypto_suite.rb', line 80

def encode_hex(bytes)
  bytes.unpack1('H*')
end

#encrypt(secret, data) ⇒ Object



113
114
115
116
117
118
119
120
121
# 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 = []) ⇒ Object



57
58
59
60
61
62
63
64
65
66
# 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) ⇒ Object



68
69
70
# File 'lib/fabric/ec_crypto_suite.rb', line 68

def generate_nonce(length = 24)
  OpenSSL::Random.random_bytes length
end

#generate_private_keyObject



50
51
52
53
54
55
# 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) ⇒ Object



72
73
74
# File 'lib/fabric/ec_crypto_suite.rb', line 72

def hexdigest(message)
  @digest_instance.hexdigest message
end

#key_from_pem(pem) ⇒ Object



149
150
151
152
# 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) ⇒ Object



159
160
161
162
163
164
# 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) ⇒ Object



154
155
156
157
# 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) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
# 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) ⇒ Object



88
89
90
91
92
93
94
95
# 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) ⇒ Object



31
32
33
34
35
36
37
38
39
# 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) ⇒ Object



41
42
43
44
45
46
47
48
# 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