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 */
NSS_LoadPublicKey(const unsigned char * certData,unsigned int certDataSize,SECKEYPublicKey ** publicKey)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
NSS_VerifyBegin(VFYContext ** ctx,SECKEYPublicKey * const * publicKey)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 */
NSS_VerifySignature(VFYContext * const * ctx,const unsigned char * signature,unsigned int signatureLen)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 */
CryptoAPI_VerifySignature(HCRYPTHASH * hash,HCRYPTKEY * pubKey,const BYTE * signature,DWORD signatureLen)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 */
CryptoAPI_LoadPublicKey(HCRYPTPROV provider,BYTE * certData,DWORD sizeOfCertData,HCRYPTKEY * publicKey)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 */
CryptoAPI_InitCryptoContext(HCRYPTPROV * provider)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 */
CryptoAPI_VerifyBegin(HCRYPTPROV provider,HCRYPTHASH * hash)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 */
CryptoAPI_VerifyUpdate(HCRYPTHASH * hash,BYTE * buf,DWORD len)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
240