Blame src/pki_container_openssh.c

Packit Service 31306d
/*
Packit Service 31306d
 * pki_container_openssh.c
Packit Service 31306d
 * This file is part of the SSH Library
Packit Service 31306d
 *
Packit Service 31306d
 * Copyright (c) 2013,2014 Aris Adamantiadis <aris@badcode.be>
Packit Service 31306d
 *
Packit Service 31306d
 * The SSH Library is free software; you can redistribute it and/or modify
Packit Service 31306d
 * it under the terms of the GNU Lesser General Public License as published by
Packit Service 31306d
 * the Free Software Foundation; either version 2.1 of the License, or (at your
Packit Service 31306d
 * option) any later version.
Packit Service 31306d
 *
Packit Service 31306d
 * The SSH Library is distributed in the hope that it will be useful, but
Packit Service 31306d
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
Packit Service 31306d
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
Packit Service 31306d
 * License for more details.
Packit Service 31306d
 *
Packit Service 31306d
 * You should have received a copy of the GNU Lesser General Public License
Packit Service 31306d
 * along with the SSH Library; see the file COPYING.  If not, write to
Packit Service 31306d
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
Packit Service 31306d
 * MA 02111-1307, USA.
Packit Service 31306d
 */
Packit Service 31306d
Packit Service 31306d
/**
Packit Service 31306d
 * @ingroup libssh_pki
Packit Service 31306d
 * *
Packit Service 31306d
 * @{
Packit Service 31306d
 */
Packit Service 31306d
Packit Service 31306d
#include "config.h"
Packit Service 31306d
Packit Service 31306d
#include <ctype.h>
Packit Service 31306d
#include <string.h>
Packit Service 31306d
#include <stdbool.h>
Packit Service 31306d
Packit Service 31306d
#include "libssh/libssh.h"
Packit Service 31306d
#include "libssh/priv.h"
Packit Service 31306d
#include "libssh/pki.h"
Packit Service 31306d
#include "libssh/pki_priv.h"
Packit Service 31306d
#include "libssh/buffer.h"
Packit Service 31306d
Packit Service 31306d
Packit Service 31306d
/**
Packit Service 31306d
 * @internal
Packit Service 31306d
 *
Packit Service 31306d
 * @brief Import a private key from a ssh buffer.
Packit Service 31306d
 *
Packit Service 31306d
 * @param[in] key_blob_buffer The key blob to import as specified in
Packit Service 31306d
 *                            key.c:key_private_serialize in OpenSSH source
Packit Service 31306d
 *                            code.
Packit Service 31306d
 *
Packit Service 31306d
 * @param[out] pkey     A pointer where the allocated key can be stored. You
Packit Service 31306d
 *                      need to free the memory.
Packit Service 31306d
 *
Packit Service 31306d
 * @return              SSH_OK on success, SSH_ERROR on error.
Packit Service 31306d
 *
Packit Service 31306d
 * @see ssh_key_free()
Packit Service 31306d
 */
Packit Service 31306d
static int pki_openssh_import_privkey_blob(ssh_buffer key_blob_buffer,
Packit Service 31306d
                                           ssh_key *pkey)
