sshdss.c 18 KB

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