feat: operator trust root: passkey-signed operator set, signed join codes, nodes verify and pin the chain at enrollment [CI SKIP]

This commit is contained in:
justme committed 2026-10-07 23:19:59 +08:00
1 parent 321cacac32
commit fc2bbdcc6d
35 files changed
+1374 -40

No files matched your search

@@ -187,6 +187,16 @@ public static class OperatorTrust
return WebAuthnAssertion.Verify(key.PublicKeySpki, authData, clientData, signature, set.RpId, SHA256.HashData(Encoding.UTF8.GetBytes(signed.Statement)), set.Origins.Contains, requireUserVerification: true, out _);
}
/// <summary>
/// 验证一个对端:和 <see cref="VerifyMember"/> 一样,只是调用方手里只有它用来认证这个对端的那把密钥(对等面公钥),
/// 所以只核对绑定里的对等面公钥;节点身份公钥由 Backend 在注册时核对。
/// </summary>
public static TrustFailure VerifyPeer(OperatorSet current, MemberProof proof, ReadOnlySpan<byte> peerPublicKey, string role)
{
Binding? binding = ParseBinding(proof.BindingStatement);
return binding is null ? TrustFailure.Malformed : VerifyMember(current, proof, binding.NodePublicKey, peerPublicKey, role);
}
/// <summary>
/// 验证一个成员:委托由当前操作员集合里的凭证签(并且在有效期内),绑定由委托里的码公钥签,
/// 绑定里的节点公钥、对等面公钥和角色对得上调用方手里的这个成员。
@@ -0,0 +1,15 @@
using Fleet.Core;
namespace Fleet.Identity;
public static class TrustMapping
{
public static SignedStatement ToSigned(this SignedStatementDto dto) => new(dto.Statement, dto.CredentialId, dto.AuthenticatorData, dto.ClientDataJson, dto.Signature);
public static SignedStatementDto ToDto(this SignedStatement signed) =>
new() { Statement = signed.Statement, CredentialId = signed.CredentialId, AuthenticatorData = signed.AuthenticatorData, ClientDataJson = signed.ClientDataJson, Signature = signed.Signature };
public static MemberProof ToProof(this MemberProofDto dto) => new(dto.Grant.ToSigned(), dto.Binding, dto.BindingSignature);
public static MemberProofDto ToDto(this MemberProof proof) => new() { Grant = proof.Grant.ToDto(), Binding = proof.BindingStatement, BindingSignature = proof.BindingSignature };
}