aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAtul Luykx <aluykx@google.com>2023-01-27 23:46:39 +0000
committerCQ Bot Account <pigweed-scoped@luci-project-accounts.iam.gserviceaccount.com>2023-01-27 23:46:39 +0000
commit5915933b017765500eb39051b8b9147188f2f300 (patch)
treeac7b92ef2c586be9d9cd6fadef69a68fa8d6d2b0
parentd99a8e130d70075bb7957d45f4bd87ad849eb16f (diff)
downloadopen-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.gn93
-rw-r--r--generate_test_values.py4
-rw-r--r--include/dice/boringssl_ecdsa_utils.h57
-rw-r--r--include/dice/config/boringssl_ecdsa_p384/dice/config.h25
-rw-r--r--include/dice/known_test_values.h168
-rw-r--r--include/dice/test_utils.h1
-rw-r--r--src/boringssl_ecdsa_utils.c297
-rw-r--r--src/boringssl_p384_ops.c69
-rw-r--r--src/cbor_p384_cert_op.c80
-rw-r--r--src/cbor_p384_cert_op_test.cc254
-rw-r--r--src/test_utils.cc149
-rw-r--r--third_party/cose-c/BUILD.gn24
-rw-r--r--third_party/cose-c/cose_p384_deps.cc148
-rw-r--r--third_party/cose-c/include/p384/cose/cose_configure.h11
14 files changed, 1374 insertions, 6 deletions
diff --git a/BUILD.gn b/BUILD.gn
index 2044cb2..4787252 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -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