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
139
140
|
# File 'lib/net/ssh/authentication/ed25519.rb', line 73
def self.read(datafull, password)
datafull = datafull.strip
raise ArgumentError.new("Expected #{MBEGIN} at start of private key") unless datafull.start_with?(MBEGIN)
raise ArgumentError.new("Expected #{MEND} at end of private key") unless datafull.end_with?(MEND)
datab64 = datafull[MBEGIN.size...-MEND.size]
data = datab64.unpack1("m")
raise ArgumentError.new("Expected #{MAGIC} at start of decoded private key") unless data.start_with?(MAGIC)
buffer = Net::SSH::Buffer.new(data[(MAGIC.size + 1)..-1])
ciphername = buffer.read_string
raise ArgumentError.new("#{ciphername} in private key is not supported") unless
CipherFactory.supported?(ciphername)
kdfname = buffer.read_string
raise ArgumentError.new("Expected #{kdfname} to be or none or bcrypt") unless %w[none bcrypt].include?(kdfname)
kdfopts = Net::SSH::Buffer.new(buffer.read_string)
num_keys = buffer.read_long
raise ArgumentError.new("Only 1 key is supported in ssh keys #{num_keys} was in private key") unless num_keys == 1
_pubkey = buffer.read_string
len = buffer.read_long
encrypted_private = buffer.read(len)
keylen, blocksize, ivlen = CipherFactory.get_lengths(ciphername, iv_len: true)
raise ArgumentError.new("Private key len:#{len} is not a multiple of #{blocksize}") if
(len < blocksize) || ((blocksize > 0) && (len % blocksize) != 0)
raise ArgumentError.new("Private key len:#{len} exceeds available data") unless encrypted_private.bytesize == len
authlen = CipherFactory.auth_length(ciphername)
auth_tag = authlen > 0 ? buffer.read(authlen) : nil
if kdfname == 'bcrypt'
salt = kdfopts.read_string
rounds = kdfopts.read_long
key = bcrypt_pbkdf_key(password, salt, keylen + ivlen, rounds)
else
key = "\x00" * (keylen + ivlen)
end
decoded = decrypt_private_key(
ciphername,
encrypted_private,
auth_tag,
key: key[0...keylen],
iv: key[keylen...(keylen + ivlen)],
encrypted_key: kdfname == 'bcrypt'
)
decoded = Net::SSH::Buffer.new(decoded)
check1 = decoded.read_long
check2 = decoded.read_long
raise DecryptError.new("Decrypt failed on private key", encrypted_key: kdfname == 'bcrypt') if (check1 != check2)
type_name = decoded.read_string
case type_name
when "ssh-ed25519"
PrivKey.new(decoded)
else
decoded.read_private_keyblob(type_name)
end
end
|