Keep the font size of 8 for the explorer property page
[TortoiseGit.git] / src / TortoisePlink / SSHDSS.C
blobf40b40eeaaf1cad247dce1a1e91435bbd7acd908
1 /*\r
2  * Digital Signature Standard implementation for PuTTY.\r
3  */\r
4 \r
5 #include <stdio.h>\r
6 #include <stdlib.h>\r
7 #include <assert.h>\r
8 \r
9 #include "ssh.h"\r
10 #include "misc.h"\r
12 static void sha_mpint(SHA_State * s, Bignum b)\r
13 {\r
14     unsigned char lenbuf[4];\r
15     int len;\r
16     len = (bignum_bitcount(b) + 8) / 8;\r
17     PUT_32BIT(lenbuf, len);\r
18     SHA_Bytes(s, lenbuf, 4);\r
19     while (len-- > 0) {\r
20         lenbuf[0] = bignum_byte(b, len);\r
21         SHA_Bytes(s, lenbuf, 1);\r
22     }\r
23     smemclr(lenbuf, sizeof(lenbuf));\r
24 }\r
26 static void sha512_mpint(SHA512_State * s, Bignum b)\r
27 {\r
28     unsigned char lenbuf[4];\r
29     int len;\r
30     len = (bignum_bitcount(b) + 8) / 8;\r
31     PUT_32BIT(lenbuf, len);\r
32     SHA512_Bytes(s, lenbuf, 4);\r
33     while (len-- > 0) {\r
34         lenbuf[0] = bignum_byte(b, len);\r
35         SHA512_Bytes(s, lenbuf, 1);\r
36     }\r
37     smemclr(lenbuf, sizeof(lenbuf));\r
38 }\r
40 static void getstring(const char **data, int *datalen,\r
41                       const char **p, int *length)\r
42 {\r
43     *p = NULL;\r
44     if (*datalen < 4)\r
45         return;\r
46     *length = toint(GET_32BIT(*data));\r
47     if (*length < 0)\r
48         return;\r
49     *datalen -= 4;\r
50     *data += 4;\r
51     if (*datalen < *length)\r
52         return;\r
53     *p = *data;\r
54     *data += *length;\r
55     *datalen -= *length;\r
56 }\r
57 static Bignum getmp(const char **data, int *datalen)\r
58 {\r
59     const char *p;\r
60     int length;\r
61     Bignum b;\r
63     getstring(data, datalen, &p, &length);\r
64     if (!p)\r
65         return NULL;\r
66     if (p[0] & 0x80)\r
67         return NULL;                   /* negative mp */\r
68     b = bignum_from_bytes((const unsigned char *)p, length);\r
69     return b;\r
70 }\r
72 static Bignum get160(const char **data, int *datalen)\r
73 {\r
74     Bignum b;\r
76     if (*datalen < 20)\r
77         return NULL;\r
79     b = bignum_from_bytes((const unsigned char *)*data, 20);\r
80     *data += 20;\r
81     *datalen -= 20;\r
83     return b;\r
84 }\r
86 static void dss_freekey(void *key);    /* forward reference */\r
88 static void *dss_newkey(const struct ssh_signkey *self,\r
89                         const char *data, int len)\r
90 {\r
91     const char *p;\r
92     int slen;\r
93     struct dss_key *dss;\r
95     dss = snew(struct dss_key);\r
96     getstring(&data, &len, &p, &slen);\r
98 #ifdef DEBUG_DSS\r
99     {\r
100         int i;\r
101         printf("key:");\r
102         for (i = 0; i < len; i++)\r
103             printf("  %02x", (unsigned char) (data[i]));\r
104         printf("\n");\r
105     }\r
106 #endif\r
108     if (!p || slen != 7 || memcmp(p, "ssh-dss", 7)) {\r
109         sfree(dss);\r
110         return NULL;\r
111     }\r
112     dss->p = getmp(&data, &len);\r
113     dss->q = getmp(&data, &len);\r
114     dss->g = getmp(&data, &len);\r
115     dss->y = getmp(&data, &len);\r
116     dss->x = NULL;\r
118     if (!dss->p || !dss->q || !dss->g || !dss->y ||\r
119         !bignum_cmp(dss->q, Zero) || !bignum_cmp(dss->p, Zero)) {\r
120         /* Invalid key. */\r
121         dss_freekey(dss);\r
122         return NULL;\r
123     }\r
125     return dss;\r
128 static void dss_freekey(void *key)\r
130     struct dss_key *dss = (struct dss_key *) key;\r
131     if (dss->p)\r
132         freebn(dss->p);\r
133     if (dss->q)\r
134         freebn(dss->q);\r
135     if (dss->g)\r
136         freebn(dss->g);\r
137     if (dss->y)\r
138         freebn(dss->y);\r
139     if (dss->x)\r
140         freebn(dss->x);\r
141     sfree(dss);\r
144 static char *dss_fmtkey(void *key)\r
146     struct dss_key *dss = (struct dss_key *) key;\r
147     char *p;\r
148     int len, i, pos, nibbles;\r
149     static const char hex[] = "0123456789abcdef";\r
150     if (!dss->p)\r
151         return NULL;\r
152     len = 8 + 4 + 1;                   /* 4 x "0x", punctuation, \0 */\r
153     len += 4 * (bignum_bitcount(dss->p) + 15) / 16;\r
154     len += 4 * (bignum_bitcount(dss->q) + 15) / 16;\r
155     len += 4 * (bignum_bitcount(dss->g) + 15) / 16;\r
156     len += 4 * (bignum_bitcount(dss->y) + 15) / 16;\r
157     p = snewn(len, char);\r
158     if (!p)\r
159         return NULL;\r
161     pos = 0;\r
162     pos += sprintf(p + pos, "0x");\r
163     nibbles = (3 + bignum_bitcount(dss->p)) / 4;\r
164     if (nibbles < 1)\r
165         nibbles = 1;\r
166     for (i = nibbles; i--;)\r
167         p[pos++] =\r
168             hex[(bignum_byte(dss->p, i / 2) >> (4 * (i % 2))) & 0xF];\r
169     pos += sprintf(p + pos, ",0x");\r
170     nibbles = (3 + bignum_bitcount(dss->q)) / 4;\r
171     if (nibbles < 1)\r
172         nibbles = 1;\r
173     for (i = nibbles; i--;)\r
174         p[pos++] =\r
175             hex[(bignum_byte(dss->q, i / 2) >> (4 * (i % 2))) & 0xF];\r
176     pos += sprintf(p + pos, ",0x");\r
177     nibbles = (3 + bignum_bitcount(dss->g)) / 4;\r
178     if (nibbles < 1)\r
179         nibbles = 1;\r
180     for (i = nibbles; i--;)\r
181         p[pos++] =\r
182             hex[(bignum_byte(dss->g, i / 2) >> (4 * (i % 2))) & 0xF];\r
183     pos += sprintf(p + pos, ",0x");\r
184     nibbles = (3 + bignum_bitcount(dss->y)) / 4;\r
185     if (nibbles < 1)\r
186         nibbles = 1;\r
187     for (i = nibbles; i--;)\r
188         p[pos++] =\r
189             hex[(bignum_byte(dss->y, i / 2) >> (4 * (i % 2))) & 0xF];\r
190     p[pos] = '\0';\r
191     return p;\r
194 static int dss_verifysig(void *key, const char *sig, int siglen,\r
195                          const char *data, int datalen)\r
197     struct dss_key *dss = (struct dss_key *) key;\r
198     const char *p;\r
199     int slen;\r
200     char hash[20];\r
201     Bignum r, s, w, gu1p, yu2p, gu1yu2p, u1, u2, sha, v;\r
202     int ret;\r
204     if (!dss->p)\r
205         return 0;\r
207 #ifdef DEBUG_DSS\r
208     {\r
209         int i;\r
210         printf("sig:");\r
211         for (i = 0; i < siglen; i++)\r
212             printf("  %02x", (unsigned char) (sig[i]));\r
213         printf("\n");\r
214     }\r
215 #endif\r
216     /*\r
217      * Commercial SSH (2.0.13) and OpenSSH disagree over the format\r
218      * of a DSA signature. OpenSSH is in line with RFC 4253:\r
219      * it uses a string "ssh-dss", followed by a 40-byte string\r
220      * containing two 160-bit integers end-to-end. Commercial SSH\r
221      * can't be bothered with the header bit, and considers a DSA\r
222      * signature blob to be _just_ the 40-byte string containing\r
223      * the two 160-bit integers. We tell them apart by measuring\r
224      * the length: length 40 means the commercial-SSH bug, anything\r
225      * else is assumed to be RFC-compliant.\r
226      */\r
227     if (siglen != 40) {                /* bug not present; read admin fields */\r
228         getstring(&sig, &siglen, &p, &slen);\r
229         if (!p || slen != 7 || memcmp(p, "ssh-dss", 7)) {\r
230             return 0;\r
231         }\r
232         sig += 4, siglen -= 4;         /* skip yet another length field */\r
233     }\r
234     r = get160(&sig, &siglen);\r
235     s = get160(&sig, &siglen);\r
236     if (!r || !s) {\r
237         if (r)\r
238             freebn(r);\r
239         if (s)\r
240             freebn(s);\r
241         return 0;\r
242     }\r
244     if (!bignum_cmp(s, Zero)) {\r
245         freebn(r);\r
246         freebn(s);\r
247         return 0;\r
248     }\r
250     /*\r
251      * Step 1. w <- s^-1 mod q.\r
252      */\r
253     w = modinv(s, dss->q);\r
254     if (!w) {\r
255         freebn(r);\r
256         freebn(s);\r
257         return 0;\r
258     }\r
260     /*\r
261      * Step 2. u1 <- SHA(message) * w mod q.\r
262      */\r
263     SHA_Simple(data, datalen, (unsigned char *)hash);\r
264     p = hash;\r
265     slen = 20;\r
266     sha = get160(&p, &slen);\r
267     u1 = modmul(sha, w, dss->q);\r
269     /*\r
270      * Step 3. u2 <- r * w mod q.\r
271      */\r
272     u2 = modmul(r, w, dss->q);\r
274     /*\r
275      * Step 4. v <- (g^u1 * y^u2 mod p) mod q.\r
276      */\r
277     gu1p = modpow(dss->g, u1, dss->p);\r
278     yu2p = modpow(dss->y, u2, dss->p);\r
279     gu1yu2p = modmul(gu1p, yu2p, dss->p);\r
280     v = modmul(gu1yu2p, One, dss->q);\r
282     /*\r
283      * Step 5. v should now be equal to r.\r
284      */\r
286     ret = !bignum_cmp(v, r);\r
288     freebn(w);\r
289     freebn(sha);\r
290     freebn(u1);\r
291     freebn(u2);\r
292     freebn(gu1p);\r
293     freebn(yu2p);\r
294     freebn(gu1yu2p);\r
295     freebn(v);\r
296     freebn(r);\r
297     freebn(s);\r
299     return ret;\r
302 static unsigned char *dss_public_blob(void *key, int *len)\r
304     struct dss_key *dss = (struct dss_key *) key;\r
305     int plen, qlen, glen, ylen, bloblen;\r
306     int i;\r
307     unsigned char *blob, *p;\r
309     plen = (bignum_bitcount(dss->p) + 8) / 8;\r
310     qlen = (bignum_bitcount(dss->q) + 8) / 8;\r
311     glen = (bignum_bitcount(dss->g) + 8) / 8;\r
312     ylen = (bignum_bitcount(dss->y) + 8) / 8;\r
314     /*\r
315      * string "ssh-dss", mpint p, mpint q, mpint g, mpint y. Total\r
316      * 27 + sum of lengths. (five length fields, 20+7=27).\r
317      */\r
318     bloblen = 27 + plen + qlen + glen + ylen;\r
319     blob = snewn(bloblen, unsigned char);\r
320     p = blob;\r
321     PUT_32BIT(p, 7);\r
322     p += 4;\r
323     memcpy(p, "ssh-dss", 7);\r
324     p += 7;\r
325     PUT_32BIT(p, plen);\r
326     p += 4;\r
327     for (i = plen; i--;)\r
328         *p++ = bignum_byte(dss->p, i);\r
329     PUT_32BIT(p, qlen);\r
330     p += 4;\r
331     for (i = qlen; i--;)\r
332         *p++ = bignum_byte(dss->q, i);\r
333     PUT_32BIT(p, glen);\r
334     p += 4;\r
335     for (i = glen; i--;)\r
336         *p++ = bignum_byte(dss->g, i);\r
337     PUT_32BIT(p, ylen);\r
338     p += 4;\r
339     for (i = ylen; i--;)\r
340         *p++ = bignum_byte(dss->y, i);\r
341     assert(p == blob + bloblen);\r
342     *len = bloblen;\r
343     return blob;\r
346 static unsigned char *dss_private_blob(void *key, int *len)\r
348     struct dss_key *dss = (struct dss_key *) key;\r
349     int xlen, bloblen;\r
350     int i;\r
351     unsigned char *blob, *p;\r
353     xlen = (bignum_bitcount(dss->x) + 8) / 8;\r
355     /*\r
356      * mpint x, string[20] the SHA of p||q||g. Total 4 + xlen.\r
357      */\r
358     bloblen = 4 + xlen;\r
359     blob = snewn(bloblen, unsigned char);\r
360     p = blob;\r
361     PUT_32BIT(p, xlen);\r
362     p += 4;\r
363     for (i = xlen; i--;)\r
364         *p++ = bignum_byte(dss->x, i);\r
365     assert(p == blob + bloblen);\r
366     *len = bloblen;\r
367     return blob;\r
370 static void *dss_createkey(const struct ssh_signkey *self,\r
371                            const unsigned char *pub_blob, int pub_len,\r
372                            const unsigned char *priv_blob, int priv_len)\r
374     struct dss_key *dss;\r
375     const char *pb = (const char *) priv_blob;\r
376     const char *hash;\r
377     int hashlen;\r
378     SHA_State s;\r
379     unsigned char digest[20];\r
380     Bignum ytest;\r
382     dss = dss_newkey(self, (char *) pub_blob, pub_len);\r
383     if (!dss)\r
384         return NULL;\r
385     dss->x = getmp(&pb, &priv_len);\r
386     if (!dss->x) {\r
387         dss_freekey(dss);\r
388         return NULL;\r
389     }\r
391     /*\r
392      * Check the obsolete hash in the old DSS key format.\r
393      */\r
394     hashlen = -1;\r
395     getstring(&pb, &priv_len, &hash, &hashlen);\r
396     if (hashlen == 20) {\r
397         SHA_Init(&s);\r
398         sha_mpint(&s, dss->p);\r
399         sha_mpint(&s, dss->q);\r
400         sha_mpint(&s, dss->g);\r
401         SHA_Final(&s, digest);\r
402         if (0 != memcmp(hash, digest, 20)) {\r
403             dss_freekey(dss);\r
404             return NULL;\r
405         }\r
406     }\r
408     /*\r
409      * Now ensure g^x mod p really is y.\r
410      */\r
411     ytest = modpow(dss->g, dss->x, dss->p);\r
412     if (0 != bignum_cmp(ytest, dss->y)) {\r
413         dss_freekey(dss);\r
414         freebn(ytest);\r
415         return NULL;\r
416     }\r
417     freebn(ytest);\r
419     return dss;\r
422 static void *dss_openssh_createkey(const struct ssh_signkey *self,\r
423                                    const unsigned char **blob, int *len)\r
425     const char **b = (const char **) blob;\r
426     struct dss_key *dss;\r
428     dss = snew(struct dss_key);\r
430     dss->p = getmp(b, len);\r
431     dss->q = getmp(b, len);\r
432     dss->g = getmp(b, len);\r
433     dss->y = getmp(b, len);\r
434     dss->x = getmp(b, len);\r
436     if (!dss->p || !dss->q || !dss->g || !dss->y || !dss->x ||\r
437         !bignum_cmp(dss->q, Zero) || !bignum_cmp(dss->p, Zero)) {\r
438         /* Invalid key. */\r
439         dss_freekey(dss);\r
440         return NULL;\r
441     }\r
443     return dss;\r
446 static int dss_openssh_fmtkey(void *key, unsigned char *blob, int len)\r
448     struct dss_key *dss = (struct dss_key *) key;\r
449     int bloblen, i;\r
451     bloblen =\r
452         ssh2_bignum_length(dss->p) +\r
453         ssh2_bignum_length(dss->q) +\r
454         ssh2_bignum_length(dss->g) +\r
455         ssh2_bignum_length(dss->y) +\r
456         ssh2_bignum_length(dss->x);\r
458     if (bloblen > len)\r
459         return bloblen;\r
461     bloblen = 0;\r
462 #define ENC(x) \\r
463     PUT_32BIT(blob+bloblen, ssh2_bignum_length((x))-4); bloblen += 4; \\r
464     for (i = ssh2_bignum_length((x))-4; i-- ;) blob[bloblen++]=bignum_byte((x),i);\r
465     ENC(dss->p);\r
466     ENC(dss->q);\r
467     ENC(dss->g);\r
468     ENC(dss->y);\r
469     ENC(dss->x);\r
471     return bloblen;\r
474 static int dss_pubkey_bits(const struct ssh_signkey *self,\r
475                            const void *blob, int len)\r
477     struct dss_key *dss;\r
478     int ret;\r
480     dss = dss_newkey(self, (const char *) blob, len);\r
481     if (!dss)\r
482         return -1;\r
483     ret = bignum_bitcount(dss->p);\r
484     dss_freekey(dss);\r
486     return ret;\r
489 Bignum *dss_gen_k(const char *id_string, Bignum modulus, Bignum private_key,\r
490                   unsigned char *digest, int digest_len)\r
492     /*\r
493      * The basic DSS signing algorithm is:\r
494      * \r
495      *  - invent a random k between 1 and q-1 (exclusive).\r
496      *  - Compute r = (g^k mod p) mod q.\r
497      *  - Compute s = k^-1 * (hash + x*r) mod q.\r
498      * \r
499      * This has the dangerous properties that:\r
500      * \r
501      *  - if an attacker in possession of the public key _and_ the\r
502      *    signature (for example, the host you just authenticated\r
503      *    to) can guess your k, he can reverse the computation of s\r
504      *    and work out x = r^-1 * (s*k - hash) mod q. That is, he\r
505      *    can deduce the private half of your key, and masquerade\r
506      *    as you for as long as the key is still valid.\r
507      * \r
508      *  - since r is a function purely of k and the public key, if\r
509      *    the attacker only has a _range of possibilities_ for k\r
510      *    it's easy for him to work through them all and check each\r
511      *    one against r; he'll never be unsure of whether he's got\r
512      *    the right one.\r
513      * \r
514      *  - if you ever sign two different hashes with the same k, it\r
515      *    will be immediately obvious because the two signatures\r
516      *    will have the same r, and moreover an attacker in\r
517      *    possession of both signatures (and the public key of\r
518      *    course) can compute k = (hash1-hash2) * (s1-s2)^-1 mod q,\r
519      *    and from there deduce x as before.\r
520      * \r
521      *  - the Bleichenbacher attack on DSA makes use of methods of\r
522      *    generating k which are significantly non-uniformly\r
523      *    distributed; in particular, generating a 160-bit random\r
524      *    number and reducing it mod q is right out.\r
525      * \r
526      * For this reason we must be pretty careful about how we\r
527      * generate our k. Since this code runs on Windows, with no\r
528      * particularly good system entropy sources, we can't trust our\r
529      * RNG itself to produce properly unpredictable data. Hence, we\r
530      * use a totally different scheme instead.\r
531      * \r
532      * What we do is to take a SHA-512 (_big_) hash of the private\r
533      * key x, and then feed this into another SHA-512 hash that\r
534      * also includes the message hash being signed. That is:\r
535      * \r
536      *   proto_k = SHA512 ( SHA512(x) || SHA160(message) )\r
537      * \r
538      * This number is 512 bits long, so reducing it mod q won't be\r
539      * noticeably non-uniform. So\r
540      * \r
541      *   k = proto_k mod q\r
542      * \r
543      * This has the interesting property that it's _deterministic_:\r
544      * signing the same hash twice with the same key yields the\r
545      * same signature.\r
546      * \r
547      * Despite this determinism, it's still not predictable to an\r
548      * attacker, because in order to repeat the SHA-512\r
549      * construction that created it, the attacker would have to\r
550      * know the private key value x - and by assumption he doesn't,\r
551      * because if he knew that he wouldn't be attacking k!\r
552      *\r
553      * (This trick doesn't, _per se_, protect against reuse of k.\r
554      * Reuse of k is left to chance; all it does is prevent\r
555      * _excessively high_ chances of reuse of k due to entropy\r
556      * problems.)\r
557      * \r
558      * Thanks to Colin Plumb for the general idea of using x to\r
559      * ensure k is hard to guess, and to the Cambridge University\r
560      * Computer Security Group for helping to argue out all the\r
561      * fine details.\r
562      */\r
563     SHA512_State ss;\r
564     unsigned char digest512[64];\r
565     Bignum proto_k, k;\r
567     /*\r
568      * Hash some identifying text plus x.\r
569      */\r
570     SHA512_Init(&ss);\r
571     SHA512_Bytes(&ss, id_string, strlen(id_string) + 1);\r
572     sha512_mpint(&ss, private_key);\r
573     SHA512_Final(&ss, digest512);\r
575     /*\r
576      * Now hash that digest plus the message hash.\r
577      */\r
578     SHA512_Init(&ss);\r
579     SHA512_Bytes(&ss, digest512, sizeof(digest512));\r
580     SHA512_Bytes(&ss, digest, digest_len);\r
582     while (1) {\r
583         SHA512_State ss2 = ss;         /* structure copy */\r
584         SHA512_Final(&ss2, digest512);\r
586         smemclr(&ss2, sizeof(ss2));\r
588         /*\r
589          * Now convert the result into a bignum, and reduce it mod q.\r
590          */\r
591         proto_k = bignum_from_bytes(digest512, 64);\r
592         k = bigmod(proto_k, modulus);\r
593         freebn(proto_k);\r
595         if (bignum_cmp(k, One) != 0 && bignum_cmp(k, Zero) != 0) {\r
596             smemclr(&ss, sizeof(ss));\r
597             smemclr(digest512, sizeof(digest512));\r
598             return k;\r
599         }\r
601         /* Very unlikely we get here, but if so, k was unsuitable. */\r
602         freebn(k);\r
603         /* Perturb the hash to think of a different k. */\r
604         SHA512_Bytes(&ss, "x", 1);\r
605         /* Go round and try again. */\r
606     }\r
609 static unsigned char *dss_sign(void *key, const char *data, int datalen,\r
610                                int *siglen)\r
612     struct dss_key *dss = (struct dss_key *) key;\r
613     Bignum k, gkp, hash, kinv, hxr, r, s;\r
614     unsigned char digest[20];\r
615     unsigned char *bytes;\r
616     int nbytes, i;\r
618     SHA_Simple(data, datalen, digest);\r
620     k = dss_gen_k("DSA deterministic k generator", dss->q, dss->x,\r
621                   digest, sizeof(digest));\r
622     kinv = modinv(k, dss->q);          /* k^-1 mod q */\r
623     assert(kinv);\r
625     /*\r
626      * Now we have k, so just go ahead and compute the signature.\r
627      */\r
628     gkp = modpow(dss->g, k, dss->p);   /* g^k mod p */\r
629     r = bigmod(gkp, dss->q);           /* r = (g^k mod p) mod q */\r
630     freebn(gkp);\r
632     hash = bignum_from_bytes(digest, 20);\r
633     hxr = bigmuladd(dss->x, r, hash);  /* hash + x*r */\r
634     s = modmul(kinv, hxr, dss->q);     /* s = k^-1 * (hash + x*r) mod q */\r
635     freebn(hxr);\r
636     freebn(kinv);\r
637     freebn(k);\r
638     freebn(hash);\r
640     /*\r
641      * Signature blob is\r
642      * \r
643      *   string  "ssh-dss"\r
644      *   string  two 20-byte numbers r and s, end to end\r
645      * \r
646      * i.e. 4+7 + 4+40 bytes.\r
647      */\r
648     nbytes = 4 + 7 + 4 + 40;\r
649     bytes = snewn(nbytes, unsigned char);\r
650     PUT_32BIT(bytes, 7);\r
651     memcpy(bytes + 4, "ssh-dss", 7);\r
652     PUT_32BIT(bytes + 4 + 7, 40);\r
653     for (i = 0; i < 20; i++) {\r
654         bytes[4 + 7 + 4 + i] = bignum_byte(r, 19 - i);\r
655         bytes[4 + 7 + 4 + 20 + i] = bignum_byte(s, 19 - i);\r
656     }\r
657     freebn(r);\r
658     freebn(s);\r
660     *siglen = nbytes;\r
661     return bytes;\r
664 const struct ssh_signkey ssh_dss = {\r
665     dss_newkey,\r
666     dss_freekey,\r
667     dss_fmtkey,\r
668     dss_public_blob,\r
669     dss_private_blob,\r
670     dss_createkey,\r
671     dss_openssh_createkey,\r
672     dss_openssh_fmtkey,\r
673     5 /* p,q,g,y,x */,\r
674     dss_pubkey_bits,\r
675     dss_verifysig,\r
676     dss_sign,\r
677     "ssh-dss",\r
678     "dss",\r
679     NULL,\r
680 };\r