Fix several issues in BaseCryptLib:
[mirror_edk2.git] / CryptoPkg / Library / BaseCryptLib / Pk / CryptPkcs7Sign.c
1 /** @file\r
2   PKCS#7 SignedData Sign Wrapper Implementation over OpenSSL.\r
3 \r
4 Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>\r
5 This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution.  The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9 \r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12 \r
13 **/\r
14 \r
15 #include "InternalCryptLib.h"\r
16 \r
17 #include <openssl/objects.h>\r
18 #include <openssl/x509.h>\r
19 #include <openssl/pkcs7.h>\r
20 \r
21 \r
22 /**\r
23   Creates a PKCS#7 signedData as described in "PKCS #7: Cryptographic Message\r
24   Syntax Standard, version 1.5". This interface is only intended to be used for\r
25   application to perform PKCS#7 functionality validation.\r
26 \r
27   @param[in]  PrivateKey       Pointer to the PEM-formatted private key data for\r
28                                data signing.\r
29   @param[in]  PrivateKeySize   Size of the PEM private key data in bytes.\r
30   @param[in]  KeyPassword      NULL-terminated passphrase used for encrypted PEM\r
31                                key data.\r
32   @param[in]  InData           Pointer to the content to be signed.\r
33   @param[in]  InDataSize       Size of InData in bytes.\r
34   @param[in]  SignCert         Pointer to signer's DER-encoded certificate to sign with.\r
35   @param[in]  OtherCerts       Pointer to an optional additional set of certificates to\r
36                                include in the PKCS#7 signedData (e.g. any intermediate\r
37                                CAs in the chain).\r
38   @param[out] SignedData       Pointer to output PKCS#7 signedData.\r
39   @param[out] SignedDataSize   Size of SignedData in bytes.\r
40 \r
41   @retval     TRUE             PKCS#7 data signing succeeded.\r
42   @retval     FALSE            PKCS#7 data signing failed.\r
43 \r
44 **/\r
45 BOOLEAN\r
46 EFIAPI\r
47 Pkcs7Sign (\r
48   IN   CONST UINT8  *PrivateKey,\r
49   IN   UINTN        PrivateKeySize,\r
50   IN   CONST UINT8  *KeyPassword,\r
51   IN   UINT8        *InData,\r
52   IN   UINTN        InDataSize,\r
53   IN   UINT8        *SignCert,\r
54   IN   UINT8        *OtherCerts      OPTIONAL,\r
55   OUT  UINT8        **SignedData,\r
56   OUT  UINTN        *SignedDataSize\r
57   )\r
58 {\r
59   BOOLEAN   Status;\r
60   EVP_PKEY  *Key;\r
61   BIO       *DataBio;\r
62   PKCS7     *Pkcs7;\r
63   UINT8     *RsaContext;\r
64   UINT8     *P7Data;\r
65   UINTN     P7DataSize;\r
66   UINT8     *Tmp;\r
67 \r
68   //\r
69   // Check input parameters.\r
70   //\r
71   if (PrivateKey == NULL || KeyPassword == NULL || InData == NULL ||\r
72     SignCert == NULL || SignedData == NULL || SignedDataSize == NULL || InDataSize > INT_MAX) {\r
73     return FALSE;\r
74   }\r
75 \r
76   RsaContext = NULL;\r
77   Key        = NULL;\r
78   Pkcs7      = NULL;\r
79   DataBio    = NULL;\r
80   Status     = FALSE;\r
81 \r
82   //\r
83   // Retrieve RSA private key from PEM data.\r
84   //\r
85   Status = RsaGetPrivateKeyFromPem (\r
86              PrivateKey,\r
87              PrivateKeySize,\r
88              (CONST CHAR8 *) KeyPassword,\r
89              (VOID **) &RsaContext\r
90              );\r
91   if (!Status) {\r
92     return Status;\r
93   }\r
94 \r
95   Status = FALSE;\r
96 \r
97   //\r
98   // Register & Initialize necessary digest algorithms and PRNG for PKCS#7 Handling\r
99   //\r
100   if (EVP_add_digest (EVP_md5 ()) == 0) {\r
101     goto _Exit;\r
102   }\r
103   if (EVP_add_digest (EVP_sha1 ()) == 0) {\r
104     goto _Exit;\r
105   }\r
106   if (EVP_add_digest (EVP_sha256 ()) == 0) {\r
107     goto _Exit;\r
108   }\r
109 \r
110   RandomSeed (NULL, 0);\r
111 \r
112   //\r
113   // Construct OpenSSL EVP_PKEY for private key.\r
114   //\r
115   Key = EVP_PKEY_new ();\r
116   if (Key == NULL) {\r
117     goto _Exit;\r
118   }\r
119   Key->save_type = EVP_PKEY_RSA;\r
120   Key->type      = EVP_PKEY_type (EVP_PKEY_RSA);\r
121   Key->pkey.rsa  = (RSA *) RsaContext;\r
122 \r
123   //\r
124   // Convert the data to be signed to BIO format. \r
125   //\r
126   DataBio = BIO_new (BIO_s_mem ());\r
127   BIO_write (DataBio, InData, (int) InDataSize);\r
128 \r
129   //\r
130   // Create the PKCS#7 signedData structure.\r
131   //\r
132   Pkcs7 = PKCS7_sign (\r
133             (X509 *) SignCert,\r
134             Key,\r
135             (STACK_OF(X509) *) OtherCerts,\r
136             DataBio,\r
137             PKCS7_BINARY | PKCS7_NOATTR | PKCS7_DETACHED\r
138             );\r
139   if (Pkcs7 == NULL) {\r
140     goto _Exit;\r
141   }\r
142 \r
143   //\r
144   // Convert PKCS#7 signedData structure into DER-encoded buffer.\r
145   //\r
146   P7DataSize = i2d_PKCS7 (Pkcs7, NULL);\r
147   if (P7DataSize <= 19) {\r
148     goto _Exit;\r
149   }\r
150 \r
151   P7Data     = malloc (P7DataSize);\r
152   if (P7Data == NULL) {\r
153     goto _Exit;\r
154   }\r
155 \r
156   Tmp        = P7Data;\r
157   P7DataSize = i2d_PKCS7 (Pkcs7, (unsigned char **) &Tmp);\r
158 \r
159   //\r
160   // Strip ContentInfo to content only for signeddata. The data be trimmed off\r
161   // is totally 19 bytes.\r
162   //\r
163   *SignedDataSize = P7DataSize - 19;\r
164   *SignedData     = malloc (*SignedDataSize);\r
165   if (*SignedData == NULL) {\r
166     OPENSSL_free (P7Data);\r
167     goto _Exit;\r
168   }\r
169 \r
170   CopyMem (*SignedData, P7Data + 19, *SignedDataSize);\r
171   \r
172   OPENSSL_free (P7Data);\r
173 \r
174   Status = TRUE;\r
175 \r
176 _Exit:\r
177   //\r
178   // Release Resources\r
179   //\r
180   if (RsaContext != NULL) {\r
181     RsaFree (RsaContext);\r
182     if (Key != NULL) {\r
183       Key->pkey.rsa = NULL;\r
184     }\r
185   }\r
186 \r
187   if (Key != NULL) {\r
188     EVP_PKEY_free (Key);\r
189   }\r
190 \r
191   if (DataBio != NULL) {\r
192     BIO_free (DataBio);\r
193   }\r
194 \r
195   if (Pkcs7 != NULL) {\r
196     PKCS7_free (Pkcs7);\r
197   }\r
198 \r
199   return Status;\r
200 }\r