diff --git a/bin/minisign b/bin/minisign index 821092e..66549f7 100755 --- a/bin/minisign +++ b/bin/minisign @@ -1,23 +1,36 @@ #!/usr/bin/env ruby # frozen_string_literal: true +require 'io/console' require 'minisign' +require 'optparse' -USAGE = <<~ENDUSAGE - Usage: - minisign -G [-f] [-p pubkey_file] [-s seckey_file] [-W] -ENDUSAGE +Signal.trap('INT') { exit } -HELP = <<~ENDHELP - - -G generate a new key pair - -f force. Combined with -G, overwrite a previous key pair - -p public key file (default: ./minisign.pub) - -s secret key file (default: ~/.minisign/minisign.key) - -W do not encrypt/decrypt the secret key with a password -ENDHELP +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 -if ARGV.empty? - puts USAGE - puts HELP +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] diff --git a/lib/minisign.rb b/lib/minisign.rb index dd35c34..4c4919e 100644 --- a/lib/minisign.rb +++ b/lib/minisign.rb @@ -4,6 +4,7 @@ require 'base64' require 'rbnacl' +require 'minisign/cli' require 'minisign/utils' require 'minisign/public_key' require 'minisign/signature' diff --git a/lib/minisign/cli.rb b/lib/minisign/cli.rb new file mode 100644 index 0000000..dc9217a --- /dev/null +++ b/lib/minisign/cli.rb @@ -0,0 +1,58 @@ +# 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 public key file (default: ./minisign.pub)' + puts '-s 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.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 = $stdin.noecho(&:gets).chomp + 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 diff --git a/minisign.pub b/minisign.pub new file mode 100644 index 0000000..e69de29 diff --git a/spec/minisign/cli_spec.rb b/spec/minisign/cli_spec.rb new file mode 100644 index 0000000..ba32802 --- /dev/null +++ b/spec/minisign/cli_spec.rb @@ -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($stdin).not_to receive(:gets) + 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($stdin).to receive(:gets).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