aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/libssh/wrapper.h1
-rw-r--r--src/pki_container_openssh.c170
-rw-r--r--src/wrapper.c6
3 files changed, 169 insertions, 8 deletions
diff --git a/include/libssh/wrapper.h b/include/libssh/wrapper.h
index a327e188..cdd72d6d 100644
--- a/include/libssh/wrapper.h
+++ b/include/libssh/wrapper.h
@@ -97,6 +97,7 @@ void crypto_free(struct ssh_crypto_struct *crypto);
void ssh_reseed(void);
+void ssh_cipher_clear(struct ssh_cipher_struct *cipher);
struct ssh_hmac_struct *ssh_get_hmactab(void);
const char *ssh_hmac_type_to_string(enum ssh_hmac_e hmac_type);
diff --git a/src/pki_container_openssh.c b/src/pki_container_openssh.c
index ce04a21d..9e9e855b 100644
--- a/src/pki_container_openssh.c
+++ b/src/pki_container_openssh.c
@@ -140,6 +140,131 @@ fail:
return SSH_ERROR;
}
+/**
+ * @brief decrypts an encrypted ed25519 private key blob
+ *
+ */
+static int pki_private_key_decrypt(ssh_string blob,
+ const char* passphrase,
+ const char *ciphername,
+ const char *kdfname,
+ ssh_string kdfoptions,
+ ssh_auth_callback auth_fn,
+ void *auth_data)
+{
+ struct ssh_cipher_struct *ciphers = ssh_get_ciphertab();
+ struct ssh_cipher_struct cipher;
+ uint8_t key_material[128];
+ char passphrase_buffer[128];
+ size_t key_material_len;
+ ssh_buffer buffer;
+ ssh_string salt;
+ uint32_t rounds;
+ int cmp;
+ int rc;
+ int i;
+
+ cmp = strcmp(ciphername, "none");
+ if (cmp == 0){
+ /* no decryption 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;
+ }
+ if (ssh_string_len(blob) % cipher.blocksize != 0) {
+ SSH_LOG(SSH_LOG_WARN,
+ "Encrypted string not multiple of blocksize: %zu",
+ ssh_string_len(blob));
+ return SSH_ERROR;
+ }
+
+ buffer = ssh_buffer_new();
+ if (buffer == NULL){
+ return SSH_ERROR;
+ }
+ rc = ssh_buffer_add_data(buffer,
+ ssh_string_data(kdfoptions),
+ ssh_string_len(kdfoptions));
+ if (rc != SSH_ERROR){
+ rc = ssh_buffer_unpack(buffer, "Sd", &salt, &rounds);
+ }
+ ssh_buffer_free(buffer);
+ if (rc == SSH_ERROR){
+ return SSH_ERROR;
+ }
+
+ /* 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("Decryption: %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) {
+ SAFE_FREE(salt);
+ 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) {
+ SAFE_FREE(salt);
+ 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);
+ SAFE_FREE(salt);
+ if (rc < 0){
+ return SSH_ERROR;
+ }
+ BURN_BUFFER(passphrase_buffer, sizeof(passphrase_buffer));
+
+ cipher.set_decrypt_key(&cipher,
+ key_material,
+ key_material + cipher.keysize/8);
+ cipher.decrypt(&cipher,
+ ssh_string_data(blob),
+ ssh_string_data(blob),
+ ssh_string_len(blob));
+ ssh_cipher_clear(&cipher);
+ return SSH_OK;
+}
+
+
/** @internal
* @brief Import a private key in OpenSSH (new) format. This format is
* typically used with ed25519 keys but can be used for others.
@@ -161,7 +286,9 @@ ssh_key ssh_pki_openssh_privkey_import(const char *text_key,
ssh_string kdfoptions = NULL;
ssh_string pubkey0 = NULL;
ssh_string privkeys = NULL;
+ ssh_string comment = NULL;
ssh_key key = NULL;
+ uint8_t padding;
cmp = strncmp(ptr, OPENSSH_HEADER_BEGIN, strlen(OPENSSH_HEADER_BEGIN));
if (cmp != 0){
@@ -212,25 +339,54 @@ ssh_key ssh_pki_openssh_privkey_import(const char *text_key,
SSH_LOG(SSH_LOG_WARN, "Not an OpenSSH private key (bad magic)");
goto error;
}
- SSH_LOG(SSH_LOG_INFO, "Opening OpenSSH private key: ciphername: %s, kdf: %s, nkeys: %d\n", ciphername, kdfname, nkeys);
- if (strcmp(ciphername, "none") != 0){
- SSH_LOG(SSH_LOG_WARN, "Unsupported cipher %s", ciphername);
- goto error;
- }
+ ssh_pki_log("Opening OpenSSH private key: ciphername: %s, kdf: %s, nkeys: %d\n", ciphername, kdfname, nkeys);
if (nkeys != 1){
SSH_LOG(SSH_LOG_WARN, "Opening OpenSSH private key: only 1 key supported (%d available)", nkeys);
goto error;
}
+ rc = pki_private_key_decrypt(privkeys,
+ passphrase,
+ ciphername,
+ kdfname,
+ kdfoptions,
+ auth_fn,
+ auth_data);
+ if (rc == SSH_ERROR){
+ goto error;
+ }
privkey_buffer = ssh_buffer_new();
- ssh_buffer_add_data(privkey_buffer, ssh_string_data(privkeys), ssh_string_len(privkeys));
+ if (privkey_buffer == NULL) {
+ rc = SSH_ERROR;
+ goto error;
+ }
+
+ ssh_buffer_set_secure(privkey_buffer);
+ ssh_buffer_add_data(privkey_buffer,
+ ssh_string_data(privkeys),
+ ssh_string_len(privkeys));
+
rc = ssh_buffer_unpack(privkey_buffer, "dd", &checkint1, &checkint2);
if (rc == SSH_ERROR || checkint1 != checkint2){
SSH_LOG(SSH_LOG_WARN, "OpenSSH private key unpack error (correct password?)");
goto error;
}
rc = pki_openssh_import_privkey_blob(privkey_buffer, &key);
-
+ if (rc == SSH_ERROR){
+ goto error;
+ }
+ comment = buffer_get_ssh_string(privkey_buffer);
+ SAFE_FREE(comment);
+ /* verify that the remaining data is correct padding */
+ for (i=1; buffer_get_rest_len(privkey_buffer) > 0; ++i){
+ buffer_get_u8(privkey_buffer, &padding);
+ if (padding != i){
+ ssh_key_free(key);
+ key = NULL;
+ ssh_pki_log("Invalid padding");
+ goto error;
+ }
+ }
error:
if(buffer != NULL){
ssh_buffer_free(buffer);
diff --git a/src/wrapper.c b/src/wrapper.c
index bcd941b3..c1dd4d03 100644
--- a/src/wrapper.c
+++ b/src/wrapper.c
@@ -103,7 +103,7 @@ static struct ssh_cipher_struct *cipher_new(int offset) {
return cipher;
}
-static void cipher_free(struct ssh_cipher_struct *cipher) {
+void ssh_cipher_clear(struct ssh_cipher_struct *cipher){
#ifdef HAVE_LIBGCRYPT
unsigned int i;
#endif
@@ -123,6 +123,10 @@ static void cipher_free(struct ssh_cipher_struct *cipher) {
#endif
SAFE_FREE(cipher->key);
}
+}
+
+static void cipher_free(struct ssh_cipher_struct *cipher) {
+ ssh_cipher_clear(cipher);
SAFE_FREE(cipher);
}