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.
@@ -0,0 +1,23 @@
1
+ module Scl
2
+ class Words < Format
3
+
4
+ WORDS = IO.read(File.expand_path(__FILE__+"/../wordlist.txt")).split("\n")
5
+ INDICES = Hash[WORDS.map.with_index{|w, i|[w,i]}]
6
+
7
+ def encode(data)
8
+ data.bytes.each_slice(2).map do |b1, b2|
9
+ WORDS[(b1 << 8) + (b2 || 0)]
10
+ end.join(' ')
11
+ end
12
+
13
+ def decode(data)
14
+ bytes = data.split(' ').each.map do |word|
15
+ bytes = INDICES[word]
16
+ b2 = bytes & 255
17
+ b1 = (bytes >> 8) & 255
18
+ [b1, b2]
19
+ end.flatten
20
+ bytes.map(&:chr).join
21
+ end
22
+ end
23
+ end
data/lib/scl/rsa.rb ADDED
@@ -0,0 +1,72 @@
1
+ module Scl
2
+ class RSA
3
+ attr_reader :private, :public
4
+ def initialize(file: nil, public: nil, private: nil, input_encoder: Format::BINARY, output_encoder: Format::BINARY)
5
+ if file
6
+ @public = OpenSSL::PKey::RSA.new(encoder.decode(IO.read("#{file}.pub"))) if File.exists?("#{file}.pub")
7
+ @private = OpenSSL::PKey::RSA.new(encoder.decode(IO.read("#{file}.priv"))) if File.exists?("#{file}.priv")
8
+ else
9
+ @public = public
10
+ @private = private
11
+ end
12
+ @public = Key.new(@public)
13
+ @private = Key.new(@private)
14
+ end
15
+
16
+ def save(dir, name='rsa-keypair')
17
+ IO.write(File.join(dir, "#{name}.pub"), output_encoder.encode(@public.export))
18
+ IO.write(File.join(dir, "#{name}.priv"), output_encoder.encode(@private.export))
19
+ dir
20
+ end
21
+
22
+ def self.generate(key_size)
23
+ rsa_pair = OpenSSL::PKey::RSA.new(key_size || 2048)
24
+ RSA.new(public: rsa_pair.public_key, private: rsa_pair)
25
+ end
26
+
27
+ def self.encrypt(msg)
28
+ pair = self.generate
29
+ [pair.private.export, pair.public.export, pair.private.encrypt(msg)]
30
+ end
31
+
32
+ class Key
33
+ attr_reader :rsa
34
+ def initialize(rsa)
35
+ @rsa = rsa
36
+ @aes = Scl::AES.new
37
+ end
38
+
39
+ def sign(data)
40
+ rsa.sign(OpenSSL::Digest::SHA256.new, data)
41
+ end
42
+
43
+ def verify(signature, data)
44
+ rsa.verify(OpenSSL::Digest::SHA256.new, signature, data)
45
+ end
46
+
47
+ def encrypt(plaintext, key=nil, iv=nil)
48
+ ciphertext, key, iv = @aes.encrypt(plaintext, key, iv)
49
+ encrypted_key =
50
+ case
51
+ when rsa.private? then rsa.private_encrypt(key)
52
+ else rsa.public_encrypt(key)
53
+ end
54
+ [encrypted_key, iv, ciphertext].join('::')
55
+ end
56
+
57
+ def decrypt(ciphertext)
58
+ encrypted_key, iv, ciphertext = ciphertext.split('::', 3)
59
+ decrypted_key =
60
+ case
61
+ when rsa.private? then rsa.private_decrypt(encrypted_key)
62
+ else rsa.public_decrypt(encrypted_key)
63
+ end
64
+ @aes.decrypt(ciphertext, decrypted_key, iv)
65
+ end
66
+
67
+ def export
68
+ rsa.export
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,122 @@
1
+ require 'openssl'
2
+ require 'ostruct'
3
+
4
+ module Scl
5
+ PRIME = OpenSSL::BN.new("214663014907494254264734401372860550616125566004826012670437788223410219893687")
6
+ ENCODED_CHUNK_SIZE = 44
7
+
8
+ class SecretShare
9
+ def initialize(minimum, shares, encoder: Format::BASE64)
10
+ raise "Minimum must be larger than zero and less than shares" unless (0..shares) === minimum
11
+ @encoder = encoder
12
+ @minimum = minimum
13
+ @shares = shares
14
+ end
15
+
16
+ def random()
17
+ @seen ||= { nil => true}
18
+ number = Random.rand(1...PRIME) while @seen[number]
19
+ @seen[number] = true
20
+ OpenSSL::BN.new(number)
21
+ end
22
+
23
+ def chunks(secret)
24
+ Enumerator.new do |enum|
25
+ as_hex = secret.each_byte.map{|b| b.to_s(16).rjust(2, '0') }.join
26
+ as_hex.chars.each_slice(64) do |slice|
27
+ chunk = OpenSSL::BN.new(slice.join.ljust(64, ?0), 16)
28
+ enum << chunk
29
+ end
30
+ end
31
+ end
32
+
33
+ def evaluate_polynomial(polynomial, x)
34
+ polynomial.reverse.reduce(OpenSSL::BN.new(0)) do |mem, value|
35
+ ((mem * x) % PRIME + value) % PRIME
36
+ end
37
+ end
38
+
39
+ def generate(secret)
40
+ polynomials = chunks(secret).map do |chunk|
41
+ polynomial = [chunk] + (@minimum - 1).times.map do |i|
42
+ random
43
+ end
44
+ end
45
+
46
+ @shares.times.map do |i|
47
+ result = ''
48
+ poly_result = polynomials.map do |polynomial|
49
+ x = random
50
+ y = evaluate_polynomial(polynomial, x)
51
+ result << base64_encode(x)
52
+ result << base64_encode(y)
53
+ end
54
+ @encoder == Format::BASE64 ? result : @encoder.encode(result)
55
+ end
56
+ end
57
+
58
+ def base64_encode(number)
59
+ return ::Base64.urlsafe_encode64(number.to_s(16).rjust(64, ?0).scan(/../).map{|x| x.hex.chr}.join)
60
+ end
61
+
62
+ def self.base64_decode(number)
63
+ ::Base64.urlsafe_decode64(number).chars.map{|x| "0#{x.ord.to_s(16)}"[-2..-1] }.join.rjust(64, ?0).hex
64
+ end
65
+
66
+ def self.extended_gcd(a, b)
67
+ abs = ->(v){ v < 0 ? v * -1 : v}
68
+ last_remainder, remainder = abs[a], abs[b]
69
+ x, last_x, y, last_y = 0, 1, 1, 0
70
+ while remainder != 0
71
+ last_remainder, (quotient, remainder) = remainder, last_remainder./(remainder)
72
+ x, last_x = last_x - quotient*x, x
73
+ y, last_y = last_y - quotient*y, y
74
+ end
75
+
76
+ return last_remainder, last_x * (a < 0 ? -1 : 1)
77
+ end
78
+
79
+ def self.mod_inverse(e, p=PRIME)
80
+ g, x = extended_gcd(e, p)
81
+ x % p
82
+ end
83
+
84
+ def self.combine(shares, encoder: nil)
85
+ abs = ->(v){ v < 0 ? v * -1 : v}
86
+ decoded = shares.map do |share|
87
+ share = encoder && encoder != Format::BASE64 ? encoder.decode(share) : share
88
+ share.chars.each_slice(ENCODED_CHUNK_SIZE).map(&:join).each_slice(2).map do |random, polynomial|
89
+ OpenStruct.new({
90
+ x: base64_decode(random),
91
+ y: base64_decode(polynomial)
92
+ })
93
+ end
94
+ end
95
+
96
+ chunks = decoded[0].length.times.map do |chunk|
97
+ secret = OpenSSL::BN.new(0)
98
+ decoded.each do |share|
99
+ point = share[chunk]
100
+ numerator, denominator = OpenSSL::BN.new(1), OpenSSL::BN.new(1)
101
+ decoded.each do |peer|
102
+ if peer != share
103
+ current = peer[chunk].x
104
+ numerator = OpenSSL::BN.new((numerator * -current).to_i % PRIME)
105
+ denominator = OpenSSL::BN.new((denominator * (point.x - current)).to_i % PRIME)
106
+ end
107
+ end
108
+ working = (point.y * numerator) * mod_inverse(denominator) + PRIME
109
+ secret = ((secret + working) + 1000 * PRIME) % PRIME
110
+ end
111
+ secret
112
+ end
113
+
114
+ hex_data = chunks.map{|chunk| chunk.to_s(16).rjust(64, "0") }.join
115
+ hex_data.chars.each_slice(64).map(&:join).map do |chunk|
116
+ chunk.gsub(/(?:00)+$/,'').chars.each_slice(2).map{|pair|
117
+ pair.join.to_i(16).chr
118
+ }
119
+ end.join
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,3 @@
1
+ module Scl
2
+ VERSION = "0.1.0"
3
+ end
data/lib/scl.rb ADDED
@@ -0,0 +1,12 @@
1
+ require "scl/version"
2
+ require "scl/dh"
3
+ require "scl/rsa"
4
+ require "scl/aes"
5
+ require "scl/secret_share"
6
+ require "scl/control"
7
+ require "scl/digest"
8
+ require "scl/formats/format"
9
+ require 'openssl'
10
+
11
+ module Scl
12
+ end
data/scl.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "scl/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "scl"
8
+ spec.version = Scl::VERSION
9
+ spec.authors = ["Wouter Coppieters"]
10
+ spec.email = ["wouter@youdo.co.nz"]
11
+
12
+ spec.summary = "Simple crypto library"
13
+ spec.description = "Simple crypto library"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
16
+ f.match(%r{^(test|spec|features)/})
17
+ end
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.15"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "pry-byebug"
25
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scl
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Wouter Coppieters
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-11-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.15'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.15'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry-byebug
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Simple crypto library
56
+ email:
57
+ - wouter@youdo.co.nz
58
+ executables:
59
+ - scl
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - CODE_OF_CONDUCT.md
65
+ - Gemfile
66
+ - README.md
67
+ - Rakefile
68
+ - bin/console
69
+ - bin/setup
70
+ - exe/scl
71
+ - lib/scl.rb
72
+ - lib/scl/aes.rb
73
+ - lib/scl/control.rb
74
+ - lib/scl/control/aes.rb
75
+ - lib/scl/control/controller.rb
76
+ - lib/scl/control/controller_module.rb
77
+ - lib/scl/control/dh.rb
78
+ - lib/scl/control/digest.rb
79
+ - lib/scl/control/output.rb
80
+ - lib/scl/control/rsa.rb
81
+ - lib/scl/control/sss.rb
82
+ - lib/scl/dh.rb
83
+ - lib/scl/digest.rb
84
+ - lib/scl/formats/auto.rb
85
+ - lib/scl/formats/base64.rb
86
+ - lib/scl/formats/binary.rb
87
+ - lib/scl/formats/format.rb
88
+ - lib/scl/formats/hex.rb
89
+ - lib/scl/formats/qrcode.rb
90
+ - lib/scl/formats/stdout.rb
91
+ - lib/scl/formats/wordlist.txt
92
+ - lib/scl/formats/words.rb
93
+ - lib/scl/rsa.rb
94
+ - lib/scl/secret_share.rb
95
+ - lib/scl/version.rb
96
+ - scl.gemspec
97
+ homepage:
98
+ licenses: []
99
+ metadata: {}
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ requirements: []
115
+ rubyforge_project:
116
+ rubygems_version: 2.5.1
117
+ signing_key:
118
+ specification_version: 4
119
+ summary: Simple crypto library
120
+ test_files: []