blob: cad875e01553ef8b59dfeb4f1cc8ffda542fffdc [file] [log] [blame]
Gilles Peskinef0fa4362018-07-16 17:08:43 +02001/**
2 * PSA API key derivation demonstration
3 *
4 * This program calculates a key ladder: a chain of secret material, each
5 * derived from the previous one in a deterministic way based on a label.
6 * Two keys are identical if and only if they are derived from the same key
7 * using the same label.
8 *
9 * The initial key is called the master key. The master key is normally
10 * randomly generated, but it could itself be derived from another key.
11 *
12 * This program derives a series of keys called intermediate keys.
13 * The first intermediate key is derived from the master key using the
14 * first label passed on the command line. Each subsequent intermediate
15 * key is derived from the previous one using the next label passed
16 * on the command line.
17 *
18 * This program has four modes of operation:
19 *
20 * - "generate": generate a random master key.
21 * - "wrap": derive a wrapping key from the last intermediate key,
22 * and use that key to encrypt-and-authenticate some data.
23 * - "unwrap": derive a wrapping key from the last intermediate key,
24 * and use that key to decrypt-and-authenticate some
25 * ciphertext created by wrap mode.
26 * - "save": save the last intermediate key so that it can be reused as
27 * the master key in another run of the program.
28 *
29 * See the usage() output for the command line usage. See the file
30 * `key_ladder_demo.sh` for an example run.
31 */
32
Bence Szépkúti86974652020-06-15 11:59:37 +020033/*
Bence Szépkúti1e148272020-08-07 13:07:28 +020034 * Copyright The Mbed TLS Contributors
Gilles Peskinef0fa4362018-07-16 17:08:43 +020035 * SPDX-License-Identifier: Apache-2.0
36 *
37 * Licensed under the Apache License, Version 2.0 (the "License"); you may
38 * not use this file except in compliance with the License.
39 * You may obtain a copy of the License at
40 *
41 * http://www.apache.org/licenses/LICENSE-2.0
42 *
43 * Unless required by applicable law or agreed to in writing, software
44 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
45 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
46 * See the License for the specific language governing permissions and
47 * limitations under the License.
Gilles Peskinef0fa4362018-07-16 17:08:43 +020048 */
49
50/* First include Mbed TLS headers to get the Mbed TLS configuration and
51 * platform definitions that we'll use in this program. Also include
52 * standard C headers for functions we'll use here. */
Bence Szépkútic662b362021-05-27 11:25:03 +020053#include "mbedtls/build_info.h"
Gilles Peskinef0fa4362018-07-16 17:08:43 +020054
Gilles Peskinef0fa4362018-07-16 17:08:43 +020055#include <stdlib.h>
Gilles Peskinef0fa4362018-07-16 17:08:43 +020056#include <stdio.h>
57#include <string.h>
58
59#include "mbedtls/platform_util.h" // for mbedtls_platform_zeroize
60
Gilles Peskine4e2cc532019-05-29 14:30:27 +020061#include <psa/crypto.h>
62
Gilles Peskinef0fa4362018-07-16 17:08:43 +020063/* If the build options we need are not enabled, compile a placeholder. */
Ronald Cronadc2ff22020-09-16 16:49:27 +020064#if !defined(MBEDTLS_SHA256_C) || !defined(MBEDTLS_MD_C) || \
65 !defined(MBEDTLS_AES_C) || !defined(MBEDTLS_CCM_C) || \
66 !defined(MBEDTLS_PSA_CRYPTO_C) || !defined(MBEDTLS_FS_IO) || \
67 defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER)
Gilles Peskinef0fa4362018-07-16 17:08:43 +020068int main( void )
69{
Ronald Cronadc2ff22020-09-16 16:49:27 +020070 printf( "MBEDTLS_SHA256_C and/or MBEDTLS_MD_C and/or "
71 "MBEDTLS_AES_C and/or MBEDTLS_CCM_C and/or "
72 "MBEDTLS_PSA_CRYPTO_C and/or MBEDTLS_FS_IO "
73 "not defined and/or MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER "
74 "defined.\n" );
Gilles Peskinef0fa4362018-07-16 17:08:43 +020075 return( 0 );
76}
77#else
78
79/* The real program starts here. */
80
Gilles Peskinef0fa4362018-07-16 17:08:43 +020081/* Run a system function and bail out if it fails. */
82#define SYS_CHECK( expr ) \
83 do \
84 { \
85 if( ! ( expr ) ) \
86 { \
87 perror( #expr ); \
88 status = DEMO_ERROR; \
89 goto exit; \
90 } \
91 } \
92 while( 0 )
93
94/* Run a PSA function and bail out if it fails. */
95#define PSA_CHECK( expr ) \
96 do \
97 { \
98 status = ( expr ); \
99 if( status != PSA_SUCCESS ) \
100 { \
Kenneth Soerensen518d4352020-04-01 17:22:45 +0200101 printf( "Error %d at line %d: %s\n", \
Jaeden Amerofa30c332018-12-21 18:42:18 +0000102 (int) status, \
103 __LINE__, \
104 #expr ); \
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200105 goto exit; \
106 } \
107 } \
108 while( 0 )
109
110/* To report operational errors in this program, use an error code that is
111 * different from every PSA error code. */
112#define DEMO_ERROR 120
113
114/* The maximum supported key ladder depth. */
115#define MAX_LADDER_DEPTH 10
116
117/* Salt to use when deriving an intermediate key. */
118#define DERIVE_KEY_SALT ( (uint8_t *) "key_ladder_demo.derive" )
119#define DERIVE_KEY_SALT_LENGTH ( strlen( (const char*) DERIVE_KEY_SALT ) )
120
121/* Salt to use when deriving a wrapping key. */
122#define WRAPPING_KEY_SALT ( (uint8_t *) "key_ladder_demo.wrap" )
123#define WRAPPING_KEY_SALT_LENGTH ( strlen( (const char*) WRAPPING_KEY_SALT ) )
124
125/* Size of the key derivation keys (applies both to the master key and
126 * to intermediate keys). */
127#define KEY_SIZE_BYTES 40
128
129/* Algorithm for key derivation. */
130#define KDF_ALG PSA_ALG_HKDF( PSA_ALG_SHA_256 )
131
132/* Type and size of the key used to wrap data. */
133#define WRAPPING_KEY_TYPE PSA_KEY_TYPE_AES
134#define WRAPPING_KEY_BITS 128
135
136/* Cipher mode used to wrap data. */
137#define WRAPPING_ALG PSA_ALG_CCM
138
139/* Nonce size used to wrap data. */
140#define WRAPPING_IV_SIZE 13
141
142/* Header used in files containing wrapped data. We'll save this header
143 * directly without worrying about data representation issues such as
144 * integer sizes and endianness, because the data is meant to be read
145 * back by the same program on the same machine. */
146#define WRAPPED_DATA_MAGIC "key_ladder_demo" // including trailing null byte
147#define WRAPPED_DATA_MAGIC_LENGTH ( sizeof( WRAPPED_DATA_MAGIC ) )
148typedef struct
149{
150 char magic[WRAPPED_DATA_MAGIC_LENGTH];
151 size_t ad_size; /* Size of the additional data, which is this header. */
152 size_t payload_size; /* Size of the encrypted data. */
153 /* Store the IV inside the additional data. It's convenient. */
154 uint8_t iv[WRAPPING_IV_SIZE];
155} wrapped_data_header_t;
156
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200157/* The modes that this program can operate in (see usage). */
158enum program_mode
159{
160 MODE_GENERATE,
161 MODE_SAVE,
162 MODE_UNWRAP,
163 MODE_WRAP
164};
165
166/* Save a key to a file. In the real world, you may want to export a derived
167 * key sometimes, to share it with another party. */
Ronald Cronadc2ff22020-09-16 16:49:27 +0200168static psa_status_t save_key( psa_key_id_t key,
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200169 const char *output_file_name )
170{
171 psa_status_t status = PSA_SUCCESS;
172 uint8_t key_data[KEY_SIZE_BYTES];
173 size_t key_size;
174 FILE *key_file = NULL;
175
Ronald Cronadc2ff22020-09-16 16:49:27 +0200176 PSA_CHECK( psa_export_key( key,
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200177 key_data, sizeof( key_data ),
178 &key_size ) );
179 SYS_CHECK( ( key_file = fopen( output_file_name, "wb" ) ) != NULL );
180 SYS_CHECK( fwrite( key_data, 1, key_size, key_file ) == key_size );
181 SYS_CHECK( fclose( key_file ) == 0 );
182 key_file = NULL;
183
184exit:
185 if( key_file != NULL)
186 fclose( key_file );
187 return( status );
188}
189
190/* Generate a master key for use in this demo.
191 *
192 * Normally a master key would be non-exportable. For the purpose of this
193 * demo, we want to save it to a file, to avoid relying on the keystore
194 * capability of the PSA crypto library. */
195static psa_status_t generate( const char *key_file_name )
196{
197 psa_status_t status = PSA_SUCCESS;
Ronald Cronadc2ff22020-09-16 16:49:27 +0200198 psa_key_id_t key = 0;
Gilles Peskinedfea0a252019-04-18 13:39:40 +0200199 psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200200
Gilles Peskinedfea0a252019-04-18 13:39:40 +0200201 psa_set_key_usage_flags( &attributes,
202 PSA_KEY_USAGE_DERIVE | PSA_KEY_USAGE_EXPORT );
203 psa_set_key_algorithm( &attributes, KDF_ALG );
204 psa_set_key_type( &attributes, PSA_KEY_TYPE_DERIVE );
Gilles Peskine3a4f1f82019-04-26 13:49:28 +0200205 psa_set_key_bits( &attributes, PSA_BYTES_TO_BITS( KEY_SIZE_BYTES ) );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200206
Ronald Cronadc2ff22020-09-16 16:49:27 +0200207 PSA_CHECK( psa_generate_key( &attributes, &key ) );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200208
Ronald Cronadc2ff22020-09-16 16:49:27 +0200209 PSA_CHECK( save_key( key, key_file_name ) );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200210
211exit:
Ronald Cronadc2ff22020-09-16 16:49:27 +0200212 (void) psa_destroy_key( key );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200213 return( status );
214}
215
216/* Load the master key from a file.
217 *
218 * In the real world, this master key would be stored in an internal memory
219 * and the storage would be managed by the keystore capability of the PSA
220 * crypto library. */
Gilles Peskineb0edfb52018-12-03 16:24:51 +0100221static psa_status_t import_key_from_file( psa_key_usage_t usage,
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200222 psa_algorithm_t alg,
Gilles Peskineb0edfb52018-12-03 16:24:51 +0100223 const char *key_file_name,
Ronald Cronadc2ff22020-09-16 16:49:27 +0200224 psa_key_id_t *master_key )
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200225{
226 psa_status_t status = PSA_SUCCESS;
Gilles Peskinedfea0a252019-04-18 13:39:40 +0200227 psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200228 uint8_t key_data[KEY_SIZE_BYTES];
229 size_t key_size;
230 FILE *key_file = NULL;
231 unsigned char extra_byte;
232
233 SYS_CHECK( ( key_file = fopen( key_file_name, "rb" ) ) != NULL );
234 SYS_CHECK( ( key_size = fread( key_data, 1, sizeof( key_data ),
235 key_file ) ) != 0 );
236 if( fread( &extra_byte, 1, 1, key_file ) != 0 )
237 {
Jaeden Amerofa30c332018-12-21 18:42:18 +0000238 printf( "Key file too large (max: %u).\n",
239 (unsigned) sizeof( key_data ) );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200240 status = DEMO_ERROR;
241 goto exit;
242 }
243 SYS_CHECK( fclose( key_file ) == 0 );
244 key_file = NULL;
245
Gilles Peskinedfea0a252019-04-18 13:39:40 +0200246 psa_set_key_usage_flags( &attributes, usage );
247 psa_set_key_algorithm( &attributes, alg );
248 psa_set_key_type( &attributes, PSA_KEY_TYPE_DERIVE );
Ronald Cronadc2ff22020-09-16 16:49:27 +0200249 PSA_CHECK( psa_import_key( &attributes, key_data, key_size, master_key ) );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200250exit:
251 if( key_file != NULL )
252 fclose( key_file );
253 mbedtls_platform_zeroize( key_data, sizeof( key_data ) );
Gilles Peskineb0edfb52018-12-03 16:24:51 +0100254 if( status != PSA_SUCCESS )
255 {
Gilles Peskine5163a922019-05-27 14:52:34 +0200256 /* If the key creation hasn't happened yet or has failed,
Ronald Cronadc2ff22020-09-16 16:49:27 +0200257 * *master_key is null. psa_destroy_key( 0 ) is
258 * guaranteed to do nothing and return PSA_SUCCESS. */
259 (void) psa_destroy_key( *master_key );
260 *master_key = 0;
Gilles Peskineb0edfb52018-12-03 16:24:51 +0100261 }
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200262 return( status );
263}
264
265/* Derive the intermediate keys, using the list of labels provided on
Ronald Cronadc2ff22020-09-16 16:49:27 +0200266 * the command line. On input, *key is the master key identifier.
267 * This function destroys the master key. On successful output, *key
268 * is the identifier of the final derived key.
269 */
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200270static psa_status_t derive_key_ladder( const char *ladder[],
Gilles Peskineb0edfb52018-12-03 16:24:51 +0100271 size_t ladder_depth,
Ronald Cronadc2ff22020-09-16 16:49:27 +0200272 psa_key_id_t *key )
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200273{
274 psa_status_t status = PSA_SUCCESS;
Gilles Peskinedfea0a252019-04-18 13:39:40 +0200275 psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
Gilles Peskine4e2cc532019-05-29 14:30:27 +0200276 psa_key_derivation_operation_t operation = PSA_KEY_DERIVATION_OPERATION_INIT;
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200277 size_t i;
Gilles Peskinedfea0a252019-04-18 13:39:40 +0200278
279 psa_set_key_usage_flags( &attributes,
280 PSA_KEY_USAGE_DERIVE | PSA_KEY_USAGE_EXPORT );
281 psa_set_key_algorithm( &attributes, KDF_ALG );
282 psa_set_key_type( &attributes, PSA_KEY_TYPE_DERIVE );
Gilles Peskine3a4f1f82019-04-26 13:49:28 +0200283 psa_set_key_bits( &attributes, PSA_BYTES_TO_BITS( KEY_SIZE_BYTES ) );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200284
285 /* For each label in turn, ... */
286 for( i = 0; i < ladder_depth; i++ )
287 {
288 /* Start deriving material from the master key (if i=0) or from
289 * the current intermediate key (if i>0). */
Gilles Peskine4e2cc532019-05-29 14:30:27 +0200290 PSA_CHECK( psa_key_derivation_setup( &operation, KDF_ALG ) );
291 PSA_CHECK( psa_key_derivation_input_bytes(
292 &operation, PSA_KEY_DERIVATION_INPUT_SALT,
293 DERIVE_KEY_SALT, DERIVE_KEY_SALT_LENGTH ) );
294 PSA_CHECK( psa_key_derivation_input_key(
295 &operation, PSA_KEY_DERIVATION_INPUT_SECRET,
Ronald Cronadc2ff22020-09-16 16:49:27 +0200296 *key ) );
Gilles Peskine4e2cc532019-05-29 14:30:27 +0200297 PSA_CHECK( psa_key_derivation_input_bytes(
298 &operation, PSA_KEY_DERIVATION_INPUT_INFO,
299 (uint8_t*) ladder[i], strlen( ladder[i] ) ) );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200300 /* When the parent key is not the master key, destroy it,
301 * since it is no longer needed. */
Ronald Cronadc2ff22020-09-16 16:49:27 +0200302 PSA_CHECK( psa_destroy_key( *key ) );
303 *key = 0;
Gilles Peskine4e2cc532019-05-29 14:30:27 +0200304 /* Derive the next intermediate key from the parent key. */
305 PSA_CHECK( psa_key_derivation_output_key( &attributes, &operation,
Ronald Cronadc2ff22020-09-16 16:49:27 +0200306 key ) );
Gilles Peskine4e2cc532019-05-29 14:30:27 +0200307 PSA_CHECK( psa_key_derivation_abort( &operation ) );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200308 }
309
310exit:
Gilles Peskine4e2cc532019-05-29 14:30:27 +0200311 psa_key_derivation_abort( &operation );
Gilles Peskineb0edfb52018-12-03 16:24:51 +0100312 if( status != PSA_SUCCESS )
313 {
Ronald Cronadc2ff22020-09-16 16:49:27 +0200314 psa_destroy_key( *key );
315 *key = 0;
Gilles Peskineb0edfb52018-12-03 16:24:51 +0100316 }
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200317 return( status );
318}
319
320/* Derive a wrapping key from the last intermediate key. */
Gilles Peskineb0edfb52018-12-03 16:24:51 +0100321static psa_status_t derive_wrapping_key( psa_key_usage_t usage,
Ronald Cronadc2ff22020-09-16 16:49:27 +0200322 psa_key_id_t derived_key,
323 psa_key_id_t *wrapping_key )
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200324{
325 psa_status_t status = PSA_SUCCESS;
Gilles Peskinedfea0a252019-04-18 13:39:40 +0200326 psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
Gilles Peskine4e2cc532019-05-29 14:30:27 +0200327 psa_key_derivation_operation_t operation = PSA_KEY_DERIVATION_OPERATION_INIT;
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200328
Ronald Cronadc2ff22020-09-16 16:49:27 +0200329 *wrapping_key = 0;
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200330
Gilles Peskine2a38e242019-05-29 14:33:00 +0200331 /* Set up a key derivation operation from the key derived from
332 * the master key. */
Gilles Peskine4e2cc532019-05-29 14:30:27 +0200333 PSA_CHECK( psa_key_derivation_setup( &operation, KDF_ALG ) );
334 PSA_CHECK( psa_key_derivation_input_bytes(
335 &operation, PSA_KEY_DERIVATION_INPUT_SALT,
336 WRAPPING_KEY_SALT, WRAPPING_KEY_SALT_LENGTH ) );
337 PSA_CHECK( psa_key_derivation_input_key(
338 &operation, PSA_KEY_DERIVATION_INPUT_SECRET,
Ronald Cronadc2ff22020-09-16 16:49:27 +0200339 derived_key ) );
Gilles Peskine4e2cc532019-05-29 14:30:27 +0200340 PSA_CHECK( psa_key_derivation_input_bytes(
341 &operation, PSA_KEY_DERIVATION_INPUT_INFO,
342 NULL, 0 ) );
Gilles Peskine2a38e242019-05-29 14:33:00 +0200343
344 /* Create the wrapping key. */
345 psa_set_key_usage_flags( &attributes, usage );
346 psa_set_key_algorithm( &attributes, WRAPPING_ALG );
347 psa_set_key_type( &attributes, PSA_KEY_TYPE_AES );
348 psa_set_key_bits( &attributes, WRAPPING_KEY_BITS );
Gilles Peskine4e2cc532019-05-29 14:30:27 +0200349 PSA_CHECK( psa_key_derivation_output_key( &attributes, &operation,
Ronald Cronadc2ff22020-09-16 16:49:27 +0200350 wrapping_key ) );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200351
352exit:
Gilles Peskine4e2cc532019-05-29 14:30:27 +0200353 psa_key_derivation_abort( &operation );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200354 return( status );
355}
356
357static psa_status_t wrap_data( const char *input_file_name,
Gilles Peskineb0edfb52018-12-03 16:24:51 +0100358 const char *output_file_name,
Ronald Cronadc2ff22020-09-16 16:49:27 +0200359 psa_key_id_t wrapping_key )
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200360{
361 psa_status_t status;
362 FILE *input_file = NULL;
363 FILE *output_file = NULL;
Bence Szépkútiec174e22021-03-19 18:46:15 +0100364 psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
365 psa_key_type_t key_type;
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200366 long input_position;
367 size_t input_size;
Gilles Peskine5e09bc72018-12-21 12:06:15 +0100368 size_t buffer_size = 0;
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200369 unsigned char *buffer = NULL;
370 size_t ciphertext_size;
371 wrapped_data_header_t header;
372
373 /* Find the size of the data to wrap. */
374 SYS_CHECK( ( input_file = fopen( input_file_name, "rb" ) ) != NULL );
375 SYS_CHECK( fseek( input_file, 0, SEEK_END ) == 0 );
376 SYS_CHECK( ( input_position = ftell( input_file ) ) != -1 );
377#if LONG_MAX > SIZE_MAX
378 if( input_position > SIZE_MAX )
379 {
Jaeden Amerofa30c332018-12-21 18:42:18 +0000380 printf( "Input file too large.\n" );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200381 status = DEMO_ERROR;
382 goto exit;
383 }
384#endif
385 input_size = input_position;
Bence Szépkútiec174e22021-03-19 18:46:15 +0100386 PSA_CHECK( psa_get_key_attributes( wrapping_key, &attributes ) );
387 key_type = psa_get_key_type( &attributes );
388 buffer_size =
389 PSA_AEAD_ENCRYPT_OUTPUT_SIZE( key_type, WRAPPING_ALG, input_size );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200390 /* Check for integer overflow. */
391 if( buffer_size < input_size )
392 {
Jaeden Amerofa30c332018-12-21 18:42:18 +0000393 printf( "Input file too large.\n" );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200394 status = DEMO_ERROR;
395 goto exit;
396 }
397
398 /* Load the data to wrap. */
399 SYS_CHECK( fseek( input_file, 0, SEEK_SET ) == 0 );
Jaeden Amerofa30c332018-12-21 18:42:18 +0000400 SYS_CHECK( ( buffer = calloc( 1, buffer_size ) ) != NULL );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200401 SYS_CHECK( fread( buffer, 1, input_size, input_file ) == input_size );
402 SYS_CHECK( fclose( input_file ) == 0 );
403 input_file = NULL;
404
405 /* Construct a header. */
406 memcpy( &header.magic, WRAPPED_DATA_MAGIC, WRAPPED_DATA_MAGIC_LENGTH );
407 header.ad_size = sizeof( header );
408 header.payload_size = input_size;
409
410 /* Wrap the data. */
411 PSA_CHECK( psa_generate_random( header.iv, WRAPPING_IV_SIZE ) );
Ronald Cronadc2ff22020-09-16 16:49:27 +0200412 PSA_CHECK( psa_aead_encrypt( wrapping_key, WRAPPING_ALG,
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200413 header.iv, WRAPPING_IV_SIZE,
414 (uint8_t *) &header, sizeof( header ),
415 buffer, input_size,
416 buffer, buffer_size,
417 &ciphertext_size ) );
418
419 /* Write the output. */
420 SYS_CHECK( ( output_file = fopen( output_file_name, "wb" ) ) != NULL );
421 SYS_CHECK( fwrite( &header, 1, sizeof( header ),
422 output_file ) == sizeof( header ) );
423 SYS_CHECK( fwrite( buffer, 1, ciphertext_size,
424 output_file ) == ciphertext_size );
425 SYS_CHECK( fclose( output_file ) == 0 );
426 output_file = NULL;
427
428exit:
429 if( input_file != NULL )
430 fclose( input_file );
431 if( output_file != NULL )
432 fclose( output_file );
433 if( buffer != NULL )
434 mbedtls_platform_zeroize( buffer, buffer_size );
Jaeden Amerofa30c332018-12-21 18:42:18 +0000435 free( buffer );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200436 return( status );
437}
438
439static psa_status_t unwrap_data( const char *input_file_name,
Gilles Peskineb0edfb52018-12-03 16:24:51 +0100440 const char *output_file_name,
Ronald Cronadc2ff22020-09-16 16:49:27 +0200441 psa_key_id_t wrapping_key )
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200442{
443 psa_status_t status;
444 FILE *input_file = NULL;
445 FILE *output_file = NULL;
Bence Szépkútiec174e22021-03-19 18:46:15 +0100446 psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
447 psa_key_type_t key_type;
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200448 unsigned char *buffer = NULL;
Gilles Peskine5e09bc72018-12-21 12:06:15 +0100449 size_t ciphertext_size = 0;
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200450 size_t plaintext_size;
451 wrapped_data_header_t header;
452 unsigned char extra_byte;
453
454 /* Load and validate the header. */
455 SYS_CHECK( ( input_file = fopen( input_file_name, "rb" ) ) != NULL );
456 SYS_CHECK( fread( &header, 1, sizeof( header ),
457 input_file ) == sizeof( header ) );
458 if( memcmp( &header.magic, WRAPPED_DATA_MAGIC,
459 WRAPPED_DATA_MAGIC_LENGTH ) != 0 )
460 {
Jaeden Amerofa30c332018-12-21 18:42:18 +0000461 printf( "The input does not start with a valid magic header.\n" );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200462 status = DEMO_ERROR;
463 goto exit;
464 }
465 if( header.ad_size != sizeof( header ) )
466 {
Jaeden Amerofa30c332018-12-21 18:42:18 +0000467 printf( "The header size is not correct.\n" );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200468 status = DEMO_ERROR;
469 goto exit;
470 }
Bence Szépkútiec174e22021-03-19 18:46:15 +0100471 PSA_CHECK( psa_get_key_attributes( wrapping_key, &attributes) );
472 key_type = psa_get_key_type( &attributes);
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200473 ciphertext_size =
Bence Szépkútiec174e22021-03-19 18:46:15 +0100474 PSA_AEAD_ENCRYPT_OUTPUT_SIZE( key_type, WRAPPING_ALG, header.payload_size );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200475 /* Check for integer overflow. */
476 if( ciphertext_size < header.payload_size )
477 {
Jaeden Amerofa30c332018-12-21 18:42:18 +0000478 printf( "Input file too large.\n" );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200479 status = DEMO_ERROR;
480 goto exit;
481 }
482
483 /* Load the payload data. */
Jaeden Amerofa30c332018-12-21 18:42:18 +0000484 SYS_CHECK( ( buffer = calloc( 1, ciphertext_size ) ) != NULL );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200485 SYS_CHECK( fread( buffer, 1, ciphertext_size,
486 input_file ) == ciphertext_size );
487 if( fread( &extra_byte, 1, 1, input_file ) != 0 )
488 {
Jaeden Amerofa30c332018-12-21 18:42:18 +0000489 printf( "Extra garbage after ciphertext\n" );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200490 status = DEMO_ERROR;
491 goto exit;
492 }
493 SYS_CHECK( fclose( input_file ) == 0 );
494 input_file = NULL;
495
496 /* Unwrap the data. */
Ronald Cronadc2ff22020-09-16 16:49:27 +0200497 PSA_CHECK( psa_aead_decrypt( wrapping_key, WRAPPING_ALG,
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200498 header.iv, WRAPPING_IV_SIZE,
499 (uint8_t *) &header, sizeof( header ),
500 buffer, ciphertext_size,
501 buffer, ciphertext_size,
502 &plaintext_size ) );
503 if( plaintext_size != header.payload_size )
504 {
Jaeden Amerofa30c332018-12-21 18:42:18 +0000505 printf( "Incorrect payload size in the header.\n" );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200506 status = DEMO_ERROR;
507 goto exit;
508 }
509
510 /* Write the output. */
511 SYS_CHECK( ( output_file = fopen( output_file_name, "wb" ) ) != NULL );
512 SYS_CHECK( fwrite( buffer, 1, plaintext_size,
513 output_file ) == plaintext_size );
514 SYS_CHECK( fclose( output_file ) == 0 );
515 output_file = NULL;
516
517exit:
518 if( input_file != NULL )
519 fclose( input_file );
520 if( output_file != NULL )
521 fclose( output_file );
522 if( buffer != NULL )
523 mbedtls_platform_zeroize( buffer, ciphertext_size );
Jaeden Amerofa30c332018-12-21 18:42:18 +0000524 free( buffer );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200525 return( status );
526}
527
528static psa_status_t run( enum program_mode mode,
529 const char *key_file_name,
530 const char *ladder[], size_t ladder_depth,
531 const char *input_file_name,
532 const char *output_file_name )
533{
534 psa_status_t status = PSA_SUCCESS;
Ronald Cronadc2ff22020-09-16 16:49:27 +0200535 psa_key_id_t derivation_key = 0;
536 psa_key_id_t wrapping_key = 0;
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200537
538 /* Initialize the PSA crypto library. */
539 PSA_CHECK( psa_crypto_init( ) );
540
541 /* Generate mode is unlike the others. Generate the master key and exit. */
542 if( mode == MODE_GENERATE )
543 return( generate( key_file_name ) );
544
545 /* Read the master key. */
Gilles Peskineb0edfb52018-12-03 16:24:51 +0100546 PSA_CHECK( import_key_from_file( PSA_KEY_USAGE_DERIVE | PSA_KEY_USAGE_EXPORT,
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200547 KDF_ALG,
Gilles Peskineb0edfb52018-12-03 16:24:51 +0100548 key_file_name,
Ronald Cronadc2ff22020-09-16 16:49:27 +0200549 &derivation_key ) );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200550
551 /* Calculate the derived key for this session. */
Gilles Peskineb0edfb52018-12-03 16:24:51 +0100552 PSA_CHECK( derive_key_ladder( ladder, ladder_depth,
Ronald Cronadc2ff22020-09-16 16:49:27 +0200553 &derivation_key ) );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200554
555 switch( mode )
556 {
557 case MODE_SAVE:
Ronald Cronadc2ff22020-09-16 16:49:27 +0200558 PSA_CHECK( save_key( derivation_key, output_file_name ) );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200559 break;
560 case MODE_UNWRAP:
Gilles Peskineb0edfb52018-12-03 16:24:51 +0100561 PSA_CHECK( derive_wrapping_key( PSA_KEY_USAGE_DECRYPT,
Ronald Cronadc2ff22020-09-16 16:49:27 +0200562 derivation_key,
563 &wrapping_key ) );
Gilles Peskineb0edfb52018-12-03 16:24:51 +0100564 PSA_CHECK( unwrap_data( input_file_name, output_file_name,
Ronald Cronadc2ff22020-09-16 16:49:27 +0200565 wrapping_key ) );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200566 break;
567 case MODE_WRAP:
Gilles Peskineb0edfb52018-12-03 16:24:51 +0100568 PSA_CHECK( derive_wrapping_key( PSA_KEY_USAGE_ENCRYPT,
Ronald Cronadc2ff22020-09-16 16:49:27 +0200569 derivation_key,
570 &wrapping_key ) );
Gilles Peskineb0edfb52018-12-03 16:24:51 +0100571 PSA_CHECK( wrap_data( input_file_name, output_file_name,
Ronald Cronadc2ff22020-09-16 16:49:27 +0200572 wrapping_key ) );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200573 break;
574 default:
575 /* Unreachable but some compilers don't realize it. */
576 break;
577 }
578
579exit:
Ronald Cronadc2ff22020-09-16 16:49:27 +0200580 /* Destroy any remaining key. Deinitializing the crypto library would do
581 * this anyway since they are volatile keys, but explicitly destroying
Ronald Cron77c89f52020-11-10 17:45:56 +0100582 * keys makes the code easier to reuse. */
Ronald Cronadc2ff22020-09-16 16:49:27 +0200583 (void) psa_destroy_key( derivation_key );
584 (void) psa_destroy_key( wrapping_key );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200585 /* Deinitialize the PSA crypto library. */
586 mbedtls_psa_crypto_free( );
587 return( status );
588}
589
590static void usage( void )
591{
Jaeden Amerofa30c332018-12-21 18:42:18 +0000592 printf( "Usage: key_ladder_demo MODE [OPTION=VALUE]...\n" );
593 printf( "Demonstrate the usage of a key derivation ladder.\n" );
594 printf( "\n" );
595 printf( "Modes:\n" );
596 printf( " generate Generate the master key\n" );
597 printf( " save Save the derived key\n" );
598 printf( " unwrap Unwrap (decrypt) input with the derived key\n" );
599 printf( " wrap Wrap (encrypt) input with the derived key\n" );
600 printf( "\n" );
601 printf( "Options:\n" );
602 printf( " input=FILENAME Input file (required for wrap/unwrap)\n" );
603 printf( " master=FILENAME File containing the master key (default: master.key)\n" );
604 printf( " output=FILENAME Output file (required for save/wrap/unwrap)\n" );
605 printf( " label=TEXT Label for the key derivation.\n" );
606 printf( " This may be repeated multiple times.\n" );
607 printf( " To get the same key, you must use the same master key\n" );
608 printf( " and the same sequence of labels.\n" );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200609}
610
611int main( int argc, char *argv[] )
612{
Gilles Peskine738f0172019-01-02 17:25:16 +0100613 const char *key_file_name = "master.key";
614 const char *input_file_name = NULL;
615 const char *output_file_name = NULL;
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200616 const char *ladder[MAX_LADDER_DEPTH];
617 size_t ladder_depth = 0;
618 int i;
619 enum program_mode mode;
620 psa_status_t status;
621
622 if( argc <= 1 ||
623 strcmp( argv[1], "help" ) == 0 ||
624 strcmp( argv[1], "-help" ) == 0 ||
625 strcmp( argv[1], "--help" ) == 0 )
626 {
627 usage( );
Jaeden Amerofa30c332018-12-21 18:42:18 +0000628 return( EXIT_SUCCESS );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200629 }
630
631 for( i = 2; i < argc; i++ )
632 {
633 char *q = strchr( argv[i], '=' );
634 if( q == NULL )
635 {
Jaeden Amerofa30c332018-12-21 18:42:18 +0000636 printf( "Missing argument to option %s\n", argv[i] );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200637 goto usage_failure;
638 }
639 *q = 0;
640 ++q;
641 if( strcmp( argv[i], "input" ) == 0 )
642 input_file_name = q;
643 else if( strcmp( argv[i], "label" ) == 0 )
644 {
645 if( ladder_depth == MAX_LADDER_DEPTH )
646 {
Jaeden Amerofa30c332018-12-21 18:42:18 +0000647 printf( "Maximum ladder depth %u exceeded.\n",
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200648 (unsigned) MAX_LADDER_DEPTH );
Jaeden Amerofa30c332018-12-21 18:42:18 +0000649 return( EXIT_FAILURE );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200650 }
651 ladder[ladder_depth] = q;
652 ++ladder_depth;
653 }
654 else if( strcmp( argv[i], "master" ) == 0 )
655 key_file_name = q;
656 else if( strcmp( argv[i], "output" ) == 0 )
657 output_file_name = q;
658 else
659 {
Jaeden Amerofa30c332018-12-21 18:42:18 +0000660 printf( "Unknown option: %s\n", argv[i] );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200661 goto usage_failure;
662 }
663 }
664
665 if( strcmp( argv[1], "generate" ) == 0 )
666 mode = MODE_GENERATE;
667 else if( strcmp( argv[1], "save" ) == 0 )
668 mode = MODE_SAVE;
669 else if( strcmp( argv[1], "unwrap" ) == 0 )
670 mode = MODE_UNWRAP;
671 else if( strcmp( argv[1], "wrap" ) == 0 )
672 mode = MODE_WRAP;
673 else
674 {
Jaeden Amerofa30c332018-12-21 18:42:18 +0000675 printf( "Unknown action: %s\n", argv[1] );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200676 goto usage_failure;
677 }
678
679 if( input_file_name == NULL &&
680 ( mode == MODE_WRAP || mode == MODE_UNWRAP ) )
681 {
Jaeden Amerofa30c332018-12-21 18:42:18 +0000682 printf( "Required argument missing: input\n" );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200683 return( DEMO_ERROR );
684 }
685 if( output_file_name == NULL &&
686 ( mode == MODE_SAVE || mode == MODE_WRAP || mode == MODE_UNWRAP ) )
687 {
Jaeden Amerofa30c332018-12-21 18:42:18 +0000688 printf( "Required argument missing: output\n" );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200689 return( DEMO_ERROR );
690 }
691
692 status = run( mode, key_file_name,
693 ladder, ladder_depth,
694 input_file_name, output_file_name );
695 return( status == PSA_SUCCESS ?
Jaeden Amerofa30c332018-12-21 18:42:18 +0000696 EXIT_SUCCESS :
697 EXIT_FAILURE );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200698
699usage_failure:
700 usage( );
Jaeden Amerofa30c332018-12-21 18:42:18 +0000701 return( EXIT_FAILURE );
Gilles Peskinef0fa4362018-07-16 17:08:43 +0200702}
703#endif /* MBEDTLS_SHA256_C && MBEDTLS_MD_C && MBEDTLS_AES_C && MBEDTLS_CCM_C && MBEDTLS_PSA_CRYPTO_C && MBEDTLS_FS_IO */