/** @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) { rc = buffer_add_u8(privkey_buffer, padding); if (rc < 0) { return SSH_ERROR; } 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; }
static void cipher_free(struct ssh_cipher_struct *cipher) { ssh_cipher_clear(cipher); SAFE_FREE(cipher); }
/** * @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; }