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 +7 -0
- data/.gitignore +9 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/README.md +71 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/scl +124 -0
- data/lib/scl/aes.rb +28 -0
- data/lib/scl/control/aes.rb +51 -0
- data/lib/scl/control/controller.rb +96 -0
- data/lib/scl/control/controller_module.rb +102 -0
- data/lib/scl/control/dh.rb +84 -0
- data/lib/scl/control/digest.rb +90 -0
- data/lib/scl/control/output.rb +14 -0
- data/lib/scl/control/rsa.rb +161 -0
- data/lib/scl/control/sss.rb +48 -0
- data/lib/scl/control.rb +14 -0
- data/lib/scl/dh.rb +52 -0
- data/lib/scl/digest.rb +29 -0
- data/lib/scl/formats/auto.rb +23 -0
- data/lib/scl/formats/base64.rb +12 -0
- data/lib/scl/formats/binary.rb +11 -0
- data/lib/scl/formats/format.rb +44 -0
- data/lib/scl/formats/hex.rb +11 -0
- data/lib/scl/formats/qrcode.rb +11 -0
- data/lib/scl/formats/stdout.rb +11 -0
- data/lib/scl/formats/wordlist.txt +235886 -0
- data/lib/scl/formats/words.rb +23 -0
- data/lib/scl/rsa.rb +72 -0
- data/lib/scl/secret_share.rb +122 -0
- data/lib/scl/version.rb +3 -0
- data/lib/scl.rb +12 -0
- data/scl.gemspec +25 -0
- metadata +120 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'json'
|
2
|
+
module Scl
|
3
|
+
module Control
|
4
|
+
class DH < ControllerModule
|
5
|
+
|
6
|
+
help <<-HELP,
|
7
|
+
syn. Step 1 of a diffie hellman exchange.
|
8
|
+
Generates a private and public key and der to be used to generate a symmetric key with a second party.
|
9
|
+
|
10
|
+
e.g
|
11
|
+
>> Using public key (Saves to [filename].enc by default, unless -o is used)
|
12
|
+
scl dh syn
|
13
|
+
scl dh syn -fbase64
|
14
|
+
scl dh syn -o ./dhell
|
15
|
+
HELP
|
16
|
+
def syn
|
17
|
+
syn = dh.syn
|
18
|
+
args.output_file ||= "dh-key-exchange"
|
19
|
+
controller.output(
|
20
|
+
Output.new(syn[:private].to_json, ".dh1.priv"),
|
21
|
+
Output.new(syn[:public].to_json, ".dh1.pub")
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
help <<-HELP,
|
26
|
+
ack. Step 2 of a diffie hellman exchange.
|
27
|
+
Requires the public output of step 1 (The der and public key)
|
28
|
+
Generates a private and public key and der to be used to generate a symmetric key with a second party.
|
29
|
+
|
30
|
+
The output [output_file].key contains the secret key generated by the DH exchange.
|
31
|
+
The public output [output_file].dh2.pub must be passed back to the first party to complete the exchange
|
32
|
+
e.g
|
33
|
+
>> Using public key (Saves to [filename].enc by default, unless -o is used)
|
34
|
+
scl dh ack ./dh-key-exchange.dh1.pub
|
35
|
+
scl dh ack ./dhell.dh1.pub -o ./dhell
|
36
|
+
HELP
|
37
|
+
def ack(file)
|
38
|
+
unless File.exists?(file)
|
39
|
+
raise ControlError.new("Couldn't find file containing part 1 of diffie hellman exchange: #{file}. File doesnt exist")
|
40
|
+
end
|
41
|
+
input = JSON.parse(input_decoder.decode(IO.read(file)))
|
42
|
+
ack = dh.ack(der: input['der'], public_key: input['public_key'])
|
43
|
+
args.output_file ||= "dh-key-exchange"
|
44
|
+
controller.output(
|
45
|
+
Output.new(ack[:private].to_json, ".dh2.key"),
|
46
|
+
Output.new(ack[:public].to_json, ".dh2.pub")
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
help <<-HELP,
|
51
|
+
fin. Step 3 and final step of a diffie hellman exchange.
|
52
|
+
Requires the private output of step 1 (The der and private key) and the public key from step 2
|
53
|
+
Generates a secret key shared with the second party
|
54
|
+
|
55
|
+
The output [output_file].key contains the secret key generated by the DH exchange.
|
56
|
+
|
57
|
+
e.g
|
58
|
+
>> Using public key (Saves to [filename].enc by default, unless -o is used)
|
59
|
+
scl dh fin ./dh-key-exchange.dh1.priv ./dh-key-exchange.dh2.pub
|
60
|
+
scl dh fin ./dhell.dh1.priv ./dhell.dh2.pub -o ./dhell
|
61
|
+
HELP
|
62
|
+
def fin(dh1_priv, dh2_pub)
|
63
|
+
unless File.exists?(dh1_priv)
|
64
|
+
raise ControlError.new("Couldn't find file private portion of part 1 of diffie hellman exchange: #{dh1_priv}. File doesnt exist")
|
65
|
+
end
|
66
|
+
unless File.exists?(dh2_pub)
|
67
|
+
raise ControlError.new("Couldn't find file containing public portion of part 2 of diffie hellman exchange: #{dh2_pub}. File doesnt exist")
|
68
|
+
end
|
69
|
+
dh1 = JSON.parse(input_decoder.decode(IO.read(dh1_priv)))
|
70
|
+
dh2 = JSON.parse(input_decoder.decode(IO.read(dh2_pub)))
|
71
|
+
result = dh.fin(der: dh1['der'], private_key: dh1['private_key'], public_key: dh2['public_key'])
|
72
|
+
args.output_file ||= "dh-key-exchange"
|
73
|
+
controller.output(
|
74
|
+
Output.new(result[:private].to_json, ".dh1.key")
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
def dh
|
80
|
+
@dh ||= Scl::DH.new
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Scl
|
2
|
+
module Control
|
3
|
+
class Digest < ControllerModule
|
4
|
+
|
5
|
+
help <<-HELP,
|
6
|
+
sign. Signs a file using a support hash algorithm. Defaults to sha256
|
7
|
+
e.g
|
8
|
+
scl digest sign /path/to/file
|
9
|
+
scl digest sign /path/to/file -o stdout
|
10
|
+
scl digest sign /path/to/file -d sha512
|
11
|
+
HELP
|
12
|
+
def sign(input_file)
|
13
|
+
signature = Scl::Digest.digest(args.digest || 'sha256', read_file(input_file, "File to sign"))
|
14
|
+
args.output_file ||= input_file
|
15
|
+
args.output_format ||= 'binary'
|
16
|
+
controller.output(
|
17
|
+
Output.new(signature, ".sig")
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
help <<-HELP,
|
22
|
+
verify
|
23
|
+
e.g.
|
24
|
+
scl digest verify file signature
|
25
|
+
scl digest verify file signature -d sha512
|
26
|
+
HELP
|
27
|
+
def verify(input_file, signature_file)
|
28
|
+
signature = Scl::Digest.digest(args.digest || 'sha256', read_file(input_file, "File to verify"))
|
29
|
+
args.input_format ||= 'binary'
|
30
|
+
if signature == input_decoder.decode(read_file(signature_file, "Signature"))
|
31
|
+
exit(0)
|
32
|
+
else
|
33
|
+
exit(1)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
help <<-HELP,
|
38
|
+
hmac. Generates an HMAC for a file, can be given an optional key or alternately one will be
|
39
|
+
generated for you. Keep hold of this key for verifying signatures in the future
|
40
|
+
e.g.
|
41
|
+
scl hmac file # Generates both a digest and a secure random key
|
42
|
+
scl hmac file -k keyfile # Generates a digest using an existing key
|
43
|
+
scl hmac file -d sha512 # Provide alternate digest algorithm
|
44
|
+
HELP
|
45
|
+
def hmac(input_file)
|
46
|
+
input_key = args.key_path ?
|
47
|
+
read_file(args.key_path) :
|
48
|
+
nil
|
49
|
+
signature, key = Scl::Digest.hmac(args.digest || 'sha256', read_file(input_file, "File to sign"), input_key)
|
50
|
+
args.output_file ||= input_file
|
51
|
+
args.output_format ||= 'binary'
|
52
|
+
controller.output(
|
53
|
+
Output.new(signature, ".sig"),
|
54
|
+
input_key ? nil : Output.new(key, '.key')
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
help <<-HELP,
|
59
|
+
hmac. Verifies the contents of a file match an HMAC signature (using a given key)
|
60
|
+
e.g.
|
61
|
+
scl hmac_verify file signature -k keyfile
|
62
|
+
HELP
|
63
|
+
def hmac_verify(input_file, signature_file)
|
64
|
+
args.input_format ||= 'binary'
|
65
|
+
key = input_decoder.decode(read_file(args.key_path, 'HMAC Key file', 'Use -k'))
|
66
|
+
data = read_file(input_file, "File to verify")
|
67
|
+
signature, key = Scl::Digest.hmac(args.digest || 'sha256', data, key)
|
68
|
+
if signature == input_decoder.decode(read_file(signature_file, "Signature"))
|
69
|
+
exit(0)
|
70
|
+
else
|
71
|
+
exit(1)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
help <<-HELP,
|
76
|
+
list. List supported hash algorithms
|
77
|
+
e.g.
|
78
|
+
scl digest list
|
79
|
+
HELP
|
80
|
+
def list
|
81
|
+
puts OpenSSL::Digest.constants.reject{|x| x.to_s =~ /Error/ }
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
def ss
|
86
|
+
@ss ||= Scl::SecretShare.new(args.min_shares.to_i, args.num_shares.to_i)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Scl
|
2
|
+
module Control
|
3
|
+
class Output
|
4
|
+
attr_reader :content
|
5
|
+
def initialize(content, suffix)
|
6
|
+
@content, @suffix = content, suffix
|
7
|
+
end
|
8
|
+
|
9
|
+
def file(path)
|
10
|
+
File.join("#{path.gsub(%r(#{@suffix.gsub('.','\.')}$),'')}#{@suffix}")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
module Scl
|
2
|
+
module Control
|
3
|
+
class RSA < ControllerModule
|
4
|
+
|
5
|
+
help <<-HELP,
|
6
|
+
Generates an RSA keypair
|
7
|
+
e.g
|
8
|
+
>> Generates a key-pair and prints to stdout
|
9
|
+
$ scl rsa generate
|
10
|
+
|
11
|
+
>> Generates a key-pair and saves to the filesystem
|
12
|
+
$ scl rsa generate -o /path/to/file
|
13
|
+
HELP
|
14
|
+
def generate
|
15
|
+
begin
|
16
|
+
result = Scl::RSA.generate(args.key_size)
|
17
|
+
controller.output(
|
18
|
+
Output.new(result.public.export, '.pub'),
|
19
|
+
Output.new(result.private.export, '.priv')
|
20
|
+
)
|
21
|
+
rescue StandardError => e
|
22
|
+
raise ControlError.new("Couldn't generate key of size #{args.key_size}")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
help <<-HELP,
|
27
|
+
Signs a file using an RSA private key.
|
28
|
+
This file can then be verified using the corresponding public-key or the same private-key
|
29
|
+
e.g
|
30
|
+
>> Sign [file] using private key
|
31
|
+
$ scl rsa sign file -Z /path/to/private/key
|
32
|
+
HELP
|
33
|
+
def sign(file)
|
34
|
+
unless args.private_key_file
|
35
|
+
raise ControlError.new("Please provide a private key file (-Z or --priv) use --help to find out more")
|
36
|
+
end
|
37
|
+
unless File.exists?(args.private_key_file)
|
38
|
+
raise ControlError.new("Private key file #{args.private_key_file} doesnt exist")
|
39
|
+
end
|
40
|
+
unless File.exists?(file)
|
41
|
+
raise ControlError.new("Couldn't find file to sign: #{file}. File doesnt exist")
|
42
|
+
end
|
43
|
+
|
44
|
+
private_key = load_key(args.private_key_file)
|
45
|
+
signature = private_key.sign(IO.read(file))
|
46
|
+
args.output_file ||= file
|
47
|
+
controller.output(
|
48
|
+
Output.new(signature, ".sig")
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
help <<-HELP,
|
53
|
+
Verifies a file matches a signature for an RSA private key
|
54
|
+
Verification can be performed using the corresponding public-key or the same private-key that generated the signature
|
55
|
+
e.g
|
56
|
+
|
57
|
+
>> Verify [file] using public key
|
58
|
+
$ scl rsa verify -p /path/to/private/key file file.sig
|
59
|
+
$ scl rsa verify --pub-key /path/to/private/key file file.sig
|
60
|
+
|
61
|
+
>> Verify [file] using private key
|
62
|
+
$ scl rsa verify -Z /path/to/private/key file file.sig
|
63
|
+
HELP
|
64
|
+
def verify(file, signature="#{file}.sig")
|
65
|
+
key_file = args.public_key_file || args.private_key_file
|
66
|
+
unless key_file
|
67
|
+
raise ControlError.new("Please provide a private or public key file (-p --pub-key, -Z or --priv-key) use --help to find out more")
|
68
|
+
end
|
69
|
+
unless File.exists?(key_file)
|
70
|
+
raise ControlError.new("Key file #{key_file} doesnt exist")
|
71
|
+
end
|
72
|
+
unless File.exists?(file)
|
73
|
+
raise ControlError.new("Couldn't find file to verify: #{file}. File doesnt exist")
|
74
|
+
end
|
75
|
+
unless File.exists?(signature)
|
76
|
+
raise ControlError.new("Couldn't find signature to verify: #{signature}. File doesnt exist")
|
77
|
+
end
|
78
|
+
key = load_key(key_file)
|
79
|
+
if key.verify(input_decoder.decode(IO.read(signature)), IO.read(file))
|
80
|
+
exit(0)
|
81
|
+
else
|
82
|
+
exit(1)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
help <<-HELP,
|
87
|
+
Encrypts a file.
|
88
|
+
e.g
|
89
|
+
>> Using public key (Saves to [filename].enc by default, unless -o is used)
|
90
|
+
scl rsa encrypt -p /path/to/public_key /path/to/file
|
91
|
+
scl rsa encrypt --pub-key /path/to/public_key /path/to/file
|
92
|
+
|
93
|
+
>> Using public key (Saves to [filename].enc by default, unless -o is used)
|
94
|
+
scl rsa encrypt -Z /path/to/private_key /path/to/file
|
95
|
+
scl rsa encrypt --priv-key /path/to/private_key /path/to/file
|
96
|
+
HELP
|
97
|
+
def encrypt(file)
|
98
|
+
key_file = args.public_key_file || args.private_key_file
|
99
|
+
unless key_file
|
100
|
+
raise ControlError.new("Please provide a private or public key file (-p --pub-key, -Z or --priv-key) use --help to find out more")
|
101
|
+
end
|
102
|
+
unless File.exists?(key_file)
|
103
|
+
raise ControlError.new("Key file #{key_file} doesnt exist")
|
104
|
+
end
|
105
|
+
unless File.exists?(file)
|
106
|
+
raise ControlError.new("Couldn't find file to verify: #{file}. File doesnt exist")
|
107
|
+
end
|
108
|
+
key = load_key(key_file)
|
109
|
+
encrypted = key.encrypt(IO.read(file))
|
110
|
+
args.output_file ||= file
|
111
|
+
controller.output(
|
112
|
+
Output.new(encrypted, '.enc')
|
113
|
+
)
|
114
|
+
end
|
115
|
+
|
116
|
+
help <<-HELP,
|
117
|
+
Decrypts a file.
|
118
|
+
e.g
|
119
|
+
>> Using public key (Writes to stdout unless -o is used)
|
120
|
+
scl rsa decrypt -p /path/to/public_key /path/to/file.enc
|
121
|
+
scl rsa decrypt --pub-key /path/to/public_key /path/to/file.enc
|
122
|
+
|
123
|
+
>> Using public key (Writes to stdout unless -o is used)
|
124
|
+
scl rsa decrypt -Z /path/to/private_key /path/to/file.enc
|
125
|
+
scl rsa decrypt --priv-key /path/to/private_key /path/to/file.enc
|
126
|
+
HELP
|
127
|
+
def decrypt(file)
|
128
|
+
key_file = args.public_key_file || args.private_key_file
|
129
|
+
unless key_file
|
130
|
+
raise ControlError.new("Please provide a private or public key file (-p --pub-key, -Z or --priv-key) use --help to find out more")
|
131
|
+
end
|
132
|
+
unless File.exists?(key_file)
|
133
|
+
raise ControlError.new("Key file #{key_file} doesnt exist")
|
134
|
+
end
|
135
|
+
unless File.exists?(file)
|
136
|
+
raise ControlError.new("Couldn't find file to verify: #{file}. File doesnt exist")
|
137
|
+
end
|
138
|
+
key = load_key(key_file)
|
139
|
+
decrypted = key.decrypt(input_decoder.decode(IO.read(file)))
|
140
|
+
args.output_format = 'binary'
|
141
|
+
controller.output(
|
142
|
+
Output.new(decrypted, '')
|
143
|
+
)
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
def load_key(key_file)
|
148
|
+
raw_input = IO.read(key_file)
|
149
|
+
puts "Decoding key using #{input_decoder.name}" if args.verbose
|
150
|
+
decoded_input = key_coder.decode(raw_input)
|
151
|
+
puts "Constructing new RSA key using decoded input" if args.verbose
|
152
|
+
begin
|
153
|
+
pkey = OpenSSL::PKey::RSA.new(decoded_input)
|
154
|
+
Scl::RSA::Key.new(pkey)
|
155
|
+
rescue StandardError => e
|
156
|
+
raise ControlError.new("Unable to construct key from decoded input #{e.message}")
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Scl
|
2
|
+
module Control
|
3
|
+
class SSS < ControllerModule
|
4
|
+
|
5
|
+
help <<-HELP,
|
6
|
+
generate. Generate a set of shares for a shared secret
|
7
|
+
Arguments are the total number of shares to generate, and the number of shares required to unlock
|
8
|
+
the secret.
|
9
|
+
e.g
|
10
|
+
scl sss generate -m 3 -n 5
|
11
|
+
scl sss generate --min-shares=11 --num-shares=14 -o output_file
|
12
|
+
|
13
|
+
Large secrets are encoded using multiple blocks, which can create large shares.
|
14
|
+
An alternative, more space efficient approach to this is to encode a shorter key using secret sharing,
|
15
|
+
and to then encrypt the large secret using this key and a block-cipher
|
16
|
+
|
17
|
+
HELP
|
18
|
+
def generate(input_file)
|
19
|
+
raise ControlError.new("Min-shares must be a positive integer") unless args.min_shares.to_i > 0
|
20
|
+
raise ControlError.new("Num-shares must be a positive integer") unless args.num_shares.to_i > 0
|
21
|
+
raise ControlError.new('Num shares must be larger than or equal to min shares') unless args.num_shares.to_i >= args.min_shares.to_i
|
22
|
+
input = read_file(input_file)
|
23
|
+
args.output_file ||= input_file
|
24
|
+
controller.output(
|
25
|
+
Output.new(ss.generate(input).join("\n"), ".shares")
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
help <<-HELP,
|
30
|
+
combine. Combines a set of shares for a shared secret
|
31
|
+
Expects as an argument a file of "\n" separate secret shares
|
32
|
+
e.g
|
33
|
+
scl sss combine shares.txt
|
34
|
+
HELP
|
35
|
+
def combine(input_file)
|
36
|
+
shares = read_file(input_file).split("\n")
|
37
|
+
controller.output(
|
38
|
+
Output.new(Scl::SecretShare.combine(shares), ".sec")
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
def ss
|
44
|
+
@ss ||= Scl::SecretShare.new(args.min_shares.to_i, args.num_shares.to_i)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/scl/control.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'scl/control/output'
|
2
|
+
require 'scl/control/controller'
|
3
|
+
require 'scl/control/controller_module'
|
4
|
+
require 'scl/control/aes'
|
5
|
+
require 'scl/control/rsa'
|
6
|
+
require 'scl/control/sss'
|
7
|
+
require 'scl/control/dh'
|
8
|
+
require 'scl/control/digest'
|
9
|
+
|
10
|
+
module Scl
|
11
|
+
module Control
|
12
|
+
class ControlError < StandardError; end
|
13
|
+
end
|
14
|
+
end
|
data/lib/scl/dh.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
module Scl
|
2
|
+
require 'base64'
|
3
|
+
class DH
|
4
|
+
attr_reader :encoder
|
5
|
+
|
6
|
+
def initialize(encoder: Format::BASE64)
|
7
|
+
@encoder = encoder
|
8
|
+
end
|
9
|
+
# :0> syn = Scl::DH.new.syn
|
10
|
+
# :1> ack = Scl::DH.new.ack(syn[:public])
|
11
|
+
# :2> shared_key1 = Scl::DH.new.fin(syn[:private].merge(ack[:public]))[:private][:shared_key]
|
12
|
+
# :3> shared_key2 = ack[:private][:shared_key]
|
13
|
+
def syn(length: 512)
|
14
|
+
dh = OpenSSL::PKey::DH.new(length)
|
15
|
+
{
|
16
|
+
private: {
|
17
|
+
der: encoder.encode(dh.public_key.to_der),
|
18
|
+
private_key: encoder.encode(dh.priv_key.to_s(16))
|
19
|
+
},
|
20
|
+
public: {
|
21
|
+
der: encoder.encode(dh.public_key.to_der),
|
22
|
+
public_key: encoder.encode(dh.pub_key.to_s(16))
|
23
|
+
}
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def ack(der:, public_key:)
|
28
|
+
dh = OpenSSL::PKey::DH.new(encoder.decode(der))
|
29
|
+
dh.generate_key!
|
30
|
+
shared_key = dh.compute_key(OpenSSL::BN.new(encoder.decode(public_key), 16))
|
31
|
+
{
|
32
|
+
private: {
|
33
|
+
shared_key: encoder.encode(shared_key)
|
34
|
+
},
|
35
|
+
public: {
|
36
|
+
public_key: encoder.encode(dh.pub_key.to_s(16))
|
37
|
+
}
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def fin(private_key:, der:, public_key:)
|
42
|
+
dh = OpenSSL::PKey::DH.new(encoder.decode(der))
|
43
|
+
dh.priv_key = OpenSSL::BN.new(encoder.decode(private_key), 16)
|
44
|
+
shared_key = dh.compute_key(OpenSSL::BN.new(encoder.decode(public_key), 16))
|
45
|
+
{
|
46
|
+
private: {
|
47
|
+
shared_key: encoder.encode(shared_key)
|
48
|
+
}
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/scl/digest.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Scl
|
2
|
+
class Digest
|
3
|
+
def self.digest(digest, data)
|
4
|
+
if digest_exists?(digest)
|
5
|
+
OpenSSL::Digest.const_get(digest.upcase).new.hexdigest(data)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.hmac(digest, data, key=nil)
|
10
|
+
if digest_exists?(digest)
|
11
|
+
require 'securerandom'
|
12
|
+
key = key || SecureRandom.hex
|
13
|
+
[OpenSSL::HMAC.hexdigest(digest, key, data), key]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def self.digest_exists?(digest)
|
19
|
+
begin
|
20
|
+
OpenSSL::Digest.const_get(digest.upcase)
|
21
|
+
rescue NameError => e
|
22
|
+
puts "Couldn't get digest type. #{digest}"
|
23
|
+
puts "Try $ scl digest list – for a list of supported digests"
|
24
|
+
return false
|
25
|
+
end
|
26
|
+
true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Scl
|
2
|
+
class Scl::Auto < Scl::Format
|
3
|
+
def encode(data)
|
4
|
+
sorted = (data.chars.map(&:ord).uniq.sort) - [10]
|
5
|
+
if sorted.first < 32 || sorted.max > 126
|
6
|
+
Scl::Format::BASE64.encode(data)
|
7
|
+
else
|
8
|
+
Scl::Format::BINARY.encode(data)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def decode(data)
|
13
|
+
png = Regexp.new("\x89PNG".force_encoding("binary"))
|
14
|
+
if /^#{png}/ === data
|
15
|
+
Scl::Format::QRCODE.decode(data)
|
16
|
+
elsif data[/[^A-Za-z0-9\+\/\n=]/]
|
17
|
+
Scl::Format::BINARY.decode(data)
|
18
|
+
else
|
19
|
+
Scl::Format::BASE64.decode(data)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Scl
|
2
|
+
class Format
|
3
|
+
def output(filename, data)
|
4
|
+
IO.write(filename, encode(data))
|
5
|
+
end
|
6
|
+
|
7
|
+
def read(filename)
|
8
|
+
decode(IO.read(filename))
|
9
|
+
end
|
10
|
+
|
11
|
+
def encode(data)
|
12
|
+
raise "Must be implemented by subclass"
|
13
|
+
end
|
14
|
+
|
15
|
+
def decode(data)
|
16
|
+
raise "Must be implemented by subclass"
|
17
|
+
end
|
18
|
+
|
19
|
+
def name
|
20
|
+
self.class.name
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'scl/formats/base64'
|
26
|
+
require 'scl/formats/binary'
|
27
|
+
require 'scl/formats/words'
|
28
|
+
require 'scl/formats/auto'
|
29
|
+
require 'scl/formats/hex'
|
30
|
+
require 'scl/formats/qrcode'
|
31
|
+
require 'scl/formats/stdout'
|
32
|
+
|
33
|
+
module Scl
|
34
|
+
class Format
|
35
|
+
|
36
|
+
BASE64 = Scl::Base64.new
|
37
|
+
BINARY = Scl::Binary.new
|
38
|
+
WORDS = Scl::Words.new
|
39
|
+
QRCODE = Scl::QRCode.new
|
40
|
+
HEX = Scl::Hex.new
|
41
|
+
AUTO = Scl::Auto.new
|
42
|
+
STDOUT = Scl::Stdout.new
|
43
|
+
end
|
44
|
+
end
|