/* * Copyright (C) 2003-2014 Free Software Foundation, Inc. * Copyright (C) 2014 Red Hat * Copyright (C) 2014 Nikos Mavrogiannopoulos * * Author: Nikos Mavrogiannopoulos * * This file is part of GnuTLS. * * The GnuTLS is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * */ #include #include #include #include #include #include #include #include "x509_int.h" #include #include #include #include static int _decode_pkcs8_ecc_key(ASN1_TYPE pkcs8_asn, gnutls_x509_privkey_t pkey); static int pkcs8_key_info(const gnutls_datum_t * raw_key, const struct pbes2_schema_st **p, struct pbkdf2_params *kdf_params, char **oid); #define PBES2_OID "1.2.840.113549.1.5.13" #define PBKDF2_OID "1.2.840.113549.1.5.12" #define DES_EDE3_CBC_OID "1.2.840.113549.3.7" #define AES_128_CBC_OID "2.16.840.1.101.3.4.1.2" #define AES_192_CBC_OID "2.16.840.1.101.3.4.1.22" #define AES_256_CBC_OID "2.16.840.1.101.3.4.1.42" #define DES_CBC_OID "1.3.14.3.2.7" /* oid_pbeWithSHAAnd3_KeyTripleDES_CBC */ #define PKCS12_PBE_3DES_SHA1_OID "1.2.840.113549.1.12.1.3" #define PKCS12_PBE_ARCFOUR_SHA1_OID "1.2.840.113549.1.12.1.1" #define PKCS12_PBE_RC2_40_SHA1_OID "1.2.840.113549.1.12.1.6" struct pbe_enc_params { gnutls_cipher_algorithm_t cipher; uint8_t iv[MAX_CIPHER_BLOCK_SIZE]; int iv_size; }; static int generate_key(schema_id schema, const char *password, struct pbkdf2_params *kdf_params, struct pbe_enc_params *enc_params, gnutls_datum_t * key); static int read_pbkdf2_params(ASN1_TYPE pbes2_asn, const gnutls_datum_t * der, struct pbkdf2_params *params); static int read_pbe_enc_params(ASN1_TYPE pbes2_asn, const gnutls_datum_t * der, struct pbe_enc_params *params); static int decrypt_data(schema_id, ASN1_TYPE pkcs8_asn, const char *root, const char *password, const struct pbkdf2_params *kdf_params, const struct pbe_enc_params *enc_params, gnutls_datum_t * decrypted_data); static int decode_private_key_info(const gnutls_datum_t * der, gnutls_x509_privkey_t pkey); static int write_schema_params(schema_id schema, ASN1_TYPE pkcs8_asn, const char *where, const struct pbkdf2_params *kdf_params, const struct pbe_enc_params *enc_params); static int encrypt_data(const gnutls_datum_t * plain, const struct pbe_enc_params *enc_params, gnutls_datum_t * key, gnutls_datum_t * encrypted); static int read_pkcs12_kdf_params(ASN1_TYPE pbes2_asn, struct pbkdf2_params *params); static int write_pkcs12_kdf_params(ASN1_TYPE pbes2_asn, const struct pbkdf2_params *params); #define PEM_PKCS8 "ENCRYPTED PRIVATE KEY" #define PEM_UNENCRYPTED_PKCS8 "PRIVATE KEY" /* Returns a negative error code if the encryption schema in * the OID is not supported. The schema ID is returned. */ /* Encodes a private key to the raw format PKCS #8 needs. * For RSA it is a PKCS #1 DER private key and for DSA it is * an ASN.1 INTEGER of the x value. */ inline static int _encode_privkey(gnutls_x509_privkey_t pkey, gnutls_datum_t * raw) { int ret; ASN1_TYPE spk = ASN1_TYPE_EMPTY; switch (pkey->pk_algorithm) { case GNUTLS_PK_RSA: case GNUTLS_PK_EC: ret = gnutls_x509_privkey_export2(pkey, GNUTLS_X509_FMT_DER, raw); if (ret < 0) { gnutls_assert(); goto error; } break; case GNUTLS_PK_DSA: /* DSAPublicKey == INTEGER */ if ((ret = asn1_create_element (_gnutls_get_gnutls_asn(), "GNUTLS.DSAPublicKey", &spk)) != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(ret); } ret = _gnutls_x509_write_int(spk, "", pkey->params.params[4], 1); if (ret < 0) { gnutls_assert(); goto error; } ret = _gnutls_x509_der_encode(spk, "", raw, 0); if (ret < 0) { gnutls_assert(); goto error; } asn1_delete_structure2(&spk, ASN1_DELETE_FLAG_ZEROIZE); break; default: gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } return 0; error: asn1_delete_structure2(&spk, ASN1_DELETE_FLAG_ZEROIZE); asn1_delete_structure(&spk); return ret; } /* * Encodes a PKCS #1 private key to a PKCS #8 private key * info. The output will be allocated and stored into der. Also * the ASN1_TYPE of private key info will be returned. */ static int encode_to_private_key_info(gnutls_x509_privkey_t pkey, gnutls_datum_t * der, ASN1_TYPE * pkey_info) { int result, len; uint8_t null = 0; const char *oid; gnutls_datum_t algo_params = { NULL, 0 }; gnutls_datum_t algo_privkey = { NULL, 0 }; oid = _gnutls_x509_pk_to_oid(pkey->pk_algorithm); if (oid == NULL) { gnutls_assert(); return GNUTLS_E_UNIMPLEMENTED_FEATURE; } result = _gnutls_x509_write_pubkey_params(pkey->pk_algorithm, &pkey->params, &algo_params); if (result < 0) { gnutls_assert(); return result; } if ((result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.pkcs-8-PrivateKeyInfo", pkey_info)) != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } /* Write the version. */ result = asn1_write_value(*pkey_info, "version", &null, 1); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } /* write the privateKeyAlgorithm * fields. (OID+NULL data) */ result = asn1_write_value(*pkey_info, "privateKeyAlgorithm.algorithm", oid, 1); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } result = asn1_write_value(*pkey_info, "privateKeyAlgorithm.parameters", algo_params.data, algo_params.size); _gnutls_free_key_datum(&algo_params); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } /* Write the raw private key */ result = _encode_privkey(pkey, &algo_privkey); if (result < 0) { gnutls_assert(); goto error; } result = asn1_write_value(*pkey_info, "privateKey", algo_privkey.data, algo_privkey.size); _gnutls_free_key_datum(&algo_privkey); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } /* Append an empty Attributes field. */ result = asn1_write_value(*pkey_info, "attributes", NULL, 0); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } /* DER Encode the generated private key info. */ len = 0; result = asn1_der_coding(*pkey_info, "", NULL, &len, NULL); if (result != ASN1_MEM_ERROR) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } /* allocate data for the der */ der->size = len; der->data = gnutls_malloc(len); if (der->data == NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } result = asn1_der_coding(*pkey_info, "", der->data, &len, NULL); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } return 0; error: asn1_delete_structure2(pkey_info, ASN1_DELETE_FLAG_ZEROIZE); _gnutls_free_datum(&algo_params); _gnutls_free_key_datum(&algo_privkey); return result; } static const struct pbes2_schema_st avail_pbes2_schemas[] = { {PBES2_3DES, "PBES2-3DES-CBC", GNUTLS_PKCS_PBES2_3DES, GNUTLS_CIPHER_3DES_CBC, 1, DES_EDE3_CBC_OID, "PKIX1.pkcs-5-des-EDE3-CBC-params"}, {PBES2_DES, "PBES2-DES-CBC", GNUTLS_PKCS_PBES2_DES, GNUTLS_CIPHER_DES_CBC, 1, DES_CBC_OID, "PKIX1.pkcs-5-des-CBC-params"}, {PBES2_AES_128, "PBES2-AES128-CBC", GNUTLS_PKCS_PBES2_AES_128, GNUTLS_CIPHER_AES_128_CBC, 1, AES_128_CBC_OID, "PKIX1.pkcs-5-aes128-CBC-params"}, {PBES2_AES_192, "PBES2-AES192-CBC", GNUTLS_PKCS_PBES2_AES_192, GNUTLS_CIPHER_AES_192_CBC, 1, AES_192_CBC_OID, "PKIX1.pkcs-5-aes192-CBC-params"}, {PBES2_AES_256, "PBES2-AES256-CBC", GNUTLS_PKCS_PBES2_AES_256, GNUTLS_CIPHER_AES_256_CBC, 1, AES_256_CBC_OID, "PKIX1.pkcs-5-aes256-CBC-params"}, {PKCS12_ARCFOUR_SHA1, "PKCS12-ARCFOUR-SHA1", GNUTLS_PKCS_PKCS12_ARCFOUR, GNUTLS_CIPHER_ARCFOUR, 0, PKCS12_PBE_ARCFOUR_SHA1_OID, NULL}, {PKCS12_RC2_40_SHA1, "PKCS12-RC2-40-SHA1", GNUTLS_PKCS_PKCS12_RC2_40, GNUTLS_CIPHER_RC2_40_CBC, 0, PKCS12_PBE_RC2_40_SHA1_OID, NULL}, {PKCS12_3DES_SHA1, "PKCS12-3DES-SHA1", GNUTLS_PKCS_PKCS12_3DES, GNUTLS_CIPHER_3DES_CBC, 0, PKCS12_PBE_3DES_SHA1_OID, NULL}, {0, 0, 0} }; #define PBES2_SCHEMA_LOOP(b) { \ const struct pbes2_schema_st * _p; \ for (_p=avail_pbes2_schemas;_p->schema != 0;_p++) { b; } \ } #define PBES2_SCHEMA_FIND_FROM_FLAGS(fl, what) \ PBES2_SCHEMA_LOOP( if (_p->flag == fl) { what; } ) int _gnutls_pkcs_flags_to_schema(unsigned int flags) { PBES2_SCHEMA_FIND_FROM_FLAGS(flags, return _p->schema;); gnutls_assert(); _gnutls_debug_log ("Selecting default encryption PKCS12_3DES_SHA1 (flags: %u).\n", flags); return PKCS12_3DES_SHA1; } /** * gnutls_pkcs_schema_get_name: * @schema: Holds the PKCS #12 or PBES2 schema (%gnutls_pkcs_encrypt_flags_t) * * This function will return a human readable description of the * PKCS12 or PBES2 schema. * * Returns: a constrant string or %NULL on error. * * Since: 3.4.0 */ const char *gnutls_pkcs_schema_get_name(unsigned int schema) { PBES2_SCHEMA_FIND_FROM_FLAGS(schema, return _p->name;); return NULL; } /** * gnutls_pkcs_schema_get_oid: * @schema: Holds the PKCS #12 or PBES2 schema (%gnutls_pkcs_encrypt_flags_t) * * This function will return the object identifier of the * PKCS12 or PBES2 schema. * * Returns: a constrant string or %NULL on error. * * Since: 3.4.0 */ const char *gnutls_pkcs_schema_get_oid(unsigned int schema) { PBES2_SCHEMA_FIND_FROM_FLAGS(schema, return _p->oid;); return NULL; } static const struct pbes2_schema_st *cipher_to_pbes2_schema(unsigned cipher) { PBES2_SCHEMA_LOOP( if (_p->cipher == cipher && _p->pbes2 != 0) { return _p; }); gnutls_assert(); return NULL; } /* returns the OID corresponding to given schema */ static int pkcs12_schema_to_oid(schema_id schema, const char **str_oid) { PBES2_SCHEMA_LOOP( if (_p->schema == schema) { if (_p->pbes2 != 0) { *str_oid = PBES2_OID; return 0; } else { *str_oid = _p->oid; return 0; } } ); gnutls_assert(); return GNUTLS_E_INTERNAL_ERROR; } static int check_pkcs12_schema(const char *oid) { if (strcmp(oid, PBES2_OID) == 0) return PBES2_GENERIC; /* ok */ PBES2_SCHEMA_LOOP(if (_p->pbes2 == 0 && strcmp(oid, _p->oid) == 0) {return _p->schema;}); _gnutls_debug_log ("PKCS #12 encryption schema OID '%s' is unsupported.\n", oid); return GNUTLS_E_UNKNOWN_CIPHER_TYPE; } static const struct pbes2_schema_st *pbes2_schema_get(schema_id schema) { PBES2_SCHEMA_LOOP(if (schema == _p->schema) return _p;); gnutls_assert(); return NULL; } /* Converts an OID to a gnutls cipher type. */ static int pbes2_oid_to_cipher(const char *oid, gnutls_cipher_algorithm_t * algo) { *algo = 0; PBES2_SCHEMA_LOOP(if (_p->pbes2 != 0 && strcmp(_p->oid, oid) == 0) { *algo = _p->cipher; return 0; } ); _gnutls_debug_log("PKCS #8 encryption OID '%s' is unsupported.\n", oid); return GNUTLS_E_UNKNOWN_CIPHER_TYPE; } /* Converts a PKCS #8 private key info to * a PKCS #8 EncryptedPrivateKeyInfo. */ static int encode_to_pkcs8_key(schema_id schema, const gnutls_datum_t * der_key, const char *password, ASN1_TYPE * out) { int result; gnutls_datum_t key = { NULL, 0 }; gnutls_datum_t tmp = { NULL, 0 }; ASN1_TYPE pkcs8_asn = ASN1_TYPE_EMPTY; struct pbkdf2_params kdf_params; struct pbe_enc_params enc_params; const char *str_oid; if ((result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.pkcs-8-EncryptedPrivateKeyInfo", &pkcs8_asn)) != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } /* Write the encryption schema OID */ result = pkcs12_schema_to_oid(schema, &str_oid); if (result < 0) { gnutls_assert(); return result; } result = asn1_write_value(pkcs8_asn, "encryptionAlgorithm.algorithm", str_oid, 1); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } /* Generate a symmetric key. */ result = generate_key(schema, password, &kdf_params, &enc_params, &key); if (result < 0) { gnutls_assert(); goto error; } result = write_schema_params(schema, pkcs8_asn, "encryptionAlgorithm.parameters", &kdf_params, &enc_params); if (result < 0) { gnutls_assert(); goto error; } /* Parameters have been encoded. Now * encrypt the Data. */ result = encrypt_data(der_key, &enc_params, &key, &tmp); if (result < 0) { gnutls_assert(); goto error; } /* write the encrypted data. */ result = asn1_write_value(pkcs8_asn, "encryptedData", tmp.data, tmp.size); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } _gnutls_free_datum(&tmp); _gnutls_free_key_datum(&key); *out = pkcs8_asn; return 0; error: _gnutls_free_key_datum(&key); _gnutls_free_datum(&tmp); asn1_delete_structure2(&pkcs8_asn, ASN1_DELETE_FLAG_ZEROIZE); return result; } /** * gnutls_x509_privkey_export_pkcs8: * @key: Holds the key * @format: the format of output params. One of PEM or DER. * @password: the password that will be used to encrypt the key. * @flags: an ORed sequence of gnutls_pkcs_encrypt_flags_t * @output_data: will contain a private key PEM or DER encoded * @output_data_size: holds the size of output_data (and will be * replaced by the actual size of parameters) * * This function will export the private key to a PKCS8 structure. * Both RSA and DSA keys can be exported. For DSA keys we use * PKCS #11 definitions. If the flags do not specify the encryption * cipher, then the default 3DES (PBES2) will be used. * * The @password can be either ASCII or UTF-8 in the default PBES2 * encryption schemas, or ASCII for the PKCS12 schemas. * * If the buffer provided is not long enough to hold the output, then * *output_data_size is updated and GNUTLS_E_SHORT_MEMORY_BUFFER will * be returned. * * If the structure is PEM encoded, it will have a header * of "BEGIN ENCRYPTED PRIVATE KEY" or "BEGIN PRIVATE KEY" if * encryption is not used. * * Returns: In case of failure a negative error code will be * returned, and 0 on success. **/ int gnutls_x509_privkey_export_pkcs8(gnutls_x509_privkey_t key, gnutls_x509_crt_fmt_t format, const char *password, unsigned int flags, void *output_data, size_t * output_data_size) { ASN1_TYPE pkcs8_asn, pkey_info; int ret; gnutls_datum_t tmp; schema_id schema; if (key == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } /* Get the private key info * tmp holds the DER encoding. */ ret = encode_to_private_key_info(key, &tmp, &pkey_info); if (ret < 0) { gnutls_assert(); return ret; } schema = _gnutls_pkcs_flags_to_schema(flags); if (((flags & GNUTLS_PKCS_PLAIN) || password == NULL) && !(flags & GNUTLS_PKCS_NULL_PASSWORD)) { _gnutls_free_datum(&tmp); ret = _gnutls_x509_export_int(pkey_info, format, PEM_UNENCRYPTED_PKCS8, output_data, output_data_size); asn1_delete_structure2(&pkey_info, ASN1_DELETE_FLAG_ZEROIZE); } else { asn1_delete_structure2(&pkey_info, ASN1_DELETE_FLAG_ZEROIZE); /* we don't need it */ ret = encode_to_pkcs8_key(schema, &tmp, password, &pkcs8_asn); _gnutls_free_key_datum(&tmp); if (ret < 0) { gnutls_assert(); return ret; } ret = _gnutls_x509_export_int(pkcs8_asn, format, PEM_PKCS8, output_data, output_data_size); asn1_delete_structure2(&pkcs8_asn, ASN1_DELETE_FLAG_ZEROIZE); } return ret; } /** * gnutls_pkcs8_info: * @data: Holds the PKCS #8 data * @format: the format of the PKCS #8 data * @schema: indicate the schema as one of %gnutls_pkcs_encrypt_flags_t * @cipher: the cipher used as %gnutls_cipher_algorithm_t * @salt: PBKDF2 salt (if non-NULL then @salt_size initially holds its size) * @salt_size: PBKDF2 salt size * @iter_count: PBKDF2 iteration count * @oid: if non-NULL it will contain an allocated null-terminated variable with the OID * * This function will provide information on the algorithms used * in a particular PKCS #8 structure. If the structure algorithms * are unknown the code %GNUTLS_E_UNKNOWN_CIPHER_TYPE will be returned, * and only @oid, will be set. That is, @oid will be set on encrypted PKCS #8 * structures whether supported or not. It must be deinitialized using gnutls_free(). * The other variables are only set on supported structures. * * Returns: %GNUTLS_E_INVALID_REQUEST if the provided structure isn't encrypted, * %GNUTLS_E_UNKNOWN_CIPHER_TYPE if the structure's encryption isn't supported, or * another negative error code in case of a failure. Zero on success. **/ int gnutls_pkcs8_info(const gnutls_datum_t * data, gnutls_x509_crt_fmt_t format, unsigned int *schema, unsigned int *cipher, void *salt, unsigned int *salt_size, unsigned int *iter_count, char **oid) { int ret = 0, need_free = 0; gnutls_datum_t _data; const struct pbes2_schema_st *p = NULL; struct pbkdf2_params kdf; if (oid) *oid = NULL; _data.data = data->data; _data.size = data->size; /* If the Certificate is in PEM format then decode it */ if (format == GNUTLS_X509_FMT_PEM) { /* Try the first header */ ret = _gnutls_fbase64_decode(PEM_UNENCRYPTED_PKCS8, data->data, data->size, &_data); if (ret < 0) { /* Try the encrypted header */ ret = _gnutls_fbase64_decode(PEM_PKCS8, data->data, data->size, &_data); if (ret < 0) { gnutls_assert(); return ret; } } need_free = 1; } ret = pkcs8_key_info(&_data, &p, &kdf, oid); if (ret == GNUTLS_E_DECRYPTION_FAILED) ret = GNUTLS_E_INVALID_REQUEST; if (ret < 0) { gnutls_assert(); goto cleanup; } if (need_free) _gnutls_free_datum(&_data); if (schema) *schema = p->flag; if (cipher) *cipher = p->cipher; if (iter_count) *iter_count = kdf.iter_count; if (salt) { if (*salt_size >= (unsigned)kdf.salt_size) { memcpy(salt, kdf.salt, kdf.salt_size); } else { *salt_size = kdf.salt_size; return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); } } if (salt_size) *salt_size = kdf.salt_size; return 0; cleanup: if (need_free) _gnutls_free_datum(&_data); return ret; } /** * gnutls_x509_privkey_export2_pkcs8: * @key: Holds the key * @format: the format of output params. One of PEM or DER. * @password: the password that will be used to encrypt the key. * @flags: an ORed sequence of gnutls_pkcs_encrypt_flags_t * @out: will contain a private key PEM or DER encoded * * This function will export the private key to a PKCS8 structure. * Both RSA and DSA keys can be exported. For DSA keys we use * PKCS #11 definitions. If the flags do not specify the encryption * cipher, then the default 3DES (PBES2) will be used. * * The @password can be either ASCII or UTF-8 in the default PBES2 * encryption schemas, or ASCII for the PKCS12 schemas. * * The output buffer is allocated using gnutls_malloc(). * * If the structure is PEM encoded, it will have a header * of "BEGIN ENCRYPTED PRIVATE KEY" or "BEGIN PRIVATE KEY" if * encryption is not used. * * Returns: In case of failure a negative error code will be * returned, and 0 on success. * * Since 3.1.3 **/ int gnutls_x509_privkey_export2_pkcs8(gnutls_x509_privkey_t key, gnutls_x509_crt_fmt_t format, const char *password, unsigned int flags, gnutls_datum_t * out) { ASN1_TYPE pkcs8_asn, pkey_info; int ret; gnutls_datum_t tmp; schema_id schema; if (key == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } /* Get the private key info * tmp holds the DER encoding. */ ret = encode_to_private_key_info(key, &tmp, &pkey_info); if (ret < 0) { gnutls_assert(); return ret; } schema = _gnutls_pkcs_flags_to_schema(flags); if (((flags & GNUTLS_PKCS_PLAIN) || password == NULL) && !(flags & GNUTLS_PKCS_NULL_PASSWORD)) { _gnutls_free_key_datum(&tmp); ret = _gnutls_x509_export_int2(pkey_info, format, PEM_UNENCRYPTED_PKCS8, out); asn1_delete_structure2(&pkey_info, ASN1_DELETE_FLAG_ZEROIZE); } else { asn1_delete_structure2(&pkey_info, ASN1_DELETE_FLAG_ZEROIZE); /* we don't need it */ ret = encode_to_pkcs8_key(schema, &tmp, password, &pkcs8_asn); _gnutls_free_key_datum(&tmp); if (ret < 0) { gnutls_assert(); return ret; } ret = _gnutls_x509_export_int2(pkcs8_asn, format, PEM_PKCS8, out); asn1_delete_structure2(&pkcs8_asn, ASN1_DELETE_FLAG_ZEROIZE); } return ret; } /* Read the parameters cipher, IV, salt etc using the given * schema ID. Initially the schema ID should have PBES2_GENERIC, for * PBES2 schemas, and will be updated by this function for details. */ static int read_pkcs_schema_params(schema_id * schema, const char *password, const uint8_t * data, int data_size, struct pbkdf2_params *kdf_params, struct pbe_enc_params *enc_params) { ASN1_TYPE pbes2_asn = ASN1_TYPE_EMPTY; int result; gnutls_datum_t tmp; const struct pbes2_schema_st *p; if (*schema == PBES2_GENERIC) { /* Now check the key derivation and the encryption * functions. */ if ((result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.pkcs-5-PBES2-params", &pbes2_asn)) != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } /* Decode the parameters. */ result = asn1_der_decoding(&pbes2_asn, data, data_size, NULL); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } tmp.data = (uint8_t *) data; tmp.size = data_size; result = read_pbkdf2_params(pbes2_asn, &tmp, kdf_params); if (result < 0) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } result = read_pbe_enc_params(pbes2_asn, &tmp, enc_params); if (result < 0) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } asn1_delete_structure2(&pbes2_asn, ASN1_DELETE_FLAG_ZEROIZE); p = cipher_to_pbes2_schema(enc_params->cipher); if (p == NULL) { result = GNUTLS_E_INVALID_REQUEST; gnutls_assert(); goto error; } *schema = p->schema; return 0; } else { /* PKCS #12 schema */ memset(enc_params, 0, sizeof(*enc_params)); p = pbes2_schema_get(*schema); if (p == NULL) { gnutls_assert(); result = GNUTLS_E_UNKNOWN_CIPHER_TYPE; goto error; } enc_params->cipher = p->cipher; enc_params->iv_size = gnutls_cipher_get_iv_size(p->cipher); if ((result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.pkcs-12-PbeParams", &pbes2_asn)) != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } /* Decode the parameters. */ result = asn1_der_decoding(&pbes2_asn, data, data_size, NULL); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } result = read_pkcs12_kdf_params(pbes2_asn, kdf_params); if (result < 0) { gnutls_assert(); goto error; } if (enc_params->iv_size) { result = _gnutls_pkcs12_string_to_key(mac_to_entry(GNUTLS_MAC_SHA1), 2 /*IV*/, kdf_params->salt, kdf_params-> salt_size, kdf_params-> iter_count, password, enc_params-> iv_size, enc_params->iv); if (result < 0) { gnutls_assert(); goto error; } } asn1_delete_structure(&pbes2_asn); return 0; } /* switch */ error: asn1_delete_structure(&pbes2_asn); return result; } /* We've gotten this far. In the real world it's almost certain * that we're dealing with a good file, but wrong password. * Sadly like 90% of random data is somehow valid DER for the * a first small number of bytes, so no easy way to guarantee. */ #define CHECK_ERR_FOR_ENCRYPTED(result) \ if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND || \ result == GNUTLS_E_ASN1_IDENTIFIER_NOT_FOUND || \ result == GNUTLS_E_ASN1_DER_ERROR || \ result == GNUTLS_E_ASN1_VALUE_NOT_FOUND || \ result == GNUTLS_E_ASN1_GENERIC_ERROR || \ result == GNUTLS_E_ASN1_VALUE_NOT_VALID || \ result == GNUTLS_E_ASN1_TAG_ERROR || \ result == GNUTLS_E_ASN1_TAG_IMPLICIT || \ result == GNUTLS_E_ASN1_TYPE_ANY_ERROR || \ result == GNUTLS_E_ASN1_SYNTAX_ERROR || \ result == GNUTLS_E_ASN1_DER_OVERFLOW) { \ result = GNUTLS_E_DECRYPTION_FAILED; \ } static int pkcs8_key_decrypt(const gnutls_datum_t * raw_key, ASN1_TYPE pkcs8_asn, const char *password, gnutls_x509_privkey_t pkey) { int result, len; char enc_oid[MAX_OID_SIZE]; gnutls_datum_t tmp; ASN1_TYPE pbes2_asn = ASN1_TYPE_EMPTY; int params_start, params_end, params_len; struct pbkdf2_params kdf_params; struct pbe_enc_params enc_params; schema_id schema; /* Check the encryption schema OID */ len = sizeof(enc_oid); result = asn1_read_value(pkcs8_asn, "encryptionAlgorithm.algorithm", enc_oid, &len); if (result != ASN1_SUCCESS) { gnutls_assert(); goto error; } if ((result = check_pkcs12_schema(enc_oid)) < 0) { gnutls_assert(); goto error; } schema = result; /* Get the DER encoding of the parameters. */ result = asn1_der_decoding_startEnd(pkcs8_asn, raw_key->data, raw_key->size, "encryptionAlgorithm.parameters", ¶ms_start, ¶ms_end); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } params_len = params_end - params_start + 1; result = read_pkcs_schema_params(&schema, password, &raw_key->data[params_start], params_len, &kdf_params, &enc_params); if (result < 0) { gnutls_assert(); goto error; } /* Parameters have been decoded. Now * decrypt the EncryptedData. */ result = decrypt_data(schema, pkcs8_asn, "encryptedData", password, &kdf_params, &enc_params, &tmp); if (result < 0) { gnutls_assert(); goto error; } result = decode_private_key_info(&tmp, pkey); _gnutls_free_key_datum(&tmp); CHECK_ERR_FOR_ENCRYPTED(result); if (result < 0) { gnutls_assert(); goto error; } return 0; error: asn1_delete_structure(&pbes2_asn); return result; } static int pkcs8_key_info(const gnutls_datum_t * raw_key, const struct pbes2_schema_st **p, struct pbkdf2_params *kdf_params, char **oid) { int result, len; char enc_oid[MAX_OID_SIZE]; int params_start, params_end, params_len; struct pbe_enc_params enc_params; schema_id schema; ASN1_TYPE pkcs8_asn = ASN1_TYPE_EMPTY; if ((result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.pkcs-8-EncryptedPrivateKeyInfo", &pkcs8_asn)) != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } result = asn1_der_decoding(&pkcs8_asn, raw_key->data, raw_key->size, NULL); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); CHECK_ERR_FOR_ENCRYPTED(result); goto error; } /* Check the encryption schema OID */ len = sizeof(enc_oid); result = asn1_read_value(pkcs8_asn, "encryptionAlgorithm.algorithm", enc_oid, &len); if (result != ASN1_SUCCESS) { gnutls_assert(); goto error; } if (oid) { *oid = gnutls_strdup(enc_oid); } if ((result = check_pkcs12_schema(enc_oid)) < 0) { gnutls_assert(); goto error; } schema = result; /* Get the DER encoding of the parameters. */ result = asn1_der_decoding_startEnd(pkcs8_asn, raw_key->data, raw_key->size, "encryptionAlgorithm.parameters", ¶ms_start, ¶ms_end); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } params_len = params_end - params_start + 1; result = read_pkcs_schema_params(&schema, NULL, &raw_key->data[params_start], params_len, kdf_params, &enc_params); if (result < 0) { gnutls_assert(); goto error; } *p = pbes2_schema_get(schema); if (*p == NULL) { gnutls_assert(); result = GNUTLS_E_UNKNOWN_CIPHER_TYPE; goto error; } return 0; error: return result; } /* Converts a PKCS #8 key to * an internal structure (gnutls_private_key) * (normally a PKCS #1 encoded RSA key) */ static int pkcs8_key_decode(const gnutls_datum_t * raw_key, const char *password, gnutls_x509_privkey_t pkey, unsigned int decrypt) { int result; ASN1_TYPE pkcs8_asn = ASN1_TYPE_EMPTY; if ((result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.pkcs-8-EncryptedPrivateKeyInfo", &pkcs8_asn)) != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } result = asn1_der_decoding(&pkcs8_asn, raw_key->data, raw_key->size, NULL); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } if (decrypt) result = pkcs8_key_decrypt(raw_key, pkcs8_asn, password, pkey); else result = 0; error: asn1_delete_structure2(&pkcs8_asn, ASN1_DELETE_FLAG_ZEROIZE); return result; } /* Decodes an RSA privateKey from a PKCS8 structure. */ static int _decode_pkcs8_rsa_key(ASN1_TYPE pkcs8_asn, gnutls_x509_privkey_t pkey) { int ret; gnutls_datum_t tmp; ret = _gnutls_x509_read_value(pkcs8_asn, "privateKey", &tmp); if (ret < 0) { gnutls_assert(); goto error; } pkey->key = _gnutls_privkey_decode_pkcs1_rsa_key(&tmp, pkey); _gnutls_free_key_datum(&tmp); if (pkey->key == NULL) { gnutls_assert(); goto error; } ret = 0; error: return ret; } /* Decodes an ECC privateKey from a PKCS8 structure. */ static int _decode_pkcs8_ecc_key(ASN1_TYPE pkcs8_asn, gnutls_x509_privkey_t pkey) { int ret; gnutls_datum_t tmp; unsigned char oid[MAX_OID_SIZE]; unsigned curve = GNUTLS_ECC_CURVE_INVALID; int len, result; /* openssl PKCS #8 files with ECC keys place the curve in * privateKeyAlgorithm.parameters instead of the ECPrivateKey.parameters. */ len = sizeof(oid); result = asn1_read_value(pkcs8_asn, "privateKeyAlgorithm.parameters", oid, &len); if (result == ASN1_SUCCESS) { ret = _gnutls_x509_read_ecc_params(oid, len, &curve); if (ret < 0) { curve = GNUTLS_ECC_CURVE_INVALID; } } ret = _gnutls_x509_read_value(pkcs8_asn, "privateKey", &tmp); if (ret < 0) { gnutls_assert(); goto error; } ret = _gnutls_privkey_decode_ecc_key(&pkey->key, &tmp, pkey, curve); _gnutls_free_key_datum(&tmp); if (ret < 0) { gnutls_assert(); goto error; } ret = 0; error: return ret; } /* Decodes an DSA privateKey and params from a PKCS8 structure. */ static int _decode_pkcs8_dsa_key(ASN1_TYPE pkcs8_asn, gnutls_x509_privkey_t pkey) { int ret; gnutls_datum_t tmp; ret = _gnutls_x509_read_value(pkcs8_asn, "privateKey", &tmp); if (ret < 0) { gnutls_assert(); goto error; } ret = _gnutls_x509_read_der_int(tmp.data, tmp.size, &pkey->params.params[4]); _gnutls_free_key_datum(&tmp); if (ret < 0) { gnutls_assert(); goto error; } ret = _gnutls_x509_read_value(pkcs8_asn, "privateKeyAlgorithm.parameters", &tmp); if (ret < 0) { gnutls_assert(); goto error; } ret = _gnutls_x509_read_pubkey_params(GNUTLS_PK_DSA, tmp.data, tmp.size, &pkey->params); _gnutls_free_datum(&tmp); if (ret < 0) { gnutls_assert(); goto error; } /* the public key can be generated as g^x mod p */ ret = _gnutls_mpi_init(&pkey->params.params[3]); if (ret < 0) { gnutls_assert(); goto error; } ret = _gnutls_mpi_powm(pkey->params.params[3], pkey->params.params[2], pkey->params.params[4], pkey->params.params[0]); if (ret < 0) { gnutls_assert(); goto error; } ret = _gnutls_asn1_encode_privkey(GNUTLS_PK_DSA, &pkey->key, &pkey->params); if (ret < 0) { gnutls_assert(); goto error; } pkey->params.params_nr = DSA_PRIVATE_PARAMS; ret = 0; error: return ret; } static int decode_private_key_info(const gnutls_datum_t * der, gnutls_x509_privkey_t pkey) { int result, len; char oid[MAX_OID_SIZE]; ASN1_TYPE pkcs8_asn = ASN1_TYPE_EMPTY; if ((result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.pkcs-8-PrivateKeyInfo", &pkcs8_asn)) != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } result = asn1_der_decoding(&pkcs8_asn, der->data, der->size, NULL); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } /* Check the private key algorithm OID */ len = sizeof(oid); result = asn1_read_value(pkcs8_asn, "privateKeyAlgorithm.algorithm", oid, &len); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } /* we only support RSA and DSA private keys. */ pkey->pk_algorithm = _gnutls_x509_oid2pk_algorithm(oid); if (pkey->pk_algorithm == GNUTLS_PK_UNKNOWN) { gnutls_assert(); _gnutls_debug_log ("PKCS #8 private key OID '%s' is unsupported.\n", oid); result = GNUTLS_E_UNKNOWN_PK_ALGORITHM; goto error; } /* Get the DER encoding of the actual private key. */ if (pkey->pk_algorithm == GNUTLS_PK_RSA) result = _decode_pkcs8_rsa_key(pkcs8_asn, pkey); else if (pkey->pk_algorithm == GNUTLS_PK_DSA) result = _decode_pkcs8_dsa_key(pkcs8_asn, pkey); else if (pkey->pk_algorithm == GNUTLS_PK_EC) result = _decode_pkcs8_ecc_key(pkcs8_asn, pkey); else return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); if (result < 0) { gnutls_assert(); return result; } result = 0; error: asn1_delete_structure2(&pkcs8_asn, ASN1_DELETE_FLAG_ZEROIZE); return result; } /** * gnutls_x509_privkey_import_pkcs8: * @key: The structure to store the parsed key * @data: The DER or PEM encoded key. * @format: One of DER or PEM * @password: the password to decrypt the key (if it is encrypted). * @flags: 0 if encrypted or GNUTLS_PKCS_PLAIN if not encrypted. * * This function will convert the given DER or PEM encoded PKCS8 2.0 * encrypted key to the native gnutls_x509_privkey_t format. The * output will be stored in @key. Both RSA and DSA keys can be * imported, and flags can only be used to indicate an unencrypted * key. * * The @password can be either ASCII or UTF-8 in the default PBES2 * encryption schemas, or ASCII for the PKCS12 schemas. * * If the Certificate is PEM encoded it should have a header of * "ENCRYPTED PRIVATE KEY", or "PRIVATE KEY". You only need to * specify the flags if the key is DER encoded, since in that case * the encryption status cannot be auto-detected. * * If the %GNUTLS_PKCS_PLAIN flag is specified and the supplied data * are encrypted then %GNUTLS_E_DECRYPTION_FAILED is returned. * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. **/ int gnutls_x509_privkey_import_pkcs8(gnutls_x509_privkey_t key, const gnutls_datum_t * data, gnutls_x509_crt_fmt_t format, const char *password, unsigned int flags) { int result = 0, need_free = 0; gnutls_datum_t _data; if (key == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } _data.data = data->data; _data.size = data->size; key->pk_algorithm = GNUTLS_PK_UNKNOWN; /* If the Certificate is in PEM format then decode it */ if (format == GNUTLS_X509_FMT_PEM) { /* Try the first header */ result = _gnutls_fbase64_decode(PEM_UNENCRYPTED_PKCS8, data->data, data->size, &_data); if (result < 0) { /* Try the encrypted header */ result = _gnutls_fbase64_decode(PEM_PKCS8, data->data, data->size, &_data); if (result < 0) { gnutls_assert(); return result; } } else if (flags == 0) flags |= GNUTLS_PKCS_PLAIN; need_free = 1; } if (key->expanded) { _gnutls_x509_privkey_reinit(key); } key->expanded = 1; /* Here we don't check for password == NULL to maintain a backwards * compatibility behavior, with old versions that were encrypting using * a NULL password. */ if (flags & GNUTLS_PKCS_PLAIN) { result = decode_private_key_info(&_data, key); if (result < 0) { /* check if it is encrypted */ if (pkcs8_key_decode(&_data, "", key, 0) == 0) result = GNUTLS_E_DECRYPTION_FAILED; } } else { /* encrypted. */ result = pkcs8_key_decode(&_data, password, key, 1); } if (result < 0) { gnutls_assert(); goto cleanup; } if (need_free) _gnutls_free_datum(&_data); /* The key has now been decoded. */ return 0; cleanup: key->pk_algorithm = GNUTLS_PK_UNKNOWN; if (need_free) _gnutls_free_datum(&_data); return result; } /* Reads the PBKDF2 parameters. */ static int read_pbkdf2_params(ASN1_TYPE pbes2_asn, const gnutls_datum_t * der, struct pbkdf2_params *params) { int params_start, params_end; int params_len, len, result; ASN1_TYPE pbkdf2_asn = ASN1_TYPE_EMPTY; char oid[MAX_OID_SIZE]; memset(params, 0, sizeof(*params)); /* Check the key derivation algorithm */ len = sizeof(oid); result = asn1_read_value(pbes2_asn, "keyDerivationFunc.algorithm", oid, &len); if (result != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(result); } _gnutls_hard_log("keyDerivationFunc.algorithm: %s\n", oid); if (strcmp(oid, PBKDF2_OID) != 0) { gnutls_assert(); _gnutls_debug_log ("PKCS #8 key derivation OID '%s' is unsupported.\n", oid); return _gnutls_asn2err(result); } result = asn1_der_decoding_startEnd(pbes2_asn, der->data, der->size, "keyDerivationFunc.parameters", ¶ms_start, ¶ms_end); if (result != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(result); } params_len = params_end - params_start + 1; /* Now check the key derivation and the encryption * functions. */ if ((result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.pkcs-5-PBKDF2-params", &pbkdf2_asn)) != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(result); } result = asn1_der_decoding(&pbkdf2_asn, &der->data[params_start], params_len, NULL); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } /* read the salt */ params->salt_size = sizeof(params->salt); result = asn1_read_value(pbkdf2_asn, "salt.specified", params->salt, ¶ms->salt_size); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } _gnutls_hard_log("salt.specified.size: %d\n", params->salt_size); /* read the iteration count */ result = _gnutls_x509_read_uint(pbkdf2_asn, "iterationCount", ¶ms->iter_count); if (result != ASN1_SUCCESS) { gnutls_assert(); goto error; } _gnutls_hard_log("iterationCount: %d\n", params->iter_count); /* read the keylength, if it is set. */ result = _gnutls_x509_read_uint(pbkdf2_asn, "keyLength", ¶ms->key_size); if (result < 0) { params->key_size = 0; } _gnutls_hard_log("keyLength: %d\n", params->key_size); /* We don't read the PRF. We only use the default. */ result = 0; error: asn1_delete_structure(&pbkdf2_asn); return result; } /* Reads the PBE parameters from PKCS-12 schemas (*&#%*&#% RSA). */ static int read_pkcs12_kdf_params(ASN1_TYPE pbes2_asn, struct pbkdf2_params *params) { int result; memset(params, 0, sizeof(*params)); /* read the salt */ params->salt_size = sizeof(params->salt); result = asn1_read_value(pbes2_asn, "salt", params->salt, ¶ms->salt_size); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } _gnutls_hard_log("salt.size: %d\n", params->salt_size); /* read the iteration count */ result = _gnutls_x509_read_uint(pbes2_asn, "iterations", ¶ms->iter_count); if (result != ASN1_SUCCESS) { gnutls_assert(); goto error; } _gnutls_hard_log("iterationCount: %d\n", params->iter_count); params->key_size = 0; return 0; error: return result; } /* Writes the PBE parameters for PKCS-12 schemas. */ static int write_pkcs12_kdf_params(ASN1_TYPE pbes2_asn, const struct pbkdf2_params *kdf_params) { int result; /* write the salt */ result = asn1_write_value(pbes2_asn, "salt", kdf_params->salt, kdf_params->salt_size); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } _gnutls_hard_log("salt.size: %d\n", kdf_params->salt_size); /* write the iteration count */ result = _gnutls_x509_write_uint32(pbes2_asn, "iterations", kdf_params->iter_count); if (result < 0) { gnutls_assert(); goto error; } _gnutls_hard_log("iterationCount: %d\n", kdf_params->iter_count); return 0; error: return result; } static int read_pbe_enc_params(ASN1_TYPE pbes2_asn, const gnutls_datum_t * der, struct pbe_enc_params *params) { int params_start, params_end; int params_len, len, result; ASN1_TYPE pbe_asn = ASN1_TYPE_EMPTY; char oid[MAX_OID_SIZE]; const struct pbes2_schema_st *p; memset(params, 0, sizeof(*params)); /* Check the encryption algorithm */ len = sizeof(oid); result = asn1_read_value(pbes2_asn, "encryptionScheme.algorithm", oid, &len); if (result != ASN1_SUCCESS) { gnutls_assert(); goto error; } _gnutls_hard_log("encryptionScheme.algorithm: %s\n", oid); if ((result = pbes2_oid_to_cipher(oid, ¶ms->cipher)) < 0) { gnutls_assert(); goto error; } result = asn1_der_decoding_startEnd(pbes2_asn, der->data, der->size, "encryptionScheme.parameters", ¶ms_start, ¶ms_end); if (result != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(result); } params_len = params_end - params_start + 1; /* Now check the encryption parameters. */ p = cipher_to_pbes2_schema(params->cipher); if (p == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } if ((result = asn1_create_element(_gnutls_get_pkix(), p->desc, &pbe_asn)) != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(result); } result = asn1_der_decoding(&pbe_asn, &der->data[params_start], params_len, NULL); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } /* read the IV */ params->iv_size = sizeof(params->iv); result = asn1_read_value(pbe_asn, "", params->iv, ¶ms->iv_size); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } _gnutls_hard_log("IV.size: %d\n", params->iv_size); result = 0; error: asn1_delete_structure(&pbe_asn); return result; } static int decrypt_data(schema_id schema, ASN1_TYPE pkcs8_asn, const char *root, const char *password, const struct pbkdf2_params *kdf_params, const struct pbe_enc_params *enc_params, gnutls_datum_t * decrypted_data) { int result; gnutls_datum_t enc = {NULL, 0}; uint8_t *key = NULL; gnutls_datum_t dkey, d_iv; cipher_hd_st ch; int ch_init = 0; int key_size; unsigned int pass_len = 0; const struct pbes2_schema_st *p; if (password) pass_len = strlen(password); result = _gnutls_x509_read_value(pkcs8_asn, root, &enc); if (result < 0) { gnutls_assert(); return _gnutls_asn2err(result); } if (kdf_params->key_size == 0) { key_size = gnutls_cipher_get_key_size(enc_params->cipher); } else key_size = kdf_params->key_size; key = gnutls_malloc(key_size); if (key == NULL) { gnutls_assert(); result = GNUTLS_E_MEMORY_ERROR; goto error; } /* generate the key */ p = pbes2_schema_get(schema); if (p != NULL && p->pbes2 != 0) { /* PBES2 */ pbkdf2_hmac_sha1(pass_len, (uint8_t*)password, kdf_params->iter_count, kdf_params->salt_size, kdf_params->salt, key_size, key); } else if (p != NULL) { /* PKCS 12 schema */ result = _gnutls_pkcs12_string_to_key(mac_to_entry(GNUTLS_MAC_SHA1), 1 /*KEY*/, kdf_params->salt, kdf_params->salt_size, kdf_params->iter_count, password, key_size, key); if (result < 0) { gnutls_assert(); goto error; } } else { gnutls_assert(); result = GNUTLS_E_UNKNOWN_CIPHER_TYPE; goto error; } /* do the decryption. */ dkey.data = key; dkey.size = key_size; d_iv.data = (uint8_t *) enc_params->iv; d_iv.size = enc_params->iv_size; result = _gnutls_cipher_init(&ch, cipher_to_entry(enc_params->cipher), &dkey, &d_iv, 0); gnutls_free(key); key = NULL; if (result < 0) { gnutls_assert(); goto error; } ch_init = 1; result = _gnutls_cipher_decrypt(&ch, enc.data, enc.size); if (result < 0) { gnutls_assert(); goto error; } decrypted_data->data = enc.data; if (gnutls_cipher_get_block_size(enc_params->cipher) != 1) decrypted_data->size = enc.size - enc.data[enc.size - 1]; else decrypted_data->size = enc.size; _gnutls_cipher_deinit(&ch); return 0; error: gnutls_free(enc.data); gnutls_free(key); if (ch_init != 0) _gnutls_cipher_deinit(&ch); return result; } /* Writes the PBKDF2 parameters. */ static int write_pbkdf2_params(ASN1_TYPE pbes2_asn, const struct pbkdf2_params *kdf_params) { int result; ASN1_TYPE pbkdf2_asn = ASN1_TYPE_EMPTY; uint8_t tmp[MAX_OID_SIZE]; /* Write the key derivation algorithm */ result = asn1_write_value(pbes2_asn, "keyDerivationFunc.algorithm", PBKDF2_OID, 1); if (result != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(result); } /* Now write the key derivation and the encryption * functions. */ if ((result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.pkcs-5-PBKDF2-params", &pbkdf2_asn)) != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(result); } result = asn1_write_value(pbkdf2_asn, "salt", "specified", 1); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } /* write the salt */ result = asn1_write_value(pbkdf2_asn, "salt.specified", kdf_params->salt, kdf_params->salt_size); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } _gnutls_hard_log("salt.specified.size: %d\n", kdf_params->salt_size); /* write the iteration count */ _gnutls_write_uint32(kdf_params->iter_count, tmp); result = asn1_write_value(pbkdf2_asn, "iterationCount", tmp, 4); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } _gnutls_hard_log("iterationCount: %d\n", kdf_params->iter_count); /* write the keylength, if it is set. */ result = asn1_write_value(pbkdf2_asn, "keyLength", NULL, 0); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } /* We write an emptry prf. */ result = asn1_write_value(pbkdf2_asn, "prf", NULL, 0); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } /* now encode them an put the DER output * in the keyDerivationFunc.parameters */ result = _gnutls_x509_der_encode_and_copy(pbkdf2_asn, "", pbes2_asn, "keyDerivationFunc.parameters", 0); if (result < 0) { gnutls_assert(); goto error; } return 0; error: asn1_delete_structure(&pbkdf2_asn); return result; } static int write_pbe_enc_params(ASN1_TYPE pbes2_asn, const struct pbe_enc_params *params) { int result; ASN1_TYPE pbe_asn = ASN1_TYPE_EMPTY; const struct pbes2_schema_st *p; /* Write the encryption algorithm */ p = cipher_to_pbes2_schema(params->cipher); if (p == NULL || p->pbes2 == 0) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } result = asn1_write_value(pbes2_asn, "encryptionScheme.algorithm", p->oid, 1); if (result != ASN1_SUCCESS) { gnutls_assert(); goto error; } _gnutls_hard_log("encryptionScheme.algorithm: %s\n", p->oid); /* Now check the encryption parameters. */ if ((result = asn1_create_element(_gnutls_get_pkix(), p->desc, &pbe_asn)) != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(result); } /* read the salt */ result = asn1_write_value(pbe_asn, "", params->iv, params->iv_size); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } _gnutls_hard_log("IV.size: %d\n", params->iv_size); /* now encode them an put the DER output * in the encryptionScheme.parameters */ result = _gnutls_x509_der_encode_and_copy(pbe_asn, "", pbes2_asn, "encryptionScheme.parameters", 0); if (result < 0) { gnutls_assert(); goto error; } return 0; error: asn1_delete_structure(&pbe_asn); return result; } /* Generates a key and also stores the key parameters. */ static int generate_key(schema_id schema, const char *password, struct pbkdf2_params *kdf_params, struct pbe_enc_params *enc_params, gnutls_datum_t * key) { unsigned char rnd[2]; unsigned int pass_len = 0; int ret; const struct pbes2_schema_st *p; if (password) pass_len = strlen(password); ret = _gnutls_rnd(GNUTLS_RND_RANDOM, rnd, 2); if (ret < 0) { gnutls_assert(); return ret; } /* generate salt */ kdf_params->salt_size = MIN(sizeof(kdf_params->salt), (unsigned) (12 + (rnd[1] % 10))); p = pbes2_schema_get(schema); if (p != NULL && p->pbes2 != 0) { /* PBES2 */ enc_params->cipher = p->cipher; } else if (p != NULL) { /* non PBES2 algorithms */ enc_params->cipher = p->cipher; kdf_params->salt_size = 8; } else { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } ret = _gnutls_rnd(GNUTLS_RND_RANDOM, kdf_params->salt, kdf_params->salt_size); if (ret < 0) { gnutls_assert(); return GNUTLS_E_RANDOM_FAILED; } kdf_params->iter_count = 5*1024 + rnd[0]; key->size = kdf_params->key_size = gnutls_cipher_get_key_size(enc_params->cipher); enc_params->iv_size = gnutls_cipher_get_iv_size(enc_params->cipher); key->data = gnutls_malloc(key->size); if (key->data == NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } /* now generate the key. */ if (p->pbes2 != 0) { pbkdf2_hmac_sha1(pass_len, (uint8_t*)password, kdf_params->iter_count, kdf_params->salt_size, kdf_params->salt, kdf_params->key_size, key->data); if (enc_params->iv_size) { ret = _gnutls_rnd(GNUTLS_RND_NONCE, enc_params->iv, enc_params->iv_size); if (ret < 0) { gnutls_assert(); return ret; } } } else { /* PKCS 12 schema */ ret = _gnutls_pkcs12_string_to_key(mac_to_entry(GNUTLS_MAC_SHA1), 1 /*KEY*/, kdf_params->salt, kdf_params->salt_size, kdf_params->iter_count, password, kdf_params->key_size, key->data); if (ret < 0) { gnutls_assert(); return ret; } /* Now generate the IV */ if (enc_params->iv_size) { ret = _gnutls_pkcs12_string_to_key(mac_to_entry(GNUTLS_MAC_SHA1), 2 /*IV*/, kdf_params->salt, kdf_params-> salt_size, kdf_params-> iter_count, password, enc_params-> iv_size, enc_params->iv); if (ret < 0) { gnutls_assert(); return ret; } } } return 0; } /* Encodes the parameters to be written in the encryptionAlgorithm.parameters * part. */ static int write_schema_params(schema_id schema, ASN1_TYPE pkcs8_asn, const char *where, const struct pbkdf2_params *kdf_params, const struct pbe_enc_params *enc_params) { int result; ASN1_TYPE pbes2_asn = ASN1_TYPE_EMPTY; const struct pbes2_schema_st *p; p = pbes2_schema_get(schema); if (p != NULL && p->pbes2 != 0) { /* PBES2 */ if ((result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.pkcs-5-PBES2-params", &pbes2_asn)) != ASN1_SUCCESS) { gnutls_assert(); return _gnutls_asn2err(result); } result = write_pbkdf2_params(pbes2_asn, kdf_params); if (result < 0) { gnutls_assert(); goto error; } result = write_pbe_enc_params(pbes2_asn, enc_params); if (result < 0) { gnutls_assert(); goto error; } result = _gnutls_x509_der_encode_and_copy(pbes2_asn, "", pkcs8_asn, where, 0); if (result < 0) { gnutls_assert(); goto error; } asn1_delete_structure(&pbes2_asn); } else if (p != NULL) { /* PKCS #12 */ if ((result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.pkcs-12-PbeParams", &pbes2_asn)) != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } result = write_pkcs12_kdf_params(pbes2_asn, kdf_params); if (result < 0) { gnutls_assert(); goto error; } result = _gnutls_x509_der_encode_and_copy(pbes2_asn, "", pkcs8_asn, where, 0); if (result < 0) { gnutls_assert(); goto error; } asn1_delete_structure(&pbes2_asn); } return 0; error: asn1_delete_structure(&pbes2_asn); return result; } static int encrypt_data(const gnutls_datum_t * plain, const struct pbe_enc_params *enc_params, gnutls_datum_t * key, gnutls_datum_t * encrypted) { int result; int data_size; uint8_t *data = NULL; gnutls_datum_t d_iv; cipher_hd_st ch; int ch_init = 0; uint8_t pad, pad_size; pad_size = gnutls_cipher_get_block_size(enc_params->cipher); if (pad_size == 1) /* stream */ pad_size = 0; data = gnutls_malloc(plain->size + pad_size); if (data == NULL) { gnutls_assert(); return GNUTLS_E_MEMORY_ERROR; } memcpy(data, plain->data, plain->size); if (pad_size > 0) { pad = pad_size - (plain->size % pad_size); if (pad == 0) pad = pad_size; memset(&data[plain->size], pad, pad); } else pad = 0; data_size = plain->size + pad; d_iv.data = (uint8_t *) enc_params->iv; d_iv.size = enc_params->iv_size; result = _gnutls_cipher_init(&ch, cipher_to_entry(enc_params->cipher), key, &d_iv, 1); if (result < 0) { gnutls_assert(); goto error; } ch_init = 1; result = _gnutls_cipher_encrypt(&ch, data, data_size); if (result < 0) { gnutls_assert(); goto error; } encrypted->data = data; encrypted->size = data_size; _gnutls_cipher_deinit(&ch); return 0; error: gnutls_free(data); if (ch_init != 0) _gnutls_cipher_deinit(&ch); return result; } /* Decrypts a PKCS #7 encryptedData. The output is allocated * and stored in dec. */ int _gnutls_pkcs7_decrypt_data(const gnutls_datum_t * data, const char *password, gnutls_datum_t * dec) { int result, len; char enc_oid[MAX_OID_SIZE]; gnutls_datum_t tmp; ASN1_TYPE pbes2_asn = ASN1_TYPE_EMPTY, pkcs7_asn = ASN1_TYPE_EMPTY; int params_start, params_end, params_len; struct pbkdf2_params kdf_params; struct pbe_enc_params enc_params; schema_id schema; if ((result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.pkcs-7-EncryptedData", &pkcs7_asn)) != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } result = asn1_der_decoding(&pkcs7_asn, data->data, data->size, NULL); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } /* Check the encryption schema OID */ len = sizeof(enc_oid); result = asn1_read_value(pkcs7_asn, "encryptedContentInfo.contentEncryptionAlgorithm.algorithm", enc_oid, &len); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } if ((result = check_pkcs12_schema(enc_oid)) < 0) { gnutls_assert(); goto error; } schema = result; /* Get the DER encoding of the parameters. */ result = asn1_der_decoding_startEnd(pkcs7_asn, data->data, data->size, "encryptedContentInfo.contentEncryptionAlgorithm.parameters", ¶ms_start, ¶ms_end); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } params_len = params_end - params_start + 1; result = read_pkcs_schema_params(&schema, password, &data->data[params_start], params_len, &kdf_params, &enc_params); if (result < ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } /* Parameters have been decoded. Now * decrypt the EncryptedData. */ result = decrypt_data(schema, pkcs7_asn, "encryptedContentInfo.encryptedContent", password, &kdf_params, &enc_params, &tmp); if (result < 0) { gnutls_assert(); goto error; } asn1_delete_structure2(&pkcs7_asn, ASN1_DELETE_FLAG_ZEROIZE); *dec = tmp; return 0; error: asn1_delete_structure(&pbes2_asn); asn1_delete_structure2(&pkcs7_asn, ASN1_DELETE_FLAG_ZEROIZE); return result; } int _gnutls_pkcs7_data_enc_info(const gnutls_datum_t * data, const struct pbes2_schema_st **p, struct pbkdf2_params *kdf_params, char **oid) { int result, len; char enc_oid[MAX_OID_SIZE]; ASN1_TYPE pbes2_asn = ASN1_TYPE_EMPTY, pkcs7_asn = ASN1_TYPE_EMPTY; int params_start, params_end, params_len; struct pbe_enc_params enc_params; schema_id schema; if ((result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.pkcs-7-EncryptedData", &pkcs7_asn)) != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } result = asn1_der_decoding(&pkcs7_asn, data->data, data->size, NULL); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } /* Check the encryption schema OID */ len = sizeof(enc_oid); result = asn1_read_value(pkcs7_asn, "encryptedContentInfo.contentEncryptionAlgorithm.algorithm", enc_oid, &len); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } if (oid) { *oid = gnutls_strdup(enc_oid); } if ((result = check_pkcs12_schema(enc_oid)) < 0) { gnutls_assert(); goto error; } schema = result; /* Get the DER encoding of the parameters. */ result = asn1_der_decoding_startEnd(pkcs7_asn, data->data, data->size, "encryptedContentInfo.contentEncryptionAlgorithm.parameters", ¶ms_start, ¶ms_end); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } params_len = params_end - params_start + 1; result = read_pkcs_schema_params(&schema, NULL, &data->data[params_start], params_len, kdf_params, &enc_params); if (result < ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } *p = pbes2_schema_get(schema); if (*p == NULL) { gnutls_assert(); result = GNUTLS_E_UNKNOWN_CIPHER_TYPE; goto error; } asn1_delete_structure2(&pkcs7_asn, ASN1_DELETE_FLAG_ZEROIZE); return 0; error: asn1_delete_structure(&pbes2_asn); asn1_delete_structure2(&pkcs7_asn, ASN1_DELETE_FLAG_ZEROIZE); return result; } /* Encrypts to a PKCS #7 encryptedData. The output is allocated * and stored in enc. */ int _gnutls_pkcs7_encrypt_data(schema_id schema, const gnutls_datum_t * data, const char *password, gnutls_datum_t * enc) { int result; gnutls_datum_t key = { NULL, 0 }; gnutls_datum_t tmp = { NULL, 0 }; ASN1_TYPE pkcs7_asn = ASN1_TYPE_EMPTY; struct pbkdf2_params kdf_params; struct pbe_enc_params enc_params; const char *str_oid; if ((result = asn1_create_element(_gnutls_get_pkix(), "PKIX1.pkcs-7-EncryptedData", &pkcs7_asn)) != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } /* Write the encryption schema OID */ result = pkcs12_schema_to_oid(schema, &str_oid); if (result < 0) { gnutls_assert(); return result; } result = asn1_write_value(pkcs7_asn, "encryptedContentInfo.contentEncryptionAlgorithm.algorithm", str_oid, 1); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } /* Generate a symmetric key. */ result = generate_key(schema, password, &kdf_params, &enc_params, &key); if (result < 0) { gnutls_assert(); goto error; } result = write_schema_params(schema, pkcs7_asn, "encryptedContentInfo.contentEncryptionAlgorithm.parameters", &kdf_params, &enc_params); if (result < 0) { gnutls_assert(); goto error; } /* Parameters have been encoded. Now * encrypt the Data. */ result = encrypt_data(data, &enc_params, &key, &tmp); if (result < 0) { gnutls_assert(); goto error; } /* write the encrypted data. */ result = asn1_write_value(pkcs7_asn, "encryptedContentInfo.encryptedContent", tmp.data, tmp.size); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } _gnutls_free_datum(&tmp); _gnutls_free_key_datum(&key); /* Now write the rest of the pkcs-7 stuff. */ result = _gnutls_x509_write_uint32(pkcs7_asn, "version", 0); if (result < 0) { gnutls_assert(); goto error; } result = asn1_write_value(pkcs7_asn, "encryptedContentInfo.contentType", DATA_OID, 1); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } result = asn1_write_value(pkcs7_asn, "unprotectedAttrs", NULL, 0); if (result != ASN1_SUCCESS) { gnutls_assert(); result = _gnutls_asn2err(result); goto error; } /* Now encode and copy the DER stuff. */ result = _gnutls_x509_der_encode(pkcs7_asn, "", enc, 0); asn1_delete_structure2(&pkcs7_asn, ASN1_DELETE_FLAG_ZEROIZE); if (result < 0) { gnutls_assert(); goto error; } error: _gnutls_free_key_datum(&key); _gnutls_free_datum(&tmp); asn1_delete_structure2(&pkcs7_asn, ASN1_DELETE_FLAG_ZEROIZE); return result; }