scl 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 043a75c0ec7aab24c5bde01c147e009b7e0bc58f
4
+ data.tar.gz: 3e1bf4954baccfab324591cddcbaa1fc60f14dab
5
+ SHA512:
6
+ metadata.gz: eb615f84d442a67a169c804f99a2a243553c99055879a52d7479e96742f43142e69da4971657f6ead3d19bea5584f3722553e7b64193f563507e6950fe19dd2a
7
+ data.tar.gz: c0f3e20309a25ff69d1e4ebb2dcfaf8e2275fee1ebd7fa48cbefeb6d379ad9a4fb1b72e0d3c2b64cceb593e26755e065be4c3bdb40095533ed71a7ab8e430c6b
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at wouter@youdo.co.nz. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in scl.gemspec
6
+ gemspec
data/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # Scl (Simple Crypto Library)
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/scl`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'scl'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install scl
22
+
23
+ ## CLI
24
+
25
+ SCL Comes with a command line interface that allows you to perform basic encryption, decryption and key generation and key negotiation operations.
26
+
27
+ Example usage:
28
+
29
+ ```
30
+ RSA:
31
+ scl rsa generate # Generate an RSA key-pair > prints to stdout in base64
32
+ scl rsa generate --size=2048 --out=/tmp/keypair # Generate an RSA key-pair with size 2048 and
33
+ # output binary as binary encoded.
34
+ # Save to path /tmp/keypair
35
+
36
+ scl rsa verify --pub-key=/tmp/keypair/rsa.pub /path/to/file "signature" # Verify the signature of a file using a public key
37
+ scl rsa sign --priv-key=/tmp/keypair/rsa.priv /path/to/file # Generate a signature, outputs to stdout
38
+ scl rsa encrypt --key=/tmp/keypair/rsa.[priv|pub] /path/to/file # Encrypt a file, output to stdout
39
+ scl rsa decrypt --key=/tmp/keypair/rsa.[priv|pub] /path/to/file # Decrypt a file, output to stdout
40
+
41
+ encrypt and decrypt both accept optional --cipher-size/-cs arguments
42
+ All rsa actions accept --format/--input-format/--output-format -f/-if/-of arguments
43
+
44
+ DH:
45
+ scl dh ping # Start a diffie hellman key-generation
46
+ scl dh pong [der] [public-key] # Complete side-1 of a diffie hellman key-generation
47
+ scl dh done [der] [public-key] [priv-key] # Complete side-2 of a diffie hellman key-generation
48
+
49
+ AES:
50
+ scl aes encrypt # Encrypt from stdin
51
+ scl aes encrypt [file] # Encrypt from file
52
+ scl aes encrypt --key="abc" [file] # Enc
53
+ ```
54
+
55
+ ## Usage
56
+
57
+ TODO: Write usage instructions here
58
+
59
+ ## Development
60
+
61
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
62
+
63
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
64
+
65
+ ## Contributing
66
+
67
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/scl. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
68
+
69
+ ## Code of Conduct
70
+
71
+ Everyone interacting in the Scl project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/scl/blob/master/CODE_OF_CONDUCT.md).
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "scl"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ require 'pry-byebug'
11
+ Pry.start
12
+
13
+ # require "irb"
14
+ # IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
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
data/exe/scl ADDED
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "scl"
5
+ require 'optparse'
6
+ require 'pry-byebug'
7
+
8
+ Options = Struct.new(
9
+ :input_format,
10
+ :output_format,
11
+ :key_format,
12
+ :module,
13
+ :action,
14
+ :verbose,
15
+ :key_path,
16
+ :key_size,
17
+ :private_key_file,
18
+ :public_key_file,
19
+ :output_file,
20
+ :min_shares,
21
+ :num_shares,
22
+ :block_size,
23
+ :block_cipher,
24
+ :digest,
25
+ :help,
26
+ :opts
27
+ )
28
+
29
+ args = Options.new()
30
+ opt_parser = OptionParser.new do |opts|
31
+ opts.banner = "Usage: scl [mode] [command] [options] "
32
+
33
+ opts.separator ""
34
+ opts.separator "Where mode is one of (aes, rsa, dh, sss, digest)"
35
+ opts.separator "try scl [mode] -h or scl [mode] [command] -h for more details"
36
+ opts.separator ""
37
+ opts.separator "Where options are:"
38
+
39
+ opts.on("-v", "--verbose", "Verbose output") do
40
+ args.verbose = true
41
+ end
42
+
43
+ opts.on(%i(words qrcode base64 binary hex stdout), "-f [FORMAT]", "--format [=FORMAT]", "Format to use for output, one of base64, qrcode, words, hex, stdout, none") do |f|
44
+ puts "Selected output format #{f}" if args.verbose
45
+ args.output_format = f
46
+ end
47
+
48
+ opts.on(%i(words qrcode base64 binary hex), "-i [INPUT_FORMAT]", "--input-format [=INPUT_FORMAT]", "Format to use for input, one of base64, qrcode, words, hex, none") do |f|
49
+ puts "Selected input format #{f}" if args.verbose
50
+ args.input_format = f
51
+ end
52
+
53
+ opts.on(%i(words qrcode base64 binary hex), "-b [KEY_FORMAT]", "--key-format [=KEY_FORMAT]", "Format to use for keys, one of base64, qrcode, words, hex, none") do |f|
54
+ puts "Selected key format #{f}" if args.verbose
55
+ args.key_format = f
56
+ end
57
+
58
+ opts.on("-k [KEY_PATH]", "--key-path [=KEY_PATH]", "The path of the key(s) to use (Looks for [key_path].pub and [key_path].priv)") do |p|
59
+ puts "Selected key path #{p}" if args.verbose
60
+ args.key_path = p
61
+ end
62
+
63
+ opts.on("-Z [PRIVATE_KEY]", "--priv-key [=PRIVATE_KEY]", "Private key file to use") do |p|
64
+ puts "Using private key file #{p}" if args.verbose
65
+ args.private_key_file = p
66
+ end
67
+
68
+ opts.on("-p [PUBLIC_KEY]", "--pub-key [=PUBLIC_KEY]", "Public key file to use") do |p|
69
+ puts "Using public key file #{p}" if args.verbose
70
+ args.public_key_file = p
71
+ end
72
+
73
+ opts.on("-o [OUTPUT_FILE]", "--out [=OUTPUT_FILE]", "File or file-prefix for where to save outputs") do |p|
74
+ puts "Using output file #{p}" if args.verbose
75
+ args.output_file = p
76
+ end
77
+
78
+ opts.on("-s [KEY_SIZE]", "--key-size [=KEY_SIZE]", Integer, "Size of the generated key") do |ks|
79
+ puts "Using key size #{ks}" if args.verbose
80
+ args.key_size = ks
81
+ end
82
+
83
+ opts.on("-m [MIN_SHARES]", "--min-shares [=MIN_SHARES]", Integer, "Size of the generated key") do |ms|
84
+ puts "Using min shares #{ms}" if args.verbose
85
+ args.min_shares = ms
86
+ end
87
+
88
+ opts.on("-n [NUM_SHARES]", "--num-shares [=NUM_SHARES]", Integer, "Size of the generated key") do |ns|
89
+ puts "Using num shares #{ns}" if args.verbose
90
+ args.num_shares = ns
91
+ end
92
+
93
+ opts.on("-S [BLOCK_SIZE]", "--block-size [=BLOCK_SIZE]", Integer, "Block size of cipher") do |bs|
94
+ puts "Using block size #{bs}" if args.verbose
95
+ args.block_size = bs
96
+ end
97
+
98
+ opts.on("-C [BLOCK_CIPHER]", "--block-size [=BLOCK_CIPHER]", String, "Block cipher") do |bc|
99
+ puts "Using block cipher #{bc}" if args.verbose
100
+ args.block_cipher = bc
101
+ end
102
+
103
+ opts.on('-d [DIGEST]', '--digest [=DIGEST]', String, 'Digest (e.g. sha256)') do |dg|
104
+ puts "Using digest #{dg}" if args.verbose
105
+ args.digest = dg
106
+ end
107
+
108
+ opts.on("-h", "--help", "Prints this help") do
109
+ args.help = true
110
+ end
111
+ args.opts = opts
112
+
113
+ end
114
+
115
+ opt_parser.parse!
116
+
117
+ unless ARGV.any?
118
+ puts args.opts
119
+ exit(0)
120
+ else
121
+ Scl::Control::Controller.new(args)
122
+ .module(ARGV.shift)
123
+ .action(ARGV.shift, ARGV)
124
+ end
data/lib/scl/aes.rb ADDED
@@ -0,0 +1,28 @@
1
+ module Scl
2
+ class AES
3
+ def initialize(block_size=256, block_cipher=:CBC)
4
+ @block_cipher = block_cipher || :CBC
5
+ @block_size = block_size || 256
6
+ end
7
+
8
+ def build_cypher
9
+ OpenSSL::Cipher::AES.new(@block_size, @block_cipher)
10
+ end
11
+
12
+ def encrypt(plaintext, key=nil, iv=nil)
13
+ block_cipher = build_cypher
14
+ block_cipher.encrypt
15
+ block_cipher.key = key ||= block_cipher.random_key
16
+ block_cipher.iv = iv ||= block_cipher.random_iv
17
+ [block_cipher.update(plaintext) + block_cipher.final, key, iv]
18
+ end
19
+
20
+ def decrypt(ciphertext, key, iv)
21
+ block_cipher = build_cypher
22
+ block_cipher.decrypt
23
+ block_cipher.key = key
24
+ block_cipher.iv = iv
25
+ block_cipher.update( ciphertext ) + block_cipher.final
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,51 @@
1
+ module Scl
2
+ module Control
3
+ class AES < ControllerModule
4
+
5
+ help <<-HELP,
6
+ encrypt. Encrypt a given file.
7
+ Can optionally be given an existing key, otherwise a unique one will be generated alongside
8
+ the cipher text.
9
+ e.g
10
+ scl aes encrypt somefile
11
+ scl aes encrypt somefile -k somekey
12
+ HELP
13
+ def encrypt(file)
14
+ input_key = args.key_path ?
15
+ read_file(args.key_path) :
16
+ nil
17
+ file_content = read_file(file)
18
+ ct, key, iv = aes.encrypt(file_content, input_key)
19
+ args.output_file ||= file
20
+ controller.output(
21
+ Output.new(iv << '::' << ct, ".enc"),
22
+ input_key ? nil : Output.new(key, '.key')
23
+ )
24
+ end
25
+
26
+
27
+ help <<-HELP,
28
+ decrypt. Decrypt a given file.
29
+ Must be given a key using -k/--key-path
30
+ e.g
31
+ scl aes decrypt somefile.enc -k somekey.key
32
+ scl aes decrypt somefile.enc -k somekey.key -o output.txt
33
+ HELP
34
+ def decrypt(file)
35
+ key = input_decoder.decode(read_file(args.key_path, "encryption key", "Use -k option"))
36
+ iv, cipher_text = input_decoder.decode(read_file(file, 'ciphertext')).split('::', 2)
37
+
38
+ plaintext = aes.decrypt(cipher_text, key, iv)
39
+ args.output_format = 'binary'
40
+ controller.output(
41
+ Output.new(plaintext, '')
42
+ )
43
+ end
44
+
45
+ private
46
+ def aes
47
+ Scl::AES.new(args.block_size, args.block_cipher)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,96 @@
1
+ module Scl
2
+ module Control
3
+ class Controller
4
+ attr_reader :args
5
+ def initialize(args)
6
+ @args = args
7
+ puts "Using output format #{output_encoder.name}" if verbose?
8
+ puts "Using input format #{input_decoder.name}" if verbose?
9
+ end
10
+
11
+ def output_encoder
12
+ coder_for(args.output_format)
13
+ end
14
+
15
+ def input_decoder
16
+ coder_for(args.input_format)
17
+ end
18
+
19
+ def key_coder
20
+ coder_for(args.key_format)
21
+ end
22
+
23
+ def output_file
24
+ "#{@args.output_file}".strip
25
+ end
26
+
27
+ def verbose?
28
+ @args.verbose
29
+ end
30
+
31
+ def module(module_name)
32
+ case module_name
33
+ when "aes" then Control::AES.new(self)
34
+ when "rsa" then Control::RSA.new(self)
35
+ when "dh" then Control::DH.new(self)
36
+ when "sss" then Control::SSS.new(self)
37
+ when "digest" then Control::Digest.new(self)
38
+ else
39
+ puts "No scl module found \"#{module_name}\""
40
+ puts args.opts
41
+ exit(1)
42
+ end
43
+ end
44
+
45
+ def coder_for(format)
46
+ case "#{format}".strip
47
+ when "base64" then Format::BASE64
48
+ when "qrcode" then Format::QRCODE
49
+ when "base64" then Format::BASE64
50
+ when "words" then Format::WORDS
51
+ when "hex" then Format::HEX
52
+ when "binary","text","none" then Format::BINARY
53
+ when "", "auto" then Format::AUTO
54
+ when "stdout" then Format::STDOUT
55
+ else
56
+ puts "Unexpected format \"#{format}\""
57
+ exit(1)
58
+ end
59
+ end
60
+
61
+ def output(*results)
62
+ case output_file
63
+ when '' then
64
+ puts "\n\n"
65
+ puts results.compact.map{|r| output_encoder.encode(r.content) }.join("\n\n")
66
+ else
67
+ results.compact.each do |result|
68
+ puts "Writing #{result.file(output_file)}" if verbose?
69
+ if args.output_format == 'stdout'
70
+ output_encoder.encode(result.content)
71
+ else
72
+ IO.write(
73
+ result.file(output_file),
74
+ output_encoder.encode(result.content)
75
+ ) if confirm_overwrite?(result.file(output_file))
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ def confirm_overwrite?(filename)
82
+ if File.exists?(filename)
83
+ puts "File #{filename} already exists. Confirm overwrite? [Yn]"
84
+ if gets.strip.downcase == 'y'
85
+ puts "Confirmed overwrite for #{filename}" if verbose?
86
+ return true
87
+ else
88
+ puts "Skipping save for #{filename}" if verbose?
89
+ return false
90
+ end
91
+ end
92
+ return true
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,102 @@
1
+ module Scl
2
+ module Control
3
+ class ControllerModule
4
+ attr_reader :controller
5
+ def initialize(controller)
6
+ @controller = controller
7
+ end
8
+
9
+ def args
10
+ @controller.args
11
+ end
12
+
13
+ def input_decoder
14
+ @controller.input_decoder
15
+ end
16
+
17
+ def output_encoder
18
+ @controller.output_encoder
19
+ end
20
+
21
+ def key_coder
22
+ @controller.key_coder
23
+ end
24
+
25
+ def help?
26
+ args.help
27
+ end
28
+
29
+ def self.help(message, method)
30
+ @@help ||= {}
31
+ @@help[self.name] ||= {}
32
+ @@help[self.name][method.to_s] = message
33
+ end
34
+
35
+ def self.print_help(args, method, own_methods=[])
36
+ module_name = self.name.split('::').last.downcase
37
+ puts "======="
38
+ if method && @@help[self.name] && @@help[self.name][method]
39
+ puts "Usage: scl #{module_name} #{method} (options)\n"
40
+ puts
41
+ puts @@help[self.name][method].split("\n").map{|line| line.gsub(/^\s{6}/,'')}.join("\n")
42
+ puts
43
+ else
44
+ puts "No help docs found for \"#{method}\"\n=======\n" if method
45
+ puts "Usage: scl #{module_name} [command] (options)"
46
+ puts
47
+ puts "Supported commands are [#{own_methods.join(' ')}]"
48
+ puts
49
+ puts "Try scl #{module_name} [command] -h for more info"
50
+ puts
51
+ puts args.opts.to_s[/Where options.*/m]
52
+ end
53
+ exit(0)
54
+ end
55
+
56
+ def action(action_name, args)
57
+ args = args.dup
58
+ ARGV.clear
59
+ if @controller.args.help || !action_name
60
+ self.class.print_help(@controller.args, action_name, self.public_methods.select{|m| self.method(m).owner == self.class })
61
+ exit(0)
62
+ end
63
+ if self.respond_to?(action_name)
64
+ begin
65
+ action = self.method(action_name)
66
+ required_args = action.arity >= 0 ? action.arity : -(action.arity + 1)
67
+ unless required_args <= args.length
68
+ raise ControlError.new("#{action_name} expected at least #{required_args} arguments\nE.g.\n#{action_name} #{action.parameters.map(&:last).map{|x| "[#{x}]"}[-required_args..-1].join(' ')} (options)")
69
+ end
70
+ self.send(action_name, *args)
71
+ rescue ArgumentError => e
72
+ puts e.message
73
+ puts "#{action_name} expects #{required_args} arguments by default\nE.g.\n#{action_name} #{action.parameters.map(&:last).map{|x| "[#{x}]"}[-required_args..-1].join(' ')}"
74
+ self.class.print_help(@controller.args, action_name)
75
+ puts e.backtrace if @controller.verbose?
76
+ rescue ControlError => e
77
+ puts e.message
78
+ puts e.cause
79
+ puts e.cause.backtrace if @controller.verbose?
80
+ self.class.print_help(@controller.args, action_name)
81
+ end
82
+ else
83
+ own_methods = self.public_methods.select{|m| self.method(m).owner == self.class }
84
+ puts "Command not supported: \"#{action_name}\""
85
+ puts "Supported commands are [#{own_methods.join(' ')}]"
86
+ exit(1)
87
+ end
88
+ end
89
+
90
+ private
91
+ def read_file(file, label='', help)
92
+ unless file
93
+ raise ControlError.new("Expected #{label} file not given\n#{help}")
94
+ end
95
+ unless File.exists?(file)
96
+ raise ControlError.new("Expected #{label} file #{file} doesnt exist\n#{help}")
97
+ end
98
+ IO.read(file)
99
+ end
100
+ end
101
+ end
102
+ end