int pki_ed25519_verify(const ssh_key pubkey, ssh_signature sig, const unsigned char *hash, size_t hlen) { unsigned long long mlen = 0; uint8_t *buffer; uint8_t *buffer2; int rc; if (pubkey == NULL || sig == NULL || hash == NULL || sig->ed25519_sig == NULL) { return SSH_ERROR; } buffer = malloc(hlen + ED25519_SIG_LEN); if (buffer == NULL) { return SSH_ERROR; } buffer2 = malloc(hlen + ED25519_SIG_LEN); if (buffer2 == NULL) { goto error; } memcpy(buffer, sig->ed25519_sig, ED25519_SIG_LEN); memcpy(buffer + ED25519_SIG_LEN, hash, hlen); rc = crypto_sign_ed25519_open(buffer2, &mlen, buffer, hlen + ED25519_SIG_LEN, *pubkey->ed25519_pubkey); BURN_BUFFER(buffer, hlen + ED25519_SIG_LEN); BURN_BUFFER(buffer2, hlen); SAFE_FREE(buffer); SAFE_FREE(buffer2); if (rc == 0) { return SSH_OK; } else { return SSH_ERROR; } error: SAFE_FREE(buffer); SAFE_FREE(buffer2); return SSH_ERROR; }
/** * @brief clean up the key and deallocate all existing keys * @param[in] key ssh_key to clean */ void ssh_key_clean (ssh_key key){ if(key == NULL) return; #ifdef HAVE_LIBGCRYPT if(key->dsa) gcry_sexp_release(key->dsa); if(key->rsa) gcry_sexp_release(key->rsa); if(key->ecdsa) gcry_sexp_release(key->ecdsa); #elif defined HAVE_LIBCRYPTO if(key->dsa) DSA_free(key->dsa); if(key->rsa) RSA_free(key->rsa); #ifdef HAVE_OPENSSL_ECC if(key->ecdsa) EC_KEY_free(key->ecdsa); #endif /* HAVE_OPENSSL_ECC */ #endif if (key->ed25519_privkey != NULL){ BURN_BUFFER(key->ed25519_privkey, sizeof(ed25519_privkey)); SAFE_FREE(key->ed25519_privkey); } SAFE_FREE(key->ed25519_pubkey); if (key->cert != NULL) { ssh_buffer_free(key->cert); } key->cert_type = SSH_KEYTYPE_UNKNOWN; key->flags=SSH_KEY_FLAG_EMPTY; key->type=SSH_KEYTYPE_UNKNOWN; key->ecdsa_nid = 0; key->type_c=NULL; key->dsa = NULL; key->rsa = NULL; key->ecdsa = NULL; }
int packet_decrypt(ssh_session session, void *data,uint32_t len) { struct ssh_cipher_struct *crypto = session->current_crypto->in_cipher; char *out = NULL; assert(len); if(len % session->current_crypto->in_cipher->blocksize != 0){ ssh_set_error(session, SSH_FATAL, "Cryptographic functions must be set on at least one blocksize (received %d)",len); return SSH_ERROR; } out = malloc(len); if (out == NULL) { return -1; } if (crypto->set_decrypt_key(crypto, session->current_crypto->decryptkey, session->current_crypto->decryptIV) < 0) { SAFE_FREE(out); return -1; } crypto->decrypt(crypto,data,out,len); memcpy(data,out,len); BURN_BUFFER(out, len); SAFE_FREE(out); return 0; }
static void des_cleanup(struct ssh_cipher_struct *cipher){ BURN_BUFFER(cipher->des3_key, sizeof(*cipher->des3_key)); SAFE_FREE(cipher->des3_key); }
ssh_string ssh_pki_openssh_privkey_export(const ssh_key privkey, const char *passphrase, ssh_auth_callback auth_fn, void *auth_data) { ssh_buffer buffer; ssh_string str = NULL; 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; if (privkey == NULL) { return NULL; } if (privkey->type != SSH_KEYTYPE_ED25519){ 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){ goto error; } ssh_get_random(&rnd, sizeof(rnd), 0); privkey_buffer = ssh_buffer_new(); if (privkey_buffer == NULL) { goto error; } /* checkint1 & 2 */ rc = ssh_buffer_pack(privkey_buffer, "dd", rnd, rnd); if (rc == SSH_ERROR){ goto error; } rc = pki_openssh_export_privkey_blob(privkey, privkey_buffer); if (rc == SSH_ERROR){ goto error; } /* comment */ rc = ssh_buffer_pack(privkey_buffer, "s", "" /* comment */); if (rc == SSH_ERROR){ goto error; } if (to_encrypt){ ssh_buffer kdf_buf; kdf_buf = ssh_buffer_new(); if (kdf_buf == NULL) { goto error; } salt = ssh_string_new(16); if (salt == NULL){ ssh_buffer_free(kdf_buf); 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", (size_t)strlen(OPENSSH_AUTH_MAGIC) + 1, OPENSSH_AUTH_MAGIC, 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 */ (size_t)ssh_buffer_get_len(privkey_buffer), ssh_buffer_get_begin(privkey_buffer)); if (rc != SSH_OK) { goto error; } b64 = bin_to_base64(ssh_buffer_get_begin(buffer), ssh_buffer_get_len(buffer)); if (b64 == NULL){ goto error; } /* we can reuse the buffer */ ssh_buffer_reinit(buffer); rc = ssh_buffer_pack(buffer, "tttttt", OPENSSH_HEADER_BEGIN, "\n", b64, "\n", OPENSSH_HEADER_END, "\n"); BURN_BUFFER(b64, strlen((char *)b64)); SAFE_FREE(b64); if (rc != SSH_OK){ goto error; } str = ssh_string_new(ssh_buffer_get_len(buffer)); if (str == NULL){ goto error; } str_len = ssh_buffer_get_len(buffer); len = buffer_get_data(buffer, ssh_string_data(str), str_len); if (str_len != len) { ssh_string_free(str); str = NULL; } error: if (privkey_buffer != NULL) { void *bufptr = ssh_buffer_get_begin(privkey_buffer); BURN_BUFFER(bufptr, ssh_buffer_get_len(privkey_buffer)); ssh_buffer_free(privkey_buffer); } SAFE_FREE(pubkey_s); SAFE_FREE(kdf_options); SAFE_FREE(salt); if (buffer != NULL) { ssh_buffer_free(buffer); } return str; }
/** @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; }
/** * @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; }
unsigned char *packet_encrypt(ssh_session session, void *data, uint32_t len) { struct ssh_cipher_struct *crypto = NULL; HMACCTX ctx = NULL; char *out = NULL; unsigned int finallen; uint32_t seq; enum ssh_hmac_e type; assert(len); assert(len); if (!session->current_crypto) { return NULL; /* nothing to do here */ } if(len % session->current_crypto->in_cipher->blocksize != 0){ ssh_set_error(session, SSH_FATAL, "Cryptographic functions must be set on at least one blocksize (received %d)",len); return NULL; } out = malloc(len); if (out == NULL) { return NULL; } type = session->current_crypto->out_hmac; seq = ntohl(session->send_seq); crypto = session->current_crypto->out_cipher; if (crypto->set_encrypt_key(crypto, session->current_crypto->encryptkey, session->current_crypto->encryptIV) < 0) { SAFE_FREE(out); return NULL; } if (session->version == 2) { ctx = hmac_init(session->current_crypto->encryptMAC, hmac_digest_len(type), type); if (ctx == NULL) { SAFE_FREE(out); return NULL; } hmac_update(ctx,(unsigned char *)&seq,sizeof(uint32_t)); hmac_update(ctx,data,len); hmac_final(ctx,session->current_crypto->hmacbuf,&finallen); #ifdef DEBUG_CRYPTO ssh_print_hexa("mac: ",data,hmac_digest_len(type)); if (finallen != hmac_digest_len(type)) { printf("Final len is %d\n",finallen); } ssh_print_hexa("Packet hmac", session->current_crypto->hmacbuf, hmac_digest_len(type)); #endif } crypto->encrypt(crypto, data, out, len); memcpy(data, out, len); BURN_BUFFER(out, len); SAFE_FREE(out); if (session->version == 2) { return session->current_crypto->hmacbuf; } return NULL; }