diff options
author | Andy Polyakov <appro@openssl.org> | 2016-07-26 16:42:41 +0200 |
---|---|---|
committer | Andy Polyakov <appro@openssl.org> | 2016-08-22 13:52:59 +0200 |
commit | 1194ea8dc3b51a35c9947ed276f38436abee5743 (patch) | |
tree | 3019d2d78ff46aea68ab706720222268d91bda6a /crypto/pkcs12 | |
parent | b799aef863a81c9a9d5dbffae12cca912ae348b2 (diff) |
crypto/pkcs12: facilitate accessing data with non-interoperable password.
Originally PKCS#12 subroutines treated password strings as ASCII.
It worked as long as they were pure ASCII, but if there were some
none-ASCII characters result was non-interoperable. But fixing it
poses problem accessing data protected with broken password. In
order to make asscess to old data possible add retry with old-style
password.
Reviewed-by: Richard Levitte <levitte@openssl.org>
Diffstat (limited to 'crypto/pkcs12')
-rw-r--r-- | crypto/pkcs12/p12_crpt.c | 31 | ||||
-rw-r--r-- | crypto/pkcs12/p12_lcl.h | 9 | ||||
-rw-r--r-- | crypto/pkcs12/p12_mutl.c | 84 | ||||
-rw-r--r-- | crypto/pkcs12/p12_utl.c | 9 |
4 files changed, 119 insertions, 14 deletions
diff --git a/crypto/pkcs12/p12_crpt.c b/crypto/pkcs12/p12_crpt.c index 1fe140ad0a..d30aab379a 100644 --- a/crypto/pkcs12/p12_crpt.c +++ b/crypto/pkcs12/p12_crpt.c @@ -17,6 +17,16 @@ void PKCS12_PBE_add(void) { } +#undef PKCS12_key_gen +/* + * See p12_multi.c:PKCS12_verify_mac() for details... + */ +extern int (*PKCS12_key_gen)(const char *pass, int passlen, + unsigned char *salt, int slen, + int id, int iter, int n, + unsigned char *out, + const EVP_MD *md_type); + int PKCS12_PBE_keyivgen(EVP_CIPHER_CTX *ctx, const char *pass, int passlen, ASN1_TYPE *param, const EVP_CIPHER *cipher, const EVP_MD *md, int en_de) @@ -25,6 +35,19 @@ int PKCS12_PBE_keyivgen(EVP_CIPHER_CTX *ctx, const char *pass, int passlen, int saltlen, iter, ret; unsigned char *salt; unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH]; + int (*pkcs12_key_gen)(const char *pass, int passlen, + unsigned char *salt, int slen, + int id, int iter, int n, + unsigned char *out, + const EVP_MD *md_type); + + if (PKCS12_key_gen == NULL || en_de) + /* + * Default to UTF-8, but force it in encrypt case. + */ + pkcs12_key_gen = PKCS12_key_gen_utf8; + else + pkcs12_key_gen = PKCS12_key_gen; if (cipher == NULL) return 0; @@ -43,14 +66,14 @@ int PKCS12_PBE_keyivgen(EVP_CIPHER_CTX *ctx, const char *pass, int passlen, iter = ASN1_INTEGER_get(pbe->iter); salt = pbe->salt->data; saltlen = pbe->salt->length; - if (!PKCS12_key_gen(pass, passlen, salt, saltlen, PKCS12_KEY_ID, - iter, EVP_CIPHER_key_length(cipher), key, md)) { + if (!(*pkcs12_key_gen)(pass, passlen, salt, saltlen, PKCS12_KEY_ID, + iter, EVP_CIPHER_key_length(cipher), key, md)) { PKCS12err(PKCS12_F_PKCS12_PBE_KEYIVGEN, PKCS12_R_KEY_GEN_ERROR); PBEPARAM_free(pbe); return 0; } - if (!PKCS12_key_gen(pass, passlen, salt, saltlen, PKCS12_IV_ID, - iter, EVP_CIPHER_iv_length(cipher), iv, md)) { + if (!(*pkcs12_key_gen)(pass, passlen, salt, saltlen, PKCS12_IV_ID, + iter, EVP_CIPHER_iv_length(cipher), iv, md)) { PKCS12err(PKCS12_F_PKCS12_PBE_KEYIVGEN, PKCS12_R_IV_GEN_ERROR); PBEPARAM_free(pbe); return 0; diff --git a/crypto/pkcs12/p12_lcl.h b/crypto/pkcs12/p12_lcl.h index 8930875153..9a27f2fa9e 100644 --- a/crypto/pkcs12/p12_lcl.h +++ b/crypto/pkcs12/p12_lcl.h @@ -42,3 +42,12 @@ struct pkcs12_bag_st { } value; }; +#undef PKCS12_key_gen +/* + * See p12_multi.c:PKCS12_verify_mac() for details... + */ +extern int (*PKCS12_key_gen)(const char *pass, int passlen, + unsigned char *salt, int slen, + int id, int iter, int n, + unsigned char *out, + const EVP_MD *md_type); diff --git a/crypto/pkcs12/p12_mutl.c b/crypto/pkcs12/p12_mutl.c index 79639c2169..325da0c207 100644 --- a/crypto/pkcs12/p12_mutl.c +++ b/crypto/pkcs12/p12_mutl.c @@ -66,9 +66,40 @@ static int pkcs12_gen_gost_mac_key(const char *pass, int passlen, return 1; } +#undef PKCS12_key_gen +/* + * |PKCS12_key_gen| is used to convey information about old-style broken + * password being used to PKCS12_PBE_keyivgen in decrypt cases. Workflow + * is if PKCS12_verify_mac notes that password encoded with compliant + * PKCS12_key_gen_utf8 conversion subroutine isn't right, while encoded + * with legacy non-compliant one is, then it sets |PKCS12_key_gen| to + * legacy PKCS12_key_gen_asc conversion subroutine, which is then picked + * by PKCS12_PBE_keyivgen. This applies to reading data. Written data + * on the other hand is protected with standard-compliant encoding, i.e. + * in backward-incompatible manner. Note that formally the approach is + * not MT-safe. Rationale is that in order to access PKCS#12 files from + * MT or even production application, you would be required to convert + * data to correct interoperable format. In which case this variable + * won't have to change. Conversion would have to be done with pkcs12 + * utility, which is not MT, and hence can tolerate it. In other words + * goal is not to make this heuristic approach work in general case, + * but in one specific one, apps/pkcs12.c. + */ +int (*PKCS12_key_gen)(const char *pass, int passlen, + unsigned char *salt, int slen, + int id, int iter, int n, + unsigned char *out, + const EVP_MD *md_type) = NULL; + + /* Generate a MAC */ -int PKCS12_gen_mac(PKCS12 *p12, const char *pass, int passlen, - unsigned char *mac, unsigned int *maclen) +static int pkcs12_gen_mac(PKCS12 *p12, const char *pass, int passlen, + unsigned char *mac, unsigned int *maclen, + int (*pkcs12_key_gen)(const char *pass, int passlen, + unsigned char *salt, int slen, + int id, int iter, int n, + unsigned char *out, + const EVP_MD *md_type)) { const EVP_MD *md_type; HMAC_CTX *hmac = NULL; @@ -79,6 +110,11 @@ int PKCS12_gen_mac(PKCS12 *p12, const char *pass, int passlen, const X509_ALGOR *macalg; const ASN1_OBJECT *macoid; + if (pkcs12_key_gen == NULL) + pkcs12_key_gen = PKCS12_key_gen; + if (pkcs12_key_gen == NULL) + pkcs12_key_gen = PKCS12_key_gen_utf8; + if (!PKCS7_type_is_data(p12->authsafes)) { PKCS12err(PKCS12_F_PKCS12_GEN_MAC, PKCS12_R_CONTENT_TYPE_NOT_DATA); return 0; @@ -111,8 +147,8 @@ int PKCS12_gen_mac(PKCS12 *p12, const char *pass, int passlen, return 0; } } else - if (!PKCS12_key_gen(pass, passlen, salt, saltlen, PKCS12_MAC_ID, iter, - md_size, key, md_type)) { + if (!(*pkcs12_key_gen)(pass, passlen, salt, saltlen, PKCS12_MAC_ID, + iter, md_size, key, md_type)) { PKCS12err(PKCS12_F_PKCS12_GEN_MAC, PKCS12_R_KEY_GEN_ERROR); return 0; } @@ -128,6 +164,12 @@ int PKCS12_gen_mac(PKCS12 *p12, const char *pass, int passlen, return 1; } +int PKCS12_gen_mac(PKCS12 *p12, const char *pass, int passlen, + unsigned char *mac, unsigned int *maclen) +{ + return pkcs12_gen_mac(p12, pass, passlen, mac, maclen, NULL); +} + /* Verify the mac */ int PKCS12_verify_mac(PKCS12 *p12, const char *pass, int passlen) { @@ -139,14 +181,36 @@ int PKCS12_verify_mac(PKCS12 *p12, const char *pass, int passlen) PKCS12err(PKCS12_F_PKCS12_VERIFY_MAC, PKCS12_R_MAC_ABSENT); return 0; } - if (!PKCS12_gen_mac(p12, pass, passlen, mac, &maclen)) { + if (!pkcs12_gen_mac(p12, pass, passlen, mac, &maclen, + PKCS12_key_gen_utf8)) { PKCS12err(PKCS12_F_PKCS12_VERIFY_MAC, PKCS12_R_MAC_GENERATION_ERROR); return 0; } X509_SIG_get0(p12->mac->dinfo, NULL, &macoct); - if ((maclen != (unsigned int)ASN1_STRING_length(macoct)) - || CRYPTO_memcmp(mac, ASN1_STRING_get0_data(macoct), maclen)) + if (maclen != (unsigned int)ASN1_STRING_length(macoct)) return 0; + + if (CRYPTO_memcmp(mac, ASN1_STRING_get0_data(macoct), maclen) != 0) { + if (pass == NULL) + return 0; + /* + * In order to facilitate accessing old data retry with + * old-style broken password ... + */ + if (!pkcs12_gen_mac(p12, pass, passlen, mac, &maclen, + PKCS12_key_gen_asc)) { + PKCS12err(PKCS12_F_PKCS12_VERIFY_MAC, PKCS12_R_MAC_GENERATION_ERROR); + return 0; + } + if ((maclen != (unsigned int)ASN1_STRING_length(macoct)) + || CRYPTO_memcmp(mac, ASN1_STRING_get0_data(macoct), maclen) != 0) + return 0; + else + PKCS12_key_gen = PKCS12_key_gen_asc; + /* + * ... and if suceeded, pass it on to PKCS12_PBE_keyivgen. + */ + } return 1; } @@ -166,7 +230,11 @@ int PKCS12_set_mac(PKCS12 *p12, const char *pass, int passlen, PKCS12err(PKCS12_F_PKCS12_SET_MAC, PKCS12_R_MAC_SETUP_ERROR); return 0; } - if (!PKCS12_gen_mac(p12, pass, passlen, mac, &maclen)) { + /* + * Note that output mac is forced to UTF-8... + */ + if (!pkcs12_gen_mac(p12, pass, passlen, mac, &maclen, + PKCS12_key_gen_utf8)) { PKCS12err(PKCS12_F_PKCS12_SET_MAC, PKCS12_R_MAC_GENERATION_ERROR); return 0; } diff --git a/crypto/pkcs12/p12_utl.c b/crypto/pkcs12/p12_utl.c index 7d471f5c0d..07014786f6 100644 --- a/crypto/pkcs12/p12_utl.c +++ b/crypto/pkcs12/p12_utl.c @@ -173,11 +173,16 @@ char *OPENSSL_uni2utf8(const unsigned char *uni, int unilen) int asclen, i, j; char *asctmp; + /* string must contain an even number of bytes */ + if (unilen & 1) + return NULL; + for (asclen = 0, i = 0; i < unilen; ) { j = bmp_to_utf8(NULL, uni+i, unilen-i); /* - * falling back to OPENSSL_uni2asc makes lesser sense, it's - * done rather to maintain symmetry... + * falling back to OPENSSL_uni2asc makes lesser sense [than + * falling back to OPENSSL_asc2uni in OPENSSL_utf82uni above], + * it's done rather to maintain symmetry... */ if (j < 0) return OPENSSL_uni2asc(uni, unilen); if (j == 4) i += 4; |