diff options
author | Aris Adamantiadis <aris@0xbadc0de.be> | 2015-01-21 14:20:48 +0100 |
---|---|---|
committer | Andreas Schneider <asn@cryptomilk.org> | 2015-02-02 14:45:52 +0100 |
commit | 9e4700cdc0a97d395b2be2ac573acab236ba1edf (patch) | |
tree | d0d8106a333a34d4d0c08975f0b0be4ab3d27562 | |
parent | b76d37b341c48ef9865183d6fe287bbf4029ccb6 (diff) | |
download | libssh-9e4700cdc0a97d395b2be2ac573acab236ba1edf.tar.gz libssh-9e4700cdc0a97d395b2be2ac573acab236ba1edf.tar.xz libssh-9e4700cdc0a97d395b2be2ac573acab236ba1edf.zip |
ed25519: Add support for OpenSSH encrypted container export
Signed-off-by: Aris Adamantiadis <aris@0xbadc0de.be>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
-rw-r--r-- | src/pki_container_openssh.c | 159 |
1 files changed, 153 insertions, 6 deletions
diff --git a/src/pki_container_openssh.c b/src/pki_container_openssh.c index 9e9e855b..c2aface2 100644 --- a/src/pki_container_openssh.c +++ b/src/pki_container_openssh.c @@ -437,6 +437,111 @@ static int pki_openssh_export_privkey_blob(const ssh_key privkey, } /** @internal + * @brief encrypts an ed25519 private key blob + * + */ +static int pki_private_key_encrypt(ssh_buffer privkey_buffer, + const char* passphrase, + const char *ciphername, + const char *kdfname, + ssh_auth_callback auth_fn, + void *auth_data, + uint32_t rounds, + ssh_string salt) +{ + struct ssh_cipher_struct *ciphers = ssh_get_ciphertab(); + struct ssh_cipher_struct cipher; + uint8_t key_material[128]; + size_t key_material_len; + char passphrase_buffer[128]; + int rc; + int i; + uint8_t padding = 1; + int cmp; + + cmp = strcmp(ciphername, "none"); + if (cmp == 0){ + /* no encryption required */ + return SSH_OK; + } + + for (i = 0; ciphers[i].name != NULL; i++) { + cmp = strcmp(ciphername, ciphers[i].name); + if (cmp == 0){ + memcpy(&cipher, &ciphers[i], sizeof(cipher)); + break; + } + } + + if (ciphers[i].name == NULL){ + SSH_LOG(SSH_LOG_WARN, "Unsupported cipher %s", ciphername); + return SSH_ERROR; + } + + cmp = strcmp(kdfname, "bcrypt"); + if (cmp != 0){ + SSH_LOG(SSH_LOG_WARN, "Unsupported KDF %s", kdfname); + return SSH_ERROR; + } + while (ssh_buffer_get_len(privkey_buffer) % cipher.blocksize != 0) { + buffer_add_u8(privkey_buffer, padding); + padding++; + } + + /* We need material for key (keysize bits / 8) and IV (blocksize) */ + key_material_len = cipher.keysize/8 + cipher.blocksize; + if (key_material_len > sizeof(key_material)){ + ssh_pki_log("Key material too big"); + return SSH_ERROR; + } + + ssh_pki_log("Encryption: %d key, %d IV, %d rounds, %zu bytes salt", + cipher.keysize/8, + cipher.blocksize, rounds, ssh_string_len(salt)); + + if (passphrase == NULL){ + if (auth_fn == NULL){ + ssh_pki_log("No passphrase provided"); + return SSH_ERROR; + } + rc = auth_fn("Passphrase", + passphrase_buffer, + sizeof(passphrase_buffer), + 0, + 0, + auth_data); + if (rc != SSH_OK){ + return SSH_ERROR; + } + passphrase = passphrase_buffer; + } + + rc = bcrypt_pbkdf(passphrase, + strlen(passphrase), + ssh_string_data(salt), + ssh_string_len(salt), + key_material, + key_material_len, + rounds); + if (rc < 0){ + return SSH_ERROR; + } + + cipher.set_encrypt_key(&cipher, + key_material, + key_material + cipher.keysize/8); + cipher.encrypt(&cipher, + ssh_buffer_get_begin(privkey_buffer), + ssh_buffer_get_begin(privkey_buffer), + ssh_buffer_get_len(privkey_buffer)); + ssh_cipher_clear(&cipher); + BURN_BUFFER(passphrase_buffer, sizeof(passphrase_buffer)); + + return SSH_OK; +} + + +/** @internal * generate an OpenSSH private key (defined in PROTOCOL.key) and output it in text format. * @param privkey[in] private key to export * @returns an SSH string containing the text representation of the exported key. @@ -453,6 +558,10 @@ ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey, ssh_string pubkey_s=NULL; ssh_buffer privkey_buffer = NULL; uint32_t rnd; + uint32_t rounds = 16; + ssh_string salt=NULL; + ssh_string kdf_options=NULL; + int to_encrypt=0; unsigned char *b64; uint32_t str_len, len; int rc; @@ -464,6 +573,10 @@ ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey, ssh_pki_log("Unsupported key type %s", privkey->type_c); return NULL; } + if (passphrase != NULL || auth_fn != NULL){ + ssh_pki_log("Enabling encryption for private key export"); + to_encrypt = 1; + } buffer = ssh_buffer_new(); pubkey_s = pki_publickey_to_blob(privkey); if(buffer == NULL || pubkey_s == NULL){ @@ -496,13 +609,45 @@ ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey, goto error; } + if (to_encrypt){ + ssh_buffer kdf_buf = ssh_buffer_new(); + salt = ssh_string_new(16); + if (kdf_buf == NULL || salt == NULL){ + goto error; + } + ssh_get_random(ssh_string_data(salt),16, 0); + ssh_buffer_pack(kdf_buf, "Sd", salt, rounds); + kdf_options = ssh_string_new(ssh_buffer_get_len(kdf_buf)); + if (kdf_options == NULL){ + ssh_buffer_free(kdf_buf); + goto error; + } + memcpy(ssh_string_data(kdf_options), + ssh_buffer_get_begin(kdf_buf), + ssh_buffer_get_len(kdf_buf)); + ssh_buffer_free(kdf_buf); + rc = pki_private_key_encrypt(privkey_buffer, + passphrase, + "aes128-cbc", + "bcrypt", + auth_fn, + auth_data, + rounds, + salt); + if (rc != SSH_OK){ + goto error; + } + } else { + kdf_options = ssh_string_new(0); + } + rc = ssh_buffer_pack(buffer, - "PsssdSdP", + "PssSdSdP", (size_t)strlen(OPENSSH_AUTH_MAGIC) + 1, OPENSSH_AUTH_MAGIC, - "none", /* ciphername */ - "none", /* kdfname */ - "", /* kdfoptions */ - (uint32_t)1, /* nkeys */ + to_encrypt ? "aes128-cbc" : "none", /* ciphername */ + to_encrypt ? "bcrypt" : "none", /* kdfname */ + kdf_options, /* kdfoptions */ + (uint32_t) 1, /* nkeys */ pubkey_s, (uint32_t)ssh_buffer_get_len(privkey_buffer), /* rest of buffer is a string */ @@ -553,7 +698,9 @@ error: ssh_buffer_free(privkey_buffer); } SAFE_FREE(pubkey_s); - if (buffer != NULL){ + SAFE_FREE(kdf_options); + SAFE_FREE(salt); + if (buffer != NULL) { ssh_buffer_free(buffer); } |