3eca8453 |
1 | /* |
a52f067e |
2 | * Generic SSH public-key handling operations. In particular, |
3 | * reading of SSH public-key files, and also the generic `sign' |
4 | * operation for ssh2 (which checks the type of the key and |
5 | * dispatches to the appropriate key-type specific function). |
3eca8453 |
6 | */ |
7 | |
8 | #include <stdio.h> |
a52f067e |
9 | #include <stdlib.h> |
3eca8453 |
10 | |
3eca8453 |
11 | #include <stdarg.h> /* FIXME */ |
12 | #include <windows.h> /* FIXME */ |
3eca8453 |
13 | |
a52f067e |
14 | #include "putty.h" |
3eca8453 |
15 | #include "ssh.h" |
16 | |
17 | #define GET_32BIT(cp) \ |
18 | (((unsigned long)(unsigned char)(cp)[0] << 24) | \ |
19 | ((unsigned long)(unsigned char)(cp)[1] << 16) | \ |
20 | ((unsigned long)(unsigned char)(cp)[2] << 8) | \ |
21 | ((unsigned long)(unsigned char)(cp)[3])) |
22 | |
23 | #define rsa_signature "SSH PRIVATE KEY FILE FORMAT 1.1\n" |
a52f067e |
24 | #define dss_signature "-----BEGIN DSA PRIVATE KEY-----\n" |
3eca8453 |
25 | |
a52f067e |
26 | #define BASE64_TOINT(x) ( (x)-'A'<26 ? (x)-'A'+0 :\ |
27 | (x)-'a'<26 ? (x)-'a'+26 :\ |
28 | (x)-'0'<10 ? (x)-'0'+52 :\ |
29 | (x)=='+' ? 62 : \ |
30 | (x)=='/' ? 63 : 0 ) |
31 | |
32 | static int loadrsakey_main(FILE *fp, struct RSAKey *key, |
33 | char **commentptr, char *passphrase) { |
3eca8453 |
34 | unsigned char buf[16384]; |
35 | unsigned char keybuf[16]; |
36 | int len; |
37 | int i, j, ciphertype; |
38 | int ret = 0; |
39 | struct MD5Context md5c; |
a52f067e |
40 | char *comment; |
3eca8453 |
41 | |
a52f067e |
42 | /* Slurp the whole file (minus the header) into a buffer. */ |
3eca8453 |
43 | len = fread(buf, 1, sizeof(buf), fp); |
44 | fclose(fp); |
45 | if (len < 0 || len == sizeof(buf)) |
46 | goto end; /* file too big or not read */ |
47 | |
a52f067e |
48 | i = 0; |
3eca8453 |
49 | |
a52f067e |
50 | /* |
51 | * A zero byte. (The signature includes a terminating NUL.) |
52 | */ |
53 | if (len-i < 1 || buf[i] != 0) |
54 | goto end; |
55 | i++; |
3eca8453 |
56 | |
a52f067e |
57 | /* One byte giving encryption type, and one reserved uint32. */ |
3eca8453 |
58 | if (len-i < 1) |
59 | goto end; |
60 | ciphertype = buf[i]; |
61 | if (ciphertype != 0 && ciphertype != SSH_CIPHER_3DES) |
62 | goto end; |
63 | i++; |
64 | if (len-i < 4) |
65 | goto end; /* reserved field not present */ |
66 | if (buf[i] != 0 || buf[i+1] != 0 || buf[i+2] != 0 || buf[i+3] != 0) |
67 | goto end; /* reserved field nonzero, panic! */ |
68 | i += 4; |
69 | |
70 | /* Now the serious stuff. An ordinary SSH 1 public key. */ |
71 | i += makekey(buf+i, key, NULL, 1); |
72 | if (len-i < 0) |
73 | goto end; /* overran */ |
74 | |
75 | /* Next, the comment field. */ |
76 | j = GET_32BIT(buf+i); |
5c58ad2d |
77 | i += 4; |
78 | if (len-i < j) goto end; |
a52f067e |
79 | comment = malloc(j+1); |
80 | if (comment) { |
81 | memcpy(comment, buf+i, j); |
82 | comment[j] = '\0'; |
5c58ad2d |
83 | } |
84 | i += j; |
a52f067e |
85 | if (commentptr) |
86 | *commentptr = comment; |
87 | if (key) |
88 | key->comment = comment; |
89 | if (!key) { |
90 | return ciphertype != 0; |
91 | } |
3eca8453 |
92 | |
93 | /* |
94 | * Decrypt remainder of buffer. |
95 | */ |
96 | if (ciphertype) { |
97 | MD5Init(&md5c); |
98 | MD5Update(&md5c, passphrase, strlen(passphrase)); |
99 | MD5Final(keybuf, &md5c); |
100 | des3_decrypt_pubkey(keybuf, buf+i, (len-i+7)&~7); |
5c58ad2d |
101 | memset(keybuf, 0, sizeof(keybuf)); /* burn the evidence */ |
3eca8453 |
102 | } |
103 | |
104 | /* |
105 | * We are now in the secret part of the key. The first four |
106 | * bytes should be of the form a, b, a, b. |
107 | */ |
108 | if (len-i < 4) goto end; |
109 | if (buf[i] != buf[i+2] || buf[i+1] != buf[i+3]) { ret = -1; goto end; } |
110 | i += 4; |
111 | |
112 | /* |
113 | * After that, we have one further bignum which is our |
114 | * decryption modulus, and then we're done. |
115 | */ |
116 | i += makeprivate(buf+i, key); |
117 | if (len-i < 0) goto end; |
118 | |
119 | ret = 1; |
120 | end: |
121 | memset(buf, 0, sizeof(buf)); /* burn the evidence */ |
122 | return ret; |
123 | } |
124 | |
a52f067e |
125 | int loadrsakey(char *filename, struct RSAKey *key, char *passphrase) { |
3eca8453 |
126 | FILE *fp; |
a52f067e |
127 | unsigned char buf[64]; |
3eca8453 |
128 | |
129 | fp = fopen(filename, "rb"); |
130 | if (!fp) |
131 | return 0; /* doesn't even exist */ |
132 | |
a52f067e |
133 | /* |
134 | * Read the first line of the file and see if it's a v1 private |
135 | * key file. |
136 | */ |
137 | if (fgets(buf, sizeof(buf), fp) && |
138 | !strcmp(buf, rsa_signature)) { |
139 | return loadrsakey_main(fp, key, NULL, passphrase); |
140 | } |
141 | |
142 | /* |
143 | * Otherwise, we have nothing. Return empty-handed. |
144 | */ |
3eca8453 |
145 | fclose(fp); |
3eca8453 |
146 | return 0; |
147 | } |
a52f067e |
148 | |
149 | /* |
150 | * See whether an RSA key is encrypted. Return its comment field as |
151 | * well. |
152 | */ |
153 | int rsakey_encrypted(char *filename, char **comment) { |
154 | FILE *fp; |
155 | unsigned char buf[64]; |
156 | |
157 | fp = fopen(filename, "rb"); |
158 | if (!fp) |
159 | return 0; /* doesn't even exist */ |
160 | |
161 | /* |
162 | * Read the first line of the file and see if it's a v1 private |
163 | * key file. |
164 | */ |
165 | if (fgets(buf, sizeof(buf), fp) && |
166 | !strcmp(buf, rsa_signature)) { |
167 | return loadrsakey_main(fp, NULL, comment, NULL); |
168 | } |
169 | return 0; /* wasn't the right kind of file */ |
170 | } |