Skip to content

Commit 92aaa41

Browse files
bifurcationrcombsclaude
authored
Add tests for custom signing functions (#462)
* Support custom signing routines in SignaturePrivateKey This enables support for signing with non-extractable private keys (e.g. TPM-bound). * Add tests for custom signing functions - Add test wrapping an HPKE-layer private key with SignerFunc - Add macOS Keychain test using Security.framework (conditional) - Link Security.framework on macOS for Keychain tests Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add throwing serialization for non-exportable keys - Replace TLS_SERIALIZABLE with custom operators that throw on non-exportable keys - Add exportable() method to check if key can be serialized - Add tests verifying serialization throws for external signers - Add tests verifying normal keys serialize/deserialize correctly Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Add missing #include <functional> Required for std::function used by SignerFunc. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Fix test to use all_supported_cipher_suites After merging main, update test to use the renamed array. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Fix formatting issues from merge Apply clang-format to fix code style after merging main. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Update CI to use clang-format 19 Upgrade from clang-format 16 to 19 to match modern formatting. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: rcombs <rcombs@rcombs.me> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 65adfde commit 92aaa41

11 files changed

Lines changed: 359 additions & 160 deletions

File tree

.github/workflows/main_ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ jobs:
6262
- name: Run clang-format style check for C/C++ programs
6363
uses: jidicula/clang-format-action@v4.11.0
6464
with:
65-
clang-format-version: 16
65+
clang-format-version: 19
6666
include-regex: '^\./(src|include|test|cmd|lib)/.*\.(cpp|h)$'
6767
fallback-style: 'Mozilla'
6868

include/mls/crypto.h

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,37 @@ struct SignaturePrivateKey
277277
void set_public_key(CipherSuite suite);
278278
std::string to_jwk(CipherSuite suite) const;
279279

280-
TLS_SERIALIZABLE(data)
280+
/// Returns true if this key can be serialized/exported
281+
bool exportable() const { return !_sign_func; }
282+
283+
/// TLS serialization - throws if key is not exportable
284+
friend tls::ostream& operator<<(tls::ostream& str,
285+
const SignaturePrivateKey& obj)
286+
{
287+
if (!obj.exportable()) {
288+
throw std::runtime_error(
289+
"Cannot serialize non-exportable SignaturePrivateKey");
290+
}
291+
return str << obj.data;
292+
}
293+
294+
friend tls::istream& operator>>(tls::istream& str, SignaturePrivateKey& obj)
295+
{
296+
obj._sign_func = nullptr;
297+
return str >> obj.data;
298+
}
299+
300+
friend bool operator==(const SignaturePrivateKey& lhs,
301+
const SignaturePrivateKey& rhs)
302+
{
303+
return lhs.data == rhs.data;
304+
}
305+
306+
friend bool operator!=(const SignaturePrivateKey& lhs,
307+
const SignaturePrivateKey& rhs)
308+
{
309+
return !(lhs == rhs);
310+
}
281311

282312
private:
283313
SignerFunc _sign_func;

lib/hpke/src/signature.cpp

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -299,14 +299,13 @@ static const Signature&
299299
sig_from_jwk(const std::string& jwk_json)
300300
{
301301
using KeyTypeAndCurve = std::tuple<std::string, std::string>;
302-
static const auto alg_sig_map = std::map<KeyTypeAndCurve, const Signature&>
303-
{
302+
static const auto alg_sig_map = std::map<KeyTypeAndCurve, const Signature&>{
304303
{ { "EC", "P-256" }, Signature::get<Signature::ID::P256_SHA256>() },
305-
{ { "EC", "P-384" }, Signature::get<Signature::ID::P384_SHA384>() },
306-
{ { "EC", "P-512" }, Signature::get<Signature::ID::P521_SHA512>() },
307-
{ { "OKP", "Ed25519" }, Signature::get<Signature::ID::Ed25519>() },
304+
{ { "EC", "P-384" }, Signature::get<Signature::ID::P384_SHA384>() },
305+
{ { "EC", "P-512" }, Signature::get<Signature::ID::P521_SHA512>() },
306+
{ { "OKP", "Ed25519" }, Signature::get<Signature::ID::Ed25519>() },
308307
#if !defined(WITH_BORINGSSL)
309-
{ { "OKP", "Ed448" }, Signature::get<Signature::ID::Ed448>() },
308+
{ { "OKP", "Ed448" }, Signature::get<Signature::ID::Ed448>() },
310309
#endif
311310
// TODO(RLB): RSA
312311
};

lib/hpke/src/userinfo_vc.cpp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,17 @@ get_optional(const json& json_object, const std::string& field_name)
5252
static const Signature&
5353
signature_from_alg(const std::string& alg)
5454
{
55-
static const auto alg_sig_map = std::map<std::string, const Signature&>
56-
{
55+
static const auto alg_sig_map = std::map<std::string, const Signature&>{
5756
{ "ES256", Signature::get<Signature::ID::P256_SHA256>() },
58-
{ "ES384", Signature::get<Signature::ID::P384_SHA384>() },
59-
{ "ES512", Signature::get<Signature::ID::P521_SHA512>() },
60-
{ "Ed25519", Signature::get<Signature::ID::Ed25519>() },
57+
{ "ES384", Signature::get<Signature::ID::P384_SHA384>() },
58+
{ "ES512", Signature::get<Signature::ID::P521_SHA512>() },
59+
{ "Ed25519", Signature::get<Signature::ID::Ed25519>() },
6160
#if !defined(WITH_BORINGSSL)
62-
{ "Ed448", Signature::get<Signature::ID::Ed448>() },
61+
{ "Ed448", Signature::get<Signature::ID::Ed448>() },
6362
#endif
64-
{ "RS256", Signature::get<Signature::ID::RSA_SHA256>() },
65-
{ "RS384", Signature::get<Signature::ID::RSA_SHA384>() },
66-
{ "RS512", Signature::get<Signature::ID::RSA_SHA512>() },
63+
{ "RS256", Signature::get<Signature::ID::RSA_SHA256>() },
64+
{ "RS384", Signature::get<Signature::ID::RSA_SHA384>() },
65+
{ "RS512", Signature::get<Signature::ID::RSA_SHA512>() },
6766
};
6867

6968
return alg_sig_map.at(alg);

lib/hpke/test/common.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ fips_disable(AEAD::ID id)
4949
bool
5050
fips_disable(Signature::ID id)
5151
{
52-
static const auto disabled = std::set<Signature::ID>
53-
{
52+
static const auto disabled = std::set<Signature::ID>{
5453
#if !defined(WITH_BORINGSSL)
5554
Signature::ID::Ed448,
5655
#endif

lib/hpke/test/hpke.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -212,16 +212,16 @@ TEST_CASE("HPKE Round-Trip")
212212
{
213213
ensure_fips_if_required();
214214

215-
const std::vector<KEM::ID> kems
216-
{
215+
const std::vector<KEM::ID> kems{
217216
KEM::ID::DHKEM_P256_SHA256, KEM::ID::DHKEM_P384_SHA384,
218-
KEM::ID::DHKEM_P384_SHA384, KEM::ID::DHKEM_P521_SHA512,
217+
KEM::ID::DHKEM_P384_SHA384, KEM::ID::DHKEM_P521_SHA512,
219218
#if !defined(WITH_BORINGSSL)
220-
KEM::ID::DHKEM_X448_SHA512,
219+
KEM::ID::DHKEM_X448_SHA512,
221220
#endif
222221
#if defined(WITH_PQ)
223-
KEM::ID::MLKEM512, KEM::ID::MLKEM768, KEM::ID::MLKEM1024,
224-
KEM::ID::MLKEM768_P256, KEM::ID::MLKEM768_X25519, KEM::ID::MLKEM1024_P384,
222+
KEM::ID::MLKEM512, KEM::ID::MLKEM768,
223+
KEM::ID::MLKEM1024, KEM::ID::MLKEM768_P256,
224+
KEM::ID::MLKEM768_X25519, KEM::ID::MLKEM1024_P384,
225225
#endif
226226
};
227227
const std::vector<KDF::ID> kdfs{ KDF::ID::HKDF_SHA256,

lib/hpke/test/kem.cpp

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@
33

44
#include "common.h"
55

6-
static const auto ids = std::vector<KEM::ID>
7-
{
6+
static const auto ids = std::vector<KEM::ID>{
87
KEM::ID::DHKEM_P256_SHA256, KEM::ID::DHKEM_P384_SHA384,
9-
KEM::ID::DHKEM_P384_SHA384, KEM::ID::DHKEM_P521_SHA512,
8+
KEM::ID::DHKEM_P384_SHA384, KEM::ID::DHKEM_P521_SHA512,
109
#if !defined(WITH_BORINGSSL)
11-
KEM::ID::DHKEM_X448_SHA512,
10+
KEM::ID::DHKEM_X448_SHA512,
1211
#endif
1312
#if defined(WITH_PQ)
14-
KEM::ID::MLKEM512, KEM::ID::MLKEM768, KEM::ID::MLKEM1024,
15-
KEM::ID::MLKEM768_P256, KEM::ID::MLKEM1024_P384, KEM::ID::MLKEM768_X25519,
13+
KEM::ID::MLKEM512, KEM::ID::MLKEM768,
14+
KEM::ID::MLKEM1024, KEM::ID::MLKEM768_P256,
15+
KEM::ID::MLKEM1024_P384, KEM::ID::MLKEM768_X25519,
1616
#endif
1717
};
1818

@@ -52,11 +52,10 @@ TEST_CASE("AuthKEM round-trip")
5252
{
5353
ensure_fips_if_required();
5454

55-
static const auto no_auth = std::vector<KEM::ID>
56-
{
55+
static const auto no_auth = std::vector<KEM::ID>{
5756
#if defined(WITH_PQ)
58-
KEM::ID::MLKEM512, KEM::ID::MLKEM768, KEM::ID::MLKEM1024,
59-
KEM::ID::MLKEM768_P256, KEM::ID::MLKEM1024_P384, KEM::ID::MLKEM768_X25519
57+
KEM::ID::MLKEM512, KEM::ID::MLKEM768, KEM::ID::MLKEM1024,
58+
KEM::ID::MLKEM768_P256, KEM::ID::MLKEM1024_P384, KEM::ID::MLKEM768_X25519
6059
#endif
6160
};
6261

0 commit comments

Comments
 (0)