Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add cli #24

Merged
merged 7 commits into from
Feb 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ doc
.yardoc
test/generated/*
!test/generated/.keep
!test/generated/cli/.keep
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ A rubygem for creating and verifying [Minisign](http://jedisct1.github.io/minisi
- [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)
- [CLI](#cli)
- [Local Development](#local-development)
- [Documentation](#documentation)

Expand Down Expand Up @@ -69,6 +70,14 @@ keypair.private_key # Minisign::PrivateKey
keypair.public_key # Minisign::PublicKey
```

## CLI

This gem provides an executable `minisign` that implements the CLI
provided by [jedisct1/minisign](https://github.com/jedisct1/minisign).

See command line options [here](https://jedisct1.github.io/minisign/#usage)
or run the executable without any arguments to see usage options.

## Local Development

```
Expand Down
36 changes: 36 additions & 0 deletions bin/minisign
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

require 'io/console'
require 'minisign'
require 'optparse'

Signal.trap('INT') { exit }

options = {}
op = OptionParser.new do |opts|
opts.on('-G') do |g|
options[:G] = g
end
opts.on('-f') do |f|
options[:f] = f
end
opts.on('-sSECRET_KEY') do |s|
options[:s] = s
end
opts.on('-pPUBLIC_KEY') do |p|
options[:p] = p
end
end

begin
op.parse!
raise OptionParser::InvalidOption if options.keys.empty?
rescue OptionParser::InvalidOption
Minisign::CLI.usage
puts ''
Minisign::CLI.help
exit 1
end

Minisign::CLI.generate(options) if options[:G]
1 change: 1 addition & 0 deletions lib/minisign.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require 'base64'
require 'rbnacl'

require 'minisign/cli'
require 'minisign/utils'
require 'minisign/public_key'
require 'minisign/signature'
Expand Down
62 changes: 62 additions & 0 deletions lib/minisign/cli.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# frozen_string_literal: true

require 'io/console'

module Minisign
# The command line interface
module CLI
# lol
def self.help
puts '-G generate a new key pair'
puts '-f force. Combined with -G, overwrite a previous key pair'
puts '-p <pubkey_file> public key file (default: ./minisign.pub)'
puts '-s <seckey_file> secret key file (default: ~/.minisign/minisign.key)'
puts '-W do not encrypt/decrypt the secret key with a password'
end

def self.usage
puts 'Usage:'
puts 'minisign -G [-f] [-p pubkey_file] [-s seckey_file] [-W]'
end

def self.prompt
$stdin.noecho(&:gets).chomp
end

def self.prevent_overwrite!(file)
return unless File.exist? file

puts 'Key generation aborted:'
puts "#{file} already exists."
puts ''
puts 'If you really want to overwrite the existing key pair, add the -f switch to'
puts 'force this operation.'
exit 1
end

# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/MethodLength
def self.generate(options)
secret_key = options[:s] || "#{Dir.home}/.minisign/minisign.key"
public_key = options[:p] || './minisign.pub'
prevent_overwrite!(public_key) unless options[:f]
prevent_overwrite!(secret_key) unless options[:f]

if options[:W]
keypair = Minisign::KeyPair.new
File.write(secret_key, keypair.private_key)
File.write(public_key, keypair.public_key)
else
print 'Password: '
password = prompt
print "\nDeriving a key from the password in order to encrypt the secret key..."
keypair = Minisign::KeyPair.new(password)
File.write(secret_key, keypair.private_key)
File.write(public_key, keypair.public_key)
print " done\n"
end
end
# rubocop:enable Metrics/MethodLength
# rubocop:enable Metrics/AbcSize
end
end
3 changes: 2 additions & 1 deletion minisign.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ Gem::Specification.new do |s|
s.authors = ['Jesse Shawl']
s.email = '[email protected]'
s.files = Dir['lib/**/*']
s.homepage =
s.executables << 'minisign'
s.homepage =
'https://rubygems.org/gems/minisign'
s.license = 'MIT'
s.add_runtime_dependency 'ed25519', '~> 1.3'
Expand Down
40 changes: 40 additions & 0 deletions spec/minisign/cli_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# frozen_string_literal: true

describe Minisign::CLI do
describe '.generate' do
before do
@options = {
p: 'test/minisign.pub',
s: 'test/minisign.key'
}
end
it 'does not overwrite existing keys' do
expect do
Minisign::CLI.generate(@options)
end.to raise_error(SystemExit)
end
it 'does not prompt for a password if -W' do
keyname = SecureRandom.uuid
SecureRandom.uuid
options = {
p: "test/generated/cli/#{keyname}.pub",
s: "test/generated/cli/#{keyname}.key",
W: true
}
expect(Minisign::CLI).not_to receive(:prompt)
Minisign::CLI.generate(options)
end
it 'writes the key files' do
keyname = SecureRandom.uuid
password = SecureRandom.uuid
options = {
p: "test/generated/cli/#{keyname}.pub",
s: "test/generated/cli/#{keyname}.key"
}
allow(Minisign::CLI).to receive(:prompt).and_return(password)
Minisign::CLI.generate(options)
expect(File.exist?(options[:p])).to eq(true)
expect(File.exist?(options[:p])).to eq(true)
end
end
end
Empty file added test/generated/cli/.keep
Empty file.
Loading