diff --git a/lib/minisign/key_pair.rb b/lib/minisign/key_pair.rb index d7656f4..3c6ccfd 100644 --- a/lib/minisign/key_pair.rb +++ b/lib/minisign/key_pair.rb @@ -1,39 +1,55 @@ # frozen_string_literal: true module Minisign + # Generate a Minisign secret and public key class KeyPair include Minisign::Utils - attr_reader :private_key def initialize(password = nil) - key_id = SecureRandom.bytes(8) - signing_key = Ed25519::SigningKey.generate + @password = password - key_data = "Ed#{key_id}#{signing_key.to_bytes}#{signing_key.verify_key.to_bytes}" - @checksum = blake2b256(key_data) - @keynum_sk = "#{key_id}#{signing_key.to_bytes}#{signing_key.verify_key.to_bytes}#{@checksum}" + kd = key_data + + @checksum = blake2b256("Ed#{kd}") + @keynum_sk = "#{kd}#{@checksum}" - kdf_opslimit_bytes = [0, 0, 0, 2, 0, 0, 0, 0] - kdf_memlimit_bytes = [0, 0, 0, 64, 0, 0, 0, 0] - @kdf_opslimit = kdf_opslimit_bytes.pack('C*') - @kdf_memlimit = kdf_memlimit_bytes.pack('C*') @kdf_salt = SecureRandom.bytes(32) - if password - p @kdf_opslimit.unpack('N*').sum - kdf_output = derive_key( - password, - @kdf_salt, - kdf_opslimit_bytes.pack("V*").unpack('N*').sum, - kdf_memlimit_bytes.pack("V*").unpack('N*').sum - ) - p "kdf_output #{kdf_output}" - @keynum_sk = xor(kdf_output, @keynum_sk.bytes).pack("C*") - end + @keynum_sk = xor(kdf_output, @keynum_sk.bytes).pack('C*') if @password @kdf_algorithm = password.nil? ? [0, 0].pack('U*') : 'Sc' - @private_key = Minisign::PrivateKey.new( + end + + def private_key + @kdf_opslimit = kdf_opslimit_bytes.pack('C*') + @kdf_memlimit = kdf_memlimit_bytes.pack('C*') + Minisign::PrivateKey.new( Base64.strict_encode64("Ed#{@kdf_algorithm}B2#{@kdf_salt}#{@kdf_opslimit}#{@kdf_memlimit}#{@keynum_sk}"), - password + @password ) end + + private + + def kdf_output + derive_key( + @password, + @kdf_salt, + kdf_opslimit_bytes.pack('V*').unpack('N*').sum, + kdf_memlimit_bytes.pack('V*').unpack('N*').sum + ) + end + + def key_data + key_id = SecureRandom.bytes(8) + signing_key = Ed25519::SigningKey.generate + "#{key_id}#{signing_key.to_bytes}#{signing_key.verify_key.to_bytes}" + end + + def kdf_opslimit_bytes + [0, 0, 0, 2, 0, 0, 0, 0] + end + + def kdf_memlimit_bytes + [0, 0, 0, 64, 0, 0, 0, 0] + end end end diff --git a/lib/minisign/private_key.rb b/lib/minisign/private_key.rb index fd28832..d17c012 100644 --- a/lib/minisign/private_key.rb +++ b/lib/minisign/private_key.rb @@ -47,8 +47,6 @@ def key_data(bytes) [bytes[0..7], bytes[8..39], bytes[40..71], bytes[72..103]] end - # rubocop:disable Layout/LineLength - # @return [Ed25519::SigningKey] the ed25519 signing key def ed25519_signing_key Ed25519::SigningKey.new(@secret_key.pack('C*')) diff --git a/lib/minisign/utils.rb b/lib/minisign/utils.rb index 940e3f8..8ca056c 100644 --- a/lib/minisign/utils.rb +++ b/lib/minisign/utils.rb @@ -11,16 +11,15 @@ def blake2b512(message) RbNaCl::Hash::Blake2b.digest(message, { digest_size: 64 }) end - # @return [Array<32 bit unsigned ints>] the byte array containing the key id, the secret and public ed25519 keys, and the checksum + # @return [Array<32 bit unsigned ints>] def xor(kdf_output, contents) - # rubocop:enable Layout/LineLength kdf_output.each_with_index.map do |b, i| contents[i] ^ b end end - # @return [String] the used to xor the ed25519 keys - def derive_key(password, kdf_salt, kdf_opslimit, kdf_memlimit) + # @return [String] the used to xor the ed25519 keys + def derive_key(password, kdf_salt, kdf_opslimit, kdf_memlimit) RbNaCl::PasswordHash.scrypt( password, kdf_salt, diff --git a/spec/minisign/key_pair_spec.rb b/spec/minisign/key_pair_spec.rb index c9e4c61..0d9662a 100644 --- a/spec/minisign/key_pair_spec.rb +++ b/spec/minisign/key_pair_spec.rb @@ -6,7 +6,7 @@ expect(keypair.private_key).to be_truthy end it 'generates a keypair with a password' do - keypair = Minisign::KeyPair.new("secret password") + keypair = Minisign::KeyPair.new('secret password') expect(keypair.private_key).to be_truthy end end