forked from net-ssh/net-ssh
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcipher_factory.rb
More file actions
138 lines (118 loc) · 5.15 KB
/
cipher_factory.rb
File metadata and controls
138 lines (118 loc) · 5.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
require 'openssl'
require 'net/ssh/transport/ctr.rb'
require 'net/ssh/transport/aes128_gcm'
require 'net/ssh/transport/aes256_gcm'
require 'net/ssh/transport/key_expander'
require 'net/ssh/transport/identity_cipher'
require 'net/ssh/transport/chacha20_poly1305_cipher_loader'
require 'net/ssh/transport/openssl_cipher_extensions'
module Net
module SSH
module Transport
# Implements a factory of OpenSSL cipher algorithms.
class CipherFactory
# Maps the SSH name of a cipher to it's corresponding OpenSSL name
SSH_TO_OSSL = {
"3des-cbc" => "des-ede3-cbc",
"blowfish-cbc" => "bf-cbc",
"aes256-cbc" => "aes-256-cbc",
"aes192-cbc" => "aes-192-cbc",
"aes128-cbc" => "aes-128-cbc",
"idea-cbc" => "idea-cbc",
"cast128-cbc" => "cast-cbc",
"rijndael-cbc@lysator.liu.se" => "aes-256-cbc",
"3des-ctr" => "des-ede3",
"blowfish-ctr" => "bf-ecb",
"aes256-ctr" => ::OpenSSL::Cipher.ciphers.include?("aes-256-ctr") ? "aes-256-ctr" : "aes-256-ecb",
"aes192-ctr" => ::OpenSSL::Cipher.ciphers.include?("aes-192-ctr") ? "aes-192-ctr" : "aes-192-ecb",
"aes128-ctr" => ::OpenSSL::Cipher.ciphers.include?("aes-128-ctr") ? "aes-128-ctr" : "aes-128-ecb",
'cast128-ctr' => 'cast5-ecb',
'aes128-gcm@openssh.com' => 'aes-128-gcm',
'aes256-gcm@openssh.com' => 'aes-256-gcm',
'chacha20-poly1305@openssh.com' => 'chacha20-poly1305',
'none' => 'none'
}
SSH_TO_CLASS = {
'aes256-gcm@openssh.com' => Net::SSH::Transport::AES256_GCM,
'aes128-gcm@openssh.com' => Net::SSH::Transport::AES128_GCM
}.tap do |hash|
if Net::SSH::Transport::ChaCha20Poly1305CipherLoader::LOADED
hash['chacha20-poly1305@openssh.com'] =
Net::SSH::Transport::ChaCha20Poly1305Cipher
end
end
# Returns true if the underlying OpenSSL library supports the given cipher,
# and false otherwise.
def self.supported?(name)
return true if SSH_TO_CLASS.key?(name)
ossl_name = SSH_TO_OSSL[name] or raise NotImplementedError, "unimplemented cipher `#{name}'"
return true if ossl_name == "none"
return SSH_TO_CLASS.key?(name) || OpenSSL::Cipher.ciphers.include?(ossl_name)
end
# Retrieves a new instance of the named algorithm. The new instance
# will be initialized using an iv and key generated from the given
# iv, key, shared, hash and digester values. Additionally, the
# cipher will be put into encryption or decryption mode, based on the
# value of the +encrypt+ parameter.
def self.get(name, options = {})
klass = SSH_TO_CLASS[name]
unless klass.nil?
key_len = klass.key_length
key = Net::SSH::Transport::KeyExpander.expand_key(key_len, options[:key], options)
return klass.new(encrypt: options[:encrypt], key: key)
end
ossl_name = SSH_TO_OSSL[name] or raise NotImplementedError, "unimplemented cipher `#{name}'"
return IdentityCipher if ossl_name == "none"
cipher = OpenSSL::Cipher.new(ossl_name)
cipher.send(options[:encrypt] ? :encrypt : :decrypt)
cipher.padding = 0
cipher.extend(Net::SSH::Transport::OpenSSLCipherExtensions)
if name =~ /-ctr(@openssh.org)?$/
if ossl_name !~ /-ctr/
cipher.extend(Net::SSH::Transport::CTR)
else
cipher = Net::SSH::Transport::OpenSSLAESCTR.new(cipher)
end
end
cipher.iv = Net::SSH::Transport::KeyExpander.expand_key(cipher.iv_len, options[:iv], options)
key_len = cipher.key_len
cipher.key_len = key_len
cipher.key = Net::SSH::Transport::KeyExpander.expand_key(key_len, options[:key], options)
return cipher
end
# Returns a two-element array containing the [ key-length,
# block-size ] for the named cipher algorithm. If the cipher
# algorithm is unknown, or is "none", 0 is returned for both elements
# of the tuple.
# if :iv_len option is supplied the third return value will be ivlen
def self.get_lengths(name, options = {})
klass = SSH_TO_CLASS[name]
unless klass.nil?
result = [klass.key_length, klass.block_size]
result << klass.iv_len if options[:iv_len]
return result
end
ossl_name = SSH_TO_OSSL[name]
if ossl_name.nil? || ossl_name == "none"
result = [0, 0]
result << 0 if options[:iv_len]
else
cipher = OpenSSL::Cipher.new(ossl_name)
key_len = cipher.key_len
cipher.key_len = key_len
block_size =
case ossl_name
when /\-ctr/
Net::SSH::Transport::OpenSSLAESCTR.block_size
else
cipher.block_size
end
result = [key_len, block_size]
result << cipher.iv_len if options[:iv_len]
end
result
end
end
end
end
end