static int dcrypt_gnutls_ctx_sym_update(struct dcrypt_context_symmetric *ctx, const unsigned char *data, size_t data_len, buffer_t *result, const char **error_r) { int ec; size_t outl = gnutls_cipher_get_block_size(ctx->cipher); unsigned char buf[outl]; ec = gnutls_cipher_encrypt2(ctx->ctx, data, data_len, buf, outl); if(ec < 0) return dcrypt_gnutls_error(ec, error_r); buffer_append(result, buf, outl); return ec; }
void Cipher::Key::set(const char *cipher) { clear(); algoid = __context::map_cipher(cipher); if(algoid) { blksize = gnutls_cipher_get_block_size((CIPHER_ID)algoid); keysize = gnutls_cipher_get_key_size((CIPHER_ID)algoid); } }
/* Deciphers the ciphertext packet, and puts the result to compress_data, of compress_size. * Returns the actual compressed packet size. */ int _gnutls_ciphertext2compressed (gnutls_session_t session, opaque * compress_data, int compress_size, gnutls_datum_t ciphertext, uint8_t type, record_parameters_st * params) { uint8_t MAC[MAX_HASH_SIZE]; uint16_t c_length; uint8_t pad; int length; uint16_t blocksize; int ret, i, pad_failed = 0; opaque preamble[PREAMBLE_SIZE]; int preamble_size; int ver = gnutls_protocol_get_version (session); int hash_size = _gnutls_hash_get_algo_len (params->mac_algorithm); blocksize = gnutls_cipher_get_block_size (params->cipher_algorithm); /* actual decryption (inplace) */ switch (_gnutls_cipher_is_block (params->cipher_algorithm)) { case CIPHER_STREAM: if ((ret = _gnutls_cipher_decrypt (¶ms->read.cipher_state, ciphertext.data, ciphertext.size)) < 0) { gnutls_assert (); return ret; } length = ciphertext.size - hash_size; break; case CIPHER_BLOCK: if ((ciphertext.size < blocksize) || (ciphertext.size % blocksize != 0)) { gnutls_assert (); return GNUTLS_E_DECRYPTION_FAILED; } if ((ret = _gnutls_cipher_decrypt (¶ms->read.cipher_state, ciphertext.data, ciphertext.size)) < 0) { gnutls_assert (); return ret; } /* ignore the IV in TLS 1.1. */ if (_gnutls_version_has_explicit_iv (session->security_parameters.version)) { ciphertext.size -= blocksize; ciphertext.data += blocksize; if (ciphertext.size == 0) { gnutls_assert (); return GNUTLS_E_DECRYPTION_FAILED; } } pad = ciphertext.data[ciphertext.size - 1] + 1; /* pad */ if ((int) pad > (int) ciphertext.size - hash_size) { gnutls_assert (); _gnutls_record_log ("REC[%p]: Short record length %d > %d - %d (under attack?)\n", session, pad, ciphertext.size, hash_size); /* We do not fail here. We check below for the * the pad_failed. If zero means success. */ pad_failed = GNUTLS_E_DECRYPTION_FAILED; } length = ciphertext.size - hash_size - pad; /* Check the pading bytes (TLS 1.x) */ if (_gnutls_version_has_variable_padding (ver) && pad_failed == 0) for (i = 2; i < pad; i++) { if (ciphertext.data[ciphertext.size - i] != ciphertext.data[ciphertext.size - 1]) pad_failed = GNUTLS_E_DECRYPTION_FAILED; } break; default: gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } if (length < 0) length = 0; c_length = _gnutls_conv_uint16 ((uint16_t) length); /* Pass the type, version, length and compressed through * MAC. */ if (params->mac_algorithm != GNUTLS_MAC_NULL) { digest_hd_st td; ret = mac_init (&td, params->mac_algorithm, params->read.mac_secret.data, params->read.mac_secret.size, ver); if (ret < 0) { gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } preamble_size = make_preamble (UINT64DATA (params->read.sequence_number), type, c_length, ver, preamble); mac_hash (&td, preamble, preamble_size, ver); if (length > 0) mac_hash (&td, ciphertext.data, length, ver); mac_deinit (&td, MAC, ver); } /* This one was introduced to avoid a timing attack against the TLS * 1.0 protocol. */ if (pad_failed != 0) { gnutls_assert (); return pad_failed; } /* HMAC was not the same. */ if (memcmp (MAC, &ciphertext.data[length], hash_size) != 0) { gnutls_assert (); return GNUTLS_E_DECRYPTION_FAILED; } /* copy the decrypted stuff to compress_data. */ if (compress_size < length) { gnutls_assert (); return GNUTLS_E_DECOMPRESSION_FAILED; } memcpy (compress_data, ciphertext.data, length); return length; }
/* This is the actual encryption * Encrypts the given compressed datum, and puts the result to cipher_data, * which has cipher_size size. * return the actual encrypted data length. */ int _gnutls_compressed2ciphertext (gnutls_session_t session, opaque * cipher_data, int cipher_size, gnutls_datum_t compressed, content_type_t _type, int random_pad, record_parameters_st * params) { uint8_t MAC[MAX_HASH_SIZE]; uint16_t c_length; uint8_t pad; int length, ret; uint8_t type = _type; opaque preamble[PREAMBLE_SIZE]; int preamble_size; int hash_size = _gnutls_hash_get_algo_len (params->mac_algorithm); int blocksize = gnutls_cipher_get_block_size (params->cipher_algorithm); cipher_type_t block_algo = _gnutls_cipher_is_block (params->cipher_algorithm); opaque *data_ptr; int ver = gnutls_protocol_get_version (session); /* Initialize MAC */ c_length = _gnutls_conv_uint16 (compressed.size); if (params->mac_algorithm != GNUTLS_MAC_NULL) { /* actually when the algorithm in not the NULL one */ digest_hd_st td; ret = mac_init (&td, params->mac_algorithm, params->write.mac_secret.data, params->write.mac_secret.size, ver); if (ret < 0) { gnutls_assert (); return ret; } preamble_size = make_preamble (UINT64DATA (params->write.sequence_number), type, c_length, ver, preamble); mac_hash (&td, preamble, preamble_size, ver); mac_hash (&td, compressed.data, compressed.size, ver); mac_deinit (&td, MAC, ver); } /* Calculate the encrypted length (padding etc.) */ length = calc_enc_length (session, compressed.size, hash_size, &pad, random_pad, block_algo, blocksize); if (length < 0) { gnutls_assert (); return length; } /* copy the encrypted data to cipher_data. */ if (cipher_size < length) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; } data_ptr = cipher_data; if (block_algo == CIPHER_BLOCK && _gnutls_version_has_explicit_iv (session->security_parameters.version)) { /* copy the random IV. */ ret = _gnutls_rnd (GNUTLS_RND_NONCE, data_ptr, blocksize); if (ret < 0) { gnutls_assert (); return ret; } data_ptr += blocksize; } memcpy (data_ptr, compressed.data, compressed.size); data_ptr += compressed.size; if (hash_size > 0) { memcpy (data_ptr, MAC, hash_size); data_ptr += hash_size; } if (block_algo == CIPHER_BLOCK && pad > 0) { memset (data_ptr, pad - 1, pad); } /* Actual encryption (inplace). */ ret = _gnutls_cipher_encrypt (¶ms->write.cipher_state, cipher_data, length); if (ret < 0) { gnutls_assert (); return ret; } return length; }
gchar *password_decrypt_gnutls(const gchar *password, const gchar *decryption_passphrase) { gchar **tokens, *tmp; gnutls_cipher_algorithm_t algo; gnutls_cipher_hd_t handle; gnutls_datum_t key, iv; int keylen, blocklen, ret; gsize len; unsigned char *buf; guint rounds; size_t commapos; g_return_val_if_fail(password != NULL, NULL); g_return_val_if_fail(decryption_passphrase != NULL, NULL); tokens = g_strsplit_set(password, "{}", 3); /* Parse the string, retrieving algorithm and encrypted data. * We expect "{algorithm,rounds}base64encodedciphertext". */ if (tokens[0] == NULL || strlen(tokens[0]) != 0 || tokens[1] == NULL || strlen(tokens[1]) == 0 || tokens[2] == NULL || strlen(tokens[2]) == 0) { debug_print("Garbled password string.\n"); g_strfreev(tokens); return NULL; } commapos = strcspn(tokens[1], ","); if (commapos == strlen(tokens[1]) || commapos == 0) { debug_print("Garbled algorithm substring.\n"); g_strfreev(tokens); return NULL; } buf = g_strndup(tokens[1], commapos); if ((algo = gnutls_cipher_get_id(buf)) == GNUTLS_CIPHER_UNKNOWN) { debug_print("Password string has unknown algorithm: '%s'\n", buf); g_free(buf); g_strfreev(tokens); return NULL; } g_free(buf); if ((rounds = atoi(tokens[1] + commapos + 1)) <= 0) { debug_print("Invalid number of rounds: %d\n", rounds); g_strfreev(tokens); return NULL; } /* ivlen = gnutls_cipher_get_iv_size(algo); */ keylen = gnutls_cipher_get_key_size(algo); blocklen = gnutls_cipher_get_block_size(algo); /* digestlen = gnutls_hash_get_len(digest); */ /* Take the passphrase and compute a key derivation of suitable * length to be used as encryption key for our block cipher. */ key.data = _make_key_deriv(decryption_passphrase, rounds, keylen); key.size = keylen; /* Prepare random IV for cipher */ iv.data = malloc(IVLEN); iv.size = IVLEN; if (!get_random_bytes(iv.data, IVLEN)) { g_free(key.data); g_free(iv.data); g_strfreev(tokens); return NULL; } /* Prepare encrypted password string for decryption. */ tmp = g_base64_decode(tokens[2], &len); g_strfreev(tokens); /* Initialize the decryption */ ret = gnutls_cipher_init(&handle, algo, &key, &iv); if (ret < 0) { debug_print("Cipher init failed: %s\n", gnutls_strerror(ret)); g_free(key.data); g_free(iv.data); return NULL; } buf = malloc(BUFSIZE + blocklen); memset(buf, 0, BUFSIZE + blocklen); ret = gnutls_cipher_decrypt2(handle, tmp, len, buf, BUFSIZE + blocklen); if (ret < 0) { debug_print("Decryption failed: %s\n", gnutls_strerror(ret)); g_free(key.data); g_free(iv.data); g_free(buf); gnutls_cipher_deinit(handle); return NULL; } /* Cleanup */ gnutls_cipher_deinit(handle); g_free(key.data); g_free(iv.data); tmp = g_strndup(buf + blocklen, MIN(strlen(buf + blocklen), BUFSIZE)); g_free(buf); return tmp; }
gchar *password_encrypt_gnutls(const gchar *password, const gchar *encryption_passphrase) { /* Another, slightly inferior combination is AES-128-CBC + SHA-256. * Any block cipher in CBC mode with keysize N and a hash algo with * digest length 2*N would do. */ gnutls_cipher_algorithm_t algo = GNUTLS_CIPHER_AES_256_CBC; gnutls_cipher_hd_t handle; gnutls_datum_t key, iv; int keylen, blocklen, ret; unsigned char *buf, *encbuf, *base, *output; guint rounds = prefs_common_get_prefs()->master_passphrase_pbkdf2_rounds; g_return_val_if_fail(password != NULL, NULL); g_return_val_if_fail(encryption_passphrase != NULL, NULL); /* ivlen = gnutls_cipher_get_iv_size(algo);*/ keylen = gnutls_cipher_get_key_size(algo); blocklen = gnutls_cipher_get_block_size(algo); /* digestlen = gnutls_hash_get_len(digest); */ /* Take the passphrase and compute a key derivation of suitable * length to be used as encryption key for our block cipher. */ key.data = _make_key_deriv(encryption_passphrase, rounds, keylen); key.size = keylen; /* Prepare random IV for cipher */ iv.data = malloc(IVLEN); iv.size = IVLEN; if (!get_random_bytes(iv.data, IVLEN)) { g_free(key.data); g_free(iv.data); return NULL; } /* Initialize the encryption */ ret = gnutls_cipher_init(&handle, algo, &key, &iv); if (ret < 0) { g_free(key.data); g_free(iv.data); return NULL; } /* Fill buf with one block of random data, our password, pad the * rest with zero bytes. */ buf = malloc(BUFSIZE + blocklen); memset(buf, 0, BUFSIZE); if (!get_random_bytes(buf, blocklen)) { g_free(buf); g_free(key.data); g_free(iv.data); gnutls_cipher_deinit(handle); return NULL; } memcpy(buf + blocklen, password, strlen(password)); /* Encrypt into encbuf */ encbuf = malloc(BUFSIZE + blocklen); memset(encbuf, 0, BUFSIZE + blocklen); ret = gnutls_cipher_encrypt2(handle, buf, BUFSIZE + blocklen, encbuf, BUFSIZE + blocklen); if (ret < 0) { g_free(key.data); g_free(iv.data); g_free(buf); g_free(encbuf); gnutls_cipher_deinit(handle); return NULL; } /* Cleanup */ gnutls_cipher_deinit(handle); g_free(key.data); g_free(iv.data); g_free(buf); /* And finally prepare the resulting string: * "{algorithm,rounds}base64encodedciphertext" */ base = g_base64_encode(encbuf, BUFSIZE); g_free(encbuf); output = g_strdup_printf("{%s,%d}%s", gnutls_cipher_get_name(algo), rounds, base); g_free(base); return output; }
/* * Helper function converts a data blob to a gnutls_datum_t. * Note that this does not copy the data. * So the returned value should be treated as read only. * And that changes to the length of the underlying DATA_BLOB * will not be reflected in the returned object. * */ static const gnutls_datum_t convert_from_data_blob(DATA_BLOB blob) { const gnutls_datum_t datum = { .size = blob.length, .data = blob.data, }; return datum; } /* * @brief Get the gnutls algorithm needed to decrypt the EncryptedSecret * * @param ldb ldb context, to allow logging. * @param es the encrypted secret * * @return The gnutls algoritm number, or 0 if there is no match. * */ static int gnutls_get_algorithm(struct ldb_context *ldb, struct EncryptedSecret *es) { switch (es->header.algorithm) { case ENC_SECRET_AES_128_AEAD: return GNUTLS_CIPHER_AES_128_GCM; default: ldb_asprintf_errstring(ldb, "Unsupported encryption algorithm %d\n", es->header.algorithm); return 0; } } /* * * @param err Pointer to an error code, set to: * LDB_SUCESS If the value was successfully encrypted * LDB_ERR_OPERATIONS_ERROR If there was an error. * * @param ctx Talloc memory context the will own the memory allocated * @param ldb ldb context, to allow logging. * @param val The ldb value to encrypt, not altered or freed * @param data The context data for this module. * * @return The encrypted ldb_val, or data_blob_null if there was an error. */ static struct ldb_val gnutls_encrypt_aead(int *err, TALLOC_CTX *ctx, struct ldb_context *ldb, const struct ldb_val val, const struct es_data *data) { struct EncryptedSecret *es = NULL; struct ldb_val enc = data_blob_null; DATA_BLOB pt = data_blob_null; gnutls_aead_cipher_hd_t cipher_hnd; int rc; TALLOC_CTX *frame = talloc_stackframe(); es = makeEncryptedSecret(ldb, frame); if (es == NULL) { goto error_exit; } pt = makePlainText(frame, ldb, val); if (pt.length == 0) { goto error_exit; } /* * Set the encryption key and initialize the encryption handle. */ { const size_t key_size = gnutls_cipher_get_key_size( data->encryption_algorithm); gnutls_datum_t cipher_key; DATA_BLOB key_blob = get_key(data); if (key_blob.length != key_size) { ldb_asprintf_errstring(ldb, "Invalid EncryptedSecrets key " "size, expected %zu bytes and " "it is %zu bytes\n", key_size, key_blob.length); goto error_exit; } cipher_key = convert_from_data_blob(key_blob); rc = gnutls_aead_cipher_init(&cipher_hnd, data->encryption_algorithm, &cipher_key); if (rc !=0) { ldb_asprintf_errstring(ldb, "gnutls_aead_cipher_init failed " "%s - %s\n", gnutls_strerror_name(rc), gnutls_strerror(rc)); goto error_exit; } } /* * Set the initialisation vector */ { unsigned iv_size = gnutls_cipher_get_iv_size( data->encryption_algorithm); uint8_t *iv; iv = talloc_zero_size(frame, iv_size); if (iv == NULL) { ldb_set_errstring(ldb, "Out of memory allocating IV\n"); goto error_exit_handle; } rc = gnutls_rnd(GNUTLS_RND_NONCE, iv, iv_size); if (rc !=0) { ldb_asprintf_errstring(ldb, "gnutls_rnd failed %s - %s\n", gnutls_strerror_name(rc), gnutls_strerror(rc)); goto error_exit_handle; } es->iv.length = iv_size; es->iv.data = iv; } /* * Encrypt the value. */ { const unsigned block_size = gnutls_cipher_get_block_size( data->encryption_algorithm); const unsigned tag_size = gnutls_cipher_get_tag_size( data->encryption_algorithm); const size_t ed_size = round_to_block_size( block_size, sizeof(struct PlaintextSecret) + val.length); const size_t en_size = ed_size + tag_size; uint8_t *ct = talloc_zero_size(frame, en_size); size_t el = en_size; if (ct == NULL) { ldb_set_errstring(ldb, "Out of memory allocation cipher " "text\n"); goto error_exit_handle; } rc = gnutls_aead_cipher_encrypt( cipher_hnd, es->iv.data, es->iv.length, &es->header, sizeof(struct EncryptedSecretHeader), tag_size, pt.data, pt.length, ct, &el); if (rc !=0) { ldb_asprintf_errstring(ldb, "gnutls_aead_cipher_encrypt '" "failed %s - %s\n", gnutls_strerror_name(rc), gnutls_strerror(rc)); *err = LDB_ERR_OPERATIONS_ERROR; return data_blob_null; } es->encrypted.length = el; es->encrypted.data = ct; gnutls_aead_cipher_deinit(cipher_hnd); } rc = ndr_push_struct_blob(&enc, ctx, es, (ndr_push_flags_fn_t) ndr_push_EncryptedSecret); if (!NDR_ERR_CODE_IS_SUCCESS(rc)) { ldb_set_errstring(ldb, "Unable to ndr push EncryptedSecret\n"); goto error_exit; } TALLOC_FREE(frame); return enc; error_exit_handle: gnutls_aead_cipher_deinit(cipher_hnd); error_exit: *err = LDB_ERR_OPERATIONS_ERROR; TALLOC_FREE(frame); return data_blob_null; } /* * @brief Decrypt data encrypted using an aead algorithm. * * Decrypt the data in ed and insert it into ev. The data was encrypted * with one of the gnutls aead compatable algorithms. * * @param err Pointer to an error code, set to: * LDB_SUCESS If the value was successfully decrypted * LDB_ERR_OPERATIONS_ERROR If there was an error. * * @param ctx The talloc context that will own the PlaintextSecret * @param ldb ldb context, to allow logging. * @param ev The value to be updated with the decrypted data. * @param ed The data to decrypt. * @param data The context data for this module. * * @return ev is updated with the unencrypted data. */ static void gnutls_decrypt_aead(int *err, TALLOC_CTX *ctx, struct ldb_context *ldb, struct EncryptedSecret *es, struct PlaintextSecret *ps, const struct es_data *data) { gnutls_aead_cipher_hd_t cipher_hnd; DATA_BLOB pt = data_blob_null; const unsigned tag_size = gnutls_cipher_get_tag_size(es->header.algorithm); int rc; /* * Get the encryption key and initialise the encryption handle */ { gnutls_datum_t cipher_key; DATA_BLOB key_blob; const int algorithm = gnutls_get_algorithm(ldb, es); const size_t key_size = gnutls_cipher_get_key_size(algorithm); key_blob = get_key(data); if (algorithm == 0) { goto error_exit; } if (key_blob.length != key_size) { ldb_asprintf_errstring(ldb, "Invalid EncryptedSecrets key " "size, expected %zu bytes and " "it is %zu bytes\n", key_size, key_blob.length); goto error_exit; } cipher_key = convert_from_data_blob(key_blob); rc = gnutls_aead_cipher_init( &cipher_hnd, algorithm, &cipher_key); if (rc != 0) { ldb_asprintf_errstring(ldb, "gnutls_aead_cipher_init failed " "%s - %s\n", gnutls_strerror_name(rc), gnutls_strerror(rc)); goto error_exit; } } /* * Decrypt and validate the encrypted value */ pt.length = es->encrypted.length; pt.data = talloc_zero_size(ctx, es->encrypted.length); if (pt.data == NULL) { ldb_set_errstring(ldb, "Out of memory allocating plain text\n"); goto error_exit_handle; } rc = gnutls_aead_cipher_decrypt(cipher_hnd, es->iv.data, es->iv.length, &es->header, sizeof(struct EncryptedSecretHeader), tag_size, es->encrypted.data, es->encrypted.length, pt.data, &pt.length); if (rc != 0) { /* * Typically this will indicate that the data has been * corrupted i.e. the tag comparison has failed. * At the moment gnutls does not provide a separate * error code to indicate this */ ldb_asprintf_errstring(ldb, "gnutls_aead_cipher_decrypt failed " "%s - %s. Data possibly corrupted or " "altered\n", gnutls_strerror_name(rc), gnutls_strerror(rc)); goto error_exit_handle; } gnutls_aead_cipher_deinit(cipher_hnd); rc = ndr_pull_struct_blob(&pt, ctx, ps, (ndr_pull_flags_fn_t) ndr_pull_PlaintextSecret); if(!NDR_ERR_CODE_IS_SUCCESS(rc)) { ldb_asprintf_errstring(ldb, "Error(%d) unpacking decrypted data, " "data possibly corrupted or altered\n", rc); goto error_exit; } return; error_exit_handle: gnutls_aead_cipher_deinit(cipher_hnd); error_exit: *err = LDB_ERR_OPERATIONS_ERROR; return; }
static void cipher_bench (int algo, int size) { int ret; gnutls_cipher_hd_t ctx; void *_key, *_iv; gnutls_datum_t key, iv; struct timespec start, stop; double secs; double data_size = 0; double dspeed, ddata; int blocksize = gnutls_cipher_get_block_size (algo); int keysize = gnutls_cipher_get_key_size (algo); char metric[16]; _key = malloc (keysize); if (_key == NULL) return; memset (_key, 0xf0, keysize); _iv = malloc (blocksize); if (_iv == NULL) return; memset (_iv, 0xf0, blocksize); iv.data = _iv; iv.size = blocksize; key.data = _key; key.size = keysize; printf ("Checking %s (%dkb payload)... ", gnutls_cipher_get_name (algo), size); fflush (stdout); must_finish = 0; alarm (5); gettime (&start); ret = gnutls_cipher_init (&ctx, algo, &key, &iv); if (ret < 0) { fprintf (stderr, "error: %s\n", gnutls_strerror (ret)); goto leave; } do { gnutls_cipher_encrypt (ctx, data, size * 1024); data_size += size * 1024; } while (must_finish == 0); gnutls_cipher_deinit (ctx); gettime (&stop); secs = (stop.tv_sec * 1000 + stop.tv_nsec / (1000 * 1000) - (start.tv_sec * 1000 + start.tv_nsec / (1000 * 1000))); secs /= 1000; value2human (data_size, secs, &ddata, &dspeed, metric); printf ("Encrypted %.2f %s in %.2f secs: ", ddata, metric, secs); printf ("%.2f %s/sec\n", dspeed, metric); leave: free (_key); free (_iv); }
/** * gnutls_x509_privkey_import_openssl: * @key: The structure to store the parsed key * @data: The DER or PEM encoded key. * @password: the password to decrypt the key (if it is encrypted). * * This function will convert the given PEM encrypted to * the native gnutls_x509_privkey_t format. The * output will be stored in @key. * * The @password should be in ASCII. If the password is not provided * or wrong then %GNUTLS_E_DECRYPTION_FAILED will be returned. * * If the Certificate is PEM encoded it should have a header of * "PRIVATE KEY" and the "DEK-Info" header. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. **/ int gnutls_x509_privkey_import_openssl(gnutls_x509_privkey_t key, const gnutls_datum_t * data, const char *password) { gnutls_cipher_hd_t handle; gnutls_cipher_algorithm_t cipher = GNUTLS_CIPHER_UNKNOWN; gnutls_datum_t b64_data; gnutls_datum_t salt, enc_key; unsigned char *key_data; size_t key_data_size; const char *pem_header = (void *) data->data; const char *pem_header_start = (void *) data->data; ssize_t pem_header_size; int ret; unsigned int i, iv_size, l; pem_header_size = data->size; pem_header = memmem(pem_header, pem_header_size, "PRIVATE KEY---", 14); if (pem_header == NULL) { gnutls_assert(); return GNUTLS_E_PARSING_ERROR; } pem_header_size -= (ptrdiff_t) (pem_header - pem_header_start); pem_header = memmem(pem_header, pem_header_size, "DEK-Info: ", 10); if (pem_header == NULL) { gnutls_assert(); return GNUTLS_E_PARSING_ERROR; } pem_header_size = data->size - (ptrdiff_t) (pem_header - pem_header_start) - 10; pem_header += 10; for (i = 0; i < sizeof(pem_ciphers) / sizeof(pem_ciphers[0]); i++) { l = strlen(pem_ciphers[i].name); if (!strncmp(pem_header, pem_ciphers[i].name, l) && pem_header[l] == ',') { pem_header += l + 1; cipher = pem_ciphers[i].cipher; break; } } if (cipher == GNUTLS_CIPHER_UNKNOWN) { _gnutls_debug_log ("Unsupported PEM encryption type: %.10s\n", pem_header); gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } iv_size = gnutls_cipher_get_iv_size(cipher); salt.size = iv_size; salt.data = gnutls_malloc(salt.size); if (!salt.data) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); for (i = 0; i < salt.size * 2; i++) { unsigned char x; const char *c = &pem_header[i]; if (*c >= '0' && *c <= '9') x = (*c) - '0'; else if (*c >= 'A' && *c <= 'F') x = (*c) - 'A' + 10; else { gnutls_assert(); /* Invalid salt in encrypted PEM file */ ret = GNUTLS_E_INVALID_REQUEST; goto out_salt; } if (i & 1) salt.data[i / 2] |= x; else salt.data[i / 2] = x << 4; } pem_header += salt.size * 2; if (*pem_header != '\r' && *pem_header != '\n') { gnutls_assert(); ret = GNUTLS_E_INVALID_REQUEST; goto out_salt; } while (*pem_header == '\n' || *pem_header == '\r') pem_header++; ret = _gnutls_base64_decode((const void *) pem_header, pem_header_size, &b64_data); if (ret < 0) { gnutls_assert(); goto out_salt; } if (b64_data.size < 16) { /* Just to be sure our parsing is OK */ gnutls_assert(); ret = GNUTLS_E_PARSING_ERROR; goto out_b64; } ret = GNUTLS_E_MEMORY_ERROR; enc_key.size = gnutls_cipher_get_key_size(cipher); enc_key.data = gnutls_malloc(enc_key.size); if (!enc_key.data) { ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); goto out_b64; } key_data_size = b64_data.size; key_data = gnutls_malloc(key_data_size); if (!key_data) { ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); goto out_enc_key; } while (1) { memcpy(key_data, b64_data.data, key_data_size); ret = openssl_hash_password(password, &enc_key, &salt); if (ret < 0) { gnutls_assert(); goto out; } ret = gnutls_cipher_init(&handle, cipher, &enc_key, &salt); if (ret < 0) { gnutls_assert(); gnutls_cipher_deinit(handle); goto out; } ret = gnutls_cipher_decrypt(handle, key_data, key_data_size); gnutls_cipher_deinit(handle); if (ret < 0) { gnutls_assert(); goto out; } /* We have to strip any padding to accept it. So a bit more ASN.1 parsing for us. */ if (key_data[0] == 0x30) { gnutls_datum_t key_datum; unsigned int blocksize = gnutls_cipher_get_block_size(cipher); unsigned int keylen = key_data[1]; unsigned int ofs = 2; if (keylen & 0x80) { int lenlen = keylen & 0x7f; keylen = 0; if (lenlen > 3) { gnutls_assert(); goto fail; } while (lenlen) { keylen <<= 8; keylen |= key_data[ofs++]; lenlen--; } } keylen += ofs; /* If there appears to be more padding than required, fail */ if (key_data_size - keylen > blocksize) { gnutls_assert(); goto fail; } /* If the padding bytes aren't all equal to the amount of padding, fail */ ofs = keylen; while (ofs < key_data_size) { if (key_data[ofs] != key_data_size - keylen) { gnutls_assert(); goto fail; } ofs++; } key_datum.data = key_data; key_datum.size = keylen; ret = gnutls_x509_privkey_import(key, &key_datum, GNUTLS_X509_FMT_DER); if (ret == 0) goto out; } fail: ret = GNUTLS_E_DECRYPTION_FAILED; goto out; } out: zeroize_key(key_data, key_data_size); gnutls_free(key_data); out_enc_key: _gnutls_free_key_datum(&enc_key); out_b64: gnutls_free(b64_data.data); out_salt: gnutls_free(salt.data); return ret; }
/* Deciphers the ciphertext packet, and puts the result to compress_data, of compress_size. * Returns the actual compressed packet size. */ static int ciphertext_to_compressed (gnutls_session_t session, gnutls_datum_t *ciphertext, opaque * compress_data, int compress_size, uint8_t type, record_parameters_st * params, uint64* sequence) { uint8_t tag[MAX_HASH_SIZE]; uint8_t pad; int length, length_to_decrypt; uint16_t blocksize; int ret, i, pad_failed = 0; opaque preamble[MAX_PREAMBLE_SIZE]; int preamble_size; int ver = gnutls_protocol_get_version (session); int tag_size = _gnutls_auth_cipher_tag_len (¶ms->read.cipher_state); int explicit_iv = _gnutls_version_has_explicit_iv (session->security_parameters.version); blocksize = gnutls_cipher_get_block_size (params->cipher_algorithm); /* actual decryption (inplace) */ switch (_gnutls_cipher_is_block (params->cipher_algorithm)) { case CIPHER_STREAM: /* The way AEAD ciphers are defined in RFC5246, it allows * only stream ciphers. */ if (explicit_iv && _gnutls_auth_cipher_is_aead(¶ms->read.cipher_state)) { uint8_t nonce[blocksize]; /* Values in AEAD are pretty fixed in TLS 1.2 for 128-bit block */ if (params->read.IV.data == NULL || params->read.IV.size != 4) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); if (ciphertext->size < tag_size+AEAD_EXPLICIT_DATA_SIZE) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); memcpy(nonce, params->read.IV.data, AEAD_IMPLICIT_DATA_SIZE); memcpy(&nonce[AEAD_IMPLICIT_DATA_SIZE], ciphertext->data, AEAD_EXPLICIT_DATA_SIZE); _gnutls_auth_cipher_setiv(¶ms->read.cipher_state, nonce, AEAD_EXPLICIT_DATA_SIZE+AEAD_IMPLICIT_DATA_SIZE); ciphertext->data += AEAD_EXPLICIT_DATA_SIZE; ciphertext->size -= AEAD_EXPLICIT_DATA_SIZE; length_to_decrypt = ciphertext->size - tag_size; } else { if (ciphertext->size < tag_size) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); length_to_decrypt = ciphertext->size; } length = ciphertext->size - tag_size; /* Pass the type, version, length and compressed through * MAC. */ preamble_size = make_preamble (UINT64DATA(*sequence), type, length, ver, preamble); _gnutls_auth_cipher_add_auth (¶ms->read.cipher_state, preamble, preamble_size); if ((ret = _gnutls_auth_cipher_decrypt (¶ms->read.cipher_state, ciphertext->data, length_to_decrypt)) < 0) return gnutls_assert_val(ret); break; case CIPHER_BLOCK: if (ciphertext->size < MAX(blocksize, tag_size) || (ciphertext->size % blocksize != 0)) return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); /* ignore the IV in TLS 1.1+ */ if (explicit_iv) { _gnutls_auth_cipher_setiv(¶ms->read.cipher_state, ciphertext->data, blocksize); ciphertext->size -= blocksize; ciphertext->data += blocksize; if (ciphertext->size == 0) { gnutls_assert (); return GNUTLS_E_DECRYPTION_FAILED; } } /* we don't use the auth_cipher interface here, since * TLS with block ciphers is impossible to be used under such * an API. (the length of plaintext is required to calculate * auth_data, but it is not available before decryption). */ if ((ret = _gnutls_cipher_decrypt (¶ms->read.cipher_state.cipher, ciphertext->data, ciphertext->size)) < 0) return gnutls_assert_val(ret); pad = ciphertext->data[ciphertext->size - 1] + 1; /* pad */ if ((int) pad > (int) ciphertext->size - tag_size) { gnutls_assert (); _gnutls_record_log ("REC[%p]: Short record length %d > %d - %d (under attack?)\n", session, pad, ciphertext->size, tag_size); /* We do not fail here. We check below for the * the pad_failed. If zero means success. */ pad_failed = GNUTLS_E_DECRYPTION_FAILED; pad %= blocksize; } length = ciphertext->size - tag_size - pad; /* Check the pading bytes (TLS 1.x) */ if (ver != GNUTLS_SSL3) for (i = 2; i < pad; i++) { if (ciphertext->data[ciphertext->size - i] != ciphertext->data[ciphertext->size - 1]) pad_failed = GNUTLS_E_DECRYPTION_FAILED; } if (length < 0) length = 0; /* Pass the type, version, length and compressed through * MAC. */ preamble_size = make_preamble (UINT64DATA(*sequence), type, length, ver, preamble); _gnutls_auth_cipher_add_auth (¶ms->read.cipher_state, preamble, preamble_size); _gnutls_auth_cipher_add_auth (¶ms->read.cipher_state, ciphertext->data, length); break; default: return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); } ret = _gnutls_auth_cipher_tag(¶ms->read.cipher_state, tag, tag_size); if (ret < 0) return gnutls_assert_val(ret); /* This one was introduced to avoid a timing attack against the TLS * 1.0 protocol. */ /* HMAC was not the same. */ if (memcmp (tag, &ciphertext->data[length], tag_size) != 0 || pad_failed != 0) return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED); /* copy the decrypted stuff to compress_data. */ if (compress_size < length) return gnutls_assert_val(GNUTLS_E_DECOMPRESSION_FAILED); if (compress_data != ciphertext->data) memcpy (compress_data, ciphertext->data, length); return length; }
/* This is the actual encryption * Encrypts the given compressed datum, and puts the result to cipher_data, * which has cipher_size size. * return the actual encrypted data length. */ static int compressed_to_ciphertext (gnutls_session_t session, opaque * cipher_data, int cipher_size, gnutls_datum_t *compressed, content_type_t type, record_parameters_st * params) { uint8_t * tag_ptr = NULL; uint8_t pad; int length, length_to_encrypt, ret; opaque preamble[MAX_PREAMBLE_SIZE]; int preamble_size; int tag_size = _gnutls_auth_cipher_tag_len (¶ms->write.cipher_state); int blocksize = gnutls_cipher_get_block_size (params->cipher_algorithm); unsigned block_algo = _gnutls_cipher_is_block (params->cipher_algorithm); opaque *data_ptr; int ver = gnutls_protocol_get_version (session); int explicit_iv = _gnutls_version_has_explicit_iv (session->security_parameters.version); int auth_cipher = _gnutls_auth_cipher_is_aead(¶ms->write.cipher_state); int random_pad; /* We don't use long padding if requested or if we are in DTLS. */ if (session->internals.priorities.no_padding == 0 && (!IS_DTLS(session))) random_pad = 1; else random_pad = 0; _gnutls_hard_log("ENC[%p]: cipher: %s, MAC: %s, Epoch: %u\n", session, gnutls_cipher_get_name(params->cipher_algorithm), gnutls_mac_get_name(params->mac_algorithm), (unsigned int)params->epoch); preamble_size = make_preamble (UINT64DATA (params->write.sequence_number), type, compressed->size, ver, preamble); /* Calculate the encrypted length (padding etc.) */ length_to_encrypt = length = calc_enc_length (session, compressed->size, tag_size, &pad, random_pad, block_algo, auth_cipher, blocksize); if (length < 0) { return gnutls_assert_val(length); } /* copy the encrypted data to cipher_data. */ if (cipher_size < length) { return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); } data_ptr = cipher_data; if (explicit_iv) { if (block_algo == CIPHER_BLOCK) { /* copy the random IV. */ ret = _gnutls_rnd (GNUTLS_RND_NONCE, data_ptr, blocksize); if (ret < 0) return gnutls_assert_val(ret); _gnutls_auth_cipher_setiv(¶ms->write.cipher_state, data_ptr, blocksize); data_ptr += blocksize; cipher_data += blocksize; length_to_encrypt -= blocksize; } else if (auth_cipher) { uint8_t nonce[blocksize]; /* Values in AEAD are pretty fixed in TLS 1.2 for 128-bit block */ if (params->write.IV.data == NULL || params->write.IV.size != AEAD_IMPLICIT_DATA_SIZE) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); /* Instead of generating a new nonce on every packet, we use the * write.sequence_number (It is a MAY on RFC 5288). */ memcpy(nonce, params->write.IV.data, params->write.IV.size); memcpy(&nonce[AEAD_IMPLICIT_DATA_SIZE], UINT64DATA(params->write.sequence_number), 8); _gnutls_auth_cipher_setiv(¶ms->write.cipher_state, nonce, AEAD_IMPLICIT_DATA_SIZE+AEAD_EXPLICIT_DATA_SIZE); /* copy the explicit part */ memcpy(data_ptr, &nonce[AEAD_IMPLICIT_DATA_SIZE], AEAD_EXPLICIT_DATA_SIZE); data_ptr += AEAD_EXPLICIT_DATA_SIZE; cipher_data += AEAD_EXPLICIT_DATA_SIZE; /* In AEAD ciphers we don't encrypt the tag */ length_to_encrypt -= AEAD_EXPLICIT_DATA_SIZE + tag_size; } } else { /* AEAD ciphers have an explicit IV. Shouldn't be used otherwise. */ if (auth_cipher) return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); } memcpy (data_ptr, compressed->data, compressed->size); data_ptr += compressed->size; if (tag_size > 0) { tag_ptr = data_ptr; data_ptr += tag_size; } if (block_algo == CIPHER_BLOCK && pad > 0) { memset (data_ptr, pad - 1, pad); } /* add the authenticate data */ _gnutls_auth_cipher_add_auth(¶ms->write.cipher_state, preamble, preamble_size); /* Actual encryption (inplace). */ ret = _gnutls_auth_cipher_encrypt_tag (¶ms->write.cipher_state, cipher_data, length_to_encrypt, tag_ptr, tag_size, compressed->size); if (ret < 0) return gnutls_assert_val(ret); return length; }
static int dcrypt_gnutls_ctx_sym_get_block_size(struct dcrypt_context_symmetric *ctx) { return gnutls_cipher_get_block_size(ctx->cipher); }