Packit Service 31306d
{
Packit Service 31306d
    enum ssh_keytypes_e type;
Packit Service 31306d
    char *type_s = NULL;
Packit Service 31306d
    ssh_key key = NULL;
Packit Service 31306d
    int rc;
Packit Service 31306d
Packit Service 31306d
    if (pkey == NULL) {
Packit Service 31306d
        return SSH_ERROR;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    rc = ssh_buffer_unpack(key_blob_buffer, "s", &type_s);
Packit Service 31306d
    if (rc == SSH_ERROR){
Packit Service 31306d
        SSH_LOG(SSH_LOG_WARN, "Unpack error");
Packit Service 31306d
        return SSH_ERROR;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    type = ssh_key_type_from_name(type_s);
Packit Service 31306d
    if (type == SSH_KEYTYPE_UNKNOWN) {
Packit Service 31306d
        SSH_LOG(SSH_LOG_WARN, "Unknown key type '%s' found!", type_s);
Packit Service 31306d
        return SSH_ERROR;
Packit Service 31306d
    }
Packit Service 31306d
    SAFE_FREE(type_s);
Packit Service 31306d
Packit Service 31306d
    rc = pki_import_privkey_buffer(type, key_blob_buffer, &key);
Packit Service 31306d
    if (rc != SSH_OK) {
Packit Service 31306d
        SSH_LOG(SSH_LOG_WARN, "Failed to read key in OpenSSH format");
Packit Service 31306d
        goto fail;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    *pkey = key;
Packit Service 31306d
    return SSH_OK;
Packit Service 31306d
fail:
Packit Service 31306d
    ssh_key_free(key);
Packit Service 31306d
Packit Service 31306d
    return SSH_ERROR;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
/**
Packit Service 31306d
 * @brief decrypts an encrypted private key blob in OpenSSH format.
Packit Service 31306d
 *
Packit Service 31306d
 */
Packit Service 31306d
static int pki_private_key_decrypt(ssh_string blob,
Packit Service 31306d
                                   const char* passphrase,
Packit Service 31306d
                                   const char *ciphername,
Packit Service 31306d
                                   const char *kdfname,
Packit Service 31306d
                                   ssh_string kdfoptions,
Packit Service 31306d
                                   ssh_auth_callback auth_fn,
Packit Service 31306d
                                   void *auth_data)
Packit Service 31306d
{
Packit Service 31306d
    struct ssh_cipher_struct *ciphers = ssh_get_ciphertab();
Packit Service 31306d
    struct ssh_cipher_struct cipher;
Packit Service 31306d
    uint8_t key_material[128] = {0};
Packit Service 31306d
    char passphrase_buffer[128] = {0};
Packit Service 31306d
    size_t key_material_len;
Packit Service 31306d
    ssh_buffer buffer = NULL;
Packit Service 31306d
    ssh_string salt = NULL;
Packit Service 31306d
    uint32_t rounds;
Packit Service 31306d
    int cmp;
Packit Service 31306d
    int rc;
Packit Service 31306d
    int i;
Packit Service 31306d
Packit Service 31306d
    cmp = strcmp(ciphername, "none");
Packit Service 31306d
    if (cmp == 0){
Packit Service 31306d
        /* no decryption required */
Packit Service 31306d
        return SSH_OK;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    for (i = 0; ciphers[i].name != NULL; i++) {
Packit Service 31306d
        cmp = strcmp(ciphername, ciphers[i].name);
Packit Service 31306d
        if (cmp == 0){
Packit Service 31306d
            memcpy(&cipher, &ciphers[i], sizeof(cipher));
Packit Service 31306d
            break;
Packit Service 31306d
        }
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    if (ciphers[i].name == NULL){
Packit Service 31306d
        SSH_LOG(SSH_LOG_WARN, "Unsupported cipher %s", ciphername);
Packit Service 31306d
        return SSH_ERROR;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    cmp = strcmp(kdfname, "bcrypt");
Packit Service 31306d
    if (cmp != 0) {
Packit Service 31306d
        SSH_LOG(SSH_LOG_WARN, "Unsupported KDF %s", kdfname);
Packit Service 31306d
        return SSH_ERROR;
Packit Service 31306d
    }
Packit Service 31306d
    if (ssh_string_len(blob) % cipher.blocksize != 0) {
Packit Service 31306d
        SSH_LOG(SSH_LOG_WARN,
Packit Service 31306d
                "Encrypted string not multiple of blocksize: %zu",
Packit Service 31306d
                ssh_string_len(blob));
Packit Service 31306d
        return SSH_ERROR;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    buffer = ssh_buffer_new();
Packit Service 31306d
    if (buffer == NULL){
Packit Service 31306d
        return SSH_ERROR;
Packit Service 31306d
    }
Packit Service 31306d
    rc = ssh_buffer_add_data(buffer,
Packit Service 31306d
                             ssh_string_data(kdfoptions),
Packit Service 31306d
                             ssh_string_len(kdfoptions));
Packit Service 31306d
    if (rc != SSH_ERROR){
Packit Service 31306d
        rc = ssh_buffer_unpack(buffer, "Sd", &salt, &rounds);
Packit Service 31306d
    }
Packit Service 31306d
    SSH_BUFFER_FREE(buffer);
Packit Service 31306d
    if (rc == SSH_ERROR){
Packit Service 31306d
        return SSH_ERROR;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    /* We need material for key (keysize bits / 8) and IV (blocksize)  */
Packit Service 31306d
    key_material_len =  cipher.keysize/8 + cipher.blocksize;
Packit Service 31306d
    if (key_material_len > sizeof(key_material)) {
Packit Service 31306d
        SSH_LOG(SSH_LOG_WARN, "Key material too big");
Packit Service 31306d
        return SSH_ERROR;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    SSH_LOG(SSH_LOG_DEBUG,
Packit Service 31306d
            "Decryption: %d key, %d IV, %d rounds, %zu bytes salt",
Packit Service 31306d
            cipher.keysize/8,
Packit Service 31306d
            cipher.blocksize,
Packit Service 31306d
            rounds,
Packit Service 31306d
            ssh_string_len(salt));
Packit Service 31306d
Packit Service 31306d
    if (passphrase == NULL) {
Packit Service 31306d
        if (auth_fn == NULL) {
Packit Service 31306d
            SAFE_FREE(salt);
Packit Service 31306d
            SSH_LOG(SSH_LOG_WARN, "No passphrase provided");
Packit Service 31306d
            return SSH_ERROR;
Packit Service 31306d
        }
Packit Service 31306d
        rc = auth_fn("Passphrase",
Packit Service 31306d
                     passphrase_buffer,
Packit Service 31306d
                     sizeof(passphrase_buffer),
Packit Service 31306d
                     0,
Packit Service 31306d
                     0,
Packit Service 31306d
                     auth_data);
Packit Service 31306d
        if (rc != SSH_OK) {
Packit Service 31306d
            SAFE_FREE(salt);
Packit Service 31306d
            return SSH_ERROR;
Packit Service 31306d
        }
Packit Service 31306d
        passphrase = passphrase_buffer;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    rc = bcrypt_pbkdf(passphrase,
Packit Service 31306d
                      strlen(passphrase),
Packit Service 31306d
                      ssh_string_data(salt),
Packit Service 31306d
                      ssh_string_len(salt),
Packit Service 31306d
                      key_material,
Packit Service 31306d
                      key_material_len,
Packit Service 31306d
                      rounds);
Packit Service 31306d
    SAFE_FREE(salt);
Packit Service 31306d
    if (rc < 0){
Packit Service 31306d
        return SSH_ERROR;
Packit Service 31306d
    }
Packit Service 31306d
    explicit_bzero(passphrase_buffer, sizeof(passphrase_buffer));
Packit Service 31306d
Packit Service 31306d
    cipher.set_decrypt_key(&cipher,
Packit Service 31306d
                           key_material,
Packit Service 31306d
                           key_material + cipher.keysize/8);
Packit Service 31306d
    cipher.decrypt(&cipher,
Packit Service 31306d
                   ssh_string_data(blob),
Packit Service 31306d
                   ssh_string_data(blob),
Packit Service 31306d
                   ssh_string_len(blob));
Packit Service 31306d
    ssh_cipher_clear(&cipher);
Packit Service 31306d
    return SSH_OK;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
Packit Service 31306d
/** @internal
Packit Service 31306d
 * @brief Import a private key in OpenSSH (new) format. This format is
Packit Service 31306d
 * typically used with ed25519 keys but can be used for others.
Packit Service 31306d
 */
Packit Service 31306d
static ssh_key
Packit Service 31306d
ssh_pki_openssh_import(const char *text_key,
Packit Service 31306d
                       const char *passphrase,
Packit Service 31306d
                       ssh_auth_callback auth_fn,
Packit Service 31306d
                       void *auth_data,
Packit Service 31306d
                       bool private)
Packit Service 31306d
{
Packit Service 31306d
    const char *ptr = text_key;
Packit Service 31306d
    const char *end;
Packit Service 31306d
    char *base64;
Packit Service 31306d
    int cmp;
Packit Service 31306d
    int rc;
Packit Service 31306d
    int i;
Packit Service 31306d
    ssh_buffer buffer = NULL, privkey_buffer=NULL;
Packit Service 31306d
    char *magic = NULL, *ciphername = NULL, *kdfname = NULL;
Packit Service 31306d
    uint32_t nkeys = 0, checkint1 = 0, checkint2 = 0xFFFF;
Packit Service 31306d
    ssh_string kdfoptions = NULL;
Packit Service 31306d
    ssh_string pubkey0 = NULL;
Packit Service 31306d
    ssh_string privkeys = NULL;
Packit Service 31306d
    ssh_string comment = NULL;
Packit Service 31306d
    ssh_key key = NULL;
Packit Service 31306d
    uint8_t padding;
Packit Service 31306d
Packit Service 31306d
    cmp = strncmp(ptr, OPENSSH_HEADER_BEGIN, strlen(OPENSSH_HEADER_BEGIN));
Packit Service 31306d
    if (cmp != 0) {
Packit Service 31306d
        SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (no header)");
Packit Service 31306d
        goto out;
Packit Service 31306d
    }
Packit Service 31306d
    ptr += strlen(OPENSSH_HEADER_BEGIN);
Packit Service 31306d
    while(ptr[0] != '\0' && !isspace((int)ptr[0])) {
Packit Service 31306d
        ptr++;
Packit Service 31306d
    }
Packit Service 31306d
    end = strstr(ptr, OPENSSH_HEADER_END);
Packit Service 31306d
    if (end == NULL) {
Packit Service 31306d
        SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (no footer)");
Packit Service 31306d
        goto out;
Packit Service 31306d
    }
Packit Service 31306d
    base64 = malloc(end - ptr + 1);
Packit Service 31306d
    if (base64 == NULL) {
Packit Service 31306d
        goto out;
Packit Service 31306d
    }
Packit Service 31306d
    for (i = 0; ptr < end; ptr++) {
Packit Service 31306d
        if (!isspace((int)ptr[0])) {
Packit Service 31306d
            base64[i] = ptr[0];
Packit Service 31306d
            i++;
Packit Service 31306d
        }
Packit Service 31306d
    }
Packit Service 31306d
    base64[i] = '\0';
Packit Service 31306d
    buffer = base64_to_bin(base64);
Packit Service 31306d
    SAFE_FREE(base64);
Packit Service 31306d
    if (buffer == NULL) {
Packit Service 31306d
        SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (base64 error)");
Packit Service 31306d
        goto out;
Packit Service 31306d
    }
Packit Service 31306d
    rc = ssh_buffer_unpack(buffer, "PssSdSS",
Packit Service 31306d
                           strlen(OPENSSH_AUTH_MAGIC) + 1,
Packit Service 31306d
                           &magic,
Packit Service 31306d
                           &ciphername,
Packit Service 31306d
                           &kdfname,
Packit Service 31306d
                           &kdfoptions,
Packit Service 31306d
                           &nkeys,
Packit Service 31306d
                           &pubkey0,
Packit Service 31306d
                           &privkeys);
Packit Service 31306d
    if (rc == SSH_ERROR) {
Packit Service 31306d
        SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (unpack error)");
Packit Service 31306d
        goto out;
Packit Service 31306d
    }
Packit Service 31306d
    cmp = strncmp(magic, OPENSSH_AUTH_MAGIC, strlen(OPENSSH_AUTH_MAGIC));
Packit Service 31306d
    if (cmp != 0) {
Packit Service 31306d
        SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (bad magic)");
Packit Service 31306d
        goto out;
Packit Service 31306d
    }
Packit Service 31306d
    SSH_LOG(SSH_LOG_INFO,
Packit Service 31306d
            "Opening OpenSSH private key: ciphername: %s, kdf: %s, nkeys: %d",
Packit Service 31306d
            ciphername,
Packit Service 31306d
            kdfname,
Packit Service 31306d
            nkeys);
Packit Service 31306d
    if (nkeys != 1) {
Packit Service 31306d
        SSH_LOG(SSH_LOG_WARN, "Opening OpenSSH private key: only 1 key supported (%d available)", nkeys);
Packit Service 31306d
        goto out;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    /* If we are interested only in public key do not progress
Packit Service 31306d
     * to the key decryption later
Packit Service 31306d
     */
Packit Service 31306d
    if (!private) {
Packit Service 31306d
        rc = ssh_pki_import_pubkey_blob(pubkey0, &key);
Packit Service 31306d
        if (rc != SSH_OK) {
Packit Service 31306d
            SSH_LOG(SSH_LOG_WARN, "Failed to import public key blob");
Packit Service 31306d
        }
Packit Service 31306d
        /* in either case we clean up here */
Packit Service 31306d
        goto out;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    rc = pki_private_key_decrypt(privkeys,
Packit Service 31306d
                                 passphrase,
Packit Service 31306d
                                 ciphername,
Packit Service 31306d
                                 kdfname,
Packit Service 31306d
                                 kdfoptions,
Packit Service 31306d
                                 auth_fn,
Packit Service 31306d
                                 auth_data);
Packit Service 31306d
    if (rc == SSH_ERROR) {
Packit Service 31306d
        goto out;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    privkey_buffer = ssh_buffer_new();
Packit Service 31306d
    if (privkey_buffer == NULL) {
Packit Service 31306d
        goto out;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    ssh_buffer_set_secure(privkey_buffer);
Packit Service 31306d
    ssh_buffer_add_data(privkey_buffer,
Packit Service 31306d
                        ssh_string_data(privkeys),
Packit Service 31306d
                        ssh_string_len(privkeys));
Packit Service 31306d
Packit Service 31306d
    rc = ssh_buffer_unpack(privkey_buffer, "dd", &checkint1, &checkint2);
Packit Service 31306d
    if (rc == SSH_ERROR || checkint1 != checkint2) {
Packit Service 31306d
        SSH_LOG(SSH_LOG_WARN, "OpenSSH private key unpack error (correct password?)");
Packit Service 31306d
        goto out;
Packit Service 31306d
    }
Packit Service 31306d
    rc = pki_openssh_import_privkey_blob(privkey_buffer, &key);
Packit Service 31306d
    if (rc == SSH_ERROR) {
Packit Service 31306d
        goto out;
Packit Service 31306d
    }
Packit Service 31306d
    comment = ssh_buffer_get_ssh_string(privkey_buffer);
Packit Service 31306d
    SAFE_FREE(comment);
Packit Service 31306d
    /* verify that the remaining data is correct padding */
Packit Service 31306d
    for (i = 1; ssh_buffer_get_len(privkey_buffer) > 0; ++i) {
Packit Service 31306d
        ssh_buffer_get_u8(privkey_buffer, &padding);
Packit Service 31306d
        if (padding != i) {
Packit Service 31306d
            ssh_key_free(key);
Packit Service 31306d
            key = NULL;
Packit Service 31306d
            SSH_LOG(SSH_LOG_WARN, "Invalid padding");
Packit Service 31306d
            goto out;
Packit Service 31306d
        }
Packit Service 31306d
    }
Packit Service 31306d
out:
Packit Service 31306d
    if (buffer != NULL) {
Packit Service 31306d
        SSH_BUFFER_FREE(buffer);
Packit Service 31306d
        buffer = NULL;
Packit Service 31306d
    }
Packit Service 31306d
    if (privkey_buffer != NULL) {
Packit Service 31306d
        SSH_BUFFER_FREE(privkey_buffer);
Packit Service 31306d
        privkey_buffer = NULL;
Packit Service 31306d
    }
Packit Service 31306d
    SAFE_FREE(magic);
Packit Service 31306d
    SAFE_FREE(ciphername);
Packit Service 31306d
    SAFE_FREE(kdfname);
Packit Service 31306d
    SAFE_FREE(kdfoptions);
Packit Service 31306d
    SAFE_FREE(pubkey0);
Packit Service 31306d
    SAFE_FREE(privkeys);
Packit Service 31306d
    return key;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
ssh_key ssh_pki_openssh_privkey_import(const char *text_key,
Packit Service 31306d
                                       const char *passphrase,
Packit Service 31306d
                                       ssh_auth_callback auth_fn,
Packit Service 31306d
                                       void *auth_data)
Packit Service 31306d
{
Packit Service 31306d
    return ssh_pki_openssh_import(text_key, passphrase, auth_fn, auth_data, true);
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
ssh_key ssh_pki_openssh_pubkey_import(const char *text_key)
Packit Service 31306d
{
Packit Service 31306d
    return ssh_pki_openssh_import(text_key, NULL, NULL, NULL, false);
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
Packit Service 31306d
/** @internal
Packit Service 31306d
 * @brief exports a private key to a string blob.
Packit Service 31306d
 * @param[in] privkey private key to convert
Packit Service 31306d
 * @param[out] buffer buffer to write the blob in.
Packit Service 31306d
 * @returns SSH_OK on success
Packit Service 31306d
 * @warning only supports ed25519 key type at the moment.
Packit Service 31306d
 */
Packit Service 31306d
static int pki_openssh_export_privkey_blob(const ssh_key privkey,
Packit Service 31306d
                                           ssh_buffer buffer)
Packit Service 31306d
{
Packit Service 31306d
    int rc;
Packit Service 31306d
Packit Service 31306d
    if (privkey->type != SSH_KEYTYPE_ED25519) {
Packit Service 31306d
        SSH_LOG(SSH_LOG_WARN, "Type %s not supported", privkey->type_c);
Packit Service 31306d
        return SSH_ERROR;
Packit Service 31306d
    }
Packit Service 31306d
    if (privkey->ed25519_privkey == NULL ||
Packit Service 31306d
        privkey->ed25519_pubkey == NULL) {
Packit Service 31306d
        return SSH_ERROR;
Packit Service 31306d
    }
Packit Service 31306d
    rc = ssh_buffer_pack(buffer,
Packit Service 31306d
                         "sdPdPP",
Packit Service 31306d
                         privkey->type_c,
Packit Service 31306d
                         (uint32_t)ED25519_KEY_LEN,
Packit Service 31306d
                         (size_t)ED25519_KEY_LEN, privkey->ed25519_pubkey,
Packit Service 31306d
                         (uint32_t)(2 * ED25519_KEY_LEN),
Packit Service 31306d
                         (size_t)ED25519_KEY_LEN, privkey->ed25519_privkey,
Packit Service 31306d
                         (size_t)ED25519_KEY_LEN, privkey->ed25519_pubkey);
Packit Service 31306d
    return rc;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
/** @internal
Packit Service 31306d
 * @brief encrypts an ed25519 private key blob
Packit Service 31306d
 *
Packit Service 31306d
 */
Packit Service 31306d
static int pki_private_key_encrypt(ssh_buffer privkey_buffer,
Packit Service 31306d
                                   const char* passphrase,
Packit Service 31306d
                                   const char *ciphername,
Packit Service 31306d
                                   const char *kdfname,
Packit Service 31306d
                                   ssh_auth_callback auth_fn,
Packit Service 31306d
                                   void *auth_data,
Packit Service 31306d
                                   uint32_t rounds,
Packit Service 31306d
                                   ssh_string salt)
Packit Service 31306d
{
Packit Service 31306d
    struct ssh_cipher_struct *ciphers = ssh_get_ciphertab();
Packit Service 31306d
    struct ssh_cipher_struct cipher;
Packit Service 31306d
    uint8_t key_material[128] = {0};
Packit Service 31306d
    size_t key_material_len;
Packit Service 31306d
    char passphrase_buffer[128] = {0};
Packit Service 31306d
    int rc;
Packit Service 31306d
    int i;
Packit Service 31306d
    int cmp;
Packit Service 31306d
Packit Service 31306d
    cmp = strcmp(ciphername, "none");
Packit Service 31306d
    if (cmp == 0){
Packit Service 31306d
        /* no encryption required */
Packit Service 31306d
        return SSH_OK;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    for (i = 0; ciphers[i].name != NULL; i++) {
Packit Service 31306d
        cmp = strcmp(ciphername, ciphers[i].name);
Packit Service 31306d
        if (cmp == 0){
Packit Service 31306d
            memcpy(&cipher, &ciphers[i], sizeof(cipher));
Packit Service 31306d
            break;
Packit Service 31306d
        }
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    if (ciphers[i].name == NULL){
Packit Service 31306d
        SSH_LOG(SSH_LOG_WARN, "Unsupported cipher %s", ciphername);
Packit Service 31306d
        return SSH_ERROR;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    cmp = strcmp(kdfname, "bcrypt");
Packit Service 31306d
    if (cmp != 0){
Packit Service 31306d
        SSH_LOG(SSH_LOG_WARN, "Unsupported KDF %s", kdfname);
Packit Service 31306d
        return SSH_ERROR;
Packit Service 31306d
    }
Packit Service 31306d
    /* We need material for key (keysize bits / 8) and IV (blocksize)  */
Packit Service 31306d
    key_material_len =  cipher.keysize/8 + cipher.blocksize;
Packit Service 31306d
    if (key_material_len > sizeof(key_material)){
Packit Service 31306d
        SSH_LOG(SSH_LOG_WARN, "Key material too big");
Packit Service 31306d
        return SSH_ERROR;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    SSH_LOG(SSH_LOG_WARN, "Encryption: %d key, %d IV, %d rounds, %zu bytes salt",
Packit Service 31306d
                cipher.keysize/8,
Packit Service 31306d
                cipher.blocksize, rounds, ssh_string_len(salt));
Packit Service 31306d
Packit Service 31306d
    if (passphrase == NULL){
Packit Service 31306d
        if (auth_fn == NULL){
Packit Service 31306d
            SSH_LOG(SSH_LOG_WARN, "No passphrase provided");
Packit Service 31306d
            return SSH_ERROR;
Packit Service 31306d
        }
Packit Service 31306d
        rc = auth_fn("Passphrase",
Packit Service 31306d
                     passphrase_buffer,
Packit Service 31306d
                     sizeof(passphrase_buffer),
Packit Service 31306d
                     0,
Packit Service 31306d
                     0,
Packit Service 31306d
                     auth_data);
Packit Service 31306d
        if (rc != SSH_OK){
Packit Service 31306d
            return SSH_ERROR;
Packit Service 31306d
        }
Packit Service 31306d
        passphrase = passphrase_buffer;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    rc = bcrypt_pbkdf(passphrase,
Packit Service 31306d
                      strlen(passphrase),
Packit Service 31306d
                      ssh_string_data(salt),
Packit Service 31306d
                      ssh_string_len(salt),
Packit Service 31306d
                      key_material,
Packit Service 31306d
                      key_material_len,
Packit Service 31306d
                      rounds);
Packit Service 31306d
    if (rc < 0){
Packit Service 31306d
        return SSH_ERROR;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    cipher.set_encrypt_key(&cipher,
Packit Service 31306d
                           key_material,
Packit Service 31306d
                           key_material + cipher.keysize/8);
Packit Service 31306d
    cipher.encrypt(&cipher,
Packit Service 31306d
                   ssh_buffer_get(privkey_buffer),
Packit Service 31306d
                   ssh_buffer_get(privkey_buffer),
Packit Service 31306d
                   ssh_buffer_get_len(privkey_buffer));
Packit Service 31306d
    ssh_cipher_clear(&cipher);
Packit Service 31306d
    explicit_bzero(passphrase_buffer, sizeof(passphrase_buffer));
Packit Service 31306d
Packit Service 31306d
    return SSH_OK;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
Packit Service 31306d
/** @internal
Packit Service 31306d
 * generate an OpenSSH private key (defined in PROTOCOL.key) and output it in text format.
Packit Service 31306d
 * @param privkey[in] private key to export
Packit Service 31306d
 * @returns an SSH string containing the text representation of the exported key.
Packit Service 31306d
 * @warning currently only supports ED25519 key types.
Packit Service 31306d
 */
Packit Service 31306d
Packit Service 31306d
ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey,
Packit Service 31306d
                                          const char *passphrase,
Packit Service 31306d
                                          ssh_auth_callback auth_fn,
Packit Service 31306d
                                          void *auth_data)
Packit Service 31306d
{
Packit Service 31306d
    ssh_buffer buffer;
Packit Service 31306d
    ssh_string str = NULL;
Packit Service 31306d
    ssh_string pubkey_s=NULL;
Packit Service 31306d
    ssh_buffer privkey_buffer = NULL;
Packit Service 31306d
    uint32_t rnd;
Packit Service 31306d
    uint32_t rounds = 16;
Packit Service 31306d
    ssh_string salt=NULL;
Packit Service 31306d
    ssh_string kdf_options=NULL;
Packit Service 31306d
    int to_encrypt=0;
Packit Service 31306d
    unsigned char *b64;
Packit Service 31306d
    uint32_t str_len, len;
Packit Service 31306d
    uint8_t padding = 1;
Packit Service 31306d
    int ok;
Packit Service 31306d
    int rc;
Packit Service 31306d
Packit Service 31306d
    if (privkey == NULL) {
Packit Service 31306d
        return NULL;
Packit Service 31306d
    }
Packit Service 31306d
    if (privkey->type != SSH_KEYTYPE_ED25519){
Packit Service 31306d
        SSH_LOG(SSH_LOG_WARN, "Unsupported key type %s", privkey->type_c);
Packit Service 31306d
        return NULL;
Packit Service 31306d
    }
Packit Service 31306d
    if (passphrase != NULL || auth_fn != NULL){
Packit Service 31306d
        SSH_LOG(SSH_LOG_INFO, "Enabling encryption for private key export");
Packit Service 31306d
        to_encrypt = 1;
Packit Service 31306d
    }
Packit Service 31306d
    buffer = ssh_buffer_new();
Packit Service 31306d
    pubkey_s = pki_publickey_to_blob(privkey);
Packit Service 31306d
    if(buffer == NULL || pubkey_s == NULL){
Packit Service 31306d
        goto error;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    ok = ssh_get_random(&rnd, sizeof(rnd), 0);
Packit Service 31306d
    if (!ok) {
Packit Service 31306d
        goto error;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    privkey_buffer = ssh_buffer_new();
Packit Service 31306d
    if (privkey_buffer == NULL) {
Packit Service 31306d
        goto error;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    /* checkint1 & 2 */
Packit Service 31306d
    rc = ssh_buffer_pack(privkey_buffer,
Packit Service 31306d
                         "dd",
Packit Service 31306d
                         rnd,
Packit Service 31306d
                         rnd);
Packit Service 31306d
    if (rc == SSH_ERROR){
Packit Service 31306d
        goto error;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    rc = pki_openssh_export_privkey_blob(privkey, privkey_buffer);
Packit Service 31306d
    if (rc == SSH_ERROR){
Packit Service 31306d
        goto error;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    /* comment */
Packit Service 31306d
    rc = ssh_buffer_pack(privkey_buffer, "s", "" /* comment */);
Packit Service 31306d
    if (rc == SSH_ERROR){
Packit Service 31306d
        goto error;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    /* Add padding regardless encryption because it is expected
Packit Service 31306d
     * by OpenSSH tools.
Packit Service 31306d
     * XXX Using 16 B as we use only AES cipher below anyway.
Packit Service 31306d
     */
Packit Service 31306d
    while (ssh_buffer_get_len(privkey_buffer) % 16 != 0) {
Packit Service 31306d
        rc = ssh_buffer_add_u8(privkey_buffer, padding);
Packit Service 31306d
        if (rc < 0) {
Packit Service 31306d
            goto error;
Packit Service 31306d
        }
Packit Service 31306d
        padding++;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    if (to_encrypt){
Packit Service 31306d
        ssh_buffer kdf_buf;
Packit Service 31306d
Packit Service 31306d
        kdf_buf = ssh_buffer_new();
Packit Service 31306d
        if (kdf_buf == NULL) {
Packit Service 31306d
            goto error;
Packit Service 31306d
        }
Packit Service 31306d
Packit Service 31306d
        salt = ssh_string_new(16);
Packit Service 31306d
        if (salt == NULL){
Packit Service 31306d
            SSH_BUFFER_FREE(kdf_buf);
Packit Service 31306d
            goto error;
Packit Service 31306d
        }
Packit Service 31306d
Packit Service 31306d
        ok = ssh_get_random(ssh_string_data(salt), 16, 0);
Packit Service 31306d
        if (!ok) {
Packit Service 31306d
            SSH_BUFFER_FREE(kdf_buf);
Packit Service 31306d
            goto error;
Packit Service 31306d
        }
Packit Service 31306d
Packit Service 31306d
        ssh_buffer_pack(kdf_buf, "Sd", salt, rounds);
Packit Service 31306d
        kdf_options = ssh_string_new(ssh_buffer_get_len(kdf_buf));
Packit Service 31306d
        if (kdf_options == NULL){
Packit Service 31306d
            SSH_BUFFER_FREE(kdf_buf);
Packit Service 31306d
            goto error;
Packit Service 31306d
        }
Packit Service 31306d
        memcpy(ssh_string_data(kdf_options),
Packit Service 31306d
               ssh_buffer_get(kdf_buf),
Packit Service 31306d
               ssh_buffer_get_len(kdf_buf));
Packit Service 31306d
        SSH_BUFFER_FREE(kdf_buf);
Packit Service 31306d
        rc = pki_private_key_encrypt(privkey_buffer,
Packit Service 31306d
                                     passphrase,
Packit Service 31306d
                                     "aes128-cbc",
Packit Service 31306d
                                     "bcrypt",
Packit Service 31306d
                                     auth_fn,
Packit Service 31306d
                                     auth_data,
Packit Service 31306d
                                     rounds,
Packit Service 31306d
                                     salt);
Packit Service 31306d
        if (rc != SSH_OK){
Packit Service 31306d
            goto error;
Packit Service 31306d
        }
Packit Service 31306d
    } else {
Packit Service 31306d
        kdf_options = ssh_string_new(0);
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    rc = ssh_buffer_pack(buffer,
Packit Service 31306d
                         "PssSdSdP",
Packit Service 31306d
                         (size_t)strlen(OPENSSH_AUTH_MAGIC) + 1, OPENSSH_AUTH_MAGIC,
Packit Service 31306d
                         to_encrypt ? "aes128-cbc" : "none", /* ciphername */
Packit Service 31306d
                         to_encrypt ? "bcrypt" : "none", /* kdfname */
Packit Service 31306d
                         kdf_options, /* kdfoptions */
Packit Service 31306d
                         (uint32_t) 1, /* nkeys */
Packit Service 31306d
                         pubkey_s,
Packit Service 31306d
                         (uint32_t)ssh_buffer_get_len(privkey_buffer),
Packit Service 31306d
                         /* rest of buffer is a string */
Packit Service 31306d
                         (size_t)ssh_buffer_get_len(privkey_buffer), ssh_buffer_get(privkey_buffer));
Packit Service 31306d
    if (rc != SSH_OK) {
Packit Service 31306d
        goto error;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    b64 = bin_to_base64(ssh_buffer_get(buffer),
Packit Service 31306d
                        ssh_buffer_get_len(buffer));
Packit Service 31306d
    if (b64 == NULL){
Packit Service 31306d
        goto error;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    /* we can reuse the buffer */
Packit Service 31306d
    ssh_buffer_reinit(buffer);
Packit Service 31306d
    rc = ssh_buffer_pack(buffer,
Packit Service 31306d
                         "tttttt",
Packit Service 31306d
                         OPENSSH_HEADER_BEGIN,
Packit Service 31306d
                         "\n",
Packit Service 31306d
                         b64,
Packit Service 31306d
                         "\n",
Packit Service 31306d
                         OPENSSH_HEADER_END,
Packit Service 31306d
                         "\n");
Packit Service 31306d
    explicit_bzero(b64, strlen((char *)b64));
Packit Service 31306d
    SAFE_FREE(b64);
Packit Service 31306d
Packit Service 31306d
    if (rc != SSH_OK){
Packit Service 31306d
        goto error;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    str = ssh_string_new(ssh_buffer_get_len(buffer));
Packit Service 31306d
    if (str == NULL){
Packit Service 31306d
        goto error;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    str_len = ssh_buffer_get_len(buffer);
Packit Service 31306d
    len = ssh_buffer_get_data(buffer, ssh_string_data(str), str_len);
Packit Service 31306d
    if (str_len != len) {
Packit Service 31306d
        SSH_STRING_FREE(str);
Packit Service 31306d
        str = NULL;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
error:
Packit Service 31306d
    if (privkey_buffer != NULL) {
Packit Service 31306d
        void *bufptr = ssh_buffer_get(privkey_buffer);
Packit Service 31306d
        explicit_bzero(bufptr, ssh_buffer_get_len(privkey_buffer));
Packit Service 31306d
        SSH_BUFFER_FREE(privkey_buffer);
Packit Service 31306d
    }
Packit Service 31306d
    SAFE_FREE(pubkey_s);
Packit Service 31306d
    SAFE_FREE(kdf_options);
Packit Service 31306d
    SAFE_FREE(salt);
Packit Service 31306d
    if (buffer != NULL) {
Packit Service 31306d
        SSH_BUFFER_FREE(buffer);
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    return str;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
Packit Service 31306d
/**
Packit Service 31306d
 * @}
Packit Service 31306d
 */