summaryrefslogtreecommitdiffstats
path: root/CryptoPkg/Library/BaseCryptLib/Pk/CryptPkcs1Oaep.c
blob: ea43c1381c198362f2f1fa2a08c459dca18f5c4c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
/** @file
  This file contains UEFI wrapper functions for RSA PKCS1v2 OAEP encryption routines.

  SPDX-License-Identifier: BSD-2-Clause-Patent

  Copyright (C) 2016 Microsoft Corporation. All Rights Reserved.
  Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>

**/

#include "InternalCryptLib.h"
#include <openssl/objects.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <Library/MemoryAllocationLib.h>

/**
  Encrypts a blob using PKCS1v2 (RSAES-OAEP) schema. On success, will return the
  encrypted message in a newly allocated buffer.

  Things that can cause a failure include:
  - X509 key size does not match any known key size.
  - Fail to parse X509 certificate.
  - Fail to allocate an intermediate buffer.
  - Null pointer provided for a non-optional parameter.
  - Data size is too large for the provided key size (max size is a function of key size
    and hash digest size).

  @param[in]  PublicKey           A pointer to the DER-encoded X509 certificate that
                                  will be used to encrypt the data.
  @param[in]  PublicKeySize       Size of the X509 cert buffer.
  @param[in]  InData              Data to be encrypted.
  @param[in]  InDataSize          Size of the data buffer.
  @param[in]  PrngSeed            [Optional] If provided, a pointer to a random seed buffer
                                  to be used when initializing the PRNG. NULL otherwise.
  @param[in]  PrngSeedSize        [Optional] If provided, size of the random seed buffer.
                                  0 otherwise.
  @param[out] EncryptedData       Pointer to an allocated buffer containing the encrypted
                                  message.
  @param[out] EncryptedDataSize   Size of the encrypted message buffer.

  @retval     TRUE                Encryption was successful.
  @retval     FALSE               Encryption failed.

**/
BOOLEAN
EFIAPI
Pkcs1v2Encrypt (
  IN   CONST UINT8  *PublicKey,
  IN   UINTN        PublicKeySize,
  IN   UINT8        *InData,
  IN   UINTN        InDataSize,
  IN   CONST UINT8  *PrngSeed   OPTIONAL,
  IN   UINTN        PrngSeedSize   OPTIONAL,
  OUT  UINT8        **EncryptedData,
  OUT  UINTN        *EncryptedDataSize
  )
{
  BOOLEAN       Result;
  CONST UINT8   *TempPointer;
  X509          *CertData;
  EVP_PKEY      *InternalPublicKey;
  EVP_PKEY_CTX  *PkeyCtx;
  UINT8         *OutData;
  UINTN         OutDataSize;

  //
  // Check input parameters.
  //
  if ((PublicKey == NULL) || (InData == NULL) ||
      (EncryptedData == NULL) || (EncryptedDataSize == NULL))
  {
    return FALSE;
  }

  //
  // Check public key size.
  //
  if (PublicKeySize > 0xFFFFFFFF) {
    //
    // Public key size is too large for implementation.
    //
    return FALSE;
  }

  *EncryptedData     = NULL;
  *EncryptedDataSize = 0;
  Result             = FALSE;
  TempPointer        = NULL;
  CertData           = NULL;
  InternalPublicKey  = NULL;
  PkeyCtx            = NULL;
  OutData            = NULL;
  OutDataSize        = 0;

  //
  // If it provides a seed then use it.
  // Ohterwise, we'll seed with fixed values and hope that the PRNG has already been
  // used enough to generate sufficient entropy.
  //
  if (PrngSeed != NULL) {
    RandomSeed (PrngSeed, PrngSeedSize);
  } else {
    RandomSeed (NULL, 0);
  }

  //
  // Parse the X509 cert and extract the public key.
  //
  TempPointer = PublicKey;
  CertData    = d2i_X509 (&CertData, &TempPointer, (UINT32)PublicKeySize);
  if (CertData == NULL) {
    //
    // Fail to parse X509 cert.
    //
    goto _Exit;
  }

  //
  // Extract the public key from the x509 cert in a format that
  // OpenSSL can use.
  //
  InternalPublicKey = X509_get_pubkey (CertData);
  if (InternalPublicKey == NULL) {
    //
    // Fail to extract public key.
    //
    goto _Exit;
  }

  //
  // Create a context for the public key operation.
  //
  PkeyCtx = EVP_PKEY_CTX_new (InternalPublicKey, NULL);
  if (PkeyCtx == NULL) {
    //
    // Fail to create contex.
    //
    goto _Exit;
  }

  //
  // Initialize the context and set the desired padding.
  //
  if ((EVP_PKEY_encrypt_init (PkeyCtx) <= 0) ||
      (EVP_PKEY_CTX_set_rsa_padding (PkeyCtx, RSA_PKCS1_OAEP_PADDING) <= 0))
  {
    //
    // Fail to initialize the context.
    //
    goto _Exit;
  }

  //
  // Determine the required buffer length for malloc'ing.
  //
  if (EVP_PKEY_encrypt (PkeyCtx, NULL, &OutDataSize, InData, InDataSize) <= 0) {
    //
    // Fail to determine output buffer size.
    //
    goto _Exit;
  }

  //
  // Allocate a buffer for the output data.
  //
  OutData = AllocatePool (OutDataSize);
  if (OutData == NULL) {
    //
    // Fail to allocate the output buffer.
    //
    goto _Exit;
  }

  //
  // Encrypt Data.
  //
  if (EVP_PKEY_encrypt (PkeyCtx, OutData, &OutDataSize, InData, InDataSize) <= 0) {
    //
    // Fail to encrypt data, need to free the output buffer.
    //
    FreePool (OutData);
    OutData     = NULL;
    OutDataSize = 0;
    goto _Exit;
  }

  //
  // Encrypt done.
  //
  *EncryptedData     = OutData;
  *EncryptedDataSize = OutDataSize;
  Result             = TRUE;

_Exit:
  //
  // Release Resources
  //
  if (CertData != NULL) {
    X509_free (CertData);
  }

  if (InternalPublicKey != NULL) {
    EVP_PKEY_free (InternalPublicKey);
  }

  if (PkeyCtx != NULL) {
    EVP_PKEY_CTX_free (PkeyCtx);
  }

  return Result;
}