cryptox.c (8034B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #ifdef XP_WIN 6 # ifndef WIN32_LEAN_AND_MEAN 7 # define WIN32_LEAN_AND_MEAN 8 # endif 9 #endif 10 11 #include <stdlib.h> 12 #include <stdio.h> 13 #include "cryptox.h" 14 15 #if defined(MAR_NSS) 16 17 /** 18 * Loads the public key for the specified cert name from the NSS store. 19 * 20 * @param certData The DER-encoded X509 certificate to extract the key from. 21 * @param certDataSize The size of certData. 22 * @param publicKey Out parameter for the public key to use. 23 * @return CryptoX_Success on success, CryptoX_Error on error. 24 */ 25 CryptoX_Result NSS_LoadPublicKey(const unsigned char* certData, 26 unsigned int certDataSize, 27 SECKEYPublicKey** publicKey) { 28 CERTCertificate* cert; 29 SECItem certDataItem = {siBuffer, (unsigned char*)certData, certDataSize}; 30 31 if (!certData || !publicKey) { 32 return CryptoX_Error; 33 } 34 35 cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &certDataItem, NULL, 36 PR_FALSE, PR_TRUE); 37 /* Get the cert and embedded public key out of the database */ 38 if (!cert) { 39 return CryptoX_Error; 40 } 41 *publicKey = CERT_ExtractPublicKey(cert); 42 CERT_DestroyCertificate(cert); 43 44 if (!*publicKey) { 45 return CryptoX_Error; 46 } 47 return CryptoX_Success; 48 } 49 50 CryptoX_Result NSS_VerifyBegin(VFYContext** ctx, 51 SECKEYPublicKey* const* publicKey) { 52 SECStatus status; 53 if (!ctx || !publicKey || !*publicKey) { 54 return CryptoX_Error; 55 } 56 57 /* Check that the key length is large enough for our requirements */ 58 if ((SECKEY_PublicKeyStrength(*publicKey) * 8) < 59 XP_MIN_SIGNATURE_LEN_IN_BYTES) { 60 fprintf(stderr, "ERROR: Key length must be >= %d bytes\n", 61 XP_MIN_SIGNATURE_LEN_IN_BYTES); 62 return CryptoX_Error; 63 } 64 65 *ctx = VFY_CreateContext(*publicKey, NULL, 66 SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION, NULL); 67 if (*ctx == NULL) { 68 return CryptoX_Error; 69 } 70 71 status = VFY_Begin(*ctx); 72 return SECSuccess == status ? CryptoX_Success : CryptoX_Error; 73 } 74 75 /** 76 * Verifies if a verify context matches the passed in signature. 77 * 78 * @param ctx The verify context that the signature should match. 79 * @param signature The signature to match. 80 * @param signatureLen The length of the signature. 81 * @return CryptoX_Success on success, CryptoX_Error on error. 82 */ 83 CryptoX_Result NSS_VerifySignature(VFYContext* const* ctx, 84 const unsigned char* signature, 85 unsigned int signatureLen) { 86 SECItem signedItem; 87 SECStatus status; 88 if (!ctx || !signature || !*ctx) { 89 return CryptoX_Error; 90 } 91 92 signedItem.len = signatureLen; 93 signedItem.data = (unsigned char*)signature; 94 status = VFY_EndWithSignature(*ctx, &signedItem); 95 return SECSuccess == status ? CryptoX_Success : CryptoX_Error; 96 } 97 98 #elif defined(XP_WIN) 99 /** 100 * Verifies if a signature + public key matches a hash context. 101 * 102 * @param hash The hash context that the signature should match. 103 * @param pubKey The public key to use on the signature. 104 * @param signature The signature to check. 105 * @param signatureLen The length of the signature. 106 * @return CryptoX_Success on success, CryptoX_Error on error. 107 */ 108 CryptoX_Result CryptoAPI_VerifySignature(HCRYPTHASH* hash, HCRYPTKEY* pubKey, 109 const BYTE* signature, 110 DWORD signatureLen) { 111 DWORD i; 112 BOOL result; 113 /* Windows APIs expect the bytes in the signature to be in little-endian 114 * order, but we write the signature in big-endian order. Other APIs like 115 * NSS and OpenSSL expect big-endian order. 116 */ 117 BYTE* signatureReversed; 118 if (!hash || !pubKey || !signature || signatureLen < 1) { 119 return CryptoX_Error; 120 } 121 122 signatureReversed = malloc(signatureLen); 123 if (!signatureReversed) { 124 return CryptoX_Error; 125 } 126 127 for (i = 0; i < signatureLen; i++) { 128 signatureReversed[i] = signature[signatureLen - 1 - i]; 129 } 130 result = CryptVerifySignature(*hash, signatureReversed, signatureLen, *pubKey, 131 NULL, 0); 132 free(signatureReversed); 133 return result ? CryptoX_Success : CryptoX_Error; 134 } 135 136 /** 137 * Obtains the public key for the passed in cert data 138 * 139 * @param provider The cyrto provider 140 * @param certData Data of the certificate to extract the public key from 141 * @param sizeOfCertData The size of the certData buffer 142 * @param certStore Pointer to the handle of the certificate store to use 143 * @param CryptoX_Success on success 144 */ 145 CryptoX_Result CryptoAPI_LoadPublicKey(HCRYPTPROV provider, BYTE* certData, 146 DWORD sizeOfCertData, 147 HCRYPTKEY* publicKey) { 148 CRYPT_DATA_BLOB blob; 149 CERT_CONTEXT* context; 150 if (!provider || !certData || !publicKey) { 151 return CryptoX_Error; 152 } 153 154 blob.cbData = sizeOfCertData; 155 blob.pbData = certData; 156 if (!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob, 157 CERT_QUERY_CONTENT_FLAG_CERT, 158 CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL, 159 NULL, NULL, (const void**)&context)) { 160 return CryptoX_Error; 161 } 162 163 if (!CryptImportPublicKeyInfo( 164 provider, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 165 &context->pCertInfo->SubjectPublicKeyInfo, publicKey)) { 166 CertFreeCertificateContext(context); 167 return CryptoX_Error; 168 } 169 170 CertFreeCertificateContext(context); 171 return CryptoX_Success; 172 } 173 174 /* Try to acquire context in this way: 175 * 1. Enhanced provider without creating a new key set 176 * 2. Enhanced provider with creating a new key set 177 * 3. Default provider without creating a new key set 178 * 4. Default provider without creating a new key set 179 * #2 and #4 should not be needed because of the CRYPT_VERIFYCONTEXT, 180 * but we add it just in case. 181 * 182 * @param provider Out parameter containing the provider handle. 183 * @return CryptoX_Success on success, CryptoX_Error on error. 184 */ 185 CryptoX_Result CryptoAPI_InitCryptoContext(HCRYPTPROV* provider) { 186 if (!CryptAcquireContext(provider, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, 187 CRYPT_VERIFYCONTEXT)) { 188 if (!CryptAcquireContext(provider, NULL, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, 189 CRYPT_NEWKEYSET | CRYPT_VERIFYCONTEXT)) { 190 if (!CryptAcquireContext(provider, NULL, NULL, PROV_RSA_AES, 191 CRYPT_VERIFYCONTEXT)) { 192 if (!CryptAcquireContext(provider, NULL, NULL, PROV_RSA_AES, 193 CRYPT_NEWKEYSET | CRYPT_VERIFYCONTEXT)) { 194 *provider = CryptoX_InvalidHandleValue; 195 return CryptoX_Error; 196 } 197 } 198 } 199 } 200 return CryptoX_Success; 201 } 202 203 /** 204 * Begins a signature verification hash context 205 * 206 * @param provider The crypt provider to use 207 * @param hash Out parameter for a handle to the hash context 208 * @return CryptoX_Success on success, CryptoX_Error on error. 209 */ 210 CryptoX_Result CryptoAPI_VerifyBegin(HCRYPTPROV provider, HCRYPTHASH* hash) { 211 BOOL result; 212 if (!provider || !hash) { 213 return CryptoX_Error; 214 } 215 216 *hash = (HCRYPTHASH)NULL; 217 result = CryptCreateHash(provider, CALG_SHA_384, 0, 0, hash); 218 return result ? CryptoX_Success : CryptoX_Error; 219 } 220 221 /** 222 * Updates a signature verification hash context 223 * 224 * @param hash The hash context to udpate 225 * @param buf The buffer to update the hash context with 226 * @param len The size of the passed in buffer 227 * @return CryptoX_Success on success, CryptoX_Error on error. 228 */ 229 CryptoX_Result CryptoAPI_VerifyUpdate(HCRYPTHASH* hash, BYTE* buf, DWORD len) { 230 BOOL result; 231 if (!hash || !buf) { 232 return CryptoX_Error; 233 } 234 235 result = CryptHashData(*hash, buf, len, 0); 236 return result ? CryptoX_Success : CryptoX_Error; 237 } 238 239 #endif