fabric-gateway 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.editorconfig +3 -0
- data/.github/workflows/rspec.yml +37 -0
- data/.github/workflows/rubocop.yml +28 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +23 -0
- data/.ruby-version +1 -0
- data/.vscode/settings.json +7 -0
- data/CODE_OF_CONDUCT.md +105 -46
- data/Gemfile +5 -3
- data/README.md +61 -14
- data/Rakefile +5 -3
- data/bin/console +4 -3
- data/bin/release +5 -0
- data/fabric-gateway.gemspec +29 -18
- data/lib/fabric/client.rb +134 -0
- data/lib/fabric/constants.rb +8 -0
- data/lib/fabric/contract.rb +163 -0
- data/lib/fabric/ec_crypto_suite.rb +199 -0
- data/lib/fabric/gateway.rb +28 -14
- data/lib/fabric/identity.rb +87 -0
- data/lib/fabric/network.rb +61 -0
- data/lib/fabric/proposal.rb +182 -0
- data/lib/fabric/proposed_transaction.rb +187 -0
- data/lib/fabric/version.rb +5 -0
- data/lib/fabric.rb +44 -0
- metadata +105 -14
- data/Gemfile.lock +0 -44
- data/lib/fabric/gateway/client.rb +0 -13
- data/lib/fabric/gateway/constants.rb +0 -20
- data/lib/fabric/gateway/ec_crypto_suite.rb +0 -172
- data/lib/fabric/gateway/identity.rb +0 -47
- data/lib/fabric/gateway/proposal.rb +0 -105
- data/lib/fabric/gateway/version.rb +0 -5
@@ -1,20 +0,0 @@
|
|
1
|
-
module Fabric
|
2
|
-
module Gateway
|
3
|
-
module Constants
|
4
|
-
## System Chaincodes
|
5
|
-
LSCC = 'lscc'.freeze
|
6
|
-
QSCC = 'qscc'.freeze
|
7
|
-
CSCC = 'cscc'.freeze
|
8
|
-
|
9
|
-
## System Channels
|
10
|
-
SYSTEM_CHANNEL_NAME = 'testchainid'.freeze
|
11
|
-
|
12
|
-
## System Functions
|
13
|
-
FUNC_GET_CHANNELS = 'GetChannels'.freeze
|
14
|
-
FUNC_GET_CONFIG_BLOCK = 'GetConfigBlock'.freeze
|
15
|
-
|
16
|
-
## Variables
|
17
|
-
CHANNEL_HEADER_VERSION = 1
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
@@ -1,172 +0,0 @@
|
|
1
|
-
require 'openssl'
|
2
|
-
|
3
|
-
module Fabric
|
4
|
-
module Gateway
|
5
|
-
# Elliptic-curve Crypto Suite using OpenSSL
|
6
|
-
class ECCryptoSuite
|
7
|
-
DEFAULT_KEY_SIZE = 256
|
8
|
-
DEFAULT_DIGEST_ALGORITHM = 'SHA256'.freeze
|
9
|
-
DEFAULT_AES_KEY_SIZE = 128
|
10
|
-
|
11
|
-
EC_CURVES = { 256 => 'prime256v1', 384 => 'secp384r1' }.freeze
|
12
|
-
|
13
|
-
CIPHER = 'aes-256-cbc'.freeze
|
14
|
-
|
15
|
-
attr_reader :key_size, :digest_algorithm, :digest, :curve, :cipher
|
16
|
-
|
17
|
-
def initialize(opts = {})
|
18
|
-
@key_size = opts[:key_size] || DEFAULT_KEY_SIZE
|
19
|
-
@digest_algorithm = opts[:digest_algorithm] || DEFAULT_DIGEST_ALGORITHM
|
20
|
-
@digest = OpenSSL::Digest.new digest_algorithm
|
21
|
-
@curve = EC_CURVES[key_size]
|
22
|
-
@cipher = opts[:cipher] || CIPHER
|
23
|
-
end
|
24
|
-
|
25
|
-
def sign(private_key, message)
|
26
|
-
digest = digest message
|
27
|
-
key = pkey_from_private_key private_key
|
28
|
-
signature = key.dsa_sign_asn1 digest
|
29
|
-
sequence = OpenSSL::ASN1.decode signature
|
30
|
-
sequence = prevent_malleability sequence, key.group.order
|
31
|
-
|
32
|
-
sequence.to_der
|
33
|
-
end
|
34
|
-
|
35
|
-
def generate_private_key
|
36
|
-
key = OpenSSL::PKey::EC.new curve
|
37
|
-
key.generate_key!
|
38
|
-
|
39
|
-
key.private_key.to_s(16).downcase
|
40
|
-
end
|
41
|
-
|
42
|
-
def generate_csr(private_key, attrs = [])
|
43
|
-
key = pkey_from_private_key private_key
|
44
|
-
|
45
|
-
req = OpenSSL::X509::Request.new
|
46
|
-
req.public_key = key
|
47
|
-
req.subject = OpenSSL::X509::Name.new attrs
|
48
|
-
req.sign key, @digest
|
49
|
-
|
50
|
-
req
|
51
|
-
end
|
52
|
-
|
53
|
-
def generate_nonce(length = 24)
|
54
|
-
OpenSSL::Random.random_bytes length
|
55
|
-
end
|
56
|
-
|
57
|
-
def hexdigest(message)
|
58
|
-
@digest.hexdigest message
|
59
|
-
end
|
60
|
-
|
61
|
-
def digest(message)
|
62
|
-
@digest.digest message
|
63
|
-
end
|
64
|
-
|
65
|
-
def encode_hex(bytes)
|
66
|
-
bytes.unpack('H*').first
|
67
|
-
end
|
68
|
-
|
69
|
-
def decode_hex(string)
|
70
|
-
[string].pack('H*')
|
71
|
-
end
|
72
|
-
|
73
|
-
def keccak256(bytes)
|
74
|
-
OpenSSL::Digest.new('SHA3-256').digest bytes
|
75
|
-
end
|
76
|
-
|
77
|
-
|
78
|
-
def restore_public_key(private_key)
|
79
|
-
private_bn = OpenSSL::BN.new private_key, 16
|
80
|
-
group = OpenSSL::PKey::EC::Group.new curve
|
81
|
-
public_bn = group.generator.mul(private_bn).to_bn
|
82
|
-
public_bn = OpenSSL::PKey::EC::Point.new(group, public_bn).to_bn
|
83
|
-
|
84
|
-
public_bn.to_s(16).downcase
|
85
|
-
end
|
86
|
-
|
87
|
-
def address_from_public_key(public_key)
|
88
|
-
bytes = decode_hex public_key
|
89
|
-
address_bytes = keccak256(bytes[1..-1])[-20..-1]
|
90
|
-
|
91
|
-
encode_hex address_bytes
|
92
|
-
end
|
93
|
-
|
94
|
-
def build_shared_key(private_key, public_key)
|
95
|
-
pkey = pkey_from_private_key private_key
|
96
|
-
public_bn = OpenSSL::BN.new public_key, 16
|
97
|
-
group = OpenSSL::PKey::EC::Group.new curve
|
98
|
-
public_point = OpenSSL::PKey::EC::Point.new group, public_bn
|
99
|
-
|
100
|
-
encode_hex pkey.dh_compute_key(public_point)
|
101
|
-
end
|
102
|
-
|
103
|
-
def encrypt(secret, data)
|
104
|
-
aes = OpenSSL::Cipher.new cipher
|
105
|
-
aes.encrypt
|
106
|
-
aes.key = decode_hex(secret)
|
107
|
-
iv = aes.random_iv
|
108
|
-
aes.iv = iv
|
109
|
-
|
110
|
-
Base64.strict_encode64(iv + aes.update(data) + aes.final)
|
111
|
-
end
|
112
|
-
|
113
|
-
def decrypt(secret, data)
|
114
|
-
return unless data
|
115
|
-
|
116
|
-
encrypted_data = Base64.strict_decode64 data
|
117
|
-
aes = OpenSSL::Cipher.new cipher
|
118
|
-
aes.decrypt
|
119
|
-
aes.key = decode_hex(secret)
|
120
|
-
aes.iv = encrypted_data[0..15]
|
121
|
-
encrypted_data = encrypted_data[16..-1]
|
122
|
-
|
123
|
-
aes.update(encrypted_data) + aes.final
|
124
|
-
end
|
125
|
-
|
126
|
-
def pkey_pem_from_private_key(private_key)
|
127
|
-
public_key = restore_public_key private_key
|
128
|
-
key = OpenSSL::PKey::EC.new curve
|
129
|
-
key.private_key = OpenSSL::BN.new private_key, 16
|
130
|
-
key.public_key = OpenSSL::PKey::EC::Point.new key.group,
|
131
|
-
OpenSSL::BN.new(public_key, 16)
|
132
|
-
|
133
|
-
pkey = OpenSSL::PKey::EC.new(key.public_key.group)
|
134
|
-
pkey.public_key = key.public_key
|
135
|
-
|
136
|
-
pkey.to_pem
|
137
|
-
end
|
138
|
-
|
139
|
-
def key_from_pem(pem)
|
140
|
-
key = OpenSSL::PKey::EC.new(pem)
|
141
|
-
key.private_key.to_s(16)
|
142
|
-
end
|
143
|
-
|
144
|
-
def pkey_from_x509_certificate(certificate)
|
145
|
-
cert = OpenSSL::X509::Certificate.new(certificate)
|
146
|
-
cert.public_key.public_key.to_bn.to_s(16)
|
147
|
-
end
|
148
|
-
|
149
|
-
private
|
150
|
-
|
151
|
-
def pkey_from_private_key(private_key)
|
152
|
-
public_key = restore_public_key private_key
|
153
|
-
key = OpenSSL::PKey::EC.new curve
|
154
|
-
key.private_key = OpenSSL::BN.new private_key, 16
|
155
|
-
key.public_key = OpenSSL::PKey::EC::Point.new key.group,
|
156
|
-
OpenSSL::BN.new(public_key, 16)
|
157
|
-
|
158
|
-
key
|
159
|
-
end
|
160
|
-
|
161
|
-
def prevent_malleability(sequence, order)
|
162
|
-
half_order = order >> 1
|
163
|
-
|
164
|
-
if (half_key = sequence.value[1].value) > half_order
|
165
|
-
sequence.value[1].value = order - half_key
|
166
|
-
end
|
167
|
-
|
168
|
-
sequence
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
@@ -1,47 +0,0 @@
|
|
1
|
-
require 'msp/identities_pb'
|
2
|
-
require 'base64'
|
3
|
-
|
4
|
-
module Fabric
|
5
|
-
module Gateway
|
6
|
-
class Identity
|
7
|
-
attr_reader :private_key,
|
8
|
-
:public_key,
|
9
|
-
:address,
|
10
|
-
:crypto_suite
|
11
|
-
|
12
|
-
attr_accessor :pem_certificate, :certificate, :mspid
|
13
|
-
|
14
|
-
def initialize(opts = {})
|
15
|
-
@crypto_suite = opts[:crypto_suite] || Fabric::Gateway.crypto_suite
|
16
|
-
|
17
|
-
@private_key = opts[:private_key] || @crypto_suite.generate_private_key
|
18
|
-
@public_key = opts[:public_key] || @crypto_suite.restore_public_key(private_key)
|
19
|
-
@certificate = opts[:certificate]
|
20
|
-
@pem_certificate = opts[:pem_certificate]
|
21
|
-
@mspid = opts[:mspid]
|
22
|
-
|
23
|
-
@address = @crypto_suite.address_from_public_key public_key
|
24
|
-
end
|
25
|
-
|
26
|
-
def generate_csr(attrs = [])
|
27
|
-
@crypto_suite.generate_csr private_key, attrs
|
28
|
-
end
|
29
|
-
|
30
|
-
def sign(message)
|
31
|
-
@crypto_suite.sign(private_key, message)
|
32
|
-
end
|
33
|
-
|
34
|
-
def shared_secret_by(public_key)
|
35
|
-
@crypto_suite.build_shared_key private_key, public_key
|
36
|
-
end
|
37
|
-
|
38
|
-
def decoded_certificate
|
39
|
-
Base64.strict_decode64 certificate
|
40
|
-
end
|
41
|
-
|
42
|
-
def serialize
|
43
|
-
Msp::SerializedIdentity.new(mspid: mspid, id_bytes: pem_certificate).to_proto
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
@@ -1,105 +0,0 @@
|
|
1
|
-
module Fabric
|
2
|
-
module Gateway
|
3
|
-
class Proposal
|
4
|
-
attr_reader :identity, :request
|
5
|
-
|
6
|
-
def initialize(identity, request = {})
|
7
|
-
@identity = identity
|
8
|
-
@request = request
|
9
|
-
|
10
|
-
assign_tx request[:transaction_info] if request[:transaction_info]
|
11
|
-
end
|
12
|
-
|
13
|
-
def crypto_suite
|
14
|
-
identity.crypto_suite
|
15
|
-
end
|
16
|
-
|
17
|
-
def nonce
|
18
|
-
@nonce ||= crypto_suite.generate_nonce
|
19
|
-
end
|
20
|
-
|
21
|
-
def channel_id
|
22
|
-
request[:channel_id]
|
23
|
-
end
|
24
|
-
|
25
|
-
def chaincode_id
|
26
|
-
request[:chaincode_id]
|
27
|
-
end
|
28
|
-
|
29
|
-
def args
|
30
|
-
request[:args].compact.map &:to_s
|
31
|
-
end
|
32
|
-
|
33
|
-
def transient
|
34
|
-
request[:transient] || {}
|
35
|
-
end
|
36
|
-
|
37
|
-
def transaction_id
|
38
|
-
request[:transaction_id]
|
39
|
-
end
|
40
|
-
|
41
|
-
def tx_id
|
42
|
-
@tx_id ||= crypto_suite.hexdigest(nonce + identity.serialize)
|
43
|
-
end
|
44
|
-
|
45
|
-
def proposal
|
46
|
-
@proposal ||= Protos::Proposal.new header: header.to_proto,
|
47
|
-
payload: chaincode_proposal.to_proto
|
48
|
-
end
|
49
|
-
|
50
|
-
def signed_proposal
|
51
|
-
proposal_bytes = proposal.to_proto
|
52
|
-
signature = identity.sign proposal_bytes
|
53
|
-
|
54
|
-
Protos::SignedProposal.new proposal_bytes: proposal_bytes, signature: signature
|
55
|
-
end
|
56
|
-
|
57
|
-
def header
|
58
|
-
Common::Header.new channel_header: channel_header.to_proto,
|
59
|
-
signature_header: signature_header.to_proto
|
60
|
-
end
|
61
|
-
|
62
|
-
def channel_header
|
63
|
-
Common::ChannelHeader.new type: Common::HeaderType::ENDORSER_TRANSACTION,
|
64
|
-
channel_id: channel_id, tx_id: tx_id,
|
65
|
-
extension: channel_header_extension.to_proto,
|
66
|
-
timestamp: tx_timestamp,
|
67
|
-
version: Constants::CHANNEL_HEADER_VERSION
|
68
|
-
end
|
69
|
-
|
70
|
-
def channel_header_extension
|
71
|
-
id = Protos::ChaincodeID.new name: chaincode_id
|
72
|
-
|
73
|
-
Protos::ChaincodeHeaderExtension.new chaincode_id: id
|
74
|
-
end
|
75
|
-
|
76
|
-
def tx_timestamp
|
77
|
-
now = Time.now
|
78
|
-
|
79
|
-
@tx_timestamp ||= Google::Protobuf::Timestamp.new seconds: now.to_i, nanos: now.nsec
|
80
|
-
end
|
81
|
-
|
82
|
-
def signature_header
|
83
|
-
Common::SignatureHeader.new creator: identity.serialize, nonce: nonce
|
84
|
-
end
|
85
|
-
|
86
|
-
def chaincode_proposal
|
87
|
-
id = Protos::ChaincodeID.new name: chaincode_id
|
88
|
-
chaincode_input = Protos::ChaincodeInput.new args: args
|
89
|
-
chaincode_spec = Protos::ChaincodeSpec.new type: Protos::ChaincodeSpec::Type::NODE,
|
90
|
-
chaincode_id: id,
|
91
|
-
input: chaincode_input
|
92
|
-
input = Protos::ChaincodeInvocationSpec.new chaincode_spec: chaincode_spec
|
93
|
-
|
94
|
-
Protos::ChaincodeProposalPayload.new input: input.to_proto, TransientMap: transient
|
95
|
-
end
|
96
|
-
|
97
|
-
private
|
98
|
-
|
99
|
-
def assign_tx(transaction_info)
|
100
|
-
@tx_id = transaction_info[:tx_id]
|
101
|
-
@nonce = crypto_suite.decode_hex transaction_info[:nonce_hex]
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|