diff options
author | Atul Luykx <aluykx@google.com> | 2023-01-27 23:46:39 +0000 |
---|---|---|
committer | CQ Bot Account <pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2023-01-27 23:46:39 +0000 |
commit | 5915933b017765500eb39051b8b9147188f2f300 (patch) | |
tree | ac7b92ef2c586be9d9cd6fadef69a68fa8d6d2b0 | |
parent | d99a8e130d70075bb7957d45f4bd87ad849eb16f (diff) | |
download | open-dice-5915933b017765500eb39051b8b9147188f2f300.tar.gz |
Add P-384 Support for CBOR templates
Implements ECDSA P-384 boringSSL operations and the relevant CBOR
operations to create a CBOR certificate.
Change-Id: I551ae7b38f01feeaf4b240b85e3b25d703bf8bcd
Reviewed-on: https://pigweed-review.googlesource.com/c/open-dice/+/71280
Reviewed-by: Darren Krahn <dkrahn@google.com>
Commit-Queue: Atul Luykx <aluykx@google.com>
Reviewed-by: Andrew Scull <ascull@google.com>
-rw-r--r-- | BUILD.gn | 93 | ||||
-rw-r--r-- | generate_test_values.py | 4 | ||||
-rw-r--r-- | include/dice/boringssl_ecdsa_utils.h | 57 | ||||
-rw-r--r-- | include/dice/config/boringssl_ecdsa_p384/dice/config.h | 25 | ||||
-rw-r--r-- | include/dice/known_test_values.h | 168 | ||||
-rw-r--r-- | include/dice/test_utils.h | 1 | ||||
-rw-r--r-- | src/boringssl_ecdsa_utils.c | 297 | ||||
-rw-r--r-- | src/boringssl_p384_ops.c | 69 | ||||
-rw-r--r-- | src/cbor_p384_cert_op.c | 80 | ||||
-rw-r--r-- | src/cbor_p384_cert_op_test.cc | 254 | ||||
-rw-r--r-- | src/test_utils.cc | 149 | ||||
-rw-r--r-- | third_party/cose-c/BUILD.gn | 24 | ||||
-rw-r--r-- | third_party/cose-c/cose_p384_deps.cc | 148 | ||||
-rw-r--r-- | third_party/cose-c/include/p384/cose/cose_configure.h | 11 |
14 files changed, 1374 insertions, 6 deletions
@@ -55,6 +55,10 @@ config("boringssl_ed25519_ops_config") { include_dirs = [ "//include/dice/config/boringssl_ed25519" ] } +config("boringssl_ecdsa_p384_ops_config") { + include_dirs = [ "//include/dice/config/boringssl_ecdsa_p384" ] +} + pw_static_library("dice_with_boringssl_ed25519_ops") { public = [ "include/dice/dice.h", @@ -72,6 +76,23 @@ pw_static_library("dice_with_boringssl_ed25519_ops") { all_dependent_configs = [ ":boringssl_ed25519_ops_config" ] } +pw_static_library("dice_with_boringssl_p384_ops") { + public = [ + "include/dice/dice.h", + "include/dice/utils.h", + ] + sources = [ + "src/boringssl_cert_op.c", + "src/boringssl_p384_ops.c", + "src/boringssl_hash_kdf_ops.c", + "src/clear_memory.c", + "src/dice.c", + "src/utils.c", + ] + deps = [ "//third_party/boringssl:crypto" ] + all_dependent_configs = [ ":boringssl_ecdsa_p384_ops_config" ] +} + config("mbedtls_ops_config") { include_dirs = [ "//include//dice/config/mbedtls_ecdsa_p256" ] defines = [ "MBEDTLS_ALLOW_PRIVATE_ACCESS" ] @@ -113,6 +134,44 @@ pw_static_library("dice_with_cbor_ed25519_cert") { all_dependent_configs = [ ":boringssl_ed25519_ops_config" ] } + +pw_static_library("boringssl_ecdsa_utils") { + public = [ + "include/dice/boringssl_ecdsa_utils.h", + ] + sources = [ + "src/boringssl_ecdsa_utils.c", + ] + deps = [ + "//third_party/boringssl:crypto", + ] +} + +pw_static_library("dice_with_cbor_p384_cert") { + public = [ + "include/dice/dice.h", + "include/dice/utils.h", + ] + sources = [ + "src/boringssl_hash_kdf_ops.c", + "src/boringssl_p384_ops.c", + "src/cbor_cert_op.c", + "src/cbor_p384_cert_op.c", + "src/clear_memory.c", + "src/dice.c", + "src/utils.c", + "src/boringssl_ecdsa_utils.c", + ] + deps = [ + ":cbor_writer", + ":boringssl_ecdsa_utils", + "//third_party/boringssl:crypto", + ] + all_dependent_configs = [ + ":boringssl_ecdsa_p384_ops_config", + ] +} + pw_static_library("dice_with_cbor_template_ed25519_cert") { public = [ "include/dice/dice.h", @@ -186,6 +245,7 @@ pw_test("boringssl_ed25519_ops_test") { ] deps = [ ":dice_with_boringssl_ed25519_ops", + ":boringssl_ecdsa_utils", "$dir_pw_string:pw_string", "//third_party/boringssl:crypto", "//third_party/cose-c:cose-c_ed25519", @@ -199,6 +259,13 @@ pw_executable("boringssl_ed25519_ops_fuzzer") { ] } +pw_executable("boringssl_p384_ops_fuzzer") { + deps = [ + ":dice_with_boringssl_p384_ops", + ":fuzzer", + ] +} + pw_test("template_cert_op_test") { sources = [ "src/template_cert_op_test.cc", @@ -206,6 +273,7 @@ pw_test("template_cert_op_test") { ] deps = [ ":dice_with_x509_template_cert", + ":boringssl_ecdsa_utils", "$dir_pw_string:pw_string", "//third_party/boringssl:crypto", "//third_party/cose-c:cose-c_ed25519", @@ -226,12 +294,27 @@ pw_test("cbor_ed25519_cert_op_test") { ] deps = [ ":dice_with_cbor_ed25519_cert", + ":boringssl_ecdsa_utils", "$dir_pw_string:pw_string", "//third_party/boringssl:crypto", "//third_party/cose-c:cose-c_ed25519", ] } +pw_test("cbor_p384_cert_op_test") { + sources = [ + "src/cbor_p384_cert_op_test.cc", + "src/test_utils.cc", + ] + deps = [ + ":dice_with_cbor_p384_cert", + ":boringssl_ecdsa_utils", + "//third_party/boringssl:crypto", + "//third_party/cose-c:cose-c_p384", + "$dir_pw_string:pw_string", + ] +} + pw_executable("cbor_ed25519_cert_op_fuzzer") { deps = [ ":dice_with_cbor_ed25519_cert", @@ -246,6 +329,7 @@ pw_test("template_cbor_ed25519_cert_op_test") { ] deps = [ ":dice_with_cbor_template_ed25519_cert", + ":boringssl_ecdsa_utils", "$dir_pw_string:pw_string", "//third_party/boringssl:crypto", "//third_party/cose-c:cose-c_ed25519", @@ -266,6 +350,7 @@ pw_test("mbedtls_ops_test") { ] deps = [ ":dice_with_mbedtls_ops", + ":boringssl_ecdsa_utils", "$dir_pw_string:pw_string", "//third_party/boringssl:crypto", "//third_party/cose-c:cose-c_ed25519", @@ -283,6 +368,7 @@ pw_test_group("tests") { tests = [ ":boringssl_ed25519_ops_test", ":cbor_ed25519_cert_op_test", + ":cbor_p384_cert_op_test", ":cbor_reader_test", ":cbor_writer_test", ":dice_test", @@ -296,6 +382,7 @@ pw_test_group("tests") { group("fuzzers") { deps = [ ":boringssl_ed25519_ops_fuzzer", + ":boringssl_p384_ops_fuzzer", ":cbor_ed25519_cert_op_fuzzer", ":cbor_reader_fuzzer", ":cbor_writer_fuzzer", @@ -417,6 +504,11 @@ pw_size_diff("library_size_report") { base = ":dice_standalone" }, { + target = ":dice_with_cbor_p384_cert" + label = "CBOR P384 Cert" + base = ":dice_standalone" + }, + { target = ":dice_with_cbor_template_ed25519_cert" label = "CBOR Template Cert" base = ":dice_standalone" @@ -434,6 +526,7 @@ group("optimized_libs") { ":dice_standalone", ":dice_with_boringssl_ed25519_ops", ":dice_with_cbor_ed25519_cert", + ":dice_with_cbor_p384_cert", ":dice_with_cbor_template_ed25519_cert", ":dice_with_mbedtls_ops", ":dice_with_x509_template_cert", diff --git a/generate_test_values.py b/generate_test_values.py index 953ef82..f4afd4f 100644 --- a/generate_test_values.py +++ b/generate_test_values.py @@ -115,13 +115,13 @@ def _generate_c(name): content += _generate_array('kExpectedCdiSeal_%s' % _to_camel_case(name), seal_cdi_data) for cert_type in ('X509', 'CBOR'): - for key_type in ('Ed25519', 'P256'): + for key_type in ('Ed25519', 'P256', 'P384'): var_name = 'kExpected%s%sCert_%s' % (_to_camel_case(cert_type), _to_camel_case(key_type), _to_camel_case(name)) cert_data = _read_file('_%s_%s_cert_%s.cert' % (cert_type, key_type, name)) - if cert_type == 'X509': + if cert_type == 'X509' and key_type != 'P384': content += ( '// $ openssl x509 -inform DER -noout -text -certopt ' 'ext_parse\n') diff --git a/include/dice/boringssl_ecdsa_utils.h b/include/dice/boringssl_ecdsa_utils.h new file mode 100644 index 0000000..229926a --- /dev/null +++ b/include/dice/boringssl_ecdsa_utils.h @@ -0,0 +1,57 @@ +// Copyright 2022 Google LLC +// +// 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 +// +// https://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. + +#ifndef BORINGSSL_ECDSA_UTILS_H_ +#define BORINGSSL_ECDSA_UTILS_H_ + +#include <stddef.h> +#include <stdint.h> + +#include "dice/dice.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define P384_PRIVATE_KEY_SIZE 48 +#define P384_PUBLIC_KEY_SIZE 96 +#define P384_SIGNATURE_SIZE 96 + +// Deterministically generates a public and private key pair from |seed|. +// Since this is deterministic, |seed| is as sensitive as a private key and can +// be used directly as the private key. The |private_key| may use an +// implementation defined format so may only be passed to the |sign| operation. +int P384KeypairFromSeed(uint8_t public_key[P384_PUBLIC_KEY_SIZE], + uint8_t private_key[P384_PRIVATE_KEY_SIZE], + const uint8_t seed[DICE_PRIVATE_KEY_SEED_SIZE]); + +// Calculates a signature of |message_size| bytes from |message| using +// |private_key|. |private_key| was generated by |keypair_from_seed| to allow +// an implementation to use their own private key format. |signature| points to +// the buffer where the calculated signature is written. +int P384Sign(uint8_t signature[P384_SIGNATURE_SIZE], const uint8_t* message, + size_t message_size, + const uint8_t private_key[P384_PRIVATE_KEY_SIZE]); + +// Verifies, using |public_key|, that |signature| covers |message_size| bytes +// from |message|. +int P384Verify(const uint8_t* message, size_t message_size, + const uint8_t signature[P384_SIGNATURE_SIZE], + const uint8_t public_key[P384_PUBLIC_KEY_SIZE]); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // BORINGSSL_ECDSA_UTILS_H_ diff --git a/include/dice/config/boringssl_ecdsa_p384/dice/config.h b/include/dice/config/boringssl_ecdsa_p384/dice/config.h new file mode 100644 index 0000000..a0f9583 --- /dev/null +++ b/include/dice/config/boringssl_ecdsa_p384/dice/config.h @@ -0,0 +1,25 @@ +// Copyright 2022 Google LLC +// +// 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 +// +// https://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. + +#ifndef DICE_CONFIG_H_ +#define DICE_CONFIG_H_ + +// ECDSA P384 +// From table 1 of RFC 9053 +#define DICE_COSE_KEY_ALG_VALUE (-35) +#define DICE_PUBLIC_KEY_SIZE 96 +#define DICE_PRIVATE_KEY_SIZE 48 +#define DICE_SIGNATURE_SIZE 96 + +#endif // DICE_DICE_CONFIG_H_ diff --git a/include/dice/known_test_values.h b/include/dice/known_test_values.h index 7391fe6..c3a052b 100644 --- a/include/dice/known_test_values.h +++ b/include/dice/known_test_values.h @@ -268,6 +268,8 @@ constexpr uint8_t kExpectedX509P256Cert_ZeroInput[705] = { 0xd7, 0x4c, 0x98, 0x1f, 0x10, 0x7f, 0x14, 0x6a, 0x45, 0xc0, 0x1c, 0x48, 0x99, 0xce, 0x6e, 0x8f, 0x6d, 0xd3, 0xdc, 0xf3, 0x93}; +constexpr uint8_t kExpectedX509P384Cert_ZeroInput[0] = {}; + constexpr uint8_t kExpectedCborEd25519Cert_ZeroInput[441] = { 0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x59, 0x01, 0x6e, 0xa8, 0x01, 0x78, 0x28, 0x37, 0x61, 0x30, 0x36, 0x65, 0x65, 0x65, 0x34, 0x31, 0x62, 0x37, @@ -309,6 +311,54 @@ constexpr uint8_t kExpectedCborEd25519Cert_ZeroInput[441] = { constexpr uint8_t kExpectedCborP256Cert_ZeroInput[0] = {}; +constexpr uint8_t kExpectedCborP384Cert_ZeroInput[542] = { + 0x84, 0x44, 0xa1, 0x01, 0x38, 0x22, 0xa0, 0x59, 0x01, 0xb2, 0xa8, 0x01, + 0x78, 0x28, 0x30, 0x34, 0x63, 0x32, 0x36, 0x35, 0x66, 0x65, 0x30, 0x36, + 0x66, 0x66, 0x32, 0x33, 0x30, 0x65, 0x33, 0x39, 0x62, 0x36, 0x33, 0x32, + 0x32, 0x65, 0x65, 0x61, 0x39, 0x65, 0x30, 0x31, 0x30, 0x37, 0x31, 0x31, + 0x66, 0x62, 0x36, 0x36, 0x62, 0x34, 0x02, 0x78, 0x28, 0x34, 0x30, 0x63, + 0x62, 0x34, 0x66, 0x30, 0x36, 0x34, 0x61, 0x36, 0x38, 0x64, 0x34, 0x30, + 0x37, 0x61, 0x30, 0x62, 0x33, 0x39, 0x30, 0x61, 0x62, 0x63, 0x63, 0x30, + 0x35, 0x61, 0x33, 0x34, 0x62, 0x66, 0x63, 0x38, 0x61, 0x66, 0x33, 0x33, + 0x66, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0x00, 0x47, 0x44, 0x53, 0x58, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, + 0x00, 0x47, 0x44, 0x54, 0x58, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, + 0x47, 0x44, 0x56, 0x41, 0x00, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58, 0x71, + 0xa6, 0x01, 0x02, 0x03, 0x38, 0x22, 0x04, 0x81, 0x02, 0x20, 0x02, 0x21, + 0x58, 0x30, 0x77, 0x19, 0x6b, 0xa5, 0x84, 0xeb, 0x79, 0x46, 0xd2, 0xfb, + 0xb0, 0xd5, 0xc8, 0x31, 0xc7, 0xad, 0x91, 0x37, 0x5e, 0x11, 0x28, 0xdb, + 0x23, 0x8c, 0xc1, 0xc6, 0x7f, 0xae, 0x5e, 0x07, 0x10, 0x95, 0x5b, 0x17, + 0xb5, 0xd5, 0x08, 0x12, 0x31, 0x06, 0xba, 0x31, 0x31, 0x10, 0x43, 0x71, + 0x51, 0xde, 0x22, 0x58, 0x30, 0x03, 0x25, 0xa9, 0x76, 0x29, 0x67, 0x9d, + 0x6b, 0xa9, 0x01, 0xb1, 0x22, 0xa0, 0x4b, 0xee, 0xf7, 0xb3, 0xe1, 0x52, + 0xfc, 0xe0, 0x3c, 0xdc, 0x5d, 0x1b, 0x58, 0x16, 0x69, 0xdd, 0x44, 0x24, + 0x67, 0xbf, 0x21, 0xd7, 0x47, 0xf3, 0x13, 0xd1, 0x47, 0x6c, 0x4b, 0xd3, + 0x05, 0xb5, 0x29, 0xa0, 0xf1, 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20, + 0x58, 0x60, 0xb0, 0xb7, 0x0f, 0x47, 0xfa, 0xba, 0xca, 0x05, 0x16, 0x4f, + 0x1e, 0xd1, 0x73, 0x15, 0x94, 0x17, 0xcd, 0x6b, 0x3c, 0x0e, 0x18, 0x77, + 0xc0, 0x6e, 0x53, 0x97, 0xf2, 0x03, 0xb9, 0xbb, 0x82, 0xde, 0xe0, 0x2d, + 0xb3, 0xc8, 0x0e, 0x7a, 0x5e, 0xb0, 0x74, 0x15, 0xbe, 0x25, 0x6a, 0x7c, + 0x90, 0x30, 0x80, 0x0c, 0x90, 0x20, 0x00, 0xc5, 0x42, 0xbf, 0xf7, 0x4e, + 0x1b, 0xd8, 0xe2, 0xe4, 0x32, 0xd5, 0xf3, 0x8e, 0x1e, 0x59, 0x9f, 0x1e, + 0x71, 0x8e, 0xc3, 0x2e, 0x50, 0x8a, 0xa0, 0x02, 0xa4, 0xd3, 0xe6, 0x8c, + 0x68, 0xbb, 0x5e, 0x33, 0xfd, 0x81, 0x12, 0xa7, 0xdc, 0x68, 0xc7, 0x7a, + 0x4b, 0xfa}; + constexpr uint8_t kExpectedCdiAttest_HashOnlyInput[32] = { 0x08, 0x4e, 0xf4, 0x06, 0xc6, 0x9b, 0xa7, 0x4b, 0x1e, 0x24, 0xd0, 0x62, 0xf9, 0xab, 0x8a, 0x8d, 0x89, 0xda, 0x6e, 0x03, 0xe4, 0xc6, @@ -554,6 +604,8 @@ constexpr uint8_t kExpectedX509P256Cert_HashOnlyInput[707] = { 0xc9, 0xe2, 0x0f, 0x50, 0xf0, 0x1c, 0x70, 0x01, 0x64, 0xa7, 0x8d, 0x7d, 0x51, 0xe9, 0x59, 0xfe, 0xe6, 0x7f, 0x31, 0x28, 0x30, 0x04, 0x04}; +constexpr uint8_t kExpectedX509P384Cert_HashOnlyInput[0] = {}; + constexpr uint8_t kExpectedCborEd25519Cert_HashOnlyInput[441] = { 0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x59, 0x01, 0x6e, 0xa8, 0x01, 0x78, 0x28, 0x34, 0x37, 0x35, 0x37, 0x30, 0x38, 0x65, 0x62, 0x33, 0x62, 0x34, @@ -595,6 +647,54 @@ constexpr uint8_t kExpectedCborEd25519Cert_HashOnlyInput[441] = { constexpr uint8_t kExpectedCborP256Cert_HashOnlyInput[0] = {}; +constexpr uint8_t kExpectedCborP384Cert_HashOnlyInput[542] = { + 0x84, 0x44, 0xa1, 0x01, 0x38, 0x22, 0xa0, 0x59, 0x01, 0xb2, 0xa8, 0x01, + 0x78, 0x28, 0x35, 0x64, 0x38, 0x62, 0x36, 0x62, 0x65, 0x37, 0x63, 0x65, + 0x33, 0x65, 0x64, 0x65, 0x36, 0x61, 0x32, 0x34, 0x31, 0x38, 0x30, 0x31, + 0x34, 0x35, 0x32, 0x33, 0x65, 0x36, 0x63, 0x39, 0x64, 0x63, 0x38, 0x37, + 0x65, 0x39, 0x38, 0x63, 0x63, 0x36, 0x02, 0x78, 0x28, 0x36, 0x35, 0x61, + 0x63, 0x35, 0x39, 0x36, 0x61, 0x62, 0x39, 0x39, 0x34, 0x30, 0x33, 0x61, + 0x38, 0x63, 0x37, 0x30, 0x32, 0x37, 0x35, 0x62, 0x31, 0x34, 0x62, 0x30, + 0x32, 0x33, 0x37, 0x33, 0x66, 0x66, 0x31, 0x34, 0x33, 0x66, 0x39, 0x31, + 0x65, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x40, 0xb7, 0xd4, 0x0c, 0xcb, + 0x22, 0x5b, 0xa5, 0x78, 0x8f, 0x98, 0xff, 0x9e, 0x86, 0x93, 0x75, 0xf6, + 0x90, 0xac, 0x50, 0xcf, 0x9e, 0xbd, 0x0a, 0xfe, 0xb1, 0xd9, 0xc2, 0x4e, + 0x52, 0x19, 0xe4, 0xde, 0x29, 0xe5, 0x61, 0xf3, 0xf9, 0x29, 0xe8, 0x40, + 0x87, 0x7a, 0xdd, 0x17, 0x48, 0x05, 0x89, 0x7e, 0x2b, 0xcb, 0x54, 0x79, + 0xcc, 0x66, 0xf1, 0xb3, 0x13, 0x29, 0x0c, 0x68, 0x96, 0xb2, 0xbb, 0x8f, + 0x3a, 0x00, 0x47, 0x44, 0x53, 0x58, 0x40, 0xcf, 0x99, 0x7b, 0xea, 0x2e, + 0x2c, 0x86, 0xa0, 0x7b, 0x52, 0x09, 0xc8, 0xb5, 0x3c, 0x41, 0x12, 0x29, + 0x28, 0x1a, 0x82, 0x0d, 0x49, 0x9c, 0x95, 0xcb, 0x0b, 0x1b, 0x31, 0x1a, + 0x01, 0x9c, 0xf2, 0x66, 0x1a, 0xd9, 0xb5, 0xce, 0x52, 0x59, 0xcb, 0xf4, + 0x81, 0x9b, 0x21, 0xaf, 0x32, 0x5d, 0x07, 0xa0, 0x1e, 0x91, 0x59, 0x6f, + 0x06, 0x55, 0x10, 0x8e, 0x2e, 0x08, 0x88, 0x52, 0x28, 0x86, 0x7f, 0x3a, + 0x00, 0x47, 0x44, 0x54, 0x58, 0x40, 0x22, 0x52, 0x60, 0x17, 0xef, 0x2c, + 0xa1, 0xf6, 0xcb, 0xed, 0x39, 0xd5, 0xe2, 0xaa, 0x65, 0x20, 0xfb, 0xad, + 0x82, 0x93, 0xe5, 0x78, 0x23, 0x22, 0x97, 0xc1, 0x6e, 0x6a, 0x4e, 0x36, + 0xd7, 0x6a, 0x61, 0x39, 0x08, 0x21, 0xd4, 0xfe, 0x92, 0x5f, 0x36, 0x2d, + 0xeb, 0x5d, 0xbb, 0x32, 0x8b, 0xe3, 0x94, 0x4f, 0xbe, 0x1b, 0x21, 0xf9, + 0xcc, 0x23, 0x73, 0x41, 0xb6, 0xb9, 0xb6, 0x98, 0xd0, 0xbc, 0x3a, 0x00, + 0x47, 0x44, 0x56, 0x41, 0x00, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58, 0x71, + 0xa6, 0x01, 0x02, 0x03, 0x38, 0x22, 0x04, 0x81, 0x02, 0x20, 0x02, 0x21, + 0x58, 0x30, 0x32, 0x81, 0xad, 0x61, 0x1e, 0x50, 0x96, 0x2b, 0x5e, 0xda, + 0xff, 0xee, 0x14, 0xa6, 0x44, 0x3d, 0xd9, 0xd1, 0x34, 0xf6, 0x64, 0xb7, + 0x61, 0x58, 0xf4, 0x9a, 0x58, 0xdb, 0xef, 0xa8, 0x87, 0x13, 0x26, 0x08, + 0x1b, 0xc7, 0xdd, 0xc5, 0x5b, 0x73, 0x42, 0xd6, 0x29, 0x87, 0x3f, 0x85, + 0xd0, 0xe4, 0x22, 0x58, 0x30, 0x60, 0x85, 0xd8, 0x42, 0x29, 0x1b, 0xc6, + 0xd9, 0xf6, 0x2f, 0x3a, 0xce, 0xa0, 0xb9, 0x40, 0xb8, 0x18, 0xde, 0xc2, + 0x5c, 0x90, 0xfb, 0x4b, 0x6d, 0x96, 0x42, 0x77, 0xe2, 0xf6, 0x58, 0x3d, + 0x37, 0xa7, 0x3d, 0x2e, 0xca, 0xd1, 0x2c, 0xa4, 0xd4, 0xa7, 0xaf, 0x25, + 0xc3, 0xb2, 0xe7, 0x34, 0xf5, 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20, + 0x58, 0x60, 0x84, 0x9a, 0x2e, 0x89, 0xeb, 0x6d, 0x61, 0x93, 0xe0, 0x0e, + 0xb9, 0x57, 0xc6, 0x84, 0x08, 0x28, 0x77, 0xeb, 0x9e, 0x39, 0xad, 0x74, + 0x78, 0xf3, 0x8f, 0xf7, 0xdf, 0xfa, 0xa2, 0xbf, 0x01, 0x4f, 0x94, 0x1a, + 0xa1, 0x27, 0xaa, 0x93, 0x57, 0xe4, 0x49, 0x07, 0xb9, 0xd7, 0x49, 0xdb, + 0x73, 0x1e, 0xa1, 0x7e, 0xf1, 0x19, 0x87, 0x8e, 0x5a, 0x89, 0xb7, 0x02, + 0x19, 0x13, 0xdb, 0x20, 0x3f, 0x5f, 0x49, 0xc9, 0xb7, 0xcf, 0x52, 0xc3, + 0xd2, 0xf7, 0x90, 0x52, 0xf7, 0xaa, 0x39, 0x66, 0x78, 0x7a, 0x5b, 0xb3, + 0xa7, 0xe9, 0x33, 0xc6, 0x4f, 0xe6, 0x78, 0xf3, 0x4b, 0x51, 0xf1, 0xed, + 0x15, 0xeb}; + constexpr uint8_t kExpectedCdiAttest_DescriptorInput[32] = { 0x20, 0xd5, 0x0c, 0x68, 0x5a, 0xd9, 0xe2, 0xdf, 0x77, 0x60, 0x78, 0x68, 0x19, 0x00, 0x24, 0xc2, 0x04, 0x4f, 0xb8, 0xde, 0x79, 0xaa, @@ -919,6 +1019,8 @@ constexpr uint8_t kExpectedX509P256Cert_DescriptorInput[927] = { 0x89, 0xe2, 0xa6, 0x14, 0x0c, 0xfa, 0xc2, 0xb1, 0xdd, 0x0c, 0x0a, 0x78, 0xac, 0x60, 0x19}; +constexpr uint8_t kExpectedX509P384Cert_DescriptorInput[0] = {}; + constexpr uint8_t kExpectedCborEd25519Cert_DescriptorInput[667] = { 0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x59, 0x02, 0x50, 0xab, 0x01, 0x78, 0x28, 0x34, 0x37, 0x35, 0x37, 0x30, 0x38, 0x65, 0x62, 0x33, 0x62, 0x34, @@ -979,6 +1081,72 @@ constexpr uint8_t kExpectedCborEd25519Cert_DescriptorInput[667] = { constexpr uint8_t kExpectedCborP256Cert_DescriptorInput[0] = {}; +constexpr uint8_t kExpectedCborP384Cert_DescriptorInput[768] = { + 0x84, 0x44, 0xa1, 0x01, 0x38, 0x22, 0xa0, 0x59, 0x02, 0x94, 0xab, 0x01, + 0x78, 0x28, 0x35, 0x64, 0x38, 0x62, 0x36, 0x62, 0x65, 0x37, 0x63, 0x65, + 0x33, 0x65, 0x64, 0x65, 0x36, 0x61, 0x32, 0x34, 0x31, 0x38, 0x30, 0x31, + 0x34, 0x35, 0x32, 0x33, 0x65, 0x36, 0x63, 0x39, 0x64, 0x63, 0x38, 0x37, + 0x65, 0x39, 0x38, 0x63, 0x63, 0x36, 0x02, 0x78, 0x28, 0x36, 0x66, 0x31, + 0x33, 0x39, 0x63, 0x37, 0x62, 0x32, 0x62, 0x31, 0x36, 0x61, 0x63, 0x38, + 0x31, 0x30, 0x32, 0x34, 0x64, 0x35, 0x37, 0x34, 0x39, 0x36, 0x62, 0x31, + 0x62, 0x37, 0x61, 0x31, 0x33, 0x66, 0x64, 0x33, 0x65, 0x38, 0x30, 0x37, + 0x66, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x40, 0xb7, 0xd4, 0x0c, 0xcb, + 0x22, 0x5b, 0xa5, 0x78, 0x8f, 0x98, 0xff, 0x9e, 0x86, 0x93, 0x75, 0xf6, + 0x90, 0xac, 0x50, 0xcf, 0x9e, 0xbd, 0x0a, 0xfe, 0xb1, 0xd9, 0xc2, 0x4e, + 0x52, 0x19, 0xe4, 0xde, 0x29, 0xe5, 0x61, 0xf3, 0xf9, 0x29, 0xe8, 0x40, + 0x87, 0x7a, 0xdd, 0x17, 0x48, 0x05, 0x89, 0x7e, 0x2b, 0xcb, 0x54, 0x79, + 0xcc, 0x66, 0xf1, 0xb3, 0x13, 0x29, 0x0c, 0x68, 0x96, 0xb2, 0xbb, 0x8f, + 0x3a, 0x00, 0x47, 0x44, 0x51, 0x58, 0x64, 0x6c, 0x46, 0x01, 0x33, 0x26, + 0x73, 0x4b, 0x22, 0x65, 0xfd, 0xfa, 0x58, 0xd7, 0x57, 0x3e, 0x95, 0x59, + 0xe0, 0x3a, 0xc3, 0xb9, 0xf7, 0xc8, 0x0e, 0x98, 0x80, 0x8c, 0xf5, 0xc4, + 0xb8, 0xaf, 0xe3, 0x16, 0x84, 0x25, 0xa5, 0x35, 0x5d, 0x17, 0x72, 0x56, + 0x8f, 0x8e, 0xec, 0x2f, 0x5a, 0x74, 0x60, 0x77, 0x2a, 0x6e, 0x90, 0xc0, + 0x4e, 0x9f, 0x87, 0x6b, 0xf4, 0x8d, 0x9c, 0x66, 0xe3, 0x0b, 0xd2, 0x10, + 0x35, 0x21, 0xa8, 0x1d, 0xa2, 0x31, 0x17, 0xe7, 0x0c, 0xdf, 0x18, 0xf7, + 0x94, 0xe4, 0xd1, 0xca, 0x32, 0x7d, 0xf2, 0x63, 0x23, 0x1d, 0xbc, 0x84, + 0x74, 0x61, 0xdb, 0x87, 0xf2, 0xab, 0x72, 0xad, 0xaf, 0x08, 0xf8, 0x3a, + 0x00, 0x47, 0x44, 0x53, 0x58, 0x28, 0x1b, 0x40, 0xc1, 0xa9, 0x77, 0x60, + 0xeb, 0xc3, 0x67, 0xf0, 0x5f, 0x6a, 0xe1, 0x5e, 0x20, 0xc2, 0x51, 0x68, + 0x4d, 0x82, 0x48, 0x8b, 0x03, 0x32, 0x16, 0x79, 0x88, 0x14, 0x37, 0x78, + 0x7f, 0x16, 0x9a, 0x06, 0xfd, 0xc0, 0x8a, 0x15, 0x80, 0x62, 0x3a, 0x00, + 0x47, 0x44, 0x52, 0x58, 0x40, 0x45, 0x00, 0xe9, 0x5c, 0xbd, 0x00, 0x57, + 0x04, 0x55, 0x87, 0x6c, 0xbd, 0x2f, 0xea, 0x41, 0x9c, 0x66, 0x42, 0x51, + 0x41, 0xbb, 0x44, 0xed, 0x0e, 0xe9, 0x66, 0xcf, 0xd5, 0x10, 0x73, 0x0d, + 0x4b, 0x48, 0xe4, 0x7a, 0x53, 0x35, 0x01, 0x0e, 0x6d, 0x15, 0x55, 0xc5, + 0xb7, 0xd2, 0xd5, 0x36, 0xb6, 0xbc, 0x7e, 0xb0, 0xf3, 0x3d, 0xe6, 0x19, + 0x78, 0x62, 0xeb, 0x02, 0x57, 0x39, 0x56, 0x73, 0x4f, 0x3a, 0x00, 0x47, + 0x44, 0x54, 0x58, 0x40, 0x22, 0x52, 0x60, 0x17, 0xef, 0x2c, 0xa1, 0xf6, + 0xcb, 0xed, 0x39, 0xd5, 0xe2, 0xaa, 0x65, 0x20, 0xfb, 0xad, 0x82, 0x93, + 0xe5, 0x78, 0x23, 0x22, 0x97, 0xc1, 0x6e, 0x6a, 0x4e, 0x36, 0xd7, 0x6a, + 0x61, 0x39, 0x08, 0x21, 0xd4, 0xfe, 0x92, 0x5f, 0x36, 0x2d, 0xeb, 0x5d, + 0xbb, 0x32, 0x8b, 0xe3, 0x94, 0x4f, 0xbe, 0x1b, 0x21, 0xf9, 0xcc, 0x23, + 0x73, 0x41, 0xb6, 0xb9, 0xb6, 0x98, 0xd0, 0xbc, 0x3a, 0x00, 0x47, 0x44, + 0x55, 0x58, 0x41, 0x92, 0xd6, 0x97, 0xb3, 0x83, 0xdf, 0xe7, 0x8c, 0xc7, + 0xbc, 0x4a, 0xfc, 0xea, 0x76, 0xc0, 0x53, 0x66, 0xbd, 0x2c, 0x1e, 0x10, + 0x31, 0x90, 0x80, 0x11, 0x2d, 0x08, 0x4d, 0x7c, 0x39, 0x76, 0xdc, 0x73, + 0xe7, 0x1c, 0x16, 0x62, 0xd5, 0x59, 0xd7, 0x49, 0x2b, 0x6a, 0xa2, 0x36, + 0x67, 0x57, 0xd1, 0xf2, 0xf9, 0xaf, 0x13, 0xd7, 0xa3, 0xe4, 0xd3, 0x39, + 0x5b, 0x02, 0x78, 0xb1, 0xe0, 0x09, 0x70, 0xa2, 0x3a, 0x00, 0x47, 0x44, + 0x56, 0x41, 0x00, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58, 0x71, 0xa6, 0x01, + 0x02, 0x03, 0x38, 0x22, 0x04, 0x81, 0x02, 0x20, 0x02, 0x21, 0x58, 0x30, + 0xb4, 0x02, 0x19, 0x48, 0xca, 0xdd, 0x23, 0x4b, 0x92, 0x91, 0x22, 0x8d, + 0xa8, 0x80, 0x85, 0xc0, 0xf9, 0x23, 0xe4, 0x89, 0xbd, 0x91, 0x8d, 0xf3, + 0x8f, 0xa3, 0x73, 0x60, 0x70, 0x19, 0xc6, 0x33, 0x76, 0xbf, 0xd4, 0x60, + 0xfa, 0xdc, 0xde, 0x46, 0x58, 0x51, 0x13, 0x1d, 0x73, 0x81, 0x79, 0xff, + 0x22, 0x58, 0x30, 0x39, 0x79, 0x1b, 0x49, 0x6a, 0xcf, 0x37, 0x8f, 0xf4, + 0x1a, 0xc2, 0x29, 0xb5, 0x80, 0x2f, 0x7b, 0x2b, 0x0a, 0x27, 0x96, 0xb3, + 0xb2, 0xc1, 0xc6, 0x6f, 0xb4, 0x16, 0xa4, 0x78, 0x76, 0x73, 0x6f, 0xcb, + 0xf5, 0x7d, 0x26, 0xc2, 0x37, 0xe9, 0x58, 0x98, 0xeb, 0xef, 0x11, 0x7c, + 0x8d, 0x1d, 0x4b, 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20, 0x58, 0x60, + 0x12, 0xa5, 0xb9, 0xc3, 0xbf, 0x06, 0x10, 0x7d, 0x22, 0x4b, 0xc1, 0xd2, + 0x58, 0xce, 0xd8, 0x4c, 0x42, 0x48, 0x69, 0x58, 0xf0, 0x83, 0x4a, 0x54, + 0x39, 0x90, 0xec, 0x10, 0xef, 0x76, 0x2e, 0xb0, 0x33, 0x82, 0x6f, 0x93, + 0x2b, 0xc3, 0xb2, 0xc4, 0x6a, 0xcb, 0xde, 0x4c, 0x21, 0x62, 0x56, 0x07, + 0xc7, 0xb1, 0xd6, 0xde, 0xff, 0x95, 0x2c, 0xce, 0xbf, 0x3c, 0x0b, 0xdc, + 0xd0, 0xc5, 0x39, 0x6d, 0xc3, 0x59, 0x23, 0x7b, 0x54, 0x11, 0xf4, 0xb1, + 0x33, 0x66, 0x94, 0x47, 0x3c, 0x14, 0x99, 0x0a, 0xca, 0xa9, 0x84, 0xf4, + 0xc9, 0x24, 0xfd, 0x11, 0xe7, 0xcc, 0x37, 0x6f, 0x12, 0x2b, 0x1a, 0xa7}; + } // namespace test } // namespace dice diff --git a/include/dice/test_utils.h b/include/dice/test_utils.h index 776e932..afc400a 100644 --- a/include/dice/test_utils.h +++ b/include/dice/test_utils.h @@ -30,6 +30,7 @@ enum CertificateType { enum KeyType { KeyType_Ed25519, KeyType_P256, + KeyType_P384, }; struct DiceStateForTest { diff --git a/src/boringssl_ecdsa_utils.c b/src/boringssl_ecdsa_utils.c new file mode 100644 index 0000000..9513cd4 --- /dev/null +++ b/src/boringssl_ecdsa_utils.c @@ -0,0 +1,297 @@ +// Copyright 2022 Google LLC +// +// 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 +// +// https://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. + +// This is an implementation of the crypto operations that uses boringssl. The +// algorithms used are SHA512, HKDF-SHA512, and ECDSA P384-SHA384. + +#include "dice/boringssl_ecdsa_utils.h" + +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include "openssl/bn.h" +#include "openssl/crypto.h" +#include "openssl/ec.h" +#include "openssl/ec_key.h" +#include "openssl/ecdsa.h" +#include "openssl/evp.h" +#include "openssl/hkdf.h" +#include "openssl/hmac.h" +#include "openssl/is_boringssl.h" +#include "openssl/sha.h" + +static int hmac(uint8_t k[64], uint8_t in[64], uint8_t *out, + unsigned int out_len) { + int ret = 0; + + if (out_len > 64 || out_len < 0) { + goto out; + } + HMAC_CTX ctx; + HMAC_CTX_init(&ctx); + if (1 != HMAC_Init_ex(&ctx, k, 64, EVP_sha512(), NULL /* impl */)) { + goto out; + } + if (1 != HMAC_Update(&ctx, in, 64)) { + goto out; + } + ret = HMAC_Final(&ctx, out, &out_len); + HMAC_CTX_cleanup(&ctx); + +out: + return ret; +} + +static int hmac3(uint8_t k[64], uint8_t in1[64], uint8_t in2, + const uint8_t *in3, unsigned int in3_len, uint8_t out[64]) { + int ret = 0; + + HMAC_CTX ctx; + HMAC_CTX_init(&ctx); + if (1 != HMAC_Init_ex(&ctx, k, 64, EVP_sha512(), NULL /* impl */)) { + goto out; + } + if (1 != HMAC_Update(&ctx, in1, 64)) { + goto out; + } + if (1 != HMAC_Update(&ctx, &in2, 1)) { + goto out; + } + if (in3 != NULL && in3_len > 0) { + if (1 != HMAC_Update(&ctx, in3, in3_len)) { + goto out; + } + } + unsigned int out_len = 64; + ret = HMAC_Final(&ctx, out, &out_len); + HMAC_CTX_cleanup(&ctx); + +out: + return ret; +} + +// Algorithm from section 3.2 of IETF RFC6979 +static BIGNUM *derivePrivateKey(const EC_GROUP *group, const uint8_t *seed, + size_t seed_size, uint8_t *private_key, + size_t private_key_len) { + BIGNUM *candidate = NULL; + uint8_t v[64]; + uint8_t k[64]; + memset(v, 1, 64); + memset(k, 0, 64); + + if (1 != hmac3(k, v, 0x00, seed, (unsigned int)seed_size, k)) { + goto err; + } + if (1 != hmac(k, v, v, sizeof(v))) { + goto err; + } + if (1 != hmac3(k, v, 0x01, seed, (unsigned int)seed_size, k)) { + goto err; + } + do { + if (1 != hmac(k, v, v, sizeof(v))) { + goto err; + } + if (1 != hmac(k, v, private_key, private_key_len)) { + goto err; + } + if (1 != hmac3(k, v, 0x00, NULL, 0, k)) { + goto err; + } + candidate = BN_bin2bn(private_key, private_key_len, NULL); + if (!candidate) { + goto err; + } + } while (BN_cmp(candidate, EC_GROUP_get0_order(group)) >= 0 || + BN_is_zero(candidate)); + goto out; + +err: + BN_clear_free(candidate); + candidate = NULL; +out: + return candidate; +} + +int P384KeypairFromSeed(uint8_t public_key[P384_PUBLIC_KEY_SIZE], + uint8_t private_key[P384_PRIVATE_KEY_SIZE], + const uint8_t seed[DICE_PRIVATE_KEY_SEED_SIZE]) { + int ret = 0; + EC_POINT *publicKey = NULL; + BIGNUM *pD = NULL; + BIGNUM *x = NULL; + BIGNUM *y = NULL; + + EC_KEY *key = EC_KEY_new_by_curve_name(NID_secp384r1); + if (!key) { + goto out; + } + const EC_GROUP *group = EC_KEY_get0_group(key); + if (!group) { + goto out; + } + publicKey = EC_POINT_new(group); + if (!publicKey) { + goto out; + } + + pD = derivePrivateKey(group, seed, DICE_PRIVATE_KEY_SEED_SIZE, private_key, + P384_PRIVATE_KEY_SIZE); + if (!pD) { + goto out; + } + if (1 != BN_bn2bin_padded(private_key, P384_PRIVATE_KEY_SIZE, pD)) { + goto out; + } + if (1 != EC_KEY_set_private_key(key, pD)) { + goto out; + } + if (1 != EC_POINT_mul(group, publicKey, pD, NULL, NULL, NULL)) { + goto out; + } + x = BN_new(); + if (!x) { + goto out; + } + y = BN_new(); + if (!y) { + goto out; + } + if (1 != EC_POINT_get_affine_coordinates_GFp(group, publicKey, x, y, NULL)) { + goto out; + } + if (BN_num_bytes(x) > P384_PRIVATE_KEY_SIZE) { + goto out; + } + BN_bn2bin(x, &public_key[0]); + if (BN_num_bytes(y) > P384_PRIVATE_KEY_SIZE) { + goto out; + } + BN_bn2bin(y, &public_key[P384_PRIVATE_KEY_SIZE]); + ret = 1; + +out: + EC_POINT_free(publicKey); + BN_clear_free(x); + BN_clear_free(y); + EC_KEY_free(key); + BN_clear_free(pD); + + return ret; +} + +int P384Sign(uint8_t signature[P384_SIGNATURE_SIZE], const uint8_t *message, + size_t message_size, + const uint8_t private_key[P384_PRIVATE_KEY_SIZE]) { + int ret = 0; + BIGNUM *pD = NULL; + EC_KEY *key = NULL; + uint8_t output[48]; + ECDSA_SIG *sig = NULL; + + pD = BN_bin2bn(private_key, P384_PRIVATE_KEY_SIZE, NULL); + if (!pD) { + goto out; + } + key = EC_KEY_new_by_curve_name(NID_secp384r1); + if (!key) { + goto out; + } + if (1 != EC_KEY_set_private_key(key, pD)) { + goto out; + } + SHA384(message, message_size, output); + sig = ECDSA_do_sign(output, 48, key); + if (!sig) { + goto out; + } + + if (BN_num_bytes(sig->r) > P384_PRIVATE_KEY_SIZE) { + goto out; + } + BN_bn2bin(sig->r, &signature[0]); + if (BN_num_bytes(sig->s) > P384_PRIVATE_KEY_SIZE) { + goto out; + } + BN_bn2bin(sig->s, &signature[P384_PRIVATE_KEY_SIZE]); + ret = 1; + +out: + EC_KEY_free(key); + BN_clear_free(pD); + ECDSA_SIG_free(sig); + return ret; +} + +int P384Verify(const uint8_t *message, size_t message_size, + const uint8_t signature[P384_SIGNATURE_SIZE], + const uint8_t public_key[P384_PUBLIC_KEY_SIZE]) { + int ret = 0; + uint8_t output[48]; + EC_KEY *key = NULL; + BIGNUM *bn_ret = NULL; + BIGNUM *x = NULL; + BIGNUM *y = NULL; + ECDSA_SIG *sig = NULL; + + SHA384(message, message_size, output); + key = EC_KEY_new_by_curve_name(NID_secp384r1); + if (!key) { + goto out; + } + x = BN_new(); + if (!x) { + goto out; + } + bn_ret = BN_bin2bn(&public_key[0], P384_PUBLIC_KEY_SIZE / 2, x); + if (!bn_ret) { + goto out; + } + y = BN_new(); + if (!y) { + goto out; + } + bn_ret = BN_bin2bn(&public_key[P384_PUBLIC_KEY_SIZE / 2], P384_PUBLIC_KEY_SIZE / 2, y); + if (!bn_ret) { + goto out; + } + if (1 != EC_KEY_set_public_key_affine_coordinates(key, x, y)) { + goto out; + } + + + sig = ECDSA_SIG_new(); + if (!sig) { + goto out; + } + bn_ret = BN_bin2bn(&signature[0], P384_SIGNATURE_SIZE / 2, sig->r); + if (!bn_ret) { + goto out; + } + bn_ret = BN_bin2bn(&signature[P384_SIGNATURE_SIZE / 2], P384_SIGNATURE_SIZE / 2, + sig->s); + if (!bn_ret) { + goto out; + } + ret = ECDSA_do_verify(output, 48, sig, key); + +out: + BN_clear_free(y); + BN_clear_free(x); + EC_KEY_free(key); + ECDSA_SIG_free(sig); + return ret; +} diff --git a/src/boringssl_p384_ops.c b/src/boringssl_p384_ops.c new file mode 100644 index 0000000..ca5e88b --- /dev/null +++ b/src/boringssl_p384_ops.c @@ -0,0 +1,69 @@ +// Copyright 2022 Google LLC +// +// 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 +// +// https://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. + +// This is an implementation of the crypto operations that uses boringssl. The +// algorithms used are SHA512, HKDF-SHA512, and Ed25519-SHA512. + +#include <stdint.h> +#include <stdio.h> + +#include "dice/boringssl_ecdsa_utils.h" +#include "dice/dice.h" +#include "dice/ops.h" + +#if DICE_PRIVATE_KEY_SEED_SIZE != 32 +#error "Private key seed is expected to be 32 bytes." +#endif +#if DICE_PUBLIC_KEY_SIZE != 96 +#error "This P-384 implementation needs 96 bytes to store the public key." +#endif +#if DICE_PRIVATE_KEY_SIZE != 48 +#error "P-384 needs 48 bytes for the private key." +#endif +#if DICE_SIGNATURE_SIZE != 96 +#error "P-384 needs 96 bytes to store the signature." +#endif + +DiceResult DiceKeypairFromSeed(void* context_not_used, + const uint8_t seed[DICE_PRIVATE_KEY_SEED_SIZE], + uint8_t public_key[DICE_PUBLIC_KEY_SIZE], + uint8_t private_key[DICE_PRIVATE_KEY_SIZE]) { + (void)context_not_used; + if (1 == P384KeypairFromSeed(public_key, private_key, seed)) { + return kDiceResultOk; + } + return kDiceResultPlatformError; +} + +DiceResult DiceSign(void* context_not_used, const uint8_t* message, + size_t message_size, + const uint8_t private_key[DICE_PRIVATE_KEY_SIZE], + uint8_t signature[DICE_SIGNATURE_SIZE]) { + (void)context_not_used; + if (1 == P384Sign(signature, message, message_size, private_key)) { + return kDiceResultOk; + } + return kDiceResultPlatformError; +} + +DiceResult DiceVerify(void* context_not_used, const uint8_t* message, + size_t message_size, + const uint8_t signature[DICE_SIGNATURE_SIZE], + const uint8_t public_key[DICE_PUBLIC_KEY_SIZE]) { + (void)context_not_used; + if (1 == P384Verify(message, message_size, signature, public_key)) { + return kDiceResultOk; + } + return kDiceResultPlatformError; +} diff --git a/src/cbor_p384_cert_op.c b/src/cbor_p384_cert_op.c new file mode 100644 index 0000000..de078b6 --- /dev/null +++ b/src/cbor_p384_cert_op.c @@ -0,0 +1,80 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +// This is a DiceGenerateCertificate implementation that generates a CWT-style +// CBOR certificate using the ED25519-SHA512 signature scheme. + +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#include "dice/cbor_writer.h" +#include "dice/dice.h" +#include "dice/ops.h" +#include "dice/ops/trait/cose.h" +#include "dice/utils.h" + +#if DICE_PUBLIC_KEY_SIZE != 96 +#error "96 bytes needed to store the public key." +#endif +#if DICE_SIGNATURE_SIZE != 96 +#error "96 bytes needed to store the signature." +#endif + +DiceResult DiceCoseEncodePublicKey( + void* context_not_used, const uint8_t public_key[DICE_PUBLIC_KEY_SIZE], + size_t buffer_size, uint8_t* buffer, size_t* encoded_size) { + (void)context_not_used; + + // Constants per RFC 8152. + const int64_t kCoseKeyKtyLabel = 1; + const int64_t kCoseKeyAlgLabel = 3; + const int64_t kCoseKeyAlgValue = DICE_COSE_KEY_ALG_VALUE; + const int64_t kCoseKeyOpsLabel = 4; + const int64_t kCoseKeyOpsValue = 2; // Verify + const int64_t kCoseKeyKtyValue = 2; // EC2 + const int64_t kCoseEc2CrvLabel = -1; + const int64_t kCoseEc2CrvValue = 2; // P-384 + const int64_t kCoseEc2XLabel = -2; + const int64_t kCoseEc2YLabel = -3; + + struct CborOut out; + CborOutInit(buffer, buffer_size, &out); + CborWriteMap(/*num_pairs=*/6, &out); + // Add the key type. + CborWriteInt(kCoseKeyKtyLabel, &out); + CborWriteInt(kCoseKeyKtyValue, &out); + // Add the algorithm. + CborWriteInt(kCoseKeyAlgLabel, &out); + CborWriteInt(kCoseKeyAlgValue, &out); + // Add the KeyOps. + CborWriteInt(kCoseKeyOpsLabel, &out); + CborWriteArray(/*num_elements=*/1, &out); + CborWriteInt(kCoseKeyOpsValue, &out); + // Add the curve. + CborWriteInt(kCoseEc2CrvLabel, &out); + CborWriteInt(kCoseEc2CrvValue, &out); + // Add the subject public key x and y coordinates + CborWriteInt(kCoseEc2XLabel, &out); + CborWriteBstr(/*data_size=*/DICE_PUBLIC_KEY_SIZE / 2, &public_key[0], &out); + CborWriteInt(kCoseEc2YLabel, &out); + CborWriteBstr(/*data_size=*/DICE_PUBLIC_KEY_SIZE / 2, + &public_key[DICE_PUBLIC_KEY_SIZE / 2], &out); + + if (CborOutOverflowed(&out)) { + return kDiceResultBufferTooSmall; + } + *encoded_size = CborOutSize(&out); + return kDiceResultOk; +} diff --git a/src/cbor_p384_cert_op_test.cc b/src/cbor_p384_cert_op_test.cc new file mode 100644 index 0000000..21d0331 --- /dev/null +++ b/src/cbor_p384_cert_op_test.cc @@ -0,0 +1,254 @@ +// Copyright 2022 Google LLC +// +// 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 +// +// https://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. + +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> + +#include <memory> + +#include "dice/config.h" +#include "dice/dice.h" +#include "dice/known_test_values.h" +#include "dice/test_framework.h" +#include "dice/test_utils.h" +#include "dice/utils.h" +#include "pw_string/format.h" + +namespace { + +using dice::test::CertificateType_Cbor; +using dice::test::DeriveFakeInputValue; +using dice::test::DiceStateForTest; +using dice::test::KeyType_P384; + +TEST(DiceOpsTest, KnownAnswerZeroInput) { + DiceStateForTest current_state = {}; + DiceStateForTest next_state = {}; + DiceInputValues input_values = {}; + DiceResult result = DiceMainFlow( + NULL, current_state.cdi_attest, current_state.cdi_seal, &input_values, + sizeof(next_state.certificate), next_state.certificate, + &next_state.certificate_size, next_state.cdi_attest, next_state.cdi_seal); + EXPECT_EQ(kDiceResultOk, result); + DumpState(CertificateType_Cbor, KeyType_P384, "zero_input", next_state); + // The CDI values should be deterministic. + ASSERT_EQ(sizeof(next_state.cdi_attest), + sizeof(dice::test::kExpectedCdiAttest_ZeroInput)); + EXPECT_EQ(0, memcmp(next_state.cdi_attest, + dice::test::kExpectedCdiAttest_ZeroInput, DICE_CDI_SIZE)); + ASSERT_EQ(sizeof(next_state.cdi_seal), + sizeof(dice::test::kExpectedCdiSeal_ZeroInput)); + EXPECT_EQ(0, memcmp(next_state.cdi_seal, + dice::test::kExpectedCdiSeal_ZeroInput, DICE_CDI_SIZE)); + ASSERT_EQ(sizeof(dice::test::kExpectedCborP384Cert_ZeroInput), + next_state.certificate_size); + // Comparing everything except for the signature, since ECDSA signatures are + // not deterministic + EXPECT_EQ(0, memcmp(dice::test::kExpectedCborP384Cert_ZeroInput, + next_state.certificate, + next_state.certificate_size - DICE_SIGNATURE_SIZE)); +} + +TEST(DiceOpsTest, KnownAnswerHashOnlyInput) { + DiceStateForTest current_state = {}; + DeriveFakeInputValue("cdi_attest", DICE_CDI_SIZE, current_state.cdi_attest); + DeriveFakeInputValue("cdi_seal", DICE_CDI_SIZE, current_state.cdi_seal); + DiceStateForTest next_state = {}; + DiceInputValues input_values = {}; + DeriveFakeInputValue("code_hash", DICE_HASH_SIZE, input_values.code_hash); + DeriveFakeInputValue("authority_hash", DICE_HASH_SIZE, + input_values.authority_hash); + input_values.config_type = kDiceConfigTypeInline; + DeriveFakeInputValue("inline_config", DICE_INLINE_CONFIG_SIZE, + input_values.config_value); + + DiceResult result = DiceMainFlow( + NULL, current_state.cdi_attest, current_state.cdi_seal, &input_values, + sizeof(next_state.certificate), next_state.certificate, + &next_state.certificate_size, next_state.cdi_attest, next_state.cdi_seal); + EXPECT_EQ(kDiceResultOk, result); + DumpState(CertificateType_Cbor, KeyType_P384, "hash_only_input", next_state); + ASSERT_EQ(sizeof(next_state.cdi_attest), + sizeof(dice::test::kExpectedCdiAttest_HashOnlyInput)); + EXPECT_EQ( + 0, memcmp(next_state.cdi_attest, + dice::test::kExpectedCdiAttest_HashOnlyInput, DICE_CDI_SIZE)); + ASSERT_EQ(sizeof(next_state.cdi_seal), + sizeof(dice::test::kExpectedCdiSeal_HashOnlyInput)); + EXPECT_EQ( + 0, memcmp(next_state.cdi_seal, dice::test::kExpectedCdiSeal_HashOnlyInput, + DICE_CDI_SIZE)); + ASSERT_EQ(sizeof(dice::test::kExpectedCborP384Cert_HashOnlyInput), + next_state.certificate_size); + EXPECT_EQ(0, memcmp(dice::test::kExpectedCborP384Cert_HashOnlyInput, + next_state.certificate, + next_state.certificate_size - DICE_SIGNATURE_SIZE)); +} + +TEST(DiceOpsTest, KnownAnswerDescriptorInput) { + DiceStateForTest current_state = {}; + DeriveFakeInputValue("cdi_attest", DICE_CDI_SIZE, current_state.cdi_attest); + DeriveFakeInputValue("cdi_seal", DICE_CDI_SIZE, current_state.cdi_seal); + + DiceStateForTest next_state = {}; + + DiceInputValues input_values = {}; + DeriveFakeInputValue("code_hash", DICE_HASH_SIZE, input_values.code_hash); + uint8_t code_descriptor[100]; + DeriveFakeInputValue("code_desc", sizeof(code_descriptor), code_descriptor); + input_values.code_descriptor = code_descriptor; + input_values.code_descriptor_size = sizeof(code_descriptor); + + uint8_t config_descriptor[40]; + DeriveFakeInputValue("config_desc", sizeof(config_descriptor), + config_descriptor); + input_values.config_descriptor = config_descriptor; + input_values.config_descriptor_size = sizeof(config_descriptor); + input_values.config_type = kDiceConfigTypeDescriptor; + + DeriveFakeInputValue("authority_hash", DICE_HASH_SIZE, + input_values.authority_hash); + uint8_t authority_descriptor[65]; + DeriveFakeInputValue("authority_desc", sizeof(authority_descriptor), + authority_descriptor); + input_values.authority_descriptor = authority_descriptor; + input_values.authority_descriptor_size = sizeof(authority_descriptor); + + DiceResult result = DiceMainFlow( + NULL, current_state.cdi_attest, current_state.cdi_seal, &input_values, + sizeof(next_state.certificate), next_state.certificate, + &next_state.certificate_size, next_state.cdi_attest, next_state.cdi_seal); + EXPECT_EQ(kDiceResultOk, result); + DumpState(CertificateType_Cbor, KeyType_P384, "descriptor_input", next_state); + // Both CDI values and the certificate should be deterministic. + EXPECT_EQ( + 0, memcmp(next_state.cdi_attest, + dice::test::kExpectedCdiAttest_DescriptorInput, DICE_CDI_SIZE)); + EXPECT_EQ( + 0, memcmp(next_state.cdi_seal, + dice::test::kExpectedCdiSeal_DescriptorInput, DICE_CDI_SIZE)); + ASSERT_EQ(sizeof(dice::test::kExpectedCborP384Cert_DescriptorInput), + next_state.certificate_size); + EXPECT_EQ(0, memcmp(dice::test::kExpectedCborP384Cert_DescriptorInput, + next_state.certificate, + next_state.certificate_size - DICE_SIGNATURE_SIZE)); +} + +TEST(DiceOpsTest, NonZeroMode) { + constexpr size_t kModeOffsetInCert = 316; + DiceStateForTest current_state = {}; + DiceStateForTest next_state = {}; + DiceInputValues input_values = {}; + input_values.mode = kDiceModeDebug; + DiceResult result = DiceMainFlow( + NULL, current_state.cdi_attest, current_state.cdi_seal, &input_values, + sizeof(next_state.certificate), next_state.certificate, + &next_state.certificate_size, next_state.cdi_attest, next_state.cdi_seal); + EXPECT_EQ(kDiceResultOk, result); + EXPECT_EQ(kDiceModeDebug, next_state.certificate[kModeOffsetInCert]); +} + +TEST(DiceOpsTest, LargeInputs) { + constexpr uint8_t kBigBuffer[1024 * 1024] = {}; + DiceStateForTest current_state = {}; + DiceStateForTest next_state = {}; + DiceInputValues input_values = {}; + input_values.code_descriptor = kBigBuffer; + input_values.code_descriptor_size = sizeof(kBigBuffer); + DiceResult result = DiceMainFlow( + NULL, current_state.cdi_attest, current_state.cdi_seal, &input_values, + sizeof(next_state.certificate), next_state.certificate, + &next_state.certificate_size, next_state.cdi_attest, next_state.cdi_seal); + EXPECT_EQ(kDiceResultBufferTooSmall, result); +} + +TEST(DiceOpsTest, InvalidConfigType) { + DiceStateForTest current_state = {}; + DiceStateForTest next_state = {}; + DiceInputValues input_values = {}; + input_values.config_type = (DiceConfigType)55; + DiceResult result = DiceMainFlow( + NULL, current_state.cdi_attest, current_state.cdi_seal, &input_values, + sizeof(next_state.certificate), next_state.certificate, + &next_state.certificate_size, next_state.cdi_attest, next_state.cdi_seal); + EXPECT_EQ(kDiceResultInvalidInput, result); +} + +TEST(DiceOpsTest, PartialCertChain) { + constexpr size_t kNumLayers = 7; + DiceStateForTest states[kNumLayers + 1] = {}; + DiceInputValues inputs[kNumLayers] = {}; + for (size_t i = 0; i < kNumLayers; ++i) { + char seed[40]; + pw::string::Format(seed, "code_hash_%zu", i); + DeriveFakeInputValue(seed, DICE_HASH_SIZE, inputs[i].code_hash); + pw::string::Format(seed, "authority_hash_%zu", i); + DeriveFakeInputValue(seed, DICE_HASH_SIZE, inputs[i].authority_hash); + inputs[i].config_type = kDiceConfigTypeInline; + pw::string::Format(seed, "inline_config_%zu", i); + DeriveFakeInputValue(seed, DICE_INLINE_CONFIG_SIZE, inputs[i].config_value); + inputs[i].mode = kDiceModeNormal; + EXPECT_EQ( + kDiceResultOk, + DiceMainFlow(/*context=*/NULL, states[i].cdi_attest, states[i].cdi_seal, + &inputs[i], sizeof(states[i + 1].certificate), + states[i + 1].certificate, &states[i + 1].certificate_size, + states[i + 1].cdi_attest, states[i + 1].cdi_seal)); + char suffix[40]; + pw::string::Format(suffix, "part_cert_chain_%zu", i); + DumpState(CertificateType_Cbor, KeyType_P384, suffix, states[i + 1]); + } + // Use the first derived CDI cert as the 'root' of partial chain. + EXPECT_TRUE(dice::test::VerifyCertificateChain( + CertificateType_Cbor, states[1].certificate, states[1].certificate_size, + &states[2], kNumLayers - 1, /*is_partial_chain=*/true)); +} + +TEST(DiceOpsTest, FullCertChain) { + constexpr size_t kNumLayers = 7; + DiceStateForTest states[kNumLayers + 1] = {}; + DiceInputValues inputs[kNumLayers] = {}; + for (size_t i = 0; i < kNumLayers; ++i) { + char seed[40]; + pw::string::Format(seed, "code_hash_%zu", i); + DeriveFakeInputValue(seed, DICE_HASH_SIZE, inputs[i].code_hash); + pw::string::Format(seed, "authority_hash_%zu", i); + DeriveFakeInputValue(seed, DICE_HASH_SIZE, inputs[i].authority_hash); + inputs[i].config_type = kDiceConfigTypeInline; + pw::string::Format(seed, "inline_config_%zu", i); + DeriveFakeInputValue(seed, DICE_INLINE_CONFIG_SIZE, inputs[i].config_value); + inputs[i].mode = kDiceModeNormal; + EXPECT_EQ( + kDiceResultOk, + DiceMainFlow(/*context=*/NULL, states[i].cdi_attest, states[i].cdi_seal, + &inputs[i], sizeof(states[i + 1].certificate), + states[i + 1].certificate, &states[i + 1].certificate_size, + states[i + 1].cdi_attest, states[i + 1].cdi_seal)); + char suffix[40]; + pw::string::Format(suffix, "full_cert_chain_%zu", i); + DumpState(CertificateType_Cbor, KeyType_P384, suffix, states[i + 1]); + } + // Use a fake self-signed UDS cert as the 'root'. + uint8_t root_certificate[dice::test::kTestCertSize]; + size_t root_certificate_size = 0; + dice::test::CreateFakeUdsCertificate( + NULL, states[0].cdi_attest, CertificateType_Cbor, KeyType_P384, + root_certificate, &root_certificate_size); + EXPECT_TRUE(dice::test::VerifyCertificateChain( + CertificateType_Cbor, root_certificate, root_certificate_size, &states[1], + kNumLayers, /*is_partial_chain=*/false)); +} + +} // namespace diff --git a/src/test_utils.cc b/src/test_utils.cc index 1988f25..cb21d3f 100644 --- a/src/test_utils.cc +++ b/src/test_utils.cc @@ -21,6 +21,7 @@ #include <memory> #include "cose/cose.h" +#include "dice/boringssl_ecdsa_utils.h" #include "dice/dice.h" #include "dice/utils.h" #include "openssl/asn1.h" @@ -36,6 +37,9 @@ #include "openssl/x509v3.h" #include "pw_string/format.h" +// The largest possible public key size among ECDSA P-384, P-256, and ED25519 +#define MAX_PUBLIC_KEY_SIZE 96 + namespace { // A scoped pointer for cn_cbor. @@ -60,6 +64,8 @@ const char* GetKeyTypeStr(dice::test::KeyType key_type) { return "Ed25519"; case dice::test::KeyType_P256: return "P256"; + case dice::test::KeyType_P384: + return "P384"; } return ""; } @@ -161,7 +167,7 @@ class HmacSha512Drbg { bssl::UniquePtr<EVP_PKEY> KeyFromRawKey( const uint8_t raw_key[DICE_PRIVATE_KEY_SEED_SIZE], - dice::test::KeyType key_type, uint8_t raw_public_key[33], + dice::test::KeyType key_type, uint8_t raw_public_key[MAX_PUBLIC_KEY_SIZE], size_t* raw_public_key_size) { if (key_type == dice::test::KeyType_Ed25519) { bssl::UniquePtr<EVP_PKEY> key( @@ -197,7 +203,26 @@ bssl::UniquePtr<EVP_PKEY> KeyFromRawKey( EC_POINT_point2oct(group, pub.get(), POINT_CONVERSION_COMPRESSED, raw_public_key, 33, /*ctx=*/nullptr); return pkey; + } else if (key_type == dice::test::KeyType_P384) { + const size_t kPublicKeySize = 96; + const size_t kPrivateKeySize = 48; + uint8_t pk[kPrivateKeySize]; + P384KeypairFromSeed(raw_public_key, pk, raw_key); + *raw_public_key_size = kPublicKeySize; + + bssl::UniquePtr<EC_KEY> key(EC_KEY_new_by_curve_name(NID_secp384r1)); + BIGNUM* x = BN_new(); + BN_bin2bn(&raw_public_key[0], kPublicKeySize / 2, x); + BIGNUM* y = BN_new(); + BN_bin2bn(&raw_public_key[kPublicKeySize / 2], kPublicKeySize / 2, y); + EC_KEY_set_public_key_affine_coordinates(key.get(), x, y); + BN_clear_free(y); + BN_clear_free(x); + bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new()); + EVP_PKEY_set1_EC_KEY(pkey.get(), key.get()); + return pkey; } + printf("ERROR: Unsupported key type.\n"); return nullptr; } @@ -299,7 +324,7 @@ bool VerifyX509CertificateChain(const uint8_t* root_certificate, return (1 == X509_verify_cert(x509_store_ctx.get())); } -void CreateCborUdsCertificate( +void CreateEd25519CborUdsCertificate( const uint8_t private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE], const uint8_t id[DICE_ID_SIZE], uint8_t certificate[dice::test::kTestCertSize], size_t* certificate_size) { @@ -383,6 +408,121 @@ void CreateCborUdsCertificate( certificate, 0, dice::test::kTestCertSize, sign1.get()); } +void CreateP384CborUdsCertificate( + const uint8_t private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE], + const uint8_t id[DICE_ID_SIZE], + uint8_t certificate[dice::test::kTestCertSize], size_t* certificate_size) { + const int64_t kCwtIssuerLabel = 1; + const int64_t kCwtSubjectLabel = 2; + const int64_t kUdsPublicKeyLabel = -4670552; + const int64_t kUdsKeyUsageLabel = -4670553; + const uint8_t kKeyUsageCertSign = 32; // Bit 5. + const uint8_t kProtectedAttributesCbor[4] = { + 0xa1 /* map(1) */, 0x01 /* alg(1) */, 0x38, 0x22 /* ES384(-34) */}; + const size_t kPublicKeySize = 96; + const size_t kPrivateKeySize = 48; + const size_t kSignatureSize = 96; + + // Public key encoded as a COSE_Key. + uint8_t public_key[kPublicKeySize]; + uint8_t private_key[kPrivateKeySize]; + P384KeypairFromSeed(public_key, private_key, private_key_seed); + cn_cbor_errback error; + ScopedCbor public_key_cbor(cn_cbor_map_create(&error)); + // kty = ec2 + cn_cbor_mapput_int(public_key_cbor.get(), 1, cn_cbor_int_create(2, &error), + &error); + // crv = P-384 + cn_cbor_mapput_int(public_key_cbor.get(), -1, cn_cbor_int_create(2, &error), + &error); + // x = public_key X + cn_cbor_mapput_int( + public_key_cbor.get(), -2, + cn_cbor_data_create(&public_key[0], kPublicKeySize / 2, &error), &error); + // y = public_key Y + cn_cbor_mapput_int(public_key_cbor.get(), -3, + cn_cbor_data_create(&public_key[kPublicKeySize / 2], + kPublicKeySize / 2, &error), + &error); + uint8_t encoded_public_key[200]; + size_t encoded_public_key_size = + cn_cbor_encoder_write(encoded_public_key, 0, 200, public_key_cbor.get()); + + // Simple CWT payload with issuer, subject, and use the same subject public + // key field as a CDI certificate to make verification easy. + char id_hex[41]; + DiceHexEncode(id, DICE_ID_SIZE, id_hex, sizeof(id_hex)); + id_hex[40] = '\0'; + ScopedCbor cwt(cn_cbor_map_create(&error)); + cn_cbor_mapput_int(cwt.get(), kCwtIssuerLabel, + cn_cbor_string_create(id_hex, &error), &error); + cn_cbor_mapput_int(cwt.get(), kCwtSubjectLabel, + cn_cbor_string_create(id_hex, &error), &error); + cn_cbor_mapput_int( + cwt.get(), kUdsPublicKeyLabel, + cn_cbor_data_create(encoded_public_key, encoded_public_key_size, &error), + &error); + uint8_t key_usage_byte = kKeyUsageCertSign; + cn_cbor_mapput_int(cwt.get(), kUdsKeyUsageLabel, + cn_cbor_data_create(&key_usage_byte, 1, &error), &error); + uint8_t payload[dice::test::kTestCertSize]; + size_t payload_size = + cn_cbor_encoder_write(payload, 0, dice::test::kTestCertSize, cwt.get()); + + // Signature over COSE Sign1 TBS. + ScopedCbor tbs_cbor(cn_cbor_array_create(&error)); + cn_cbor_array_append(tbs_cbor.get(), + cn_cbor_string_create("Signature1", &error), &error); + cn_cbor_array_append(tbs_cbor.get(), + cn_cbor_data_create(kProtectedAttributesCbor, 4, &error), + &error); + cn_cbor_array_append(tbs_cbor.get(), cn_cbor_data_create(NULL, 0, &error), + &error); + cn_cbor_array_append(tbs_cbor.get(), + cn_cbor_data_create(payload, payload_size, &error), + &error); + uint8_t tbs[dice::test::kTestCertSize]; + size_t tbs_size = + cn_cbor_encoder_write(tbs, 0, dice::test::kTestCertSize, tbs_cbor.get()); + uint8_t signature[kSignatureSize]; + P384Sign(signature, tbs, tbs_size, private_key); + + // COSE Sign1. + ScopedCbor sign1(cn_cbor_array_create(&error)); + cn_cbor_array_append(sign1.get(), + cn_cbor_data_create(kProtectedAttributesCbor, 4, &error), + &error); + cn_cbor_array_append(sign1.get(), cn_cbor_map_create(&error), &error); + cn_cbor_array_append( + sign1.get(), cn_cbor_data_create(payload, payload_size, &error), &error); + cn_cbor_array_append(sign1.get(), + cn_cbor_data_create(signature, kSignatureSize, &error), + &error); + *certificate_size = cn_cbor_encoder_write( + certificate, 0, dice::test::kTestCertSize, sign1.get()); +} + +void CreateCborUdsCertificate( + const uint8_t private_key_seed[DICE_PRIVATE_KEY_SEED_SIZE], + dice::test::KeyType key_type, const uint8_t id[DICE_ID_SIZE], + uint8_t certificate[dice::test::kTestCertSize], size_t* certificate_size) { + switch (key_type) { + case dice::test::KeyType_Ed25519: + CreateEd25519CborUdsCertificate(private_key_seed, id, certificate, + certificate_size); + break; + case dice::test::KeyType_P256: + printf( + "Error: encountered unsupported KeyType P256 when creating CBOR UDS " + "certificate\n"); + break; + case dice::test::KeyType_P384: + CreateP384CborUdsCertificate(private_key_seed, id, certificate, + certificate_size); + break; + } +} + ScopedCbor ExtractCwtFromCborCertificate(const uint8_t* certificate, size_t certificate_size) { cn_cbor_errback error; @@ -624,7 +764,7 @@ void CreateFakeUdsCertificate(void* context, const uint8_t uds[32], uint8_t raw_key[DICE_PRIVATE_KEY_SEED_SIZE]; DiceDeriveCdiPrivateKeySeed(context, uds, raw_key); - uint8_t raw_public_key[33]; + uint8_t raw_public_key[MAX_PUBLIC_KEY_SIZE]; size_t raw_public_key_size = 0; bssl::UniquePtr<EVP_PKEY> key( KeyFromRawKey(raw_key, key_type, raw_public_key, &raw_public_key_size)); @@ -635,7 +775,8 @@ void CreateFakeUdsCertificate(void* context, const uint8_t uds[32], if (cert_type == CertificateType_X509) { CreateX509UdsCertificate(key.get(), id, certificate, certificate_size); } else { - CreateCborUdsCertificate(raw_key, id, certificate, certificate_size); + CreateCborUdsCertificate(raw_key, key_type, id, certificate, + certificate_size); } char filename[100]; diff --git a/third_party/cose-c/BUILD.gn b/third_party/cose-c/BUILD.gn index 3a3175f..e5a46aa 100644 --- a/third_party/cose-c/BUILD.gn +++ b/third_party/cose-c/BUILD.gn @@ -22,6 +22,13 @@ config("external_config_ed25519") { ] } +config("external_config_p384") { + include_dirs = [ + "src/include", + "include/p384", + ] +} + config("internal_config") { visibility = [ ":*" ] # Only targets in this file can depend on this. include_dirs = [ "src/src" ] @@ -44,3 +51,20 @@ pw_static_library("cose-c_ed25519") { "//third_party/cn-cbor:cn-cbor", ] } + +pw_static_library("cose-c_p384") { + public = [ "src/include/cose/cose.h" ] + sources = [ + "cose_p384_deps.cc", + "src/src/Cose.cpp", + "src/src/CoseKey.cpp", + "src/src/Sign1.cpp", + "src/src/cbor.cpp", + ] + public_configs = [ ":external_config_p384" ] + configs = [ ":internal_config" ] + public_deps = [ + "//third_party/boringssl:crypto", + "//third_party/cn-cbor:cn-cbor", + ] +} diff --git a/third_party/cose-c/cose_p384_deps.cc b/third_party/cose-c/cose_p384_deps.cc new file mode 100644 index 0000000..795b080 --- /dev/null +++ b/third_party/cose-c/cose_p384_deps.cc @@ -0,0 +1,148 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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. + +#include <stdint.h> +#include <string.h> + +#include "cose/cose.h" +#include "cose/cose_configure.h" +#include "cose_int.h" +#include "openssl/bn.h" +#include "openssl/ec.h" +#include "openssl/ec_key.h" +#include "openssl/ecdsa.h" +#include "openssl/evp.h" +#include "openssl/hkdf.h" +#include "openssl/is_boringssl.h" +#include "openssl/sha.h" + +// Gets the public key from a well-formed ECDSA P-384 COSE_Key. On +// success populates |public_key| and returns true; public_key must hold 96 bytes +// (uncompressed format). +static bool GetPublicKeyFromCbor(const cn_cbor *key, uint8_t *public_key) { + const int64_t kCoseKeyAlgLabel = 3; + const int64_t kCoseKeyOpsLabel = 4; + const uint64_t kCoseKeyOpsVerify = 2; + const int64_t kCoseAlgEs384 = -35; + + // Mandatory attributes. + cn_cbor *type = cn_cbor_mapget_int(key, COSE_Key_Type); + cn_cbor *curve = cn_cbor_mapget_int(key, COSE_Key_OPK_Curve); + if (!type || !curve) { + return false; + } + if (type->type != CN_CBOR_UINT || curve->type != CN_CBOR_UINT) { + return false; + } + + if (type->v.uint != COSE_Key_Type_EC2 || curve->v.uint != COSE_Curve_P384) { + return false; + } + + cn_cbor *x = cn_cbor_mapget_int(key, COSE_Key_EC2_X); + if (!x || x->type != CN_CBOR_BYTES || x->length != (PUBLIC_KEY_SIZE / 2)) { + return false; + } + + cn_cbor *y = cn_cbor_mapget_int(key, COSE_Key_EC2_Y); + if (!y || y->type != CN_CBOR_BYTES || y->length != (PUBLIC_KEY_SIZE / 2)) { + return false; + } + + cn_cbor *alg = cn_cbor_mapget_int(key, kCoseKeyAlgLabel); + if (alg) { + if (alg->type != CN_CBOR_INT || alg->v.sint != kCoseAlgEs384) { + return false; + } + } + + cn_cbor *ops = cn_cbor_mapget_int(key, kCoseKeyOpsLabel); + if (ops) { + if (ops->type != CN_CBOR_ARRAY || ops->length == 0) { + return false; + } + bool found_verify = false; + for (size_t i = 0; i < ops->length; ++i) { + cn_cbor *item = cn_cbor_index(ops, i); + if (!item || item->type != CN_CBOR_UINT) { + return false; + } + if (item->v.uint == kCoseKeyOpsVerify) { + found_verify = true; + } + } + if (!found_verify) { + return false; + } + } + + memcpy(&public_key[0], x->v.bytes, PUBLIC_KEY_SIZE / 2); + memcpy(&public_key[PUBLIC_KEY_SIZE / 2], y->v.bytes, PUBLIC_KEY_SIZE / 2); + return true; +} + +bool ECDSA_Verify(COSE *cose_signer, int signature_index, COSE_KEY *cose_key, + int cbitsDigest, const byte *message, size_t message_size, + cose_errback *) { + (void)cbitsDigest; + cn_cbor *signature = _COSE_arrayget_int(cose_signer, signature_index); + cn_cbor *key = cose_key->m_cborKey; + if (!signature || !key) { + return false; + } + if (signature->type != CN_CBOR_BYTES || signature->length != PUBLIC_KEY_SIZE) { + return false; + } + uint8_t public_key[PUBLIC_KEY_SIZE]; + if (!GetPublicKeyFromCbor(key, public_key)) { + return false; + } + + // Implementation of ECDSA verification starts here + uint8_t output[48]; + SHA384(message, message_size, output); + EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_secp384r1); + BIGNUM *x = BN_new(); + BN_bin2bn(&public_key[0], 48, x); + BIGNUM *y = BN_new(); + BN_bin2bn(&public_key[48], 48, y); + int result = EC_KEY_set_public_key_affine_coordinates(eckey, x, y); + + BN_clear_free(y); + BN_clear_free(x); + + if (result == 0) { + printf("Setting affine coordinates failed\n"); + return false; + } + + ECDSA_SIG *sig = ECDSA_SIG_new(); + BN_bin2bn(&(signature->v.bytes[0]), 48, sig->r); + BN_bin2bn(&(signature->v.bytes[48]), 48, sig->s); + result = ECDSA_do_verify(output, 48, sig, eckey); + + EC_KEY_free(eckey); + ECDSA_SIG_free(sig); + if (1 != result) { + return false; + } + return true; +} + +// A stub for 'ECDSA_Sign'. This is unused, but helps make linkers happy. +bool ECDSA_Sign(COSE * /*cose_signer*/, int /*signature_index*/, + COSE_KEY * /*cose_key*/, const byte * /*message*/, + size_t /*message_size*/, cose_errback *) { + return false; +} diff --git a/third_party/cose-c/include/p384/cose/cose_configure.h b/third_party/cose-c/include/p384/cose/cose_configure.h new file mode 100644 index 0000000..ebb4469 --- /dev/null +++ b/third_party/cose-c/include/p384/cose/cose_configure.h @@ -0,0 +1,11 @@ +#define USE_ECDSA_SHA_384 +#define PUBLIC_KEY_SIZE 96 + +#define INCLUDE_ENCRYPT 0 +#define INCLUDE_ENCRYPT0 0 +#define INCLUDE_MAC 0 +#define INCLUDE_MAC0 0 +#define INCLUDE_SIGN 0 +#define INCLUDE_SIGN1 1 +#define INCLUDE_COUNTERSIGNATURE 0 +#define INCLUDE_COUNTERSIGNATURE1 0 |