Skip to main content

codec_multikey/
binding.rs

1// SPDX-FileCopyrightText: Copyright © 2026 ReallyMe LLC. All rights reserved
2//
3// SPDX-License-Identifier: MIT OR Apache-2.0
4
5use crate::error::{
6    classify_binding_algorithm, classify_binding_type, classify_multikey_codec, MultikeyError,
7};
8use crate::parse::ParsedMultikey;
9
10/// Generic binding compatibility rules.
11/// Binding labels are protocol-facing metadata and are validated here
12/// as algorithm constraints over multikey-encoded public keys.
13/// Returns whether a binding-type label is compatible with a codec name.
14///
15/// Generic `Multikey` matches any supported codec; profile-specific labels
16/// match only their one codec. Unknown labels return `false`.
17pub fn binding_type_matches_codec(binding_type: &str, codec_name: &str) -> bool {
18    match binding_type {
19        // Generic Multikey (ALL supported algorithms)
20        "Multikey" => matches!(
21            codec_name,
22            "ed25519-pub"
23                | "ed448-pub"
24                | "x25519-pub"
25                | "p256-pub"
26                | "p384-pub"
27                | "p521-pub"
28                | "rsa-pub"
29                | "secp256k1-pub"
30                | "mldsa-44-pub"
31                | "mldsa-65-pub"
32                | "mldsa-87-pub"
33                | "mlkem-512-pub"
34                | "mlkem-768-pub"
35                | "mlkem-1024-pub"
36        ),
37
38        // Profile-specific / constrained bindings
39        "P256Key2024" => codec_name == "p256-pub",
40        "P384Key2024" => codec_name == "p384-pub",
41        "P521Key2024" => codec_name == "p521-pub",
42        "RsaVerificationKey2024" => codec_name == "rsa-pub",
43        "ML_DSA_44Key2024" => codec_name == "mldsa-44-pub",
44        "ML_DSA_65Key2024" => codec_name == "mldsa-65-pub",
45        "ML_DSA_87Key2024" => codec_name == "mldsa-87-pub",
46        "MLKEM512Key2024" => codec_name == "mlkem-512-pub",
47        "MLKEM768Key2024" => codec_name == "mlkem-768-pub",
48        "MLKEM1024Key2024" => codec_name == "mlkem-1024-pub",
49
50        _ => false,
51    }
52}
53
54/// Binding metadata to validate against a parsed multikey.
55pub struct KeyBindingInput<'a> {
56    /// The binding-type label (e.g. `Multikey`, `P256Key2024`).
57    pub binding_type: &'a str,
58    /// Optional explicit algorithm label; required for non-`Multikey` types.
59    pub algorithm: Option<&'a str>,
60}
61
62/// Validates that a binding's type and algorithm agree with a parsed key.
63///
64/// Fails closed: returns an error on a type/codec mismatch, an algorithm
65/// mismatch, or a missing required algorithm.
66pub fn validate_key_binding(
67    binding: KeyBindingInput<'_>,
68    parsed: &ParsedMultikey,
69) -> Result<(), MultikeyError> {
70    if !binding_type_matches_codec(binding.binding_type, parsed.codec_name) {
71        return Err(MultikeyError::BindingTypeCodecMismatch {
72            binding_type: classify_binding_type(binding.binding_type),
73            codec: classify_multikey_codec(parsed.codec_name),
74            algorithm: classify_binding_algorithm(parsed.alg),
75        });
76    }
77
78    if let Some(binding_alg) = binding.algorithm {
79        if binding_alg != parsed.alg {
80            return Err(MultikeyError::BindingAlgorithmMismatch {
81                binding_alg: classify_binding_algorithm(binding_alg),
82                codec_algorithm: classify_binding_algorithm(parsed.alg),
83            });
84        }
85    } else if binding.binding_type != "Multikey" {
86        return Err(MultikeyError::BindingAlgorithmMissing {
87            binding_type: classify_binding_type(binding.binding_type),
88        });
89    }
90
91    Ok(())
92}