scl 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []