diff --git a/CHANGELOG.md b/CHANGELOG.md index e7b33e8..5fdc30d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- Support for changing or removing the password from the private key + ## [0.1.0] - 2024-02-09 ### Added diff --git a/README.md b/README.md index 4ab59d3..dec15b3 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ A rubygem for creating and verifying [Minisign](http://jedisct1.github.io/minisi - [Read a public key](#read-a-public-key) - [Verify a signature](#verify-a-signature) - [Read a private key](#read-a-private-key) + - [Change the private key's password](#change-the-private-keys-password) - [Create a signature](#create-a-signature) - [Generate a key pair](#generate-a-key-pair) - [Local Development](#local-development) @@ -41,6 +42,15 @@ password = "password" # optional, if the key is not encrypted private_key = Minisign::PrivateKey.new(File.read("minisign.key"), password) ``` +### Change the private key's password + +```rb +password = "new password" +private_key.change_password! password +# or remove the password +private_key.change_password! nil +``` + ### Create a signature ```rb diff --git a/lib/minisign/private_key.rb b/lib/minisign/private_key.rb index 42f3a0c..3267a1e 100644 --- a/lib/minisign/private_key.rb +++ b/lib/minisign/private_key.rb @@ -61,6 +61,14 @@ def to_s "untrusted comment: #{@untrusted_comment}\n#{Base64.strict_encode64(data)}\n" end + # Change or remove a password + # + # @param new_password [String] + def change_password!(new_password) + @password = new_password + @bytes[2..3] = [0, 0] if new_password.nil? # kdf_algorithm + end + private def signature_algorithm diff --git a/spec/minisign/private_key_spec.rb b/spec/minisign/private_key_spec.rb index 093137f..defb3c5 100644 --- a/spec/minisign/private_key_spec.rb +++ b/spec/minisign/private_key_spec.rb @@ -107,4 +107,45 @@ )).to be(true) end end + + describe '#change_password!' do + before do + @private_key = Minisign::PrivateKey.new(File.read('test/minisign.key'), 'password') + end + it 'changes the password' do + random_trusted_comment = SecureRandom.uuid + new_password = SecureRandom.uuid + original_public_key = @private_key.public_key + original_signature = @private_key.sign('example.txt', 'example', random_trusted_comment) + original_private_key = @private_key.to_s + @private_key.change_password! new_password + new_signature = @private_key.sign('example.txt', 'example', random_trusted_comment) + expect(original_signature.to_s).to eq(new_signature.to_s) + expect(original_public_key.to_s).to eq(@private_key.public_key.to_s) + expect(original_private_key.to_s).not_to eq(@private_key.to_s) + expect do + Minisign::PrivateKey.new(@private_key.to_s, new_password) + end.not_to raise_error + expect do + Minisign::PrivateKey.new(@private_key.to_s) + end.to raise_error('Missing password for encrypted key') + + File.write('test/generated/new-password.key', @private_key) + path = 'test/generated' + command = "echo #{new_password} | #{path}/minisign -Sm #{path}/.keep -s #{path}/new-password.key" + expect(system(command)).to be(true) + end + + it 'removes the password if nil' do + @private_key.change_password! nil + expect do + Minisign::PrivateKey.new(@private_key.to_s) + end.not_to raise_error + File.write('test/generated/removed-password.key', @private_key) + path = 'test/generated' + # does not prompt for password + command = "#{path}/minisign -Sm #{path}/.keep -s #{path}/removed-password.key" + expect(system(command)).to be(true) + end + end end