Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
jshawl committed Feb 10, 2024
1 parent 3cb4513 commit 57253b4
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 43 deletions.
12 changes: 5 additions & 7 deletions lib/minisign/private_key.rb
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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*')}")
Expand Down
38 changes: 21 additions & 17 deletions lib/minisign/public_key.rb
Original file line number Diff line number Diff line change
@@ -1,59 +1,63 @@
# 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)
end

# @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
Expand Down
4 changes: 3 additions & 1 deletion lib/minisign/signature.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
28 changes: 10 additions & 18 deletions spec/minisign/private_key_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,47 +36,39 @@
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
expect(@private_key.key_id).to eq([166, 41, 163, 171, 79, 169, 183, 76])
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
Expand Down

0 comments on commit 57253b4

Please sign in to comment.