From 57253b4bc34042769b65890473ab6b463700f30b Mon Sep 17 00:00:00 2001 From: Jesse Shawl Date: Fri, 9 Feb 2024 18:27:03 -0600 Subject: [PATCH] refactor --- lib/minisign/private_key.rb | 12 ++++------ lib/minisign/public_key.rb | 38 +++++++++++++++++-------------- lib/minisign/signature.rb | 4 +++- spec/minisign/private_key_spec.rb | 28 ++++++++--------------- 4 files changed, 39 insertions(+), 43 deletions(-) diff --git a/lib/minisign/private_key.rb b/lib/minisign/private_key.rb index 3dca932..42f3a0c 100644 --- a/lib/minisign/private_key.rb +++ b/lib/minisign/private_key.rb @@ -1,21 +1,17 @@ # frozen_string_literal: true module Minisign - # Parse ed25519 signing key from minisign private key + # The private key used to create signatures class PrivateKey include Utils - attr_reader :kdf_salt, :kdf_opslimit, :kdf_memlimit, - :key_id, :ed25519_public_key_bytes, :ed25519_private_key_bytes, :checksum + attr_reader :key_id # Parse signing information from the minisign private key # # @param str [String] The minisign private key # @param password [String] The password used to encrypt the private key # @example - # Minisign::PrivateKey.new( - # File.read("test/minisign.key") - # 'password' - # ) + # Minisign::PrivateKey.new(File.read("test/minisign.key"), 'password') def initialize(str, password = nil) comment, data = str.split("\n") @password = password @@ -27,6 +23,8 @@ def initialize(str, password = nil) assert_valid_key! end + # Get the corresponding public key from the private key + # # @return [Minisign::PublicKey] def public_key data = Base64.strict_encode64("Ed#{@key_id.pack('C*')}#{@ed25519_public_key_bytes.pack('C*')}") diff --git a/lib/minisign/public_key.rb b/lib/minisign/public_key.rb index 6477b67..fdf3405 100644 --- a/lib/minisign/public_key.rb +++ b/lib/minisign/public_key.rb @@ -1,14 +1,16 @@ # frozen_string_literal: true module Minisign - # Parse ed25519 verify key from minisign public key + # The public key used to verify signatures class PublicKey include Utils - # Parse the ed25519 verify key from the minisign public key + # Read a minisign public key # # @param str [String] The minisign public key # @example # Minisign::PublicKey.new('RWTg6JXWzv6GDtDphRQ/x7eg0LaWBcTxPZ7i49xEeiqXVcR+r79OZRWM') + # # or from a file: + # Minisign::PublicKey.new(File.read('test/minisign.pub')) def initialize(str) @lines = str.split("\n") @decoded = Base64.strict_decode64(@lines.last) @@ -16,44 +18,46 @@ def initialize(str) # @return [String] the key id # @example - # Minisign::PublicKey.new('RWTg6JXWzv6GDtDphRQ/x7eg0LaWBcTxPZ7i49xEeiqXVcR+r79OZRWM').key_id + # public_key.key_id # #=> "E86FECED695E8E0" def key_id key_id_binary_string.bytes.map { |c| c.to_s(16) }.reverse.join.upcase end - def untrusted_comment - if @lines.length == 1 - "minisign public key #{key_id}" - else - @lines.first.split('untrusted comment: ').last - end - end - # Verify a message's signature # - # @param sig [Minisign::Signature] + # @param signature [Minisign::Signature] # @param message [String] the content that was signed # @return [String] the trusted comment # @raise Ed25519::VerifyError on invalid signatures # @raise RuntimeError on tampered trusted comments - def verify(sig, message) - assert_matching_key_ids!(sig.key_id, key_id) - ed25519_verify_key.verify(sig.signature, blake2b512(message)) + # @raise RuntimeError on mismatching key ids + def verify(signature, message) + assert_matching_key_ids!(signature.key_id, key_id) + ed25519_verify_key.verify(signature.signature, blake2b512(message)) begin - ed25519_verify_key.verify(sig.trusted_comment_signature, sig.signature + sig.trusted_comment) + ed25519_verify_key.verify(signature.trusted_comment_signature, signature.signature + signature.trusted_comment) rescue Ed25519::VerifyError raise 'Comment signature verification failed' end - "Signature and comment signature verified\nTrusted comment: #{sig.trusted_comment}" + "Signature and comment signature verified\nTrusted comment: #{signature.trusted_comment}" end + # @return [String] The public key that can be written to a file def to_s "untrusted comment: #{untrusted_comment}\n#{key_data}\n" end private + def untrusted_comment + if @lines.length == 1 + "minisign public key #{key_id}" + else + @lines.first.split('untrusted comment: ').last + end + end + def key_id_binary_string @decoded[2..9] end diff --git a/lib/minisign/signature.rb b/lib/minisign/signature.rb index 1fadbc3..6787832 100644 --- a/lib/minisign/signature.rb +++ b/lib/minisign/signature.rb @@ -26,15 +26,17 @@ def trusted_comment @lines[2].split('trusted comment: ')[1] end + # @return [String] the signature for the trusted comment def trusted_comment_signature Base64.decode64(@lines[3]) end - # @return [String] the signature + # @return [String] the global signature def signature encoded_signature[10..] end + # @return [String] The signature that can be written to a file def to_s "#{@lines.join("\n")}\n" end diff --git a/spec/minisign/private_key_spec.rb b/spec/minisign/private_key_spec.rb index 9c35203..093137f 100644 --- a/spec/minisign/private_key_spec.rb +++ b/spec/minisign/private_key_spec.rb @@ -36,16 +36,17 @@ end it 'parses the kdf_salt' do - expect(@private_key.kdf_salt).to eq([17, 255, 178, 97, 174, 94, 1, 125, 252, 62, 7, 107, 35, 116, 204, 199, 12, - 190, 222, 200, 51, 166, 7, 25, 89, 5, 225, 56, 170, 157, 127, 219]) + kdf_salt = @private_key.instance_variable_get('@kdf_salt') + expect(kdf_salt).to eq([17, 255, 178, 97, 174, 94, 1, 125, 252, 62, 7, 107, 35, 116, 204, 199, 12, + 190, 222, 200, 51, 166, 7, 25, 89, 5, 225, 56, 170, 157, 127, 219]) end it 'parses the kdf_opslimit' do - expect(@private_key.kdf_opslimit).to eq(33_554_432) + expect(@private_key.instance_variable_get('@kdf_opslimit')).to eq(33_554_432) end it 'parses the kdf_memlimit' do - expect(@private_key.kdf_memlimit).to eq(1_073_741_824) + expect(@private_key.instance_variable_get('@kdf_memlimit')).to eq(1_073_741_824) end it 'parses the key id' do @@ -53,30 +54,21 @@ end it 'parses the public key' do - key = @private_key.ed25519_public_key_bytes + key = @private_key.instance_variable_get('@ed25519_public_key_bytes') expect(key).to eq([108, 35, 192, 26, 47, 128, 233, 165, 133, 38, 242, 5, 76, 55, 135, 40, 103, 72, 230, 43, 184, 117, 219, 37, 173, 250, 196, 122, 252, 174, 173, 140]) end it 'parses the secret key' do - key = @private_key.ed25519_private_key_bytes + key = @private_key.instance_variable_get('@ed25519_private_key_bytes') expect(key).to eq([65, 87, 110, 33, 168, 130, 118, 100, 249, 200, 160, 167, 47, 59, 141, 122, 156, 38, 80, 199, 139, 1, 21, 18, 116, 110, 204, 131, 199, 202, 181, 87]) end it 'parses the checksum' do - expect(@private_key.checksum).to eq([19, 146, 239, 121, 33, 164, 216, 219, 8, 104, 111, 52, 198, 78, 21, 236, - 113, 255, 174, 47, 39, 216, 61, 198, 233, 161, 233, 143, 84, 246, 255, 150]) - - key_data = [ - [69, 100], - @private_key.key_id, - @private_key.ed25519_private_key_bytes, - @private_key.ed25519_public_key_bytes - ].inject(&:+).pack('C*') - - computed_checksum = blake2b256(key_data).bytes - expect(@private_key.checksum).to eq(computed_checksum) + checksum = @private_key.instance_variable_get('@checksum') + expect(checksum).to eq([19, 146, 239, 121, 33, 164, 216, 219, 8, 104, 111, 52, 198, 78, 21, 236, + 113, 255, 174, 47, 39, 216, 61, 198, 233, 161, 233, 143, 84, 246, 255, 150]) end it 'can be written to a file' do