]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - sshpubk.c
Make memory management uniform: _everything_ now goes through the
[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 = smalloc(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     fclose(fp);
181     return 0;                          /* wasn't the right kind of file */
182 }
183
184 /*
185  * Save an RSA key file. Return nonzero on success.
186  */
187 int saversakey(char *filename, struct RSAKey *key, struct RSAAux *aux,
188                char *passphrase) {
189     unsigned char buf[16384];
190     unsigned char keybuf[16];
191     struct MD5Context md5c;
192     unsigned char *p, *estart;
193     FILE *fp;
194
195     /*
196      * Write the initial signature.
197      */
198     p = buf;
199     memcpy(p, rsa_signature, sizeof(rsa_signature));
200     p += sizeof(rsa_signature);
201
202     /*
203      * One byte giving encryption type, and one reserved (zero)
204      * uint32.
205      */
206     *p++ = (passphrase ? SSH_CIPHER_3DES : 0);
207     PUT_32BIT(p, 0); p += 4;
208
209     /*
210      * An ordinary SSH 1 public key consists of: a uint32
211      * containing the bit count, then two bignums containing the
212      * modulus and exponent respectively.
213      */
214     PUT_32BIT(p, ssh1_bignum_bitcount(key->modulus)); p += 4;
215     p += ssh1_write_bignum(p, key->modulus);
216     p += ssh1_write_bignum(p, key->exponent);
217
218     /*
219      * A string containing the comment field.
220      */
221     if (key->comment) {
222         PUT_32BIT(p, strlen(key->comment)); p += 4;
223         memcpy(p, key->comment, strlen(key->comment));
224         p += strlen(key->comment);
225     } else {
226         PUT_32BIT(p, 0); p += 4;
227     }
228
229     /*
230      * The encrypted portion starts here.
231      */
232     estart = p;
233
234     /*
235      * Two bytes, then the same two bytes repeated.
236      */
237     *p++ = random_byte();
238     *p++ = random_byte();
239     p[0] = p[-2]; p[1] = p[-1]; p += 2;
240
241     /*
242      * Four more bignums: the decryption exponent, then iqmp, then
243      * q, then p.
244      */
245     p += ssh1_write_bignum(p, key->private_exponent);
246     p += ssh1_write_bignum(p, aux->iqmp);
247     p += ssh1_write_bignum(p, aux->q);
248     p += ssh1_write_bignum(p, aux->p);
249
250     /*
251      * Now write zeros until the encrypted portion is a multiple of
252      * 8 bytes.
253      */
254     while ((p-estart) % 8)
255         *p++ = '\0';
256
257     /*
258      * Now encrypt the encrypted portion.
259      */
260     if (passphrase) {
261         MD5Init(&md5c);
262         MD5Update(&md5c, passphrase, strlen(passphrase));
263         MD5Final(keybuf, &md5c);
264         des3_encrypt_pubkey(keybuf, estart, p-estart);
265         memset(keybuf, 0, sizeof(keybuf));    /* burn the evidence */
266     }
267
268     /*
269      * Done. Write the result to the file.
270      */
271     fp = fopen(filename, "wb");
272     if (fp) {
273         int ret = (fwrite(buf, 1, p-buf, fp) == (size_t)(p-buf));
274         ret = ret && (fclose(fp) == 0);
275         return ret;
276     } else
277         return 0;
278 }