1 /*- 2 * Copyright 2007-2021 The OpenSSL Project Authors. All Rights Reserved. 3 * Copyright Nokia 2007-2019 4 * Copyright Siemens AG 2015-2019 5 * 6 * Licensed under the Apache License 2.0 (the "License"). You may not use 7 * this file except in compliance with the License. You can obtain a copy 8 * in the file LICENSE in the source distribution or at 9 * https://www.openssl.org/source/license.html 10 * 11 * CRMF implementation by Martin Peylo, Miikka Viljanen, and David von Oheimb. 12 */ 13 14 15 #include <string.h> 16 17 #include <openssl/rand.h> 18 #include <openssl/evp.h> 19 #include <openssl/hmac.h> 20 21 /* explicit #includes not strictly needed since implied by the above: */ 22 #include <openssl/asn1t.h> 23 #include <openssl/crmf.h> 24 #include <openssl/err.h> 25 #include <openssl/evp.h> 26 #include <openssl/params.h> 27 #include <openssl/core_names.h> 28 29 #include "internal/sizes.h" 30 31 #include "crmf_local.h" 32 33 /*- 34 * creates and initializes OSSL_CRMF_PBMPARAMETER (section 4.4) 35 * |slen| SHOULD be at least 8 (16 is common) 36 * |owfnid| e.g., NID_sha256 37 * |itercnt| MUST be >= 100 (e.g., 500) and <= OSSL_CRMF_PBM_MAX_ITERATION_COUNT 38 * |macnid| e.g., NID_hmac_sha1 39 * returns pointer to OSSL_CRMF_PBMPARAMETER on success, NULL on error 40 */ 41 OSSL_CRMF_PBMPARAMETER *OSSL_CRMF_pbmp_new(OSSL_LIB_CTX *libctx, size_t slen, 42 int owfnid, size_t itercnt, 43 int macnid) 44 { 45 OSSL_CRMF_PBMPARAMETER *pbm = NULL; 46 unsigned char *salt = NULL; 47 48 if ((pbm = OSSL_CRMF_PBMPARAMETER_new()) == NULL) 49 goto err; 50 51 /* 52 * salt contains a randomly generated value used in computing the key 53 * of the MAC process. The salt SHOULD be at least 8 octets (64 54 * bits) long. 55 */ 56 if ((salt = OPENSSL_malloc(slen)) == NULL) 57 goto err; 58 if (RAND_bytes_ex(libctx, salt, slen, 0) <= 0) { 59 ERR_raise(ERR_LIB_CRMF, CRMF_R_FAILURE_OBTAINING_RANDOM); 60 goto err; 61 } 62 if (!ASN1_OCTET_STRING_set(pbm->salt, salt, (int)slen)) 63 goto err; 64 65 /* 66 * owf identifies the hash algorithm and associated parameters used to 67 * compute the key used in the MAC process. All implementations MUST 68 * support SHA-1. 69 */ 70 if (!X509_ALGOR_set0(pbm->owf, OBJ_nid2obj(owfnid), V_ASN1_UNDEF, NULL)) { 71 ERR_raise(ERR_LIB_CRMF, CRMF_R_SETTING_OWF_ALGOR_FAILURE); 72 goto err; 73 } 74 75 /* 76 * iterationCount identifies the number of times the hash is applied 77 * during the key computation process. The iterationCount MUST be a 78 * minimum of 100. Many people suggest using values as high as 1000 79 * iterations as the minimum value. The trade off here is between 80 * protection of the password from attacks and the time spent by the 81 * server processing all of the different iterations in deriving 82 * passwords. Hashing is generally considered a cheap operation but 83 * this may not be true with all hash functions in the future. 84 */ 85 if (itercnt < 100) { 86 ERR_raise(ERR_LIB_CRMF, CRMF_R_ITERATIONCOUNT_BELOW_100); 87 goto err; 88 } 89 if (itercnt > OSSL_CRMF_PBM_MAX_ITERATION_COUNT) { 90 ERR_raise(ERR_LIB_CRMF, CRMF_R_BAD_PBM_ITERATIONCOUNT); 91 goto err; 92 } 93 94 if (!ASN1_INTEGER_set(pbm->iterationCount, itercnt)) { 95 ERR_raise(ERR_LIB_CRMF, CRMF_R_CRMFERROR); 96 goto err; 97 } 98 99 /* 100 * mac identifies the algorithm and associated parameters of the MAC 101 * function to be used. All implementations MUST support HMAC-SHA1 [HMAC]. 102 * All implementations SHOULD support DES-MAC and Triple-DES-MAC [PKCS11]. 103 */ 104 if (!X509_ALGOR_set0(pbm->mac, OBJ_nid2obj(macnid), V_ASN1_UNDEF, NULL)) { 105 ERR_raise(ERR_LIB_CRMF, CRMF_R_SETTING_MAC_ALGOR_FAILURE); 106 goto err; 107 } 108 109 OPENSSL_free(salt); 110 return pbm; 111 err: 112 OPENSSL_free(salt); 113 OSSL_CRMF_PBMPARAMETER_free(pbm); 114 return NULL; 115 } 116 117 /*- 118 * calculates the PBM based on the settings of the given OSSL_CRMF_PBMPARAMETER 119 * |pbmp| identifies the algorithms, salt to use 120 * |msg| message to apply the PBM for 121 * |msglen| length of the message 122 * |sec| key to use 123 * |seclen| length of the key 124 * |out| pointer to the computed mac, will be set on success 125 * |outlen| if not NULL, will set variable to the length of the mac on success 126 * returns 1 on success, 0 on error 127 */ 128 int OSSL_CRMF_pbm_new(OSSL_LIB_CTX *libctx, const char *propq, 129 const OSSL_CRMF_PBMPARAMETER *pbmp, 130 const unsigned char *msg, size_t msglen, 131 const unsigned char *sec, size_t seclen, 132 unsigned char **out, size_t *outlen) 133 { 134 int mac_nid, hmac_md_nid = NID_undef; 135 char mdname[OSSL_MAX_NAME_SIZE]; 136 char hmac_mdname[OSSL_MAX_NAME_SIZE]; 137 EVP_MD *owf = NULL; 138 EVP_MD_CTX *ctx = NULL; 139 unsigned char basekey[EVP_MAX_MD_SIZE]; 140 unsigned int bklen = EVP_MAX_MD_SIZE; 141 int64_t iterations; 142 unsigned char *mac_res = 0; 143 int ok = 0; 144 145 if (out == NULL || pbmp == NULL || pbmp->mac == NULL 146 || pbmp->mac->algorithm == NULL || msg == NULL || sec == NULL) { 147 ERR_raise(ERR_LIB_CRMF, CRMF_R_NULL_ARGUMENT); 148 goto err; 149 } 150 if ((mac_res = OPENSSL_malloc(EVP_MAX_MD_SIZE)) == NULL) 151 goto err; 152 153 /* 154 * owf identifies the hash algorithm and associated parameters used to 155 * compute the key used in the MAC process. All implementations MUST 156 * support SHA-1. 157 */ 158 OBJ_obj2txt(mdname, sizeof(mdname), pbmp->owf->algorithm, 0); 159 if ((owf = EVP_MD_fetch(libctx, mdname, propq)) == NULL) { 160 ERR_raise(ERR_LIB_CRMF, CRMF_R_UNSUPPORTED_ALGORITHM); 161 goto err; 162 } 163 164 if ((ctx = EVP_MD_CTX_new()) == NULL) 165 goto err; 166 167 /* compute the basekey of the salted secret */ 168 if (!EVP_DigestInit_ex(ctx, owf, NULL)) 169 goto err; 170 /* first the secret */ 171 if (!EVP_DigestUpdate(ctx, sec, seclen)) 172 goto err; 173 /* then the salt */ 174 if (!EVP_DigestUpdate(ctx, pbmp->salt->data, pbmp->salt->length)) 175 goto err; 176 if (!EVP_DigestFinal_ex(ctx, basekey, &bklen)) 177 goto err; 178 if (!ASN1_INTEGER_get_int64(&iterations, pbmp->iterationCount) 179 || iterations < 100 /* min from RFC */ 180 || iterations > OSSL_CRMF_PBM_MAX_ITERATION_COUNT) { 181 ERR_raise(ERR_LIB_CRMF, CRMF_R_BAD_PBM_ITERATIONCOUNT); 182 goto err; 183 } 184 185 /* the first iteration was already done above */ 186 while (--iterations > 0) { 187 if (!EVP_DigestInit_ex(ctx, owf, NULL)) 188 goto err; 189 if (!EVP_DigestUpdate(ctx, basekey, bklen)) 190 goto err; 191 if (!EVP_DigestFinal_ex(ctx, basekey, &bklen)) 192 goto err; 193 } 194 195 /* 196 * mac identifies the algorithm and associated parameters of the MAC 197 * function to be used. All implementations MUST support HMAC-SHA1 [HMAC]. 198 * All implementations SHOULD support DES-MAC and Triple-DES-MAC [PKCS11]. 199 */ 200 mac_nid = OBJ_obj2nid(pbmp->mac->algorithm); 201 202 if (!EVP_PBE_find(EVP_PBE_TYPE_PRF, mac_nid, NULL, &hmac_md_nid, NULL) 203 || OBJ_obj2txt(hmac_mdname, sizeof(hmac_mdname), 204 OBJ_nid2obj(hmac_md_nid), 0) <= 0) { 205 ERR_raise(ERR_LIB_CRMF, CRMF_R_UNSUPPORTED_ALGORITHM); 206 goto err; 207 } 208 if (EVP_Q_mac(libctx, "HMAC", propq, hmac_mdname, NULL, basekey, bklen, 209 msg, msglen, mac_res, EVP_MAX_MD_SIZE, outlen) == NULL) 210 goto err; 211 212 ok = 1; 213 214 err: 215 OPENSSL_cleanse(basekey, bklen); 216 EVP_MD_free(owf); 217 EVP_MD_CTX_free(ctx); 218 219 if (ok == 1) { 220 *out = mac_res; 221 return 1; 222 } 223 224 OPENSSL_free(mac_res); 225 226 if (pbmp != NULL && pbmp->mac != NULL) { 227 char buf[128]; 228 229 if (OBJ_obj2txt(buf, sizeof(buf), pbmp->mac->algorithm, 0)) 230 ERR_add_error_data(1, buf); 231 } 232 return 0; 233 } 234