From 4c018c998829560e9677aa00f0d84eda45a9e3ef Mon Sep 17 00:00:00 2001 From: claude-release Date: Sat, 18 Jul 2026 16:54:45 +0100 Subject: [PATCH 1/2] =?UTF-8?q?keychain:=20Secure=20Enclave=20custody=20fi?= =?UTF-8?q?xes=20=E2=80=94=20KEL=20anchors=20sign=20through=20the=20hardwa?= =?UTF-8?q?re-aware=20signer;=20software=20keys=20stored,=20never=20silent?= =?UTF-8?q?ly=20replaced?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Auths-Scope: sign_commit Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EO1cBsYoV5izKvdIL6TstN5TOQl1hYN3WnhtAOh1lwAp Auths-Anchor-Seq: 13 --- .auths/ci-bundle.json | 371 +++++++++++++++++- .../auths-core/src/storage/secure_enclave.rs | 59 ++- crates/auths-id/src/keri/delegation.rs | 22 +- 3 files changed, 441 insertions(+), 11 deletions(-) diff --git a/.auths/ci-bundle.json b/.auths/ci-bundle.json index fea7116f..38d27edb 100644 --- a/.auths/ci-bundle.json +++ b/.auths/ci-bundle.json @@ -3,6 +3,21 @@ "public_key_hex": "03319793ee258fcf8fd518d8224bd39d381a004b586c84a63cc3a1be018477088f", "curve": "p256", "attestation_chain": [ + { + "version": 1, + "rid": ".auths", + "issuer": "did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd", + "subject": "did:keri:EFBGdabu4x82TD_Wn9gI3qUpDjnK3idAz4qLRS2YmdQy", + "device_public_key": { + "curve": "p256", + "key": "02f0b52b1829bb0a848dcfc7eec67d21bc032fdad711815c222363f7b9ff932800" + }, + "identity_signature": "ba82e88fd4da085bfb20122b3069ae7fce97e2618e32891e8f4f4e72a402d684e2a0a7f8cec16d1ed8e38a61291a547b2fbeabdaa799b1912624d107b7f59f8b", + "device_signature": "c9a04a5135642bb87804d24c27b2d50b51bd8aea22b99cfb5453e4db264dd24857404c44fead4ae13247e60ad83602bf407b31949f20c1977eee02acc4470547", + "expires_at": "2026-08-17T15:23:20Z", + "timestamp": "2026-07-18T15:23:24.735834Z", + "signer_type": "Agent" + }, { "version": 1, "rid": ".auths", @@ -50,12 +65,362 @@ "d": "EPbW1zoEt_Gxw2QJBtiuS0TGzdaQwkHe1fdKnjZ-WaO9" } ] + }, + { + "v": "KERI10JSON00013a_", + "t": "ixn", + "d": "EEfSEmwF2xMyUATv5yDcKdFaieSnIA-tEBr4-R2wxAjv", + "i": "EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd", + "s": "2", + "p": "EEEA2-C23Lc2BAOhUqcFY38G2AtjCNHwH56ivk9Mst8l", + "a": [ + { + "i": "EPlN9Y8qKPDEq8gg80D6N_rCN3aANK6xAoMr8OTGJ1tR", + "s": "0", + "d": "EPlN9Y8qKPDEq8gg80D6N_rCN3aANK6xAoMr8OTGJ1tR" + } + ] + }, + { + "v": "KERI10JSON00013a_", + "t": "ixn", + "d": "EGhlOnTYWuh6TQAqAVIk2tZH4ZVqWYmJYOwtzBUCjl4W", + "i": "EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd", + "s": "3", + "p": "EEfSEmwF2xMyUATv5yDcKdFaieSnIA-tEBr4-R2wxAjv", + "a": [ + { + "i": "EIv02OckbaGFpRONOurWMi_f5_Qf8unjgG5PnVCG2MiU", + "s": "0", + "d": "EIv02OckbaGFpRONOurWMi_f5_Qf8unjgG5PnVCG2MiU" + } + ] + }, + { + "v": "KERI10JSON00013a_", + "t": "ixn", + "d": "EDW6BakILqIdi4V3ZSSAUSfghoHkPs_RPoW2gIN7duAO", + "i": "EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd", + "s": "4", + "p": "EGhlOnTYWuh6TQAqAVIk2tZH4ZVqWYmJYOwtzBUCjl4W", + "a": [ + { + "i": "EGpEBKDnvEUv0dsQ1MVaZbBq0qx8p1iLA4AFcNiPlk5M", + "s": "0", + "d": "EGpEBKDnvEUv0dsQ1MVaZbBq0qx8p1iLA4AFcNiPlk5M" + } + ] + }, + { + "v": "KERI10JSON00013a_", + "t": "ixn", + "d": "EGkVke_RT3jbpWAQ1lmBOXDjaCCB_mJ6LI4Rw1B8rN5Y", + "i": "EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd", + "s": "5", + "p": "EDW6BakILqIdi4V3ZSSAUSfghoHkPs_RPoW2gIN7duAO", + "a": [ + { + "i": "EB46LmlwowYkoVNeSxMVs68LRc7g6vI1hsPnCUBtzKjt", + "s": "0", + "d": "EB46LmlwowYkoVNeSxMVs68LRc7g6vI1hsPnCUBtzKjt" + } + ] + }, + { + "v": "KERI10JSON00013a_", + "t": "ixn", + "d": "EDVMENEeyz5EkhDfCWjSP_ZMFuzJFJ3ofVvEr-WKQSN6", + "i": "EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd", + "s": "6", + "p": "EGkVke_RT3jbpWAQ1lmBOXDjaCCB_mJ6LI4Rw1B8rN5Y", + "a": [ + { + "i": "EFBGdabu4x82TD_Wn9gI3qUpDjnK3idAz4qLRS2YmdQy", + "s": "0", + "d": "EFBGdabu4x82TD_Wn9gI3qUpDjnK3idAz4qLRS2YmdQy" + } + ] + }, + { + "v": "KERI10JSON000105_", + "t": "ixn", + "d": "EFXvPGjPvzLIKwWrAuvfH0akAfQf7060evdDYy9ezpgW", + "i": "EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd", + "s": "7", + "p": "EDVMENEeyz5EkhDfCWjSP_ZMFuzJFJ3ofVvEr-WKQSN6", + "a": [ + { + "d": "agent:EFBGdabu4x82TD_Wn9gI3qUpDjnK3idAz4qLRS2YmdQy" + } + ] + }, + { + "v": "KERI10JSON00012e_", + "t": "ixn", + "d": "EMdZyZkadYolw2Wm87OYkUqrLcmUIDVrjE57hYdr1aS9", + "i": "EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd", + "s": "8", + "p": "EFXvPGjPvzLIKwWrAuvfH0akAfQf7060evdDYy9ezpgW", + "a": [ + { + "d": "agentscope:EFBGdabu4x82TD_Wn9gI3qUpDjnK3idAz4qLRS2YmdQy:1786980200:sign_commit,sign_release" + } + ] + }, + { + "v": "KERI10JSON0000ff_", + "t": "ixn", + "d": "EHoKJCGZk960cpfR3GCV8La5wEB_7UUEQKVIsM54-u7r", + "i": "EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd", + "s": "9", + "p": "EMdZyZkadYolw2Wm87OYkUqrLcmUIDVrjE57hYdr1aS9", + "a": [ + { + "d": "EF-P7CX_EyjZ85cbInn0B74kMMs8YjcbUwVj3USilFKY" + } + ] + }, + { + "v": "KERI10JSON0000ff_", + "t": "ixn", + "d": "EO0uFfkch9BKjPNzOB5vLZiO4GvytVAuBMY2HAjQahAK", + "i": "EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd", + "s": "a", + "p": "EHoKJCGZk960cpfR3GCV8La5wEB_7UUEQKVIsM54-u7r", + "a": [ + { + "d": "EFBGdabu4x82TD_Wn9gI3qUpDjnK3idAz4qLRS2YmdQy" + } + ] + }, + { + "v": "KERI10JSON00013a_", + "t": "ixn", + "d": "EIGyARxCYrYKnchn_OsVWrzLK8eXfSLlqcVPl3ur8epp", + "i": "EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd", + "s": "b", + "p": "EO0uFfkch9BKjPNzOB5vLZiO4GvytVAuBMY2HAjQahAK", + "a": [ + { + "i": "EO1cBsYoV5izKvdIL6TstN5TOQl1hYN3WnhtAOh1lwAp", + "s": "0", + "d": "EO1cBsYoV5izKvdIL6TstN5TOQl1hYN3WnhtAOh1lwAp" + } + ] + }, + { + "v": "KERI10JSON000105_", + "t": "ixn", + "d": "EKvB1FemDJ0kEwiqhmTQljb5_clKGiQ9qjb5MilfuU2Q", + "i": "EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd", + "s": "c", + "p": "EIGyARxCYrYKnchn_OsVWrzLK8eXfSLlqcVPl3ur8epp", + "a": [ + { + "d": "agent:EO1cBsYoV5izKvdIL6TstN5TOQl1hYN3WnhtAOh1lwAp" + } + ] + }, + { + "v": "KERI10JSON00012e_", + "t": "ixn", + "d": "EP0KgPUSLmYprFsHWfFbw0rxaIDq1ePdvSyImKVwxYkn", + "i": "EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd", + "s": "d", + "p": "EKvB1FemDJ0kEwiqhmTQljb5_clKGiQ9qjb5MilfuU2Q", + "a": [ + { + "d": "agentscope:EO1cBsYoV5izKvdIL6TstN5TOQl1hYN3WnhtAOh1lwAp:1786981212:sign_commit,sign_release" + } + ] } ], "kel_attachments": [ "2d4141424141417877504838766b56354c766b697652353842764a524a766e49416a53415a6331714e385a3072636c444d49317132454a4f754a6e61507931304d6b675f3363755f525a7570696639646565656459586a6455415f34", - "2d414142414144696a3038345f61366a637153616f63636e4a4d62305259495237634e725464354439355f612d754f59525f635051736b5457455049452d386777764f33576131687161754a643952706d3258752d71625368467155" + "2d414142414144696a3038345f61366a637153616f63636e4a4d62305259495237634e725464354439355f612d754f59525f635051736b5457455049452d386777764f33576131687161754a643952706d3258752d71625368467155", + "2d4141424141414f705763614767782d2d4e75524d593853506f7a346d6850734c5247645752423962353645654954572d6e6b79544b695f76706c7241676857665875653169685f4147634b424c7257433378746676595534497369", + "2d41414241414463705f6b6a6c3551684c3067764266744b70587176554953714269443679533955694a5a4330315f6d43617a6c414d48324f794b2d3953304a44787462545a384f316a4a4234686679456d4d785572413269636f30", + "2d41414241414132704e4a43317138376a66743636476151503334344a7166337132627537397732684555634f684e4e7768622d30476b6741494d726d6b6d2d6a37426c305f462d4c487772527549486874394e6168472d4f333152", + "2d41414241414172347475664c3078463833436470306e685f675761492d6d4a5971705879665a52466969307269756b41657739743371644c78445548337768475755436c504f54305a7664537756564d794378586d78327a666136", + "2d4141424141427041646c717a662d75635a61394845736c6c626b546633444867457a717044696c486943486c2d6846775849554778486c453136695961305570792d6233625a6f695357624c555f316658573148484e5f6863374d", + "2d4141424141436d4b366f4a7335415946743639702d5a5552434c596a696f6447377233366a38326e6a3644572d7a6151496d3856745f35346a4c4d7556494f78587569387548754f4f4667705346514a2d36766e64507646727851", + "2d414142414141784b59343473366b59356b72446f495975417432526b4a4a357a657732744552505a49424e5371767167737454476e43757a5055564851744152584264704a7261767946685f39527831487172396a31384a2d4457", + "2d414142414141495f4b55594b4f73764e62326f4c7473556c6d61536965436b714f6b334b7935546c336c722d52574834653954525571786b63756a34375065726e536a615653742d6d47463771734453395764725178346d526d2d", + "2d4141424141425a484831596835716553506b744d56316a4e456673366f506561525f45727265354f706232756d4334772d46426f557850634c776f7361326a507a6d3969554448754e677a4d4d58314d42483334384c41724a7569", + "2d414142414142514b6478305f794a754657466c55704f3550714746316e303979762d6b2d35467873715043385a72745959745f357532734932445749744b6d4359434339306b7a4d532d556b584c4a7a7a454d4371696676724579", + "2d4141424141427a526777374658754d575a7354594a326b79784965447947327a63326c3170424d35436d6c475f5a4d64394c63646b417746693263774e4f7043433937416472624677724e774f4765356f584f443067535f634e44", + "2d414142414144575161325572665f635762554b72366250366a47626d4257386950554d395655425a4f71745f426a492d75656371564761495f664a3869384153754e55693872426d7076392d7475752d4b7243656876513043364d" + ], + "device_kels": [ + { + "did": "did:keri:EB46LmlwowYkoVNeSxMVs68LRc7g6vI1hsPnCUBtzKjt", + "kel": [ + { + "v": "KERI10JSON00015f_", + "t": "dip", + "d": "EB46LmlwowYkoVNeSxMVs68LRc7g6vI1hsPnCUBtzKjt", + "i": "EB46LmlwowYkoVNeSxMVs68LRc7g6vI1hsPnCUBtzKjt", + "s": "0", + "kt": "1", + "k": [ + "DNXo-V5u8_vuBzkKYoaRRCO2DnaqYbWlTqHvHRowIjSq" + ], + "nt": "1", + "n": [ + "EMf_-qFIcaO8PCkHnTQ1kg7lsD5_QAsAj94Bo4Fu7UHi" + ], + "bt": "0", + "b": [], + "c": [], + "a": [], + "di": "EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd" + } + ], + "kel_attachments": [ + "2d414142414141533962696a475866547550686c48343344435532686659584844697644597937474a62394a5249776a31576f4f594a68784a4e4d6d66727636622d574b4e51485a59722d523270435031454f66423158764d2d6b4d2d47414230414141414141414141414141414141414141414141414645476b566b655f5254336a6270574151316c6d424f58446a614343425f6d4a364c49345277314238724e3559" + ] + }, + { + "did": "did:keri:EFBGdabu4x82TD_Wn9gI3qUpDjnK3idAz4qLRS2YmdQy", + "kel": [ + { + "v": "KERI10JSON00015f_", + "t": "dip", + "d": "EFBGdabu4x82TD_Wn9gI3qUpDjnK3idAz4qLRS2YmdQy", + "i": "EFBGdabu4x82TD_Wn9gI3qUpDjnK3idAz4qLRS2YmdQy", + "s": "0", + "kt": "1", + "k": [ + "DM7Fn3BNMxniy7-3mPMsnGf-D0LzbfArArziOd2HJqm7" + ], + "nt": "1", + "n": [ + "EF2b6jAzpSeVs_ClZbYSP-zCLnSyTSSPQ_XvadOCFiNt" + ], + "bt": "0", + "b": [], + "c": [], + "a": [], + "di": "EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd" + } + ], + "kel_attachments": [ + "2d414142414142637a74747658326335574d575a41785f6c6170454955654a4553736c3438326d5071724867306f734e324849684d622d68786d4b5a2d3671467a32615578753067657774516674637773724f684a6a47326568414b2d4741423041414141414141414141414141414141414141414141474544564d454e4565797a35456b68446643576a53505f5a4d46757a4a464a336f66567645722d574b51534e36" + ] + }, + { + "did": "did:keri:EGpEBKDnvEUv0dsQ1MVaZbBq0qx8p1iLA4AFcNiPlk5M", + "kel": [ + { + "v": "KERI10JSON00015f_", + "t": "dip", + "d": "EGpEBKDnvEUv0dsQ1MVaZbBq0qx8p1iLA4AFcNiPlk5M", + "i": "EGpEBKDnvEUv0dsQ1MVaZbBq0qx8p1iLA4AFcNiPlk5M", + "s": "0", + "kt": "1", + "k": [ + "DM_3EYHf6RSHG-tdXVR_cU-Lvof3rkWYtT-PYhpU-IRe" + ], + "nt": "1", + "n": [ + "ELnchwp59KdisZ-hIRguRa0t5dl1DigLxwxfr_qWHkHE" + ], + "bt": "0", + "b": [], + "c": [], + "a": [], + "di": "EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd" + } + ], + "kel_attachments": [ + "2d414142414142386f412d6750665f5761363035454339564c7970796a4c4a467570453167777555315f446a6d7450625739546a716b4c59636c77743733397142576a7a4174662d4b757738376a6e67796b597a384d6669535f30502d4741423041414141414141414141414141414141414141414141454544573642616b494c714964693456335a53534155536667686f486b50735f52506f573267494e376475414f" + ] + }, + { + "did": "did:keri:EIv02OckbaGFpRONOurWMi_f5_Qf8unjgG5PnVCG2MiU", + "kel": [ + { + "v": "KERI10JSON00015f_", + "t": "dip", + "d": "EIv02OckbaGFpRONOurWMi_f5_Qf8unjgG5PnVCG2MiU", + "i": "EIv02OckbaGFpRONOurWMi_f5_Qf8unjgG5PnVCG2MiU", + "s": "0", + "kt": "1", + "k": [ + "DPYt_OxIt9CqR_4W3LHZLFm092qERcNr5F7716rbuy2h" + ], + "nt": "1", + "n": [ + "EPz5DqpWQTuIHeVm92lv5aZ0IUeRuNrt1VNd65HWumph" + ], + "bt": "0", + "b": [], + "c": [], + "a": [], + "di": "EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd" + } + ], + "kel_attachments": [ + "2d4141424141434e58784447677344644b4e576c77796b726c6936546e42556863416a6a4b37795948556256796c5f4732483075716c35354b636d646c5665504e4e64786576355068546b454b59717447504e30484f4e54333351462d4741423041414141414141414141414141414141414141414141444547686c4f6e545957756836545141714156496b32745a48345a567157596d4a594f77747a4255436a6c3457" + ] + }, + { + "did": "did:keri:EO1cBsYoV5izKvdIL6TstN5TOQl1hYN3WnhtAOh1lwAp", + "kel": [ + { + "v": "KERI10JSON00015f_", + "t": "dip", + "d": "EO1cBsYoV5izKvdIL6TstN5TOQl1hYN3WnhtAOh1lwAp", + "i": "EO1cBsYoV5izKvdIL6TstN5TOQl1hYN3WnhtAOh1lwAp", + "s": "0", + "kt": "1", + "k": [ + "DEd5DmPuvZG7EDCFZC8UtoDWWwsbxO8_hJbXMK1PPYPQ" + ], + "nt": "1", + "n": [ + "EBpDD46IT-XapGnnZasYr_BEUfCA1xM_u2--i3c0SzYS" + ], + "bt": "0", + "b": [], + "c": [], + "a": [], + "di": "EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd" + } + ], + "kel_attachments": [ + "2d414142414142712d64486638466d33484662743575684461677869677675736d75563651547355435a56334a6d744d32583858764879542d3873416742584f4d34455979697a6d6c7a47556a4f546c5669397270536c615f444d4d2d47414230414141414141414141414141414141414141414141414c45494779415278435972594b6e63686e5f4f735657727a4c4b38655866534c6c716356506c33757238657070" + ] + }, + { + "did": "did:keri:EPlN9Y8qKPDEq8gg80D6N_rCN3aANK6xAoMr8OTGJ1tR", + "kel": [ + { + "v": "KERI10JSON00015f_", + "t": "dip", + "d": "EPlN9Y8qKPDEq8gg80D6N_rCN3aANK6xAoMr8OTGJ1tR", + "i": "EPlN9Y8qKPDEq8gg80D6N_rCN3aANK6xAoMr8OTGJ1tR", + "s": "0", + "kt": "1", + "k": [ + "DJj3LOcZEo0z7Gm1Lkxe9Emk98WG1wwTcLnC52Dx_J7n" + ], + "nt": "1", + "n": [ + "EIsYqqpF-rTb-9NugFe8PBbJLgfUxGbrhbknzliu9EgC" + ], + "bt": "0", + "b": [], + "c": [], + "a": [], + "di": "EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd" + } + ], + "kel_attachments": [ + "2d414142414141414b643631664e503247624b37324c3057774d32583978474f303042613955496277786b4539475a676354304c3951332d6f315a50364a356c57674e454f5832534133554f50484836684d7546525247435f4d414d2d47414230414141414141414141414141414141414141414141414345456653456d774632784d7955415476357944634b6446616965536e49412d74454272342d52327778416a76" + ] + } ], - "bundle_timestamp": "2026-06-19T14:19:08.262292Z", + "bundle_timestamp": "2026-07-18T15:55:57.244316Z", "max_valid_for_secs": 31536000 -} +} \ No newline at end of file diff --git a/crates/auths-core/src/storage/secure_enclave.rs b/crates/auths-core/src/storage/secure_enclave.rs index 47dc1d1a..61aabd4a 100644 --- a/crates/auths-core/src/storage/secure_enclave.rs +++ b/crates/auths-core/src/storage/secure_enclave.rs @@ -115,6 +115,13 @@ impl SecureEnclaveKeyStorage { self.keys_dir.join(format!("{}.meta.json", alias.as_str())) } + /// A SOFTWARE key stored under this backend (e.g. a delegated agent's + /// Ed25519 key, which the enclave cannot hold). The blob is the caller's + /// passphrase-encrypted key material, stored verbatim. + fn swkey_path(&self, alias: &KeyAlias) -> PathBuf { + self.keys_dir.join(format!("{}.swkey", alias.as_str())) + } + fn load_handle(&self, alias: &KeyAlias) -> Result, AgentError> { let mut cache = self.handle_cache.lock().unwrap_or_else(|e| e.into_inner()); if let Some(h) = cache.get(alias.as_str()) { @@ -140,7 +147,31 @@ impl KeyStorage for SecureEnclaveKeyStorage { role: KeyRole, _encrypted_key_data: &[u8], ) -> Result<(), AgentError> { - // Ignore encrypted_key_data — generate key in SE hardware + // NON-EMPTY material is a SOFTWARE key the caller already encrypted (a + // delegated agent's Ed25519 key, an imported key, a pairing-born device + // key): store it verbatim. The enclave cannot hold it, and silently + // minting an unrelated hardware key here would DISCARD the only copy of + // a key the KEL already anchored — the delegation would "succeed" with + // its private key lost. + if !_encrypted_key_data.is_empty() { + let path = self.swkey_path(alias); + fs::write(&path, _encrypted_key_data).map_err(|e| { + AgentError::IO(std::io::Error::other(format!( + "failed to write software key item: {e}" + ))) + })?; + fs::set_permissions(&path, fs::Permissions::from_mode(0o600)).ok(); + let meta = KeyMetadata { + identity_did: identity_did.to_string(), + role: format!("{role:?}"), + }; + let meta_json = serde_json::to_string_pretty(&meta).unwrap_or_default(); + fs::write(self.meta_path(alias), meta_json).ok(); + return Ok(()); + } + + // EMPTY material is the hardware-creation convention (identity init on + // this backend passes no blob): generate the key inside the enclave. let mut handle_buf = vec![0u8; 512]; let mut handle_len: usize = 0; let mut pubkey_buf = vec![0u8; 65]; @@ -190,7 +221,19 @@ impl KeyStorage for SecureEnclaveKeyStorage { } fn load_key(&self, alias: &KeyAlias) -> Result<(IdentityDID, KeyRole, Vec), AgentError> { - let handle = self.load_handle(alias)?; + // A software item (delegated/imported key) loads its encrypted blob; a + // hardware key loads its opaque handle. Handle wins if both exist. + let handle = if self.handle_path(alias).exists() { + self.load_handle(alias)? + } else { + let sw = self.swkey_path(alias); + fs::read(&sw).map_err(|e| { + AgentError::StorageError(format!( + "SE key handle not found for '{}': {e}", + alias.as_str() + )) + })? + }; // Read metadata. A key whose identity binding is missing must not load under a // placeholder DID — without it we cannot prove which identity the key belongs to. @@ -233,6 +276,14 @@ impl KeyStorage for SecureEnclaveKeyStorage { ))) })?; } + let sw_path = self.swkey_path(alias); + if sw_path.exists() { + fs::remove_file(&sw_path).map_err(|e| { + AgentError::IO(std::io::Error::other(format!( + "failed to delete software key item: {e}" + ))) + })?; + } let meta_path = self.meta_path(alias); if meta_path.exists() { fs::remove_file(&meta_path).ok(); @@ -251,6 +302,10 @@ impl KeyStorage for SecureEnclaveKeyStorage { #[allow(clippy::disallowed_methods)] // INVARIANT: alias comes from a filename we created in store_key aliases.push(KeyAlias::new_unchecked(alias)); + } else if let Some(alias) = name.strip_suffix(".swkey") { + #[allow(clippy::disallowed_methods)] + // INVARIANT: alias comes from a filename we created in store_key + aliases.push(KeyAlias::new_unchecked(alias)); } } } diff --git a/crates/auths-id/src/keri/delegation.rs b/crates/auths-id/src/keri/delegation.rs index 11b66720..028b0cf5 100644 --- a/crates/auths-id/src/keri/delegation.rs +++ b/crates/auths-id/src/keri/delegation.rs @@ -337,12 +337,22 @@ pub fn stage_root_anchor_ixn( let canonical = serialize_for_signing(&Event::Ixn(ixn.clone())) .map_err(|e| InitError::Keri(e.to_string()))?; - let (_did, _role, encrypted) = keychain.load_key(root_alias)?; - let pass = passphrase_provider - .get_passphrase(&format!("Enter passphrase for root key '{}':", root_alias))?; - let pkcs8 = Pkcs8Der::new(decrypt_keypair(&encrypted, &pass)?.to_vec()); - let sig = sign_with_pkcs8_for_init(root_curve, &pkcs8, &canonical) - .map_err(|e| InitError::Crypto(e.to_string()))?; + // The root key may live in a HARDWARE backend (Secure Enclave), whose blob is + // an opaque handle no software decrypt can open. Route the signature through + // the keychain's hardware-aware signer — the same dispatch the commit-signing + // path trusts — instead of decrypting a seed that may not exist in software. + let (sig, _pubkey, signed_curve) = auths_core::storage::keychain::sign_with_key( + keychain, + root_alias, + passphrase_provider, + &canonical, + ) + .map_err(|e| InitError::Crypto(e.to_string()))?; + if signed_curve != root_curve { + return Err(InitError::Crypto(format!( + "root key curve mismatch: expected {root_curve:?}, key signed as {signed_curve:?}" + ))); + } let attachment = serialize_attachment(&[IndexedSignature { index: 0, prior_index: None, From a7c4f52d4630d53e6d87ce691bb8e8a16ce6ca0f Mon Sep 17 00:00:00 2001 From: bordumb Date: Sat, 18 Jul 2026 17:03:12 +0100 Subject: [PATCH 2/2] fix: formatting Auths-Id: did:keri:EB5cPHY0t-ejNC_rUzPS1dclTvd6kG-R9mQzjozCuGgd Auths-Device: did:keri:EPlN9Y8qKPDEq8gg80D6N_rCN3aANK6xAoMr8OTGJ1tR Auths-Anchor-Seq: 10 --- .auths/ci-bundle.json | 2 +- .github/workflows/verify-commits.yml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.auths/ci-bundle.json b/.auths/ci-bundle.json index 38d27edb..50cb4d68 100644 --- a/.auths/ci-bundle.json +++ b/.auths/ci-bundle.json @@ -423,4 +423,4 @@ ], "bundle_timestamp": "2026-07-18T15:55:57.244316Z", "max_valid_for_secs": 31536000 -} \ No newline at end of file +} diff --git a/.github/workflows/verify-commits.yml b/.github/workflows/verify-commits.yml index 508f35ce..fe9bcdfe 100644 --- a/.github/workflows/verify-commits.yml +++ b/.github/workflows/verify-commits.yml @@ -7,7 +7,6 @@ on: permissions: contents: read pull-requests: write - jobs: verify: runs-on: ubuntu-latest