📄 import.c
字号:
len = base64_decode_atom(base64_bit, out);
if (len <= 0) {
errmsg = "Invalid base64 encoding";
goto error;
}
if (ret->keyblob_len + len > ret->keyblob_size) {
ret->keyblob_size = ret->keyblob_len + len + 256;
ret->keyblob = sresize(ret->keyblob, ret->keyblob_size,
unsigned char);
}
memcpy(ret->keyblob + ret->keyblob_len, out, len);
ret->keyblob_len += len;
memset(out, 0, sizeof(out));
}
p++;
}
}
}
if (ret->keyblob_len == 0 || !ret->keyblob) {
errmsg = "Key body not present";
goto error;
}
if (ret->encrypted && ret->keyblob_len % 8 != 0) {
errmsg = "Encrypted key blob is not a multiple of cipher block size";
goto error;
}
memset(buffer, 0, sizeof(buffer));
memset(base64_bit, 0, sizeof(base64_bit));
return ret;
error:
memset(buffer, 0, sizeof(buffer));
memset(base64_bit, 0, sizeof(base64_bit));
if (ret) {
if (ret->keyblob) {
memset(ret->keyblob, 0, ret->keyblob_size);
sfree(ret->keyblob);
}
memset(&ret, 0, sizeof(ret));
sfree(ret);
}
return NULL;
}
int openssh_encrypted(const Filename *filename)
{
struct openssh_key *key = load_openssh_key(filename);
int ret;
if (!key)
return 0;
ret = key->encrypted;
memset(key->keyblob, 0, key->keyblob_size);
sfree(key->keyblob);
memset(&key, 0, sizeof(key));
sfree(key);
return ret;
}
struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase)
{
struct openssh_key *key = load_openssh_key(filename);
struct ssh2_userkey *retkey;
unsigned char *p;
int ret, id, len, flags;
int i, num_integers;
struct ssh2_userkey *retval = NULL;
char *errmsg;
unsigned char *blob;
int blobsize = 0, blobptr, privptr;
char *modptr = NULL;
int modlen = 0;
blob = NULL;
if (!key)
return NULL;
if (key->encrypted) {
/*
* Derive encryption key from passphrase and iv/salt:
*
* - let block A equal MD5(passphrase || iv)
* - let block B equal MD5(A || passphrase || iv)
* - block C would be MD5(B || passphrase || iv) and so on
* - encryption key is the first N bytes of A || B
*/
struct MD5Context md5c;
unsigned char keybuf[32];
MD5Init(&md5c);
MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));
MD5Update(&md5c, (unsigned char *)key->iv, 8);
MD5Final(keybuf, &md5c);
MD5Init(&md5c);
MD5Update(&md5c, keybuf, 16);
MD5Update(&md5c, (unsigned char *)passphrase, strlen(passphrase));
MD5Update(&md5c, (unsigned char *)key->iv, 8);
MD5Final(keybuf+16, &md5c);
/*
* Now decrypt the key blob.
*/
des3_decrypt_pubkey_ossh(keybuf, (unsigned char *)key->iv,
key->keyblob, key->keyblob_len);
memset(&md5c, 0, sizeof(md5c));
memset(keybuf, 0, sizeof(keybuf));
}
/*
* Now we have a decrypted key blob, which contains an ASN.1
* encoded private key. We must now untangle the ASN.1.
*
* We expect the whole key blob to be formatted as a SEQUENCE
* (0x30 followed by a length code indicating that the rest of
* the blob is part of the sequence). Within that SEQUENCE we
* expect to see a bunch of INTEGERs. What those integers mean
* depends on the key type:
*
* - For RSA, we expect the integers to be 0, n, e, d, p, q,
* dmp1, dmq1, iqmp in that order. (The last three are d mod
* (p-1), d mod (q-1), inverse of q mod p respectively.)
*
* - For DSA, we expect them to be 0, p, q, g, y, x in that
* order.
*/
p = key->keyblob;
/* Expect the SEQUENCE header. Take its absence as a failure to decrypt. */
ret = ber_read_id_len(p, key->keyblob_len, &id, &len, &flags);
p += ret;
if (ret < 0 || id != 16) {
errmsg = "ASN.1 decoding failure";
retval = SSH2_WRONG_PASSPHRASE;
goto error;
}
/* Expect a load of INTEGERs. */
if (key->type == OSSH_RSA)
num_integers = 9;
else if (key->type == OSSH_DSA)
num_integers = 6;
else
num_integers = 0; /* placate compiler warnings */
/*
* Space to create key blob in.
*/
blobsize = 256+key->keyblob_len;
blob = snewn(blobsize, unsigned char);
PUT_32BIT(blob, 7);
if (key->type == OSSH_DSA)
memcpy(blob+4, "ssh-dss", 7);
else if (key->type == OSSH_RSA)
memcpy(blob+4, "ssh-rsa", 7);
blobptr = 4+7;
privptr = -1;
for (i = 0; i < num_integers; i++) {
ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p,
&id, &len, &flags);
p += ret;
if (ret < 0 || id != 2 ||
key->keyblob+key->keyblob_len-p < len) {
errmsg = "ASN.1 decoding failure";
retval = SSH2_WRONG_PASSPHRASE;
goto error;
}
if (i == 0) {
/*
* The first integer should be zero always (I think
* this is some sort of version indication).
*/
if (len != 1 || p[0] != 0) {
errmsg = "Version number mismatch";
goto error;
}
} else if (key->type == OSSH_RSA) {
/*
* Integers 1 and 2 go into the public blob but in the
* opposite order; integers 3, 4, 5 and 8 go into the
* private blob. The other two (6 and 7) are ignored.
*/
if (i == 1) {
/* Save the details for after we deal with number 2. */
modptr = (char *)p;
modlen = len;
} else if (i != 6 && i != 7) {
PUT_32BIT(blob+blobptr, len);
memcpy(blob+blobptr+4, p, len);
blobptr += 4+len;
if (i == 2) {
PUT_32BIT(blob+blobptr, modlen);
memcpy(blob+blobptr+4, modptr, modlen);
blobptr += 4+modlen;
privptr = blobptr;
}
}
} else if (key->type == OSSH_DSA) {
/*
* Integers 1-4 go into the public blob; integer 5 goes
* into the private blob.
*/
PUT_32BIT(blob+blobptr, len);
memcpy(blob+blobptr+4, p, len);
blobptr += 4+len;
if (i == 4)
privptr = blobptr;
}
/* Skip past the number. */
p += len;
}
/*
* Now put together the actual key. Simplest way to do this is
* to assemble our own key blobs and feed them to the createkey
* functions; this is a bit faffy but it does mean we get all
* the sanity checks for free.
*/
assert(privptr > 0); /* should have bombed by now if not */
retkey = snew(struct ssh2_userkey);
retkey->alg = (key->type == OSSH_RSA ? &ssh_rsa : &ssh_dss);
retkey->data = retkey->alg->createkey(blob, privptr,
blob+privptr, blobptr-privptr);
if (!retkey->data) {
sfree(retkey);
errmsg = "unable to create key data structure";
goto error;
}
retkey->comment = dupstr("imported-openssh-key");
errmsg = NULL; /* no error */
retval = retkey;
error:
if (blob) {
memset(blob, 0, blobsize);
sfree(blob);
}
memset(key->keyblob, 0, key->keyblob_size);
sfree(key->keyblob);
memset(&key, 0, sizeof(key));
sfree(key);
return retval;
}
int openssh_write(const Filename *filename, struct ssh2_userkey *key,
char *passphrase)
{
unsigned char *pubblob, *privblob, *spareblob;
int publen, privlen, sparelen = 0;
unsigned char *outblob;
int outlen;
struct mpint_pos numbers[9];
int nnumbers, pos, len, seqlen, i;
char *header, *footer;
char zero[1];
unsigned char iv[8];
int ret = 0;
FILE *fp;
/*
* Fetch the key blobs.
*/
pubblob = key->alg->public_blob(key->data, &publen);
privblob = key->alg->private_blob(key->data, &privlen);
spareblob = outblob = NULL;
/*
* Find the sequence of integers to be encoded into the OpenSSH
* key blob, and also decide on the header line.
*/
if (key->alg == &ssh_rsa) {
int pos;
struct mpint_pos n, e, d, p, q, iqmp, dmp1, dmq1;
Bignum bd, bp, bq, bdmp1, bdmq1;
pos = 4 + GET_32BIT(pubblob);
pos += ssh2_read_mpint(pubblob+pos, publen-pos, &e);
pos += ssh2_read_mpint(pubblob+pos, publen-pos, &n);
pos = 0;
pos += ssh2_read_mpint(privblob+pos, privlen-pos, &d);
pos += ssh2_read_mpint(privblob+pos, privlen-pos, &p);
pos += ssh2_read_mpint(privblob+pos, privlen-pos, &q);
pos += ssh2_read_mpint(privblob+pos, privlen-pos, &iqmp);
assert(e.start && iqmp.start); /* can't go wrong */
/* We also need d mod (p-1) and d mod (q-1). */
bd = bignum_from_bytes(d.start, d.bytes);
bp = bignum_from_bytes(p.start, p.bytes);
bq = bignum_from_bytes(q.start, q.bytes);
decbn(bp);
decbn(bq);
bdmp1 = bigmod(bd, bp);
bdmq1 = bigmod(bd, bq);
freebn(bd);
freebn(bp);
freebn(bq);
dmp1.bytes = (bignum_bitcount(bdmp1)+8)/8;
dmq1.bytes = (bignum_bitcount(bdmq1)+8)/8;
sparelen = dmp1.bytes + dmq1.bytes;
spareblob = snewn(sparelen, unsigned char);
dmp1.start = spareblob;
dmq1.start = spareblob + dmp1.bytes;
for (i = 0; i < dmp1.bytes; i++)
spareblob[i] = bignum_byte(bdmp1, dmp1.bytes-1 - i);
for (i = 0; i < dmq1.bytes; i++)
spareblob[i+dmp1.bytes] = bignum_byte(bdmq1, dmq1.bytes-1 - i);
freebn(bdmp1);
freebn(bdmq1);
numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0';
numbers[1] = n;
numbers[2] = e;
numbers[3] = d;
numbers[4] = p;
numbers[5] = q;
numbers[6] = dmp1;
numbers[7] = dmq1;
numbers[8] = iqmp;
nnumbers = 9;
header = "-----BEGIN RSA PRIVATE KEY-----\n";
footer = "-----END RSA PRIVATE KEY-----\n";
} else if (key->alg == &ssh_dss) {
int pos;
struct mpint_pos p, q, g, y, x;
pos = 4 + GET_32BIT(pubblob);
pos += ssh2_read_mpint(pubblob+pos, publen-pos, &p);
pos += ssh2_read_mpint(pubblob+pos, publen-pos, &q);
pos += ssh2_read_mpint(pubblob+pos, publen-pos, &g);
pos += ssh2_read_mpint(pubblob+pos, publen-pos, &y);
pos = 0;
pos += ssh2_read_mpint(privblob+pos, privlen-pos, &x);
assert(y.start && x.start); /* can't go wrong */
numbers[0].start = zero; numbers[0].bytes = 1; zero[0] = '\0';
numbers[1] = p;
numbers[2] = q;
numbers[3] = g;
numbers[4] = y;
numbers[5] = x;
nnumbers = 6;
header = "-----BEGIN DSA PRIVATE KEY-----\n";
footer = "-----END DSA PRIVATE KEY-----\n";
} else {
assert(0); /* zoinks! */
}
/*
* Now count up the total size of the ASN.1 encoded integers,
* so as to determine the length of the containing SEQUENCE.
*/
len = 0;
for (i = 0; i < nnumbers; i++) {
len += ber_write_id_len(NULL, 2, numbers[i].bytes, 0);
len += numbers[i].bytes;
}
seqlen = len;
/* Now add on the SEQUENCE header. */
len += ber_write_id_len(NULL, 16, seqlen, ASN1_CONSTRUCTED);
/* Round up to the cipher block size, ensuring we have at least one
* byte of padding (see below). */
outlen = len;
if (passphrase)
outlen = (outlen+8) &~ 7;
/*
* Now we know how big outblob needs to be. Allocate it.
*/
outblob = snewn(outlen, unsigned char);
/*
* And write the data into it.
*/
pos = 0;
pos += ber_write_id_len(outblob+pos, 16, seqlen, ASN1_CONSTRUCTED);
for (i = 0; i < nnumbers; i++) {
pos += ber_write_id_len(outblob+pos, 2, numbers[i].bytes, 0);
memcpy(outblob+pos, numbers[i].start, numbers[i].bytes);
pos += numbers[i].bytes;
}
/*
* Padding on OpenSSH keys is deterministic. The number of
* padding bytes is always more than zero, and always at most
* the cipher block length. The value of each padding byte is
* equal to the number of padding bytes. So a plaintext that's
* an exact multiple of the block size will be padded with 08
* 08 08 08 08 08 08 08 (assuming a 64-bit block cipher); a
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -