aspis 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 185a743d7eac9bf51be9f4fe226230f1f80c40a62a4bb0f0369220225df4b1ce
4
+ data.tar.gz: ae52af9fd222c6d38c749e0c768bc27f33f141e0dd628e750b689ed31e10eabd
5
+ SHA512:
6
+ metadata.gz: 36b039f303d773fef53aeeaef29d0991a30d7c4093d9e52e6d755e4876ce1c5836efdbfa3b348329b7d1101f6b9c6aa27853009ee2a353885a1c14af161cf22e
7
+ data.tar.gz: 2a987e228220b1996b5de171e05f1bdc91ad7f10892b65903df24c3ea30ae5f4153506421f562f336f19310a551042cc2e1e512ca4516e7a88feb5164db96021
Binary file
Binary file
@@ -0,0 +1 @@
1
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in aspis.gemspec
8
+ gemspec
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2020, Joseph Fierro <joseph.fierro@logosnetworks.com>
2
+
3
+ Permission to use, copy, modify, and/or distribute this software for any purpose
4
+ with or without fee is hereby granted, provided that the above copyright notice
5
+ and this permission notice appear in all copies.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
8
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
9
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
10
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
11
+ OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
12
+ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
13
+ THIS SOFTWARE.
@@ -0,0 +1,99 @@
1
+ ## aspis
2
+ aspis is an encryption filter gem that encrypts anything it receives from stdin and sends the ciphertext to stdout.
3
+ It relies on Libsodium (via RbNaCl) for its cryptographic primitives. Output is in JSON format.
4
+
5
+ - Cipher: XSalsa20-Poly1305
6
+ - Public Keys: Curve25519
7
+ - Key exchange: X25519 (static-static Diffie Hellman)
8
+ - Password-based key derivation: Argon2i
9
+ - Nonces are randomly generated.
10
+
11
+ ### Setup
12
+ aspis requires Libsodium and the Ruby gem RbNaCl.
13
+
14
+ To improve security, aspis is cryptographically signed.
15
+ It is strongly encouraged to verify this signature before installing the
16
+ aspis gem.
17
+
18
+ First, add my cert:
19
+ ```
20
+ gem cert --add <(curl -Ls https://raw.github.com/jsfierro/aspis/certs/jsfierro.pem)
21
+ ```
22
+ Then install the gem:
23
+ ```
24
+ gem install aspis -P MediumSecurity
25
+ ```
26
+ "MediumSecurity" will verify the signature on aspis, but not its dependencies in case they are
27
+ not signed. "HighSecurity" will require signatures on all dependencies as well.
28
+
29
+ Or to build and install from Github:
30
+ ```
31
+ $ git clone https://github.com/jsfierro/aspis
32
+ $ cd aspis
33
+ # bundle exec rake install
34
+ ```
35
+
36
+ ### Usage
37
+ Basic syntax:
38
+ ```
39
+ aspis [-n] [-o] [-m] -e|-d|-g
40
+ ```
41
+ The flags -e or -d tell aspis to encrypt or decrypt respectively.
42
+ The -g flag creates a new keypair in the ~/.aspis directory, creating that directory if necessary.
43
+
44
+ If used with the -n option, aspis will look for the password in the environment
45
+ variable ASPIS_PASS rather than asking for it on the command line. This is useful if
46
+ you want to use aspis in a script, but environment variables are not as secure as they may seem.
47
+ It is best to disable your shell's history if using this option to prevent something like
48
+ ```
49
+ export ASPIS_PASS=p@ssw0rd
50
+ ```
51
+ from appearing in the shell's history file.
52
+
53
+ To manually tweak Argon2i's CPU ops and memory parameters, use the -o and -m
54
+ options. Memory is specified in MiB.
55
+
56
+ Simple example of encrypting a message with a password-derived key:
57
+ ```
58
+ echo "hello" | aspis -e
59
+ ```
60
+ Encrypt file.pdf and write the output to file.enc:
61
+ ```
62
+ aspis -e file.pdf > file.enc
63
+ ```
64
+ Encrypt a file piped via cat(1) and send it over the network with netcat:
65
+ ```
66
+ cat file.pdf | aspis -e | nc 192.168.1.1 4444
67
+ ```
68
+ Alice encrypts a file for Bob, using his public key and her default private key located in ~/.aspis:
69
+ ```
70
+ aspis -e -p bob-pubkey message-for-bob.txt > message-for-bob.enc
71
+ ```
72
+ Bob would decrypt the above file like so:
73
+ ```
74
+ aspis -d -p alice-pubkey message-for-bob.enc > message-for-bob.txt
75
+ ```
76
+ To send an encrypted email to Bob, you can use aspis with mutt like so:
77
+ ```
78
+ echo "top secret message for Bob" | aspis -e -p bob-pubkey | mutt -s "Urgent, read immediately" bob@mailercorp.com
79
+ ```
80
+ Bob would then open the email in mutt, and press "|" to pipe the message to an external command. To decrypt, he would
81
+ grep for the aspis portion of the email, then pipe that into aspis:
82
+ ```
83
+ grep ciphertext | aspis -d -p alice-pubkey
84
+ ```
85
+ The now decrypted message body will be displayed.
86
+
87
+ ### Security considerations
88
+ aspis uses the NaCl crypto_box function underneath (via Libsodium) for its public key cryptography.
89
+ Key exchanges in asymmetric mode are static-static Diffie Hellman, which means that the shared key between 2 parties will be the same for all
90
+ of their communication. One desirable property of static-static DH (or "full static" as it is sometimes known) is that it provides
91
+ mutual authentication. Once Alice and Bob have each other's public keys, they can be sure that future messages 1.) can only be decrypted by Alice or Bob, and 2.) are actually from Alice or Bob.
92
+
93
+ The unfortunate part of this scheme is that their shared key is always the same, until one of them changes their long-term keys. This means that if an
94
+ attacker is able to compromise either Alice or Bob's key, it exposes all past messages between them. A scheme that provides forward secrecy would only expose messages encrypted with the compromised key, not all messages. However, this is harder to do for the asynchronous communication for which aspis is likely to be used.
95
+
96
+
97
+ I took the view that key impersonation is easier for an attacker and thus more likely than key compromise, and chose a scheme that provides mutual authentication to counter this. Yes, I am aware of its drawbacks.
98
+
99
+ Consult the NaCl paper for formal discussion of the cryptography used in aspis: https://cr.yp.to/highspeed/naclcrypto-20090310.pdf
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ task default: :spec
data/aspis.1 ADDED
@@ -0,0 +1,73 @@
1
+ .\"
2
+ .\"Copyright (c) 2020 Joseph Fierro <joseph.fierro@logosnetworks.com>
3
+ .\"
4
+ .\"Permission to use, copy, modify, and distribute this software for any
5
+ .\"purpose with or without fee is hereby granted, provided that the above
6
+ .\"copyright notice and this permission notice appear in all copies.
7
+ .\"
8
+ .\"THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9
+ .\"WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10
+ .\"MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11
+ .\"ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12
+ .\"WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13
+ .\"ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14
+ .\"OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
+ .Dd $Mdocdate: May 9 2020 $
16
+ .Dt ASPIS 1
17
+ .Os
18
+ .Sh NAME
19
+ .Nm aspis
20
+ .Nd Encryption filter utility
21
+ .Sh SYNOPSIS
22
+ .Nm aspis
23
+ .Fl h
24
+ .Nm aspis
25
+ .Op Fl n
26
+ .Op Fl o Ar ops
27
+ .Op Fl m Ar memory
28
+ .Op Fl p Ar public_key
29
+ .Op Fl k Ar private_key
30
+ .Fl e
31
+ .Fl d
32
+ .Fl g
33
+ .Sh DESCRIPTION
34
+ .Nm
35
+ is an encryption utility that encrypts anything it reads from stdin and sends the ciphertext to
36
+ stdout in JSON format. It relies on Libsodium (via RbNaCl) for its crypto primitives.
37
+
38
+ The following options select the operation:
39
+ .Bl -tag -width Dsssigfile
40
+ .It Fl h
41
+ Print out usage information and exit
42
+ .It Fl n
43
+ The nopass option. Looks for passphrases in the environment variable ASPIS_PASS
44
+ instead of asking for them on the command line. This is useful for embedding
45
+ .Nm
46
+ in scripts, but is less secure. It is recommended to disable shell history when using
47
+ this feature to prevent something like "export ASPIS_PASS=p@ssw0rd" from being written
48
+ to your shell's history file.
49
+ .It Fl e
50
+ Encrypt. Data will be read from stdin, and encrypted with XSalsa20-Poly1305
51
+ with a key derived via Argon2i. Ciphertext and the header info will be sent to stdout
52
+ in JSON format.
53
+ .It Fl d
54
+ Decrypt.
55
+ .It Fl g
56
+ Generate new key pair. This will look for the directory ~/.aspis, create it if necessary,
57
+ and create the files "public_key" and "private_key" there. The public_key can be freely distributed.
58
+ .Nm
59
+ will read from stdin and write the plaintext to stdout.
60
+ .It Fl o Ar ops
61
+ The ops parameter (number of iterations) for Argon2i. The default is 10, and minimum is 3.
62
+ .It Fl m Ar memory
63
+ The memory to be consumed by Argon2i, in MiB. Default is 1024, and minimum is 64.
64
+ .It Fl p Ar public_key
65
+ When encrypting, the recipient's public key. When decrypting a message sent to you, the sender's public key.
66
+ .It Fl k Ar private_key
67
+ Your private key. If this option is omitted,
68
+ .Nm
69
+ will look in the ~/.aspis directory for a file called "private_key" and use that by default.
70
+ .El
71
+ .Pp
72
+ .Sh AUTHOR
73
+ Joseph Fierro <joseph.fierro@logosnetworks.com>
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'aspis/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'aspis'
9
+ spec.version = Aspis::VERSION
10
+ spec.authors = ['Joseph Fierro']
11
+ spec.email = ['joseph.fierro@logosnetworks.com']
12
+ spec.cert_chain = ['certs/jsfierro.pem']
13
+ spec.signing_key = File.expand_path("~/.ssh/gem-private_key.pem") if $0 =~ /gem\z/
14
+
15
+ spec.summary = 'command line encryption tool using rbnacl'
16
+ spec.homepage = 'https://github.com/jsfierro/aspis'
17
+ spec.license = 'ISC'
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
+ end
24
+ spec.bindir = 'exe'
25
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ['lib']
27
+
28
+ spec.add_runtime_dependency 'rbnacl', '>= 7.0.0'
29
+ spec.add_development_dependency 'bundler', '>= 1.17'
30
+ spec.add_development_dependency 'rake', '>= 13.0.1'
31
+ end
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'aspis'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env sh
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,26 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIEbjCCAtagAwIBAgIBATANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDDCVqb3Nl
3
+ cGguZmllcnJvL0RDPWxvZ29zbmV0d29ya3MvREM9Y29tMB4XDTIwMDUxOTIzNDIx
4
+ MloXDTIxMDUxOTIzNDIxMlowMDEuMCwGA1UEAwwlam9zZXBoLmZpZXJyby9EQz1s
5
+ b2dvc25ldHdvcmtzL0RDPWNvbTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoC
6
+ ggGBAKMlytqAlJVr/1C2pfb9ecMSd9RSHhKB0FtBT19jUvGs5drds4vZOywVdVf6
7
+ BEstZSnqUfwXkC8Y96nlYBlb7oVxHnNhoYijsxxAHDVJlhNvj1+zZMMtpgwdxCW8
8
+ Ei9pJcVpIIABY31bG2ribxMoxThplGBmHeF+kMwszvH1EXGZRd+CZ8/7hgV27j4w
9
+ Got0/6fxMN+OkfhA75sGjF5b7CiXqG290aLa5ts2xD81vBVUQz3X4wcddZe/TCEX
10
+ GvJjDQi78DoBbjOZUmbqTFBA7+rUJhTjfjnJ7jKfxPcTjkUZWX6egZSLHEyzYhdk
11
+ XjG8R6YanIEA0mhJZ0v7ood8diNii7TrvdcrkmzRxQW1Unp0sa17rNIoxehDE5fa
12
+ ZVRWytp52okUVbMYBoV9Ukg5pvJXU8EAfIf4yclc/PMV2k2qaxfPFxdBhma9hrlF
13
+ FDVGlM6SGzVFggpD04a4OWATqWvjoT2Nl9H9o3PRX/Vq8Iiv6r+JnMcC09tG2O+h
14
+ i48HwQIDAQABo4GSMIGPMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW
15
+ BBQaUvmMaP4VQp+k+WduTXxeEkCQJDAqBgNVHREEIzAhgR9qb3NlcGguZmllcnJv
16
+ QGxvZ29zbmV0d29ya3MuY29tMCoGA1UdEgQjMCGBH2pvc2VwaC5maWVycm9AbG9n
17
+ b3NuZXR3b3Jrcy5jb20wDQYJKoZIhvcNAQELBQADggGBADmpMf6ax1fn549BNxOT
18
+ BrErMUCmxil+X+4J3JYdHqa2xFSc6l032TfNkt+uuXoBBIGJHw2rqZj6Su8fxpjU
19
+ UCX5ZRY7flIEdFwSP4KrcVb8E7dQE7VQaU2UU/DUdds2Sec8g3b3oXuEMcUQITIK
20
+ NF85RYv9WRUyRLI7yqs5HqGxxeLtYRZ9vaqNzMs/h658QfPmFWqsWL73Dx808jvx
21
+ 2pKrAvcVNEJ9mNI5SKy/6VNHzBBMTn1JBgTCEdpos5vMuA0PMQlgvc3GiPHw3gLH
22
+ T9FE7HMc1aiO9utsli+EMOJ9wr4TLoN7e3J4ndH8jGg/46VbqIVGL/jQsFQCDqKI
23
+ nM4hcwmd/0ss5wQBOx+5n36nR+Adb1HHOVXdX2P5ktxUEd4Xkt51q+NxO//RYngB
24
+ GJRqwrLlLH1jf+y9mULfzRHWpAl2aXcxCW57d/+fCCeRz7+v9Qz/Eq2ieKucVeNi
25
+ sEIE5PVGOPAiOJxBYbphEwugUb/AFjziPepwMN0jDLx6CQ==
26
+ -----END CERTIFICATE-----
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'aspis'
5
+
6
+ AspisInit.init
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'aspis/version'
4
+ require 'aspis/asymmetric_encrypt'
5
+ require 'aspis/asymmetric_decrypt'
6
+ require 'aspis/symmetric_encrypt'
7
+ require 'aspis/symmetric_decrypt'
8
+ require 'aspis/generate_keys'
9
+ require 'aspis/aspis_init'
10
+
11
+ aspis_init if __FILE__ == $PROGRAM_NAME
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2020 Joseph Fierro <joseph.fierro@logosnetworks.com>
4
+ #
5
+ # Permission to use, copy, modify, and distribute this software for any
6
+ # purpose with or without fee is hereby granted, provided that the above
7
+ # copyright notice and this permission notice appear in all copies.
8
+ #
9
+ # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
+ require 'optparse'
17
+
18
+ require_relative 'asymmetric_encrypt.rb'
19
+ require_relative 'asymmetric_decrypt.rb'
20
+ require_relative 'symmetric_encrypt.rb'
21
+ require_relative 'symmetric_decrypt.rb'
22
+ require_relative 'generate_keys.rb'
23
+ require_relative 'version.rb'
24
+
25
+ module AspisInit
26
+ def self.init
27
+ options = {}
28
+ OptionParser.new do |opts|
29
+ opts.banner = "Encryption filter utility.\n"
30
+
31
+ opts.version = Aspis::VERSION
32
+
33
+ opts.on('-e', '--encrypt', 'Encrypt') do
34
+ options[:mode] = 'encrypt'
35
+ end
36
+
37
+ opts.on('-d', '--decrypt', 'Decrypt') do
38
+ options[:mode] = 'decrypt'
39
+ end
40
+
41
+ opts.on('-g', '--generate', 'Generate key pair') do
42
+ options[:mode] = 'generate'
43
+ end
44
+
45
+ opts.on('-n', '--nopass', 'Use env variable ASPIS_PASS for passphrase') do
46
+ options[:ask_pass] = false
47
+ end
48
+
49
+ opts.on('-p', '--pubkey public_key', String, "Recipient's public key") do |p|
50
+ options[:public_key] = p
51
+ end
52
+
53
+ opts.on('-k', '--privatekey private_key', String, "Sender's private key") do |k|
54
+ options[:private_key] = k
55
+ end
56
+
57
+ opts.on('-o', '--opslimit opslimit', Integer, 'Argon2i ops parameter') do |o|
58
+ options[:opslimit] = o
59
+ end
60
+
61
+ opts.on('-m', '--memlimit memlimit', Integer, 'Argon2i memory in MiB') do |m|
62
+ options[:memlimit] = m
63
+ end
64
+
65
+ opts.on('-h', '--help', 'Displays help') do
66
+ puts opts
67
+ exit
68
+ end
69
+ end.parse!
70
+
71
+ abort('Fatal error: must enter -g, -e, or -d') unless options[:mode]
72
+
73
+ run(options)
74
+ end
75
+
76
+ def self.run(options)
77
+ if options[:public_key]
78
+ unless options[:private_key]
79
+ aspis_dir = File.expand_path('~/.aspis')
80
+ options[:private_key] = aspis_dir + '/private_key'
81
+ end
82
+ end
83
+
84
+ case options[:mode]
85
+ when 'encrypt'
86
+ if options[:public_key]
87
+ puts AsymmetricEncrypt.encrypt(ARGF.read, options[:public_key], options[:private_key], options[:ask_pass])
88
+ else
89
+ puts SymmetricEncrypt.encrypt(ARGF.read, options[:opslimit], options[:memlimit], options[:ask_pass])
90
+ end
91
+ when 'decrypt'
92
+ if options[:public_key]
93
+ puts AsymmetricDecrypt.decrypt(ARGF.read, options[:public_key], options[:private_key], options[:ask_pass])
94
+ else
95
+ puts SymmetricDecrypt.decrypt(ARGF.read, options[:ask_pass])
96
+ end
97
+ when 'generate'
98
+ GenerateKeys.generate(options[:opslimit], options[:memlimit], options[:ask_pass])
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rbnacl'
4
+ require 'fileutils'
5
+ require 'base64'
6
+ require 'json'
7
+
8
+ require_relative 'symmetric_decrypt.rb'
9
+
10
+ module AsymmetricDecrypt
11
+ def self.decrypt(input, public_key, private_key, ask_pass)
12
+ sender_public_key = File.read(public_key)
13
+ sender_public_key = Base64.decode64(sender_public_key)
14
+
15
+ recipient_private_key = File.read(private_key)
16
+ recipient_private_key = SymmetricDecrypt.decrypt(recipient_private_key, ask_pass)
17
+
18
+ box = RbNaCl::SimpleBox.from_keypair(sender_public_key, recipient_private_key)
19
+
20
+ input = JSON.parse(input)
21
+ ciphertext = input['ciphertext']
22
+ ciphertext = Base64.decode64(ciphertext)
23
+ box.decrypt(ciphertext)
24
+ end
25
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rbnacl'
4
+ require 'fileutils'
5
+ require 'base64'
6
+ require 'json'
7
+
8
+ require_relative 'symmetric_decrypt.rb'
9
+ require_relative 'version.rb'
10
+
11
+ module AsymmetricEncrypt
12
+ def self.encrypt(plaintext, public_key, private_key, ask_pass)
13
+ recipient_public_key = File.read(public_key)
14
+ recipient_public_key = Base64.decode64(recipient_public_key)
15
+
16
+ sender_private_key = File.read(private_key)
17
+ sender_private_key = SymmetricDecrypt.decrypt(sender_private_key, ask_pass)
18
+
19
+ box = RbNaCl::SimpleBox.from_keypair(recipient_public_key, sender_private_key)
20
+ ciphertext = box.encrypt(plaintext)
21
+ ciphertext = Base64.strict_encode64(ciphertext)
22
+
23
+ output = { version: Aspis::VERSION,
24
+ ciphertext: ciphertext }
25
+ JSON.generate(output)
26
+ end
27
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rbnacl'
4
+ require 'fileutils'
5
+ require 'base64'
6
+
7
+ require_relative 'symmetric_encrypt.rb'
8
+
9
+ module GenerateKeys
10
+ def self.generate(opslimit, memlimit, ask_pass)
11
+ aspis_dir = File.expand_path('~/.aspis')
12
+ FileUtils.mkdir_p(aspis_dir) unless Dir.exist?(aspis_dir)
13
+
14
+ private_key = RbNaCl::PrivateKey.generate
15
+ public_key = private_key.public_key
16
+ public_key = Base64.strict_encode64(public_key)
17
+
18
+ # Encrypt private key before writing to disk
19
+ private_key = SymmetricEncrypt.encrypt(private_key, opslimit, memlimit, ask_pass)
20
+
21
+ File.write(aspis_dir + '/private_key', private_key)
22
+ File.write(aspis_dir + '/public_key', public_key)
23
+ puts('Keys created in ~/.aspis')
24
+ end
25
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rbnacl'
4
+ require 'json'
5
+ require 'base64'
6
+
7
+ module SymmetricDecrypt
8
+ def self.decrypt(input, ask_pass)
9
+ input = JSON.parse(input)
10
+
11
+ salt = input['salt']
12
+ salt = Base64.decode64(salt)
13
+
14
+ ops = input['ops']
15
+ mem = input['mem']
16
+ key_size = input['key_size']
17
+
18
+ ciphertext = input['ciphertext']
19
+ ciphertext = Base64.decode64(ciphertext)
20
+
21
+ password = if ask_pass == false
22
+ ENV['ASPIS_PASS']
23
+ else
24
+ IO.console.getpass 'Enter passphrase: '
25
+ end
26
+
27
+ key = RbNaCl::PasswordHash.argon2i(
28
+ password,
29
+ salt,
30
+ ops,
31
+ mem,
32
+ key_size
33
+ )
34
+
35
+ box = RbNaCl::SimpleBox.from_secret_key(key)
36
+ box.decrypt(ciphertext)
37
+ end
38
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rbnacl'
4
+ require 'json'
5
+ require 'base64'
6
+ require 'io/console'
7
+
8
+ module SymmetricEncrypt
9
+ def self.timingsafe_compare(secret1, secret2)
10
+ check = secret1.bytesize ^ secret2.bytesize
11
+ secret1.bytes.zip(secret2.bytes) { |x, y| check |= x ^ y.to_i }
12
+ check.zero?
13
+ end
14
+
15
+ def self.kdf_gen(salt, opslimit, memlimit, ask_pass)
16
+ if ask_pass == false
17
+ password = ENV['ASPIS_PASS']
18
+ else
19
+ password = IO.console.getpass('Enter passphrase: ')
20
+ password2 = IO.console.getpass('Confirm passphrase: ')
21
+ unless timingsafe_compare(password, password2)
22
+ abort('Passphrases do not match')
23
+ end
24
+ end
25
+
26
+ RbNaCl::PasswordHash.argon2i(
27
+ password,
28
+ salt,
29
+ opslimit,
30
+ memlimit,
31
+ 32
32
+ )
33
+ end
34
+
35
+ def self.encrypt(plaintext, opslimit, memlimit, ask_pass)
36
+ opslimit ||= 10
37
+ abort('Error: KDF opslimit must be >= 3') if opslimit < 3
38
+
39
+ memlimit ||= 1024
40
+ abort('Error: KDF memory must be >= 64 MiB') if memlimit < 64
41
+ memlimit = 1_048_576 * memlimit
42
+
43
+ salt = RbNaCl::Random.random_bytes(RbNaCl::PasswordHash::Argon2::SALTBYTES)
44
+
45
+ key = kdf_gen(salt, opslimit, memlimit, ask_pass)
46
+
47
+ box = RbNaCl::SimpleBox.from_secret_key(key)
48
+ ciphertext = box.encrypt(plaintext)
49
+
50
+ ciphertext = Base64.strict_encode64(ciphertext)
51
+ salt = Base64.strict_encode64(salt)
52
+
53
+ output = { version: Aspis::VERSION,
54
+ salt: salt,
55
+ ops: opslimit,
56
+ mem: memlimit,
57
+ key_size: 32,
58
+ ciphertext: ciphertext }
59
+
60
+ JSON.generate(output)
61
+ end
62
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aspis
4
+ VERSION = '0.1.0'
5
+ end
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aspis
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Joseph Fierro
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIEbjCCAtagAwIBAgIBATANBgkqhkiG9w0BAQsFADAwMS4wLAYDVQQDDCVqb3Nl
14
+ cGguZmllcnJvL0RDPWxvZ29zbmV0d29ya3MvREM9Y29tMB4XDTIwMDUxOTIzNDIx
15
+ MloXDTIxMDUxOTIzNDIxMlowMDEuMCwGA1UEAwwlam9zZXBoLmZpZXJyby9EQz1s
16
+ b2dvc25ldHdvcmtzL0RDPWNvbTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoC
17
+ ggGBAKMlytqAlJVr/1C2pfb9ecMSd9RSHhKB0FtBT19jUvGs5drds4vZOywVdVf6
18
+ BEstZSnqUfwXkC8Y96nlYBlb7oVxHnNhoYijsxxAHDVJlhNvj1+zZMMtpgwdxCW8
19
+ Ei9pJcVpIIABY31bG2ribxMoxThplGBmHeF+kMwszvH1EXGZRd+CZ8/7hgV27j4w
20
+ Got0/6fxMN+OkfhA75sGjF5b7CiXqG290aLa5ts2xD81vBVUQz3X4wcddZe/TCEX
21
+ GvJjDQi78DoBbjOZUmbqTFBA7+rUJhTjfjnJ7jKfxPcTjkUZWX6egZSLHEyzYhdk
22
+ XjG8R6YanIEA0mhJZ0v7ood8diNii7TrvdcrkmzRxQW1Unp0sa17rNIoxehDE5fa
23
+ ZVRWytp52okUVbMYBoV9Ukg5pvJXU8EAfIf4yclc/PMV2k2qaxfPFxdBhma9hrlF
24
+ FDVGlM6SGzVFggpD04a4OWATqWvjoT2Nl9H9o3PRX/Vq8Iiv6r+JnMcC09tG2O+h
25
+ i48HwQIDAQABo4GSMIGPMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQW
26
+ BBQaUvmMaP4VQp+k+WduTXxeEkCQJDAqBgNVHREEIzAhgR9qb3NlcGguZmllcnJv
27
+ QGxvZ29zbmV0d29ya3MuY29tMCoGA1UdEgQjMCGBH2pvc2VwaC5maWVycm9AbG9n
28
+ b3NuZXR3b3Jrcy5jb20wDQYJKoZIhvcNAQELBQADggGBADmpMf6ax1fn549BNxOT
29
+ BrErMUCmxil+X+4J3JYdHqa2xFSc6l032TfNkt+uuXoBBIGJHw2rqZj6Su8fxpjU
30
+ UCX5ZRY7flIEdFwSP4KrcVb8E7dQE7VQaU2UU/DUdds2Sec8g3b3oXuEMcUQITIK
31
+ NF85RYv9WRUyRLI7yqs5HqGxxeLtYRZ9vaqNzMs/h658QfPmFWqsWL73Dx808jvx
32
+ 2pKrAvcVNEJ9mNI5SKy/6VNHzBBMTn1JBgTCEdpos5vMuA0PMQlgvc3GiPHw3gLH
33
+ T9FE7HMc1aiO9utsli+EMOJ9wr4TLoN7e3J4ndH8jGg/46VbqIVGL/jQsFQCDqKI
34
+ nM4hcwmd/0ss5wQBOx+5n36nR+Adb1HHOVXdX2P5ktxUEd4Xkt51q+NxO//RYngB
35
+ GJRqwrLlLH1jf+y9mULfzRHWpAl2aXcxCW57d/+fCCeRz7+v9Qz/Eq2ieKucVeNi
36
+ sEIE5PVGOPAiOJxBYbphEwugUb/AFjziPepwMN0jDLx6CQ==
37
+ -----END CERTIFICATE-----
38
+ date: 2020-05-20 00:00:00.000000000 Z
39
+ dependencies:
40
+ - !ruby/object:Gem::Dependency
41
+ name: rbnacl
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 7.0.0
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 7.0.0
54
+ - !ruby/object:Gem::Dependency
55
+ name: bundler
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '1.17'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '1.17'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rake
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 13.0.1
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: 13.0.1
82
+ description:
83
+ email:
84
+ - joseph.fierro@logosnetworks.com
85
+ executables:
86
+ - aspis
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - Gemfile
92
+ - LICENSE.txt
93
+ - README.md
94
+ - Rakefile
95
+ - aspis.1
96
+ - aspis.gemspec
97
+ - bin/console
98
+ - bin/setup
99
+ - certs/jsfierro.pem
100
+ - exe/aspis
101
+ - lib/aspis.rb
102
+ - lib/aspis/aspis_init.rb
103
+ - lib/aspis/asymmetric_decrypt.rb
104
+ - lib/aspis/asymmetric_encrypt.rb
105
+ - lib/aspis/generate_keys.rb
106
+ - lib/aspis/symmetric_decrypt.rb
107
+ - lib/aspis/symmetric_encrypt.rb
108
+ - lib/aspis/version.rb
109
+ homepage: https://github.com/jsfierro/aspis
110
+ licenses:
111
+ - ISC
112
+ metadata: {}
113
+ post_install_message:
114
+ rdoc_options: []
115
+ require_paths:
116
+ - lib
117
+ required_ruby_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ required_rubygems_version: !ruby/object:Gem::Requirement
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ requirements: []
128
+ rubygems_version: 3.0.3
129
+ signing_key:
130
+ specification_version: 4
131
+ summary: command line encryption tool using rbnacl
132
+ test_files: []
Binary file