diff --git a/programs/.gitignore b/programs/.gitignore index 550239eff6bb..44e904a95434 100644 --- a/programs/.gitignore +++ b/programs/.gitignore @@ -13,8 +13,10 @@ *.exe aes/crypt_and_hash +cipher/cipher_aead_demo hash/generic_sum hash/hello +hash/md_hmac_demo hash/md5sum hash/sha1sum hash/sha2sum @@ -38,7 +40,9 @@ pkey/rsa_sign pkey/rsa_sign_pss pkey/rsa_verify pkey/rsa_verify_pss +psa/aead_demo psa/crypto_examples +psa/hmac_demo psa/key_ladder_demo psa/psa_constant_names random/gen_entropy diff --git a/programs/CMakeLists.txt b/programs/CMakeLists.txt index a8492c6199f0..0633aa6499e2 100644 --- a/programs/CMakeLists.txt +++ b/programs/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(aes) +add_subdirectory(cipher) if (NOT WIN32) add_subdirectory(fuzz) endif() diff --git a/programs/Makefile b/programs/Makefile index 1ebf8d241e46..fdfece72ac63 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -80,8 +80,10 @@ endif ## make sure to check that it still works if you tweak the format here. APPS = \ aes/crypt_and_hash \ + cipher/cipher_aead_demo \ hash/generic_sum \ hash/hello \ + hash/md_hmac_demo \ pkey/dh_client \ pkey/dh_genprime \ pkey/dh_server \ @@ -102,7 +104,9 @@ APPS = \ pkey/rsa_sign_pss \ pkey/rsa_verify \ pkey/rsa_verify_pss \ + psa/aead_demo \ psa/crypto_examples \ + psa/hmac_demo \ psa/key_ladder_demo \ psa/psa_constant_names \ random/gen_entropy \ @@ -195,14 +199,22 @@ aes/crypt_and_hash$(EXEXT): aes/crypt_and_hash.c $(DEP) echo " CC aes/crypt_and_hash.c" $(CC) $(LOCAL_CFLAGS) $(CFLAGS) aes/crypt_and_hash.c $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ -hash/hello$(EXEXT): hash/hello.c $(DEP) - echo " CC hash/hello.c" - $(CC) $(LOCAL_CFLAGS) $(CFLAGS) hash/hello.c $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ +cipher/cipher_aead_demo$(EXEXT): cipher/cipher_aead_demo.c $(DEP) + echo " CC cipher/cipher_aead_demo.c" + $(CC) $(LOCAL_CFLAGS) $(CFLAGS) cipher/cipher_aead_demo.c $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ hash/generic_sum$(EXEXT): hash/generic_sum.c $(DEP) echo " CC hash/generic_sum.c" $(CC) $(LOCAL_CFLAGS) $(CFLAGS) hash/generic_sum.c $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ +hash/hello$(EXEXT): hash/hello.c $(DEP) + echo " CC hash/hello.c" + $(CC) $(LOCAL_CFLAGS) $(CFLAGS) hash/hello.c $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ + +hash/md_hmac_demo$(EXEXT): hash/md_hmac_demo.c $(DEP) + echo " CC hash/md_hmac_demo.c" + $(CC) $(LOCAL_CFLAGS) $(CFLAGS) hash/md_hmac_demo.c $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ + pkey/dh_client$(EXEXT): pkey/dh_client.c $(DEP) echo " CC pkey/dh_client.c" $(CC) $(LOCAL_CFLAGS) $(CFLAGS) pkey/dh_client.c $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ @@ -283,6 +295,18 @@ pkey/rsa_encrypt$(EXEXT): pkey/rsa_encrypt.c $(DEP) echo " CC pkey/rsa_encrypt.c" $(CC) $(LOCAL_CFLAGS) $(CFLAGS) pkey/rsa_encrypt.c $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ +psa/aead_demo$(EXEXT): psa/aead_demo.c $(DEP) + echo " CC psa/aead_demo.c" + $(CC) $(LOCAL_CFLAGS) $(CFLAGS) psa/aead_demo.c $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ + +psa/crypto_examples$(EXEXT): psa/crypto_examples.c $(DEP) + echo " CC psa/crypto_examples.c" + $(CC) $(LOCAL_CFLAGS) $(CFLAGS) psa/crypto_examples.c $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ + +psa/hmac_demo$(EXEXT): psa/hmac_demo.c $(DEP) + echo " CC psa/hmac_demo.c" + $(CC) $(LOCAL_CFLAGS) $(CFLAGS) psa/hmac_demo.c $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ + psa/key_ladder_demo$(EXEXT): psa/key_ladder_demo.c $(DEP) echo " CC psa/key_ladder_demo.c" $(CC) $(LOCAL_CFLAGS) $(CFLAGS) psa/key_ladder_demo.c $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ @@ -427,10 +451,6 @@ x509/req_app$(EXEXT): x509/req_app.c $(DEP) echo " CC x509/req_app.c" $(CC) $(LOCAL_CFLAGS) $(CFLAGS) x509/req_app.c $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ -psa/crypto_examples$(EXEXT): psa/crypto_examples.c $(DEP) - echo " CC psa/crypto_examples.c" - $(CC) $(LOCAL_CFLAGS) $(CFLAGS) psa/crypto_examples.c $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ - clean: ifndef WINDOWS rm -f $(EXES) diff --git a/programs/cipher/CMakeLists.txt b/programs/cipher/CMakeLists.txt new file mode 100644 index 000000000000..93e5f31ee8a5 --- /dev/null +++ b/programs/cipher/CMakeLists.txt @@ -0,0 +1,13 @@ +set(executables + cipher_aead_demo +) + +foreach(exe IN LISTS executables) + add_executable(${exe} ${exe}.c $) + target_link_libraries(${exe} ${mbedcrypto_target}) + target_include_directories(${exe} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../tests/include) +endforeach() + +install(TARGETS ${executables} + DESTINATION "bin" + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) diff --git a/programs/cipher/cipher_aead_demo.c b/programs/cipher/cipher_aead_demo.c new file mode 100644 index 000000000000..18bd66c81242 --- /dev/null +++ b/programs/cipher/cipher_aead_demo.c @@ -0,0 +1,271 @@ +/** + * Cipher API multi-part AEAD demonstration. + * + * This program AEAD-encrypts a message, using the algorithm and key size + * specified on the command line, using the multi-part API. + * + * It comes with a companion program psa/aead_demo.c, which does the same + * operations with the PSA Crypto API. The goal is that comparing the two + * programs will help people migrating to the PSA Crypto API. + * + * When used with multi-part AEAD operations, the `mbedtls_cipher_context` + * serves a triple purpose (1) hold the key, (2) store the algorithm when no + * operation is active, and (3) save progress information for the current + * operation. With PSA those roles are held by disinct objects: (1) a + * psa_key_id_t to hold the key, a (2) psa_algorithm_t to represent the + * algorithm, and (3) a psa_operation_t for multi-part progress. + * + * On the other hand, with PSA, the algorithms encodes the desired tag length; + * with Cipher the desired tag length needs to be tracked separately. + * + * This program and its companion psa/aead_demo.c illustrate this by doing the + * same sequence of multi-part AEAD computation with both APIs; looking at the + * two side by side should make the differences and similarities clear. + */ + +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* First include Mbed TLS headers to get the Mbed TLS configuration and + * platform definitions that we'll use in this program. Also include + * standard C headers for functions we'll use here. */ +#include "mbedtls/build_info.h" + +#include "mbedtls/cipher.h" + +#include +#include +#include + +/* If the build options we need are not enabled, compile a placeholder. */ +#if !defined(MBEDTLS_CIPHER_C) || \ + !defined(MBEDTLS_AES_C) || !defined(MBEDTLS_GCM_C) || \ + !defined(MBEDTLS_CHACHAPOLY_C) +int main( void ) +{ + printf( "MBEDTLS_MD_C and/or " + "MBEDTLS_AES_C and/or MBEDTLS_GCM_C and/or " + "MBEDTLS_CHACHAPOLY_C not defined\r\n" ); + return( 0 ); +} +#else + +/* The real program starts here. */ + +const char usage[] = +"Usage: cipher_aead_demo [aes128-gcm|aes256-gcm|aes128-gcm_8|chachapoly]"; + +/* Dummy data for encryption: IV/nonce, additional data, 2-part message */ +const unsigned char iv1[12] = { 0x00 }; +const unsigned char add_data1[] = { 0x01, 0x02 }; +const unsigned char msg1_part1[] = { 0x03, 0x04 }; +const unsigned char msg1_part2[] = { 0x05, 0x06, 0x07 }; + +/* Dummy data (2nd message) */ +const unsigned char iv2[12] = { 0x10 }; +const unsigned char add_data2[] = { 0x11, 0x12 }; +const unsigned char msg2_part1[] = { 0x13, 0x14 }; +const unsigned char msg2_part2[] = { 0x15, 0x16, 0x17 }; + +/* Maximum total size of the messages */ +#define MSG1_SIZE ( sizeof( msg1_part1 ) + sizeof( msg1_part2 ) ) +#define MSG2_SIZE ( sizeof( msg2_part1 ) + sizeof( msg2_part2 ) ) +#define MSG_MAX_SIZE ( MSG1_SIZE > MSG2_SIZE ? MSG1_SIZE : MSG2_SIZE ) + +/* Dummy key material - never do this in production! + * 32-byte is enough to all the key size supported by this program. */ +const unsigned char key_bytes[32] = { 0x2a }; + +/* Print the contents of a buffer in hex */ +void print_buf( const char *title, unsigned char *buf, size_t len ) +{ + printf( "%s:", title ); + for( size_t i = 0; i < len; i++ ) + printf( " %02x", buf[i] ); + printf( "\n" ); +} + +/* Run an Mbed TLS function and bail out if it fails. + * A string description of the error code can be recovered with: + * programs/util/strerror */ +#define CHK( expr ) \ + do \ + { \ + ret = ( expr ); \ + if( ret != 0 ) \ + { \ + printf( "Error %d at line %d: %s\n", \ + ret, \ + __LINE__, \ + #expr ); \ + goto exit; \ + } \ + } while( 0 ) + +/* + * Prepare encryption material: + * - interpret command-line argument + * - set up key + * - outputs: context and tag length, which together hold all the information + */ +static int aead_prepare( const char *info, + mbedtls_cipher_context_t *ctx, + size_t *tag_len ) +{ + int ret; + + /* Convert arg to type + tag_len */ + mbedtls_cipher_type_t type; + if( strcmp( info, "aes128-gcm" ) == 0 ) { + type = MBEDTLS_CIPHER_AES_128_GCM; + *tag_len = 16; + } else if( strcmp( info, "aes256-gcm" ) == 0 ) { + type = MBEDTLS_CIPHER_AES_256_GCM; + *tag_len = 16; + } else if( strcmp( info, "aes128-gcm_8" ) == 0 ) { + type = MBEDTLS_CIPHER_AES_128_GCM; + *tag_len = 8; + } else if( strcmp( info, "chachapoly" ) == 0 ) { + type = MBEDTLS_CIPHER_CHACHA20_POLY1305; + *tag_len = 16; + } else { + puts( usage ); + return( MBEDTLS_ERR_CIPHER_BAD_INPUT_DATA ); + } + + /* Prepare context for the given type */ + CHK( mbedtls_cipher_setup( ctx, + mbedtls_cipher_info_from_type( type ) ) ); + + /* Import key */ + int key_len = mbedtls_cipher_get_key_bitlen( ctx ); + CHK( mbedtls_cipher_setkey( ctx, key_bytes, key_len, MBEDTLS_ENCRYPT ) ); + +exit: + return( ret ); +} + +/* + * Print out some information. + * + * All of this information was present in the command line argument, but his + * function demonstrates how each piece can be recovered from (ctx, tag_len). + */ +static void aead_info( const mbedtls_cipher_context_t *ctx, size_t tag_len ) +{ + mbedtls_cipher_type_t type = mbedtls_cipher_get_type( ctx ); + const mbedtls_cipher_info_t *info = mbedtls_cipher_info_from_type( type ); + const char *ciph = mbedtls_cipher_info_get_name( info ); + int key_bits = mbedtls_cipher_get_key_bitlen( ctx ); + mbedtls_cipher_mode_t mode = mbedtls_cipher_get_cipher_mode( ctx ); + + const char *mode_str = mode == MBEDTLS_MODE_GCM ? "GCM" + : mode == MBEDTLS_MODE_CHACHAPOLY ? "ChachaPoly" + : "???"; + + printf( "%s, %d, %s, %u\n", + ciph, key_bits, mode_str, (unsigned) tag_len ); +} + +/* + * Encrypt a 2-part message. + */ +static int aead_encrypt( mbedtls_cipher_context_t *ctx, size_t tag_len, + const unsigned char *iv, size_t iv_len, + const unsigned char *ad, size_t ad_len, + const unsigned char *part1, size_t part1_len, + const unsigned char *part2, size_t part2_len ) +{ + int ret; + size_t olen; +#define MAX_TAG_LENGTH 16 + unsigned char out[MSG_MAX_SIZE + MAX_TAG_LENGTH]; + unsigned char *p = out; + + CHK( mbedtls_cipher_set_iv( ctx, iv, iv_len ) ); + CHK( mbedtls_cipher_reset( ctx ) ); + CHK( mbedtls_cipher_update_ad( ctx, ad, ad_len ) ); + CHK( mbedtls_cipher_update( ctx, part1, part1_len, p, &olen ) ); + p += olen; + CHK( mbedtls_cipher_update( ctx, part2, part2_len, p, &olen ) ); + p += olen; + CHK( mbedtls_cipher_finish( ctx, p, &olen ) ); + p += olen; + CHK( mbedtls_cipher_write_tag( ctx, p, tag_len ) ); + p += tag_len; + + olen = p - out; + print_buf( "out", out, olen ); + +exit: + return( ret ); +} + +/* + * AEAD demo: set up key/alg, print out info, encrypt messages. + */ +static int aead_demo( const char *info ) +{ + int ret = 0; + + mbedtls_cipher_context_t ctx; + size_t tag_len; + + mbedtls_cipher_init( &ctx ); + + CHK( aead_prepare( info, &ctx, &tag_len ) ); + + aead_info( &ctx, tag_len ); + + CHK( aead_encrypt( &ctx, tag_len, + iv1, sizeof( iv1 ), add_data1, sizeof( add_data1 ), + msg1_part1, sizeof( msg1_part1 ), + msg1_part2, sizeof( msg1_part2 ) ) ); + CHK( aead_encrypt( &ctx, tag_len, + iv2, sizeof( iv2 ), add_data2, sizeof( add_data2 ), + msg2_part1, sizeof( msg2_part1 ), + msg2_part2, sizeof( msg2_part2 ) ) ); + +exit: + mbedtls_cipher_free( &ctx ); + + return( ret ); +} + + +/* + * Main function + */ +int main( int argc, char **argv ) +{ + /* Check usage */ + if( argc != 2 ) + { + puts( usage ); + return( 1 ); + } + + int ret; + + /* Run the demo */ + CHK( aead_demo( argv[1] ) ); + +exit: + return( ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE ); +} + +#endif diff --git a/programs/hash/CMakeLists.txt b/programs/hash/CMakeLists.txt index 729474c0321b..da981884436b 100644 --- a/programs/hash/CMakeLists.txt +++ b/programs/hash/CMakeLists.txt @@ -1,6 +1,7 @@ set(executables generic_sum hello + md_hmac_demo ) foreach(exe IN LISTS executables) diff --git a/programs/hash/md_hmac_demo.c b/programs/hash/md_hmac_demo.c new file mode 100644 index 000000000000..d4cc3ccd4c37 --- /dev/null +++ b/programs/hash/md_hmac_demo.c @@ -0,0 +1,147 @@ +/** + * MD API multi-part HMAC demonstration. + * + * This programs computes the HMAC of two messages using the multi-part API. + * + * This is a companion to psa/hmac_demo.c, doing the same operations with the + * legacy MD API. The goal is that comparing the two programs will help people + * migrating to the PSA Crypto API. + * + * When it comes to multi-part HMAC operations, the `mbedtls_md_context` + * serves a dual purpose (1) hold the key, and (2) save progress information + * for the current operation. With PSA those roles are held by two disinct + * objects: (1) a psa_key_id_t to hold the key, and (2) a psa_operation_t for + * multi-part progress. + * + * This program and its companion psa/hmac_demo.c illustrate this by doing the + * same sequence of multi-part HMAC computation with both APIs; looking at the + * two side by side should make the differences and similarities clear. + */ + +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* First include Mbed TLS headers to get the Mbed TLS configuration and + * platform definitions that we'll use in this program. Also include + * standard C headers for functions we'll use here. */ +#include "mbedtls/build_info.h" + +#include "mbedtls/md.h" + +#include "mbedtls/platform_util.h" // for mbedtls_platform_zeroize + +#include +#include + +/* If the build options we need are not enabled, compile a placeholder. */ +#if !defined(MBEDTLS_MD_C) +int main( void ) +{ + printf( "MBEDTLS_MD_C not defined\r\n" ); + return( 0 ); +} +#else + +/* The real program starts here. */ + +/* Dummy inputs for HMAC */ +const unsigned char msg1_part1[] = { 0x01, 0x02 }; +const unsigned char msg1_part2[] = { 0x03, 0x04 }; +const unsigned char msg2_part1[] = { 0x05, 0x05 }; +const unsigned char msg2_part2[] = { 0x06, 0x06 }; + +/* Dummy key material - never do this in production! + * This example program uses SHA-256, so a 32-byte key makes sense. */ +const unsigned char key_bytes[32] = { 0 }; + +/* Print the contents of a buffer in hex */ +void print_buf( const char *title, unsigned char *buf, size_t len ) +{ + printf( "%s:", title ); + for( size_t i = 0; i < len; i++ ) + printf( " %02x", buf[i] ); + printf( "\n" ); +} + +/* Run an Mbed TLS function and bail out if it fails. + * A string description of the error code can be recovered with: + * programs/util/strerror */ +#define CHK( expr ) \ + do \ + { \ + ret = ( expr ); \ + if( ret != 0 ) \ + { \ + printf( "Error %d at line %d: %s\n", \ + ret, \ + __LINE__, \ + #expr ); \ + goto exit; \ + } \ + } while( 0 ) + +/* + * This function demonstrates computation of the HMAC of two messages using + * the multipart API. + */ +int hmac_demo(void) +{ + int ret; + const mbedtls_md_type_t alg = MBEDTLS_MD_SHA256; + unsigned char out[MBEDTLS_MD_MAX_SIZE]; // safe but not optimal + + mbedtls_md_context_t ctx; + + mbedtls_md_init( &ctx ); + + /* prepare context and load key */ + // the last argument to setup is 1 to enable HMAC (not just hashing) + const mbedtls_md_info_t *info = mbedtls_md_info_from_type( alg ); + CHK( mbedtls_md_setup( &ctx, info, 1 ) ); + CHK( mbedtls_md_hmac_starts( &ctx, key_bytes, sizeof( key_bytes ) ) ); + + /* compute HMAC(key, msg1_part1 | msg1_part2) */ + CHK( mbedtls_md_hmac_update( &ctx, msg1_part1, sizeof( msg1_part1 ) ) ); + CHK( mbedtls_md_hmac_update( &ctx, msg1_part2, sizeof( msg1_part2 ) ) ); + CHK( mbedtls_md_hmac_finish( &ctx, out ) ); + print_buf( "msg1", out, mbedtls_md_get_size( info ) ); + + /* compute HMAC(key, msg2_part1 | msg2_part2) */ + CHK( mbedtls_md_hmac_reset( &ctx ) ); // prepare for new operation + CHK( mbedtls_md_hmac_update( &ctx, msg2_part1, sizeof( msg2_part1 ) ) ); + CHK( mbedtls_md_hmac_update( &ctx, msg2_part2, sizeof( msg2_part2 ) ) ); + CHK( mbedtls_md_hmac_finish( &ctx, out ) ); + print_buf( "msg2", out, mbedtls_md_get_size( info ) ); + +exit: + mbedtls_md_free( &ctx ); + mbedtls_platform_zeroize( out, sizeof( out ) ); + + return( ret ); +} + +int main(void) +{ + int ret; + + CHK( hmac_demo() ); + +exit: + return( ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE ); +} + +#endif diff --git a/programs/psa/CMakeLists.txt b/programs/psa/CMakeLists.txt index 26ca73c185ce..7ba4af63d9b4 100644 --- a/programs/psa/CMakeLists.txt +++ b/programs/psa/CMakeLists.txt @@ -1,5 +1,7 @@ set(executables + aead_demo crypto_examples + hmac_demo key_ladder_demo psa_constant_names ) diff --git a/programs/psa/aead_demo.c b/programs/psa/aead_demo.c new file mode 100644 index 000000000000..5bc0af029744 --- /dev/null +++ b/programs/psa/aead_demo.c @@ -0,0 +1,293 @@ +/** + * PSA API multi-part AEAD demonstration. + * + * This program AEAD-encrypts a message, using the algorithm and key size + * specified on the command line, using the multi-part API. + * + * It comes with a companion program cipher/cipher_aead_demo.c, which does the + * same operations with the legacy Cipher API. The goal is that comparing the + * two programs will help people migrating to the PSA Crypto API. + * + * When used with multi-part AEAD operations, the `mbedtls_cipher_context` + * serves a triple purpose (1) hold the key, (2) store the algorithm when no + * operation is active, and (3) save progress information for the current + * operation. With PSA those roles are held by disinct objects: (1) a + * psa_key_id_t to hold the key, a (2) psa_algorithm_t to represent the + * algorithm, and (3) a psa_operation_t for multi-part progress. + * + * On the other hand, with PSA, the algorithms encodes the desired tag length; + * with Cipher the desired tag length needs to be tracked separately. + * + * This program and its companion cipher/cipher_aead_demo.c illustrate this by + * doing the same sequence of multi-part AEAD computation with both APIs; + * looking at the two side by side should make the differences and + * similarities clear. + */ + +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* First include Mbed TLS headers to get the Mbed TLS configuration and + * platform definitions that we'll use in this program. Also include + * standard C headers for functions we'll use here. */ +#include "mbedtls/build_info.h" + +#include "psa/crypto.h" + +#include +#include +#include + +/* If the build options we need are not enabled, compile a placeholder. */ +#if !defined(MBEDTLS_PSA_CRYPTO_C) || \ + !defined(MBEDTLS_AES_C) || !defined(MBEDTLS_GCM_C) || \ + !defined(MBEDTLS_CHACHAPOLY_C) || \ + defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER) +int main( void ) +{ + printf( "MBEDTLS_PSA_CRYPTO_C and/or " + "MBEDTLS_AES_C and/or MBEDTLS_GCM_C and/or " + "MBEDTLS_CHACHAPOLY_C not defined, and/or " + "MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER defined\r\n" ); + return( 0 ); +} +#else + +/* The real program starts here. */ + +const char usage[] = +"Usage: aead_demo [aes128-gcm|aes256-gcm|aes128-gcm_8|chachapoly]"; + +/* Dummy data for encryption: IV/nonce, additional data, 2-part message */ +const unsigned char iv1[12] = { 0x00 }; +const unsigned char add_data1[] = { 0x01, 0x02 }; +const unsigned char msg1_part1[] = { 0x03, 0x04 }; +const unsigned char msg1_part2[] = { 0x05, 0x06, 0x07 }; + +/* Dummy data (2nd message) */ +const unsigned char iv2[12] = { 0x10 }; +const unsigned char add_data2[] = { 0x11, 0x12 }; +const unsigned char msg2_part1[] = { 0x13, 0x14 }; +const unsigned char msg2_part2[] = { 0x15, 0x16, 0x17 }; + +/* Maximum total size of the messages */ +#define MSG1_SIZE ( sizeof( msg1_part1 ) + sizeof( msg1_part2 ) ) +#define MSG2_SIZE ( sizeof( msg2_part1 ) + sizeof( msg2_part2 ) ) +#define MSG_MAX_SIZE ( MSG1_SIZE > MSG2_SIZE ? MSG1_SIZE : MSG2_SIZE ) + +/* Dummy key material - never do this in production! + * 32-byte is enough to all the key size supported by this program. */ +const unsigned char key_bytes[32] = { 0x2a }; + +/* Print the contents of a buffer in hex */ +void print_buf( const char *title, uint8_t *buf, size_t len ) +{ + printf( "%s:", title ); + for( size_t i = 0; i < len; i++ ) + printf( " %02x", buf[i] ); + printf( "\n" ); +} + +/* Run a PSA function and bail out if it fails. + * The symbolic name of the error code can be recovered using: + * programs/psa/psa_consant_name status */ +#define PSA_CHECK( expr ) \ + do \ + { \ + status = ( expr ); \ + if( status != PSA_SUCCESS ) \ + { \ + printf( "Error %d at line %d: %s\n", \ + (int) status, \ + __LINE__, \ + #expr ); \ + goto exit; \ + } \ + } \ + while( 0 ) + +/* + * Prepare encryption material: + * - interpret command-line argument + * - set up key + * - outputs: key and algorithm, which together hold all the information + */ +static psa_status_t aead_prepare( const char *info, + psa_key_id_t *key, + psa_algorithm_t *alg ) +{ + psa_status_t status; + + /* Convert arg to alg + key_bits + key_type */ + size_t key_bits; + psa_key_type_t key_type; + if( strcmp( info, "aes128-gcm" ) == 0 ) { + *alg = PSA_ALG_GCM; + key_bits = 128; + key_type = PSA_KEY_TYPE_AES; + } else if( strcmp( info, "aes256-gcm" ) == 0 ) { + *alg = PSA_ALG_GCM; + key_bits = 256; + key_type = PSA_KEY_TYPE_AES; + } else if( strcmp( info, "aes128-gcm_8" ) == 0 ) { + *alg = PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, 8); + key_bits = 128; + key_type = PSA_KEY_TYPE_AES; + } else if( strcmp( info, "chachapoly" ) == 0 ) { + *alg = PSA_ALG_CHACHA20_POLY1305; + key_bits = 256; + key_type = PSA_KEY_TYPE_CHACHA20; + } else { + puts( usage ); + return( PSA_ERROR_INVALID_ARGUMENT ); + } + + /* Prepare key attibutes */ + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_set_key_usage_flags( &attributes, PSA_KEY_USAGE_ENCRYPT ); + psa_set_key_algorithm( &attributes, *alg ); + psa_set_key_type( &attributes, key_type ); + psa_set_key_bits( &attributes, key_bits ); // optional + + /* Import key */ + PSA_CHECK( psa_import_key( &attributes, key_bytes, key_bits / 8, key ) ); + +exit: + return( status ); +} + +/* + * Print out some information. + * + * All of this information was present in the command line argument, but his + * function demonstrates how each piece can be recovered from (key, alg). + */ +static void aead_info( psa_key_id_t key, psa_algorithm_t alg ) +{ + psa_key_attributes_t attr = PSA_KEY_ATTRIBUTES_INIT; + (void) psa_get_key_attributes( key, &attr ); + psa_key_type_t key_type = psa_get_key_type( &attr ); + size_t key_bits = psa_get_key_bits( &attr ); + psa_algorithm_t base_alg = PSA_ALG_AEAD_WITH_DEFAULT_LENGTH_TAG( alg ); + size_t tag_len = PSA_AEAD_TAG_LENGTH( key_type, key_bits, alg ); + + const char *type_str = key_type == PSA_KEY_TYPE_AES ? "AES" + : key_type == PSA_KEY_TYPE_CHACHA20 ? "Chacha" + : "???"; + const char *base_str = base_alg == PSA_ALG_GCM ? "GCM" + : base_alg == PSA_ALG_CHACHA20_POLY1305 ? "ChachaPoly" + : "???"; + + printf( "%s, %u, %s, %u\n", + type_str, (unsigned) key_bits, base_str, (unsigned) tag_len ); +} + +/* + * Encrypt a 2-part message. + */ +static int aead_encrypt( psa_key_id_t key, psa_algorithm_t alg, + const unsigned char *iv, size_t iv_len, + const unsigned char *ad, size_t ad_len, + const unsigned char *part1, size_t part1_len, + const unsigned char *part2, size_t part2_len ) +{ + psa_status_t status; + size_t olen, olen_tag; + unsigned char out[PSA_AEAD_ENCRYPT_OUTPUT_MAX_SIZE(MSG_MAX_SIZE)]; + unsigned char *p = out, *end = out + sizeof( out ); + unsigned char tag[PSA_AEAD_TAG_MAX_SIZE]; + + psa_aead_operation_t op = PSA_AEAD_OPERATION_INIT; + PSA_CHECK( psa_aead_encrypt_setup( &op, key, alg ) ); + + PSA_CHECK( psa_aead_set_nonce( &op, iv, iv_len ) ); + PSA_CHECK( psa_aead_update_ad( &op, ad, ad_len ) ); + PSA_CHECK( psa_aead_update( &op, part1, part1_len, p, end - p, &olen ) ); + p += olen; + PSA_CHECK( psa_aead_update( &op, part2, part2_len, p, end - p, &olen ) ); + p += olen; + PSA_CHECK( psa_aead_finish( &op, p, end - p, &olen, + tag, sizeof( tag ), &olen_tag ) ); + p += olen; + memcpy( p, tag, olen_tag ); + p += olen_tag; + + olen = p - out; + print_buf( "out", out, olen ); + +exit: + psa_aead_abort( &op ); // required on errors, harmless on success + return( status ); +} + +/* + * AEAD demo: set up key/alg, print out info, encrypt messages. + */ +static psa_status_t aead_demo( const char *info ) +{ + psa_status_t status; + + psa_key_id_t key; + psa_algorithm_t alg; + + PSA_CHECK( aead_prepare( info, &key, &alg ) ); + + aead_info( key, alg ); + + PSA_CHECK( aead_encrypt( key, alg, + iv1, sizeof( iv1 ), add_data1, sizeof( add_data1 ), + msg1_part1, sizeof( msg1_part1 ), + msg1_part2, sizeof( msg1_part2 ) ) ); + PSA_CHECK( aead_encrypt( key, alg, + iv2, sizeof( iv2 ), add_data2, sizeof( add_data2 ), + msg2_part1, sizeof( msg2_part1 ), + msg2_part2, sizeof( msg2_part2 ) ) ); + +exit: + psa_destroy_key( key ); + + return( status ); +} + +/* + * Main function + */ +int main( int argc, char **argv ) +{ + psa_status_t status = PSA_SUCCESS; + + /* Check usage */ + if( argc != 2 ) + { + puts( usage ); + return( EXIT_FAILURE ); + } + + /* Initialize the PSA crypto library. */ + PSA_CHECK( psa_crypto_init( ) ); + + /* Run the demo */ + PSA_CHECK( aead_demo( argv[1] ) ); + + /* Deinitialize the PSA crypto library. */ + mbedtls_psa_crypto_free( ); + +exit: + return( status == PSA_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE ); +} + +#endif diff --git a/programs/psa/hmac_demo.c b/programs/psa/hmac_demo.c new file mode 100644 index 000000000000..aa56b413be45 --- /dev/null +++ b/programs/psa/hmac_demo.c @@ -0,0 +1,169 @@ +/** + * PSA API multi-part HMAC demonstration. + * + * This programs computes the HMAC of two messages using the multi-part API. + * + * It comes with a companion program hash/md_hmac_demo.c, which does the same + * operations with the legacy MD API. The goal is that comparing the two + * programs will help people migrating to the PSA Crypto API. + * + * When it comes to multi-part HMAC operations, the `mbedtls_md_context` + * serves a dual purpose (1) hold the key, and (2) save progress information + * for the current operation. With PSA those roles are held by two disinct + * objects: (1) a psa_key_id_t to hold the key, and (2) a psa_operation_t for + * multi-part progress. + * + * This program and its companion hash/md_hmac_demo.c illustrate this by doing + * the same sequence of multi-part HMAC computation with both APIs; looking at + * the two side by side should make the differences and similarities clear. + */ + +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* First include Mbed TLS headers to get the Mbed TLS configuration and + * platform definitions that we'll use in this program. Also include + * standard C headers for functions we'll use here. */ +#include "mbedtls/build_info.h" + +#include "psa/crypto.h" + +#include "mbedtls/platform_util.h" // for mbedtls_platform_zeroize + +#include +#include + +/* If the build options we need are not enabled, compile a placeholder. */ +#if !defined(MBEDTLS_PSA_CRYPTO_C) || \ + defined(MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER) +int main( void ) +{ + printf( "MBEDTLS_PSA_CRYPTO_C not defined, " + "and/or MBEDTLS_PSA_CRYPTO_KEY_ID_ENCODES_OWNER defined\r\n" ); + return( 0 ); +} +#else + +/* The real program starts here. */ + +/* Dummy inputs for HMAC */ +const unsigned char msg1_part1[] = { 0x01, 0x02 }; +const unsigned char msg1_part2[] = { 0x03, 0x04 }; +const unsigned char msg2_part1[] = { 0x05, 0x05 }; +const unsigned char msg2_part2[] = { 0x06, 0x06 }; + +/* Dummy key material - never do this in production! + * This example program uses SHA-256, so a 32-byte key makes sense. */ +const unsigned char key_bytes[32] = { 0 }; + +/* Print the contents of a buffer in hex */ +void print_buf( const char *title, uint8_t *buf, size_t len ) +{ + printf( "%s:", title ); + for( size_t i = 0; i < len; i++ ) + printf( " %02x", buf[i] ); + printf( "\n" ); +} + +/* Run a PSA function and bail out if it fails. + * The symbolic name of the error code can be recovered using: + * programs/psa/psa_consant_name status */ +#define PSA_CHECK( expr ) \ + do \ + { \ + status = ( expr ); \ + if( status != PSA_SUCCESS ) \ + { \ + printf( "Error %d at line %d: %s\n", \ + (int) status, \ + __LINE__, \ + #expr ); \ + goto exit; \ + } \ + } \ + while( 0 ) + +/* + * This function demonstrates computation of the HMAC of two messages using + * the multipart API. + */ +psa_status_t hmac_demo(void) +{ + psa_status_t status; + const psa_algorithm_t alg = PSA_ALG_HMAC(PSA_ALG_SHA_256); + uint8_t out[PSA_MAC_MAX_SIZE]; // safe but not optimal + /* PSA_MAC_LENGTH(PSA_KEY_TYPE_HMAC, 8 * sizeof( key_bytes ), alg) + * should work but see https://github.com/ARMmbed/mbedtls/issues/4320 */ + + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_key_id_t key = 0; + + /* prepare key */ + psa_set_key_usage_flags( &attributes, PSA_KEY_USAGE_SIGN_MESSAGE ); + psa_set_key_algorithm( &attributes, alg ); + psa_set_key_type( &attributes, PSA_KEY_TYPE_HMAC ); + psa_set_key_bits( &attributes, 8 * sizeof( key_bytes ) ); // optional + + status = psa_import_key( &attributes, + key_bytes, sizeof( key_bytes ), &key ); + if( status != PSA_SUCCESS ) + return( status ); + + /* prepare operation */ + psa_mac_operation_t op = PSA_MAC_OPERATION_INIT; + size_t out_len = 0; + + /* compute HMAC(key, msg1_part1 | msg1_part2) */ + PSA_CHECK( psa_mac_sign_setup( &op, key, alg ) ); + PSA_CHECK( psa_mac_update( &op, msg1_part1, sizeof( msg1_part1 ) ) ); + PSA_CHECK( psa_mac_update( &op, msg1_part2, sizeof( msg1_part2 ) ) ); + PSA_CHECK( psa_mac_sign_finish( &op, out, sizeof( out ), &out_len ) ); + print_buf( "msg1", out, out_len ); + + /* compute HMAC(key, msg2_part1 | msg2_part2) */ + PSA_CHECK( psa_mac_sign_setup( &op, key, alg ) ); + PSA_CHECK( psa_mac_update( &op, msg2_part1, sizeof( msg2_part1 ) ) ); + PSA_CHECK( psa_mac_update( &op, msg2_part2, sizeof( msg2_part2 ) ) ); + PSA_CHECK( psa_mac_sign_finish( &op, out, sizeof( out ), &out_len ) ); + print_buf( "msg2", out, out_len ); + +exit: + psa_mac_abort( &op ); // needed on error, harmless on success + psa_destroy_key( key ); + mbedtls_platform_zeroize( out, sizeof( out ) ); + + return( status ); +} + +int main(void) +{ + psa_status_t status = PSA_SUCCESS; + + /* Initialize the PSA crypto library. */ + PSA_CHECK( psa_crypto_init( ) ); + + /* Run the demo */ + PSA_CHECK( hmac_demo() ); + + /* Deinitialize the PSA crypto library. */ + mbedtls_psa_crypto_free( ); + +exit: + return( status == PSA_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE ); +} + +#endif