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
+ ]
0 commit comments