sgpg 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +3 -0
- data/LICENSE +674 -0
- data/README.md +50 -0
- data/bin/sgpg +72 -0
- data/lib/sgpg/archive.rb +91 -0
- data/lib/sgpg/cryptsetup.rb +69 -0
- data/lib/sgpg/gpg.rb +47 -0
- data/lib/sgpg/helper.rb +14 -0
- data/lib/sgpg/mount.rb +67 -0
- data/lib/sgpg/option.rb +9 -0
- data/lib/sgpg/version.rb +5 -0
- data/lib/sgpg/yaml_config.rb +68 -0
- data/lib/sgpg.rb +90 -0
- data/sgpg.gemspec +45 -0
- data.tar.gz.sig +0 -0
- metadata +97 -0
- metadata.gz.sig +0 -0
    
        data/README.md
    ADDED
    
    | @@ -0,0 +1,50 @@ | |
| 1 | 
            +
            # Sgpg
         | 
| 2 | 
            +
            Short gpg, tool for manage your gpg key (backup tarball, unprivileged keys, etc)
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            Followed my [post](https://szorfein.github.io/gpg/build-secure-gpg-key/) to
         | 
| 5 | 
            +
            create a secure gpg key, i need to update my key all the 6 month on each PC
         | 
| 6 | 
            +
             and each time, it's very very annoying so i build this tool to gain in time
         | 
| 7 | 
            +
             and sanity :).
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            So with this tool, 'i,you,we' should use no more than 3 commands max instead of 50...
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            You always need to create a gpg key as well
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                gpg --expert --full-generate-key
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            ## Install sgpg locally
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                gem install sgpg
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            You also need to install dependencies: tar, cryptsetup, shred and gpg.
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            ## Configure
         | 
| 22 | 
            +
            The config is located at ~/.config/sgpg/config.yml. You can use the command line with `--save`:
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                sgpg --disk /dev/sdc2 --disk-encrypt --key szorfein@protonmail.com --save
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            You can register the disk/by-id or disk/by-uuid if you prefer.
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                sgpg --disk /dev/disk/by-id/wmn-0xXXXX-part2 --disk-encrypt --save
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            ## Usage
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                sgpg -h
         | 
| 33 | 
            +
             | 
| 34 | 
            +
            When subkeys expire:
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                sgpg --last-master --edit-key # update expired keys, change password, etc...
         | 
| 37 | 
            +
                sgpg --export
         | 
| 38 | 
            +
                sgpg --close # unmount and close disk
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            Import the last unpriviliged key (laptop and other)
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                sgpg --last-lesser --edit-key # trust (555)
         | 
| 43 | 
            +
                sgpg --close # unmount and close disk
         | 
| 44 | 
            +
             | 
| 45 | 
            +
            ## Gem push
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                gem login
         | 
| 48 | 
            +
                gem build sgpg.gemspec
         | 
| 49 | 
            +
                gem push sgpg-0.0.1.gem
         | 
| 50 | 
            +
             | 
    
        data/bin/sgpg
    ADDED
    
    | @@ -0,0 +1,72 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require 'sgpg'
         | 
| 5 | 
            +
            require 'optparse'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            config_file = Sgpg::YamlConfig.new
         | 
| 8 | 
            +
            config_file.load
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            options = config_file.config
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            OptionParser.new do |opts|
         | 
| 13 | 
            +
              opts.banner = 'Usage: sgpg.rb [options]'
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              opts.on('-d', '--disk DEV', 'Specify device disk to use to save your keys') do |dev|
         | 
| 16 | 
            +
                options[:disk] = dev
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              opts.on('--disk-encrypt', 'If we need to use cryptsetup to open the disk.') do
         | 
| 20 | 
            +
                options[:crypted] = true
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              opts.on('-k', '--key NAME', 'Use the key name, default use ENV["USER"]') do |name|
         | 
| 24 | 
            +
                options[:keyname] = name
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              opts.on('-e', '--edit-key', 'Edit your secret key.') do
         | 
| 28 | 
            +
                Sgpg::Main.edit_key(options)
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              opts.on('-l', '--list-key', "Display the last secret key #{Sgpg::MOUNTPOINT}.") do
         | 
| 32 | 
            +
                Sgpg.list_keys(options[:keyname])
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              opts.on('-o', '--open', "Open and mount device disk at #{Sgpg::MOUNTPOINT}.") do
         | 
