From 419c82c8b15a903a9f0df61ba7888a8a268a1a22 Mon Sep 17 00:00:00 2001 From: Jonathan Chan Date: Sat, 21 Oct 2023 16:51:35 -0500 Subject: [PATCH 01/20] updating ruby-version, refactoring ec_crypto_suite to support OpenSSL 3.0 and improve clarity --- .ruby-version | 2 +- lib/fabric/ec_crypto_suite.rb | 44 +++--- lib/fabric/entities/identity.rb | 2 +- spec/fabric/ec_crypto_suite_spec.rb | 229 +++++++++++++++++++++++++++- 4 files changed, 253 insertions(+), 24 deletions(-) diff --git a/.ruby-version b/.ruby-version index a603bb5..be94e6f 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.7.5 +3.2.2 diff --git a/lib/fabric/ec_crypto_suite.rb b/lib/fabric/ec_crypto_suite.rb index 5c644b8..89febf7 100644 --- a/lib/fabric/ec_crypto_suite.rb +++ b/lib/fabric/ec_crypto_suite.rb @@ -40,7 +40,7 @@ def sign(private_key, message) def verify(public_key, message, signature) digest = digest message - openssl_pkey = openssl_pkey_from_public_key public_key + openssl_pkey = pkey_from_public_key public_key sequence = OpenSSL::ASN1.decode signature return false unless check_malleability sequence, openssl_pkey.group.order @@ -133,15 +133,31 @@ def decrypt(secret, data) aes.update(encrypted_data) + aes.final end - def pkey_pem_from_private_key(private_key) + # when https://github.com/ruby/openssl/pull/555 gets merged, consider refactoring + # the code here + 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) - pkey = OpenSSL::PKey::EC.new(key.public_key.group) - pkey.public_key = key.public_key + group = OpenSSL::PKey::EC::Group.new(curve) + + private_key_bn = OpenSSL::BN.new(private_key, 16) + public_key_bn = OpenSSL::BN.new(public_key, 16) + public_key_point = OpenSSL::PKey::EC::Point.new(group, public_key_bn) + + asn1 = OpenSSL::ASN1::Sequence( + [ + OpenSSL::ASN1::Integer.new(1), + OpenSSL::ASN1::OctetString(private_key_bn.to_s(2)), + OpenSSL::ASN1::ObjectId(curve, 0, :EXPLICIT), + OpenSSL::ASN1::BitString(public_key_point.to_octet_string(:uncompressed), 1, :EXPLICIT) + ] + ) + + OpenSSL::PKey::EC.new(asn1.to_der) + end + + def pem_from_private_key(private_key) + pkey=pkey_from_private_key(private_key) pkey.to_pem end @@ -151,12 +167,12 @@ def key_from_pem(pem) key.private_key.to_s(16).downcase end - def pkey_from_x509_certificate(certificate) + def public_key_from_x509_certificate(certificate) cert = OpenSSL::X509::Certificate.new(certificate) cert.public_key.public_key.to_bn.to_s(16).downcase end - def openssl_pkey_from_public_key(public_key) + def 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)) @@ -165,15 +181,7 @@ def openssl_pkey_from_public_key(public_key) private - 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 # barely understand this code - this link provides a good explanation: # http://coders-errand.com/malleability-ecdsa-signatures/ diff --git a/lib/fabric/entities/identity.rb b/lib/fabric/entities/identity.rb index 2656f1f..7f41a7d 100644 --- a/lib/fabric/entities/identity.rb +++ b/lib/fabric/entities/identity.rb @@ -42,7 +42,7 @@ def initialize(private_key: nil, public_key: nil, certificate: nil, msp_id: nil, # @return [boolean] true if valid, false otherwise # def validate_key_integrity - cert_pubkey = @crypto_suite.pkey_from_x509_certificate(certificate) + cert_pubkey = @crypto_suite.public_key_from_x509_certificate(certificate) priv_pubkey = @crypto_suite.restore_public_key(@private_key) @public_key == cert_pubkey && @public_key == priv_pubkey diff --git a/spec/fabric/ec_crypto_suite_spec.rb b/spec/fabric/ec_crypto_suite_spec.rb index 4fb7f64..e972394 100644 --- a/spec/fabric/ec_crypto_suite_spec.rb +++ b/spec/fabric/ec_crypto_suite_spec.rb @@ -1,8 +1,229 @@ # frozen_string_literal: true RSpec.describe Fabric::ECCryptoSuite do - describe '#openssl_pkey_from_public_key' do - subject(:crypto_suite) { described_class.new } + let(:private_key) { 'd62e76ab4a907d7634ada0d9709b4ed2bfc7c51c421127b7fc93c0141e461797' } + let(:public_key) { '04a01f01fa942d2233a64aebe0b36c16ebdfd1c453ac5297591f20e2bfaba869e17e15f5f7367ee6f16121c64cac3ecdd517920a36f5145dc2a881ae9371873ac6' } + let(:random_secret) { 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' } + + subject(:crypto_suite) { described_class.new } + describe '#initialize' do + context 'when no parameters are passed' do + it 'utilizes default options' do + expect(crypto_suite.key_size).to eql(256) + expect(crypto_suite.digest_algorithm).to eql('SHA256') + expect(crypto_suite.curve).to eql('prime256v1') + expect(crypto_suite.cipher).to eql('aes-256-cbc') + end + end + + context 'when options are passed' do + subject(:crypto_suite) do + described_class.new( + { + key_size: 384, + digest_algorithm: 'SHA224', + cipher: 'aes-128-cbc' + } + ) + end + it 'utilizes options passed' do + expect(crypto_suite.key_size).to eql(384) + expect(crypto_suite.digest_algorithm).to eql('SHA224') + expect(crypto_suite.curve).to eql('secp384r1') + expect(crypto_suite.cipher).to eql('aes-128-cbc') + end + end + end + + describe '#sign' do + it 'creates a valid signature' do + signature = crypto_suite.sign(private_key, 'this is a test') + expect(crypto_suite.verify(public_key, 'this is a test', signature)).to eql(true) + end + end + + describe '#verify' do + context 'when the signature is invalid' do + it 'raises an error' do + expect { crypto_suite.verify(public_key, 'this is a test', 'invalid') }.to raise_error(OpenSSL::ASN1::ASN1Error) + end + end + + context 'when the signature matches the key and the message' do + it 'returns true' do + signature = crypto_suite.sign(private_key, 'this is a test') + expect(crypto_suite.verify(public_key, 'this is a test', signature)).to eql(true) + end + end + + context 'when the signature does not match the key and the message' do + it 'return false' do + signature = crypto_suite.sign(private_key, 'this is a test') + expect(crypto_suite.verify(public_key, 'this is not a test', signature)).to eql(false) + end + end + end + + describe '#generate_private_key' do + it 'generates a valid EC private_key' do + private_key = crypto_suite.generate_private_key + expect(private_key).to be_a(String) + + public_key = crypto_suite.restore_public_key private_key + + + group = OpenSSL::PKey::EC::Group.new(crypto_suite.curve) + + private_key_bn = OpenSSL::BN.new(private_key, 16) + public_key_bn = OpenSSL::BN.new(public_key, 16) + public_key_point = OpenSSL::PKey::EC::Point.new(group, public_key_bn) + + asn1 = OpenSSL::ASN1::Sequence( + [ + OpenSSL::ASN1::Integer.new(1), + OpenSSL::ASN1::OctetString(private_key_bn.to_s(2)), + OpenSSL::ASN1::ObjectId(crypto_suite.curve, 0, :EXPLICIT), + OpenSSL::ASN1::BitString(public_key_point.to_octet_string(:uncompressed), 1, :EXPLICIT) + ] + ) + + pkey = OpenSSL::PKey::EC.new(asn1.to_der) + + expect(pkey.private?).to eql(true) + expect(pkey.private_key.to_s(16).downcase).to eql(private_key) + end + end + + describe '#generate_csr' do + it 'generates a OpenSSL::X509::Request with the proper key' do + req = crypto_suite.generate_csr(private_key) + + expect(req).to be_a(OpenSSL::X509::Request) + expect(req.public_key.private_key.to_s(16).downcase).to eql(private_key) + end + end + + describe '#generate_nonce' do + context 'when no parameters pass' do + it 'generates a random 24 byte string' do + expect(crypto_suite.generate_nonce.length).to eql(24) + end + end + + context 'when passing byte length' do + it 'generates a random byte string the size passed in' do + expect(crypto_suite.generate_nonce(50).length).to eql(50) + end + end + end + + describe '#hexdigest' do + it 'generates a hexdigest' do + expect(crypto_suite.hexdigest('hello world')).to eql('b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9') + end + end + + describe '#digest' do + it 'generates a digest' do + digest = crypto_suite.digest('hello world') + expected_digest = ['b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9'].pack('H*') + expect(digest).to eql(expected_digest) + end + end + + describe '#encode_hex' do + it 'converts bytes to hex' do + expect(crypto_suite.encode_hex('a')).to eql('61') + end + end + + describe '#decode_hex' do + it 'converts bytes to hex' do + expect(crypto_suite.decode_hex('61')).to eql('a') + end + end + + describe '#restore_public_key' do + it 'regenerates the public key from the private key' do + expect(crypto_suite.restore_public_key(private_key)).to eql(public_key) + end + end + + describe '#address_from_public_key' do + it 'returns the address from the public key' do + expect(crypto_suite.address_from_public_key(public_key)).to eql('5de210d00aa3614d0e99ff84fe380bd34835f66e') + end + end + + # TODO - it might be useful to test that the shared key can be properly utilized as well + describe '#build_shared_key' do + let(:random_public_key) do + '04293ed1ea547c079f06f7bc6aa8adec39fd465ba839323a262fc7abab7714ba6' \ + 'e680305dcfdf97043bfb1817a932cd7f4883d255b03ef303cf6651d765b9b3418' + end + it 'returns a shared key' do + shared_key = crypto_suite.build_shared_key(private_key, random_public_key) + expect(shared_key).to eql('f1388005817ef6c5f0e8d4f655b000c083a67926c991eaea3da4adf1fc20ceb5') + end + end + + describe '#decrypt' do + context 'when data is nil' do + it 'returns nil' do + expect(crypto_suite.decrypt(random_secret, nil)).to be_nil + end + end + end + + describe '#encrypt/#decrypt' do + it 'properly able to encrypte and decrypt strings' do + expect(crypto_suite.decrypt(random_secret, crypto_suite.encrypt(random_secret, 'this is a test'))).to eql('this is a test') + end + end + + describe '#pkey_from_private_key' do + it 'converts a private key into a OpenSSL::PKey::EC' do + pkey = crypto_suite.pkey_from_private_key(private_key) + expect(pkey).to be_a(OpenSSL::PKey::EC) + expect(pkey.private_key).to eql(OpenSSL::BN.new(private_key, 16)) + end + end + + describe '#pem_from_private_key' do + let(:expected_pem) do + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEINYudqtKkH12NK2g2XCbTtK/x8UcQhEnt/yTwBQeRheXoAoGCCqGSM49\n" \ + "AwEHoUQDQgAEoB8B+pQtIjOmSuvgs2wW69/RxFOsUpdZHyDiv6uoaeF+FfX3Nn7m\n" \ + "8WEhxkysPs3VF5IKNvUUXcKoga6TcYc6xg==\n" \ + "-----END EC PRIVATE KEY-----\n" + end + it 'returns a pem from private key' do + pem = crypto_suite.pem_from_private_key(private_key) + expect(pem).to eql(expected_pem) + end + end + + describe '#public_key_from_x509_certificate' do + let(:random_certificate) do + "-----BEGIN CERTIFICATE-----\n" \ + "MIIBHjCBxaADAgECAgEBMAoGCCqGSM49BAMCMBcxFTATBgNVBAoTDERvY2tlciwg\n" \ + "SW5jLjAeFw0xMzA3MjUwMTEwMjRaFw0xNTA3MjUwMTEwMjRaMBcxFTATBgNVBAoT\n" \ + "DERvY2tlciwgSW5jLjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABMolCWAO0iP7\n" \ + "tkX/KLjQ9CKeOoHYynBgfFcd1ZGoxcefmIbWjHx29eWI3xlhbjS6ssSxhrw1Kuh5\n" \ + "RrASfUCHD7SjAjAAMAoGCCqGSM49BAMCA0gAMEUCIQDRLQTSSeqjsxsb+q4exLSt\n" \ + "EM7f7/ymBzoUzbXU7wI9AgIgXCWaI++GkopGT8T2qV/3+NL0U+fYM0ZjSNSiwaK3\n" \ + "+kA=\n" \ + "-----END CERTIFICATE-----" + end + let(:random_certificate_public_key) { '04ca2509600ed223fbb645ff28b8d0f4229e3a81d8ca70607c571dd591a8c5c79f9886d68c7c76f5e588df19616e34bab2c4b186bc352ae87946b0127d40870fb4' } + + it 'returns public_key from x509 certificate' do + public_key=crypto_suite.public_key_from_x509_certificate(random_certificate) + expect(public_key).to eql(random_certificate_public_key) + end + end + + describe '#pkey_from_public_key' do let(:random_public_key) do '04293ed1ea547c079f06f7bc6aa8adec39fd465ba839323a262fc7abab7714ba6' \ @@ -10,11 +231,11 @@ end it 'returns an OpenSSL::PKey::EC object' do - expect(crypto_suite.openssl_pkey_from_public_key(random_public_key)).to be_a(OpenSSL::PKey::EC) + expect(crypto_suite.pkey_from_public_key(random_public_key)).to be_a(OpenSSL::PKey::EC) end it 'generates a matching public key' do - expect(crypto_suite.openssl_pkey_from_public_key(random_public_key).public_key.to_bn.to_s(16).downcase) + expect(crypto_suite.pkey_from_public_key(random_public_key).public_key.to_bn.to_s(16).downcase) .to eql(random_public_key) end end From dfe9fb0dca8f31dc067e94892a1d093779bca016 Mon Sep 17 00:00:00 2001 From: Jonathan Chan Date: Sat, 21 Oct 2023 17:33:17 -0500 Subject: [PATCH 02/20] updating dependencies --- .github/workflows/rspec.yml | 8 +++++--- fabric-gateway.gemspec | 17 ++++++++--------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index aa89e72..6018393 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -22,7 +22,7 @@ jobs: ruby-version: ['2.6', '2.7', '3.0'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Ruby # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, # change this to (see https://github.com/ruby/setup-ruby#versioning): @@ -33,5 +33,7 @@ jobs: bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Run tests run: bundle exec rake - env: - CODECOV_TOKEN: 4b75ccca-3149-4610-99fb-3a5aeff4ff00 + - uses: codecov/codecov-action@v3 + with: + token: 4b75ccca-3149-4610-99fb-3a5aeff4ff00 + diff --git a/fabric-gateway.gemspec b/fabric-gateway.gemspec index 57b6aae..1867a83 100644 --- a/fabric-gateway.gemspec +++ b/fabric-gateway.gemspec @@ -28,17 +28,16 @@ Gem::Specification.new do |spec| spec.bindir = 'exe' spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ['lib'] - spec.add_dependency('google-protobuf', '>= 3.19.1') + spec.add_dependency('google-protobuf', '~> 3.24', '>= 3.24.4') spec.add_dependency('grpc', '~> 1.42') - spec.add_development_dependency('codecov', '~> 0.6.0') - spec.add_development_dependency('factory_bot', '~> 6.2.0') - spec.add_development_dependency('grpc-tools', '~> 1.46.2') + spec.add_development_dependency('factory_bot', '~> 6.3.0') + spec.add_development_dependency('grpc-tools', '~> 1.59.0') spec.add_development_dependency('rake-notes', '~> 0.2.0') - spec.add_development_dependency('rubocop', '~> 1.23.0') - spec.add_development_dependency('rubocop-rspec', '~> 2.6.0') - spec.add_development_dependency('simplecov', '~> 0.21.2') - spec.add_development_dependency('timecop', '~> 0.9.4') - spec.add_development_dependency('yard', '~> 0.9.27') + spec.add_development_dependency('rubocop', '~> 1.57', '>= 1.57.1') + spec.add_development_dependency('rubocop-rspec', '~> 2.24.1') + spec.add_development_dependency('simplecov', '~> 0.22.0') + spec.add_development_dependency('timecop', '~> 0.9.8') + spec.add_development_dependency('yard', '~> 0.9.34') spec.metadata = { 'rubygems_mfa_required' => 'true' } From 49e2768dbcb2f999d505a5f225d18f5deac5e858 Mon Sep 17 00:00:00 2001 From: Jonathan Chan Date: Sat, 21 Oct 2023 17:35:17 -0500 Subject: [PATCH 03/20] fixing rubocop errors --- lib/fabric/ec_crypto_suite.rb | 4 +- spec/fabric/ec_crypto_suite_spec.rb | 39 +++++++++++-------- .../entities/chaincode_events_request_spec.rb | 2 +- spec/fabric/entities/proposal_spec.rb | 6 +-- .../entities/proposed_transaction_spec.rb | 2 +- spec/fabric/fabric_spec.rb | 8 ++-- spec/spec_helper.rb | 4 +- .../accessor_shared_contexts.rb | 10 ++--- spec/support/shared_context/client_mocks.rb | 16 ++++---- 9 files changed, 48 insertions(+), 43 deletions(-) diff --git a/lib/fabric/ec_crypto_suite.rb b/lib/fabric/ec_crypto_suite.rb index 89febf7..e68e994 100644 --- a/lib/fabric/ec_crypto_suite.rb +++ b/lib/fabric/ec_crypto_suite.rb @@ -157,7 +157,7 @@ def pkey_from_private_key(private_key) end def pem_from_private_key(private_key) - pkey=pkey_from_private_key(private_key) + pkey = pkey_from_private_key(private_key) pkey.to_pem end @@ -181,8 +181,6 @@ def pkey_from_public_key(public_key) private - - # barely understand this code - this link provides a good explanation: # http://coders-errand.com/malleability-ecdsa-signatures/ def prevent_malleability(sequence, order) diff --git a/spec/fabric/ec_crypto_suite_spec.rb b/spec/fabric/ec_crypto_suite_spec.rb index e972394..2456393 100644 --- a/spec/fabric/ec_crypto_suite_spec.rb +++ b/spec/fabric/ec_crypto_suite_spec.rb @@ -1,11 +1,14 @@ # frozen_string_literal: true RSpec.describe Fabric::ECCryptoSuite do + subject(:crypto_suite) { described_class.new } + let(:private_key) { 'd62e76ab4a907d7634ada0d9709b4ed2bfc7c51c421127b7fc93c0141e461797' } - let(:public_key) { '04a01f01fa942d2233a64aebe0b36c16ebdfd1c453ac5297591f20e2bfaba869e17e15f5f7367ee6f16121c64cac3ecdd517920a36f5145dc2a881ae9371873ac6' } + let(:public_key) do + '04a01f01fa942d2233a64aebe0b36c16ebdfd1c453ac5297591f20e2bfaba869e17e15f5f7367ee6f16121c64cac3ecdd517920a36f5145dc2a881ae9371873ac6' + end let(:random_secret) { 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' } - subject(:crypto_suite) { described_class.new } describe '#initialize' do context 'when no parameters are passed' do it 'utilizes default options' do @@ -16,16 +19,17 @@ end end - context 'when options are passed' do + context 'when options are passed' do subject(:crypto_suite) do described_class.new( - { + { key_size: 384, digest_algorithm: 'SHA224', cipher: 'aes-128-cbc' } ) end + it 'utilizes options passed' do expect(crypto_suite.key_size).to eql(384) expect(crypto_suite.digest_algorithm).to eql('SHA224') @@ -52,14 +56,14 @@ context 'when the signature matches the key and the message' do it 'returns true' do signature = crypto_suite.sign(private_key, 'this is a test') - expect(crypto_suite.verify(public_key, 'this is a test', signature)).to eql(true) + expect(crypto_suite.verify(public_key, 'this is a test', signature)).to eql(true) end end context 'when the signature does not match the key and the message' do it 'return false' do signature = crypto_suite.sign(private_key, 'this is a test') - expect(crypto_suite.verify(public_key, 'this is not a test', signature)).to eql(false) + expect(crypto_suite.verify(public_key, 'this is not a test', signature)).to eql(false) end end end @@ -70,7 +74,6 @@ expect(private_key).to be_a(String) public_key = crypto_suite.restore_public_key private_key - group = OpenSSL::PKey::EC::Group.new(crypto_suite.curve) @@ -88,7 +91,7 @@ ) pkey = OpenSSL::PKey::EC.new(asn1.to_der) - + expect(pkey.private?).to eql(true) expect(pkey.private_key.to_s(16).downcase).to eql(private_key) end @@ -155,15 +158,16 @@ end end - # TODO - it might be useful to test that the shared key can be properly utilized as well + # TODO: - it might be useful to test that the shared key can be properly utilized as well describe '#build_shared_key' do let(:random_public_key) do '04293ed1ea547c079f06f7bc6aa8adec39fd465ba839323a262fc7abab7714ba6' \ 'e680305dcfdf97043bfb1817a932cd7f4883d255b03ef303cf6651d765b9b3418' end + it 'returns a shared key' do shared_key = crypto_suite.build_shared_key(private_key, random_public_key) - expect(shared_key).to eql('f1388005817ef6c5f0e8d4f655b000c083a67926c991eaea3da4adf1fc20ceb5') + expect(shared_key).to eql('f1388005817ef6c5f0e8d4f655b000c083a67926c991eaea3da4adf1fc20ceb5') end end @@ -177,7 +181,8 @@ describe '#encrypt/#decrypt' do it 'properly able to encrypte and decrypt strings' do - expect(crypto_suite.decrypt(random_secret, crypto_suite.encrypt(random_secret, 'this is a test'))).to eql('this is a test') + expect(crypto_suite.decrypt(random_secret, + crypto_suite.encrypt(random_secret, 'this is a test'))).to eql('this is a test') end end @@ -197,6 +202,7 @@ "8WEhxkysPs3VF5IKNvUUXcKoga6TcYc6xg==\n" \ "-----END EC PRIVATE KEY-----\n" end + it 'returns a pem from private key' do pem = crypto_suite.pem_from_private_key(private_key) expect(pem).to eql(expected_pem) @@ -213,18 +219,19 @@ "RrASfUCHD7SjAjAAMAoGCCqGSM49BAMCA0gAMEUCIQDRLQTSSeqjsxsb+q4exLSt\n" \ "EM7f7/ymBzoUzbXU7wI9AgIgXCWaI++GkopGT8T2qV/3+NL0U+fYM0ZjSNSiwaK3\n" \ "+kA=\n" \ - "-----END CERTIFICATE-----" + '-----END CERTIFICATE-----' + end + let(:random_certificate_public_key) do + '04ca2509600ed223fbb645ff28b8d0f4229e3a81d8ca70607c571dd591a8c5c79f9886d68c7c76f5e588df19616e34bab2c4b186bc352ae87946b0127d40870fb4' end - let(:random_certificate_public_key) { '04ca2509600ed223fbb645ff28b8d0f4229e3a81d8ca70607c571dd591a8c5c79f9886d68c7c76f5e588df19616e34bab2c4b186bc352ae87946b0127d40870fb4' } - it 'returns public_key from x509 certificate' do - public_key=crypto_suite.public_key_from_x509_certificate(random_certificate) + it 'returns public_key from x509 certificate' do + public_key = crypto_suite.public_key_from_x509_certificate(random_certificate) expect(public_key).to eql(random_certificate_public_key) end end describe '#pkey_from_public_key' do - let(:random_public_key) do '04293ed1ea547c079f06f7bc6aa8adec39fd465ba839323a262fc7abab7714ba6' \ 'e680305dcfdf97043bfb1817a932cd7f4883d255b03ef303cf6651d765b9b3418' diff --git a/spec/fabric/entities/chaincode_events_request_spec.rb b/spec/fabric/entities/chaincode_events_request_spec.rb index d9fd0ed..1a4dcb9 100644 --- a/spec/fabric/entities/chaincode_events_request_spec.rb +++ b/spec/fabric/entities/chaincode_events_request_spec.rb @@ -269,7 +269,7 @@ expect(contract.client).to have_received(:chaincode_events) .with(chaincode_events_request.signed_request, {}) do |&block| - expect(block).to be(nil) + expect(block).to be_nil end end end diff --git a/spec/fabric/entities/proposal_spec.rb b/spec/fabric/entities/proposal_spec.rb index 07b421d..24a41b9 100644 --- a/spec/fabric/entities/proposal_spec.rb +++ b/spec/fabric/entities/proposal_spec.rb @@ -326,7 +326,7 @@ let!(:response) { proposal.new_evaluate_request } it 'returns an EvaluateRequest' do - expect(response).to be_a(::Gateway::EvaluateRequest) + expect(response).to be_a(Gateway::EvaluateRequest) end it 'sets the channel_id' do @@ -346,7 +346,7 @@ let!(:response) { proposal.new_endorse_request } it 'returns an EndorseRequest' do - expect(response).to be_a(::Gateway::EndorseRequest) + expect(response).to be_a(Gateway::EndorseRequest) end it 'sets the channel_id' do @@ -367,7 +367,7 @@ let!(:response) { proposal.new_prepared_transaction(expected_envelope) } it 'returns a PreparedTransaction' do - expect(response).to be_a(::Gateway::PreparedTransaction) + expect(response).to be_a(Gateway::PreparedTransaction) end it 'sets the transaction_id' do diff --git a/spec/fabric/entities/proposed_transaction_spec.rb b/spec/fabric/entities/proposed_transaction_spec.rb index 60c2717..9675250 100644 --- a/spec/fabric/entities/proposed_transaction_spec.rb +++ b/spec/fabric/entities/proposed_transaction_spec.rb @@ -46,7 +46,7 @@ end it 'produces a proposed_transaction' do - expect(proposed_transaction.proposed_transaction).to be_a(::Gateway::ProposedTransaction) + expect(proposed_transaction.proposed_transaction).to be_a(Gateway::ProposedTransaction) end it 'produces a valid proposed_transaction' do diff --git a/spec/fabric/fabric_spec.rb b/spec/fabric/fabric_spec.rb index 5d7acea..fb6c0c9 100644 --- a/spec/fabric/fabric_spec.rb +++ b/spec/fabric/fabric_spec.rb @@ -2,10 +2,10 @@ RSpec.describe Fabric do it 'has a version number' do - expect(Fabric::VERSION).not_to be nil + expect(Fabric::VERSION).not_to be_nil end - it { expect(Fabric::Error.new).to be_a_kind_of(StandardError) } - it { expect(Fabric::InvalidArgument.new).to be_a_kind_of(Fabric::Error) } - it { expect(Fabric::NotYetImplemented.new).to be_a_kind_of(Fabric::Error) } + it { expect(Fabric::Error.new).to be_a(StandardError) } + it { expect(Fabric::InvalidArgument.new).to be_a(Fabric::Error) } + it { expect(Fabric::NotYetImplemented.new).to be_a(Fabric::Error) } end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index dde51a5..d3ae375 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -8,8 +8,8 @@ require 'simplecov' SimpleCov.start - require 'codecov' - SimpleCov.formatter = SimpleCov::Formatter::Codecov + require 'simplecov-cobertura' + SimpleCov.formatter = SimpleCov::Formatter::CoberturaFormatter end require 'fabric' diff --git a/spec/support/shared_context/accessor_shared_contexts.rb b/spec/support/shared_context/accessor_shared_contexts.rb index 8044676..90bd9b9 100644 --- a/spec/support/shared_context/accessor_shared_contexts.rb +++ b/spec/support/shared_context/accessor_shared_contexts.rb @@ -3,13 +3,13 @@ RSpec.shared_examples 'a gateway accessor' do describe '#gateway' do it 'returns the gateway' do - expect(subject.gateway).to be_a(::Fabric::Gateway) + expect(subject.gateway).to be_a(Fabric::Gateway) end end describe '#client' do it 'returns a Fabric::Client' do - expect(subject.client).to be_a(::Fabric::Client) + expect(subject.client).to be_a(Fabric::Client) end it 'returns the client from gateway' do @@ -19,7 +19,7 @@ describe '#signer' do it 'returns a Fabric::Identity' do - expect(subject.signer).to be_a(::Fabric::Identity) + expect(subject.signer).to be_a(Fabric::Identity) end it 'returns the signer from the gateway' do @@ -39,7 +39,7 @@ describe '#gateway' do it 'returns the gateway' do - expect(subject.gateway).to be_a(::Fabric::Gateway) + expect(subject.gateway).to be_a(Fabric::Gateway) end it 'returns the gateway from the network' do @@ -53,7 +53,7 @@ describe '#network' do it 'returns the network' do - expect(subject.network).to be_a(::Fabric::Network) + expect(subject.network).to be_a(Fabric::Network) end it 'returns the network from the contract' do diff --git a/spec/support/shared_context/client_mocks.rb b/spec/support/shared_context/client_mocks.rb index ca9e7cc..7b379ab 100644 --- a/spec/support/shared_context/client_mocks.rb +++ b/spec/support/shared_context/client_mocks.rb @@ -14,15 +14,15 @@ def sent_chaincode_input_args attr_reader :sent_call_options def decode_proposal(request) - @sent_proposal = ::Protos::Proposal.decode(request) - @sent_chaincode_proposal_payload = ::Protos::ChaincodeProposalPayload.decode(sent_proposal.payload) - @sent_chaincode_proposal_input = ::Protos::ChaincodeInvocationSpec.decode(sent_chaincode_proposal_payload.input) + @sent_proposal = Protos::Proposal.decode(request) + @sent_chaincode_proposal_payload = Protos::ChaincodeProposalPayload.decode(sent_proposal.payload) + @sent_chaincode_proposal_input = Protos::ChaincodeInvocationSpec.decode(sent_chaincode_proposal_payload.input) end def mock_evaluate_response(return_payload) - mock_protos_response = object_double(::Protos::Response.new) + mock_protos_response = object_double(Protos::Response.new) allow(mock_protos_response).to receive(:payload).and_return(return_payload) - mock_evaluate_response = object_double(::Gateway::EvaluateResponse.new) + mock_evaluate_response = object_double(Gateway::EvaluateResponse.new) allow(mock_evaluate_response).to receive(:result).and_return(mock_protos_response) mock_evaluate_response @@ -33,7 +33,7 @@ def mock_evaluate_response(return_payload) # end def mock_endorse_response(return_payload) - mock_endorse_response = object_double(::Gateway::EndorseResponse.new) + mock_endorse_response = object_double(Gateway::EndorseResponse.new) allow(mock_endorse_response).to receive(:prepared_transaction).and_return(return_payload) mock_endorse_response @@ -41,7 +41,7 @@ def mock_endorse_response(return_payload) def setup_evaluate_mock(client, return_payload) allow(client).to receive(:evaluate) do |arg, arg2| - expect(arg).to be_a(::Gateway::EvaluateRequest) + expect(arg).to be_a(Gateway::EvaluateRequest) decode_proposal(arg.proposed_transaction.proposal_bytes) @sent_evaluate_request = arg @sent_call_options = arg2 @@ -58,7 +58,7 @@ def setup_evaluate_mock(client, return_payload) def setup_endorse_mock(client, return_payload) allow(client).to receive(:endorse) do |arg, arg2| - expect(arg).to be_a(::Gateway::EndorseRequest) + expect(arg).to be_a(Gateway::EndorseRequest) @sent_endorse_request = arg @sent_call_options = arg2 end.and_return(mock_endorse_response(return_payload)) From ad00b011e9cee732064af2d27410a2f8f7d37a86 Mon Sep 17 00:00:00 2001 From: Jonathan Chan Date: Sat, 21 Oct 2023 17:37:26 -0500 Subject: [PATCH 04/20] fixing failing test runner --- fabric-gateway.gemspec | 1 + 1 file changed, 1 insertion(+) diff --git a/fabric-gateway.gemspec b/fabric-gateway.gemspec index 1867a83..eb7e8ff 100644 --- a/fabric-gateway.gemspec +++ b/fabric-gateway.gemspec @@ -36,6 +36,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency('rubocop', '~> 1.57', '>= 1.57.1') spec.add_development_dependency('rubocop-rspec', '~> 2.24.1') spec.add_development_dependency('simplecov', '~> 0.22.0') + spec.add_development_dependency('simplecov-cobertura', '~> 2.1') spec.add_development_dependency('timecop', '~> 0.9.8') spec.add_development_dependency('yard', '~> 0.9.34') spec.metadata = { From 1e125f7359fb06d111b77c38f8b25b431f8d30d9 Mon Sep 17 00:00:00 2001 From: Jonathan Chan Date: Sat, 21 Oct 2023 17:45:00 -0500 Subject: [PATCH 05/20] fixing more correctable rubocop errors --- lib/fabric/entities/status.rb | 4 ++-- spec/fabric/contract_spec.rb | 8 ++++---- spec/fabric/ec_crypto_suite_spec.rb | 16 ++++++++-------- spec/fabric/network_spec.rb | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/fabric/entities/status.rb b/lib/fabric/entities/status.rb index 2069d9c..6b73a40 100644 --- a/lib/fabric/entities/status.rb +++ b/lib/fabric/entities/status.rb @@ -6,9 +6,9 @@ module Fabric # class Status TRANSACTION_STATUSES = ::Protos::TxValidationCode.constants.map(&::Protos::TxValidationCode.method(:const_get)) - .collect do |i| + .to_h do |i| [::Protos::TxValidationCode.lookup(i), i] - end.to_h + end # @return [Integer] Block number in which the transaction committed. attr_reader :block_number diff --git a/spec/fabric/contract_spec.rb b/spec/fabric/contract_spec.rb index 53a570e..766b021 100644 --- a/spec/fabric/contract_spec.rb +++ b/spec/fabric/contract_spec.rb @@ -176,8 +176,8 @@ end describe '#submit' do - let(:proposal_double) { instance_double('Proposal') } - let(:transaction_double) { instance_double('Transaction') } + let(:proposal_double) { instance_double(Proposal) } + let(:transaction_double) { instance_double(Transaction) } before do allow(transaction_double).to receive(:result).and_return('mocked result') @@ -229,8 +229,8 @@ end describe '#submit_transaction' do - let(:proposal_double) { instance_double('Proposal') } - let(:transaction_double) { instance_double('Transaction') } + let(:proposal_double) { instance_double(Proposal) } + let(:transaction_double) { instance_double(Transaction) } before do allow(transaction_double).to receive(:result).and_return('mocked result') diff --git a/spec/fabric/ec_crypto_suite_spec.rb b/spec/fabric/ec_crypto_suite_spec.rb index 2456393..07dc9a3 100644 --- a/spec/fabric/ec_crypto_suite_spec.rb +++ b/spec/fabric/ec_crypto_suite_spec.rb @@ -12,7 +12,7 @@ describe '#initialize' do context 'when no parameters are passed' do it 'utilizes default options' do - expect(crypto_suite.key_size).to eql(256) + expect(crypto_suite.key_size).to be(256) expect(crypto_suite.digest_algorithm).to eql('SHA256') expect(crypto_suite.curve).to eql('prime256v1') expect(crypto_suite.cipher).to eql('aes-256-cbc') @@ -31,7 +31,7 @@ end it 'utilizes options passed' do - expect(crypto_suite.key_size).to eql(384) + expect(crypto_suite.key_size).to be(384) expect(crypto_suite.digest_algorithm).to eql('SHA224') expect(crypto_suite.curve).to eql('secp384r1') expect(crypto_suite.cipher).to eql('aes-128-cbc') @@ -42,7 +42,7 @@ describe '#sign' do it 'creates a valid signature' do signature = crypto_suite.sign(private_key, 'this is a test') - expect(crypto_suite.verify(public_key, 'this is a test', signature)).to eql(true) + expect(crypto_suite.verify(public_key, 'this is a test', signature)).to be(true) end end @@ -56,14 +56,14 @@ context 'when the signature matches the key and the message' do it 'returns true' do signature = crypto_suite.sign(private_key, 'this is a test') - expect(crypto_suite.verify(public_key, 'this is a test', signature)).to eql(true) + expect(crypto_suite.verify(public_key, 'this is a test', signature)).to be(true) end end context 'when the signature does not match the key and the message' do it 'return false' do signature = crypto_suite.sign(private_key, 'this is a test') - expect(crypto_suite.verify(public_key, 'this is not a test', signature)).to eql(false) + expect(crypto_suite.verify(public_key, 'this is not a test', signature)).to be(false) end end end @@ -92,7 +92,7 @@ pkey = OpenSSL::PKey::EC.new(asn1.to_der) - expect(pkey.private?).to eql(true) + expect(pkey.private?).to be(true) expect(pkey.private_key.to_s(16).downcase).to eql(private_key) end end @@ -109,13 +109,13 @@ describe '#generate_nonce' do context 'when no parameters pass' do it 'generates a random 24 byte string' do - expect(crypto_suite.generate_nonce.length).to eql(24) + expect(crypto_suite.generate_nonce.length).to be(24) end end context 'when passing byte length' do it 'generates a random byte string the size passed in' do - expect(crypto_suite.generate_nonce(50).length).to eql(50) + expect(crypto_suite.generate_nonce(50).length).to be(50) end end end diff --git a/spec/fabric/network_spec.rb b/spec/fabric/network_spec.rb index 8970283..3851a6c 100644 --- a/spec/fabric/network_spec.rb +++ b/spec/fabric/network_spec.rb @@ -68,7 +68,7 @@ describe '#new_chaincode_events' do let(:contract) { build(:contract) } - let(:spied_chaincode_events_request) { instance_double('ChaincodeEventsRequest') } + let(:spied_chaincode_events_request) { instance_double(ChaincodeEventsRequest) } before do allow(Fabric::ChaincodeEventsRequest).to receive(:new).and_return(spied_chaincode_events_request) From 3c5d469ab8572793c2e6015ba561a44d4d404d88 Mon Sep 17 00:00:00 2001 From: Jonathan Chan Date: Sat, 21 Oct 2023 17:45:26 -0500 Subject: [PATCH 06/20] updating rspec to run on modern versions of ruby --- .github/workflows/rspec.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index 6018393..c2a6e0f 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - ruby-version: ['2.6', '2.7', '3.0'] + ruby-version: ['2.7', '3.0', '3.1', '3.2'] steps: - uses: actions/checkout@v4 From 19d361ebfbd3194fe74f052f913694cabff89e7a Mon Sep 17 00:00:00 2001 From: Jonathan Chan Date: Sat, 21 Oct 2023 17:48:22 -0500 Subject: [PATCH 07/20] move development dependencies from gemspec to Gemfile --- Gemfile | 10 ++++++++++ fabric-gateway.gemspec | 9 --------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Gemfile b/Gemfile index 013b495..bdd10a6 100644 --- a/Gemfile +++ b/Gemfile @@ -7,3 +7,13 @@ gemspec gem 'rake', '~> 12.3.3' gem 'rspec', '~> 3.0' + +gem 'factory_bot', '~> 6.3.0' +gem 'grpc-tools', '~> 1.59.0' +gem 'rake-notes', '~> 0.2.0' +gem 'rubocop', '~> 1.57', '>= 1.57.1' +gem 'rubocop-rspec', '~> 2.24.1' +gem 'simplecov', '~> 0.22.0' +gem 'simplecov-cobertura', '~> 2.1' +gem 'timecop', '~> 0.9.8' +gem 'yard', '~> 0.9.34' diff --git a/fabric-gateway.gemspec b/fabric-gateway.gemspec index eb7e8ff..5e8aa7a 100644 --- a/fabric-gateway.gemspec +++ b/fabric-gateway.gemspec @@ -30,15 +30,6 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] spec.add_dependency('google-protobuf', '~> 3.24', '>= 3.24.4') spec.add_dependency('grpc', '~> 1.42') - spec.add_development_dependency('factory_bot', '~> 6.3.0') - spec.add_development_dependency('grpc-tools', '~> 1.59.0') - spec.add_development_dependency('rake-notes', '~> 0.2.0') - spec.add_development_dependency('rubocop', '~> 1.57', '>= 1.57.1') - spec.add_development_dependency('rubocop-rspec', '~> 2.24.1') - spec.add_development_dependency('simplecov', '~> 0.22.0') - spec.add_development_dependency('simplecov-cobertura', '~> 2.1') - spec.add_development_dependency('timecop', '~> 0.9.8') - spec.add_development_dependency('yard', '~> 0.9.34') spec.metadata = { 'rubygems_mfa_required' => 'true' } From e03dead57b2dade308b0b692dd9993f822460371 Mon Sep 17 00:00:00 2001 From: Jonathan Chan Date: Sat, 21 Oct 2023 18:02:52 -0500 Subject: [PATCH 08/20] updating workflows --- .github/workflows/rspec.yml | 2 +- .github/workflows/yardoc.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index c2a6e0f..24deda8 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -27,7 +27,7 @@ jobs: # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby, # change this to (see https://github.com/ruby/setup-ruby#versioning): # uses: ruby/setup-ruby@v1 - uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e + uses: ruby/setup-ruby@master with: ruby-version: ${{ matrix.ruby-version }} bundler-cache: true # runs 'bundle install' and caches installed gems automatically diff --git a/.github/workflows/yardoc.yml b/.github/workflows/yardoc.yml index f5351a4..71942f6 100644 --- a/.github/workflows/yardoc.yml +++ b/.github/workflows/yardoc.yml @@ -11,7 +11,7 @@ jobs: - name: Set up Ruby 2.7 uses: ruby/setup-ruby@v1 with: - ruby-version: 2.7 + ruby-version: 3.2 - name: Cache gems uses: actions/cache@v1 with: From 2c9170fc982ef21dd45a05f7d6aada4ff8f3696f Mon Sep 17 00:00:00 2001 From: Jonathan Chan Date: Sat, 21 Oct 2023 18:09:41 -0500 Subject: [PATCH 09/20] Fixing github actions to properly run --- .github/workflows/yardoc.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/yardoc.yml b/.github/workflows/yardoc.yml index 71942f6..f94c080 100644 --- a/.github/workflows/yardoc.yml +++ b/.github/workflows/yardoc.yml @@ -22,7 +22,6 @@ jobs: - name: Install gems run: | bundle config path vendor/bundle - bundle config set without 'default doc job cable storage ujs test db' bundle install --jobs 4 --retry 3 - name: Run yard stats run: bundle exec yard stats --list-undoc --no-cache --fail-on-warning From 3cb3d80beeb1ab97b93056242efa7f45b880e490 Mon Sep 17 00:00:00 2001 From: Jonathan Chan Date: Sat, 21 Oct 2023 19:48:11 -0500 Subject: [PATCH 10/20] more fixes for openssl3.0 compatibility --- lib/fabric/ec_crypto_suite.rb | 21 ++++++++++++++++----- spec/fabric/contract_spec.rb | 8 ++++---- spec/fabric/ec_crypto_suite_spec.rb | 15 +++++++++++++++ spec/fabric/network_spec.rb | 2 +- 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/lib/fabric/ec_crypto_suite.rb b/lib/fabric/ec_crypto_suite.rb index e68e994..6e3e24e 100644 --- a/lib/fabric/ec_crypto_suite.rb +++ b/lib/fabric/ec_crypto_suite.rb @@ -48,8 +48,7 @@ def verify(public_key, message, signature) end def generate_private_key - key = OpenSSL::PKey::EC.new curve - key.generate_key! + key = OpenSSL::PKey::EC.generate(curve) key.private_key.to_s(16).downcase end @@ -173,10 +172,22 @@ def public_key_from_x509_certificate(certificate) end def 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)) + group = OpenSSL::PKey::EC::Group.new(curve) - pkey + public_key_bn = OpenSSL::BN.new(public_key, 16) + public_key_point = OpenSSL::PKey::EC::Point.new(group, public_key_bn) + + asn1 = OpenSSL::ASN1::Sequence.new( + [ + OpenSSL::ASN1::Sequence.new([ + OpenSSL::ASN1::ObjectId.new('id-ecPublicKey'), + OpenSSL::ASN1::ObjectId.new(group.curve_name) + ]), + OpenSSL::ASN1::BitString.new(public_key_point.to_octet_string(:uncompressed)) + ] + ) + + OpenSSL::PKey::EC.new(asn1.to_der) end private diff --git a/spec/fabric/contract_spec.rb b/spec/fabric/contract_spec.rb index 766b021..7155001 100644 --- a/spec/fabric/contract_spec.rb +++ b/spec/fabric/contract_spec.rb @@ -176,8 +176,8 @@ end describe '#submit' do - let(:proposal_double) { instance_double(Proposal) } - let(:transaction_double) { instance_double(Transaction) } + let(:proposal_double) { instance_double(Fabric::Proposal) } + let(:transaction_double) { instance_double(Fabric::Transaction) } before do allow(transaction_double).to receive(:result).and_return('mocked result') @@ -229,8 +229,8 @@ end describe '#submit_transaction' do - let(:proposal_double) { instance_double(Proposal) } - let(:transaction_double) { instance_double(Transaction) } + let(:proposal_double) { instance_double(Fabric::Proposal) } + let(:transaction_double) { instance_double(Fabric::Transaction) } before do allow(transaction_double).to receive(:result).and_return('mocked result') diff --git a/spec/fabric/ec_crypto_suite_spec.rb b/spec/fabric/ec_crypto_suite_spec.rb index 07dc9a3..34ee608 100644 --- a/spec/fabric/ec_crypto_suite_spec.rb +++ b/spec/fabric/ec_crypto_suite_spec.rb @@ -245,5 +245,20 @@ expect(crypto_suite.pkey_from_public_key(random_public_key).public_key.to_bn.to_s(16).downcase) .to eql(random_public_key) end + + it 'should be a public key' do + expect(crypto_suite.pkey_from_public_key(random_public_key).public?).to be(true) + expect(crypto_suite.pkey_from_public_key(random_public_key).private?).to be(false) + end + + it 'creates a matching public key' do + private_pkey = OpenSSL::PKey::EC.generate(crypto_suite.curve) + public_pem = private_pkey.public_to_pem + + public_key = crypto_suite.restore_public_key(private_pkey.private_key.to_s(16).downcase) + public_pkey = crypto_suite.pkey_from_public_key(public_key) + + expect(public_pem).to eql(public_pkey.to_pem) + end end end diff --git a/spec/fabric/network_spec.rb b/spec/fabric/network_spec.rb index 3851a6c..7a6f466 100644 --- a/spec/fabric/network_spec.rb +++ b/spec/fabric/network_spec.rb @@ -68,7 +68,7 @@ describe '#new_chaincode_events' do let(:contract) { build(:contract) } - let(:spied_chaincode_events_request) { instance_double(ChaincodeEventsRequest) } + let(:spied_chaincode_events_request) { instance_double(Fabric::ChaincodeEventsRequest) } before do allow(Fabric::ChaincodeEventsRequest).to receive(:new).and_return(spied_chaincode_events_request) From 61676762915f05d7b8cf9838f1a3a4718d169ebe Mon Sep 17 00:00:00 2001 From: Jonathan Chan Date: Sat, 21 Oct 2023 20:00:20 -0500 Subject: [PATCH 11/20] skipping test on ruby 2.7 --- spec/fabric/ec_crypto_suite_spec.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/fabric/ec_crypto_suite_spec.rb b/spec/fabric/ec_crypto_suite_spec.rb index 34ee608..78c8d3e 100644 --- a/spec/fabric/ec_crypto_suite_spec.rb +++ b/spec/fabric/ec_crypto_suite_spec.rb @@ -252,6 +252,7 @@ end it 'creates a matching public key' do + skip('Test unsupported on ruby 2.x') if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.0.0') private_pkey = OpenSSL::PKey::EC.generate(crypto_suite.curve) public_pem = private_pkey.public_to_pem From b18288346e4388755274fc8e3ad3c36c6dccd02c Mon Sep 17 00:00:00 2001 From: Jonathan Chan Date: Sat, 21 Oct 2023 20:12:05 -0500 Subject: [PATCH 12/20] Fixing rubocop errors and getting it to work --- .github/workflows/rubocop.yml | 1 - .rubocop.yml | 4 +++- lib/fabric/ec_crypto_suite.rb | 12 +++++++++--- spec/fabric/ec_crypto_suite_spec.rb | 8 +++++--- spec/factories.rb | 10 +++++----- 5 files changed, 22 insertions(+), 13 deletions(-) diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml index d201e1d..fc04373 100644 --- a/.github/workflows/rubocop.yml +++ b/.github/workflows/rubocop.yml @@ -22,7 +22,6 @@ jobs: - name: Install gems run: | bundle config path vendor/bundle - bundle config set without 'default doc job cable storage ujs test db' bundle install --jobs 4 --retry 3 - name: Run RuboCop run: bundle exec rubocop --parallel diff --git a/.rubocop.yml b/.rubocop.yml index ffcd4cb..dc5ff0e 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -20,4 +20,6 @@ AllCops: - 'lib/orderer/*' - 'lib/peer/*' - +RSpec/SpecFilePathFormat: + Exclude: + - spec/fabric/entities/* diff --git a/lib/fabric/ec_crypto_suite.rb b/lib/fabric/ec_crypto_suite.rb index 6e3e24e..6957ae1 100644 --- a/lib/fabric/ec_crypto_suite.rb +++ b/lib/fabric/ec_crypto_suite.rb @@ -134,6 +134,8 @@ def decrypt(secret, data) # when https://github.com/ruby/openssl/pull/555 gets merged, consider refactoring # the code here + # rubocop:disable Metrics/AbcSize + # rubocop:disable Metrics/MethodLength def pkey_from_private_key(private_key) public_key = restore_public_key private_key @@ -154,6 +156,8 @@ def pkey_from_private_key(private_key) OpenSSL::PKey::EC.new(asn1.to_der) end + # rubocop:enable Metrics/AbcSize + # rubocop:enable Metrics/MethodLength def pem_from_private_key(private_key) pkey = pkey_from_private_key(private_key) @@ -171,6 +175,7 @@ def public_key_from_x509_certificate(certificate) cert.public_key.public_key.to_bn.to_s(16).downcase end + # rubocop:disable Metrics/MethodLength def pkey_from_public_key(public_key) group = OpenSSL::PKey::EC::Group.new(curve) @@ -180,15 +185,16 @@ def pkey_from_public_key(public_key) asn1 = OpenSSL::ASN1::Sequence.new( [ OpenSSL::ASN1::Sequence.new([ - OpenSSL::ASN1::ObjectId.new('id-ecPublicKey'), - OpenSSL::ASN1::ObjectId.new(group.curve_name) - ]), + OpenSSL::ASN1::ObjectId.new('id-ecPublicKey'), + OpenSSL::ASN1::ObjectId.new(group.curve_name) + ]), OpenSSL::ASN1::BitString.new(public_key_point.to_octet_string(:uncompressed)) ] ) OpenSSL::PKey::EC.new(asn1.to_der) end + # rubocop:enable Metrics/MethodLength private diff --git a/spec/fabric/ec_crypto_suite_spec.rb b/spec/fabric/ec_crypto_suite_spec.rb index 78c8d3e..3b8419a 100644 --- a/spec/fabric/ec_crypto_suite_spec.rb +++ b/spec/fabric/ec_crypto_suite_spec.rb @@ -246,8 +246,11 @@ .to eql(random_public_key) end - it 'should be a public key' do + it 'is a public key' do expect(crypto_suite.pkey_from_public_key(random_public_key).public?).to be(true) + end + + it 'is not a private key' do expect(crypto_suite.pkey_from_public_key(random_public_key).private?).to be(false) end @@ -257,9 +260,8 @@ public_pem = private_pkey.public_to_pem public_key = crypto_suite.restore_public_key(private_pkey.private_key.to_s(16).downcase) - public_pkey = crypto_suite.pkey_from_public_key(public_key) - expect(public_pem).to eql(public_pkey.to_pem) + expect(public_pem).to eql(crypto_suite.pkey_from_public_key(public_key).to_pem) end end end diff --git a/spec/factories.rb b/spec/factories.rb index 29039cf..2b51a71 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -30,8 +30,8 @@ factory :gateway, class: 'Fabric::Gateway' do transient do - signer { build(:identity) } - client { build(:simple_client) } + signer { association :identity } + client { association :simple_client } end initialize_with { Fabric::Gateway.new(signer, client) } @@ -39,7 +39,7 @@ factory :network, class: 'Fabric::Network' do transient do - gateway { build(:gateway) } + gateway { association :gateway } name { 'testnet' } end @@ -48,7 +48,7 @@ factory :contract, class: 'Fabric::Contract' do transient do - network { build(:network) } + network { association :network } chaincode_name { 'testchaincode' } contract_name { 'testcontract' } end @@ -58,7 +58,7 @@ factory :proposed_transaction, class: 'Fabric::ProposedTransaction' do transient do - contract { build(:contract) } + contract { association :contract } transaction_name { 'testtransaction' } arguments { [] } transient_data { {} } From 1dbdb94fc5b03a2f0848163dfa61bdc91588f3ad Mon Sep 17 00:00:00 2001 From: Jonathan Chan Date: Sat, 21 Oct 2023 20:33:11 -0500 Subject: [PATCH 13/20] corrected all rubocop errors --- spec/fabric/ec_crypto_suite_spec.rb | 82 ++++++++++++++++++----------- 1 file changed, 50 insertions(+), 32 deletions(-) diff --git a/spec/fabric/ec_crypto_suite_spec.rb b/spec/fabric/ec_crypto_suite_spec.rb index 3b8419a..4c57ef7 100644 --- a/spec/fabric/ec_crypto_suite_spec.rb +++ b/spec/fabric/ec_crypto_suite_spec.rb @@ -5,16 +5,26 @@ let(:private_key) { 'd62e76ab4a907d7634ada0d9709b4ed2bfc7c51c421127b7fc93c0141e461797' } let(:public_key) do - '04a01f01fa942d2233a64aebe0b36c16ebdfd1c453ac5297591f20e2bfaba869e17e15f5f7367ee6f16121c64cac3ecdd517920a36f5145dc2a881ae9371873ac6' + '04a01f01fa942d2233a64aebe0b36c16ebdfd1c453ac5297591f20e2bfaba869e17e15f5f7367ee6f1' \ + '6121c64cac3ecdd517920a36f5145dc2a881ae9371873ac6' end let(:random_secret) { 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' } describe '#initialize' do context 'when no parameters are passed' do - it 'utilizes default options' do + it 'utilizes default key_size' do expect(crypto_suite.key_size).to be(256) + end + + it 'utilizes default digest_algorithm' do expect(crypto_suite.digest_algorithm).to eql('SHA256') + end + + it 'utilizes default curve' do expect(crypto_suite.curve).to eql('prime256v1') + end + + it 'utilizes default cipher' do expect(crypto_suite.cipher).to eql('aes-256-cbc') end end @@ -30,10 +40,19 @@ ) end - it 'utilizes options passed' do + it 'utilizes key_size option passed' do expect(crypto_suite.key_size).to be(384) + end + + it 'utilizes digest_algorithm option passed' do expect(crypto_suite.digest_algorithm).to eql('SHA224') + end + + it 'utilizes curve option passed' do expect(crypto_suite.curve).to eql('secp384r1') + end + + it 'utilizes cipher option passed' do expect(crypto_suite.cipher).to eql('aes-128-cbc') end end @@ -69,40 +88,33 @@ end describe '#generate_private_key' do - it 'generates a valid EC private_key' do + it 'generates a private_key string' do private_key = crypto_suite.generate_private_key expect(private_key).to be_a(String) + end - public_key = crypto_suite.restore_public_key private_key - - group = OpenSSL::PKey::EC::Group.new(crypto_suite.curve) - - private_key_bn = OpenSSL::BN.new(private_key, 16) - public_key_bn = OpenSSL::BN.new(public_key, 16) - public_key_point = OpenSSL::PKey::EC::Point.new(group, public_key_bn) - - asn1 = OpenSSL::ASN1::Sequence( - [ - OpenSSL::ASN1::Integer.new(1), - OpenSSL::ASN1::OctetString(private_key_bn.to_s(2)), - OpenSSL::ASN1::ObjectId(crypto_suite.curve, 0, :EXPLICIT), - OpenSSL::ASN1::BitString(public_key_point.to_octet_string(:uncompressed), 1, :EXPLICIT) - ] - ) - - pkey = OpenSSL::PKey::EC.new(asn1.to_der) - + it 'generates a private_key' do + private_key = crypto_suite.generate_private_key + pkey = crypto_suite.pkey_from_private_key(private_key) expect(pkey.private?).to be(true) + end + + it 'matches itself' do + private_key = crypto_suite.generate_private_key + pkey = crypto_suite.pkey_from_private_key(private_key) expect(pkey.private_key.to_s(16).downcase).to eql(private_key) end end describe '#generate_csr' do - it 'generates a OpenSSL::X509::Request with the proper key' do - req = crypto_suite.generate_csr(private_key) + subject(:response_value) { crypto_suite.generate_csr(private_key) } + + it 'generates a OpenSSL::X509::Request' do + expect(response_value).to be_a(OpenSSL::X509::Request) + end - expect(req).to be_a(OpenSSL::X509::Request) - expect(req.public_key.private_key.to_s(16).downcase).to eql(private_key) + it 'returns the proper value' do + expect(response_value.public_key.private_key.to_s(16).downcase).to eql(private_key) end end @@ -122,7 +134,8 @@ describe '#hexdigest' do it 'generates a hexdigest' do - expect(crypto_suite.hexdigest('hello world')).to eql('b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9') + expected_digest = 'b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9' + expect(crypto_suite.hexdigest('hello world')).to eql(expected_digest) end end @@ -187,10 +200,14 @@ end describe '#pkey_from_private_key' do + subject(:return_value) { crypto_suite.pkey_from_private_key(private_key) } + it 'converts a private key into a OpenSSL::PKey::EC' do - pkey = crypto_suite.pkey_from_private_key(private_key) - expect(pkey).to be_a(OpenSSL::PKey::EC) - expect(pkey.private_key).to eql(OpenSSL::BN.new(private_key, 16)) + expect(return_value).to be_a(OpenSSL::PKey::EC) + end + + it 'private_key is expected to equal the original private key' do + expect(return_value.private_key).to eql(OpenSSL::BN.new(private_key, 16)) end end @@ -222,7 +239,8 @@ '-----END CERTIFICATE-----' end let(:random_certificate_public_key) do - '04ca2509600ed223fbb645ff28b8d0f4229e3a81d8ca70607c571dd591a8c5c79f9886d68c7c76f5e588df19616e34bab2c4b186bc352ae87946b0127d40870fb4' + '04ca2509600ed223fbb645ff28b8d0f4229e3a81d8ca70607c571dd591a8c5c79f9886d' \ + '68c7c76f5e588df19616e34bab2c4b186bc352ae87946b0127d40870fb4' end it 'returns public_key from x509 certificate' do From cfe10c27b77177d8757fa759bb8c6be190c7d218 Mon Sep 17 00:00:00 2001 From: Jonathan Chan Date: Sat, 21 Oct 2023 22:01:24 -0500 Subject: [PATCH 14/20] fix dumb ruby 2.x bug --- fabric-gateway.gemspec | 2 +- spec/fabric/client_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fabric-gateway.gemspec b/fabric-gateway.gemspec index 5e8aa7a..226a772 100644 --- a/fabric-gateway.gemspec +++ b/fabric-gateway.gemspec @@ -12,7 +12,7 @@ Gem::Specification.new do |spec| spec.description = 'Ruby port of the Hyperledger Fabric Gateway SDK' spec.homepage = 'https://github.com/ethicalidentity/fabric-gateway-ruby' spec.license = 'MIT' - spec.required_ruby_version = Gem::Requirement.new('>= 2.6.0') + spec.required_ruby_version = Gem::Requirement.new('>= 2.7.0') spec.metadata['allowed_push_host'] = 'https://rubygems.org' diff --git a/spec/fabric/client_spec.rb b/spec/fabric/client_spec.rb index 42aafa6..b4f24ce 100644 --- a/spec/fabric/client_spec.rb +++ b/spec/fabric/client_spec.rb @@ -57,7 +57,7 @@ context 'when grpc_client host and creds are passed' do let(:expected_args) do - if RUBY_VERSION.start_with?('2.6') + if RUBY_VERSION.start_with?('2') ['localhost:1234', :this_channel_is_insecure, {}] else ['localhost:1234', :this_channel_is_insecure] From 5f512517edbc40768cd50a372a04bcfdcd2fa408 Mon Sep 17 00:00:00 2001 From: Jonathan Chan Date: Mon, 23 Oct 2023 09:19:55 -0500 Subject: [PATCH 15/20] fix linting error --- .rubocop.yml | 2 +- spec/support/shared_context/client_mocks.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index dc5ff0e..ec78c35 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -9,7 +9,7 @@ Metrics/BlockLength: AllCops: NewCops: enable - TargetRubyVersion: 2.6 + TargetRubyVersion: 2.7 Exclude: - vendor/bundle/**/* # exclude protoc generated code diff --git a/spec/support/shared_context/client_mocks.rb b/spec/support/shared_context/client_mocks.rb index 7b379ab..f217f02 100644 --- a/spec/support/shared_context/client_mocks.rb +++ b/spec/support/shared_context/client_mocks.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # rubocop:disable Metrics/BlockLength -RSpec.shared_context 'client mocks' do # rubocop:disable Rspec/ContextWording +RSpec.shared_context 'client mocks' do # rubocop:disable RSpec/ContextWording attr_reader :sent_evaluate_request, :sent_endorse_request, :sent_submit_request attr_reader :sent_proposal attr_reader :sent_chaincode_proposal_payload From 385f21c9c860b8456f2745ad4c037fcea8a4afaf Mon Sep 17 00:00:00 2001 From: Jonathan Chan Date: Tue, 24 Oct 2023 16:04:57 -0500 Subject: [PATCH 16/20] removing ruby 2.7 support --- .github/workflows/rspec.yml | 2 +- fabric-gateway.gemspec | 2 +- spec/fabric/client_spec.rb | 12 ++---------- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml index 24deda8..c475264 100644 --- a/.github/workflows/rspec.yml +++ b/.github/workflows/rspec.yml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - ruby-version: ['2.7', '3.0', '3.1', '3.2'] + ruby-version: ['3.0', '3.1', '3.2'] steps: - uses: actions/checkout@v4 diff --git a/fabric-gateway.gemspec b/fabric-gateway.gemspec index 226a772..4b79fd1 100644 --- a/fabric-gateway.gemspec +++ b/fabric-gateway.gemspec @@ -12,7 +12,7 @@ Gem::Specification.new do |spec| spec.description = 'Ruby port of the Hyperledger Fabric Gateway SDK' spec.homepage = 'https://github.com/ethicalidentity/fabric-gateway-ruby' spec.license = 'MIT' - spec.required_ruby_version = Gem::Requirement.new('>= 2.7.0') + spec.required_ruby_version = Gem::Requirement.new('>= 3.0') spec.metadata['allowed_push_host'] = 'https://rubygems.org' diff --git a/spec/fabric/client_spec.rb b/spec/fabric/client_spec.rb index b4f24ce..de79c26 100644 --- a/spec/fabric/client_spec.rb +++ b/spec/fabric/client_spec.rb @@ -33,11 +33,7 @@ context 'when passing invalid client_opts' do let(:expected_message) do - if RUBY_VERSION.start_with?('2.6') - 'unknown keyword: bad_arg' - else - 'unknown keyword: :bad_arg' - end + 'unknown keyword: :bad_arg' end it 'raises an error' do @@ -57,11 +53,7 @@ context 'when grpc_client host and creds are passed' do let(:expected_args) do - if RUBY_VERSION.start_with?('2') - ['localhost:1234', :this_channel_is_insecure, {}] - else - ['localhost:1234', :this_channel_is_insecure] - end + ['localhost:1234', :this_channel_is_insecure] end before do From 057fc37b3ccd9fa437af19ec92529e3dc2b4fa0e Mon Sep 17 00:00:00 2001 From: Jonathan Chan Date: Tue, 24 Oct 2023 16:08:40 -0500 Subject: [PATCH 17/20] removing rake notes as it is deprecated --- Gemfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Gemfile b/Gemfile index bdd10a6..b42ed4a 100644 --- a/Gemfile +++ b/Gemfile @@ -10,7 +10,6 @@ gem 'rspec', '~> 3.0' gem 'factory_bot', '~> 6.3.0' gem 'grpc-tools', '~> 1.59.0' -gem 'rake-notes', '~> 0.2.0' gem 'rubocop', '~> 1.57', '>= 1.57.1' gem 'rubocop-rspec', '~> 2.24.1' gem 'simplecov', '~> 0.22.0' From 399525c38f403b278a9c4fa69172d6d0b9e0585d Mon Sep 17 00:00:00 2001 From: Jonathan Chan Date: Tue, 24 Oct 2023 16:10:43 -0500 Subject: [PATCH 18/20] Removing more ruby 2 conditionals and fixing up Rakefile removing cruft --- Rakefile | 1 - spec/fabric/ec_crypto_suite_spec.rb | 1 - 2 files changed, 2 deletions(-) diff --git a/Rakefile b/Rakefile index 564b8d2..82bb534 100644 --- a/Rakefile +++ b/Rakefile @@ -2,7 +2,6 @@ require 'bundler/gem_tasks' require 'rspec/core/rake_task' -require 'rake/notes/rake_task' RSpec::Core::RakeTask.new(:spec) diff --git a/spec/fabric/ec_crypto_suite_spec.rb b/spec/fabric/ec_crypto_suite_spec.rb index 4c57ef7..76ffcbe 100644 --- a/spec/fabric/ec_crypto_suite_spec.rb +++ b/spec/fabric/ec_crypto_suite_spec.rb @@ -273,7 +273,6 @@ end it 'creates a matching public key' do - skip('Test unsupported on ruby 2.x') if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.0.0') private_pkey = OpenSSL::PKey::EC.generate(crypto_suite.curve) public_pem = private_pkey.public_to_pem From d71087cee726865cfaa538d0eb0d9da9931c5deb Mon Sep 17 00:00:00 2001 From: Jonathan Chan Date: Tue, 24 Oct 2023 16:16:32 -0500 Subject: [PATCH 19/20] fixing rubocop to ruby 3.0 --- .rubocop.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.rubocop.yml b/.rubocop.yml index ec78c35..ba763ad 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -9,7 +9,7 @@ Metrics/BlockLength: AllCops: NewCops: enable - TargetRubyVersion: 2.7 + TargetRubyVersion: 3.0 Exclude: - vendor/bundle/**/* # exclude protoc generated code From f99691cadc3c52f6dca5c34eee6b73997a835eb9 Mon Sep 17 00:00:00 2001 From: Jonathan Chan Date: Tue, 24 Oct 2023 16:25:10 -0500 Subject: [PATCH 20/20] updating rubocop job --- .github/workflows/rubocop.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rubocop.yml b/.github/workflows/rubocop.yml index fc04373..81db61f 100644 --- a/.github/workflows/rubocop.yml +++ b/.github/workflows/rubocop.yml @@ -8,10 +8,10 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Set up Ruby 2.7 + - name: Set up Ruby 3.0 uses: ruby/setup-ruby@v1 with: - ruby-version: 2.7 + ruby-version: 3.0 - name: Cache gems uses: actions/cache@v1 with: