Skip to content
Snippets Groups Projects
import.c 68.8 KiB
Newer Older
    }

    /*
     * Get hold of the encrypted part of the key.
     */
    ciphertext = get_string(src);
    if (ciphertext.len == 0) {
        errmsg = "no key data found";
        goto error;
    }

    /*
     * Decrypt it if necessary.
     */
    if (encrypted) {
        /*
         * Derive encryption key from passphrase and iv/salt:
         *
         *  - let block A equal MD5(passphrase)
         *  - let block B equal MD5(passphrase || A)
         *  - block C would be MD5(passphrase || A || B) and so on
         *  - encryption key is the first N bytes of A || B
         */
        unsigned char keybuf[32], iv[8];
        if (ciphertext.len % 8 != 0) {
            errmsg = "encrypted part of key is not a multiple of cipher block"
        sshcom_derivekey(ptrlen_from_asciz(passphrase), keybuf);
        /*
         * Now decrypt the key blob in place (casting away const from
         * ciphertext being a ptrlen).
         */
        des3_decrypt_pubkey_ossh(keybuf, iv,
                                 (char *)ciphertext.ptr, ciphertext.len);
        smemclr(keybuf, sizeof(keybuf));
        /*
         * Hereafter we return WRONG_PASSPHRASE for any parsing
         * error. (But only if we've just tried to decrypt it!
         * Returning WRONG_PASSPHRASE for an unencrypted key is
         * automatic doom.)
         */
        if (encrypted)
            ret = SSH2_WRONG_PASSPHRASE;
    }

    /*
     * Expect the ciphertext to be formatted as a containing string,
     * and reinitialise src to start parsing the inside of that string.
    BinarySource_BARE_INIT_PL(src, ciphertext);
    str = get_string(src);
    if (get_err(src)) {
        errmsg = "containing string was ill-formed";
        goto error;
    }
    BinarySource_BARE_INIT_PL(src, str);

    /*
     * Now we break down into RSA versus DSA. In either case we'll
     * construct public and private blobs in our own format, and
     * end up feeding them to ssh_key_new_priv().
    if (type == RSA) {
        ptrlen n, e, d, u, p, q;

        e = get_mp_sshcom_as_string(src);
        d = get_mp_sshcom_as_string(src);
        n = get_mp_sshcom_as_string(src);
        u = get_mp_sshcom_as_string(src);
        p = get_mp_sshcom_as_string(src);
        q = get_mp_sshcom_as_string(src);
        if (get_err(src)) {
            errmsg = "key data did not contain six integers";
            goto error;
        }

        alg = &ssh_rsa;
        put_stringz(blob, "ssh-rsa");
        put_mp_ssh2_from_string(blob, e);
        put_mp_ssh2_from_string(blob, n);
        publen = blob->len;
        put_mp_ssh2_from_string(blob, d);
        put_mp_ssh2_from_string(blob, q);
        put_mp_ssh2_from_string(blob, p);
        put_mp_ssh2_from_string(blob, u);
        ptrlen p, q, g, x, y;

        assert(type == DSA); /* the only other option from the if above */

        if (get_uint32(src) != 0) {
            errmsg = "predefined DSA parameters not supported";
            goto error;
        }
        p = get_mp_sshcom_as_string(src);
        g = get_mp_sshcom_as_string(src);
        q = get_mp_sshcom_as_string(src);
        y = get_mp_sshcom_as_string(src);
        x = get_mp_sshcom_as_string(src);
        if (get_err(src)) {
            errmsg = "key data did not contain five integers";
            goto error;
        }

        alg = &ssh_dss;
        put_stringz(blob, "ssh-dss");
        put_mp_ssh2_from_string(blob, p);
        put_mp_ssh2_from_string(blob, q);
        put_mp_ssh2_from_string(blob, g);
        put_mp_ssh2_from_string(blob, y);
        publen = blob->len;
        put_mp_ssh2_from_string(blob, x);
    retkey = snew(ssh2_userkey);
    retkey->key = ssh_key_new_priv(
        alg, make_ptrlen(blob->u, publen),
        make_ptrlen(blob->u + publen, blob->len - publen));
    if (!retkey->key) {
        sfree(retkey);
        errmsg = "unable to create key data structure";
        goto error;
    }
    retkey->comment = dupstr(key->comment);

    errmsg = NULL; /* no error */
    ret = retkey;

    error:
        strbuf_free(blob);
    strbuf_free(key->keyblob);
    sfree(key);
    if (errmsg_p) *errmsg_p = errmsg;
static bool sshcom_write(
    const Filename *filename, ssh2_userkey *key, const char *passphrase)
    strbuf *pubblob, *privblob, *outblob;
    int nnumbers, lenpos, i;
    bool initial_zero;
    BinarySource src[1];
    const char *type;
    char *ciphertext;
    int cipherlen;
    bool ret = false;
    FILE *fp;

    /*
     * Fetch the key blobs.
     */
    pubblob = strbuf_new();
    ssh_key_public_blob(key->key, BinarySink_UPCAST(pubblob));
    ssh_key_private_blob(key->key, BinarySink_UPCAST(privblob));
    outblob = NULL;

    /*
     * Find the sequence of integers to be encoded into the OpenSSH
     * key blob, and also decide on the header line.
     */
    if (ssh_key_alg(key->key) == &ssh_rsa) {
        ptrlen n, e, d, p, q, iqmp;
        /*
         * These blobs were generated from inside PuTTY, so we needn't
         * treat them as untrusted.
         */
        BinarySource_BARE_INIT(src, pubblob->u, pubblob->len);
        get_string(src);               /* skip algorithm name */
        e = get_string(src);
        n = get_string(src);
        BinarySource_BARE_INIT(src, privblob->u, privblob->len);
        d = get_string(src);
        p = get_string(src);
        q = get_string(src);
        iqmp = get_string(src);

        assert(!get_err(src));         /* can't go wrong */

        numbers[0] = e;
        numbers[1] = d;
        numbers[2] = n;
        numbers[3] = iqmp;
        numbers[4] = q;
        numbers[5] = p;

        nnumbers = 6;
        initial_zero = false;
        type = "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}";
    } else if (ssh_key_alg(key->key) == &ssh_dss) {
        ptrlen p, q, g, y, x;
        /*
         * These blobs were generated from inside PuTTY, so we needn't
         * treat them as untrusted.
         */
        BinarySource_BARE_INIT(src, pubblob->u, pubblob->len);
        get_string(src);               /* skip algorithm name */
        p = get_string(src);
        q = get_string(src);
        g = get_string(src);
        y = get_string(src);
        BinarySource_BARE_INIT(src, privblob->u, privblob->len);
        x = get_string(src);
        assert(!get_err(src));         /* can't go wrong */

        numbers[0] = p;
        numbers[1] = g;
        numbers[2] = q;
        numbers[3] = y;
        numbers[4] = x;

        nnumbers = 5;
        initial_zero = true;
        type = "dl-modp{sign{dsa-nist-sha1},dh{plain}}";
        goto error;                    /* unsupported key type */

    /*
     * Create the unencrypted key blob.
     */
    put_uint32(outblob, SSHCOM_MAGIC_NUMBER);
    put_uint32(outblob, 0);          /* length field, fill in later */
    put_stringz(outblob, type);
    put_stringz(outblob, passphrase ? "3des-cbc" : "none");
    lenpos = outblob->len;      /* remember this position */
    put_uint32(outblob, 0);            /* encrypted-blob size */
    put_uint32(outblob, 0);            /* encrypted-payload size */
    if (initial_zero)
        put_uint32(outblob, 0);
    for (i = 0; i < nnumbers; i++)
        put_mp_sshcom_from_string(outblob, numbers[i]);
    /* Now wrap up the encrypted payload. */
    PUT_32BIT_MSB_FIRST(outblob->s + lenpos + 4,
                        outblob->len - (lenpos + 8));
    /* Pad encrypted blob to a multiple of cipher block size. */
    if (passphrase) {
        int padding = -(outblob->len - (lenpos+4)) & 7;
        uint8_t padding_buf[8];
        random_read(padding_buf, padding);
        put_data(outblob, padding_buf, padding);
    ciphertext = outblob->s + lenpos + 4;
    cipherlen = outblob->len - (lenpos + 4);
    assert(!passphrase || cipherlen % 8 == 0);
    /* Wrap up the encrypted blob string. */
    PUT_32BIT_MSB_FIRST(outblob->s + lenpos, cipherlen);
    /* And finally fill in the total length field. */
    PUT_32BIT_MSB_FIRST(outblob->s + 4, outblob->len);

    /*
     * Encrypt the key.
     */
    if (passphrase) {
        unsigned char keybuf[32], iv[8];
        sshcom_derivekey(ptrlen_from_asciz(passphrase), keybuf);
        /*
         * Now decrypt the key blob.
         */
        memset(iv, 0, sizeof(iv));
        des3_encrypt_pubkey_ossh(keybuf, iv, ciphertext, cipherlen);
        smemclr(keybuf, sizeof(keybuf));
    }

    /*
     * And save it. We'll use Unix line endings just in case it's
     * subsequently transferred in binary mode.
     */
    fp = f_open(filename, "wb", true);      /* ensure Unix line endings */
    fputs("---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----\n", fp);
    fprintf(fp, "Comment: \"");
    /*
     * Comment header is broken with backslash-newline if it goes
     * over 70 chars. Although it's surrounded by quotes, it
     * _doesn't_ escape backslashes or quotes within the string.
     * Don't ask me, I didn't design it.
     */
    {
        int slen = 60;                 /* starts at 60 due to "Comment: " */
        char *c = key->comment;
        while ((int)strlen(c) > slen) {
            fprintf(fp, "%.*s\\\n", slen, c);
            c += slen;
            slen = 70;                 /* allow 70 chars on subsequent lines */
        }
        fprintf(fp, "%s\"\n", c);
    base64_encode(fp, outblob->u, outblob->len, 70);
    fputs("---- END SSH2 ENCRYPTED PRIVATE KEY ----\n", fp);
    fclose(fp);
    if (outblob)
        strbuf_free(outblob);
    if (privblob)
        strbuf_free(privblob);
    if (pubblob)
        strbuf_free(pubblob);