| 36 | 
            +
                Sgpg.open(options[:disk], options[:crypted])
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              opts.on('-p', '--path-key PATH', 'use archive PATH') do |path|
         | 
| 40 | 
            +
                raise "path #{path} didn't found" unless File.exist? path
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                options[:keypath] = path
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              opts.on('--last-master', 'Use the last archive master') do
         | 
| 46 | 
            +
                options[:keypath] = Sgpg.last_key(options, 'master')
         | 
| 47 | 
            +
              end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
              opts.on('--last-lesser', 'Use the last archive lesser (unpriviliged)') do
         | 
| 50 | 
            +
                options[:keypath] = Sgpg.last_key(options, 'lesser')
         | 
| 51 | 
            +
              end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
              opts.on('-c', '--close', 'Unmount and close disk device.') do
         | 
| 54 | 
            +
                Sgpg.close(options[:disk], options[:crypted])
         | 
| 55 | 
            +
              end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
              opts.on('-s', '--save', 'Save current optargs in the config file') do
         | 
| 58 | 
            +
                config_file.save(options)
         | 
| 59 | 
            +
              end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
              opts.on('--export', 'Export your current master key and create both archives (master and lesser)') do
         | 
| 62 | 
            +
                Sgpg::Main.export_secret(options)
         | 
| 63 | 
            +
                Sgpg::Main.lesser_keys(options)
         | 
| 64 | 
            +
              end
         | 
| 65 | 
            +
            end.parse!
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            raise 'no disk to use' if !options[:disk] || options[:disk] == ''
         | 
| 68 | 
            +
            raise 'no key to use' if !options[:keyname] || options[:keybase] == ''
         | 
| 69 | 
            +
             | 
| 70 | 
            +
            puts config_file
         | 
| 71 | 
            +
             | 
| 72 | 
            +
            puts "Sgpg v.#{Sgpg::VERSION}"
         | 
    
        data/lib/sgpg/archive.rb
    ADDED
    
    | @@ -0,0 +1,91 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'fileutils'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Sgpg
         | 
| 6 | 
            +
              # Interact with program tar from unix
         | 
| 7 | 
            +
              class Archive
         | 
| 8 | 
            +
                # Code here
         | 
| 9 | 
            +
                def initialize(key, name = ENV['USER'])
         | 
| 10 | 
            +
                  @key = key || ''
         | 
| 11 | 
            +
                  @name = name
         | 
| 12 | 
            +
                  @date = Time.now.strftime('%Y-%m-%d')
         | 
| 13 | 
            +
                  puts "create key #{@name}-#{@date}-master.tar"
         | 
| 14 | 
            +
                  FileUtils.mkdir_p Sgpg::WORKDIR
         | 
| 15 | 
            +
                  @gpg = Gpg.new(@name)
         | 
| 16 | 
            +
                  # make it compatabble with Tails Linux
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def create_master_tar
         | 
| 20 | 
            +
                  Dir.chdir(Sgpg::WORKDIR)
         | 
| 21 | 
            +
                  @gpg.export_secret_keys(Sgpg::WORKDIR)
         | 
| 22 | 
            +
                  create_tar('master')
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                def create_lesser_tar
         | 
| 26 | 
            +
                  Dir.chdir(Sgpg::WORKDIR)
         | 
| 27 | 
            +
                  @gpg.export_subkey(Sgpg::WORKDIR)
         | 
| 28 | 
            +
                  @gpg.delete_keys
         | 
| 29 | 
            +
                  @gpg.import_lesser_keys
         | 
| 30 | 
            +
                  @gpg.export_secret_keys(Sgpg::WORKDIR)
         | 
| 31 | 
            +
                  create_tar('lesser')
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def extract
         | 
| 35 | 
            +
                  raise 'No key found, try with --path-key PATH.' unless File.exist?(@key)
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  Dir.chdir(Sgpg::WORKDIR)
         | 
| 38 | 
            +
                  puts "Unpacking archive #{@key}..."
         | 
| 39 | 
            +
                  system('tar', 'xvf', @key)
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                def import
         | 
| 43 | 
            +
                  @gpg.delete_keys
         | 
| 44 | 
            +
                  puts 'Importing gpg keys...'
         | 
| 45 | 
            +
                  keys = Dir.glob("#{Sgpg::WORKDIR}/*.key")
         | 
| 46 | 
            +
                  import_secret(keys)
         | 
| 47 | 
            +
                  import_public(keys)
         | 
| 48 | 
            +
                  @gpg.edit_key
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                def move(pathdir)
         | 
| 52 | 
            +
                  raise "Dir #{pathdir} no found." unless Dir.exist?(pathdir)
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  tar = Dir.glob("#{Sgpg::WORKDIR}/*.tar")
         | 
| 55 | 
            +
                  raise 'No archive found.' unless tar.length >= 1
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  mv(tar, pathdir)
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                private
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                def import_secret(keys)
         | 
| 63 | 
            +
                  keys.each { |k| system('gpg', '-a', '--import', k) if k.match(/secret/) }
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                def import_public(keys)
         | 
| 67 | 
            +
                  keys.each { |k| system('gpg', '-a', '--import', k) if k.match(/public/) }
         | 
| 68 | 
            +
                end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                # suffix should be 'master' or 'lesser' (keys without privilege)
         | 
| 71 | 
            +
                def create_tar(suffix = 'master')
         | 
| 72 | 
            +
                  if suffix == 'master'
         | 
| 73 | 
            +
                    system("tar -cf #{@name}-#{@date}-#{suffix}-keys.tar *.key revocation.cert")
         | 
| 74 | 
            +
                  else
         | 
| 75 | 
            +
                    system("tar -cf #{@name}-#{@date}-#{suffix}-keys.tar *.key")
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
                end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                def mv(tar, pathdir)
         | 
| 80 | 
            +
                  puts "Moving archive at #{pathdir}..."
         | 
| 81 | 
            +
                  case Helper.auth?
         | 
| 82 | 
            +
                  when :root
         | 
| 83 | 
            +
                    tar.each { |f| FileUtils.mv(f, "#{pathdir}/#{f}") }
         | 
| 84 | 
            +
                  when :sudo
         | 
| 85 | 
            +
                    tar.each { |f| system('sudo', 'mv', f, "#{pathdir}/") }
         | 
| 86 | 
            +
                  when :doas
         | 
| 87 | 
            +
                    tar.each { |f| system('doas', 'mv', f, "#{pathdir}/") }
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
                end
         | 
| 90 | 
            +
              end
         | 
| 91 | 
            +
            end
         | 
| @@ -0,0 +1,69 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # lib/cryptsetup.rb
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Sgpg
         | 
| 6 | 
            +
              # Manipule program cryptsetup
         | 
| 7 | 
            +
              class Cryptsetup
         | 
| 8 | 
            +
                class InvalidDisk < StandardError; end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def initialize(disk)
         | 
| 11 | 
            +
                  raise "no disk #{disk} specified..." unless disk
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  @disk = disk
         | 
| 14 | 
            +
                  @mapname = 'sgpg'
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                # tails linux make persistent volume on second partiton 'disk_name'2
         | 
| 18 | 
            +
                def open
         | 
| 19 | 
            +
                  check_disk
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  case Helper.auth?
         | 
| 22 | 
            +
                  when :root
         | 
| 23 | 
            +
                    open_with
         | 
| 24 | 
            +
                  when :doas
         | 
| 25 | 
            +
                    open_with 'doas'
         | 
| 26 | 
            +
                  when :sudo
         | 
| 27 | 
            +
                    open_with 'sudo'
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                def close
         | 
| 32 | 
            +
                  case Helper.auth?
         | 
| 33 | 
            +
                  when :root
         | 
| 34 | 
            +
                    close_with
         | 
| 35 | 
            +
                  when :doas
         | 
| 36 | 
            +
                    close_with 'doas'
         | 
| 37 | 
            +
                  when :sudo
         | 
| 38 | 
            +
                    close_with 'sudo'
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                protected
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                def check_disk
         | 
| 45 | 
            +
                  raise InvalidDisk, "Disk #{@disk} not exist or not plugged." unless
         | 
| 46 | 
            +
                    File.exist?(@disk)
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                private
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                def open_with(prefix = '')
         | 
| 52 | 
            +
                  if prefix
         | 
| 53 | 
            +
                    puts "openning disk #{@disk} with #{prefix}..."
         | 
| 54 | 
            +
                  else
         | 
| 55 | 
            +
                    puts "openning disk #{@disk}..."
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  raise "unable to open #{@disk} #{prefix}" unless
         | 
