]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - sshdss.c
RSA key authentication in ssh1 works; SSH2 is nearly there
[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 static void getstring(char **data, int *datalen, char **p, int *length) {
13     *p = NULL;
14     if (*datalen < 4)
15         return;
16     *length = GET_32BIT(*data);
17     *datalen -= 4; *data += 4;
18     if (*datalen < *length)
19         return;
20     *p = *data;
21     *data += *length; *datalen -= *length;
22 }
23 static Bignum getmp(char **data, int *datalen) {
24     char *p;
25     int i, j, length;
26     Bignum b;
27
28     getstring(data, datalen, &p, &length);
29     if (!p)
30         return NULL;
31     if (p[0] & 0x80)
32         return NULL;                   /* negative mp */
33     b = newbn((length+1)/2);
34     for (i = 0; i < length; i++) {
35         j = length - 1 - i;
36         if (j & 1)
37             b[j/2+1] |= ((unsigned char)p[i]) << 8;
38         else
39             b[j/2+1] |= ((unsigned char)p[i]);
40     }
41     return b;
42 }
43
44 static Bignum get160(char **data, int *datalen) {
45     char *p;
46     int i, j, length;
47     Bignum b;
48
49     p = *data;
50     *data += 20; *datalen -= 20;
51
52     length = 20;
53     while (length > 0 && !p[0])
54         p++, length--;
55     b = newbn((length+1)/2);
56     for (i = 0; i < length; i++) {
57         j = length - 1 - i;
58         if (j & 1)
59             b[j/2+1] |= ((unsigned char)p[i]) << 8;
60         else
61             b[j/2+1] |= ((unsigned char)p[i]);
62     }
63     return b;
64 }
65
66 static Bignum dss_p, dss_q, dss_g, dss_y;
67
68 static void dss_setkey(char *data, int len) {
69     char *p;
70     int slen;
71     getstring(&data, &len, &p, &slen);
72     if (!p || memcmp(p, "ssh-dss", 7)) {
73         dss_p = NULL;
74         return;
75     }
76     dss_p = getmp(&data, &len);
77     dss_q = getmp(&data, &len);
78     dss_g = getmp(&data, &len);
79     dss_y = getmp(&data, &len);
80 }
81
82 static char *dss_fmtkey(void) {
83     char *p;
84     int len;
85     int i;
86     if (!dss_p)
87         return NULL;
88     len = 7 + 4 + 1;                   /* "ssh-dss", punctuation, \0 */
89     len += 4 * (dss_p[0] + dss_q[0] + dss_g[0] + dss_y[0]);   /* digits */
90     p = malloc(len);
91     if (!p) return NULL;
92     strcpy(p, "ssh-dss:");
93     for (i = dss_p[0]; i > 0; i--) sprintf(p+strlen(p), "%04X", dss_p[i]);
94     strcat(p, "/");
95     for (i = dss_q[0]; i > 0; i--) sprintf(p+strlen(p), "%04X", dss_q[i]);
96     strcat(p, "/");
97     for (i = dss_g[0]; i > 0; i--) sprintf(p+strlen(p), "%04X", dss_g[i]);
98     strcat(p, "/");
99     for (i = dss_y[0]; i > 0; i--) sprintf(p+strlen(p), "%04X", dss_y[i]);
100     return p;
101 }
102
103 static int dss_verifysig(char *sig, int siglen, char *data, int datalen) {
104     char *p;
105     int i, slen;
106     char hash[20];
107     Bignum qm2, r, s, w, i1, i2, i3, u1, u2, sha, v;
108     int ret;
109
110     if (!dss_p)
111         return 0;
112
113     getstring(&sig, &siglen, &p, &slen);
114     if (!p || memcmp(p, "ssh-dss", 7)) {
115         return 0;
116     }
117     sig += 4, siglen -= 4;             /* skip yet another length field */
118     r = get160(&sig, &siglen);
119     s = get160(&sig, &siglen);
120     if (!r || !s)
121         return 0;
122
123     /*
124      * Step 1. w <- s^-1 mod q.
125      */
126     w = newbn(dss_q[0]);
127     qm2 = copybn(dss_q);
128     decbn(qm2); decbn(qm2);
129     /* Now qm2 is q-2, and by Fermat's Little Theorem, s^qm2 == s^-1 (mod q).
130      * This is a silly way to do it; may fix it later. */
131     modpow(s, qm2, dss_q, w);
132
133     /*
134      * Step 2. u1 <- SHA(message) * w mod q.
135      */
136     u1 = newbn(dss_q[0]);
137     SHA_Simple(data, datalen, hash);
138     p = hash; slen = 20; sha = get160(&p, &slen);
139     modmul(sha, w, dss_q, u1);
140
141     /*
142      * Step 3. u2 <- r * w mod q.
143      */
144     u2 = newbn(dss_q[0]);
145     modmul(r, w, dss_q, u2);
146
147     /*
148      * Step 4. v <- (g^u1 * y^u2 mod p) mod q.
149      */
150     i1 = newbn(dss_p[0]);
151     i2 = newbn(dss_p[0]);
152     i3 = newbn(dss_p[0]);
153     v = newbn(dss_q[0]);
154     modpow(dss_g, u1, dss_p, i1);
155     modpow(dss_y, u2, dss_p, i2);
156     modmul(i1, i2, dss_p, i3);
157     modmul(i3, One, dss_q, v);
158
159     /*
160      * Step 5. v should now be equal to r.
161      */
162
163     ret = 1;
164     for (i = 1; i <= v[0] || i <= r[0]; i++) {
165         if ((i > v[0] && r[i] != 0) ||
166             (i > r[0] && v[i] != 0) ||
167             (i <= v[0] && i <= r[0] && r[i] != v[i]))
168             ret = 0;
169     }
170
171     freebn(w);
172     freebn(qm2);
173     freebn(sha);
174     freebn(i1);
175     freebn(i2);
176     freebn(i3);
177     freebn(v);
178     freebn(r);
179     freebn(s);
180
181     return ret;
182 }
183
184 struct ssh_hostkey ssh_dss = {
185     dss_setkey,
186     dss_fmtkey,
187     dss_verifysig,
188     "ssh-dss"
189 };