1
+ REBOL [
2
+ Title: "REBOL 3 codec: PuTTY Private Key"
3
+ Author: "Oldes"
4
+ Rights: "Copyright (C) 2020 Oldes. All rights reserved."
5
+ License: "BSD-3"
6
+ Test: %tests/units/rsa-test.r3
7
+ Note: {
8
+ * it extract (and inits) only RSA keys so far
9
+ }
10
+ ]
11
+
12
+ register-codec [
13
+ name: 'ppk
14
+ title: "PuTTY Private Key"
15
+ suffixes: [%.ppk ]
16
+
17
+ decode : function [
18
+ "Decodes PuTTY key file"
19
+ data [binary! string! file! ]
20
+ /password pass [string! binary! ] "Optional password for encrypted keys"
21
+ /local type encr comm line pmac
22
+ ;return: [handle! none!] "RSA private key handle on success"
23
+ ] [
24
+ if file? data [data: read /string data]
25
+ if binary? data [data: to string! data]
26
+ sp: charset " ^-^/^M "
27
+ !sp: complement sp
28
+ !crlf: complement charset "^M^/ "
29
+ try /except [
30
+ parse data [
31
+ "PuTTY-User-Key-File-" ["1:" (vers: 1 ) | "2:" (vers: 2 )]
32
+ any sp copy type some !sp some sp
33
+ "Encryption:"
34
+ any sp copy encr some !sp some sp
35
+ "Comment: "
36
+ any sp copy comm some !crlf some sp
37
+ "Public-Lines:"
38
+ any sp copy num some !sp some sp
39
+ (
40
+ num: to integer! to string! num
41
+ pub: make binary! 64 * num
42
+ )
43
+ num [ copy line any !crlf some sp (append pub line) ]
44
+ "Private-Lines:"
45
+ any sp copy num some !sp some sp
46
+ (
47
+ num: to integer! to string! num
48
+ pri: make binary! 64 * num
49
+ )
50
+ num [ copy line any !crlf some sp (append pri line) ]
51
+ ["Private-MAC:" (mac?: true) | "Private-Hash:" (mac?: false)]
52
+ any sp copy pmac some !sp any sp
53
+
54
+ |
55
+
56
+ "---- BEGIN SSH2 PUBLIC KEY ----" to end (
57
+ return codecs/ssh-key/decode/password data pass
58
+ )
59
+
60
+ ]
61
+ pub: debase pub 64
62
+ pri: debase pri 64
63
+
64
+ if encr = "aes256-cbc" [
65
+ try /except [
66
+ pass: either password [copy pass][
67
+ ask/hide ajoin ["Key password for " mold comm ": " ]
68
+ ]
69
+ key: join checksum /secure join #{ 00000000 } pass
70
+ checksum /secure join #{ 00000001 } pass
71
+ key: aes/decrypt/key copy/part key 32 none
72
+ pri: aes/decrypt/stream key pri
73
+ ][
74
+ ;clean pass data in memory
75
+ forall pass [pass/1: random 255 ]
76
+ print "Failed to decrypt private key!"
77
+ return none
78
+ ]
79
+ ]
80
+
81
+ macdata: either vers = 1 [ pri ][
82
+ select binary/write 800 [
83
+ UI32BYTES :type
84
+ UI32BYTES :encr
85
+ UI32BYTES :comm
86
+ UI32BYTES :pub
87
+ UI32BYTES :pri
88
+ ] 'buffer
89
+ ]
90
+ mackey: checksum /secure join "putty-private-key-file-mac-key" any [pass "" ]
91
+ if pass [forall pass [pass/1: random 255 ]]
92
+ if pmac <> form either mac? [
93
+ checksum /secure/key macdata mackey
94
+ ][ checksum /secure macdata ] [
95
+ print either key ["Wrong password!" ]["MAC failed!" ]
96
+ return none
97
+ ]
98
+ binary/read pub [
99
+ t: UI32BYTES
100
+ e: UI32BYTES ;exponent
101
+ n: UI32BYTES ;modulus
102
+ ]
103
+ binary/read pri [
104
+ d: UI32BYTES
105
+ p: UI32BYTES
106
+ q: UI32BYTES
107
+ i: UI32BYTES
108
+ ]
109
+ if "ssh-rsa" = to string! t [
110
+ return rsa-init/private n e d p q none none i
111
+ ]
112
+ ][
113
+ print system/state/last-error
114
+ ]
115
+ none
116
+ ]
117
+ ]
0 commit comments