| 59 | 
            +
                    system(prefix, 'cryptsetup', 'open', '--type', 'luks', @disk, @mapname)
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                def close_with(prefix = '')
         | 
| 63 | 
            +
                  puts "closing disk #{@disk}..."
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  raise "closing disk #{@disk} failed" unless
         | 
| 66 | 
            +
                    system(prefix, 'cryptsetup', 'close', @mapname)
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
              end
         | 
| 69 | 
            +
            end
         | 
    
        data/lib/sgpg/gpg.rb
    ADDED
    
    | @@ -0,0 +1,47 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Sgpg
         | 
| 4 | 
            +
              # Interact with GnuPG from unix
         | 
| 5 | 
            +
              class Gpg
         | 
| 6 | 
            +
                # Code here
         | 
| 7 | 
            +
                def initialize(keyname)
         | 
| 8 | 
            +
                  @keyname = keyname
         | 
| 9 | 
            +
                  puts "using key #{@keyname}"
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def delete_keys
         | 
| 13 | 
            +
                  return unless system('gpg', '-k', @keyname)
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  puts "Found older key name #{@keyname}, need to be deleted to import new..."
         | 
| 16 | 
            +
                  system('gpg', '--delete-secret-keys', @keyname)
         | 
| 17 | 
            +
                  system('gpg', '--delete-keys', @keyname)
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def edit_key
         | 
| 21 | 
            +
                  raise "No key #{@keyname} found." unless system('gpg', '-k', @keyname)
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  system('gpg', '--edit-key', @keyname)
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def export_secret_keys(pathdir)
         | 
| 27 | 
            +
                  raise "No dir #{pathdir} exist" unless File.exist?(pathdir)
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  output = "#{pathdir}/#{@keyname}"
         | 
| 30 | 
            +
                  system("gpg --armor --export-secret-keys #{@keyname} > #{output}-secret.key")
         | 
| 31 | 
            +
                  system("gpg --armor --export #{@keyname} > #{output}-public.key")
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def export_subkey(pathdir)
         | 
| 35 | 
            +
                  raise "No dir #{pathdir} exist" unless File.exist?(pathdir)
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  system("gpg --export-secret-subkeys #{@keyname} > #{pathdir}/subkeys")
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                def import_lesser_keys
         | 
| 41 | 
            +
                  keypath = "#{Sgpg::WORKDIR}/subkeys"
         | 
| 42 | 
            +
                  raise "No key #{keypath} exist" unless File.exist?(keypath)
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  system('gpg', '--import', keypath)
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
            end
         | 
    
        data/lib/sgpg/helper.rb
    ADDED
    
    | @@ -0,0 +1,14 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # lib/helper.rb
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Sgpg
         | 
| 6 | 
            +
              # Reusable functions for the program
         | 
| 7 | 
            +
              module Helper
         | 
| 8 | 
            +
                def self.auth?
         | 
| 9 | 
            +
                  return :root if Process.uid == '0'
         | 
| 10 | 
            +
                  return :doas if File.exist?('/bin/doas') || File.exist?('/sbin/doas')
         | 
| 11 | 
            +
                  return :sudo if File.exist?('/bin/sudo') || File.exist?('/sbin/sudo')
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
              end
         | 
| 14 | 
            +
            end
         | 
    
        data/lib/sgpg/mount.rb
    ADDED
    
    | @@ -0,0 +1,67 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # lib/mount.rb
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            require 'fileutils'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            module Sgpg
         | 
| 8 | 
            +
              # (un)mount a device to a mountpoint
         | 
| 9 | 
            +
              class Mount
         | 
| 10 | 
            +
                # Argument 'disk' = full path of the disk.
         | 
| 11 | 
            +
                # e.g: /dev/mapper/sgpg for cryptsetup (See Sgpg::Cryptsetup) of just /dev/sdX
         | 
| 12 | 
            +
                def initialize(disk)
         | 
| 13 | 
            +
                  @user = ENV['USER'] || 'root'
         | 
| 14 | 
            +
                  @disk = disk
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def open
         | 
| 18 | 
            +
                  case Helper.auth?
         | 
| 19 | 
            +
                  when :root
         | 
| 20 | 
            +
                    open_with
         | 
| 21 | 
            +
                  when :doas
         | 
| 22 | 
            +
                    open_with 'doas'
         | 
| 23 | 
            +
                  when :sudo
         | 
| 24 | 
            +
                    open_with 'sudo'
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                def close
         | 
