]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - sshpubk.c
PuTTYgen initial version. Still to do are basic user-friendliness
[PuTTY.git] / sshpubk.c
1 /*
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).
6  */
7
8 #include <stdio.h>
9 #include <stdlib.h>
10
11 #include "ssh.h"
12
13 #define PUT_32BIT(cp, value) do { \
14   (cp)[3] = (value); \
15   (cp)[2] = (value) >> 8; \
16   (cp)[1] = (value) >> 16; \
17   (cp)[0] = (value) >> 24; } while (0)
18
19 #define GET_32BIT(cp) \
20     (((unsigned long)(unsigned char)(cp)[0] << 24) | \
21     ((unsigned long)(unsigned char)(cp)[1] << 16) | \
22     ((unsigned long)(unsigned char)(cp)[2] << 8) | \
23     ((unsigned long)(unsigned char)(cp)[3]))
24
25 #define rsa_signature "SSH PRIVATE KEY FILE FORMAT 1.1\n"
26
27 #define BASE64_TOINT(x) ( (x)-'A'<26 ? (x)-'A'+0 :\
28                           (x)-'a'<26 ? (x)-'a'+26 :\
29                           (x)-'0'<10 ? (x)-'0'+52 :\
30                           (x)=='+' ? 62 : \
31                           (x)=='/' ? 63 : 0 )
32
33 static int loadrsakey_main(FILE *fp, struct RSAKey *key, struct RSAAux *aux,
34                            char **commentptr, char *passphrase) {
35     unsigned char buf[16384];
36     unsigned char keybuf[16];
37     int len;
38     int i, j, ciphertype;
39     int ret = 0;
40     struct MD5Context md5c;
41     char *comment;
42
43     /* Slurp the whole file (minus the header) into a buffer. */
44     len = fread(buf, 1, sizeof(buf), fp);
45     fclose(fp);
46     if (len < 0 || len == sizeof(buf))
47         goto end;                      /* file too big or not read */
48
49     i = 0;
50
51     /*
52      * A zero byte. (The signature includes a terminating NUL.)
53      */
54     if (len-i < 1 || buf[i] != 0)
55         goto end;
56     i++;
57
58     /* One byte giving encryption type, and one reserved uint32. */
59     if (len-i < 1)
60         goto end;
61     ciphertype = buf[i];
62     if (ciphertype != 0 && ciphertype != SSH_CIPHER_3DES)
63         goto end;
64     i++;
65     if (len-i < 4)
66         goto end;                      /* reserved field not present */
67     if (buf[i] != 0 || buf[i+1] != 0 || buf[i+2] != 0 || buf[i+3] != 0)
68         goto end;                      /* reserved field nonzero, panic! */
69     i += 4;
70
71     /* Now the serious stuff. An ordinary SSH 1 public key. */
72     i += makekey(buf+i, key, NULL, 1);
73     if (len-i < 0)
74         goto end;                      /* overran */
75
76     /* Next, the comment field. */
77     j = GET_32BIT(buf+i);
78     i += 4;
79     if (len-i < j) goto end;
80     comment = malloc(j+1);
81     if (comment) {
82         memcpy(comment, buf+i, j);
83         comment[j] = '\0';
84     }
85     i += j;
86     if (commentptr)
87         *commentptr = comment;
88     if (key)
89         key->comment = comment;
90     if (!key) {
91         return ciphertype != 0;
92     }
93
94     /*
95      * Decrypt remainder of buffer.
96      */
97     if (ciphertype) {
98         MD5Init(&md5c);
99         MD5Update(&md5c, passphrase, strlen(passphrase));
100         MD5Final(keybuf, &md5c);
101         des3_decrypt_pubkey(keybuf, buf+i, (len-i+7)&~7);
102         memset(keybuf, 0, sizeof(keybuf));    /* burn the evidence */
103     }
104
105     /*
106      * We are now in the secret part of the key. The first four
107      * bytes should be of the form a, b, a, b.
108      */
109     if (len-i < 4) goto end;
110     if (buf[i] != buf[i+2] || buf[i+1] != buf[i+3]) { ret = -1; goto end; }
111     i += 4;
112
113     /*
114      * After that, we have one further bignum which is our
115      * decryption exponent, and then the three auxiliary values
116      * (iqmp, q, p).
117      */
118     i += makeprivate(buf+i, key);
119     if (len-i < 0) goto end;
120     if (aux) {
121         i += ssh1_read_bignum(buf+i, &aux->iqmp);
122         if (len-i < 0) goto end;
123         i += ssh1_read_bignum(buf+i, &aux->q);
124         if (len-i < 0) goto end;
125         i += ssh1_read_bignum(buf+i, &aux->p);
126         if (len-i < 0) goto end;
127     }
128
129     ret = 1;
130     end:
131     memset(buf, 0, sizeof(buf));       /* burn the evidence */
132     return ret;
133 }
134
135 int loadrsakey(char *filename, struct RSAKey *key, struct RSAAux *aux,
136                char *passphrase) {
137     FILE *fp;
138     unsigned char buf[64];
139
140     fp = fopen(filename, "rb");
141     if (!fp)
142         return 0;                      /* doesn't even exist */
143
144     /*
145      * Read the first line of the file and see if it's a v1 private
146      * key file.
147      */
148     if (fgets(buf, sizeof(buf), fp) &&
149         !strcmp(buf, rsa_signature)) {
150         return loadrsakey_main(fp, key, aux, NULL, passphrase);
151     }
152
153     /*
154      * Otherwise, we have nothing. Return empty-handed.
155      */
156     fclose(fp);
157     return 0;
158 }
159
160 /*
161  * See whether an RSA key is encrypted. Return its comment field as
162  * well.
163  */
164 int rsakey_encrypted(char *filename, char **comment) {
165     FILE *fp;
166     unsigned char buf[64];
167
168     fp = fopen(filename, "rb");
169     if (!fp)
170         return 0;                      /* doesn't even exist */
171
172     /*
173      * Read the first line of the file and see if it's a v1 private
174      * key file.
175      */
176     if (fgets(buf, sizeof(buf), fp) &&
177         !strcmp(buf, rsa_signature)) {
178         return loadrsakey_main(fp, NULL, NULL, comment, NULL);
179     }
180     return 0;                          /* wasn't the right kind of file */
181 }
182
183 /*
184  * Save an RSA key file. Return nonzero on success.
185  */
186 int saversakey(char *filename, struct RSAKey *key, struct RSAAux *aux,
187                char *passphrase) {
188     unsigned char buf[16384];
189     unsigned char keybuf[16];
190     struct MD5Context md5c;
191     char *p, *estart;
192     FILE *fp;
193
194     /*
195      * Write the initial signature.
196      */
197     p = buf;
198     memcpy(p, rsa_signature, sizeof(rsa_signature));
199     p += sizeof(rsa_signature);
200
201     /*
202      * One byte giving encryption type, and one reserved (zero)
203      * uint32.
204      */
205     *p++ = (passphrase ? SSH_CIPHER_3DES : 0);
206     PUT_32BIT(p, 0); p += 4;
207
208     /*
209      * An ordinary SSH 1 public key consists of: a uint32
210      * containing the bit count, then two bignums containing the
211      * modulus and exponent respectively.
212      */
213     PUT_32BIT(p, ssh1_bignum_bitcount(key->modulus)); p += 4;
214     p += ssh1_write_bignum(p, key->modulus);
215     p += ssh1_write_bignum(p, key->exponent);
216
217     /*
218      * A string containing the comment field.
219      */
220     if (key->comment) {
221         PUT_32BIT(p, strlen(key->comment)); p += 4;
222         memcpy(p, key->comment, strlen(key->comment));
223         p += strlen(key->comment);
224     } else {
225         PUT_32BIT(p, 0); p += 4;
226     }
227
228     /*
229      * The encrypted portion starts here.
230      */
231     estart = p;
232
233     /*
234      * Two bytes, then the same two bytes repeated.
235      */
236     *p++ = random_byte();
237     *p++ = random_byte();
238     p[0] = p[-2]; p[1] = p[-1]; p += 2;
239
240     /*
241      * Four more bignums: the decryption exponent, then iqmp, then
242      * q, then p.
243      */
244     p += ssh1_write_bignum(p, key->private_exponent);
245     p += ssh1_write_bignum(p, aux->iqmp);
246     p += ssh1_write_bignum(p, aux->q);
247     p += ssh1_write_bignum(p, aux->p);
248
249     /*
250      * Now write zeros until the encrypted portion is a multiple of
251      * 8 bytes.
252      */
253     while ((p-estart) % 8)
254         *p++ = '\0';
255
256     /*
257      * Now encrypt the encrypted portion.
258      */
259     if (passphrase) {
260         MD5Init(&md5c);
261         MD5Update(&md5c, passphrase, strlen(passphrase));
262         MD5Final(keybuf, &md5c);
263         des3_encrypt_pubkey(keybuf, estart, p-estart);
264         memset(keybuf, 0, sizeof(keybuf));    /* burn the evidence */
265     }
266
267     /*
268      * Done. Write the result to the file.
269      */
270     fp = fopen(filename, "wb");
271     if (fp) {
272         int ret = (fwrite(buf, 1, p-buf, fp) == (size_t)(p-buf));
273         ret = ret && (fclose(fp) == 0);
274         return ret;
275     } else
276         return 0;
277 }