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