| 29 | 
            +
                  case Helper.auth?
         | 
| 30 | 
            +
                  when :root
         | 
| 31 | 
            +
                    close_with
         | 
| 32 | 
            +
                  when :doas
         | 
| 33 | 
            +
                    close_with 'doas'
         | 
| 34 | 
            +
                  when :sudo
         | 
| 35 | 
            +
                    close_with 'sudo'
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                protected
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                def open_with(prefix = '')
         | 
| 42 | 
            +
                  prefix == '' ? create_with_ruby : create_with_system(prefix)
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  raise "Unable to mount #{@disk}" unless
         | 
| 45 | 
            +
                    system(prefix, 'mount', '-t', 'ext4', @disk, Sgpg::MOUNTPOINT)
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                def close_with(prefix = '')
         | 
| 49 | 
            +
                  raise "Unable to umount #{@disk}" unless
         | 
| 50 | 
            +
                    system(prefix, 'umount', Sgpg::MOUNTPOINT)
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                private
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                def create_wiht_ruby
         | 
| 56 | 
            +
                  FileUtils.mkdir_p Sgpg::MOUNTPOINT
         | 
| 57 | 
            +
                  FileUtils.chown @user, @user, Sgpg::MOUNTPOINT, verbose: true
         | 
| 58 | 
            +
                  FileUtils.chmod 0644, Sgpg::MOUNTPOINT, verbose: true
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                def create_with_system(prefix)
         | 
| 62 | 
            +
                  system(prefix, 'mkdir', '-p', Sgpg::MOUNTPOINT)
         | 
| 63 | 
            +
                  system(prefix, 'chown', "#{@user}:#{@user}", Sgpg::MOUNTPOINT)
         | 
| 64 | 
            +
                  system(prefix, 'chmod', '0644', Sgpg::MOUNTPOINT)
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
              end
         | 
| 67 | 
            +
            end
         | 
    
        data/lib/sgpg/option.rb
    ADDED
    
    
    
        data/lib/sgpg/version.rb
    ADDED
    
    
| @@ -0,0 +1,68 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'yaml'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Sgpg
         | 
| 6 | 
            +
              # Manage the configuration file
         | 
| 7 | 
            +
              class YamlConfig
         | 
| 8 | 
            +
                attr_reader :config
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def initialize
         | 
| 11 | 
            +
                  @file_dir = "#{ENV['HOME']}/.config/sgpg"
         | 
| 12 | 
            +
                  @filename = 'config.yml'
         | 
| 13 | 
            +
                  @full_path = "#{@file_dir}/#{@filename}"
         | 
| 14 | 
            +
                  @config = {
         | 
| 15 | 
            +
                    disk: '',
         | 
| 16 | 
            +
                    keyname: '',
         | 
| 17 | 
            +
                    crypted: false
         | 
| 18 | 
            +
                  }
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                def load
         | 
| 22 | 
            +
                  if !Dir.exist?(@file_dir) || !File.exist?(@full_path)
         | 
| 23 | 
            +
                    create_dir
         | 
| 24 | 
            +
                    write_config unless File.exist?(@full_path)
         | 
| 25 | 
            +
                  else
         | 
| 26 | 
            +
                    loading
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def to_s
         | 
| 31 | 
            +
                  "Using disk >> #{@config[:disk]}, key >> #{@config[:keyname]}"
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def save(opts)
         | 
| 35 | 
            +
                  puts "saving options #{opts}"
         | 
| 36 | 
            +
                  @config[:disk] = opts[:disk] if opts[:disk]
         | 
| 37 | 
            +
                  @config[:keyname] = opts[:keyname] if opts[:keyname]
         | 
| 38 | 
            +
                  @config[:crypted] = opts[:crypted] if opts[:crypted]
         | 
| 39 | 
            +
                  puts "current #{config}"
         | 
| 40 | 
            +
                  write_config
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                protected
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                def loading
         | 
| 46 | 
            +
                  puts "Loading #{@full_path}..."
         | 
| 47 | 
            +
                  @config = YAML.load_file(@full_path)
         | 
| 48 | 
            +
                rescue Psych::SyntaxError => e
         | 
| 49 | 
            +
                  puts "YAML syntax error: #{e.message}"
         | 
| 50 | 
            +
                rescue Errno::ENOENT
         | 
| 51 | 
            +
                  puts "File not found: #{@full_path}"
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                private
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                def create_dir
         | 
