Skip to content

Commit 4c07b7a

Browse files
committed
FEAT: added ssh-key (Secure Shell Key) codec (so far only RSA keys)
1 parent 3d1436a commit 4c07b7a

File tree

4 files changed

+167
-2
lines changed

4 files changed

+167
-2
lines changed

src/mezz/boot-files.r

+6-2
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,20 @@ REBOL [
5050
%mezz-date.r ; Internet date support
5151
;%mezz-tag.r ; build-tag
5252
%mezz-tail.r
53-
%codec-json.r
5453
%codec-unixtime.r
54+
;- cryptographic
5555
%codec-utc-time.r
5656
%codec-pkix.r
5757
%codec-der.r
5858
%codec-crt.r
59+
%codec-ssh-key.r
60+
;- compression
5961
%codec-gzip.r
60-
%codec-xml.r
6162
%codec-zip.r
6263
%codec-tar.r
64+
;- other
65+
%codec-json.r
66+
%codec-xml.r
6367
%codec-html-entities.r
6468
; optional files added in make-boot.r per os and product
6569
;%codec-wav.r

src/mezz/codec-ssh-key.r

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
REBOL [
2+
Title: "REBOL 3 codec: Secure Shell Key"
3+
Author: "Oldes"
4+
Rights: "Copyright (C) 2018 Oldes. All rights reserved."
5+
License: "BSD-3"
6+
Test: %tests/units/crypt-test.r3
7+
Note: {
8+
* it extract (and inits) only RSA keys so far
9+
* encrypted keys only with `AES-128-CBC` encryption
10+
}
11+
]
12+
13+
wrap [
14+
init-from-ssh2-key: function [data][
15+
try [
16+
binary/read data [
17+
v: UI32BYTES
18+
e: UI32BYTES
19+
n: UI32BYTES
20+
]
21+
v: to string! v
22+
if v = "ssh-rsa" [
23+
return rsa-init n e
24+
]
25+
]
26+
print ["Not RSA key! (" v ")"]
27+
none
28+
]
29+
30+
register-codec [
31+
name: 'ssh-key
32+
title: "Secure Shell Key"
33+
; not using suffixes as there is no standard!
34+
35+
decode: function [
36+
"Decodes and initilize SSH key"
37+
key [binary! string! file!]
38+
/password p [string! binary!] "Optional password"
39+
][
40+
case [
41+
file? key [ key: read key ]
42+
string? key [ key: to binary! key ]
43+
]
44+
; try to load key as a PKIX structure
45+
try [ pkix: codecs/pkix/decode key ]
46+
if none? pkix [
47+
; if failed to load as PKIX, try to treat it as a *.PUB file
48+
return either parse key [
49+
"ssh-rsa " copy data to [#" " | end] to end
50+
][ init-from-ssh2-key debase data 64
51+
][ init-from-ssh2-key key ]
52+
]
53+
if "4,ENCRYPTED" = select pkix/header "Proc-Type" [
54+
print "ENCRYPTED key!"
55+
try/except [
56+
dek-info: select pkix/header "DEK-Info"
57+
;probe dek-info
58+
parse dek-info [
59+
"AES-128-CBC" #"," copy iv to end
60+
]
61+
iv: debase iv 16
62+
unless password [p: ask/hide "Pasword: "]
63+
p: checksum/method
64+
join to binary! p copy/part iv 8
65+
'md5
66+
d: aes/key/decrypt p iv
67+
pkix/binary: aes/stream d pkix/binary
68+
][ return none ]
69+
]
70+
71+
switch pkix/label [
72+
"SSH2 PUBLIC KEY" [
73+
return init-from-ssh2-key pkix/binary
74+
]
75+
]
76+
; decode DER structure from decoded PKIX binary
77+
try/except [
78+
data: codecs/der/decode pkix/binary
79+
][
80+
print "Failed to decode DER day for RSA key!"
81+
probe system/state/last-error
82+
return none
83+
]
84+
85+
switch pkix/label [
86+
"PUBLIC KEY" [
87+
; resolve RSA public data from the DER structure (PKCS#1)
88+
all [
89+
parse data [
90+
'SEQUENCE into [
91+
'SEQUENCE set v block! ; AlgorithmIdentifier
92+
'BIT_STRING set data binary! ; PublicKey
93+
(
94+
data: codecs/der/decode data
95+
)
96+
]
97+
]
98+
v/OBJECT_IDENTIFIER = #{2A864886F70D010101} ;= rsaEncryption
99+
parse data [
100+
'SEQUENCE into [
101+
'INTEGER set n binary! ;modulus
102+
'INTEGER set e binary! ;publicExponent
103+
]
104+
]
105+
]
106+
; resolve RSA handle from parsed data
107+
return rsa-init n e
108+
]
109+
"RSA PUBLIC KEY" [
110+
; resolve RSA public data from the DER structure (PKCS#1)
111+
parse data [
112+
'SEQUENCE into [
113+
'INTEGER set n binary! ;modulus
114+
'INTEGER set e binary! ;publicExponent
115+
]
116+
]
117+
; resolve RSA handle from parsed data
118+
return rsa-init n e
119+
]
120+
"RSA PRIVATE KEY" [
121+
; resolve RSA private data from the DER structure (PKCS#1)
122+
parse data [
123+
'SEQUENCE into [
124+
'INTEGER set v binary! ;version
125+
'INTEGER set n binary! ;modulus
126+
'INTEGER set e binary! ;publicExponent
127+
'INTEGER set d binary! ;privateExponent
128+
'INTEGER set p binary! ;prime1
129+
'INTEGER set q binary! ;prime2
130+
'INTEGER set dp binary! ;exponent1 d mod (p-1)
131+
'INTEGER set dq binary! ;exponent2 d mod (q-1)
132+
'INTEGER set inv binary! ;coefficient (inverse of q) mod p
133+
to end
134+
]
135+
to end
136+
]
137+
; resolve RSA handle from parsed data
138+
return rsa-init/private n e d p q dp dq inv
139+
]
140+
]
141+
none ; no success!
142+
]
143+
]
144+
]

src/tests/units/crypt-test.r3

+8
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,8 @@ sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV
176176

177177
--assert block? Load-PKIX pkix
178178
--assert binary? Load-PKIX/binary pkix
179+
--assert error? try [decode 'ssh-key pkix] ;- because it contains unsupported ssh-dss
180+
179181

180182
--test-- "SSH-public-key-3"
181183
pkix:
@@ -217,4 +219,10 @@ sY29ouezv4Xz2PuMch5VGPP+CDqzCM4loWgV
217219

218220
===end-group===
219221

222+
223+
===start-group=== "SSH-key codec"
224+
--test-- "Init RSA key from file"
225+
--assert handle? try [key: decode 'ssh-key read %units/files/rebol-public.ppk]
226+
rsa key none ; release it, as it is not GCed yet.
227+
220228
~~~end-file~~~
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---- BEGIN SSH2 PUBLIC KEY ----
2+
Comment: "rsa-key-rebol-test"
3+
AAAAB3NzaC1yc2EAAAABJQAAAQEA8mjwC6ZCwpQCnDXqU7g2tyvMFoWpJV+myfBz
4+
9zbhw7rHxUmFubMtEwCKQhYccQxbPCcWu/KIg5TOhmcf9RK1xIuUrOUiUdM8uOwR
5+
S+e5kitTUgux/wjyMNlpK5laIS1hFHiFhCxecN7Won3bsPDMm5Wi3dBMv1+1jK1r
6+
lDtJYxDDcJE2T59m1UI4AN/Pm7dndr11yCmUdKy6VACf5V7u4OX5uC3fsTEuCV1P
7+
2WwLBqtZMYG3jjzuRTana+s8n2TXk5D9lXoPMc7Tj4/aSw50UUAYhmVpie/8vtVw
8+
A7MhOCA4us6q3+Mx07vq8EmzbLOGtP9taXQkDF6JR6wY4IEXBw==
9+
---- END SSH2 PUBLIC KEY ----

0 commit comments

Comments
 (0)