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 }