| 57 | 
            +
                  return if Dir.exist? @file_dir
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  puts "Creating #{@file_dir}..."
         | 
| 60 | 
            +
                  Dir.mkdir @file_dir
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                def write_config
         | 
| 64 | 
            +
                  in_yaml = YAML.dump(@config)
         | 
| 65 | 
            +
                  File.write(@full_path, in_yaml)
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
              end
         | 
| 68 | 
            +
            end
         | 
    
        data/lib/sgpg.rb
    ADDED
    
    | @@ -0,0 +1,90 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative 'sgpg/version'
         | 
| 4 | 
            +
            require_relative 'sgpg/option'
         | 
| 5 | 
            +
            require_relative 'sgpg/yaml_config'
         | 
| 6 | 
            +
            require_relative 'sgpg/helper'
         | 
| 7 | 
            +
            require_relative 'sgpg/cryptsetup'
         | 
| 8 | 
            +
            require_relative 'sgpg/mount'
         | 
| 9 | 
            +
            require_relative 'sgpg/gpg'
         | 
| 10 | 
            +
            require_relative 'sgpg/archive'
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            # Manage your gpg key easyly
         | 
| 13 | 
            +
            module Sgpg
         | 
| 14 | 
            +
              def self.open(disk, is_crypted = false)
         | 
| 15 | 
            +
                return if Dir.glob("#{Sgpg::MOUNTPOINT}/*").length >= 1
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                puts "Open device #{disk}..."
         | 
| 18 | 
            +
                if is_crypted
         | 
| 19 | 
            +
                  Cryptsetup.new(disk).open
         | 
| 20 | 
            +
                  Mount.new('/dev/mapper/sgpg').open
         | 
| 21 | 
            +
                else
         | 
| 22 | 
            +
                  Mount.new(disk).open
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              def self.close(disk, is_crypted = false)
         | 
| 27 | 
            +
                if is_crypted
         | 
| 28 | 
            +
                  Mount.new('/dev/mapper/sgpg').close
         | 
| 29 | 
            +
                  Cryptsetup.new(disk).close
         | 
| 30 | 
            +
                else
         | 
| 31 | 
            +
                  Mount.new(disk).close
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
              end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
              def self.list_keys(keyname)
         | 
| 36 | 
            +
                keys = Dir.glob("#{Sgpg::MOUNTPOINT}/Persistent/#{keyname}*.tar")
         | 
| 37 | 
            +
                puts "Listing keys for #{keyname}..."
         | 
| 38 | 
            +
                keys = Dir.glob("#{Sgpg::MOUNTPOINT}/Persistent/*.tar") if keys == [] || keys == ''
         | 
| 39 | 
            +
                puts keys
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
              # argument 'suffix' = master or lesser
         | 
| 43 | 
            +
              def self.last_key(opts, suffix = 'master')
         | 
| 44 | 
            +
                Sgpg.open(opts[:disk], opts[:crypted])
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                keys = Dir.glob("#{Sgpg::KEYDIR}/#{opts[:keyname]}*#{suffix}*.tar").sort
         | 
| 47 | 
            +
                raise 'No keys found' unless keys.length >= 1
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                keys[0]
         | 
| 50 | 
            +
              end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
              def self.clear_keys
         | 
| 53 | 
            +
                return unless Dir.glob("#{Sgpg::WORKDIR}/*.key").length >= 1
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                print "Clearing keys located at #{Sgpg::WORKDIR}? (y/n) "
         | 
| 56 | 
            +
                choice = gets.chomp
         | 
| 57 | 
            +
                return unless choice.match(/^y|^Y/)
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                puts "Clearing #{Sgpg::WORKDIR}..."
         | 
| 60 | 
            +
                system("shred -u #{Sgpg::WORKDIR}/*.key")
         | 
| 61 | 
            +
              end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
              # Main logic here
         | 
| 64 | 
            +
              module Main
         | 
| 65 | 
            +
                # import and edit a gpg key from an archive (opts[:keypath])
         | 
| 66 | 
            +
                def self.edit_key(opts)
         | 
| 67 | 
            +
                  Sgpg.open(opts[:disk], opts[:crypted])
         | 
| 68 | 
            +
                  archive = Archive.new(opts[:keypath], opts[:keyname])
         | 
| 69 | 
            +
                  archive.extract
         | 
