fabric-gateway 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|