• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *    http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "x509_cert_cms_generator_openssl.h"
17 
18 #include "cf_blob.h"
19 #include "config.h"
20 #include "cf_log.h"
21 #include "cf_memory.h"
22 #include "utils.h"
23 #include "cf_result.h"
24 #include "certificate_openssl_common.h"
25 #include "fwk_class.h"
26 #include "certificate_openssl_class.h"
27 #include <openssl/bio.h>
28 #include <openssl/err.h>
29 #include <openssl/evp.h>
30 #include <openssl/decoder.h>
31 #include <openssl/encoder.h>
32 #include <openssl/pem.h>
33 #include <openssl/cms.h>
34 
35 #define MAX_SIGNER_NUM 20
36 #define MAX_CERT_NUM 60
37 
38 #define ERR_PRIVATE_KEY_PASSWORD_PKCS8 0x11800074
39 #define ERR_PRIVATE_KEY_PASSWORD_PKCS1 0x4800065
40 
41 typedef struct {
42     HcfCmsGeneratorSpi base;
43     CMS_ContentInfo *cms;
44 } HcfCmsGeneratorOpensslImpl;
45 
46 #define X509_CERT_CMS_GENERATOR_OPENSSL_CLASS "X509CertCmsGeneratorOpensslClass"
47 
GetCmsGeneratorClass(void)48 static const char *GetCmsGeneratorClass(void)
49 {
50     return X509_CERT_CMS_GENERATOR_OPENSSL_CLASS;
51 }
52 
DestroyCmsGenerator(CfObjectBase * self)53 static void DestroyCmsGenerator(CfObjectBase *self)
54 {
55     if (self == NULL) {
56         LOGE("Invalid params!");
57         return;
58     }
59     if (!CfIsClassMatch(self, GetCmsGeneratorClass())) {
60         LOGE("Class is not match.");
61         return;
62     }
63     HcfCmsGeneratorOpensslImpl *impl = (HcfCmsGeneratorOpensslImpl *)self;
64     if (impl->cms != NULL) {
65         CMS_ContentInfo_free(impl->cms);
66     }
67     CfFree(impl);
68 }
69 
GetHashDigest(const char * mdName,const EVP_MD ** md)70 static CfResult GetHashDigest(const char *mdName, const EVP_MD **md)
71 {
72     if (mdName == NULL) {
73         LOGE("Invalid params, mdName is null!");
74         return CF_INVALID_PARAMS;
75     }
76     if (strcmp(mdName, "SHA1") == 0) {
77         *md = EVP_sha1();
78     } else if (strcmp(mdName, "SHA256") == 0) {
79         *md = EVP_sha256();
80     } else if (strcmp(mdName, "SHA384") == 0) {
81         *md = EVP_sha384();
82     } else if (strcmp(mdName, "SHA512") == 0) {
83         *md = EVP_sha512();
84     } else {
85         LOGE("Invalid digest algorithm.");
86         return CF_INVALID_PARAMS;
87     }
88 
89     return CF_SUCCESS;
90 }
91 
GetSignerFlags(const HcfCmsSignerOptions * options,unsigned int * tflags)92 static void GetSignerFlags(const HcfCmsSignerOptions *options, unsigned int *tflags)
93 {
94     if (!(options->addCert)) {
95         *tflags |= SMIME_NOCERTS;
96     }
97     if (!(options->addAttr)) {
98         *tflags |= SMIME_NOATTR;
99     }
100     if (!(options->addSmimeCapAttr)) {
101         *tflags |= CMS_NOSMIMECAP;
102     }
103 }
104 
IsInvalidPrivateKeyPassword(const PrivateKeyInfo * privateKey)105 static CfResult IsInvalidPrivateKeyPassword(const PrivateKeyInfo *privateKey)
106 {
107     unsigned long err = ERR_peek_last_error();
108     if ((err == ERR_PRIVATE_KEY_PASSWORD_PKCS8 || err == ERR_PRIVATE_KEY_PASSWORD_PKCS1)
109         && privateKey->privateKeyPassword != NULL) {
110         return CF_ERR_CERT_INVALID_PRIVATE_KEY;
111     }
112     return CF_ERR_CRYPTO_OPERATION;
113 }
114 
ConvertPemToKey(const PrivateKeyInfo * privateKey,EVP_PKEY ** pkey)115 static CfResult ConvertPemToKey(const PrivateKeyInfo *privateKey, EVP_PKEY **pkey)
116 {
117     BIO *bio = BIO_new(BIO_s_mem());
118     if (bio == NULL) {
119         LOGE("Failed to init bio.");
120         CfPrintOpensslError();
121         return CF_ERR_CRYPTO_OPERATION;
122     }
123     if (BIO_write(bio, privateKey->privateKey->data, privateKey->privateKey->len) <= 0) {
124         BIO_free(bio);
125         LOGE("Failed to write pem private key to bio");
126         CfPrintOpensslError();
127         return CF_ERR_CRYPTO_OPERATION;
128     }
129     EVP_PKEY *pkeyRet = NULL;
130     const char *priPassword = privateKey->privateKeyPassword;
131     if (priPassword == NULL) {
132         priPassword = "";
133     }
134     ERR_clear_error();
135     pkeyRet = PEM_read_bio_PrivateKey(bio, pkey, NULL, (char *)priPassword);
136     BIO_free(bio);
137     if (pkeyRet == NULL) {
138         LOGE("Failed to read private key from bio");
139         CfResult ret = IsInvalidPrivateKeyPassword(privateKey);
140         CfPrintOpensslError();
141         return ret;
142     }
143     return CF_SUCCESS;
144 }
145 
ConvertDerToKey(const PrivateKeyInfo * privateKey,EVP_PKEY ** pkey)146 static CfResult ConvertDerToKey(const PrivateKeyInfo *privateKey, EVP_PKEY **pkey)
147 {
148     OSSL_DECODER_CTX *dctx = OSSL_DECODER_CTX_new_for_pkey(pkey, "DER", NULL, "RSA", EVP_PKEY_KEYPAIR, NULL, NULL);
149     if (dctx == NULL) {
150         LOGE("Failed to init decoder context.");
151         CfPrintOpensslError();
152         return CF_ERR_CRYPTO_OPERATION;
153     }
154     const char *priPassword = privateKey->privateKeyPassword;
155     if (priPassword == NULL) {
156         priPassword = "";
157     }
158 
159     if (OSSL_DECODER_CTX_set_passphrase(dctx, (const unsigned char *)priPassword, strlen(priPassword)) != 1) {
160         LOGE("Failed to set passphrase.");
161         CfPrintOpensslError();
162         OSSL_DECODER_CTX_free(dctx);
163         EVP_PKEY_free(*pkey);
164         return CF_ERR_CRYPTO_OPERATION;
165     }
166     const unsigned char *pdata = privateKey->privateKey->data;
167     size_t pdataLen = privateKey->privateKey->len;
168     ERR_clear_error();
169     if (OSSL_DECODER_from_data(dctx, &pdata, &pdataLen) != 1) {
170         LOGE("Failed to decode private key.");
171         CfResult ret = IsInvalidPrivateKeyPassword(privateKey);
172         CfPrintOpensslError();
173         OSSL_DECODER_CTX_free(dctx);
174         EVP_PKEY_free(*pkey);
175         return ret;
176     }
177     OSSL_DECODER_CTX_free(dctx);
178     return CF_SUCCESS;
179 }
180 
GetPrivateKey(const PrivateKeyInfo * privateKey,EVP_PKEY ** pkey)181 static CfResult GetPrivateKey(const PrivateKeyInfo *privateKey, EVP_PKEY **pkey)
182 {
183     if (privateKey->privateKey->encodingFormat == CF_FORMAT_PEM) {
184         return ConvertPemToKey(privateKey, pkey);
185     } else if (privateKey->privateKey->encodingFormat == CF_FORMAT_DER) {
186         return ConvertDerToKey(privateKey, pkey);
187     }
188     return CF_INVALID_PARAMS;
189 }
190 
CheckSignerParam(HcfCmsGeneratorSpi * self,const HcfCertificate * x509Cert,const PrivateKeyInfo * privateKey,const HcfCmsSignerOptions * options)191 static CfResult CheckSignerParam(HcfCmsGeneratorSpi *self, const HcfCertificate *x509Cert,
192                                  const PrivateKeyInfo *privateKey, const HcfCmsSignerOptions *options)
193 {
194     if ((self == NULL) || (x509Cert == NULL) || (privateKey == NULL) || (options == NULL)) {
195         LOGE("Invalid input parameter.");
196         return CF_INVALID_PARAMS;
197     }
198     if (!CfIsClassMatch((CfObjectBase *)self, GetCmsGeneratorClass())) {
199         LOGE("Class is not match.");
200         return CF_INVALID_PARAMS;
201     }
202     HcfCmsGeneratorOpensslImpl *impl = (HcfCmsGeneratorOpensslImpl *)self;
203     if (impl->cms == NULL) {
204         LOGE("Cms_ContentInfo is NULL.");
205         return CF_ERR_CRYPTO_OPERATION;
206     }
207 
208     int numSigners = CMS_get0_SignerInfos(impl->cms) ? sk_CMS_SignerInfo_num(CMS_get0_SignerInfos(impl->cms)) : 0;
209     if (numSigners >= MAX_SIGNER_NUM) {
210         LOGE("Maximum number of signers (20) exceeded.");
211         return CF_ERR_CRYPTO_OPERATION;
212     }
213     return CF_SUCCESS;
214 }
215 
AddSignerOpenssl(HcfCmsGeneratorSpi * self,const HcfCertificate * x509Cert,const PrivateKeyInfo * privateKey,const HcfCmsSignerOptions * options)216 static CfResult AddSignerOpenssl(HcfCmsGeneratorSpi *self, const HcfCertificate *x509Cert,
217                                  const PrivateKeyInfo *privateKey, const HcfCmsSignerOptions *options)
218 {
219     CfResult ret = CheckSignerParam(self, x509Cert, privateKey, options);
220     if (ret != CF_SUCCESS) {
221         return ret;
222     }
223     HcfCmsGeneratorOpensslImpl *impl = (HcfCmsGeneratorOpensslImpl *)self;
224     HcfX509CertificateImpl *CertImpl = (HcfX509CertificateImpl *)x509Cert;
225     if (!CfIsClassMatch((CfObjectBase *)(CertImpl->spiObj), X509_CERT_OPENSSL_CLASS)) {
226         LOGE("Input wrong openssl class type!");
227         return CF_INVALID_PARAMS;
228     }
229     HcfOpensslX509Cert *realCert = (HcfOpensslX509Cert *)(CertImpl->spiObj);
230     X509 *x509 = realCert->x509;
231     if (x509 == NULL) {
232         LOGE("x509 is NULL.");
233         return CF_ERR_CRYPTO_OPERATION;
234     }
235     const EVP_MD *md = NULL;
236     ret = GetHashDigest(options->mdName, &md);
237     if (ret != CF_SUCCESS) {
238         return ret;
239     }
240 
241     unsigned int tflags = 0;
242     GetSignerFlags(options, &tflags);
243 
244     EVP_PKEY *pkey = NULL;
245     ret = GetPrivateKey(privateKey, &pkey);
246     if (ret != CF_SUCCESS || pkey == NULL) {
247         return ret;
248     }
249     // Add RSA key type check
250     if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA) {
251         LOGE("Only RSA keys are supported.");
252         EVP_PKEY_free(pkey);
253         return CF_NOT_SUPPORT;
254     }
255     if (!CMS_add1_signer(impl->cms, x509, pkey, md, tflags)) {
256         LOGE("CMS_add1_signer fail.");
257         CfPrintOpensslError();
258         EVP_PKEY_free(pkey);
259         return CF_ERR_CRYPTO_OPERATION;
260     }
261     EVP_PKEY_free(pkey);
262     return CF_SUCCESS;
263 }
264 
GetX509FromCertificate(const HcfCertificate * cert)265 static X509 *GetX509FromCertificate(const HcfCertificate *cert)
266 {
267     if (!CfIsClassMatch((CfObjectBase *)cert, HCF_X509_CERTIFICATE_CLASS)) {
268         LOGE("Input wrong openssl class type!");
269         return NULL;
270     }
271     HcfX509CertificateImpl *impl = (HcfX509CertificateImpl *)cert;
272     if (!CfIsClassMatch((CfObjectBase *)(impl->spiObj), X509_CERT_OPENSSL_CLASS)) {
273         LOGE("Input wrong openssl class type!");
274         return NULL;
275     }
276     HcfOpensslX509Cert *realCert = (HcfOpensslX509Cert *)(impl->spiObj);
277     return realCert->x509;
278 }
279 
AddCertOpenssl(HcfCmsGeneratorSpi * self,const HcfCertificate * cert)280 static CfResult AddCertOpenssl(HcfCmsGeneratorSpi *self, const HcfCertificate *cert)
281 {
282     if ((self == NULL) || (cert == NULL)) {
283         LOGE("Invalid params!");
284         return CF_INVALID_PARAMS;  // Changed from false to proper error code
285     }
286     if (!CfIsClassMatch((CfObjectBase *)self, GetCmsGeneratorClass())) {
287         LOGE("Input wrong class type!");
288         return CF_INVALID_PARAMS;  // Changed from false to proper error code
289     }
290 
291     HcfCmsGeneratorOpensslImpl *impl = (HcfCmsGeneratorOpensslImpl *)self;
292     if (impl->cms == NULL) {
293         LOGE("impl->cms is NULL.");
294         return CF_INVALID_PARAMS;
295     }
296 
297     // Add check for maximum number of certificates
298     STACK_OF(X509) *certs = CMS_get1_certs(impl->cms);
299     int numCerts = certs ? sk_X509_num(certs) : 0;
300     sk_X509_pop_free(certs, X509_free);  // Free the cert stack after counting
301     if (numCerts >= MAX_CERT_NUM) {
302         LOGE("Maximum number of certificates (60) exceeded.");
303         return CF_ERR_CRYPTO_OPERATION;
304     }
305 
306     X509 *certOpenssl = GetX509FromCertificate(cert);
307     if (certOpenssl == NULL) {
308         LOGE("Input Cert is wrong!");
309         return CF_INVALID_PARAMS;  // Changed from false to proper error code
310     }
311 
312     if (!CMS_add1_cert(impl->cms, certOpenssl)) {
313         LOGE("CMS_add1_cert fail.");
314         CfPrintOpensslError();
315         return CF_ERR_CRYPTO_OPERATION;
316     }
317     return CF_SUCCESS;
318 }
319 
WriteBioToCms(CMS_ContentInfo * cms,const HcfCmsGeneratorOptions * options,CfBlob * out)320 static CfResult WriteBioToCms(CMS_ContentInfo *cms, const HcfCmsGeneratorOptions *options, CfBlob *out)
321 {
322     BIO *outBio = BIO_new(BIO_s_mem());
323     if (outBio == NULL) {
324         LOGE("BIO_new error");
325         return CF_ERR_MALLOC;
326     }
327     if (options->outFormat == CMS_PEM) {
328         if (!PEM_write_bio_CMS(outBio, cms)) {
329             LOGE("PEM_write_bio_CMS fail.");
330             CfPrintOpensslError();
331             BIO_free(outBio);
332             return CF_ERR_CRYPTO_OPERATION;
333         }
334     } else if (options->outFormat == CMS_DER) {
335         if (!i2d_CMS_bio(outBio, cms)) {
336             LOGE("i2d_CMS_bio fail.");
337             CfPrintOpensslError();
338             BIO_free(outBio);
339             return CF_ERR_CRYPTO_OPERATION;
340         }
341     } else {
342         LOGE("Invalid outFormat.");
343         BIO_free(outBio);
344         return CF_INVALID_PARAMS;
345     }
346     BUF_MEM *bufMem = NULL;
347     if (BIO_get_mem_ptr(outBio, &bufMem) <= 0 || bufMem == NULL) {
348         LOGE("BIO_get_mem_ptr fail.");
349         BIO_free(outBio);
350         return CF_ERR_CRYPTO_OPERATION;
351     }
352     CfResult res = DeepCopyDataToOut(bufMem->data, bufMem->length, out);
353     BIO_free(outBio);
354     return res;
355 }
356 
DoFinalOpenssl(HcfCmsGeneratorSpi * self,const CfBlob * content,const HcfCmsGeneratorOptions * options,CfBlob * out)357 static CfResult DoFinalOpenssl(HcfCmsGeneratorSpi *self, const CfBlob *content, const HcfCmsGeneratorOptions *options,
358                                CfBlob *out)
359 {
360     if ((self == NULL) || (content == NULL) || (out == NULL) || (options == NULL)) {
361         LOGE("Invalid params!");
362         return CF_INVALID_PARAMS;
363     }
364     if (!CfIsClassMatch((CfObjectBase *)self, GetCmsGeneratorClass())) {
365         LOGE("Class is not match.");
366         return CF_INVALID_PARAMS;
367     }
368     HcfCmsGeneratorOpensslImpl *impl = (HcfCmsGeneratorOpensslImpl *)self;
369     if (impl->cms == NULL) {
370         LOGE("impl->cms is NULL.");
371         return CF_INVALID_PARAMS;
372     }
373     BIO *bio = BIO_new(BIO_s_mem());
374     if (bio == NULL) {
375         LOGE("Failed to new memory for bio.");
376         return CF_INVALID_PARAMS;
377     }
378     if (BIO_write(bio, content->data, content->size) <= 0) {
379         BIO_free(bio);
380         LOGE("Failed to write content to bio");
381         CfPrintOpensslError();
382         return CF_ERR_CRYPTO_OPERATION;
383     }
384     unsigned int flags = 0;
385     if (options->dataFormat == BINARY) {
386         flags |= SMIME_BINARY;
387     } else if (options->dataFormat == TEXT) {
388         flags |= SMIME_TEXT;
389     } else {
390         LOGE("Invalid dataFormat.");
391         BIO_free(bio);
392         return CF_INVALID_PARAMS;
393     }
394     int detached = options->isDetachedContent ? true : false;
395     if (detached) {
396         CMS_set_detached(impl->cms, detached);
397     }
398     int ret = CMS_final(impl->cms, bio, NULL, flags);
399     BIO_free(bio);
400     if (ret != 1) {
401         LOGE("CMS_final fail.");
402         CfPrintOpensslError();
403         return CF_ERR_CRYPTO_OPERATION;
404     }
405     return WriteBioToCms(impl->cms, options, out);
406 }
407 
HcfCmsGeneratorSpiCreate(HcfCmsContentType type,HcfCmsGeneratorSpi ** spi)408 CfResult HcfCmsGeneratorSpiCreate(HcfCmsContentType type, HcfCmsGeneratorSpi **spi)
409 {
410     if (spi == NULL) {
411         LOGE("Invalid params, spi is null!");
412         return CF_INVALID_PARAMS;
413     }
414     if (type != SIGNED_DATA) {
415         LOGE("Invalid params,type is not supported!");
416         return CF_INVALID_PARAMS;
417     }
418     HcfCmsGeneratorOpensslImpl *cmsGenerator =
419         (HcfCmsGeneratorOpensslImpl *)CfMalloc(sizeof(HcfCmsGeneratorOpensslImpl), 0);
420     if (cmsGenerator == NULL) {
421         LOGE("Failed to allocate cmsGenerator memory!");
422         return CF_ERR_MALLOC;
423     }
424     CMS_ContentInfo *cms = NULL;
425     cms = CMS_sign_ex(NULL, NULL, NULL, NULL, CMS_PARTIAL, NULL, NULL);
426     if (cms == NULL) {
427         LOGE("Cms_sign_ex fail.");
428         CfPrintOpensslError();
429         CfFree(cmsGenerator);
430         cmsGenerator = NULL;
431         return CF_ERR_CRYPTO_OPERATION;
432     }
433     cmsGenerator->cms = cms;
434     cmsGenerator->base.base.getClass = GetCmsGeneratorClass;
435     cmsGenerator->base.base.destroy = DestroyCmsGenerator;
436     cmsGenerator->base.engineAddSigner = AddSignerOpenssl;
437     cmsGenerator->base.engineAddCert = AddCertOpenssl;
438     cmsGenerator->base.engineDoFinal = DoFinalOpenssl;
439     *spi = (HcfCmsGeneratorSpi *)cmsGenerator;
440     return CF_SUCCESS;
441 }