| 70 | 
            +
                  archive.import
         | 
| 71 | 
            +
                  Sgpg.clear_keys
         | 
| 72 | 
            +
                end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                # export your real keys and create an archive (tar)
         | 
| 75 | 
            +
                def self.export_secret(opts)
         | 
| 76 | 
            +
                  archive = Archive.new(opts[:keypath], opts[:keyname])
         | 
| 77 | 
            +
                  archive.create_master_tar
         | 
| 78 | 
            +
                  archive.move(Sgpg::KEYDIR)
         | 
| 79 | 
            +
                  Sgpg.clear_keys
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                # create an unpriviliged gpg key (no change can be made)
         | 
| 83 | 
            +
                def self.lesser_keys(opts)
         | 
| 84 | 
            +
                  archive = Archive.new(opts[:keypath], opts[:keyname])
         | 
| 85 | 
            +
                  archive.create_lesser_tar
         | 
| 86 | 
            +
                  archive.move(Sgpg::KEYDIR)
         | 
| 87 | 
            +
                  Sgpg.clear_keys
         | 
| 88 | 
            +
                end
         | 
| 89 | 
            +
              end
         | 
| 90 | 
            +
            end
         | 
    
        data/sgpg.gemspec
    ADDED
    
    | @@ -0,0 +1,45 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative 'lib/sgpg/version'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            # https://guides.rubygems.org/specification-reference/
         | 
| 6 | 
            +
            Gem::Specification.new do |s|
         | 
| 7 | 
            +
              s.name = 'sgpg'
         | 
| 8 | 
            +
              s.summary = 'Short gpg, tool for manage your gpg key (backup tarball, unprivileged keys, etc...)'
         | 
| 9 | 
            +
              s.version = Sgpg::VERSION
         | 
| 10 | 
            +
              s.platform = Gem::Platform::RUBY
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              s.description = <<-DESCRIPTION
         | 
| 13 | 
            +
                Short gpg, tool for manage your gpg key (backup tarball, unprivileged keys, etc...)
         | 
| 14 | 
            +
              DESCRIPTION
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              s.email = 'szorfein@protonmail.com'
         | 
| 17 | 
            +
              s.homepage = 'https://github.com/szorfein/sgpg'
         | 
| 18 | 
            +
              s.license = 'GPL-3'
         | 
| 19 | 
            +
              s.author = 'szorfein'
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              s.metadata = {
         | 
| 22 | 
            +
                'bug_tracker_uri' => 'https://github.com/szorfein/sgpg/issues',
         | 
| 23 | 
            +
                'changelog_uri' => 'https://github.com/szorfein/sgpg/blob/main/CHANGELOG.md',
         | 
| 24 | 
            +
                'source_code_uri' => 'https://github.com/szorfein/sgpg',
         | 
| 25 | 
            +
                'wiki_uri' => 'https://github.com/szorfein/sgpg/wiki',
         | 
| 26 | 
            +
                'funding_uri' => 'https://patreon.com/szorfein'
         | 
| 27 | 
            +
              }
         | 
| 28 | 
            +
             | 
| 29 | 
            +
              s.files = Dir.glob('{lib,bin}/**/*', File::FNM_DOTMATCH).reject { |f| File.directory?(f) }
         | 
| 30 | 
            +
              s.files += %w[CHANGELOG.md LICENSE README.md]
         | 
| 31 | 
            +
              s.files += %w[sgpg.gemspec]
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              s.bindir = 'bin'
         | 
| 34 | 
            +
              s.executables << 'sgpg'
         | 
| 35 | 
            +
              s.extra_rdoc_files = %w[README.md]
         | 
| 36 | 
            +
             | 
| 37 | 
            +
              # s.cert_chain = %w[certs/szorfein.pem]
         | 
| 38 | 
            +
              # s.signing_key = File.expand_path('~/.ssh/gem-private_key.pem')
         | 
| 39 | 
            +
             | 
| 40 | 
            +
              s.required_ruby_version = '>=2.6'
         | 
| 41 | 
            +
              s.requirements << 'gpg'
         | 
| 42 | 
            +
              s.requirements << 'cryptsetup'
         | 
| 43 | 
            +
              s.requirements << 'shred'
         | 
| 44 | 
            +
              s.requirements << 'tar'
         | 
| 45 | 
            +
            end
         | 
    
        data.tar.gz.sig
    ADDED
    
    | Binary file |