]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - sshdss.c
Extra crash-safety in decoding a DSS signature blob
[PuTTY.git] / sshdss.c
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 #include "ssh.h"
5
6 #define GET_32BIT(cp) \
7     (((unsigned long)(unsigned char)(cp)[0] << 24) | \
8     ((unsigned long)(unsigned char)(cp)[1] << 16) | \
9     ((unsigned long)(unsigned char)(cp)[2] << 8) | \
10     ((unsigned long)(unsigned char)(cp)[3]))
11
12 #define PUT_32BIT(cp, value) { \
13     (cp)[0] = (unsigned char)((value) >> 24); \
14     (cp)[1] = (unsigned char)((value) >> 16); \
15     (cp)[2] = (unsigned char)((value) >> 8); \
16     (cp)[3] = (unsigned char)(value); }
17
18 #if 0
19 #define DEBUG_DSS
20 #else
21 #define diagbn(x,y)
22 #endif
23
24 static void getstring(char **data, int *datalen, char **p, int *length) {
25     *p = NULL;
26     if (*datalen < 4)
27         return;
28     *length = GET_32BIT(*data);
29     *datalen -= 4; *data += 4;
30     if (*datalen < *length)
31         return;
32     *p = *data;
33     *data += *length; *datalen -= *length;
34 }
35 static Bignum getmp(char **data, int *datalen) {
36     char *p;
37     int length;
38     Bignum b;
39
40     getstring(data, datalen, &p, &length);
41     if (!p)
42         return NULL;
43     if (p[0] & 0x80)
44         return NULL;                   /* negative mp */
45     b = bignum_from_bytes(p, length);
46     return b;
47 }
48
49 static Bignum get160(char **data, int *datalen) {
50     Bignum b;
51
52     b = bignum_from_bytes(*data, 20);
53     *data += 20; *datalen -= 20;
54
55     return b;
56 }
57
58 struct dss_key {
59     Bignum p, q, g, y;
60 };
61
62 static void *dss_newkey(char *data, int len) {
63     char *p;
64     int slen;
65     struct dss_key *dss;
66
67     dss = smalloc(sizeof(struct dss_key));
68     if (!dss) return NULL;
69     getstring(&data, &len, &p, &slen);
70
71 #ifdef DEBUG_DSS
72     {
73         int i;
74         printf("key:");
75         for (i=0;i<len;i++)
76             printf("  %02x", (unsigned char)(data[i]));
77         printf("\n");
78     }
79 #endif
80
81     if (!p || memcmp(p, "ssh-dss", 7)) {
82         sfree(dss);
83         return NULL;
84     }
85     dss->p = getmp(&data, &len);
86     dss->q = getmp(&data, &len);
87     dss->g = getmp(&data, &len);
88     dss->y = getmp(&data, &len);
89
90     return dss;
91 }
92
93 static void dss_freekey(void *key) {
94     struct dss_key *dss = (struct dss_key *)key;
95     freebn(dss->p);
96     freebn(dss->q);
97     freebn(dss->g);
98     freebn(dss->y);
99     sfree(dss);
100 }
101
102 static char *dss_fmtkey(void *key) {
103     struct dss_key *dss = (struct dss_key *)key;
104     char *p;
105     int len, i, pos, nibbles;
106     static const char hex[] = "0123456789abcdef";
107     if (!dss->p)
108         return NULL;
109     len = 8 + 4 + 1;                   /* 4 x "0x", punctuation, \0 */
110     len += 4 * (ssh1_bignum_bitcount(dss->p)+15)/16;
111     len += 4 * (ssh1_bignum_bitcount(dss->q)+15)/16;
112     len += 4 * (ssh1_bignum_bitcount(dss->g)+15)/16;
113     len += 4 * (ssh1_bignum_bitcount(dss->y)+15)/16;
114     p = smalloc(len);
115     if (!p) return NULL;
116
117     pos = 0;
118     pos += sprintf(p+pos, "0x");
119     nibbles = (3 + ssh1_bignum_bitcount(dss->p))/4; if (nibbles<1) nibbles=1;
120     for (i=nibbles; i-- ;)
121         p[pos++] = hex[(bignum_byte(dss->p, i/2) >> (4*(i%2))) & 0xF];
122     pos += sprintf(p+pos, ",0x");
123     nibbles = (3 + ssh1_bignum_bitcount(dss->q))/4; if (nibbles<1) nibbles=1;
124     for (i=nibbles; i-- ;)
125         p[pos++] = hex[(bignum_byte(dss->q, i/2) >> (4*(i%2))) & 0xF];
126     pos += sprintf(p+pos, ",0x");
127     nibbles = (3 + ssh1_bignum_bitcount(dss->g))/4; if (nibbles<1) nibbles=1;
128     for (i=nibbles; i-- ;)
129         p[pos++] = hex[(bignum_byte(dss->g, i/2) >> (4*(i%2))) & 0xF];
130     pos += sprintf(p+pos, ",0x");
131     nibbles = (3 + ssh1_bignum_bitcount(dss->y))/4; if (nibbles<1) nibbles=1;
132     for (i=nibbles; i-- ;)
133         p[pos++] = hex[(bignum_byte(dss->y, i/2) >> (4*(i%2))) & 0xF];
134     p[pos] = '\0';
135     return p;
136 }
137
138 static char *dss_fingerprint(void *key) {
139     struct dss_key *dss = (struct dss_key *)key;
140     struct MD5Context md5c;
141     unsigned char digest[16], lenbuf[4];
142     char buffer[16*3+40];
143     char *ret;
144     int numlen, i;
145
146     MD5Init(&md5c);
147     MD5Update(&md5c, "\0\0\0\7ssh-dss", 11);
148
149 #define ADD_BIGNUM(bignum) \
150     numlen = (ssh1_bignum_bitcount(bignum)+8)/8; \
151     PUT_32BIT(lenbuf, numlen); MD5Update(&md5c, lenbuf, 4); \
152     for (i = numlen; i-- ;) { \
153         unsigned char c = bignum_byte(bignum, i); \
154         MD5Update(&md5c, &c, 1); \
155     }
156     ADD_BIGNUM(dss->p);
157     ADD_BIGNUM(dss->q);
158     ADD_BIGNUM(dss->g);
159     ADD_BIGNUM(dss->y);
160 #undef ADD_BIGNUM
161
162     MD5Final(digest, &md5c);
163
164     sprintf(buffer, "%d ", ssh1_bignum_bitcount(dss->p));
165     for (i = 0; i < 16; i++)
166         sprintf(buffer+strlen(buffer), "%s%02x", i?":":"", digest[i]);
167     ret = smalloc(strlen(buffer)+1);
168     if (ret)
169         strcpy(ret, buffer);
170     return ret;
171 }
172
173 static int dss_verifysig(void *key, char *sig, int siglen,
174                          char *data, int datalen) {
175     struct dss_key *dss = (struct dss_key *)key;
176     char *p;
177     int slen;
178     char hash[20];
179     Bignum r, s, w, gu1p, yu2p, gu1yu2p, u1, u2, sha, v;
180     int ret;
181
182     if (!dss->p)
183         return 0;
184
185 #ifdef DEBUG_DSS
186     {
187         int i;
188         printf("sig:");
189         for (i=0;i<siglen;i++)
190             printf("  %02x", (unsigned char)(sig[i]));
191         printf("\n");
192     }
193 #endif
194     /*
195      * Commercial SSH (2.0.13) and OpenSSH disagree over the format
196      * of a DSA signature. OpenSSH is in line with the IETF drafts:
197      * it uses a string "ssh-dss", followed by a 40-byte string
198      * containing two 160-bit integers end-to-end. Commercial SSH
199      * can't be bothered with the header bit, and considers a DSA
200      * signature blob to be _just_ the 40-byte string containing
201      * the two 160-bit integers. We tell them apart by measuring
202      * the length: length 40 means the commercial-SSH bug, anything
203      * else is assumed to be IETF-compliant.
204      */
205     if (siglen != 40) {                /* bug not present; read admin fields */
206         getstring(&sig, &siglen, &p, &slen);
207         if (!p || slen != 7 || memcmp(p, "ssh-dss", 7)) {
208             return 0;
209         }
210         sig += 4, siglen -= 4;             /* skip yet another length field */
211     }
212     diagbn("p=", dss->p);
213     diagbn("q=", dss->q);
214     diagbn("g=", dss->g);
215     diagbn("y=", dss->y);
216     r = get160(&sig, &siglen);
217     diagbn("r=", r);
218     s = get160(&sig, &siglen);
219     diagbn("s=", s);
220     if (!r || !s)
221         return 0;
222
223     /*
224      * Step 1. w <- s^-1 mod q.
225      */
226     w = modinv(s, dss->q);
227     diagbn("w=", w);
228
229     /*
230      * Step 2. u1 <- SHA(message) * w mod q.
231      */
232     SHA_Simple(data, datalen, hash);
233     p = hash; slen = 20; sha = get160(&p, &slen);
234     diagbn("sha=", sha);
235     u1 = modmul(sha, w, dss->q);
236     diagbn("u1=", u1);
237
238     /*
239      * Step 3. u2 <- r * w mod q.
240      */
241     u2 = modmul(r, w, dss->q);
242     diagbn("u2=", u2);
243
244     /*
245      * Step 4. v <- (g^u1 * y^u2 mod p) mod q.
246      */
247     gu1p = modpow(dss->g, u1, dss->p);
248     diagbn("gu1p=", gu1p);
249     yu2p = modpow(dss->y, u2, dss->p);
250     diagbn("yu2p=", yu2p);
251     gu1yu2p = modmul(gu1p, yu2p, dss->p);
252     diagbn("gu1yu2p=", gu1yu2p);
253     v = modmul(gu1yu2p, One, dss->q);
254     diagbn("gu1yu2q=v=", v);
255     diagbn("r=", r);
256
257     /*
258      * Step 5. v should now be equal to r.
259      */
260
261     ret = !bignum_cmp(v, r);
262
263     freebn(w);
264     freebn(sha);
265     freebn(gu1p);
266     freebn(yu2p);
267     freebn(gu1yu2p);
268     freebn(v);
269     freebn(r);
270     freebn(s);
271
272     return ret;
273 }
274
275 int dss_sign(void *key, char *sig, int siglen,
276              char *data, int datalen) {
277     return 0;                          /* do nothing */
278 }
279
280 struct ssh_signkey ssh_dss = {
281     dss_newkey,
282     dss_freekey,
283     dss_fmtkey,
284     dss_fingerprint,
285     dss_verifysig,
286     dss_sign,
287     "ssh-dss",
288     "dss"
289 };