Mercurial > dropbear
comparison keyimport.c @ 1914:f978a15194ba
Remove commented ssh.com code from keyimport
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Wed, 30 Mar 2022 10:10:15 +0800 |
parents | 38c6fd7d7a82 |
children |
comparison
equal
deleted
inserted
replaced
1913:38c6fd7d7a82 | 1914:f978a15194ba |
---|---|
1 /* | 1 /* |
2 * Based on PuTTY's import.c for importing/exporting OpenSSH and SSH.com | 2 * Based on PuTTY's import.c for importing/exporting OpenSSH and SSH.com |
3 * keyfiles. | 3 * keyfiles. |
4 * | 4 * |
5 * Modifications copyright 2003 Matt Johnston | 5 * Modifications copyright 2003-2022 Matt Johnston |
6 * | 6 * |
7 * PuTTY is copyright 1997-2003 Simon Tatham. | 7 * PuTTY is copyright 1997-2003 Simon Tatham. |
8 * | 8 * |
9 * Portions copyright Robert de Bath, Joris van Rantwijk, Delian | 9 * Portions copyright Robert de Bath, Joris van Rantwijk, Delian |
10 * Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, | 10 * Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, |
1119 buf_burn_free(extrablob); | 1119 buf_burn_free(extrablob); |
1120 } | 1120 } |
1121 return ret; | 1121 return ret; |
1122 } | 1122 } |
1123 | 1123 |
1124 #if 0 | |
1125 /* XXX TODO ssh.com stuff isn't going yet */ | |
1126 | |
1127 /* ---------------------------------------------------------------------- | |
1128 * Code to read ssh.com private keys. | |
1129 */ | |
1130 | |
1131 /* | |
1132 * The format of the base64 blob is largely ssh2-packet-formatted, | |
1133 * except that mpints are a bit different: they're more like the | |
1134 * old ssh1 mpint. You have a 32-bit bit count N, followed by | |
1135 * (N+7)/8 bytes of data. | |
1136 * | |
1137 * So. The blob contains: | |
1138 * | |
1139 * - uint32 0x3f6ff9eb (magic number) | |
1140 * - uint32 size (total blob size) | |
1141 * - string key-type (see below) | |
1142 * - string cipher-type (tells you if key is encrypted) | |
1143 * - string encrypted-blob | |
1144 * | |
1145 * (The first size field includes the size field itself and the | |
1146 * magic number before it. All other size fields are ordinary ssh2 | |
1147 * strings, so the size field indicates how much data is to | |
1148 * _follow_.) | |
1149 * | |
1150 * The encrypted blob, once decrypted, contains a single string | |
1151 * which in turn contains the payload. (This allows padding to be | |
1152 * added after that string while still making it clear where the | |
1153 * real payload ends. Also it probably makes for a reasonable | |
1154 * decryption check.) | |
1155 * | |
1156 * The payload blob, for an RSA key, contains: | |
1157 * - mpint e | |
1158 * - mpint d | |
1159 * - mpint n (yes, the public and private stuff is intermixed) | |
1160 * - mpint u (presumably inverse of p mod q) | |
1161 * - mpint p (p is the smaller prime) | |
1162 * - mpint q (q is the larger) | |
1163 * | |
1164 * For a DSA key, the payload blob contains: | |
1165 * - uint32 0 | |
1166 * - mpint p | |
1167 * - mpint g | |
1168 * - mpint q | |
1169 * - mpint y | |
1170 * - mpint x | |
1171 * | |
1172 * Alternatively, if the parameters are `predefined', that | |
1173 * (0,p,g,q) sequence can be replaced by a uint32 1 and a string | |
1174 * containing some predefined parameter specification. *shudder*, | |
1175 * but I doubt we'll encounter this in real life. | |
1176 * | |
1177 * The key type strings are ghastly. The RSA key I looked at had a | |
1178 * type string of | |
1179 * | |
1180 * `if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}' | |
1181 * | |
1182 * and the DSA key wasn't much better: | |
1183 * | |
1184 * `dl-modp{sign{dsa-nist-sha1},dh{plain}}' | |
1185 * | |
1186 * It isn't clear that these will always be the same. I think it | |
1187 * might be wise just to look at the `if-modn{sign{rsa' and | |
1188 * `dl-modp{sign{dsa' prefixes. | |
1189 * | |
1190 * Finally, the encryption. The cipher-type string appears to be | |
1191 * either `none' or `3des-cbc'. Looks as if this is SSH2-style | |
1192 * 3des-cbc (i.e. outer cbc rather than inner). The key is created | |
1193 * from the passphrase by means of yet another hashing faff: | |
1194 * | |
1195 * - first 16 bytes are MD5(passphrase) | |
1196 * - next 16 bytes are MD5(passphrase || first 16 bytes) | |
1197 * - if there were more, they'd be MD5(passphrase || first 32), | |
1198 * and so on. | |
1199 */ | |
1200 | |
1201 #define SSHCOM_MAGIC_NUMBER 0x3f6ff9eb | |
1202 | |
1203 struct sshcom_key { | |
1204 char comment[256]; /* allowing any length is overkill */ | |
1205 unsigned char *keyblob; | |
1206 int keyblob_len, keyblob_size; | |
1207 }; | |
1208 | |
1209 static struct sshcom_key *load_sshcom_key(const char *filename) | |
1210 { | |
1211 struct sshcom_key *ret; | |
1212 FILE *fp; | |
1213 char buffer[256]; | |
1214 int len; | |
1215 char *errmsg, *p; | |
1216 int headers_done; | |
1217 char base64_bit[4]; | |
1218 int base64_chars = 0; | |
1219 | |
1220 ret = snew(struct sshcom_key); | |
1221 ret->comment[0] = '\0'; | |
1222 ret->keyblob = NULL; | |
1223 ret->keyblob_len = ret->keyblob_size = 0; | |
1224 | |
1225 fp = fopen(filename, "r"); | |
1226 if (!fp) { | |
1227 errmsg = "Unable to open key file"; | |
1228 goto error; | |
1229 } | |
1230 if (!fgets(buffer, sizeof(buffer), fp) || | |
1231 0 != strcmp(buffer, "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----\n")) { | |
1232 errmsg = "File does not begin with ssh.com key header"; | |
1233 goto error; | |
1234 } | |
1235 | |
1236 headers_done = 0; | |
1237 while (1) { | |
1238 if (!fgets(buffer, sizeof(buffer), fp)) { | |
1239 errmsg = "Unexpected end of file"; | |
1240 goto error; | |
1241 } | |
1242 if (!strcmp(buffer, "---- END SSH2 ENCRYPTED PRIVATE KEY ----\n")) | |
1243 break; /* done */ | |
1244 if ((p = strchr(buffer, ':')) != NULL) { | |
1245 if (headers_done) { | |
1246 errmsg = "Header found in body of key data"; | |
1247 goto error; | |
1248 } | |
1249 *p++ = '\0'; | |
1250 while (*p && isspace((unsigned char)*p)) p++; | |
1251 /* | |
1252 * Header lines can end in a trailing backslash for | |
1253 * continuation. | |
1254 */ | |
1255 while ((len = strlen(p)) > (int)(sizeof(buffer) - (p-buffer) -1) || | |
1256 p[len-1] != '\n' || p[len-2] == '\\') { | |
1257 if (len > (int)((p-buffer) + sizeof(buffer)-2)) { | |
1258 errmsg = "Header line too long to deal with"; | |
1259 goto error; | |
1260 } | |
1261 if (!fgets(p+len-2, sizeof(buffer)-(p-buffer)-(len-2), fp)) { | |
1262 errmsg = "Unexpected end of file"; | |
1263 goto error; | |
1264 } | |
1265 } | |
1266 p[strcspn(p, "\n")] = '\0'; | |
1267 if (!strcmp(buffer, "Comment")) { | |
1268 /* Strip quotes in comment if present. */ | |
1269 if (p[0] == '"' && p[strlen(p)-1] == '"') { | |
1270 p++; | |
1271 p[strlen(p)-1] = '\0'; | |
1272 } | |
1273 strncpy(ret->comment, p, sizeof(ret->comment)); | |
1274 ret->comment[sizeof(ret->comment)-1] = '\0'; | |
1275 } | |
1276 } else { | |
1277 headers_done = 1; | |
1278 | |
1279 p = buffer; | |
1280 while (isbase64(*p)) { | |
1281 base64_bit[base64_chars++] = *p; | |
1282 if (base64_chars == 4) { | |
1283 unsigned char out[3]; | |
1284 | |
1285 base64_chars = 0; | |
1286 | |
1287 len = base64_decode_atom(base64_bit, out); | |
1288 | |
1289 if (len <= 0) { | |
1290 errmsg = "Invalid base64 encoding"; | |
1291 goto error; | |
1292 } | |
1293 | |
1294 if (ret->keyblob_len + len > ret->keyblob_size) { | |
1295 ret->keyblob_size = ret->keyblob_len + len + 256; | |
1296 ret->keyblob = sresize(ret->keyblob, ret->keyblob_size, | |
1297 unsigned char); | |
1298 } | |
1299 | |
1300 memcpy(ret->keyblob + ret->keyblob_len, out, len); | |
1301 ret->keyblob_len += len; | |
1302 } | |
1303 | |
1304 p++; | |
1305 } | |
1306 } | |
1307 } | |
1308 | |
1309 if (ret->keyblob_len == 0 || !ret->keyblob) { | |
1310 errmsg = "Key body not present"; | |
1311 goto error; | |
1312 } | |
1313 | |
1314 return ret; | |
1315 | |
1316 error: | |
1317 if (ret) { | |
1318 if (ret->keyblob) { | |
1319 memset(ret->keyblob, 0, ret->keyblob_size); | |
1320 m_free(ret->keyblob); | |
1321 } | |
1322 memset(ret, 0, sizeof(*ret)); | |
1323 m_free(ret); | |
1324 } | |
1325 return NULL; | |
1326 } | |
1327 | |
1328 int sshcom_encrypted(const char *filename, char **comment) | |
1329 { | |
1330 struct sshcom_key *key = load_sshcom_key(filename); | |
1331 int pos, len, answer; | |
1332 | |
1333 *comment = NULL; | |
1334 if (!key) | |
1335 return 0; | |
1336 | |
1337 /* | |
1338 * Check magic number. | |
1339 */ | |
1340 if (GET_32BIT(key->keyblob) != 0x3f6ff9eb) | |
1341 return 0; /* key is invalid */ | |
1342 | |
1343 /* | |
1344 * Find the cipher-type string. | |
1345 */ | |
1346 answer = 0; | |
1347 pos = 8; | |
1348 if (key->keyblob_len < pos+4) | |
1349 goto done; /* key is far too short */ | |
1350 len = toint(GET_32BIT(key->keyblob + pos)); | |
1351 if (len < 0 || len > key->keyblob_len - pos - 4) | |
1352 goto done; /* key is far too short */ | |
1353 pos += 4 + len; /* skip key type */ | |
1354 len = toint(GET_32BIT(key->keyblob + pos)); /* find cipher-type length */ | |
1355 if (len < 0 || len > key->keyblob_len - pos - 4) | |
1356 goto done; /* cipher type string is incomplete */ | |
1357 if (len != 4 || 0 != memcmp(key->keyblob + pos + 4, "none", 4)) | |
1358 answer = 1; | |
1359 | |
1360 done: | |
1361 *comment = dupstr(key->comment); | |
1362 memset(key->keyblob, 0, key->keyblob_size); | |
1363 m_free(key->keyblob); | |
1364 memset(key, 0, sizeof(*key)); | |
1365 m_free(key); | |
1366 return answer; | |
1367 } | |
1368 | |
1369 static int sshcom_read_mpint(void *data, int len, struct mpint_pos *ret) | |
1370 { | |
1371 unsigned bits, bytes; | |
1372 unsigned char *d = (unsigned char *) data; | |
1373 | |
1374 if (len < 4) | |
1375 goto error; | |
1376 bits = GET_32BIT(d); | |
1377 | |
1378 bytes = (bits + 7) / 8; | |
1379 if (len < 4+bytes) | |
1380 goto error; | |
1381 | |
1382 ret->start = d + 4; | |
1383 ret->bytes = bytes; | |
1384 return bytes+4; | |
1385 | |
1386 error: | |
1387 ret->start = NULL; | |
1388 ret->bytes = -1; | |
1389 return len; /* ensure further calls fail as well */ | |
1390 } | |
1391 | |
1392 static int sshcom_put_mpint(void *target, void *data, int len) | |
1393 { | |
1394 unsigned char *d = (unsigned char *)target; | |
1395 unsigned char *i = (unsigned char *)data; | |
1396 int bits = len * 8 - 1; | |
1397 | |
1398 while (bits > 0) { | |
1399 if (*i & (1 << (bits & 7))) | |
1400 break; | |
1401 if (!(bits-- & 7)) | |
1402 i++, len--; | |
1403 } | |
1404 | |
1405 PUT_32BIT(d, bits+1); | |
1406 memcpy(d+4, i, len); | |
1407 return len+4; | |
1408 } | |
1409 | |
1410 sign_key *sshcom_read(const char *filename, char *passphrase) | |
1411 { | |
1412 struct sshcom_key *key = load_sshcom_key(filename); | |
1413 char *errmsg; | |
1414 int pos, len; | |
1415 const char prefix_rsa[] = "if-modn{sign{rsa"; | |
1416 const char prefix_dsa[] = "dl-modp{sign{dsa"; | |
1417 enum { RSA, DSA } type; | |
1418 int encrypted; | |
1419 char *ciphertext; | |
1420 int cipherlen; | |
1421 struct ssh2_userkey *ret = NULL, *retkey; | |
1422 const struct ssh_signkey *alg; | |
1423 unsigned char *blob = NULL; | |
1424 int blobsize = 0, publen, privlen; | |
1425 | |
1426 if (!key) | |
1427 return NULL; | |
1428 | |
1429 /* | |
1430 * Check magic number. | |
1431 */ | |
1432 if (GET_32BIT(key->keyblob) != SSHCOM_MAGIC_NUMBER) { | |
1433 errmsg = "Key does not begin with magic number"; | |
1434 goto error; | |
1435 } | |
1436 | |
1437 /* | |
1438 * Determine the key type. | |
1439 */ | |
1440 pos = 8; | |
1441 if (key->keyblob_len < pos+4 || | |
1442 (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) { | |
1443 errmsg = "Key blob does not contain a key type string"; | |
1444 goto error; | |
1445 } | |
1446 if (len > sizeof(prefix_rsa) - 1 && | |
1447 !memcmp(key->keyblob+pos+4, prefix_rsa, sizeof(prefix_rsa) - 1)) { | |
1448 type = RSA; | |
1449 } else if (len > sizeof(prefix_dsa) - 1 && | |
1450 !memcmp(key->keyblob+pos+4, prefix_dsa, sizeof(prefix_dsa) - 1)) { | |
1451 type = DSA; | |
1452 } else { | |
1453 errmsg = "Key is of unknown type"; | |
1454 goto error; | |
1455 } | |
1456 pos += 4+len; | |
1457 | |
1458 /* | |
1459 * Determine the cipher type. | |
1460 */ | |
1461 if (key->keyblob_len < pos+4 || | |
1462 (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) { | |
1463 errmsg = "Key blob does not contain a cipher type string"; | |
1464 goto error; | |
1465 } | |
1466 if (len == 4 && !memcmp(key->keyblob+pos+4, "none", 4)) | |
1467 encrypted = 0; | |
1468 else if (len == 8 && !memcmp(key->keyblob+pos+4, "3des-cbc", 8)) | |
1469 encrypted = 1; | |
1470 else { | |
1471 errmsg = "Key encryption is of unknown type"; | |
1472 goto error; | |
1473 } | |
1474 pos += 4+len; | |
1475 | |
1476 /* | |
1477 * Get hold of the encrypted part of the key. | |
1478 */ | |
1479 if (key->keyblob_len < pos+4 || | |
1480 (len = GET_32BIT(key->keyblob + pos)) > key->keyblob_len - pos - 4) { | |
1481 errmsg = "Key blob does not contain actual key data"; | |
1482 goto error; | |
1483 } | |
1484 ciphertext = (char *)key->keyblob + pos + 4; | |
1485 cipherlen = len; | |
1486 if (cipherlen == 0) { | |
1487 errmsg = "Length of key data is zero"; | |
1488 goto error; | |
1489 } | |
1490 | |
1491 /* | |
1492 * Decrypt it if necessary. | |
1493 */ | |
1494 if (encrypted) { | |
1495 /* | |
1496 * Derive encryption key from passphrase and iv/salt: | |
1497 * | |
1498 * - let block A equal MD5(passphrase) | |
1499 * - let block B equal MD5(passphrase || A) | |
1500 * - block C would be MD5(passphrase || A || B) and so on | |
1501 * - encryption key is the first N bytes of A || B | |
1502 */ | |
1503 struct MD5Context md5c; | |
1504 unsigned char keybuf[32], iv[8]; | |
1505 | |
1506 if (cipherlen % 8 != 0) { | |
1507 errmsg = "Encrypted part of key is not a multiple of cipher block" | |
1508 " size"; | |
1509 goto error; | |
1510 } | |
1511 | |
1512 MD5Init(&md5c); | |
1513 MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); | |
1514 MD5Final(keybuf, &md5c); | |
1515 | |
1516 MD5Init(&md5c); | |
1517 MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); | |
1518 MD5Update(&md5c, keybuf, 16); | |
1519 MD5Final(keybuf+16, &md5c); | |
1520 | |
1521 /* | |
1522 * Now decrypt the key blob. | |
1523 */ | |
1524 memset(iv, 0, sizeof(iv)); | |
1525 des3_decrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext, | |
1526 cipherlen); | |
1527 | |
1528 memset(&md5c, 0, sizeof(md5c)); | |
1529 memset(keybuf, 0, sizeof(keybuf)); | |
1530 | |
1531 /* | |
1532 * Hereafter we return WRONG_PASSPHRASE for any parsing | |
1533 * error. (But only if we've just tried to decrypt it! | |
1534 * Returning WRONG_PASSPHRASE for an unencrypted key is | |
1535 * automatic doom.) | |
1536 */ | |
1537 if (encrypted) | |
1538 ret = SSH2_WRONG_PASSPHRASE; | |
1539 } | |
1540 | |
1541 /* | |
1542 * Strip away the containing string to get to the real meat. | |
1543 */ | |
1544 len = toint(GET_32BIT(ciphertext)); | |
1545 if (len < 0 || len > cipherlen-4) { | |
1546 errmsg = "containing string was ill-formed"; | |
1547 goto error; | |
1548 } | |
1549 ciphertext += 4; | |
1550 cipherlen = len; | |
1551 | |
1552 /* | |
1553 * Now we break down into RSA versus DSA. In either case we'll | |
1554 * construct public and private blobs in our own format, and | |
1555 * end up feeding them to alg->createkey(). | |
1556 */ | |
1557 blobsize = cipherlen + 256; | |
1558 blob = snewn(blobsize, unsigned char); | |
1559 privlen = 0; | |
1560 if (type == RSA) { | |
1561 struct mpint_pos n, e, d, u, p, q; | |
1562 int pos = 0; | |
1563 pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &e); | |
1564 pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &d); | |
1565 pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &n); | |
1566 pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &u); | |
1567 pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &p); | |
1568 pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &q); | |
1569 if (!q.start) { | |
1570 errmsg = "key data did not contain six integers"; | |
1571 goto error; | |
1572 } | |
1573 | |
1574 alg = &ssh_rsa; | |
1575 pos = 0; | |
1576 pos += put_string(blob+pos, "ssh-rsa", 7); | |
1577 pos += put_mp(blob+pos, e.start, e.bytes); | |
1578 pos += put_mp(blob+pos, n.start, n.bytes); | |
1579 publen = pos; | |
1580 pos += put_string(blob+pos, d.start, d.bytes); | |
1581 pos += put_mp(blob+pos, q.start, q.bytes); | |
1582 pos += put_mp(blob+pos, p.start, p.bytes); | |
1583 pos += put_mp(blob+pos, u.start, u.bytes); | |
1584 privlen = pos - publen; | |
1585 } else if (type == DSA) { | |
1586 struct mpint_pos p, q, g, x, y; | |
1587 int pos = 4; | |
1588 if (GET_32BIT(ciphertext) != 0) { | |
1589 errmsg = "predefined DSA parameters not supported"; | |
1590 goto error; | |
1591 } | |
1592 pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &p); | |
1593 pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &g); | |
1594 pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &q); | |
1595 pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &y); | |
1596 pos += sshcom_read_mpint(ciphertext+pos, cipherlen-pos, &x); | |
1597 if (!x.start) { | |
1598 errmsg = "key data did not contain five integers"; | |
1599 goto error; | |
1600 } | |
1601 | |
1602 alg = &ssh_dss; | |
1603 pos = 0; | |
1604 pos += put_string(blob+pos, "ssh-dss", 7); | |
1605 pos += put_mp(blob+pos, p.start, p.bytes); | |
1606 pos += put_mp(blob+pos, q.start, q.bytes); | |
1607 pos += put_mp(blob+pos, g.start, g.bytes); | |
1608 pos += put_mp(blob+pos, y.start, y.bytes); | |
1609 publen = pos; | |
1610 pos += put_mp(blob+pos, x.start, x.bytes); | |
1611 privlen = pos - publen; | |
1612 } else | |
1613 return NULL; | |
1614 | |
1615 dropbear_assert(privlen > 0); /* should have bombed by now if not */ | |
1616 | |
1617 retkey = snew(struct ssh2_userkey); | |
1618 retkey->alg = alg; | |
1619 retkey->data = alg->createkey(blob, publen, blob+publen, privlen); | |
1620 if (!retkey->data) { | |
1621 m_free(retkey); | |
1622 errmsg = "unable to create key data structure"; | |
1623 goto error; | |
1624 } | |
1625 retkey->comment = dupstr(key->comment); | |
1626 | |
1627 errmsg = NULL; /* no error */ | |
1628 ret = retkey; | |
1629 | |
1630 error: | |
1631 if (blob) { | |
1632 memset(blob, 0, blobsize); | |
1633 m_free(blob); | |
1634 } | |
1635 memset(key->keyblob, 0, key->keyblob_size); | |
1636 m_free(key->keyblob); | |
1637 memset(key, 0, sizeof(*key)); | |
1638 m_free(key); | |
1639 return ret; | |
1640 } | |
1641 | |
1642 int sshcom_write(const char *filename, sign_key *key, | |
1643 char *passphrase) | |
1644 { | |
1645 unsigned char *pubblob, *privblob; | |
1646 int publen, privlen; | |
1647 unsigned char *outblob; | |
1648 int outlen; | |
1649 struct mpint_pos numbers[6]; | |
1650 int nnumbers, initial_zero, pos, lenpos, i; | |
1651 char *type; | |
1652 char *ciphertext; | |
1653 int cipherlen; | |
1654 int ret = 0; | |
1655 FILE *fp; | |
1656 | |
1657 /* | |
1658 * Fetch the key blobs. | |
1659 */ | |
1660 pubblob = key->alg->public_blob(key->data, &publen); | |
1661 privblob = key->alg->private_blob(key->data, &privlen); | |
1662 outblob = NULL; | |
1663 | |
1664 /* | |
1665 * Find the sequence of integers to be encoded into the OpenSSH | |
1666 * key blob, and also decide on the header line. | |
1667 */ | |
1668 if (key->alg == &ssh_rsa) { | |
1669 int pos; | |
1670 struct mpint_pos n, e, d, p, q, iqmp; | |
1671 | |
1672 pos = 4 + GET_32BIT(pubblob); | |
1673 pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e); | |
1674 pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n); | |
1675 pos = 0; | |
1676 pos += ssh2_read_mpint(privblob+pos, privlen-pos, &d); | |
1677 pos += ssh2_read_mpint(privblob+pos, privlen-pos, &p); | |
1678 pos += ssh2_read_mpint(privblob+pos, privlen-pos, &q); | |
1679 pos += ssh2_read_mpint(privblob+pos, privlen-pos, &iqmp); | |
1680 | |
1681 dropbear_assert(e.start && iqmp.start); /* can't go wrong */ | |
1682 | |
1683 numbers[0] = e; | |
1684 numbers[1] = d; | |
1685 numbers[2] = n; | |
1686 numbers[3] = iqmp; | |
1687 numbers[4] = q; | |
1688 numbers[5] = p; | |
1689 | |
1690 nnumbers = 6; | |
1691 initial_zero = 0; | |
1692 type = "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}"; | |
1693 } else if (key->alg == &ssh_dss) { | |
1694 int pos; | |
1695 struct mpint_pos p, q, g, y, x; | |
1696 | |
1697 pos = 4 + GET_32BIT(pubblob); | |
1698 pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p); | |
1699 pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q); | |
1700 pos += ssh2_read_mpint(pubblob+pos, publen-pos, &g); | |
1701 pos += ssh2_read_mpint(pubblob+pos, publen-pos, &y); | |
1702 pos = 0; | |
1703 pos += ssh2_read_mpint(privblob+pos, privlen-pos, &x); | |
1704 | |
1705 dropbear_assert(y.start && x.start); /* can't go wrong */ | |
1706 | |
1707 numbers[0] = p; | |
1708 numbers[1] = g; | |
1709 numbers[2] = q; | |
1710 numbers[3] = y; | |
1711 numbers[4] = x; | |
1712 | |
1713 nnumbers = 5; | |
1714 initial_zero = 1; | |
1715 type = "dl-modp{sign{dsa-nist-sha1},dh{plain}}"; | |
1716 } else { | |
1717 dropbear_assert(0); /* zoinks! */ | |
1718 } | |
1719 | |
1720 /* | |
1721 * Total size of key blob will be somewhere under 512 plus | |
1722 * combined length of integers. We'll calculate the more | |
1723 * precise size as we construct the blob. | |
1724 */ | |
1725 outlen = 512; | |
1726 for (i = 0; i < nnumbers; i++) | |
1727 outlen += 4 + numbers[i].bytes; | |
1728 outblob = snewn(outlen, unsigned char); | |
1729 | |
1730 /* | |
1731 * Create the unencrypted key blob. | |
1732 */ | |
1733 pos = 0; | |
1734 PUT_32BIT(outblob+pos, SSHCOM_MAGIC_NUMBER); pos += 4; | |
1735 pos += 4; /* length field, fill in later */ | |
1736 pos += put_string(outblob+pos, type, strlen(type)); | |
1737 { | |
1738 char *ciphertype = passphrase ? "3des-cbc" : "none"; | |
1739 pos += put_string(outblob+pos, ciphertype, strlen(ciphertype)); | |
1740 } | |
1741 lenpos = pos; /* remember this position */ | |
1742 pos += 4; /* encrypted-blob size */ | |
1743 pos += 4; /* encrypted-payload size */ | |
1744 if (initial_zero) { | |
1745 PUT_32BIT(outblob+pos, 0); | |
1746 pos += 4; | |
1747 } | |
1748 for (i = 0; i < nnumbers; i++) | |
1749 pos += sshcom_put_mpint(outblob+pos, | |
1750 numbers[i].start, numbers[i].bytes); | |
1751 /* Now wrap up the encrypted payload. */ | |
1752 PUT_32BIT(outblob+lenpos+4, pos - (lenpos+8)); | |
1753 /* Pad encrypted blob to a multiple of cipher block size. */ | |
1754 if (passphrase) { | |
1755 int padding = -(pos - (lenpos+4)) & 7; | |
1756 while (padding--) | |
1757 outblob[pos++] = random_byte(); | |
1758 } | |
1759 ciphertext = (char *)outblob+lenpos+4; | |
1760 cipherlen = pos - (lenpos+4); | |
1761 dropbear_assert(!passphrase || cipherlen % 8 == 0); | |
1762 /* Wrap up the encrypted blob string. */ | |
1763 PUT_32BIT(outblob+lenpos, cipherlen); | |
1764 /* And finally fill in the total length field. */ | |
1765 PUT_32BIT(outblob+4, pos); | |
1766 | |
1767 dropbear_assert(pos < outlen); | |
1768 | |
1769 /* | |
1770 * Encrypt the key. | |
1771 */ | |
1772 if (passphrase) { | |
1773 /* | |
1774 * Derive encryption key from passphrase and iv/salt: | |
1775 * | |
1776 * - let block A equal MD5(passphrase) | |
1777 * - let block B equal MD5(passphrase || A) | |
1778 * - block C would be MD5(passphrase || A || B) and so on | |
1779 * - encryption key is the first N bytes of A || B | |
1780 */ | |
1781 struct MD5Context md5c; | |
1782 unsigned char keybuf[32], iv[8]; | |
1783 | |
1784 MD5Init(&md5c); | |
1785 MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); | |
1786 MD5Final(keybuf, &md5c); | |
1787 | |
1788 MD5Init(&md5c); | |
1789 MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase)); | |
1790 MD5Update(&md5c, keybuf, 16); | |
1791 MD5Final(keybuf+16, &md5c); | |
1792 | |
1793 /* | |
1794 * Now decrypt the key blob. | |
1795 */ | |
1796 memset(iv, 0, sizeof(iv)); | |
1797 des3_encrypt_pubkey_ossh(keybuf, iv, (unsigned char *)ciphertext, | |
1798 cipherlen); | |
1799 | |
1800 memset(&md5c, 0, sizeof(md5c)); | |
1801 memset(keybuf, 0, sizeof(keybuf)); | |
1802 } | |
1803 | |
1804 /* | |
1805 * And save it. We'll use Unix line endings just in case it's | |
1806 * subsequently transferred in binary mode. | |
1807 */ | |
1808 fp = fopen(filename, "wb"); /* ensure Unix line endings */ | |
1809 if (!fp) | |
1810 goto error; | |
1811 fputs("---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----\n", fp); | |
1812 fprintf(fp, "Comment: \""); | |
1813 /* | |
1814 * Comment header is broken with backslash-newline if it goes | |
1815 * over 70 chars. Although it's surrounded by quotes, it | |
1816 * _doesn't_ escape backslashes or quotes within the string. | |
1817 * Don't ask me, I didn't design it. | |
1818 */ | |
1819 { | |
1820 int slen = 60; /* starts at 60 due to "Comment: " */ | |
1821 char *c = key->comment; | |
1822 while ((int)strlen(c) > slen) { | |
1823 fprintf(fp, "%.*s\\\n", slen, c); | |
1824 c += slen; | |
1825 slen = 70; /* allow 70 chars on subsequent lines */ | |
1826 } | |
1827 fprintf(fp, "%s\"\n", c); | |
1828 } | |
1829 base64_encode_fp(fp, outblob, pos, 70); | |
1830 fputs("---- END SSH2 ENCRYPTED PRIVATE KEY ----\n", fp); | |
1831 fclose(fp); | |
1832 ret = 1; | |
1833 | |
1834 error: | |
1835 if (outblob) { | |
1836 memset(outblob, 0, outlen); | |
1837 m_free(outblob); | |
1838 } | |
1839 if (privblob) { | |
1840 memset(privblob, 0, privlen); | |
1841 m_free(privblob); | |
1842 } | |
1843 if (pubblob) { | |
1844 memset(pubblob, 0, publen); | |
1845 m_free(pubblob); | |
1846 } | |
1847 return ret; | |
1848 } | |
1849 #endif /* ssh.com stuff disabled */ | |
1850 | |
1851 /* From PuTTY misc.c */ | 1124 /* From PuTTY misc.c */ |
1852 static int toint(unsigned u) | 1125 static int toint(unsigned u) |
1853 { | 1126 { |
1854 /* | 1127 /* |
1855 * Convert an unsigned to an int, without running into the | 1128 * Convert an unsigned to an int, without running into the |