Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
jshawl committed Jan 31, 2024
1 parent ef408cc commit ee85cd7
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 93 deletions.
1 change: 1 addition & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ Metrics/BlockLength:
IgnoredMethods: ['describe', 'context']
AllCops:
NewCops: enable
TargetRubyVersion: 2.7.7
94 changes: 2 additions & 92 deletions lib/minisign.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,95 +3,5 @@
require 'ed25519'
require 'base64'
require 'openssl'

# `minisign` is a rubygem for verifying {https://jedisct1.github.io/minisign minisign} signatures.
# @author Jesse Shawl
module Minisign
# Parse a .minisig file's contents
class Signature
# @param str [String] The contents of the .minisig file
# @example
# Minisign::Signature.new(File.read('test/example.txt.minisig'))
def initialize(str)
@lines = str.split("\n")
end

# @return [String] the key id
# @example
# Minisign::Signature.new(File.read('test/example.txt.minisig')).key_id
# #=> "E86FECED695E8E0"
def key_id
encoded_signature[2..9].bytes.map { |c| c.to_s(16) }.reverse.join.upcase
end

# @return [String] the trusted comment
# @example
# Minisign::Signature.new(File.read('test/example.txt.minisig')).trusted_comment
# #=> "timestamp:1653934067\tfile:example.txt\thashed"
def trusted_comment
@lines[2].split('trusted comment: ')[1]
end

def trusted_comment_signature
Base64.decode64(@lines[3])
end

# @return [String] the signature
def signature
encoded_signature[10..]
end

private

def encoded_signature
Base64.decode64(@lines[1])
end
end

# Parse ed25519 verify key from minisign public key
class PublicKey
# Parse the ed25519 verify key from the minisign public key
#
# @param str [String] The minisign public key
# @example
# Minisign::PublicKey.new('RWTg6JXWzv6GDtDphRQ/x7eg0LaWBcTxPZ7i49xEeiqXVcR+r79OZRWM')
def initialize(str)
@decoded = Base64.strict_decode64(str)
@public_key = @decoded[10..]
@verify_key = Ed25519::VerifyKey.new(@public_key)
end

# @return [String] the key id
# @example
# Minisign::PublicKey.new('RWTg6JXWzv6GDtDphRQ/x7eg0LaWBcTxPZ7i49xEeiqXVcR+r79OZRWM').key_id
# #=> "E86FECED695E8E0"
def key_id
@decoded[2..9].bytes.map { |c| c.to_s(16) }.reverse.join.upcase
end

# Verify a message's signature
#
# @param sig [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)
blake = OpenSSL::Digest.new('BLAKE2b512')
ensure_matching_key_ids(sig.key_id, key_id)
@verify_key.verify(sig.signature, blake.digest(message))
begin
@verify_key.verify(sig.trusted_comment_signature, sig.signature + sig.trusted_comment)
rescue Ed25519::VerifyError
raise 'Comment signature verification failed'
end
"Signature and comment signature verified\nTrusted comment: #{sig.trusted_comment}"
end

private

def ensure_matching_key_ids(key_id1, key_id2)
raise "Signature key id is #{key_id1}\nbut the key id in the public key is #{key_id2}" unless key_id1 == key_id2
end
end
end
require 'signature'
require 'public_key'
50 changes: 50 additions & 0 deletions lib/public_key.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# frozen_string_literal: true

module Minisign
# Parse ed25519 verify key from minisign public key
class PublicKey
# Parse the ed25519 verify key from the minisign public key
#
# @param str [String] The minisign public key
# @example
# Minisign::PublicKey.new('RWTg6JXWzv6GDtDphRQ/x7eg0LaWBcTxPZ7i49xEeiqXVcR+r79OZRWM')
def initialize(str)
@decoded = Base64.strict_decode64(str)
@public_key = @decoded[10..]
@verify_key = Ed25519::VerifyKey.new(@public_key)
end

# @return [String] the key id
# @example
# Minisign::PublicKey.new('RWTg6JXWzv6GDtDphRQ/x7eg0LaWBcTxPZ7i49xEeiqXVcR+r79OZRWM').key_id
# #=> "E86FECED695E8E0"
def key_id
@decoded[2..9].bytes.map { |c| c.to_s(16) }.reverse.join.upcase
end

# Verify a message's signature
#
# @param sig [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)
blake = OpenSSL::Digest.new('BLAKE2b512')
ensure_matching_key_ids(sig.key_id, key_id)
@verify_key.verify(sig.signature, blake.digest(message))
begin
@verify_key.verify(sig.trusted_comment_signature, sig.signature + sig.trusted_comment)
rescue Ed25519::VerifyError
raise 'Comment signature verification failed'
end
"Signature and comment signature verified\nTrusted comment: #{sig.trusted_comment}"
end

private

def ensure_matching_key_ids(key_id1, key_id2)
raise "Signature key id is #{key_id1}\nbut the key id in the public key is #{key_id2}" unless key_id1 == key_id2
end
end
end
44 changes: 44 additions & 0 deletions lib/signature.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# frozen_string_literal: true

module Minisign
# Parse a .minisig file's contents
class Signature
# @param str [String] The contents of the .minisig file
# @example
# Minisign::Signature.new(File.read('test/example.txt.minisig'))
def initialize(str)
@lines = str.split("\n")
end

# @return [String] the key id
# @example
# Minisign::Signature.new(File.read('test/example.txt.minisig')).key_id
# #=> "E86FECED695E8E0"
def key_id
encoded_signature[2..9].bytes.map { |c| c.to_s(16) }.reverse.join.upcase
end

# @return [String] the trusted comment
# @example
# Minisign::Signature.new(File.read('test/example.txt.minisig')).trusted_comment
# #=> "timestamp:1653934067\tfile:example.txt\thashed"
def trusted_comment
@lines[2].split('trusted comment: ')[1]
end

def trusted_comment_signature
Base64.decode64(@lines[3])
end

# @return [String] the signature
def signature
encoded_signature[10..]
end

private

def encoded_signature
Base64.decode64(@lines[1])
end
end
end
2 changes: 1 addition & 1 deletion minisign.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ Gem::Specification.new do |s|
'https://rubygems.org/gems/minisign'
s.license = 'MIT'
s.add_runtime_dependency 'ed25519', '~> 1.3'
s.required_ruby_version = '>= 2.6.0'
s.required_ruby_version = '>= 2.7.7'
s.metadata['rubygems_mfa_required'] = 'true'
end

0 comments on commit ee85cd7

Please sign in to comment.