1 /*
2 * FreeRTOS V202212.00
3 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of
6 * this software and associated documentation files (the "Software"), to deal in
7 * the Software without restriction, including without limitation the rights to
8 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 * the Software, and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * https://www.FreeRTOS.org
23 * https://github.com/FreeRTOS
24 *
25 */
26
27 /* FreeRTOS includes. */
28 #include "FreeRTOS.h"
29 #include "task.h"
30
31 /* Standard include. */
32 #include "stdio.h"
33
34 /* PKCS #11 includes. */
35 #include "core_pkcs11_config.h"
36 #include "core_pkcs11.h"
37 #include "pkcs11.h"
38 #include "core_pki_utils.h"
39
40 /* Demo includes. */
41 #include "demo_helpers.h"
42 #include "pkcs11_demos.h"
43
44 /**
45 * This function details how to use the PKCS #11 "Sign and Verify" functions to
46 * create and interact with digital signatures.
47 * The functions described are all defined in
48 * http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/os/pkcs11-base-v2.40-os.html
49 * please consult the standard for more information regarding these functions.
50 *
51 * The standard has grouped the functions presented in this demo as:
52 * Object Management Functions
53 * Signing and MACing Functions
54 */
vPKCS11SignVerifyDemo(void)55 void vPKCS11SignVerifyDemo( void )
56 {
57 /* This demo will use the generated private and public key from the
58 * "objects.c" demo and use them to sign and verify the integrity of a
59 * message digest. This demo will use concepts from all the other demos,
60 * and is recommended be done last.
61 *
62 * The intention of this demo is how to use PKCS #11's Crypotki API to do
63 * these signature operations, not to explain when and why they should be
64 * used. For a deeper understanding of that please read:
65 * https://en.wikipedia.org/wiki/Public_key_infrastructure
66 * https://en.wikipedia.org/wiki/Transport_Layer_Security
67 * https://en.wikipedia.org/wiki/Digital_signature
68 */
69 configPRINTF( ( "\r\nStarting PKCS #11 Sign and Verify Demo.\r\n" ) );
70
71 /* Helper / previously explained variables. */
72 CK_RV xResult = CKR_OK;
73 CK_SESSION_HANDLE hSession = CK_INVALID_HANDLE;
74 CK_SLOT_ID * pxSlotId = NULL;
75 CK_ULONG ulSlotCount = 0;
76 CK_ULONG ulIndex = 0;
77 CK_OBJECT_HANDLE xPrivateKeyHandle = CK_INVALID_HANDLE;
78 CK_OBJECT_HANDLE xPublicKeyHandle = CK_INVALID_HANDLE;
79 CK_FUNCTION_LIST_PTR pxFunctionList = NULL;
80 CK_BYTE * pxDerPublicKey = NULL;
81 CK_ULONG ulDerPublicKeyLength = 0;
82
83 /* Digest variables. See "mechanisms_and_digests" for an explanation. */
84 CK_BYTE pxKnownMessage[] = { "Hello world" };
85 CK_BYTE xDigestResult[ pkcs11SHA256_DIGEST_LENGTH ] = { 0 };
86 CK_ULONG ulDigestLength = pkcs11SHA256_DIGEST_LENGTH;
87 CK_MECHANISM xDigestMechanism = { 0 };
88
89 /* Signing variables. */
90 /* The ECDSA mechanism will be used to sign the message digest. */
91 CK_MECHANISM xMechanism = { CKM_ECDSA, NULL, 0 };
92
93 /* This signature buffer will be used to store the signature created by the
94 * private key. (64 bytes). We pad it with an extra 8 bytes so it can be
95 * converted to an ASN.1 encoding. */
96 CK_BYTE xSignature[ pkcs11ECDSA_P256_SIGNATURE_LENGTH + 8 ] = { 0 };
97 CK_ULONG ulSignatureLength = sizeof( xSignature );
98 size_t xSignatureLength = 0U;
99
100 /* Ensure the Cryptoki library has the necessary functions implemented. */
101 xResult = C_GetFunctionList( &pxFunctionList );
102 configASSERT( xResult == CKR_OK );
103 configASSERT( pxFunctionList->C_SignInit != NULL );
104 configASSERT( pxFunctionList->C_Sign != NULL );
105 configASSERT( pxFunctionList->C_FindObjectsInit != NULL );
106 configASSERT( pxFunctionList->C_FindObjects != NULL );
107 configASSERT( pxFunctionList->C_FindObjectsFinal != NULL );
108 configASSERT( pxFunctionList->C_Login != NULL );
109 configASSERT( pxFunctionList->C_InitToken != NULL );
110 configASSERT( pxFunctionList->C_GetTokenInfo != NULL );
111
112 /* Instead of using the vStart helper, we will use the "core_pkcs11.h"
113 * functions that help wrap around some common PKCS #11 use cases.
114 *
115 * This function will:
116 * Initialize the PKCS #11 module if it is not already.
117 * Initialize a PKCS #11 session.
118 */
119 xResult = xInitializePkcs11Session( &hSession );
120 configASSERT( xResult == CKR_OK );
121 configASSERT( hSession != CK_INVALID_HANDLE );
122
123 /* This function will:
124 * Initialize the PKCS #11 module if it is not already.
125 * Initialize the token to be used.
126 *
127 * Note: By default this function will always initialize the token in the
128 * first slot in the slot list. If it desired to use a different slot, it
129 * is necessary to modify the implementation of this function to use a
130 * different slot. */
131 xResult = xInitializePkcs11Token();
132 configASSERT( xResult == CKR_OK );
133
134 /* This function will:
135 * Query the Cryptoki library for the total number of slots. Malloc an array
136 * of slots. Then the pxSlotId and ulSlotCount variables will be updated to
137 * point to the slot array, and the total slot count.
138 */
139 xResult = xGetSlotList( &pxSlotId, &ulSlotCount );
140 configASSERT( xResult == CKR_OK );
141 configASSERT( ulSlotCount != 0 );
142 configASSERT( pxSlotId != NULL );
143
144 /***************************** Find Objects *****************************/
145
146 /* This function will:
147 * Find an object, given it's label.
148 *
149 * This is done using the FindObjects group of functions defined as
150 * "Object Management Functions" in PKCS #11.
151 *
152 * This will acquire the object handle for the private key created in the
153 * "objects.c" demo.
154 */
155 xResult = xFindObjectWithLabelAndClass( hSession,
156 pkcs11configLABEL_DEVICE_PRIVATE_KEY_FOR_TLS,
157 sizeof( pkcs11configLABEL_DEVICE_PRIVATE_KEY_FOR_TLS ) - 1UL,
158 CKO_PRIVATE_KEY,
159 &xPrivateKeyHandle );
160 configASSERT( xResult == CKR_OK );
161 configASSERT( xPrivateKeyHandle != CK_INVALID_HANDLE );
162
163 /* Acquire the object handle for the public key created in the "objects.c"
164 * demo. */
165 xResult = xFindObjectWithLabelAndClass( hSession,
166 pkcs11configLABEL_DEVICE_PUBLIC_KEY_FOR_TLS,
167 sizeof( pkcs11configLABEL_DEVICE_PUBLIC_KEY_FOR_TLS ) - 1UL,
168 CKO_PUBLIC_KEY,
169 &xPublicKeyHandle );
170 configASSERT( xResult == CKR_OK );
171 configASSERT( xPublicKeyHandle != CK_INVALID_HANDLE );
172
173 /***************************** Buffer Digest *****************************/
174 xDigestMechanism.mechanism = CKM_SHA256;
175
176 /* Initializes the digest operation and sets what mechanism will be used
177 * for the digest. */
178 xResult = pxFunctionList->C_DigestInit( hSession,
179 &xDigestMechanism );
180 configASSERT( CKR_OK == xResult );
181
182 /* Pass a pointer to the buffer of bytes to be hashed, and it's size. */
183 xResult = pxFunctionList->C_DigestUpdate( hSession,
184 pxKnownMessage,
185 /* Strip NULL Terminator. */
186 sizeof( pxKnownMessage ) - 1 );
187 configASSERT( CKR_OK == xResult );
188
189 /* Retrieve the digest buffer length. When passing in a NULL pointer as the
190 * second argument, instead of a point to a buffer, this will signal the
191 * Cryptoki library to fill the third parameter with the required amount of
192 * bytes to store the resulting digest.
193 */
194 xResult = pxFunctionList->C_DigestFinal( hSession,
195 NULL,
196 &ulDigestLength );
197 configASSERT( CKR_OK == xResult );
198
199 /* Since the length of a SHA-256 digest is known, we made an assumption and
200 * allocated the buffer originally with the known length. Assert to make sure
201 * we queried the length we expected. */
202 configASSERT( pkcs11SHA256_DIGEST_LENGTH == ulDigestLength );
203
204 /* Now that ulDigestLength contains the required byte length, retrieve the
205 * digest buffer.
206 */
207 xResult = pxFunctionList->C_DigestFinal( hSession,
208 xDigestResult,
209 &ulDigestLength );
210 configASSERT( CKR_OK == xResult );
211
212 /********************************* Sign **********************************/
213
214 configPRINTF( ( "Signing known message:\r\n %s\r\n",
215 ( char * ) pxKnownMessage ) );
216
217 /* Initializes the sign operation and sets what mechanism will be used
218 * for signing the message digest. Specify what object handle to use for this
219 * operation, in this case the private key object handle. */
220 xResult = pxFunctionList->C_SignInit( hSession,
221 &xMechanism,
222 xPrivateKeyHandle );
223 configASSERT( xResult == CKR_OK );
224
225 /* Sign the message digest that was created with the C_Digest series of
226 * functions. A signature will be created using the private key specified in
227 * C_SignInit and put in the byte buffer xSignature. */
228 xResult = pxFunctionList->C_Sign( hSession,
229 xDigestResult,
230 pkcs11SHA256_DIGEST_LENGTH,
231 xSignature,
232 &ulSignatureLength );
233 configASSERT( xResult == CKR_OK );
234 configASSERT( ulSignatureLength == pkcs11ECDSA_P256_SIGNATURE_LENGTH );
235
236
237 /********************************* Verify **********************************/
238
239 /* Verify the signature created by C_Sign. First we will verify that the
240 * same Cryptoki library was able to trust itself.
241 *
242 * C_VerifyInit will begin the verify operation, by specifying what mechanism
243 * to use (CKM_ECDSA, the same as the sign operation) and then specifying
244 * which public key handle to use.
245 */
246 xResult = pxFunctionList->C_VerifyInit( hSession,
247 &xMechanism,
248 xPublicKeyHandle );
249 configASSERT( xResult == CKR_OK );
250
251 /* Given the signature and it's length, the Cryptoki will use the public key
252 * to verify that the signature was created by the corresponding private key.
253 * If C_Verify returns CKR_OK, it means that the sender of the message has
254 * the same private key as the private key that was used to generate the
255 * public key, and we can trust that the message we received was from that
256 * sender.
257 *
258 * Note that we are not using the actual message, but the digest that we
259 * created earlier of the message, for the verification.
260 */
261 xResult = pxFunctionList->C_Verify( hSession,
262 xDigestResult,
263 pkcs11SHA256_DIGEST_LENGTH,
264 xSignature,
265 ulSignatureLength );
266
267 if( xResult == CKR_OK )
268 {
269 configPRINTF( ( "The signature of the digest was verified with the" \
270 " public key and can be trusted.\r\n" ) );
271 }
272 else
273 {
274 configPRINTF( ( "Unable to verify the signature with the given public" \
275 " key, the message cannot be trusted.\r\n" ) );
276 }
277
278 /* Export public key as hex bytes and print the hex representation of the
279 * public key.
280 *
281 * We need to export the public key so that it can be used by a different
282 * device to verify messages signed by the private key of the device that
283 * generated the key pair.
284 *
285 * To do this, we will output the hex representation of the public key.
286 * Then create an empty text file called "DevicePublicKeyAsciiHex.txt".
287 *
288 * Copy and paste the hex value of the public key into this text file.
289 *
290 * Then we will need to convert the text file to binary using the xxd tool.
291 *
292 * xxd will take a text file that contains hex data and output a binary of
293 * the hex in the file. See "$ man xxd" for more information about xxd.
294 *
295 * Copy the below command into the terminal.
296 * "$ xxd -r -ps DevicePublicKeyAsciiHex.txt DevicePublicKeyDer.bin"
297 *
298 * Now that we have the binary encoding of the public key, we will convert
299 * it to PEM using OpenSSL.
300 *
301 * The following command will create a PEM file of the public key called
302 * "public_key.pem"
303 *
304 * "$ openssl ec -inform der -in DevicePublicKeyDer.bin -pubin -pubout -outform pem -out public_key.pem"
305 *
306 * Now we can use the extracted public key to verify the signature of the
307 * device's private key.
308 *
309 * WARNING: Running the object generation demo will create a new key pair,
310 * and make it necessary to redo these steps!
311 *
312 */
313 configPRINTF( ( "Verifying with public key.\r\n" ) );
314 vExportPublicKey( hSession,
315 xPublicKeyHandle,
316 &pxDerPublicKey,
317 &ulDerPublicKeyLength );
318 vWriteHexBytesToConsole( "Public Key in Hex Format",
319 pxDerPublicKey,
320 ulDerPublicKeyLength );
321
322 /* This utility function converts the PKCS #11 signature into an ASN.1
323 * encoded binary der signature. This is necessary so we can export the
324 * signature and verify it with OpenSSL, otherwise OpenSSL will not be able
325 * to parse the buffer.
326 *
327 * See https://en.wikipedia.org/wiki/ASN.1 for more information about the
328 * ASN.1 encoding format.
329 */
330 xSignatureLength = ulSignatureLength;
331 PKI_pkcs11SignatureTombedTLSSignature( xSignature, &xSignatureLength );
332
333
334 /* The following loop will output the signature in hex.
335 *
336 * In order to get the signature exported in binary form copy the output
337 * of the loop, and paste it to an empty text file.
338 *
339 * Then we will need to convert the text file to binary using the xxd tool.
340 *
341 * The following commands outline this process.
342 * Write buffer to signature.txt
343 * xxd will take a text file that contains hex data and output a binary of
344 * the hex in the file. See "$ man xxd" for more information about xxd.
345 *
346 * Copy the below command into the terminal.
347 * "$ xxd -r -ps signature.txt signature.bin"
348 *
349 * Next, we need to copy the original message that the Cryptoki library
350 * signed, the following shell command will create the message without any
351 * newlines, so the messages are similar.
352 *
353 * The contents of the echo command can be replaced with whatever data was
354 * in the known message, but the example uses "Hello world" to make it easier
355 * for copy and pasting.
356 *
357 * "$ echo -n "Hello world" > msg.txt"
358 *
359 * Now we will use OpenSSL to verify that the signature we created can be
360 * trusted by another device using the public key we created and then
361 * extracted earlier.
362 *
363 * "$ openssl dgst -sha256 -verify public_key.pem -signature signature.bin msg.txt"
364 * This command should output "Verified OK" and we then know we can trust
365 * the sender of the message!
366 */
367 configPRINTF( ( "Created signature: \r\n" ) );
368
369 for( ulIndex = 0; ulIndex < ulSignatureLength; ulIndex++ )
370 {
371 configPRINTF( ( "%02x", xSignature[ ulIndex ] ) );
372 }
373
374 configPRINTF( ( "\r\n" ) );
375
376 configPRINTF( ( "Finished PKCS #11 Sign and Verify Demo.\r\n" ) );
377 }
378