diff --git a/.gitignore b/.gitignore index 7a51535..2ce50e3 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ Cargo.lock /target /Cargo.lock + +plan.md +session.json diff --git a/CHANGELOG.md b/CHANGELOG.md index d80a986..55458c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,22 @@ All notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.1.0] - 2026-10-07 + +### Added + +- `ViewBuilder` (`views::builder`): builder-pattern view creation with a fluent kind selector and a `.build()` terminal, re-exported at the crate root and in the prelude. Standard codecs dispatch to the built-in views. Custom protocol keys (codec `Codec::Identity` with `AlgorithmName`/`KeyType` attributes) dispatch to caller-supplied local-codec factories on fallthrough, per cryptidtech/multi-key#6. +- `Builder::with_algorithm_name(name)` and `Builder::with_key_type(key_type)` stamp the `AlgorithmName` (27) and `KeyType` (28) attributes for custom keys, following the byte conventions from cryptidtech/provenance-specifications#4. + +### Changed + +- The `cipher_view`, `kdf_view`, `cipher_attr_view`, and `kdf_attr_view` fallthrough errors now report the codec that actually lacks support (the second key's codec or the attribute-derived codec) instead of the multikey's own codec. Error variants are unchanged. +- Added an exact `pkcs1 = "=0.8.0-rc.4"` dependency pin. `sad-rsa 0.2.3` does not compile against `pkcs1 0.8.0-rc.5` (published 2026-10-05), so a fresh dependency resolve fails. The pin forces rc.4 until sad-rsa ships a compatible release. + +### Deprecated + +- The `Views` extension trait and `impl Views for Multikey`. Source-compatible in this release. Use `ViewBuilder` instead. Both will be removed in the next major release. Note: `multi_sig::Views` is a different, unrelated trait. + ## [2.0.0] - 2026-09-14 ### Added @@ -243,6 +259,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Major dependency updates: ed25519-dalek 3, blsful 4, elliptic-curve 0.14, vsss-rs 6, ssh-key 0.7. - Initial published release on crates.io as `multi-key`. +[2.1.0]: https://github.com/cryptidtech/multi-key/compare/v2.0.0...v2.1.0 [1.1.1]: https://github.com/cryptidtech/multi-key/compare/v1.1.0...v1.1.1 [1.2.2]: https://github.com/cryptidtech/multi-key/compare/v1.2.1...v1.2.2 [1.2.1]: https://github.com/cryptidtech/multi-key/compare/v1.2.0...v1.2.1 diff --git a/Cargo.toml b/Cargo.toml index 11c8be4..ce84996 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "multi-key" -version = "2.0.0" +version = "2.1.0" edition = "2024" rust-version = "1.96" authors = ["Dave Grantham "] @@ -60,6 +60,13 @@ multi-util = "1.1" p256 = { version = "0.14", features = ["ecdsa", "ecdh"] } p384 = { version = "0.14", features = ["ecdsa", "ecdh"] } p521 = { version = "0.14", features = ["ecdsa", "ecdh"] } +# Compatibility pin. sad-rsa 0.2.3 (the newest release of its line) cannot +# compile against pkcs1 0.8.0-rc.5, which changed `RsaPrivateKey` and +# `RsaPublicKey` from lifetime parameters to generic parameters. sad-rsa +# permits `pkcs1 ^0.8.0-rc.4`, so a fresh resolve picks rc.5 and fails. Pin +# the exact rc.4 dependency to force it; remove this entry once sad-rsa +# publishes a rc.5-compatible release. +pkcs1 = "=0.8.0-rc.4" poly1305 = "0.9" pq-mayo = "0.6.0" rand = "0.10" diff --git a/README.md b/README.md index dcba803..d37c202 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,98 @@ Two additional modules provide threshold functionality outside the view traits: Operations that seem to mutate the Multi-Key (for example, encrypt, decrypt, convert) in fact do a copy-on-write (CoW) operation. They return a new Multi-Key with the mutation applied. +### Creating Views + +Views are created through the builder-pattern `multi_key::ViewBuilder` +(re-exported at the crate root and in the prelude). Select a view kind +fluently, then build: + +```rust +use multi_key::{Builder, ViewBuilder}; +use multi_codec::Codec; + +let mut rng = rand::rng(); +let mk = Builder::new_from_random_bytes(Codec::Ed25519Priv, &mut rng) + .unwrap() + .try_build() + .unwrap(); + +// sign a message and verify it +let signer = ViewBuilder::new(&mk).sign().build().unwrap(); +let sig = signer.sign(b"message", false, None).unwrap(); +let verifier = ViewBuilder::new(&mk).verify().build().unwrap(); +verifier.verify(&sig, Some(b"message")).unwrap(); +``` + +The 15 kinds that need no second key all follow this shape: `attr`, +`cipher_attr`, `data`, `kdf_attr`, `threshold_attr`, `threshold_key`, +`conv`, `fingerprint`, `sign`, `verify`, `seal`, `open`, `threshold`, +`disclosure`, and `merkle_state`. The `cipher` and `kdf` kinds attach the +second key their dispatch reads. A view borrows its Multikey, so keep the +Multikey alive while the view is in use: + +```rust +// encrypt/decrypt the Multi-Key at rest +let encrypter = ViewBuilder::new(&mk).cipher(&cipher_key).build()?; +let encrypted = encrypter.encrypt()?; + +// derive a cipher key from a passphrase +let kdf = ViewBuilder::new(&mk).kdf(&kdf_key).build()?; +let derived = kdf.derive_key(b"passphrase")?; +``` + +The original `multi_key::Views` extension trait is deprecated since 2.1.0 +in favor of `ViewBuilder`. It stays as a source-compatible delegating shim +with the same `*_view()` methods, marked as deprecated at compile time. +Note that `multi_sig::Views` is a different, unrelated trait. + +### Custom Protocol Keys + +A custom protocol key (sigil-0) carries the `Codec::Identity` codec with the `AlgorithmName` attribute (code 27, the protocol name) and the `KeyType` attribute (code 28, one raw byte) from [provenance-specifications][PROVENANCE]. The `KeyType` byte convention is absent or 0 for public keys and 1 for secret keys. Custom keys have no built-in views, so `ViewBuilder` dispatches them to caller-supplied local-codec factories registered with `with_local_codec`. Different custom protocols share the `Codec::Identity` codec, so a factory identifies the key's protocol through the `AlgorithmName` attribute (not through the codec): + +```rust +use multi_key::{AttrId, Builder, Error, SignView, ViewBuilder}; +use multi_codec::Codec; +use multi_sig::Multisig; + +let mk = Builder::new(Codec::Identity) + .with_key_bytes(b"my-custom-key-material".as_slice()) + .with_algorithm_name("my-protocol") + .with_key_type(1) // 1 = secret key + .try_build() + .unwrap(); + +struct MyProtocolSign { + algorithm: String, +} + +impl SignView for MyProtocolSign { + fn sign(&self, _: &[u8], _: bool, _: Option) -> Result { + Err(Error::UnsupportedAlgorithm(format!("{} sign", self.algorithm))) + } +} + +// the factory builds a MyProtocolSign for the key it is handed. a real +// factory for several protocols sharing Identity reads the AlgorithmName +// attribute from the passed-in key and branches on it, serving only its +// own protocol. +let signer = ViewBuilder::new(&mk) + .sign() + .with_local_codec(Codec::Identity, |key| { + let name = key + .attributes + .get(&AttrId::AlgorithmName) + .ok_or_else(|| Error::UnsupportedAlgorithm("AlgorithmName missing".into()))?; + Ok(Box::new(MyProtocolSign { + algorithm: String::from_utf8_lossy(name).into_owned(), + })) + }) + .build() + .unwrap(); +``` + +Built-in views always win: a factory registered for a standard codec whose built-in view exists is never consulted. A repeat `with_local_codec` call for the same kind and codec replaces the earlier factory. Factory errors propagate unchanged. The `disclosure` kind is codec-independent (its view applies to every key), so a factory registered for that kind is never consulted. + ## SSH Key Conversions This crate converts to and from the SSH key format with the [`ssh-key`][SSHKEY] crate. Standard SSH algorithms are handled natively. Non-standard algorithms use the [RFC 4251][RFC4251] "additional algorithms" mechanism with an `ssh_key::Algorithm::Other` opaque key and an algorithm name ending in the literal `@multikey` suffix. This is a wire-format identifier, distinct from the crate name. @@ -263,7 +355,7 @@ There are three ways to produce shares in a given disclosure mode: **1. Direct creation via `split_with_disclosure()`:** ```rust -use multi_key::{Builder, Views, ThresholdDisclosure}; +use multi_key::{Builder, ViewBuilder, ThresholdDisclosure}; let meta_key = multi_key::generate_meta_key(); let meta_mk = Builder::new(Codec::Chacha20Poly1305) @@ -271,8 +363,9 @@ let meta_mk = Builder::new(Codec::Chacha20Poly1305) .try_build()?; // BLS Shamir split with FullConfidentialial disclosure -let shares = mk.threshold_view()?.split_with_disclosure(3, 5, - ThresholdDisclosure::FullConfidentialial, Some(&meta_mk))?; +let shares = ViewBuilder::new(&mk).threshold().build()? + .split_with_disclosure(3, 5, + ThresholdDisclosure::FullConfidentialial, Some(&meta_mk))?; ``` **2. Builder construction:** @@ -288,7 +381,7 @@ let share = Builder::new(Codec::Bls12381G2PrivShare) **3. Convert an existing share:** ```rust -let encrypted = share.disclosure_view()? +let encrypted = ViewBuilder::new(&share).disclosure().build()? .to_disclosure(ThresholdDisclosure::FullConfidentialial, Some(&meta_mk), None)?; ``` @@ -297,14 +390,14 @@ let encrypted = share.disclosure_view()? Use `read_threshold_params()` with the `meta_key` to decrypt `t` and `n`: ```rust -let (t, n) = encrypted.disclosure_view()? +let (t, n) = ViewBuilder::new(&encrypted).disclosure().build()? .read_threshold_params(Some(&meta_mk))?; ``` ### Combining Encrypted Shares ```rust -let combined = mk.threshold_view()? +let combined = ViewBuilder::new(&mk).threshold().build()? .combine_with_meta(Some(&meta_mk))?; ``` @@ -324,15 +417,15 @@ The `to_disclosure()` method converts between any pair of modes. It reads the cu ```rust // Full → Partial -let partial = full.disclosure_view()? +let partial = ViewBuilder::new(&full).disclosure().build()? .to_disclosure(ThresholdDisclosure::Partial, Some(&meta_mk), None)?; // Partial → FullConfidentialial -let confidential = partial.disclosure_view()? +let confidential = ViewBuilder::new(&partial).disclosure().build()? .to_disclosure(ThresholdDisclosure::FullConfidentialial, Some(&meta_mk), Some(&meta_mk))?; // FullConfidentialial → Full -let full_again = confidential.disclosure_view()? +let full_again = ViewBuilder::new(&confidential).disclosure().build()? .to_disclosure(ThresholdDisclosure::Full, None, Some(&meta_mk))?; ``` @@ -366,7 +459,7 @@ Individual KEM views may restrict the allowed AEAD codec. For example, X25519-ML - Private keys are wrapped in `Zeroizing` buffers and automatically zeroized on drop. - `Debug` output for private key material is redacted. -- All views are thread-safe (`Send` + `Sync`) for concurrent operations. +- The `ViewBuilder` is `Send` + `Sync` (with and without registered factories). The view trait objects it returns carry no `Send`/`Sync` supertrait, so they do not carry that bound at the type level. - Mutation operations use copy-on-write semantics. They return a new `Multi-Key` rather than mutating in place. ## Links diff --git a/src/cipher.rs b/src/cipher.rs index 935e1dc..24b2936 100644 --- a/src/cipher.rs +++ b/src/cipher.rs @@ -82,7 +82,7 @@ impl Builder { #[cfg(test)] mod tests { use super::*; - use crate::{Views, kdf, mk}; + use crate::{ViewBuilder, kdf, mk}; #[test] fn test_chacha20() { @@ -104,7 +104,7 @@ mod tests { .unwrap(); // get the kdf view on the kdf multikey - let kdf = ciphermk.kdf_view(&kdfmk).unwrap(); + let kdf = ViewBuilder::new(&ciphermk).kdf(&kdfmk).build().unwrap(); // derive a key for the cipher multikey to use let ciphermk = kdf @@ -120,24 +120,24 @@ mod tests { .unwrap(); // get the cipher view on the multikey - let cipher = mk.cipher_view(&ciphermk).unwrap(); + let cipher = ViewBuilder::new(&mk).cipher(&ciphermk).build().unwrap(); // encrypt the secret key let mk = cipher.encrypt().unwrap(); // make sure all of the attributes are right - let attr = mk.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk).attr().build().unwrap(); assert!(attr.is_encrypted()); assert!(!attr.is_public_key()); assert!(attr.is_secret_key()); - let kd = mk.data_view().unwrap(); + let kd = ViewBuilder::new(&mk).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_err()); - let cattr = mk.cipher_attr_view().unwrap(); + let cattr = ViewBuilder::new(&mk).cipher_attr().build().unwrap(); assert_eq!(Codec::Chacha20Poly1305, cattr.cipher_codec().unwrap()); assert!(cattr.nonce_bytes().is_ok()); assert_eq!(32, cattr.key_length().unwrap()); - let kattr = mk.kdf_attr_view().unwrap(); + let kattr = ViewBuilder::new(&mk).kdf_attr().build().unwrap(); assert_eq!(Codec::BcryptPbkdf, kattr.kdf_codec().unwrap()); } } diff --git a/src/kdf.rs b/src/kdf.rs index 6f6c3c4..361f5b3 100644 --- a/src/kdf.rs +++ b/src/kdf.rs @@ -90,7 +90,7 @@ impl Builder { #[cfg(test)] mod tests { use super::*; - use crate::{Views, cipher}; + use crate::{ViewBuilder, cipher}; #[test] fn test_bcrypt() { @@ -109,18 +109,18 @@ mod tests { .unwrap(); // get the kdf view - let kdf = ciphermk.kdf_view(&kdfmk).unwrap(); + let kdf = ViewBuilder::new(&ciphermk).kdf(&kdfmk).build().unwrap(); // derive a key from the passphrase and add it to the cipher multikey let ciphermk = kdf .derive_key(b"for great justice, move every zig!") .unwrap(); - let kattr = ciphermk.kdf_attr_view().unwrap(); + let kattr = ViewBuilder::new(&ciphermk).kdf_attr().build().unwrap(); assert_eq!(Codec::BcryptPbkdf, kattr.kdf_codec().unwrap()); assert_eq!(salt, kattr.salt_bytes().unwrap().to_vec()); assert_eq!(10, kattr.rounds().unwrap()); - let kd = ciphermk.data_view().unwrap(); + let kd = ViewBuilder::new(&ciphermk).data().build().unwrap(); assert_eq!( vec![ 119, 109, 13, 221, 140, 26, 88, 179, 135, 17, 119, 25, 176, 99, 5, 2, 203, 25, 82, diff --git a/src/keysplit.rs b/src/keysplit.rs index 41af1ab..ec0882f 100644 --- a/src/keysplit.rs +++ b/src/keysplit.rs @@ -20,7 +20,7 @@ //! normal Multikey encoders. use crate::mk::Attributes; -use crate::{AttrId, Builder, Error, Multikey, Views}; +use crate::{AttrId, Builder, Error, Multikey}; use blsful::inner_types::{G1Projective, G2Projective, Scalar as BlsScalar}; use curve25519_dalek::{ristretto::RistrettoPoint, scalar::Scalar as DalekScalar}; use elliptic_curve::ff::PrimeField; @@ -440,7 +440,7 @@ pub fn split( return Err(err("need 2 <= threshold <= limit <= 255")); } let codec = mk.codec(); - let secret = mk.data_view()?.secret_bytes()?; + let secret = crate::views::dispatch::dispatch_data_view(mk)?.secret_bytes()?; let payloads = build_payloads(codec, &secret, threshold, limit, rng)?; payloads.iter().map(|p| wrap_share(mk, p)).collect() } @@ -526,6 +526,7 @@ pub fn combine(shares: &[Multikey]) -> Result { #[cfg(test)] mod tests { use super::*; + use crate::ViewBuilder; use crate::mk; fn gen_key(codec: Codec) -> Multikey { @@ -536,7 +537,13 @@ mod tests { } fn secret(mk: &Multikey) -> Vec { - mk.data_view().unwrap().secret_bytes().unwrap().to_vec() + ViewBuilder::new(mk) + .data() + .build() + .unwrap() + .secret_bytes() + .unwrap() + .to_vec() } /// Split → verify every share → combine a non-contiguous subset → assert the @@ -750,8 +757,9 @@ mod tests { "mixed codecs" ); assert!(combine(&[]).is_err(), "empty set"); - let pk = gen_key(Codec::P256Priv) - .conv_view() + let pk = ViewBuilder::new(&gen_key(Codec::P256Priv)) + .conv() + .build() .unwrap() .to_public_key() .unwrap(); diff --git a/src/lib.rs b/src/lib.rs index 144c6ca..828708a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,8 @@ //! - Key encryption and decryption //! - Threshold key sharing (Shamir Secret Sharing) //! - SSH key format conversion +//! - View creation through the fluent `ViewBuilder`, including +//! local-codec factories for custom protocol keys //! - Nonce generation //! //! ## Quick Start @@ -50,6 +52,34 @@ //! assert_eq!(mk1, mk2); //! ``` //! +//! ### Creating Views +//! +//! Views are created with [`ViewBuilder`]: select one view kind with a +//! fluent selector, then build it. Standard codecs dispatch to the built-in +//! views; custom protocol keys (the `Codec::Identity` codec carrying the +//! `AlgorithmName`/`KeyType` attributes) have no built-in views and +//! dispatch to caller-supplied local-codec factories registered with +//! `with_local_codec`. The `cipher` and `kdf` kinds additionally attach the +//! second key, for example +//! `ViewBuilder::new(&mk).cipher(&cipher_key).build()`. +//! +//! ```rust +//! use multi_key::{Builder, ViewBuilder}; +//! use multi_codec::Codec; +//! +//! let mut rng = rand::rng(); +//! let mk = Builder::new_from_random_bytes(Codec::Ed25519Priv, &mut rng) +//! .unwrap() +//! .try_build() +//! .unwrap(); +//! +//! let signer = ViewBuilder::new(&mk).sign().build().unwrap(); +//! let sig = signer.sign(b"message", false, None).unwrap(); +//! +//! let verifier = ViewBuilder::new(&mk).verify().build().unwrap(); +//! verifier.verify(&sig, Some(b"message")).unwrap(); +//! ``` +//! //! ## Features //! //! - **`serde`** (default): Enables serde serialization @@ -72,6 +102,12 @@ //! McEliece multikeys still decode in default builds, but key generation //! fails with `UnsupportedCodec`. //! +//! The `Views` extension trait (and its impl for `Multikey`) is deprecated +//! since 2.1.0. It stays as a source-compatible delegating shim that calls +//! the same dispatch core [`ViewBuilder`] uses; construct views with +//! [`ViewBuilder`] instead. Note that `multi_sig::Views` is a different, +//! unrelated trait. +//! //! ## Security //! //! - Private keys are automatically zeroized on drop @@ -142,6 +178,13 @@ pub mod kdf; /// Key views pub mod views; +#[allow(deprecated)] +pub use views::Views; +pub use views::builder::{ + AttrKind, CipherAttrKind, CipherKind, ConvKind, DataKind, DisclosureKind, FingerprintKind, + KdfAttrKind, KdfKind, MerkleStateKind, OpenKind, SealKind, SignKind, ThresholdAttrKind, + ThresholdKeyKind, ThresholdKind, Unselected, VerifyKind, ViewBuilder, ViewKind, +}; pub use views::threshold_marker::{ self, MarkerView, ThresholdParticipant, ThresholdScheme, group_public_key, participants, set_group_public_key, set_participants, threshold_kind, threshold_params, @@ -154,7 +197,7 @@ pub use views::threshold_meta::{ pub use views::{ AttrView, CipherAttrView, CipherView, ConvView, DataView, FingerprintView, KdfAttrView, KdfView, MerkleStateView, OpenView, SealView, SignView, ThresholdAttrView, - ThresholdDisclosureView, ThresholdKeyView, ThresholdView, VerifyView, Views, + ThresholdDisclosureView, ThresholdKeyView, ThresholdView, VerifyView, }; /// Key splitting / recombination (verifiable threshold shares) diff --git a/src/mk.rs b/src/mk.rs index 81b4d99..bfcb352 100644 --- a/src/mk.rs +++ b/src/mk.rs @@ -1,22 +1,20 @@ // SPDX-License-Identifier: Apache-2.0 +#[allow(deprecated)] +use crate::views::Views; #[cfg(feature = "lamport")] use crate::views::lamport; #[cfg(feature = "lamport")] use crate::views::lamport_merkle; #[cfg(feature = "xmss")] use crate::views::xmss; -#[cfg(feature = "deprecated")] -use crate::views::{classic_mceliece, x25519_mceliece348864}; use crate::{ AttrId, AttrView, CipherAttrView, CipherView, ConvView, DataView, Error, FingerprintView, KdfAttrView, KdfView, MerkleStateView, OpenView, SealView, SignView, ThresholdAttrView, - ThresholdDisclosureView, ThresholdKeyView, ThresholdView, VerifyView, Views, - error::{AttributesError, CipherError, ConversionsError, KdfError, SealError, ThresholdError}, + ThresholdDisclosureView, ThresholdKeyView, ThresholdView, VerifyView, + error::{ConversionsError, ThresholdError}, views::{ - bcrypt, bls12381, bls12381_g1_fndsa512, bls12381_g1_mayo1, bls12381_g1_mayo2, - bls12381_g1_mldsa65, chacha20, ed25519, ed25519_fndsa512, ed25519_mayo2, ed25519_mldsa65, - fn_dsa, frodokem, mayo, ml_dsa, ml_kem, nist_p, rsa, secp256k1, slh_dsa, sntrup, - threshold_meta, x25519, x25519_frodokem640, x25519_mlkem768, x25519_sntrup761, xeddsa, + bls12381, fn_dsa, frodokem, ml_dsa, rsa, secp256k1, slh_dsa, threshold_meta, + x25519_frodokem640, }, }; @@ -395,7 +393,7 @@ impl Null for Multikey { impl fmt::Debug for Multikey { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // get an attributes view on the key - let attr = self.attr_view().map_err(|_| fmt::Error)?; + let attr = crate::views::dispatch::dispatch_attr_view(self).map_err(|_| fmt::Error)?; write!( f, @@ -407,1563 +405,91 @@ impl fmt::Debug for Multikey { } } +#[allow(deprecated)] impl Views for Multikey { /// Provide a read-only view of the basic attributes in the viewed Multikey fn attr_view<'a>(&'a self) -> Result, Error> { - match self.codec { - Codec::Bls12381G1PrivShare - | Codec::Bls12381G1Priv - | Codec::Bls12381G1Pub - | Codec::Bls12381G1PubShare - | Codec::Bls12381G2PrivShare - | Codec::Bls12381G2Priv - | Codec::Bls12381G2Pub - | Codec::Bls12381G2PubShare => Ok(Box::new(bls12381::View::try_from(self)?)), - Codec::Ed25519Pub | Codec::Ed25519Priv => Ok(Box::new(ed25519::View::try_from(self)?)), - Codec::Secp256K1Pub | Codec::Secp256K1Priv => { - Ok(Box::new(secp256k1::View::try_from(self)?)) - } - Codec::Chacha20Poly1305 => Ok(Box::new(chacha20::View::try_from(self)?)), - Codec::SlhDsaSha2128FPub - | Codec::SlhDsaSha2128SPub - | Codec::SlhDsaSha2192FPub - | Codec::SlhDsaSha2192SPub - | Codec::SlhDsaSha2256FPub - | Codec::SlhDsaSha2256SPub - | Codec::SlhDsaShake128FPub - | Codec::SlhDsaShake128SPub - | Codec::SlhDsaShake192FPub - | Codec::SlhDsaShake192SPub - | Codec::SlhDsaShake256FPub - | Codec::SlhDsaShake256SPub - | Codec::SlhDsaSha2128FPriv - | Codec::SlhDsaSha2128SPriv - | Codec::SlhDsaSha2192FPriv - | Codec::SlhDsaSha2192SPriv - | Codec::SlhDsaSha2256FPriv - | Codec::SlhDsaSha2256SPriv - | Codec::SlhDsaShake128FPriv - | Codec::SlhDsaShake128SPriv - | Codec::SlhDsaShake192FPriv - | Codec::SlhDsaShake192SPriv - | Codec::SlhDsaShake256FPriv - | Codec::SlhDsaShake256SPriv => Ok(Box::new(slh_dsa::View::try_from(self)?)), - Codec::MlDsa65Pub | Codec::MlDsa65Priv | Codec::MlDsa87Pub | Codec::MlDsa87Priv => { - Ok(Box::new(ml_dsa::View::try_from(self)?)) - } - Codec::Mayo1Pub - | Codec::Mayo1Priv - | Codec::Mayo2Pub - | Codec::Mayo2Priv - | Codec::Mayo3Pub - | Codec::Mayo3Priv - | Codec::Mayo5Pub - | Codec::Mayo5Priv => Ok(Box::new(mayo::View::try_from(self)?)), - Codec::FnDsa512Pub - | Codec::FnDsa512Priv - | Codec::FnDsa1024Pub - | Codec::FnDsa1024Priv => Ok(Box::new(fn_dsa::View::try_from(self)?)), - Codec::Mlkem768Pub - | Codec::Mlkem768Priv - | Codec::Mlkem1024Pub - | Codec::Mlkem1024Priv => Ok(Box::new(ml_kem::View::try_from(self)?)), - Codec::Sntrup761Pub - | Codec::Sntrup761Priv - | Codec::Sntrup857Pub - | Codec::Sntrup857Priv - | Codec::Sntrup953Pub - | Codec::Sntrup953Priv - | Codec::Sntrup1013Pub - | Codec::Sntrup1013Priv - | Codec::Sntrup1277Pub - | Codec::Sntrup1277Priv => Ok(Box::new(sntrup::View::try_from(self)?)), - #[cfg(feature = "deprecated")] - #[allow(deprecated)] - Codec::Mceliece348864Pub | Codec::Mceliece348864Priv => { - Ok(Box::new(classic_mceliece::View::try_from(self)?)) - } - Codec::FrodoKem640AesPub - | Codec::FrodoKem640AesPriv - | Codec::FrodoKem976AesPub - | Codec::FrodoKem976AesPriv - | Codec::FrodoKem1344AesPub - | Codec::FrodoKem1344AesPriv - | Codec::FrodoKem640ShakePub - | Codec::FrodoKem640ShakePriv - | Codec::FrodoKem976ShakePub - | Codec::FrodoKem976ShakePriv - | Codec::FrodoKem1344ShakePub - | Codec::FrodoKem1344ShakePriv => Ok(Box::new(frodokem::View::try_from(self)?)), - Codec::X25519Pub | Codec::X25519Priv => Ok(Box::new(x25519::View::try_from(self)?)), - Codec::X25519Sntrup761Pub | Codec::X25519Sntrup761Priv => { - Ok(Box::new(x25519_sntrup761::View::try_from(self)?)) - } - Codec::X25519Frodokem640AesPub - | Codec::X25519Frodokem640AesPriv - | Codec::X25519Frodokem640ShakePub - | Codec::X25519Frodokem640ShakePriv => { - Ok(Box::new(x25519_frodokem640::View::try_from(self)?)) - } - #[cfg(feature = "deprecated")] - #[allow(deprecated)] - Codec::X25519Mceliece348864Pub | Codec::X25519Mceliece348864Priv => { - Ok(Box::new(x25519_mceliece348864::View::try_from(self)?)) - } - Codec::X25519Mlkem768Pub | Codec::X25519Mlkem768Priv => { - Ok(Box::new(x25519_mlkem768::View::try_from(self)?)) - } - Codec::Ed25519Mayo2Pub | Codec::Ed25519Mayo2Priv => { - Ok(Box::new(ed25519_mayo2::View::try_from(self)?)) - } - Codec::Ed25519Mldsa65Pub | Codec::Ed25519Mldsa65Priv => { - Ok(Box::new(ed25519_mldsa65::View::try_from(self)?)) - } - Codec::Bls12381G1Mldsa65Pub | Codec::Bls12381G1Mldsa65Priv => { - Ok(Box::new(bls12381_g1_mldsa65::View::try_from(self)?)) - } - Codec::Bls12381G1Fndsa512Pub | Codec::Bls12381G1Fndsa512Priv => { - Ok(Box::new(bls12381_g1_fndsa512::View::try_from(self)?)) - } - Codec::Bls12381G1Mayo1Pub | Codec::Bls12381G1Mayo1Priv => { - Ok(Box::new(bls12381_g1_mayo1::View::try_from(self)?)) - } - Codec::Bls12381G1Mayo2Pub | Codec::Bls12381G1Mayo2Priv => { - Ok(Box::new(bls12381_g1_mayo2::View::try_from(self)?)) - } - Codec::Ed25519Fndsa512Pub | Codec::Ed25519Fndsa512Priv => { - Ok(Box::new(ed25519_fndsa512::View::try_from(self)?)) - } - Codec::P256Pub - | Codec::P256Priv - | Codec::P384Pub - | Codec::P384Priv - | Codec::P521Pub - | Codec::P521Priv => Ok(Box::new(nist_p::View::try_from(self)?)), - Codec::Rsa2048Pub - | Codec::Rsa2048Priv - | Codec::Rsa3072Pub - | Codec::Rsa3072Priv - | Codec::Rsa4096Pub - | Codec::Rsa4096Priv => Ok(Box::new(rsa::View::try_from(self)?)), - #[cfg(feature = "lamport")] - Codec::LamportSha3256Pub - | Codec::LamportSha3256Priv - | Codec::LamportSha3256PrivShare - | Codec::LamportSha3384Pub - | Codec::LamportSha3384Priv - | Codec::LamportSha3384PrivShare - | Codec::LamportSha3512Pub - | Codec::LamportSha3512Priv - | Codec::LamportSha3512PrivShare - | Codec::LamportSha2256Pub - | Codec::LamportSha2256Priv - | Codec::LamportSha2256PrivShare - | Codec::LamportSha2384Pub - | Codec::LamportSha2384Priv - | Codec::LamportSha2384PrivShare - | Codec::LamportSha2512Pub - | Codec::LamportSha2512Priv - | Codec::LamportSha2512PrivShare - | Codec::LamportBlake2B512Pub - | Codec::LamportBlake2B512Priv - | Codec::LamportBlake2B512PrivShare - | Codec::LamportBlake2S256Pub - | Codec::LamportBlake2S256Priv - | Codec::LamportBlake2S256PrivShare - | Codec::LamportBlake3256Pub - | Codec::LamportBlake3256Priv - | Codec::LamportBlake3256PrivShare - | Codec::LamportShake128Pub - | Codec::LamportShake128Priv - | Codec::LamportShake128PrivShare - | Codec::LamportShake256Pub - | Codec::LamportShake256Priv - | Codec::LamportShake256PrivShare => Ok(Box::new(lamport::View::try_from(self)?)), - #[cfg(feature = "lamport")] - Codec::LamportMerkleSha3256Pub - | Codec::LamportMerkleSha3256Priv - | Codec::LamportMerkleSha3256PrivShare - | Codec::LamportMerkleSha3384Pub - | Codec::LamportMerkleSha3384Priv - | Codec::LamportMerkleSha3384PrivShare - | Codec::LamportMerkleSha3512Pub - | Codec::LamportMerkleSha3512Priv - | Codec::LamportMerkleSha3512PrivShare - | Codec::LamportMerkleSha2256Pub - | Codec::LamportMerkleSha2256Priv - | Codec::LamportMerkleSha2256PrivShare - | Codec::LamportMerkleSha2384Pub - | Codec::LamportMerkleSha2384Priv - | Codec::LamportMerkleSha2384PrivShare - | Codec::LamportMerkleSha2512Pub - | Codec::LamportMerkleSha2512Priv - | Codec::LamportMerkleSha2512PrivShare - | Codec::LamportMerkleBlake2B512Pub - | Codec::LamportMerkleBlake2B512Priv - | Codec::LamportMerkleBlake2B512PrivShare - | Codec::LamportMerkleBlake2S256Pub - | Codec::LamportMerkleBlake2S256Priv - | Codec::LamportMerkleBlake2S256PrivShare - | Codec::LamportMerkleBlake3256Pub - | Codec::LamportMerkleBlake3256Priv - | Codec::LamportMerkleBlake3256PrivShare - | Codec::LamportMerkleShake128Pub - | Codec::LamportMerkleShake128Priv - | Codec::LamportMerkleShake128PrivShare - | Codec::LamportMerkleShake256Pub - | Codec::LamportMerkleShake256Priv - | Codec::LamportMerkleShake256PrivShare => { - Ok(Box::new(lamport_merkle::View::try_from(self)?)) - } - #[cfg(feature = "xmss")] - Codec::XmssSha210256Pub - | Codec::XmssSha210256Priv - | Codec::XmssSha216256Pub - | Codec::XmssSha216256Priv - | Codec::XmssSha220256Pub - | Codec::XmssSha220256Priv => Ok(Box::new(xmss::View::try_from(self)?)), - _ => Err(AttributesError::UnsupportedCodec(self.codec).into()), - } + crate::views::dispatch::dispatch_attr_view(self) } /// Provide a read-only view of the cipher attributes in the viewed Multikey fn cipher_attr_view<'a>(&'a self) -> Result, Error> { - let codec = if let Some(bytes) = self.attributes.get(&AttrId::CipherCodec) { - Codec::try_from(bytes.as_slice())? - } else { - self.codec - }; - match codec { - Codec::Chacha20Poly1305 => Ok(Box::new(chacha20::View::try_from(self)?)), - _ => Err(CipherError::UnsupportedCodec(self.codec).into()), - } + crate::views::dispatch::dispatch_cipher_attr_view(self) } /// Provide a read-only view to key data in the viewed Multikey fn data_view<'a>(&'a self) -> Result, Error> { - match self.codec { - Codec::Bls12381G1PrivShare - | Codec::Bls12381G1Priv - | Codec::Bls12381G1Pub - | Codec::Bls12381G1PubShare - | Codec::Bls12381G2PrivShare - | Codec::Bls12381G2Priv - | Codec::Bls12381G2Pub - | Codec::Bls12381G2PubShare => Ok(Box::new(bls12381::View::try_from(self)?)), - Codec::Ed25519Pub | Codec::Ed25519Priv => Ok(Box::new(ed25519::View::try_from(self)?)), - Codec::Secp256K1Pub | Codec::Secp256K1Priv => { - Ok(Box::new(secp256k1::View::try_from(self)?)) - } - Codec::Chacha20Poly1305 => Ok(Box::new(chacha20::View::try_from(self)?)), - Codec::SlhDsaSha2128FPub - | Codec::SlhDsaSha2128SPub - | Codec::SlhDsaSha2192FPub - | Codec::SlhDsaSha2192SPub - | Codec::SlhDsaSha2256FPub - | Codec::SlhDsaSha2256SPub - | Codec::SlhDsaShake128FPub - | Codec::SlhDsaShake128SPub - | Codec::SlhDsaShake192FPub - | Codec::SlhDsaShake192SPub - | Codec::SlhDsaShake256FPub - | Codec::SlhDsaShake256SPub - | Codec::SlhDsaSha2128FPriv - | Codec::SlhDsaSha2128SPriv - | Codec::SlhDsaSha2192FPriv - | Codec::SlhDsaSha2192SPriv - | Codec::SlhDsaSha2256FPriv - | Codec::SlhDsaSha2256SPriv - | Codec::SlhDsaShake128FPriv - | Codec::SlhDsaShake128SPriv - | Codec::SlhDsaShake192FPriv - | Codec::SlhDsaShake192SPriv - | Codec::SlhDsaShake256FPriv - | Codec::SlhDsaShake256SPriv => Ok(Box::new(slh_dsa::View::try_from(self)?)), - Codec::MlDsa65Pub | Codec::MlDsa65Priv | Codec::MlDsa87Pub | Codec::MlDsa87Priv => { - Ok(Box::new(ml_dsa::View::try_from(self)?)) - } - Codec::Mayo1Pub - | Codec::Mayo1Priv - | Codec::Mayo2Pub - | Codec::Mayo2Priv - | Codec::Mayo3Pub - | Codec::Mayo3Priv - | Codec::Mayo5Pub - | Codec::Mayo5Priv => Ok(Box::new(mayo::View::try_from(self)?)), - Codec::FnDsa512Pub - | Codec::FnDsa512Priv - | Codec::FnDsa1024Pub - | Codec::FnDsa1024Priv => Ok(Box::new(fn_dsa::View::try_from(self)?)), - Codec::Mlkem768Pub - | Codec::Mlkem768Priv - | Codec::Mlkem1024Pub - | Codec::Mlkem1024Priv => Ok(Box::new(ml_kem::View::try_from(self)?)), - Codec::Sntrup761Pub - | Codec::Sntrup761Priv - | Codec::Sntrup857Pub - | Codec::Sntrup857Priv - | Codec::Sntrup953Pub - | Codec::Sntrup953Priv - | Codec::Sntrup1013Pub - | Codec::Sntrup1013Priv - | Codec::Sntrup1277Pub - | Codec::Sntrup1277Priv => Ok(Box::new(sntrup::View::try_from(self)?)), - #[cfg(feature = "deprecated")] - #[allow(deprecated)] - Codec::Mceliece348864Pub | Codec::Mceliece348864Priv => { - Ok(Box::new(classic_mceliece::View::try_from(self)?)) - } - Codec::FrodoKem640AesPub - | Codec::FrodoKem640AesPriv - | Codec::FrodoKem976AesPub - | Codec::FrodoKem976AesPriv - | Codec::FrodoKem1344AesPub - | Codec::FrodoKem1344AesPriv - | Codec::FrodoKem640ShakePub - | Codec::FrodoKem640ShakePriv - | Codec::FrodoKem976ShakePub - | Codec::FrodoKem976ShakePriv - | Codec::FrodoKem1344ShakePub - | Codec::FrodoKem1344ShakePriv => Ok(Box::new(frodokem::View::try_from(self)?)), - Codec::X25519Pub | Codec::X25519Priv => Ok(Box::new(x25519::View::try_from(self)?)), - Codec::X25519Sntrup761Pub | Codec::X25519Sntrup761Priv => { - Ok(Box::new(x25519_sntrup761::View::try_from(self)?)) - } - Codec::X25519Frodokem640AesPub - | Codec::X25519Frodokem640AesPriv - | Codec::X25519Frodokem640ShakePub - | Codec::X25519Frodokem640ShakePriv => { - Ok(Box::new(x25519_frodokem640::View::try_from(self)?)) - } - #[cfg(feature = "deprecated")] - #[allow(deprecated)] - Codec::X25519Mceliece348864Pub | Codec::X25519Mceliece348864Priv => { - Ok(Box::new(x25519_mceliece348864::View::try_from(self)?)) - } - Codec::X25519Mlkem768Pub | Codec::X25519Mlkem768Priv => { - Ok(Box::new(x25519_mlkem768::View::try_from(self)?)) - } - Codec::Ed25519Mayo2Pub | Codec::Ed25519Mayo2Priv => { - Ok(Box::new(ed25519_mayo2::View::try_from(self)?)) - } - Codec::Ed25519Mldsa65Pub | Codec::Ed25519Mldsa65Priv => { - Ok(Box::new(ed25519_mldsa65::View::try_from(self)?)) - } - Codec::Bls12381G1Mldsa65Pub | Codec::Bls12381G1Mldsa65Priv => { - Ok(Box::new(bls12381_g1_mldsa65::View::try_from(self)?)) - } - Codec::Bls12381G1Fndsa512Pub | Codec::Bls12381G1Fndsa512Priv => { - Ok(Box::new(bls12381_g1_fndsa512::View::try_from(self)?)) - } - Codec::Bls12381G1Mayo1Pub | Codec::Bls12381G1Mayo1Priv => { - Ok(Box::new(bls12381_g1_mayo1::View::try_from(self)?)) - } - Codec::Bls12381G1Mayo2Pub | Codec::Bls12381G1Mayo2Priv => { - Ok(Box::new(bls12381_g1_mayo2::View::try_from(self)?)) - } - Codec::Ed25519Fndsa512Pub | Codec::Ed25519Fndsa512Priv => { - Ok(Box::new(ed25519_fndsa512::View::try_from(self)?)) - } - Codec::P256Pub - | Codec::P256Priv - | Codec::P384Pub - | Codec::P384Priv - | Codec::P521Pub - | Codec::P521Priv => Ok(Box::new(nist_p::View::try_from(self)?)), - Codec::Rsa2048Pub - | Codec::Rsa2048Priv - | Codec::Rsa3072Pub - | Codec::Rsa3072Priv - | Codec::Rsa4096Pub - | Codec::Rsa4096Priv => Ok(Box::new(rsa::View::try_from(self)?)), - #[cfg(feature = "lamport")] - Codec::LamportSha3256Pub - | Codec::LamportSha3256Priv - | Codec::LamportSha3256PrivShare - | Codec::LamportSha3384Pub - | Codec::LamportSha3384Priv - | Codec::LamportSha3384PrivShare - | Codec::LamportSha3512Pub - | Codec::LamportSha3512Priv - | Codec::LamportSha3512PrivShare - | Codec::LamportSha2256Pub - | Codec::LamportSha2256Priv - | Codec::LamportSha2256PrivShare - | Codec::LamportSha2384Pub - | Codec::LamportSha2384Priv - | Codec::LamportSha2384PrivShare - | Codec::LamportSha2512Pub - | Codec::LamportSha2512Priv - | Codec::LamportSha2512PrivShare - | Codec::LamportBlake2B512Pub - | Codec::LamportBlake2B512Priv - | Codec::LamportBlake2B512PrivShare - | Codec::LamportBlake2S256Pub - | Codec::LamportBlake2S256Priv - | Codec::LamportBlake2S256PrivShare - | Codec::LamportBlake3256Pub - | Codec::LamportBlake3256Priv - | Codec::LamportBlake3256PrivShare - | Codec::LamportShake128Pub - | Codec::LamportShake128Priv - | Codec::LamportShake128PrivShare - | Codec::LamportShake256Pub - | Codec::LamportShake256Priv - | Codec::LamportShake256PrivShare => Ok(Box::new(lamport::View::try_from(self)?)), - #[cfg(feature = "lamport")] - Codec::LamportMerkleSha3256Pub - | Codec::LamportMerkleSha3256Priv - | Codec::LamportMerkleSha3256PrivShare - | Codec::LamportMerkleSha3384Pub - | Codec::LamportMerkleSha3384Priv - | Codec::LamportMerkleSha3384PrivShare - | Codec::LamportMerkleSha3512Pub - | Codec::LamportMerkleSha3512Priv - | Codec::LamportMerkleSha3512PrivShare - | Codec::LamportMerkleSha2256Pub - | Codec::LamportMerkleSha2256Priv - | Codec::LamportMerkleSha2256PrivShare - | Codec::LamportMerkleSha2384Pub - | Codec::LamportMerkleSha2384Priv - | Codec::LamportMerkleSha2384PrivShare - | Codec::LamportMerkleSha2512Pub - | Codec::LamportMerkleSha2512Priv - | Codec::LamportMerkleSha2512PrivShare - | Codec::LamportMerkleBlake2B512Pub - | Codec::LamportMerkleBlake2B512Priv - | Codec::LamportMerkleBlake2B512PrivShare - | Codec::LamportMerkleBlake2S256Pub - | Codec::LamportMerkleBlake2S256Priv - | Codec::LamportMerkleBlake2S256PrivShare - | Codec::LamportMerkleBlake3256Pub - | Codec::LamportMerkleBlake3256Priv - | Codec::LamportMerkleBlake3256PrivShare - | Codec::LamportMerkleShake128Pub - | Codec::LamportMerkleShake128Priv - | Codec::LamportMerkleShake128PrivShare - | Codec::LamportMerkleShake256Pub - | Codec::LamportMerkleShake256Priv - | Codec::LamportMerkleShake256PrivShare => { - Ok(Box::new(lamport_merkle::View::try_from(self)?)) - } - #[cfg(feature = "xmss")] - Codec::XmssSha210256Pub - | Codec::XmssSha210256Priv - | Codec::XmssSha216256Pub - | Codec::XmssSha216256Priv - | Codec::XmssSha220256Pub - | Codec::XmssSha220256Priv => Ok(Box::new(xmss::View::try_from(self)?)), - _ => Err(ConversionsError::UnsupportedCodec(self.codec).into()), - } + crate::views::dispatch::dispatch_data_view(self) } /// Provide a read-only view of the kdf attributes in the viewed Multikey fn kdf_attr_view<'a>(&'a self) -> Result, Error> { - let codec = if let Some(bytes) = self.attributes.get(&AttrId::KdfCodec) { - Codec::try_from(bytes.as_slice())? - } else { - self.codec - }; - match codec { - Codec::BcryptPbkdf => Ok(Box::new(bcrypt::View::try_from(self)?)), - _ => Err(KdfError::UnsupportedCodec(self.codec).into()), - } + crate::views::dispatch::dispatch_kdf_attr_view(self) } /// Provide a read-only view of the threshold attributes in the viewed Multikey fn threshold_attr_view<'a>(&'a self) -> Result, Error> { - match self.codec { - Codec::Bls12381G1PrivShare - | Codec::Bls12381G1Priv - | Codec::Bls12381G1Pub - | Codec::Bls12381G1PubShare - | Codec::Bls12381G2PrivShare - | Codec::Bls12381G2Priv - | Codec::Bls12381G2Pub - | Codec::Bls12381G2PubShare => Ok(Box::new(bls12381::View::try_from(self)?)), - Codec::Ed25519ThreshPrivShare - | Codec::P256ThreshPrivShare - | Codec::P384ThreshPrivShare - | Codec::Secp256K1ThreshPrivShare - | Codec::Bls12381ThreshPrivShare - | Codec::Ed448ThreshPrivShare => { - Ok(Box::new(crate::views::dkg_threshold::View::try_from(self)?)) - } - _ => Err(ConversionsError::UnsupportedCodec(self.codec).into()), - } + crate::views::dispatch::dispatch_threshold_attr_view(self) } /// Provide a read-only view of higher-level threshold key metadata in the viewed Multikey fn threshold_key_view<'a>(&'a self) -> Result, Error> { - match self.codec { - Codec::Ed25519ThreshPrivShare - | Codec::P256ThreshPrivShare - | Codec::P384ThreshPrivShare - | Codec::Secp256K1ThreshPrivShare - | Codec::Bls12381ThreshPrivShare - | Codec::Ed448ThreshPrivShare => { - Ok(Box::new(crate::views::dkg_threshold::View::try_from(self)?)) - } - _ => Err(ConversionsError::UnsupportedCodec(self.codec).into()), - } + crate::views::dispatch::dispatch_threshold_key_view(self) } /// Provide an interface to do encryption/decryption of the viewed Multikey fn cipher_view<'a>(&'a self, cipher: &'a Multikey) -> Result, Error> { - match cipher.codec { - Codec::Chacha20Poly1305 => Ok(Box::new(chacha20::View::new(self, cipher))), - _ => Err(CipherError::UnsupportedCodec(self.codec).into()), - } + crate::views::dispatch::dispatch_cipher_view(self, cipher) } /// Provide an interface to do key conversions from the viewe Multikey fn conv_view<'a>(&'a self) -> Result, Error> { - match self.codec { - Codec::Bls12381G1PrivShare - | Codec::Bls12381G1Priv - | Codec::Bls12381G1Pub - | Codec::Bls12381G1PubShare - | Codec::Bls12381G2PrivShare - | Codec::Bls12381G2Priv - | Codec::Bls12381G2Pub - | Codec::Bls12381G2PubShare => Ok(Box::new(bls12381::View::try_from(self)?)), - Codec::Ed25519Pub | Codec::Ed25519Priv => Ok(Box::new(ed25519::View::try_from(self)?)), - Codec::Secp256K1Pub | Codec::Secp256K1Priv => { - Ok(Box::new(secp256k1::View::try_from(self)?)) - } - Codec::SlhDsaSha2128FPub - | Codec::SlhDsaSha2128SPub - | Codec::SlhDsaSha2192FPub - | Codec::SlhDsaSha2192SPub - | Codec::SlhDsaSha2256FPub - | Codec::SlhDsaSha2256SPub - | Codec::SlhDsaShake128FPub - | Codec::SlhDsaShake128SPub - | Codec::SlhDsaShake192FPub - | Codec::SlhDsaShake192SPub - | Codec::SlhDsaShake256FPub - | Codec::SlhDsaShake256SPub - | Codec::SlhDsaSha2128FPriv - | Codec::SlhDsaSha2128SPriv - | Codec::SlhDsaSha2192FPriv - | Codec::SlhDsaSha2192SPriv - | Codec::SlhDsaSha2256FPriv - | Codec::SlhDsaSha2256SPriv - | Codec::SlhDsaShake128FPriv - | Codec::SlhDsaShake128SPriv - | Codec::SlhDsaShake192FPriv - | Codec::SlhDsaShake192SPriv - | Codec::SlhDsaShake256FPriv - | Codec::SlhDsaShake256SPriv => Ok(Box::new(slh_dsa::View::try_from(self)?)), - Codec::MlDsa65Pub | Codec::MlDsa65Priv | Codec::MlDsa87Pub | Codec::MlDsa87Priv => { - Ok(Box::new(ml_dsa::View::try_from(self)?)) - } - Codec::Mayo1Pub - | Codec::Mayo1Priv - | Codec::Mayo2Pub - | Codec::Mayo2Priv - | Codec::Mayo3Pub - | Codec::Mayo3Priv - | Codec::Mayo5Pub - | Codec::Mayo5Priv => Ok(Box::new(mayo::View::try_from(self)?)), - Codec::FnDsa512Pub - | Codec::FnDsa512Priv - | Codec::FnDsa1024Pub - | Codec::FnDsa1024Priv => Ok(Box::new(fn_dsa::View::try_from(self)?)), - Codec::Mlkem768Pub - | Codec::Mlkem768Priv - | Codec::Mlkem1024Pub - | Codec::Mlkem1024Priv => Ok(Box::new(ml_kem::View::try_from(self)?)), - Codec::Sntrup761Pub - | Codec::Sntrup761Priv - | Codec::Sntrup857Pub - | Codec::Sntrup857Priv - | Codec::Sntrup953Pub - | Codec::Sntrup953Priv - | Codec::Sntrup1013Pub - | Codec::Sntrup1013Priv - | Codec::Sntrup1277Pub - | Codec::Sntrup1277Priv => Ok(Box::new(sntrup::View::try_from(self)?)), - #[cfg(feature = "deprecated")] - #[allow(deprecated)] - Codec::Mceliece348864Pub | Codec::Mceliece348864Priv => { - Ok(Box::new(classic_mceliece::View::try_from(self)?)) - } - Codec::FrodoKem640AesPub - | Codec::FrodoKem640AesPriv - | Codec::FrodoKem976AesPub - | Codec::FrodoKem976AesPriv - | Codec::FrodoKem1344AesPub - | Codec::FrodoKem1344AesPriv - | Codec::FrodoKem640ShakePub - | Codec::FrodoKem640ShakePriv - | Codec::FrodoKem976ShakePub - | Codec::FrodoKem976ShakePriv - | Codec::FrodoKem1344ShakePub - | Codec::FrodoKem1344ShakePriv => Ok(Box::new(frodokem::View::try_from(self)?)), - Codec::X25519Pub | Codec::X25519Priv => Ok(Box::new(x25519::View::try_from(self)?)), - Codec::X25519Sntrup761Pub | Codec::X25519Sntrup761Priv => { - Ok(Box::new(x25519_sntrup761::View::try_from(self)?)) - } - Codec::X25519Frodokem640AesPub - | Codec::X25519Frodokem640AesPriv - | Codec::X25519Frodokem640ShakePub - | Codec::X25519Frodokem640ShakePriv => { - Ok(Box::new(x25519_frodokem640::View::try_from(self)?)) - } - #[cfg(feature = "deprecated")] - #[allow(deprecated)] - Codec::X25519Mceliece348864Pub | Codec::X25519Mceliece348864Priv => { - Ok(Box::new(x25519_mceliece348864::View::try_from(self)?)) - } - Codec::X25519Mlkem768Pub | Codec::X25519Mlkem768Priv => { - Ok(Box::new(x25519_mlkem768::View::try_from(self)?)) - } - Codec::Ed25519Mayo2Pub | Codec::Ed25519Mayo2Priv => { - Ok(Box::new(ed25519_mayo2::View::try_from(self)?)) - } - Codec::Ed25519Mldsa65Pub | Codec::Ed25519Mldsa65Priv => { - Ok(Box::new(ed25519_mldsa65::View::try_from(self)?)) - } - Codec::Bls12381G1Mldsa65Pub | Codec::Bls12381G1Mldsa65Priv => { - Ok(Box::new(bls12381_g1_mldsa65::View::try_from(self)?)) - } - Codec::Bls12381G1Fndsa512Pub | Codec::Bls12381G1Fndsa512Priv => { - Ok(Box::new(bls12381_g1_fndsa512::View::try_from(self)?)) - } - Codec::Bls12381G1Mayo1Pub | Codec::Bls12381G1Mayo1Priv => { - Ok(Box::new(bls12381_g1_mayo1::View::try_from(self)?)) - } - Codec::Bls12381G1Mayo2Pub | Codec::Bls12381G1Mayo2Priv => { - Ok(Box::new(bls12381_g1_mayo2::View::try_from(self)?)) - } - Codec::Ed25519Fndsa512Pub | Codec::Ed25519Fndsa512Priv => { - Ok(Box::new(ed25519_fndsa512::View::try_from(self)?)) - } - Codec::P256Pub - | Codec::P256Priv - | Codec::P384Pub - | Codec::P384Priv - | Codec::P521Pub - | Codec::P521Priv => Ok(Box::new(nist_p::View::try_from(self)?)), - Codec::Rsa2048Pub - | Codec::Rsa2048Priv - | Codec::Rsa3072Pub - | Codec::Rsa3072Priv - | Codec::Rsa4096Pub - | Codec::Rsa4096Priv => Ok(Box::new(rsa::View::try_from(self)?)), - #[cfg(feature = "lamport")] - Codec::LamportSha3256Pub - | Codec::LamportSha3256Priv - | Codec::LamportSha3256PrivShare - | Codec::LamportSha3384Pub - | Codec::LamportSha3384Priv - | Codec::LamportSha3384PrivShare - | Codec::LamportSha3512Pub - | Codec::LamportSha3512Priv - | Codec::LamportSha3512PrivShare - | Codec::LamportSha2256Pub - | Codec::LamportSha2256Priv - | Codec::LamportSha2256PrivShare - | Codec::LamportSha2384Pub - | Codec::LamportSha2384Priv - | Codec::LamportSha2384PrivShare - | Codec::LamportSha2512Pub - | Codec::LamportSha2512Priv - | Codec::LamportSha2512PrivShare - | Codec::LamportBlake2B512Pub - | Codec::LamportBlake2B512Priv - | Codec::LamportBlake2B512PrivShare - | Codec::LamportBlake2S256Pub - | Codec::LamportBlake2S256Priv - | Codec::LamportBlake2S256PrivShare - | Codec::LamportBlake3256Pub - | Codec::LamportBlake3256Priv - | Codec::LamportBlake3256PrivShare - | Codec::LamportShake128Pub - | Codec::LamportShake128Priv - | Codec::LamportShake128PrivShare - | Codec::LamportShake256Pub - | Codec::LamportShake256Priv - | Codec::LamportShake256PrivShare => Ok(Box::new(lamport::View::try_from(self)?)), - #[cfg(feature = "lamport")] - Codec::LamportMerkleSha3256Pub - | Codec::LamportMerkleSha3256Priv - | Codec::LamportMerkleSha3256PrivShare - | Codec::LamportMerkleSha3384Pub - | Codec::LamportMerkleSha3384Priv - | Codec::LamportMerkleSha3384PrivShare - | Codec::LamportMerkleSha3512Pub - | Codec::LamportMerkleSha3512Priv - | Codec::LamportMerkleSha3512PrivShare - | Codec::LamportMerkleSha2256Pub - | Codec::LamportMerkleSha2256Priv - | Codec::LamportMerkleSha2256PrivShare - | Codec::LamportMerkleSha2384Pub - | Codec::LamportMerkleSha2384Priv - | Codec::LamportMerkleSha2384PrivShare - | Codec::LamportMerkleSha2512Pub - | Codec::LamportMerkleSha2512Priv - | Codec::LamportMerkleSha2512PrivShare - | Codec::LamportMerkleBlake2B512Pub - | Codec::LamportMerkleBlake2B512Priv - | Codec::LamportMerkleBlake2B512PrivShare - | Codec::LamportMerkleBlake2S256Pub - | Codec::LamportMerkleBlake2S256Priv - | Codec::LamportMerkleBlake2S256PrivShare - | Codec::LamportMerkleBlake3256Pub - | Codec::LamportMerkleBlake3256Priv - | Codec::LamportMerkleBlake3256PrivShare - | Codec::LamportMerkleShake128Pub - | Codec::LamportMerkleShake128Priv - | Codec::LamportMerkleShake128PrivShare - | Codec::LamportMerkleShake256Pub - | Codec::LamportMerkleShake256Priv - | Codec::LamportMerkleShake256PrivShare => { - Ok(Box::new(lamport_merkle::View::try_from(self)?)) - } - #[cfg(feature = "xmss")] - Codec::XmssSha210256Pub - | Codec::XmssSha210256Priv - | Codec::XmssSha216256Pub - | Codec::XmssSha216256Priv - | Codec::XmssSha220256Pub - | Codec::XmssSha220256Priv => Ok(Box::new(xmss::View::try_from(self)?)), - _ => Err(ConversionsError::UnsupportedCodec(self.codec).into()), - } + crate::views::dispatch::dispatch_conv_view(self) } /// Provide an interface to do key conversions from the viewe Multikey fn fingerprint_view<'a>(&'a self) -> Result, Error> { - match self.codec { - Codec::Bls12381G1PrivShare - | Codec::Bls12381G1Priv - | Codec::Bls12381G1Pub - | Codec::Bls12381G1PubShare - | Codec::Bls12381G2PrivShare - | Codec::Bls12381G2Priv - | Codec::Bls12381G2Pub - | Codec::Bls12381G2PubShare => Ok(Box::new(bls12381::View::try_from(self)?)), - Codec::Ed25519Pub | Codec::Ed25519Priv => Ok(Box::new(ed25519::View::try_from(self)?)), - Codec::Secp256K1Pub | Codec::Secp256K1Priv => { - Ok(Box::new(secp256k1::View::try_from(self)?)) - } - Codec::Chacha20Poly1305 => Ok(Box::new(chacha20::View::try_from(self)?)), - Codec::SlhDsaSha2128FPub - | Codec::SlhDsaSha2128SPub - | Codec::SlhDsaSha2192FPub - | Codec::SlhDsaSha2192SPub - | Codec::SlhDsaSha2256FPub - | Codec::SlhDsaSha2256SPub - | Codec::SlhDsaShake128FPub - | Codec::SlhDsaShake128SPub - | Codec::SlhDsaShake192FPub - | Codec::SlhDsaShake192SPub - | Codec::SlhDsaShake256FPub - | Codec::SlhDsaShake256SPub - | Codec::SlhDsaSha2128FPriv - | Codec::SlhDsaSha2128SPriv - | Codec::SlhDsaSha2192FPriv - | Codec::SlhDsaSha2192SPriv - | Codec::SlhDsaSha2256FPriv - | Codec::SlhDsaSha2256SPriv - | Codec::SlhDsaShake128FPriv - | Codec::SlhDsaShake128SPriv - | Codec::SlhDsaShake192FPriv - | Codec::SlhDsaShake192SPriv - | Codec::SlhDsaShake256FPriv - | Codec::SlhDsaShake256SPriv => Ok(Box::new(slh_dsa::View::try_from(self)?)), - Codec::MlDsa65Pub | Codec::MlDsa65Priv | Codec::MlDsa87Pub | Codec::MlDsa87Priv => { - Ok(Box::new(ml_dsa::View::try_from(self)?)) - } - Codec::Mayo1Pub - | Codec::Mayo1Priv - | Codec::Mayo2Pub - | Codec::Mayo2Priv - | Codec::Mayo3Pub - | Codec::Mayo3Priv - | Codec::Mayo5Pub - | Codec::Mayo5Priv => Ok(Box::new(mayo::View::try_from(self)?)), - Codec::FnDsa512Pub - | Codec::FnDsa512Priv - | Codec::FnDsa1024Pub - | Codec::FnDsa1024Priv => Ok(Box::new(fn_dsa::View::try_from(self)?)), - Codec::Mlkem768Pub - | Codec::Mlkem768Priv - | Codec::Mlkem1024Pub - | Codec::Mlkem1024Priv => Ok(Box::new(ml_kem::View::try_from(self)?)), - Codec::Sntrup761Pub - | Codec::Sntrup761Priv - | Codec::Sntrup857Pub - | Codec::Sntrup857Priv - | Codec::Sntrup953Pub - | Codec::Sntrup953Priv - | Codec::Sntrup1013Pub - | Codec::Sntrup1013Priv - | Codec::Sntrup1277Pub - | Codec::Sntrup1277Priv => Ok(Box::new(sntrup::View::try_from(self)?)), - #[cfg(feature = "deprecated")] - #[allow(deprecated)] - Codec::Mceliece348864Pub | Codec::Mceliece348864Priv => { - Ok(Box::new(classic_mceliece::View::try_from(self)?)) - } - Codec::FrodoKem640AesPub - | Codec::FrodoKem640AesPriv - | Codec::FrodoKem976AesPub - | Codec::FrodoKem976AesPriv - | Codec::FrodoKem1344AesPub - | Codec::FrodoKem1344AesPriv - | Codec::FrodoKem640ShakePub - | Codec::FrodoKem640ShakePriv - | Codec::FrodoKem976ShakePub - | Codec::FrodoKem976ShakePriv - | Codec::FrodoKem1344ShakePub - | Codec::FrodoKem1344ShakePriv => Ok(Box::new(frodokem::View::try_from(self)?)), - Codec::X25519Pub | Codec::X25519Priv => Ok(Box::new(x25519::View::try_from(self)?)), - Codec::X25519Sntrup761Pub | Codec::X25519Sntrup761Priv => { - Ok(Box::new(x25519_sntrup761::View::try_from(self)?)) - } - Codec::X25519Frodokem640AesPub - | Codec::X25519Frodokem640AesPriv - | Codec::X25519Frodokem640ShakePub - | Codec::X25519Frodokem640ShakePriv => { - Ok(Box::new(x25519_frodokem640::View::try_from(self)?)) - } - #[cfg(feature = "deprecated")] - #[allow(deprecated)] - Codec::X25519Mceliece348864Pub | Codec::X25519Mceliece348864Priv => { - Ok(Box::new(x25519_mceliece348864::View::try_from(self)?)) - } - Codec::X25519Mlkem768Pub | Codec::X25519Mlkem768Priv => { - Ok(Box::new(x25519_mlkem768::View::try_from(self)?)) - } - Codec::Ed25519Mayo2Pub | Codec::Ed25519Mayo2Priv => { - Ok(Box::new(ed25519_mayo2::View::try_from(self)?)) - } - Codec::Ed25519Mldsa65Pub | Codec::Ed25519Mldsa65Priv => { - Ok(Box::new(ed25519_mldsa65::View::try_from(self)?)) - } - Codec::Bls12381G1Mldsa65Pub | Codec::Bls12381G1Mldsa65Priv => { - Ok(Box::new(bls12381_g1_mldsa65::View::try_from(self)?)) - } - Codec::Bls12381G1Fndsa512Pub | Codec::Bls12381G1Fndsa512Priv => { - Ok(Box::new(bls12381_g1_fndsa512::View::try_from(self)?)) - } - Codec::Bls12381G1Mayo1Pub | Codec::Bls12381G1Mayo1Priv => { - Ok(Box::new(bls12381_g1_mayo1::View::try_from(self)?)) - } - Codec::Bls12381G1Mayo2Pub | Codec::Bls12381G1Mayo2Priv => { - Ok(Box::new(bls12381_g1_mayo2::View::try_from(self)?)) - } - Codec::Ed25519Fndsa512Pub | Codec::Ed25519Fndsa512Priv => { - Ok(Box::new(ed25519_fndsa512::View::try_from(self)?)) - } - Codec::P256Pub - | Codec::P256Priv - | Codec::P384Pub - | Codec::P384Priv - | Codec::P521Pub - | Codec::P521Priv => Ok(Box::new(nist_p::View::try_from(self)?)), - Codec::Rsa2048Pub - | Codec::Rsa2048Priv - | Codec::Rsa3072Pub - | Codec::Rsa3072Priv - | Codec::Rsa4096Pub - | Codec::Rsa4096Priv => Ok(Box::new(rsa::View::try_from(self)?)), - #[cfg(feature = "lamport")] - Codec::LamportSha3256Pub - | Codec::LamportSha3256Priv - | Codec::LamportSha3256PrivShare - | Codec::LamportSha3384Pub - | Codec::LamportSha3384Priv - | Codec::LamportSha3384PrivShare - | Codec::LamportSha3512Pub - | Codec::LamportSha3512Priv - | Codec::LamportSha3512PrivShare - | Codec::LamportSha2256Pub - | Codec::LamportSha2256Priv - | Codec::LamportSha2256PrivShare - | Codec::LamportSha2384Pub - | Codec::LamportSha2384Priv - | Codec::LamportSha2384PrivShare - | Codec::LamportSha2512Pub - | Codec::LamportSha2512Priv - | Codec::LamportSha2512PrivShare - | Codec::LamportBlake2B512Pub - | Codec::LamportBlake2B512Priv - | Codec::LamportBlake2B512PrivShare - | Codec::LamportBlake2S256Pub - | Codec::LamportBlake2S256Priv - | Codec::LamportBlake2S256PrivShare - | Codec::LamportBlake3256Pub - | Codec::LamportBlake3256Priv - | Codec::LamportBlake3256PrivShare - | Codec::LamportShake128Pub - | Codec::LamportShake128Priv - | Codec::LamportShake128PrivShare - | Codec::LamportShake256Pub - | Codec::LamportShake256Priv - | Codec::LamportShake256PrivShare => Ok(Box::new(lamport::View::try_from(self)?)), - #[cfg(feature = "lamport")] - Codec::LamportMerkleSha3256Pub - | Codec::LamportMerkleSha3256Priv - | Codec::LamportMerkleSha3256PrivShare - | Codec::LamportMerkleSha3384Pub - | Codec::LamportMerkleSha3384Priv - | Codec::LamportMerkleSha3384PrivShare - | Codec::LamportMerkleSha3512Pub - | Codec::LamportMerkleSha3512Priv - | Codec::LamportMerkleSha3512PrivShare - | Codec::LamportMerkleSha2256Pub - | Codec::LamportMerkleSha2256Priv - | Codec::LamportMerkleSha2256PrivShare - | Codec::LamportMerkleSha2384Pub - | Codec::LamportMerkleSha2384Priv - | Codec::LamportMerkleSha2384PrivShare - | Codec::LamportMerkleSha2512Pub - | Codec::LamportMerkleSha2512Priv - | Codec::LamportMerkleSha2512PrivShare - | Codec::LamportMerkleBlake2B512Pub - | Codec::LamportMerkleBlake2B512Priv - | Codec::LamportMerkleBlake2B512PrivShare - | Codec::LamportMerkleBlake2S256Pub - | Codec::LamportMerkleBlake2S256Priv - | Codec::LamportMerkleBlake2S256PrivShare - | Codec::LamportMerkleBlake3256Pub - | Codec::LamportMerkleBlake3256Priv - | Codec::LamportMerkleBlake3256PrivShare - | Codec::LamportMerkleShake128Pub - | Codec::LamportMerkleShake128Priv - | Codec::LamportMerkleShake128PrivShare - | Codec::LamportMerkleShake256Pub - | Codec::LamportMerkleShake256Priv - | Codec::LamportMerkleShake256PrivShare => { - Ok(Box::new(lamport_merkle::View::try_from(self)?)) - } - #[cfg(feature = "xmss")] - Codec::XmssSha210256Pub - | Codec::XmssSha210256Priv - | Codec::XmssSha216256Pub - | Codec::XmssSha216256Priv - | Codec::XmssSha220256Pub - | Codec::XmssSha220256Priv => Ok(Box::new(xmss::View::try_from(self)?)), - _ => Err(ConversionsError::UnsupportedCodec(self.codec).into()), - } + crate::views::dispatch::dispatch_fingerprint_view(self) } /// Provide an interface to do kdf operations from the viewed Multikey fn kdf_view<'a>(&'a self, kdf: &'a Multikey) -> Result, Error> { - match kdf.codec { - Codec::BcryptPbkdf => Ok(Box::new(bcrypt::View::new(self, kdf))), - _ => Err(KdfError::UnsupportedCodec(self.codec).into()), - } + crate::views::dispatch::dispatch_kdf_view(self, kdf) } /// Provide an interface to seal (encrypt) plaintext fn seal_view<'a>(&'a self) -> Result, Error> { - match self.codec { - Codec::Bls12381G1Pub - | Codec::Bls12381G1Priv - | Codec::Bls12381G2Pub - | Codec::Bls12381G2Priv => Ok(Box::new(bls12381::View::try_from(self)?)), - Codec::Mlkem768Pub - | Codec::Mlkem768Priv - | Codec::Mlkem1024Pub - | Codec::Mlkem1024Priv => Ok(Box::new(ml_kem::View::try_from(self)?)), - Codec::Sntrup761Pub - | Codec::Sntrup761Priv - | Codec::Sntrup857Pub - | Codec::Sntrup857Priv - | Codec::Sntrup953Pub - | Codec::Sntrup953Priv - | Codec::Sntrup1013Pub - | Codec::Sntrup1013Priv - | Codec::Sntrup1277Pub - | Codec::Sntrup1277Priv => Ok(Box::new(sntrup::View::try_from(self)?)), - #[cfg(feature = "deprecated")] - #[allow(deprecated)] - Codec::Mceliece348864Pub | Codec::Mceliece348864Priv => { - Ok(Box::new(classic_mceliece::View::try_from(self)?)) - } - Codec::FrodoKem640AesPub - | Codec::FrodoKem640AesPriv - | Codec::FrodoKem976AesPub - | Codec::FrodoKem976AesPriv - | Codec::FrodoKem1344AesPub - | Codec::FrodoKem1344AesPriv - | Codec::FrodoKem640ShakePub - | Codec::FrodoKem640ShakePriv - | Codec::FrodoKem976ShakePub - | Codec::FrodoKem976ShakePriv - | Codec::FrodoKem1344ShakePub - | Codec::FrodoKem1344ShakePriv => Ok(Box::new(frodokem::View::try_from(self)?)), - Codec::X25519Pub | Codec::X25519Priv => Ok(Box::new(x25519::View::try_from(self)?)), - Codec::X25519Sntrup761Pub | Codec::X25519Sntrup761Priv => { - Ok(Box::new(x25519_sntrup761::View::try_from(self)?)) - } - Codec::X25519Frodokem640AesPub - | Codec::X25519Frodokem640AesPriv - | Codec::X25519Frodokem640ShakePub - | Codec::X25519Frodokem640ShakePriv => { - Ok(Box::new(x25519_frodokem640::View::try_from(self)?)) - } - #[cfg(feature = "deprecated")] - #[allow(deprecated)] - Codec::X25519Mceliece348864Pub | Codec::X25519Mceliece348864Priv => { - Ok(Box::new(x25519_mceliece348864::View::try_from(self)?)) - } - Codec::X25519Mlkem768Pub | Codec::X25519Mlkem768Priv => { - Ok(Box::new(x25519_mlkem768::View::try_from(self)?)) - } - Codec::Rsa2048Pub - | Codec::Rsa2048Priv - | Codec::Rsa3072Pub - | Codec::Rsa3072Priv - | Codec::Rsa4096Pub - | Codec::Rsa4096Priv => Ok(Box::new(rsa::View::try_from(self)?)), - Codec::P256Pub - | Codec::P256Priv - | Codec::P384Pub - | Codec::P384Priv - | Codec::P521Pub - | Codec::P521Priv => Ok(Box::new(nist_p::View::try_from(self)?)), - Codec::Secp256K1Pub | Codec::Secp256K1Priv => { - Ok(Box::new(secp256k1::View::try_from(self)?)) - } - _ => Err(SealError::NotEncryptionKey.into()), - } + crate::views::dispatch::dispatch_seal_view(self) } /// Provide an interface to open (decrypt) sealed data fn open_view<'a>(&'a self) -> Result, Error> { - match self.codec { - Codec::Bls12381G1Pub - | Codec::Bls12381G1Priv - | Codec::Bls12381G2Pub - | Codec::Bls12381G2Priv => Ok(Box::new(bls12381::View::try_from(self)?)), - Codec::Mlkem768Pub - | Codec::Mlkem768Priv - | Codec::Mlkem1024Pub - | Codec::Mlkem1024Priv => Ok(Box::new(ml_kem::View::try_from(self)?)), - Codec::Sntrup761Pub - | Codec::Sntrup761Priv - | Codec::Sntrup857Pub - | Codec::Sntrup857Priv - | Codec::Sntrup953Pub - | Codec::Sntrup953Priv - | Codec::Sntrup1013Pub - | Codec::Sntrup1013Priv - | Codec::Sntrup1277Pub - | Codec::Sntrup1277Priv => Ok(Box::new(sntrup::View::try_from(self)?)), - #[cfg(feature = "deprecated")] - #[allow(deprecated)] - Codec::Mceliece348864Pub | Codec::Mceliece348864Priv => { - Ok(Box::new(classic_mceliece::View::try_from(self)?)) - } - Codec::FrodoKem640AesPub - | Codec::FrodoKem640AesPriv - | Codec::FrodoKem976AesPub - | Codec::FrodoKem976AesPriv - | Codec::FrodoKem1344AesPub - | Codec::FrodoKem1344AesPriv - | Codec::FrodoKem640ShakePub - | Codec::FrodoKem640ShakePriv - | Codec::FrodoKem976ShakePub - | Codec::FrodoKem976ShakePriv - | Codec::FrodoKem1344ShakePub - | Codec::FrodoKem1344ShakePriv => Ok(Box::new(frodokem::View::try_from(self)?)), - Codec::X25519Pub | Codec::X25519Priv => Ok(Box::new(x25519::View::try_from(self)?)), - Codec::X25519Sntrup761Pub | Codec::X25519Sntrup761Priv => { - Ok(Box::new(x25519_sntrup761::View::try_from(self)?)) - } - Codec::X25519Frodokem640AesPub - | Codec::X25519Frodokem640AesPriv - | Codec::X25519Frodokem640ShakePub - | Codec::X25519Frodokem640ShakePriv => { - Ok(Box::new(x25519_frodokem640::View::try_from(self)?)) - } - #[cfg(feature = "deprecated")] - #[allow(deprecated)] - Codec::X25519Mceliece348864Pub | Codec::X25519Mceliece348864Priv => { - Ok(Box::new(x25519_mceliece348864::View::try_from(self)?)) - } - Codec::X25519Mlkem768Pub | Codec::X25519Mlkem768Priv => { - Ok(Box::new(x25519_mlkem768::View::try_from(self)?)) - } - Codec::Rsa2048Pub - | Codec::Rsa2048Priv - | Codec::Rsa3072Pub - | Codec::Rsa3072Priv - | Codec::Rsa4096Pub - | Codec::Rsa4096Priv => Ok(Box::new(rsa::View::try_from(self)?)), - Codec::P256Pub - | Codec::P256Priv - | Codec::P384Pub - | Codec::P384Priv - | Codec::P521Pub - | Codec::P521Priv => Ok(Box::new(nist_p::View::try_from(self)?)), - Codec::Secp256K1Pub | Codec::Secp256K1Priv => { - Ok(Box::new(secp256k1::View::try_from(self)?)) - } - _ => Err(SealError::NotEncryptionKey.into()), - } + crate::views::dispatch::dispatch_open_view(self) } /// Provide an interface to sign a message and return a Multisig fn sign_view<'a>(&'a self) -> Result, Error> { - match self.codec { - Codec::Bls12381G1PrivShare - | Codec::Bls12381G1Priv - | Codec::Bls12381G1Pub - | Codec::Bls12381G1PubShare - | Codec::Bls12381G2PrivShare - | Codec::Bls12381G2Priv - | Codec::Bls12381G2Pub - | Codec::Bls12381G2PubShare => Ok(Box::new(bls12381::View::try_from(self)?)), - Codec::Ed25519Pub | Codec::Ed25519Priv => Ok(Box::new(ed25519::View::try_from(self)?)), - Codec::Secp256K1Pub | Codec::Secp256K1Priv => { - Ok(Box::new(secp256k1::View::try_from(self)?)) - } - Codec::SlhDsaSha2128FPub - | Codec::SlhDsaSha2128SPub - | Codec::SlhDsaSha2192FPub - | Codec::SlhDsaSha2192SPub - | Codec::SlhDsaSha2256FPub - | Codec::SlhDsaSha2256SPub - | Codec::SlhDsaShake128FPub - | Codec::SlhDsaShake128SPub - | Codec::SlhDsaShake192FPub - | Codec::SlhDsaShake192SPub - | Codec::SlhDsaShake256FPub - | Codec::SlhDsaShake256SPub - | Codec::SlhDsaSha2128FPriv - | Codec::SlhDsaSha2128SPriv - | Codec::SlhDsaSha2192FPriv - | Codec::SlhDsaSha2192SPriv - | Codec::SlhDsaSha2256FPriv - | Codec::SlhDsaSha2256SPriv - | Codec::SlhDsaShake128FPriv - | Codec::SlhDsaShake128SPriv - | Codec::SlhDsaShake192FPriv - | Codec::SlhDsaShake192SPriv - | Codec::SlhDsaShake256FPriv - | Codec::SlhDsaShake256SPriv => Ok(Box::new(slh_dsa::View::try_from(self)?)), - Codec::MlDsa65Pub | Codec::MlDsa65Priv | Codec::MlDsa87Pub | Codec::MlDsa87Priv => { - Ok(Box::new(ml_dsa::View::try_from(self)?)) - } - Codec::Mayo1Pub - | Codec::Mayo1Priv - | Codec::Mayo2Pub - | Codec::Mayo2Priv - | Codec::Mayo3Pub - | Codec::Mayo3Priv - | Codec::Mayo5Pub - | Codec::Mayo5Priv => Ok(Box::new(mayo::View::try_from(self)?)), - Codec::FnDsa512Pub - | Codec::FnDsa512Priv - | Codec::FnDsa1024Pub - | Codec::FnDsa1024Priv => Ok(Box::new(fn_dsa::View::try_from(self)?)), - Codec::Ed25519Mayo2Pub | Codec::Ed25519Mayo2Priv => { - Ok(Box::new(ed25519_mayo2::View::try_from(self)?)) - } - Codec::Ed25519Mldsa65Pub | Codec::Ed25519Mldsa65Priv => { - Ok(Box::new(ed25519_mldsa65::View::try_from(self)?)) - } - Codec::Bls12381G1Mldsa65Pub | Codec::Bls12381G1Mldsa65Priv => { - Ok(Box::new(bls12381_g1_mldsa65::View::try_from(self)?)) - } - Codec::Bls12381G1Fndsa512Pub | Codec::Bls12381G1Fndsa512Priv => { - Ok(Box::new(bls12381_g1_fndsa512::View::try_from(self)?)) - } - Codec::Bls12381G1Mayo1Pub | Codec::Bls12381G1Mayo1Priv => { - Ok(Box::new(bls12381_g1_mayo1::View::try_from(self)?)) - } - Codec::Bls12381G1Mayo2Pub | Codec::Bls12381G1Mayo2Priv => { - Ok(Box::new(bls12381_g1_mayo2::View::try_from(self)?)) - } - Codec::Ed25519Fndsa512Pub | Codec::Ed25519Fndsa512Priv => { - Ok(Box::new(ed25519_fndsa512::View::try_from(self)?)) - } - Codec::P256Pub - | Codec::P256Priv - | Codec::P384Pub - | Codec::P384Priv - | Codec::P521Pub - | Codec::P521Priv => Ok(Box::new(nist_p::View::try_from(self)?)), - Codec::Rsa2048Pub - | Codec::Rsa2048Priv - | Codec::Rsa3072Pub - | Codec::Rsa3072Priv - | Codec::Rsa4096Pub - | Codec::Rsa4096Priv => Ok(Box::new(rsa::View::try_from(self)?)), - #[cfg(feature = "lamport")] - Codec::LamportSha3256Pub - | Codec::LamportSha3256Priv - | Codec::LamportSha3256PrivShare - | Codec::LamportSha3384Pub - | Codec::LamportSha3384Priv - | Codec::LamportSha3384PrivShare - | Codec::LamportSha3512Pub - | Codec::LamportSha3512Priv - | Codec::LamportSha3512PrivShare - | Codec::LamportSha2256Pub - | Codec::LamportSha2256Priv - | Codec::LamportSha2256PrivShare - | Codec::LamportSha2384Pub - | Codec::LamportSha2384Priv - | Codec::LamportSha2384PrivShare - | Codec::LamportSha2512Pub - | Codec::LamportSha2512Priv - | Codec::LamportSha2512PrivShare - | Codec::LamportBlake2B512Pub - | Codec::LamportBlake2B512Priv - | Codec::LamportBlake2B512PrivShare - | Codec::LamportBlake2S256Pub - | Codec::LamportBlake2S256Priv - | Codec::LamportBlake2S256PrivShare - | Codec::LamportBlake3256Pub - | Codec::LamportBlake3256Priv - | Codec::LamportBlake3256PrivShare - | Codec::LamportShake128Pub - | Codec::LamportShake128Priv - | Codec::LamportShake128PrivShare - | Codec::LamportShake256Pub - | Codec::LamportShake256Priv - | Codec::LamportShake256PrivShare => Ok(Box::new(lamport::View::try_from(self)?)), - #[cfg(feature = "lamport")] - Codec::LamportMerkleSha3256Pub - | Codec::LamportMerkleSha3256Priv - | Codec::LamportMerkleSha3256PrivShare - | Codec::LamportMerkleSha3384Pub - | Codec::LamportMerkleSha3384Priv - | Codec::LamportMerkleSha3384PrivShare - | Codec::LamportMerkleSha3512Pub - | Codec::LamportMerkleSha3512Priv - | Codec::LamportMerkleSha3512PrivShare - | Codec::LamportMerkleSha2256Pub - | Codec::LamportMerkleSha2256Priv - | Codec::LamportMerkleSha2256PrivShare - | Codec::LamportMerkleSha2384Pub - | Codec::LamportMerkleSha2384Priv - | Codec::LamportMerkleSha2384PrivShare - | Codec::LamportMerkleSha2512Pub - | Codec::LamportMerkleSha2512Priv - | Codec::LamportMerkleSha2512PrivShare - | Codec::LamportMerkleBlake2B512Pub - | Codec::LamportMerkleBlake2B512Priv - | Codec::LamportMerkleBlake2B512PrivShare - | Codec::LamportMerkleBlake2S256Pub - | Codec::LamportMerkleBlake2S256Priv - | Codec::LamportMerkleBlake2S256PrivShare - | Codec::LamportMerkleBlake3256Pub - | Codec::LamportMerkleBlake3256Priv - | Codec::LamportMerkleBlake3256PrivShare - | Codec::LamportMerkleShake128Pub - | Codec::LamportMerkleShake128Priv - | Codec::LamportMerkleShake128PrivShare - | Codec::LamportMerkleShake256Pub - | Codec::LamportMerkleShake256Priv - | Codec::LamportMerkleShake256PrivShare => { - Ok(Box::new(lamport_merkle::View::try_from(self)?)) - } - #[cfg(feature = "xmss")] - Codec::XmssSha210256Pub - | Codec::XmssSha210256Priv - | Codec::XmssSha216256Pub - | Codec::XmssSha216256Priv - | Codec::XmssSha220256Pub - | Codec::XmssSha220256Priv => Ok(Box::new(xmss::View::try_from(self)?)), - Codec::X25519Pub | Codec::X25519Priv => Ok(Box::new(xeddsa::View::try_from(self)?)), - _ => Err(ConversionsError::UnsupportedCodec(self.codec).into()), - } + crate::views::dispatch::dispatch_sign_view(self) } /// Provide an interface to do threshold operations on the Multikey fn threshold_view<'a>(&'a self) -> Result, Error> { - match self.codec { - Codec::Bls12381G1Priv | Codec::Bls12381G2Priv => { - Ok(Box::new(bls12381::View::try_from(self)?)) - } - #[cfg(feature = "lamport")] - Codec::LamportSha3256Pub - | Codec::LamportSha3256Priv - | Codec::LamportSha3256PrivShare - | Codec::LamportSha3384Pub - | Codec::LamportSha3384Priv - | Codec::LamportSha3384PrivShare - | Codec::LamportSha3512Pub - | Codec::LamportSha3512Priv - | Codec::LamportSha3512PrivShare - | Codec::LamportSha2256Pub - | Codec::LamportSha2256Priv - | Codec::LamportSha2256PrivShare - | Codec::LamportSha2384Pub - | Codec::LamportSha2384Priv - | Codec::LamportSha2384PrivShare - | Codec::LamportSha2512Pub - | Codec::LamportSha2512Priv - | Codec::LamportSha2512PrivShare - | Codec::LamportBlake2B512Pub - | Codec::LamportBlake2B512Priv - | Codec::LamportBlake2B512PrivShare - | Codec::LamportBlake2S256Pub - | Codec::LamportBlake2S256Priv - | Codec::LamportBlake2S256PrivShare - | Codec::LamportBlake3256Pub - | Codec::LamportBlake3256Priv - | Codec::LamportBlake3256PrivShare - | Codec::LamportShake128Pub - | Codec::LamportShake128Priv - | Codec::LamportShake128PrivShare - | Codec::LamportShake256Pub - | Codec::LamportShake256Priv - | Codec::LamportShake256PrivShare => Ok(Box::new(lamport::View::try_from(self)?)), - #[cfg(feature = "lamport")] - Codec::LamportMerkleSha3256Pub - | Codec::LamportMerkleSha3256Priv - | Codec::LamportMerkleSha3256PrivShare - | Codec::LamportMerkleSha3384Pub - | Codec::LamportMerkleSha3384Priv - | Codec::LamportMerkleSha3384PrivShare - | Codec::LamportMerkleSha3512Pub - | Codec::LamportMerkleSha3512Priv - | Codec::LamportMerkleSha3512PrivShare - | Codec::LamportMerkleSha2256Pub - | Codec::LamportMerkleSha2256Priv - | Codec::LamportMerkleSha2256PrivShare - | Codec::LamportMerkleSha2384Pub - | Codec::LamportMerkleSha2384Priv - | Codec::LamportMerkleSha2384PrivShare - | Codec::LamportMerkleSha2512Pub - | Codec::LamportMerkleSha2512Priv - | Codec::LamportMerkleSha2512PrivShare - | Codec::LamportMerkleBlake2B512Pub - | Codec::LamportMerkleBlake2B512Priv - | Codec::LamportMerkleBlake2B512PrivShare - | Codec::LamportMerkleBlake2S256Pub - | Codec::LamportMerkleBlake2S256Priv - | Codec::LamportMerkleBlake2S256PrivShare - | Codec::LamportMerkleBlake3256Pub - | Codec::LamportMerkleBlake3256Priv - | Codec::LamportMerkleBlake3256PrivShare - | Codec::LamportMerkleShake128Pub - | Codec::LamportMerkleShake128Priv - | Codec::LamportMerkleShake128PrivShare - | Codec::LamportMerkleShake256Pub - | Codec::LamportMerkleShake256Priv - | Codec::LamportMerkleShake256PrivShare => { - Ok(Box::new(lamport_merkle::View::try_from(self)?)) - } - _ => Err(ConversionsError::UnsupportedCodec(self.codec).into()), - } + crate::views::dispatch::dispatch_threshold_view(self) } /// Provide an interface to verify a Multisig and optional message fn verify_view<'a>(&'a self) -> Result, Error> { - match self.codec { - Codec::Bls12381G1PrivShare - | Codec::Bls12381G1Priv - | Codec::Bls12381G1Pub - | Codec::Bls12381G1PubShare - | Codec::Bls12381G2PrivShare - | Codec::Bls12381G2Priv - | Codec::Bls12381G2Pub - | Codec::Bls12381G2PubShare => Ok(Box::new(bls12381::View::try_from(self)?)), - Codec::Ed25519Pub | Codec::Ed25519Priv => Ok(Box::new(ed25519::View::try_from(self)?)), - Codec::Secp256K1Pub | Codec::Secp256K1Priv => { - Ok(Box::new(secp256k1::View::try_from(self)?)) - } - Codec::SlhDsaSha2128FPub - | Codec::SlhDsaSha2128SPub - | Codec::SlhDsaSha2192FPub - | Codec::SlhDsaSha2192SPub - | Codec::SlhDsaSha2256FPub - | Codec::SlhDsaSha2256SPub - | Codec::SlhDsaShake128FPub - | Codec::SlhDsaShake128SPub - | Codec::SlhDsaShake192FPub - | Codec::SlhDsaShake192SPub - | Codec::SlhDsaShake256FPub - | Codec::SlhDsaShake256SPub - | Codec::SlhDsaSha2128FPriv - | Codec::SlhDsaSha2128SPriv - | Codec::SlhDsaSha2192FPriv - | Codec::SlhDsaSha2192SPriv - | Codec::SlhDsaSha2256FPriv - | Codec::SlhDsaSha2256SPriv - | Codec::SlhDsaShake128FPriv - | Codec::SlhDsaShake128SPriv - | Codec::SlhDsaShake192FPriv - | Codec::SlhDsaShake192SPriv - | Codec::SlhDsaShake256FPriv - | Codec::SlhDsaShake256SPriv => Ok(Box::new(slh_dsa::View::try_from(self)?)), - Codec::MlDsa65Pub | Codec::MlDsa65Priv | Codec::MlDsa87Pub | Codec::MlDsa87Priv => { - Ok(Box::new(ml_dsa::View::try_from(self)?)) - } - Codec::Mayo1Pub - | Codec::Mayo1Priv - | Codec::Mayo2Pub - | Codec::Mayo2Priv - | Codec::Mayo3Pub - | Codec::Mayo3Priv - | Codec::Mayo5Pub - | Codec::Mayo5Priv => Ok(Box::new(mayo::View::try_from(self)?)), - Codec::FnDsa512Pub - | Codec::FnDsa512Priv - | Codec::FnDsa1024Pub - | Codec::FnDsa1024Priv => Ok(Box::new(fn_dsa::View::try_from(self)?)), - Codec::Ed25519Mayo2Pub | Codec::Ed25519Mayo2Priv => { - Ok(Box::new(ed25519_mayo2::View::try_from(self)?)) - } - Codec::Ed25519Mldsa65Pub | Codec::Ed25519Mldsa65Priv => { - Ok(Box::new(ed25519_mldsa65::View::try_from(self)?)) - } - Codec::Bls12381G1Mldsa65Pub | Codec::Bls12381G1Mldsa65Priv => { - Ok(Box::new(bls12381_g1_mldsa65::View::try_from(self)?)) - } - Codec::Bls12381G1Fndsa512Pub | Codec::Bls12381G1Fndsa512Priv => { - Ok(Box::new(bls12381_g1_fndsa512::View::try_from(self)?)) - } - Codec::Bls12381G1Mayo1Pub | Codec::Bls12381G1Mayo1Priv => { - Ok(Box::new(bls12381_g1_mayo1::View::try_from(self)?)) - } - Codec::Bls12381G1Mayo2Pub | Codec::Bls12381G1Mayo2Priv => { - Ok(Box::new(bls12381_g1_mayo2::View::try_from(self)?)) - } - Codec::Ed25519Fndsa512Pub | Codec::Ed25519Fndsa512Priv => { - Ok(Box::new(ed25519_fndsa512::View::try_from(self)?)) - } - Codec::P256Pub - | Codec::P256Priv - | Codec::P384Pub - | Codec::P384Priv - | Codec::P521Pub - | Codec::P521Priv => Ok(Box::new(nist_p::View::try_from(self)?)), - Codec::Rsa2048Pub - | Codec::Rsa2048Priv - | Codec::Rsa3072Pub - | Codec::Rsa3072Priv - | Codec::Rsa4096Pub - | Codec::Rsa4096Priv => Ok(Box::new(rsa::View::try_from(self)?)), - #[cfg(feature = "lamport")] - Codec::LamportSha3256Pub - | Codec::LamportSha3256Priv - | Codec::LamportSha3256PrivShare - | Codec::LamportSha3384Pub - | Codec::LamportSha3384Priv - | Codec::LamportSha3384PrivShare - | Codec::LamportSha3512Pub - | Codec::LamportSha3512Priv - | Codec::LamportSha3512PrivShare - | Codec::LamportSha2256Pub - | Codec::LamportSha2256Priv - | Codec::LamportSha2256PrivShare - | Codec::LamportSha2384Pub - | Codec::LamportSha2384Priv - | Codec::LamportSha2384PrivShare - | Codec::LamportSha2512Pub - | Codec::LamportSha2512Priv - | Codec::LamportSha2512PrivShare - | Codec::LamportBlake2B512Pub - | Codec::LamportBlake2B512Priv - | Codec::LamportBlake2B512PrivShare - | Codec::LamportBlake2S256Pub - | Codec::LamportBlake2S256Priv - | Codec::LamportBlake2S256PrivShare - | Codec::LamportBlake3256Pub - | Codec::LamportBlake3256Priv - | Codec::LamportBlake3256PrivShare - | Codec::LamportShake128Pub - | Codec::LamportShake128Priv - | Codec::LamportShake128PrivShare - | Codec::LamportShake256Pub - | Codec::LamportShake256Priv - | Codec::LamportShake256PrivShare => Ok(Box::new(lamport::View::try_from(self)?)), - #[cfg(feature = "lamport")] - Codec::LamportMerkleSha3256Pub - | Codec::LamportMerkleSha3256Priv - | Codec::LamportMerkleSha3256PrivShare - | Codec::LamportMerkleSha3384Pub - | Codec::LamportMerkleSha3384Priv - | Codec::LamportMerkleSha3384PrivShare - | Codec::LamportMerkleSha3512Pub - | Codec::LamportMerkleSha3512Priv - | Codec::LamportMerkleSha3512PrivShare - | Codec::LamportMerkleSha2256Pub - | Codec::LamportMerkleSha2256Priv - | Codec::LamportMerkleSha2256PrivShare - | Codec::LamportMerkleSha2384Pub - | Codec::LamportMerkleSha2384Priv - | Codec::LamportMerkleSha2384PrivShare - | Codec::LamportMerkleSha2512Pub - | Codec::LamportMerkleSha2512Priv - | Codec::LamportMerkleSha2512PrivShare - | Codec::LamportMerkleBlake2B512Pub - | Codec::LamportMerkleBlake2B512Priv - | Codec::LamportMerkleBlake2B512PrivShare - | Codec::LamportMerkleBlake2S256Pub - | Codec::LamportMerkleBlake2S256Priv - | Codec::LamportMerkleBlake2S256PrivShare - | Codec::LamportMerkleBlake3256Pub - | Codec::LamportMerkleBlake3256Priv - | Codec::LamportMerkleBlake3256PrivShare - | Codec::LamportMerkleShake128Pub - | Codec::LamportMerkleShake128Priv - | Codec::LamportMerkleShake128PrivShare - | Codec::LamportMerkleShake256Pub - | Codec::LamportMerkleShake256Priv - | Codec::LamportMerkleShake256PrivShare => { - Ok(Box::new(lamport_merkle::View::try_from(self)?)) - } - #[cfg(feature = "xmss")] - Codec::XmssSha210256Pub - | Codec::XmssSha210256Priv - | Codec::XmssSha216256Pub - | Codec::XmssSha216256Priv - | Codec::XmssSha220256Pub - | Codec::XmssSha220256Priv => Ok(Box::new(xmss::View::try_from(self)?)), - Codec::X25519Pub | Codec::X25519Priv => Ok(Box::new(xeddsa::View::try_from(self)?)), - _ => Err(ConversionsError::UnsupportedCodec(self.codec).into()), - } + crate::views::dispatch::dispatch_verify_view(self) } /// Provide an interface for threshold disclosure mode operations fn disclosure_view<'a>(&'a self) -> Result, Error> { - Ok(Box::new(threshold_meta::DisclosureView::new(self))) + Ok(crate::views::dispatch::dispatch_disclosure_view(self)) } /// Provide an interface for merkle-tree state introspection fn merkle_state_view<'a>(&'a self) -> Result, Error> { - #[cfg(feature = "lamport")] - match self.codec { - Codec::LamportMerkleSha3256Pub - | Codec::LamportMerkleSha3256Priv - | Codec::LamportMerkleSha3384Pub - | Codec::LamportMerkleSha3384Priv - | Codec::LamportMerkleSha3512Pub - | Codec::LamportMerkleSha3512Priv - | Codec::LamportMerkleSha2256Pub - | Codec::LamportMerkleSha2256Priv - | Codec::LamportMerkleSha2384Pub - | Codec::LamportMerkleSha2384Priv - | Codec::LamportMerkleSha2512Pub - | Codec::LamportMerkleSha2512Priv - | Codec::LamportMerkleBlake2B512Pub - | Codec::LamportMerkleBlake2B512Priv - | Codec::LamportMerkleBlake2S256Pub - | Codec::LamportMerkleBlake2S256Priv - | Codec::LamportMerkleBlake3256Pub - | Codec::LamportMerkleBlake3256Priv - | Codec::LamportMerkleShake128Pub - | Codec::LamportMerkleShake128Priv - | Codec::LamportMerkleShake256Pub - | Codec::LamportMerkleShake256Priv => { - Ok(Box::new(lamport_merkle::View::try_from(self)?)) - } - _ => Err(ConversionsError::UnsupportedCodec(self.codec).into()), - } - #[cfg(not(feature = "lamport"))] - Err(ConversionsError::UnsupportedCodec(self.codec).into()) + crate::views::dispatch::dispatch_merkle_state_view(self) } } @@ -3372,6 +1898,33 @@ impl Builder { self.with_attribute(AttrId::KeyData, &bytes.as_ref().to_vec()) } + /// Set the algorithm name attribute of a custom protocol key. + /// + /// Stores `name` as UTF-8 bytes under [`AttrId::AlgorithmName`] (code 27). + /// A sigil-0 custom protocol key (the [`multi_codec::Codec::Identity`] + /// codec, carrying `AlgorithmName` and `KeyType` per + /// provenance-specifications#4) has no built-in views, so the + /// [`crate::views::builder::ViewBuilder`] local-codec factories identify + /// the key's protocol through this attribute. + /// + /// The name is stored as given and the empty string is accepted: + /// validation of custom-key semantics is future spec work. + pub fn with_algorithm_name(self, name: &str) -> Self { + self.with_attribute(AttrId::AlgorithmName, &name.as_bytes().to_vec()) + } + + /// Set the key type attribute of a custom protocol key. + /// + /// Stores `key_type` as one raw byte under [`AttrId::KeyType`] (code 28). + /// The byte convention from provenance-specifications#4: absent or 0 + /// means public, 1 means secret. + /// + /// The byte is stored unvalidated: validation of custom-key semantics is + /// future spec work. + pub fn with_key_type(self, key_type: u8) -> Self { + self.with_attribute(AttrId::KeyType, &vec![key_type]) + } + /// add in the merkle-tree depth value (one raw byte, 1..=3) pub fn with_depth(self, depth: u8) -> Self { self.with_attribute(AttrId::Depth, &vec![depth]) @@ -3492,7 +2045,7 @@ impl Builder { if let Some(shares) = self.shares { for share in &shares { mk = { - let tv = mk.threshold_view()?; + let tv = crate::views::dispatch::dispatch_threshold_view(&mk)?; tv.add_share(share)? }; } @@ -3506,7 +2059,8 @@ impl Builder { #[cfg(test)] mod tests { use super::*; - use crate::{cipher, kdf}; + use crate::views::{bcrypt, chacha20}; + use crate::{ViewBuilder, cipher, kdf}; use multi_sig::EncodedMultisig; use ssh_key::private::Ed25519Keypair; @@ -3520,7 +2074,7 @@ mod tests { .try_build() .unwrap(); let (vpk, epk) = { - let conv = mk.conv_view().unwrap(); + let conv = ViewBuilder::new(&mk).conv().build().unwrap(); let pk = conv.to_public_key().unwrap(); (Into::>::into(pk.clone()), EncodedMultikey::from(pk)) }; @@ -3565,7 +2119,7 @@ mod tests { .with_comment("test key") .try_build() .unwrap(); - let conv = mk.conv_view().unwrap(); + let conv = ViewBuilder::new(&mk).conv().build().unwrap(); let pk = conv.to_public_key().unwrap(); let ssh_key = conv.to_ssh_public_key().unwrap(); let mk2 = Builder::new_from_ssh_public_key(&ssh_key) @@ -3585,7 +2139,7 @@ mod tests { .with_comment("test key") .try_build() .unwrap(); - let conv = mk.conv_view().unwrap(); + let conv = ViewBuilder::new(&mk).conv().build().unwrap(); let ssh_key = conv.to_ssh_private_key().unwrap(); let mk2 = Builder::new_from_ssh_private_key(&ssh_key) .unwrap() @@ -3604,12 +2158,12 @@ mod tests { .with_comment("test key") .try_build() .unwrap(); - let cv = sk1.conv_view().unwrap(); + let cv = ViewBuilder::new(&sk1).conv().build().unwrap(); let public_key = cv.to_ssh_public_key().unwrap(); let private_key = cv.to_ssh_private_key().unwrap(); let pk1 = cv.to_public_key().unwrap(); - let cv = pk1.conv_view().unwrap(); + let cv = ViewBuilder::new(&pk1).conv().build().unwrap(); assert_eq!(public_key, cv.to_ssh_public_key().unwrap()); let sk2 = Builder::new_from_ssh_private_key(&private_key) @@ -3635,11 +2189,11 @@ mod tests { .try_build() .unwrap(); - let attr = mk1.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk1).attr().build().unwrap(); assert!(!attr.is_encrypted()); assert!(!attr.is_public_key()); assert!(attr.is_secret_key()); - let kd = mk1.data_view().unwrap(); + let kd = ViewBuilder::new(&mk1).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); @@ -3655,7 +2209,7 @@ mod tests { .unwrap(); // get the kdf view on the cipher multikey so we can generate a // new cipher multikey with the same parameters and the generated key - let kdf = ciphermk.kdf_view(&kdfmk).unwrap(); + let kdf = ViewBuilder::new(&ciphermk).kdf(&kdfmk).build().unwrap(); // derive a key from the passphrase and add it to the cipher multikey let ciphermk = kdf .derive_key(b"for great justice, move every zig!") @@ -3663,17 +2217,17 @@ mod tests { // get the cipher view on the unencrypted ed25519 secret key so // that we can create a new ed25519 secret key with an encrypted // key and the kdf and cipher attributes and data - let cipher = mk1.cipher_view(&ciphermk).unwrap(); + let cipher = ViewBuilder::new(&mk1).cipher(&ciphermk).build().unwrap(); // encrypt the multikey using the cipher cipher.encrypt().unwrap() }; - let attr = mk2.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk2).attr().build().unwrap(); assert!(attr.is_encrypted()); assert!(!attr.is_public_key()); assert!(attr.is_secret_key()); - let kd = mk2.data_view().unwrap(); + let kd = ViewBuilder::new(&mk2).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_err()); // encrypted key @@ -3689,23 +2243,23 @@ mod tests { .try_build() .unwrap(); // get the kdf view - let kdf = ciphermk.kdf_view(&kdfmk).unwrap(); + let kdf = ViewBuilder::new(&ciphermk).kdf(&kdfmk).build().unwrap(); // derive a key from the passphrase and add it to the cipher multikey let ciphermk = kdf .derive_key(b"for great justice, move every zig!") .unwrap(); // get the cipher view - let cipher = mk2.cipher_view(&ciphermk).unwrap(); + let cipher = ViewBuilder::new(&mk2).cipher(&ciphermk).build().unwrap(); // decrypt the multikey using the cipher cipher.decrypt().unwrap() }; - let attr = mk3.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk3).attr().build().unwrap(); assert!(!attr.is_encrypted()); assert!(!attr.is_public_key()); assert!(attr.is_secret_key()); - let kd = mk3.data_view().unwrap(); + let kd = ViewBuilder::new(&mk3).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); @@ -3724,20 +2278,20 @@ mod tests { .try_build() .unwrap(); - let attr = mk.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk).attr().build().unwrap(); assert!(!attr.is_encrypted()); assert!(!attr.is_public_key()); assert!(attr.is_secret_key()); - let kd = mk.data_view().unwrap(); + let kd = ViewBuilder::new(&mk).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); - let conv = mk.conv_view().unwrap(); + let conv = ViewBuilder::new(&mk).conv().build().unwrap(); let pk = EncodedMultikey::new(Base::Base16Lower, conv.to_public_key().unwrap()); println!("{} pubkey: {}", codec, pk); let msg = b"for great justice, move every zig!"; - let signmk = mk.sign_view().unwrap(); + let signmk = ViewBuilder::new(&mk).sign().build().unwrap(); let signature = if codec == Codec::Bls12381G1Priv || codec == Codec::Bls12381G2Priv { signmk.sign(msg.as_slice(), false, Some(2_u8)).unwrap() } else { @@ -3746,7 +2300,7 @@ mod tests { let sig = EncodedMultisig::new(Base::Base16Lower, signature.clone()); println!("signaure: {}", sig); - let verifymk = mk.verify_view().unwrap(); + let verifymk = ViewBuilder::new(&mk).verify().build().unwrap(); assert!(verifymk.verify(&signature, Some(msg.as_slice())).is_ok()); } } @@ -3761,11 +2315,11 @@ mod tests { .try_build() .unwrap(); - let attr = mk.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk).attr().build().unwrap(); assert!(!attr.is_encrypted()); assert!(!attr.is_public_key()); assert!(attr.is_secret_key()); - let kd = mk.data_view().unwrap(); + let kd = ViewBuilder::new(&mk).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); @@ -3773,7 +2327,7 @@ mod tests { hex::decode("8bb78be51ac7cc98f44e38947ff8a128764ec039b89687a790dfa8444ba97682") .unwrap(); - let signmk = mk.sign_view().unwrap(); + let signmk = ViewBuilder::new(&mk).sign().build().unwrap(); let signature = if codec == Codec::Bls12381G1Priv || codec == Codec::Bls12381G2Priv { signmk.sign(&msg, true, Some(2_u8)).unwrap() } else { @@ -3783,7 +2337,7 @@ mod tests { // make sure the message is stored correctly in the signature assert_eq!(signature.message, msg); - let verifymk = mk.verify_view().unwrap(); + let verifymk = ViewBuilder::new(&mk).verify().build().unwrap(); assert!(verifymk.verify(&signature, None).is_ok()); } } @@ -3798,7 +2352,7 @@ mod tests { .try_build() .unwrap(); let (vpk, epk) = { - let conv = mk.conv_view().unwrap(); + let conv = ViewBuilder::new(&mk).conv().build().unwrap(); let pk = conv.to_public_key().unwrap(); (Into::>::into(pk.clone()), EncodedMultikey::from(pk)) }; @@ -3843,25 +2397,25 @@ mod tests { .try_build() .unwrap(); - let attr = mk.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk).attr().build().unwrap(); assert!(!attr.is_encrypted()); assert!(!attr.is_public_key()); assert!(attr.is_secret_key()); - let kd = mk.data_view().unwrap(); + let kd = ViewBuilder::new(&mk).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); - let conv = mk.conv_view().unwrap(); + let conv = ViewBuilder::new(&mk).conv().build().unwrap(); let pk = EncodedMultikey::new(Base::Base16Lower, conv.to_public_key().unwrap()); println!("{} pubkey: {}", codec, pk); let msg = b"for great justice, move every zig!"; - let signmk = mk.sign_view().unwrap(); + let signmk = ViewBuilder::new(&mk).sign().build().unwrap(); let signature = signmk.sign(msg.as_slice(), false, None).unwrap(); let sig = EncodedMultisig::new(Base::Base16Lower, signature.clone()); println!("signature: {}", sig); - let verifymk = mk.verify_view().unwrap(); + let verifymk = ViewBuilder::new(&mk).verify().build().unwrap(); assert!(verifymk.verify(&signature, Some(msg.as_slice())).is_ok()); } } @@ -3876,11 +2430,11 @@ mod tests { .try_build() .unwrap(); - let attr = mk.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk).attr().build().unwrap(); assert!(!attr.is_encrypted()); assert!(!attr.is_public_key()); assert!(attr.is_secret_key()); - let kd = mk.data_view().unwrap(); + let kd = ViewBuilder::new(&mk).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); @@ -3888,13 +2442,13 @@ mod tests { hex::decode("8bb78be51ac7cc98f44e38947ff8a128764ec039b89687a790dfa8444ba97682") .unwrap(); - let signmk = mk.sign_view().unwrap(); + let signmk = ViewBuilder::new(&mk).sign().build().unwrap(); let signature = signmk.sign(&msg, true, None).unwrap(); // make sure the message is stored correctly in the signature assert_eq!(signature.message, msg); - let verifymk = mk.verify_view().unwrap(); + let verifymk = ViewBuilder::new(&mk).verify().build().unwrap(); assert!(verifymk.verify(&signature, None).is_ok()); } } @@ -3909,7 +2463,7 @@ mod tests { .try_build() .unwrap(); let (vpk, epk) = { - let conv = mk.conv_view().unwrap(); + let conv = ViewBuilder::new(&mk).conv().build().unwrap(); let pk = conv.to_public_key().unwrap(); (Into::>::into(pk.clone()), EncodedMultikey::from(pk)) }; @@ -3960,25 +2514,25 @@ mod tests { .try_build() .unwrap(); - let attr = mk.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk).attr().build().unwrap(); assert!(!attr.is_encrypted()); assert!(!attr.is_public_key()); assert!(attr.is_secret_key()); - let kd = mk.data_view().unwrap(); + let kd = ViewBuilder::new(&mk).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); - let conv = mk.conv_view().unwrap(); + let conv = ViewBuilder::new(&mk).conv().build().unwrap(); let pk = EncodedMultikey::new(Base::Base16Lower, conv.to_public_key().unwrap()); println!("{} pubkey: {}", codec, pk); let msg = b"for great justice, move every zig!"; - let signmk = mk.sign_view().unwrap(); + let signmk = ViewBuilder::new(&mk).sign().build().unwrap(); let signature = signmk.sign(msg.as_slice(), false, None).unwrap(); let sig = EncodedMultisig::new(Base::Base16Lower, signature.clone()); println!("signature: {}", sig); - let verifymk = mk.verify_view().unwrap(); + let verifymk = ViewBuilder::new(&mk).verify().build().unwrap(); assert!(verifymk.verify(&signature, Some(msg.as_slice())).is_ok()); } }) @@ -4003,11 +2557,11 @@ mod tests { .try_build() .unwrap(); - let attr = mk.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk).attr().build().unwrap(); assert!(!attr.is_encrypted()); assert!(!attr.is_public_key()); assert!(attr.is_secret_key()); - let kd = mk.data_view().unwrap(); + let kd = ViewBuilder::new(&mk).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); @@ -4016,13 +2570,13 @@ mod tests { ) .unwrap(); - let signmk = mk.sign_view().unwrap(); + let signmk = ViewBuilder::new(&mk).sign().build().unwrap(); let signature = signmk.sign(&msg, true, None).unwrap(); // make sure the message is stored correctly in the signature assert_eq!(signature.message, msg); - let verifymk = mk.verify_view().unwrap(); + let verifymk = ViewBuilder::new(&mk).verify().build().unwrap(); assert!(verifymk.verify(&signature, None).is_ok()); } }) @@ -4042,7 +2596,7 @@ mod tests { .try_build() .unwrap(); let (vpk, epk) = { - let conv = mk.conv_view().unwrap(); + let conv = ViewBuilder::new(&mk).conv().build().unwrap(); let pk = conv.to_public_key().unwrap(); (Into::>::into(pk.clone()), EncodedMultikey::from(pk)) }; @@ -4094,25 +2648,25 @@ mod tests { .try_build() .unwrap(); - let attr = mk.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk).attr().build().unwrap(); assert!(!attr.is_encrypted()); assert!(!attr.is_public_key()); assert!(attr.is_secret_key()); - let kd = mk.data_view().unwrap(); + let kd = ViewBuilder::new(&mk).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); - let conv = mk.conv_view().unwrap(); + let conv = ViewBuilder::new(&mk).conv().build().unwrap(); let pk = EncodedMultikey::new(Base::Base16Lower, conv.to_public_key().unwrap()); println!("{} pubkey: {}", codec, pk); let msg = b"for great justice, move every zig!"; - let signmk = mk.sign_view().unwrap(); + let signmk = ViewBuilder::new(&mk).sign().build().unwrap(); let signature = signmk.sign(msg.as_slice(), false, None).unwrap(); let sig = EncodedMultisig::new(Base::Base16Lower, signature.clone()); println!("signature: {}", sig); - let verifymk = mk.verify_view().unwrap(); + let verifymk = ViewBuilder::new(&mk).verify().build().unwrap(); assert!(verifymk.verify(&signature, Some(msg.as_slice())).is_ok()); } }) @@ -4137,11 +2691,11 @@ mod tests { .try_build() .unwrap(); - let attr = mk.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk).attr().build().unwrap(); assert!(!attr.is_encrypted()); assert!(!attr.is_public_key()); assert!(attr.is_secret_key()); - let kd = mk.data_view().unwrap(); + let kd = ViewBuilder::new(&mk).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); @@ -4150,13 +2704,13 @@ mod tests { ) .unwrap(); - let signmk = mk.sign_view().unwrap(); + let signmk = ViewBuilder::new(&mk).sign().build().unwrap(); let signature = signmk.sign(&msg, true, None).unwrap(); // make sure the message is stored correctly in the signature assert_eq!(signature.message, msg); - let verifymk = mk.verify_view().unwrap(); + let verifymk = ViewBuilder::new(&mk).verify().build().unwrap(); assert!(verifymk.verify(&signature, None).is_ok()); } }) @@ -4175,7 +2729,7 @@ mod tests { .unwrap(); assert_eq!("test key".to_string(), mk1.comment); - let tv = mk1.threshold_view().unwrap(); + let tv = ViewBuilder::new(&mk1).threshold().build().unwrap(); let shares = tv.split(3, 4).unwrap(); assert_eq!(4, shares.len()); for share in &shares { @@ -4185,7 +2739,7 @@ mod tests { let msg = hex::decode("8bb78be51ac7cc98f44e38947ff8a128764ec039b89687a790dfa8444ba97682") .unwrap(); - let signmk = shares[0].sign_view().unwrap(); + let signmk = ViewBuilder::new(&shares[0]).sign().build().unwrap(); let signature = signmk.sign(msg.as_slice(), false, Some(2_u8)).unwrap(); let ms: EncodedMultisig = BaseEncoded::new(Base::Base32Z, signature); let s = ms.to_string(); @@ -4198,11 +2752,11 @@ mod tests { let mk2 = builder.try_build().unwrap(); assert_eq!("test key".to_string(), mk2.comment); - let av = mk2.threshold_attr_view().unwrap(); + let av = ViewBuilder::new(&mk2).threshold_attr().build().unwrap(); assert_eq!(3, av.threshold().unwrap()); assert_eq!(4, av.limit().unwrap()); - let tv = mk2.threshold_view().unwrap(); + let tv = ViewBuilder::new(&mk2).threshold().build().unwrap(); let mk3 = tv.combine().unwrap(); assert_eq!("test key".to_string(), mk3.comment); @@ -4217,16 +2771,16 @@ mod tests { .with_comment("test key") .try_build() .unwrap(); - let tv = mk.threshold_view().unwrap(); + let tv = ViewBuilder::new(&mk).threshold().build().unwrap(); let sk1 = { tv.split(3, 4).unwrap()[0].clone() }; assert_eq!(Codec::Bls12381G1PrivShare, sk1.codec); - let cv = sk1.conv_view().unwrap(); + let cv = ViewBuilder::new(&sk1).conv().build().unwrap(); let public_key = cv.to_ssh_public_key().unwrap(); let private_key = cv.to_ssh_private_key().unwrap(); let pk1 = cv.to_public_key().unwrap(); - let cv = pk1.conv_view().unwrap(); + let cv = ViewBuilder::new(&pk1).conv().build().unwrap(); assert_eq!(public_key, cv.to_ssh_public_key().unwrap()); let sk2 = Builder::new_from_ssh_private_key(&private_key) @@ -4253,13 +2807,13 @@ mod tests { .try_build() .unwrap(); - let attr = mk.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk).attr().build().unwrap(); assert_eq!(mk.codec, Codec::Ed25519Pub); assert_eq!(mk.comment, "test key".to_string()); assert!(!attr.is_encrypted()); assert!(attr.is_public_key()); assert!(!attr.is_secret_key()); - let kd = mk.data_view().unwrap(); + let kd = ViewBuilder::new(&mk).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_err()); // public key } @@ -4275,13 +2829,13 @@ mod tests { .try_build() .unwrap(); - let attr = mk.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk).attr().build().unwrap(); assert_eq!(mk.codec(), Codec::Ed25519Priv); assert_eq!(mk.comment, "test key".to_string()); assert!(!attr.is_encrypted()); assert!(!attr.is_public_key()); assert!(attr.is_secret_key()); - let kd = mk.data_view().unwrap(); + let kd = ViewBuilder::new(&mk).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); } @@ -4290,14 +2844,14 @@ mod tests { fn test_pub_from_string() { let s = "fba24ed010874657374206b6579010120f9ddcd5118319cc69e6985ef3f4ee3b6c591d46255e1ae5569c8662111b7d3c2".to_string(); let mk = EncodedMultikey::try_from(s.as_str()).unwrap(); - let attr = mk.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk).attr().build().unwrap(); assert_eq!(mk.codec(), Codec::Ed25519Pub); assert_eq!(mk.encoding(), Base::Base16Lower); assert_eq!(mk.comment, "test key".to_string()); assert!(!attr.is_encrypted()); assert!(attr.is_public_key()); assert!(!attr.is_secret_key()); - let kd = mk.data_view().unwrap(); + let kd = ViewBuilder::new(&mk).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_err()); // public key } @@ -4306,14 +2860,14 @@ mod tests { fn test_priv_from_string() { let s = "fba2480260874657374206b657901012064e58adf88f85cbec6a0448a0803f9d28cf9231a7141be413f83cf6aa883cd04".to_string(); let mk = EncodedMultikey::try_from(s.as_str()).unwrap(); - let attr = mk.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk).attr().build().unwrap(); assert_eq!(mk.codec(), Codec::Ed25519Priv); assert_eq!(mk.encoding(), Base::Base16Lower); assert_eq!(mk.comment, "test key".to_string()); assert!(!attr.is_encrypted()); assert!(!attr.is_public_key()); assert!(attr.is_secret_key()); - let kd = mk.data_view().unwrap(); + let kd = ViewBuilder::new(&mk).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); } @@ -4322,13 +2876,13 @@ mod tests { fn test_pub_from_vec() { let b = hex::decode("ba24ed010874657374206b6579010120f9ddcd5118319cc69e6985ef3f4ee3b6c591d46255e1ae5569c8662111b7d3c2").unwrap(); let mk = Multikey::try_from(b.as_slice()).unwrap(); - let attr = mk.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk).attr().build().unwrap(); assert_eq!(mk.codec(), Codec::Ed25519Pub); assert_eq!(mk.comment, "test key".to_string()); assert!(!attr.is_encrypted()); assert!(attr.is_public_key()); assert!(!attr.is_secret_key()); - let kd = mk.data_view().unwrap(); + let kd = ViewBuilder::new(&mk).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_err()); // public key } @@ -4337,13 +2891,13 @@ mod tests { fn test_priv_from_vec() { let b = hex::decode("ba2480260874657374206b657901012064e58adf88f85cbec6a0448a0803f9d28cf9231a7141be413f83cf6aa883cd04").unwrap(); let mk = Multikey::try_from(b.as_slice()).unwrap(); - let attr = mk.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk).attr().build().unwrap(); assert_eq!(mk.codec(), Codec::Ed25519Priv); assert_eq!(mk.comment, "test key".to_string()); assert!(!attr.is_encrypted()); assert!(!attr.is_public_key()); assert!(attr.is_secret_key()); - let kd = mk.data_view().unwrap(); + let kd = ViewBuilder::new(&mk).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); } @@ -4366,13 +2920,13 @@ mod tests { .with_comment("test key") .try_build() .unwrap(); - let attr = mk.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk).attr().build().unwrap(); assert_eq!(mk.codec(), Codec::Ed25519Priv); assert_eq!(mk.comment, "test key".to_string()); assert!(!attr.is_encrypted()); assert!(!attr.is_public_key()); assert!(attr.is_secret_key()); - let kd = mk.data_view().unwrap(); + let kd = ViewBuilder::new(&mk).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); } diff --git a/src/nonce.rs b/src/nonce.rs index 37a3db1..655d402 100644 --- a/src/nonce.rs +++ b/src/nonce.rs @@ -168,7 +168,7 @@ impl Builder { #[cfg(test)] mod tests { use super::*; - use crate::{Views, mk}; + use crate::{ViewBuilder, mk}; #[test] fn test_random() { @@ -215,7 +215,7 @@ mod tests { let msg = hex::decode("8bb78be51ac7cc98f44e38947ff8a128764ec039b89687a790dfa8444ba97682") .unwrap(); - let signmk = mk.sign_view().unwrap(); + let signmk = ViewBuilder::new(&mk).sign().build().unwrap(); let signature = signmk.sign(msg.as_slice(), false, None).unwrap(); let s: Vec = signature.into(); diff --git a/src/serde/mod.rs b/src/serde/mod.rs index b9857c9..2cee2a2 100644 --- a/src/serde/mod.rs +++ b/src/serde/mod.rs @@ -5,7 +5,7 @@ mod ser; #[cfg(test)] mod tests { - use crate::{Builder, EncodedMultikey, Multikey, Views, cipher, kdf, nonce}; + use crate::{Builder, EncodedMultikey, Multikey, ViewBuilder, cipher, kdf, nonce}; use multi_base::Base; use multi_codec::Codec; use multi_hash::EncodedMultihash; @@ -37,15 +37,15 @@ mod tests { .try_build() .unwrap(); let skh = { - let fv = sk.fingerprint_view().unwrap(); + let fv = ViewBuilder::new(&sk).fingerprint().build().unwrap(); EncodedMultihash::new(Base::Base58Btc, fv.fingerprint(Codec::Blake2S256).unwrap()) }; let pk = { - let cv = sk.conv_view().unwrap(); + let cv = ViewBuilder::new(&sk).conv().build().unwrap(); cv.to_public_key().unwrap() }; let pkh = { - let fv = sk.fingerprint_view().unwrap(); + let fv = ViewBuilder::new(&sk).fingerprint().build().unwrap(); EncodedMultihash::new(Base::Base58Btc, fv.fingerprint(Codec::Blake2S256).unwrap()) }; @@ -73,7 +73,7 @@ mod tests { // try to get the associated public key let mk = { - let conv = sk.conv_view().unwrap(); + let conv = ViewBuilder::new(&sk).conv().build().unwrap(); conv.to_public_key().unwrap() }; @@ -129,7 +129,7 @@ mod tests { .unwrap(); let mk = { - let conv = sk.conv_view().unwrap(); + let conv = ViewBuilder::new(&sk).conv().build().unwrap(); conv.to_public_key().unwrap() }; @@ -167,11 +167,11 @@ mod tests { .try_build() .unwrap(); - let attr = mk1.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk1).attr().build().unwrap(); assert!(!attr.is_encrypted()); assert!(!attr.is_public_key()); assert!(attr.is_secret_key()); - let kd = mk1.data_view().unwrap(); + let kd = ViewBuilder::new(&mk1).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); @@ -192,13 +192,13 @@ mod tests { .unwrap(); // get the kdf view - let kdf = ciphermk.kdf_view(&kdfmk).unwrap(); + let kdf = ViewBuilder::new(&ciphermk).kdf(&kdfmk).build().unwrap(); // derive a key from the passphrase and add it to the cipher multikey let ciphermk = kdf .derive_key(b"for great justice, move every zig!") .unwrap(); // get the cipher view - let cipher = mk1.cipher_view(&ciphermk).unwrap(); + let cipher = ViewBuilder::new(&mk1).cipher(&ciphermk).build().unwrap(); // encrypt the multikey using the cipher cipher.encrypt().unwrap() }; @@ -216,7 +216,13 @@ mod tests { // assert exact ciphertext bytes: ChaCha20Poly1305 appends a 16-byte // Poly1305 tag, so the encrypted form differs from the legacy bare-stream // output (see test_chacha20_aead_roundtrip for the crypto itself). - assert!(mk2.attr_view().unwrap().is_encrypted()); + assert!( + ViewBuilder::new(&mk2) + .attr() + .build() + .unwrap() + .is_encrypted() + ); let json = serde_json::to_string(&mk2).unwrap(); let mk3: Multikey = serde_json::from_str(&json).unwrap(); assert_eq!(mk2, mk3); @@ -232,11 +238,11 @@ mod tests { .try_build() .unwrap(); - let attr = mk1.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk1).attr().build().unwrap(); assert!(!attr.is_encrypted()); assert!(!attr.is_public_key()); assert!(attr.is_secret_key()); - let kd = mk1.data_view().unwrap(); + let kd = ViewBuilder::new(&mk1).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); @@ -256,20 +262,26 @@ mod tests { .unwrap(); // get the kdf view - let kdf = ciphermk.kdf_view(&kdfmk).unwrap(); + let kdf = ViewBuilder::new(&ciphermk).kdf(&kdfmk).build().unwrap(); // derive a key from the passphrase and add it to the cipher multikey let ciphermk = kdf .derive_key(b"for great justice, move every zig!") .unwrap(); // get the cipher view - let cipher = mk1.cipher_view(&ciphermk).unwrap(); + let cipher = ViewBuilder::new(&mk1).cipher(&ciphermk).build().unwrap(); // encrypt the multikey using the cipher cipher.encrypt().unwrap() }; // No exact-ciphertext assertion (ChaCha20Poly1305 tag); assert the // encrypted key round-trips through serde unchanged. - assert!(mk2.attr_view().unwrap().is_encrypted()); + assert!( + ViewBuilder::new(&mk2) + .attr() + .build() + .unwrap() + .is_encrypted() + ); let json = serde_json::to_string(&mk2).unwrap(); let mk3: Multikey = serde_json::from_str(&json).unwrap(); assert_eq!(mk2, mk3); @@ -285,11 +297,11 @@ mod tests { .try_build() .unwrap(); - let attr = mk1.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk1).attr().build().unwrap(); assert!(!attr.is_encrypted()); assert!(!attr.is_public_key()); assert!(attr.is_secret_key()); - let kd = mk1.data_view().unwrap(); + let kd = ViewBuilder::new(&mk1).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); @@ -310,13 +322,13 @@ mod tests { .unwrap(); // get the kdf view - let kdf = ciphermk.kdf_view(&kdfmk).unwrap(); + let kdf = ViewBuilder::new(&ciphermk).kdf(&kdfmk).build().unwrap(); // derive a key from the passphrase and add it to the cipher multikey let ciphermk = kdf .derive_key(b"for great justice, move every zig!") .unwrap(); // get the cipher view - let cipher = mk1.cipher_view(&ciphermk).unwrap(); + let cipher = ViewBuilder::new(&mk1).cipher(&ciphermk).build().unwrap(); // encrypt the multikey using the cipher cipher.encrypt().unwrap() }; @@ -350,11 +362,11 @@ mod tests { .unwrap(); let mk1 = emk.to_inner(); - let attr = mk1.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk1).attr().build().unwrap(); assert!(!attr.is_encrypted()); assert!(!attr.is_public_key()); assert!(attr.is_secret_key()); - let kd = mk1.data_view().unwrap(); + let kd = ViewBuilder::new(&mk1).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); @@ -375,13 +387,13 @@ mod tests { .unwrap(); // get the kdf view - let kdf = ciphermk.kdf_view(&kdfmk).unwrap(); + let kdf = ViewBuilder::new(&ciphermk).kdf(&kdfmk).build().unwrap(); // derive a key from the passphrase and add it to the cipher multikey let ciphermk = kdf .derive_key(b"for great justice, move every zig!") .unwrap(); // get the cipher view - let cipher = mk1.cipher_view(&ciphermk).unwrap(); + let cipher = ViewBuilder::new(&mk1).cipher(&ciphermk).build().unwrap(); // encrypt the multikey using the cipher cipher.encrypt().unwrap() }; @@ -418,24 +430,53 @@ mod tests { .with_nonce(&nonce) .try_build() .unwrap(); - let ciphermk = ciphermk - .kdf_view(&kdfmk) + let ciphermk = ViewBuilder::new(&ciphermk) + .kdf(&kdfmk) + .build() .unwrap() .derive_key(b"for great justice, move every zig!") .unwrap(); // encrypt → must be longer by the 16-byte tag - let enc = mk1.cipher_view(&ciphermk).unwrap().encrypt().unwrap(); - assert!(enc.attr_view().unwrap().is_encrypted()); + let enc = ViewBuilder::new(&mk1) + .cipher(&ciphermk) + .build() + .unwrap() + .encrypt() + .unwrap(); + assert!( + ViewBuilder::new(&enc) + .attr() + .build() + .unwrap() + .is_encrypted() + ); assert_eq!( - enc.data_view().unwrap().key_bytes().unwrap().len(), + ViewBuilder::new(&enc) + .data() + .build() + .unwrap() + .key_bytes() + .unwrap() + .len(), plain.len() + 16 ); // decrypt → recovers the original plaintext - let dec = enc.cipher_view(&ciphermk).unwrap().decrypt().unwrap(); + let dec = ViewBuilder::new(&enc) + .cipher(&ciphermk) + .build() + .unwrap() + .decrypt() + .unwrap(); assert_eq!( - dec.data_view().unwrap().secret_bytes().unwrap().as_slice(), + ViewBuilder::new(&dec) + .data() + .build() + .unwrap() + .secret_bytes() + .unwrap() + .as_slice(), plain.as_slice() ); } @@ -452,7 +493,7 @@ mod tests { // try to get the associated public key let pk = { - let conv = sk.conv_view().unwrap(); + let conv = ViewBuilder::new(&sk).conv().build().unwrap(); conv.to_public_key().unwrap() }; diff --git a/src/views.rs b/src/views.rs index adf7553..c411cdc 100644 --- a/src/views.rs +++ b/src/views.rs @@ -1,4 +1,22 @@ // SPDX-License-Identifier: Apache-2.0 +//! View traits and their per-algorithm implementations. +//! +//! A "view" is an abstract, algorithm-specific interface over the attributes +//! and key material of a [`Multikey`](crate::Multikey). The traits here +//! cover general, cipher, kdf, and threshold attribute inspection +//! ([`AttrView`], [`CipherAttrView`], [`KdfAttrView`], [`ThresholdAttrView`], +//! [`ThresholdKeyView`]), key data and conversion ([`DataView`], +//! [`ConvView`], [`FingerprintView`]), key operation ([`CipherView`], +//! [`KdfView`], [`SealView`], [`OpenView`], [`SignView`], [`VerifyView`], +//! [`ThresholdView`]), and introspection ([`ThresholdDisclosureView`], +//! [`MerkleStateView`]). +//! +//! Construct views with +//! [`ViewBuilder`](crate::views::builder::ViewBuilder): a fluent +//! kind selector plus optional local-codec factories for custom protocol +//! keys. The deprecated [`Views`] trait delegates to the same internal +//! dispatch core the builder uses. The marker and threshold metadata +//! helpers are also public: [`threshold_marker`] and [`threshold_meta`]. use crate::{Error, Multikey}; use multi_codec::Codec; use multi_hash::Multihash; @@ -58,6 +76,13 @@ pub(crate) mod x25519_sntrup761; #[cfg(feature = "xmss")] pub(crate) mod xmss; +// shared dispatch core: every view-kind constructor dispatches on the codec here +pub(crate) mod dispatch; + +// builder-pattern view creation (`ViewBuilder`): fluent kind selection, +// optional local-codec factories, and a `.build()` terminal +pub mod builder; + // shared AEAD helper used by ml_kem, sntrup, x25519, and hybrid KEM views // (plus the deprecated classic_mceliece views) pub(crate) mod aead; @@ -299,6 +324,19 @@ pub trait MerkleStateView { } /// trait for getting the other views +/// +/// Deprecated: construct views with [`ViewBuilder`](builder::ViewBuilder) +/// instead. The builder selects a view kind fluently, dispatches through +/// the same internal dispatch core this trait delegates to, and adds +/// local-codec factories for custom protocol keys. This trait and its impl +/// for `Multikey` remain as a source-compatible delegating shim. +/// +/// Note: `multi_sig::Views` is a different, unrelated trait over +/// [`Multisig`]; this deprecation does not affect it. +#[deprecated( + since = "2.1.0", + note = "use ViewBuilder instead; this shim trait and its impl for Multikey will be removed in the next major release. multi_sig::Views is a different, unrelated trait" +)] pub trait Views { /// Provide a read-only view of the basic attributes in the viewed Multikey fn attr_view<'a>(&'a self) -> Result, Error>; diff --git a/src/views/bcrypt.rs b/src/views/bcrypt.rs index 0e21f42..2304026 100644 --- a/src/views/bcrypt.rs +++ b/src/views/bcrypt.rs @@ -1,5 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -use crate::{AttrId, Error, KdfAttrView, KdfView, Multikey, Views, error::KdfError}; +use crate::views::dispatch::{dispatch_cipher_attr_view, dispatch_kdf_attr_view}; +use crate::{AttrId, Error, KdfAttrView, KdfView, Multikey, error::KdfError}; use multi_codec::Codec; use multi_util::Varuint; use zeroize::Zeroizing; @@ -35,13 +36,13 @@ impl<'a> KdfView for View<'a> { // get the salt data and rounds attribute let (salt, rounds) = { - let kattr = kdf.kdf_attr_view()?; + let kattr = dispatch_kdf_attr_view(kdf)?; (kattr.salt_bytes()?, kattr.rounds()?) }; // get the key length from the viewed Multikey let key_length = { - let cattr = self.mk.cipher_attr_view()?; + let cattr = dispatch_cipher_attr_view(self.mk)?; cattr.key_length()? }; diff --git a/src/views/bls12381.rs b/src/views/bls12381.rs index 04d589b..0d5fb41 100644 --- a/src/views/bls12381.rs +++ b/src/views/bls12381.rs @@ -1,8 +1,12 @@ // SPDX-License-Identifier: Apache-2.0 +use crate::views::dispatch::{ + dispatch_attr_view, dispatch_conv_view, dispatch_data_view, dispatch_disclosure_view, + dispatch_fingerprint_view, dispatch_threshold_attr_view, +}; use crate::{ AttrId, AttrView, Builder, CipherAttrView, ConvView, DataView, Error, FingerprintView, KdfAttrView, Multikey, OpenView, SealView, SignView, ThresholdAttrView, ThresholdView, - VerifyView, Views, + VerifyView, error::{ AttributesError, CipherError, ConversionsError, KdfError, SealError, SignError, ThresholdError, VerifyError, @@ -349,19 +353,19 @@ impl<'a> KdfAttrView for View<'a> { impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if attr.is_secret_key() { // convert to a public key Multikey let pk = self.to_public_key()?; // get a conversions view on the public key - let fp = pk.fingerprint_view()?; + let fp = dispatch_fingerprint_view(&pk)?; // get the fingerprint let f = fp.fingerprint(codec)?; Ok(f) } else { // get the key bytes let bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.key_bytes()? }; @@ -376,7 +380,7 @@ impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { // get the secret key bytes let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -405,7 +409,7 @@ impl<'a> ConvView for View<'a> { .try_build() } Codec::Bls12381G1PrivShare => { - let av = self.mk.threshold_attr_view()?; + let av = dispatch_threshold_attr_view(self.mk)?; let threshold = av.threshold()?; let limit = av.limit()?; let identifier = bytes_to_identifier(av.identifier()?)?; @@ -450,7 +454,7 @@ impl<'a> ConvView for View<'a> { .try_build() } Codec::Bls12381G2PrivShare => { - let av = self.mk.threshold_attr_view()?; + let av = dispatch_threshold_attr_view(self.mk)?; let threshold = av.threshold()?; let limit = av.limit()?; let identifier = bytes_to_identifier(av.identifier()?)?; @@ -484,7 +488,7 @@ impl<'a> ConvView for View<'a> { } let key_bytes = { - let kd = pk.data_view()?; + let kd = dispatch_data_view(&pk)?; kd.key_bytes()? }; @@ -499,7 +503,7 @@ impl<'a> ConvView for View<'a> { ALGORITHM_NAME_G1 } Codec::Bls12381G1PubShare => { - let tav = pk.threshold_attr_view()?; + let tav = dispatch_threshold_attr_view(&pk)?; let key_share: Vec = KeyShare( bytes_to_identifier(tav.identifier()?)?, tav.threshold()?, @@ -519,7 +523,7 @@ impl<'a> ConvView for View<'a> { ALGORITHM_NAME_G2 } Codec::Bls12381G2PubShare => { - let tav = pk.threshold_attr_view()?; + let tav = dispatch_threshold_attr_view(&pk)?; let key_share: Vec = KeyShare( bytes_to_identifier(tav.identifier()?)?, tav.threshold()?, @@ -553,14 +557,14 @@ impl<'a> ConvView for View<'a> { /// try to convert a Multikey to an ssh_key::PrivateKey fn to_ssh_private_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; let pk = self.to_public_key()?; let key_bytes = { - let kd = pk.data_view()?; + let kd = dispatch_data_view(&pk)?; kd.key_bytes()? }; @@ -579,7 +583,7 @@ impl<'a> ConvView for View<'a> { ALGORITHM_NAME_G1 } Codec::Bls12381G1PrivShare => { - let sav = self.mk.threshold_attr_view()?; + let sav = dispatch_threshold_attr_view(self.mk)?; let secret_key_share: Vec = KeyShare( bytes_to_identifier(sav.identifier()?)?, sav.threshold()?, @@ -587,7 +591,7 @@ impl<'a> ConvView for View<'a> { secret_bytes.to_vec(), ) .into(); - let pav = pk.threshold_attr_view()?; + let pav = dispatch_threshold_attr_view(&pk)?; let public_key_share: Vec = KeyShare( bytes_to_identifier(pav.identifier()?)?, pav.threshold()?, @@ -613,7 +617,7 @@ impl<'a> ConvView for View<'a> { ALGORITHM_NAME_G2 } Codec::Bls12381G2PrivShare => { - let sav = self.mk.threshold_attr_view()?; + let sav = dispatch_threshold_attr_view(self.mk)?; let secret_key_share: Vec = KeyShare( bytes_to_identifier(sav.identifier()?)?, sav.threshold()?, @@ -621,7 +625,7 @@ impl<'a> ConvView for View<'a> { secret_bytes.to_vec(), ) .into(); - let pav = pk.threshold_attr_view()?; + let pav = dispatch_threshold_attr_view(&pk)?; let public_key_share: Vec = KeyShare( bytes_to_identifier(pav.identifier()?)?, pav.threshold()?, @@ -670,14 +674,14 @@ impl<'a> SignView for View<'a> { fn sign(&self, msg: &[u8], combined: bool, scheme: Option) -> Result { let scheme = scheme.ok_or(SignError::MissingScheme)?; - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if !attr.is_secret_key() { return Err(SignError::NotSigningKey.into()); } // get the secret key bytes let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -715,7 +719,7 @@ impl<'a> SignView for View<'a> { Ok(ms.try_build()?) } Codec::Bls12381G1PrivShare => { - let av = self.mk.threshold_attr_view()?; + let av = dispatch_threshold_attr_view(self.mk)?; let threshold = av.threshold()?; let limit = av.limit()?; let identifier = bytes_to_identifier(av.identifier()?)?; @@ -769,7 +773,7 @@ impl<'a> SignView for View<'a> { Ok(ms.try_build()?) } Codec::Bls12381G2PrivShare => { - let av = self.mk.threshold_attr_view()?; + let av = dispatch_threshold_attr_view(self.mk)?; let threshold = av.threshold()?; let limit = av.limit()?; let identifier = bytes_to_identifier(av.identifier()?)?; @@ -805,14 +809,14 @@ impl<'a> ThresholdView for View<'a> { return Err(ThresholdError::InvalidThresholdLimit(threshold, limit).into()); } - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if !attr.is_secret_key() { return Err(ThresholdError::NotASecretKey.into()); } // get the secret key bytes let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -920,12 +924,12 @@ impl<'a> ThresholdView for View<'a> { let (key_share, identifier, threshold, limit) = { // get the share attributes - let av = share.threshold_attr_view()?; + let av = dispatch_threshold_attr_view(share)?; let identifier = bytes_to_identifier(av.identifier()?)?; let threshold = av.threshold()?; let limit = av.limit()?; // get the key data - let dv = share.data_view()?; + let dv = dispatch_data_view(share)?; let key_bytes = dv.key_bytes()?; // return the data ( @@ -937,7 +941,7 @@ impl<'a> ThresholdView for View<'a> { }; let threshold_data: Vec = { - let av = self.mk.threshold_attr_view()?; + let av = dispatch_threshold_attr_view(self.mk)?; let mut tdata = match av.threshold_data() { Ok(b) => ThresholdData::try_from(b) .map_err(|e| ThresholdError::ShareCombineFailed(e.to_string()))?, @@ -954,7 +958,7 @@ impl<'a> ThresholdView for View<'a> { // if this multikey doesn't already have the threshold/limit set, then // set it to match the values from the first share - let av = share.threshold_attr_view()?; + let av = dispatch_threshold_attr_view(share)?; let threshold = av.threshold().unwrap_or(threshold); let limit = av.limit().unwrap_or(limit); let comment = if self.mk.comment.is_empty() { @@ -975,7 +979,7 @@ impl<'a> ThresholdView for View<'a> { fn combine(&self) -> Result { // get the current threshold data let (threshold_data, threshold) = { - let av = self.mk.threshold_attr_view()?; + let av = dispatch_threshold_attr_view(self.mk)?; ( match av.threshold_data() { Ok(b) => ThresholdData::try_from(b) @@ -1047,7 +1051,7 @@ impl<'a> ThresholdView for View<'a> { let shares = self.split(threshold, limit)?; shares .iter() - .map(|s| s.disclosure_view()?.to_disclosure(mode, meta_key, None)) + .map(|s| dispatch_disclosure_view(s).to_disclosure(mode, meta_key, None)) .collect() } @@ -1062,9 +1066,9 @@ impl<'a> ThresholdView for View<'a> { // get the share data let (key_share, identifier) = { - let av = share.threshold_attr_view()?; + let av = dispatch_threshold_attr_view(share)?; let identifier = bytes_to_identifier(av.identifier()?)?; - let dv = share.data_view()?; + let dv = dispatch_data_view(share)?; let key_bytes = dv.key_bytes()?; ( KeyShare(identifier, share_t, share_n, key_bytes.to_vec()), @@ -1074,7 +1078,7 @@ impl<'a> ThresholdView for View<'a> { // update threshold data let threshold_data: Vec = { - let av = self.mk.threshold_attr_view()?; + let av = dispatch_threshold_attr_view(self.mk)?; let mut tdata = match av.threshold_data() { Ok(b) => ThresholdData::try_from(b) .map_err(|e| ThresholdError::ShareCombineFailed(e.to_string()))?, @@ -1109,7 +1113,7 @@ impl<'a> ThresholdView for View<'a> { // get the current threshold data let threshold_data = { - let av = self.mk.threshold_attr_view()?; + let av = dispatch_threshold_attr_view(self.mk)?; match av.threshold_data() { Ok(b) => ThresholdData::try_from(b) .map_err(|e| ThresholdError::ShareCombineFailed(e.to_string()))?, @@ -1181,9 +1185,9 @@ impl<'a> VerifyView for View<'a> { /// error rather than being misverified on the wrong curve. This avoids the /// downgrade heuristic described in the security audit (M1). fn verify(&self, multisig: &Multisig, msg: Option<&[u8]>) -> Result<(), Error> { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; let pubmk = if attr.is_secret_key() { - let kc = self.mk.conv_view()?; + let kc = dispatch_conv_view(self.mk)?; kc.to_public_key()? } else { @@ -1192,7 +1196,7 @@ impl<'a> VerifyView for View<'a> { // get the secret key bytes let key_bytes = { - let kd = pubmk.data_view()?; + let kd = dispatch_data_view(&pubmk)?; let key_bytes = kd.key_bytes()?; key_bytes.to_vec() }; @@ -1261,7 +1265,7 @@ impl<'a> VerifyView for View<'a> { "Invalid public key share point".to_string(), ))?; let pk_identifier = { - let pav = pubmk.threshold_attr_view()?; + let pav = dispatch_threshold_attr_view(&pubmk)?; bytes_to_identifier(pav.identifier()?)? }; let public_key: PublicKeyShare = PublicKeyShare( @@ -1362,7 +1366,7 @@ impl<'a> VerifyView for View<'a> { "Invalid public key share point".to_string(), ))?; let pk_identifier = { - let pav = pubmk.threshold_attr_view()?; + let pav = dispatch_threshold_attr_view(&pubmk)?; bytes_to_identifier(pav.identifier()?)? }; let public_key: PublicKeyShare = PublicKeyShare( @@ -1443,12 +1447,12 @@ impl<'a> SealView for View<'a> { _aead_codec: Codec, _aad: &[u8], ) -> Result<(Vec, Option), Error> { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if !attr.is_public_key() { return Err(SealError::NotEncapsulationKey.into()); } - let key_bytes = self.mk.data_view()?.key_bytes()?; + let key_bytes = dispatch_data_view(self.mk)?.key_bytes()?; // Generate a fresh per-message identifier. The recipient proves key // ownership by signing this id; the resulting signature is the @@ -1501,12 +1505,12 @@ impl<'a> OpenView for View<'a> { _ephemeral: Option<&Multikey>, _aad: &[u8], ) -> Result>, Error> { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if !attr.is_secret_key() { return Err(SealError::NotDecapsulationKey.into()); } - let secret_bytes = self.mk.data_view()?.secret_bytes()?; + let secret_bytes = dispatch_data_view(self.mk)?.secret_bytes()?; // Parse the sealed blob: [id Varbytes][ciphertext bytes (remainder)] let (id_vb, ct_bytes) = Varbytes::try_decode_from(sealed_msg) diff --git a/src/views/bls12381_g1_fndsa512.rs b/src/views/bls12381_g1_fndsa512.rs index 20cfab2..30a3ce1 100644 --- a/src/views/bls12381_g1_fndsa512.rs +++ b/src/views/bls12381_g1_fndsa512.rs @@ -7,11 +7,11 @@ //! Public encoding is classical-first: bls_g1_pub (96) || fn_dsa_verifying_key. use super::bls12381_hybrid as bls; +use crate::views::dispatch::{dispatch_attr_view, dispatch_conv_view, dispatch_data_view}; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, SignView, VerifyView, error::{AttributesError, ConversionsError, SignError, VerifyError}, - views::Views, }; use fn_dsa::{ DOMAIN_NONE, FN_DSA_LOGN_512, HASH_ID_RAW, SigningKey as _, SigningKeyStandard, @@ -71,7 +71,7 @@ impl<'a> DataView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -120,7 +120,7 @@ impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { let pub_bytes = if self.is_secret_key() { let pk = self.to_public_key()?; - let dv = pk.data_view()?; + let dv = dispatch_data_view(&pk)?; dv.key_bytes()? } else { self.key_bytes()? @@ -131,13 +131,13 @@ impl<'a> FingerprintView for View<'a> { impl<'a> SignView for View<'a> { fn sign(&self, msg: &[u8], combined: bool, _scheme: Option) -> Result { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if !attr.is_secret_key() { return Err(SignError::NotSigningKey.into()); } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; if secret_bytes.len() != bls::BLS_G1_SECRET_LEN + sign_key_size(LOGN) { @@ -192,16 +192,16 @@ impl<'a> VerifyView for View<'a> { return Err(VerifyError::MissingMessage.into()); }; - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; let pubmk = if attr.is_secret_key() { - let kc = self.mk.conv_view()?; + let kc = dispatch_conv_view(self.mk)?; kc.to_public_key()? } else { self.mk.clone() }; let key_bytes = { - let kd = pubmk.data_view()?; + let kd = dispatch_data_view(&pubmk)?; kd.key_bytes()? }; if key_bytes.len() != bls::BLS_G1_PUB_LEN + vrfy_key_size(LOGN) { @@ -246,7 +246,7 @@ impl<'a> VerifyView for View<'a> { #[cfg(test)] mod tests { use super::*; - use crate::views::Views; + use crate::ViewBuilder; #[test] fn test_sign_verify_roundtrip() { @@ -255,14 +255,31 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let msg = b"hello BLS12-381-G1-FN-DSA-512 hybrid (Birds of Prey 1)!"; - let sig = sk.sign_view().unwrap().sign(msg, false, None).unwrap(); - pk.verify_view().unwrap().verify(&sig, Some(msg)).unwrap(); + let sig = ViewBuilder::new(&sk) + .sign() + .build() + .unwrap() + .sign(msg, false, None) + .unwrap(); + ViewBuilder::new(&pk) + .verify() + .build() + .unwrap() + .verify(&sig, Some(msg)) + .unwrap(); assert!( - pk.verify_view() + ViewBuilder::new(&pk) + .verify() + .build() .unwrap() .verify(&sig, Some(b"wrong message")) .is_err() diff --git a/src/views/bls12381_g1_mayo1.rs b/src/views/bls12381_g1_mayo1.rs index 87dea29..b1ec040 100644 --- a/src/views/bls12381_g1_mayo1.rs +++ b/src/views/bls12381_g1_mayo1.rs @@ -7,11 +7,11 @@ //! Public encoding is classical-first: bls_g1_pub (96) || mayo1_pub. use super::bls12381_hybrid as bls; +use crate::views::dispatch::{dispatch_attr_view, dispatch_conv_view, dispatch_data_view}; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, SignView, VerifyView, error::{AttributesError, ConversionsError, SignError, VerifyError}, - views::Views, }; use ml_dsa::signature::{Signer, Verifier}; use multi_codec::Codec; @@ -74,7 +74,7 @@ impl<'a> DataView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; if secret_bytes.len() != PRIV_SEED_LEN { @@ -116,7 +116,7 @@ impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { let pub_bytes = if self.is_secret_key() { let pk = self.to_public_key()?; - let dv = pk.data_view()?; + let dv = dispatch_data_view(&pk)?; dv.key_bytes()? } else { self.key_bytes()? @@ -127,13 +127,13 @@ impl<'a> FingerprintView for View<'a> { impl<'a> SignView for View<'a> { fn sign(&self, msg: &[u8], combined: bool, _scheme: Option) -> Result { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if !attr.is_secret_key() { return Err(SignError::NotSigningKey.into()); } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; if secret_bytes.len() != PRIV_SEED_LEN { @@ -180,16 +180,16 @@ impl<'a> VerifyView for View<'a> { return Err(VerifyError::MissingMessage.into()); }; - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; let pubmk = if attr.is_secret_key() { - let kc = self.mk.conv_view()?; + let kc = dispatch_conv_view(self.mk)?; kc.to_public_key()? } else { self.mk.clone() }; let key_bytes = { - let kd = pubmk.data_view()?; + let kd = dispatch_data_view(&pubmk)?; kd.key_bytes()? }; if key_bytes.len() != PUB_KEY_LEN { @@ -233,7 +233,7 @@ impl<'a> VerifyView for View<'a> { #[cfg(test)] mod tests { use super::*; - use crate::views::Views; + use crate::ViewBuilder; #[test] fn test_sign_verify_roundtrip() { @@ -242,14 +242,31 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let msg = b"hello BLS12-381-G1-MAYO-1 hybrid (Birds of Prey 1)!"; - let sig = sk.sign_view().unwrap().sign(msg, false, None).unwrap(); - pk.verify_view().unwrap().verify(&sig, Some(msg)).unwrap(); + let sig = ViewBuilder::new(&sk) + .sign() + .build() + .unwrap() + .sign(msg, false, None) + .unwrap(); + ViewBuilder::new(&pk) + .verify() + .build() + .unwrap() + .verify(&sig, Some(msg)) + .unwrap(); assert!( - pk.verify_view() + ViewBuilder::new(&pk) + .verify() + .build() .unwrap() .verify(&sig, Some(b"wrong message")) .is_err() diff --git a/src/views/bls12381_g1_mayo2.rs b/src/views/bls12381_g1_mayo2.rs index 43f9533..e1e9d11 100644 --- a/src/views/bls12381_g1_mayo2.rs +++ b/src/views/bls12381_g1_mayo2.rs @@ -7,11 +7,11 @@ //! Public encoding is classical-first: bls_g1_pub (96) || mayo2_pub. use super::bls12381_hybrid as bls; +use crate::views::dispatch::{dispatch_attr_view, dispatch_conv_view, dispatch_data_view}; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, SignView, VerifyView, error::{AttributesError, ConversionsError, SignError, VerifyError}, - views::Views, }; use ml_dsa::signature::{Signer, Verifier}; use multi_codec::Codec; @@ -74,7 +74,7 @@ impl<'a> DataView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; if secret_bytes.len() != PRIV_SEED_LEN { @@ -116,7 +116,7 @@ impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { let pub_bytes = if self.is_secret_key() { let pk = self.to_public_key()?; - let dv = pk.data_view()?; + let dv = dispatch_data_view(&pk)?; dv.key_bytes()? } else { self.key_bytes()? @@ -127,13 +127,13 @@ impl<'a> FingerprintView for View<'a> { impl<'a> SignView for View<'a> { fn sign(&self, msg: &[u8], combined: bool, _scheme: Option) -> Result { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if !attr.is_secret_key() { return Err(SignError::NotSigningKey.into()); } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; if secret_bytes.len() != PRIV_SEED_LEN { @@ -180,16 +180,16 @@ impl<'a> VerifyView for View<'a> { return Err(VerifyError::MissingMessage.into()); }; - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; let pubmk = if attr.is_secret_key() { - let kc = self.mk.conv_view()?; + let kc = dispatch_conv_view(self.mk)?; kc.to_public_key()? } else { self.mk.clone() }; let key_bytes = { - let kd = pubmk.data_view()?; + let kd = dispatch_data_view(&pubmk)?; kd.key_bytes()? }; if key_bytes.len() != PUB_KEY_LEN { @@ -233,7 +233,7 @@ impl<'a> VerifyView for View<'a> { #[cfg(test)] mod tests { use super::*; - use crate::views::Views; + use crate::ViewBuilder; #[test] fn test_sign_verify_roundtrip() { @@ -242,14 +242,31 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let msg = b"hello BLS12-381-G1-MAYO-2 hybrid (Birds of Prey 1)!"; - let sig = sk.sign_view().unwrap().sign(msg, false, None).unwrap(); - pk.verify_view().unwrap().verify(&sig, Some(msg)).unwrap(); + let sig = ViewBuilder::new(&sk) + .sign() + .build() + .unwrap() + .sign(msg, false, None) + .unwrap(); + ViewBuilder::new(&pk) + .verify() + .build() + .unwrap() + .verify(&sig, Some(msg)) + .unwrap(); assert!( - pk.verify_view() + ViewBuilder::new(&pk) + .verify() + .build() .unwrap() .verify(&sig, Some(b"wrong message")) .is_err() diff --git a/src/views/bls12381_g1_mldsa65.rs b/src/views/bls12381_g1_mldsa65.rs index d5cc0bf..3f11311 100644 --- a/src/views/bls12381_g1_mldsa65.rs +++ b/src/views/bls12381_g1_mldsa65.rs @@ -6,11 +6,11 @@ //! Public encoding is classical-first: bls_g1_pub (96) || mldsa65_pub. use super::bls12381_hybrid as bls; +use crate::views::dispatch::{dispatch_attr_view, dispatch_conv_view, dispatch_data_view}; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, SignView, VerifyView, error::{AttributesError, ConversionsError, SignError, VerifyError}, - views::Views, }; use ml_dsa::{ EncodedSignature, EncodedVerifyingKey, MlDsa65, Seed as MldsaSeed, Signature as MldsaSig, @@ -78,7 +78,7 @@ impl<'a> DataView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -127,7 +127,7 @@ impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { let pub_bytes = if self.is_secret_key() { let pk = self.to_public_key()?; - let dv = pk.data_view()?; + let dv = dispatch_data_view(&pk)?; dv.key_bytes()? } else { self.key_bytes()? @@ -138,13 +138,13 @@ impl<'a> FingerprintView for View<'a> { impl<'a> SignView for View<'a> { fn sign(&self, msg: &[u8], combined: bool, _scheme: Option) -> Result { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if !attr.is_secret_key() { return Err(SignError::NotSigningKey.into()); } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; if secret_bytes.len() != PRIV_SEED_LEN { @@ -192,16 +192,16 @@ impl<'a> VerifyView for View<'a> { return Err(VerifyError::MissingMessage.into()); }; - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; let pubmk = if attr.is_secret_key() { - let kc = self.mk.conv_view()?; + let kc = dispatch_conv_view(self.mk)?; kc.to_public_key()? } else { self.mk.clone() }; let key_bytes = { - let kd = pubmk.data_view()?; + let kd = dispatch_data_view(&pubmk)?; kd.key_bytes()? }; if key_bytes.len() != PUB_KEY_LEN { @@ -255,7 +255,7 @@ impl<'a> VerifyView for View<'a> { #[cfg(test)] mod tests { use super::*; - use crate::views::Views; + use crate::ViewBuilder; #[test] fn test_sign_verify_roundtrip() { @@ -264,15 +264,32 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let msg = b"hello BLS12-381-G1-ML-DSA-65 hybrid (Birds of Prey 1)!"; - let sig = sk.sign_view().unwrap().sign(msg, false, None).unwrap(); - pk.verify_view().unwrap().verify(&sig, Some(msg)).unwrap(); + let sig = ViewBuilder::new(&sk) + .sign() + .build() + .unwrap() + .sign(msg, false, None) + .unwrap(); + ViewBuilder::new(&pk) + .verify() + .build() + .unwrap() + .verify(&sig, Some(msg)) + .unwrap(); // wrong message must fail assert!( - pk.verify_view() + ViewBuilder::new(&pk) + .verify() + .build() .unwrap() .verify(&sig, Some(b"wrong message")) .is_err() @@ -286,9 +303,20 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); assert_eq!( - pk.data_view().unwrap().key_bytes().unwrap().len(), + ViewBuilder::new(&pk) + .data() + .build() + .unwrap() + .key_bytes() + .unwrap() + .len(), PUB_KEY_LEN ); } diff --git a/src/views/builder.rs b/src/views/builder.rs new file mode 100644 index 0000000..4256111 --- /dev/null +++ b/src/views/builder.rs @@ -0,0 +1,1945 @@ +// SPDX-License-Identifier: Apache-2.0 + +//! Builder-pattern view creation over Multikeys. +//! +//! A [`ViewBuilder`] selects one view kind, optionally registers local codec +//! factories for that kind, and builds the view. It is the builder-pattern +//! alternative to the many `*_view()` methods of the deprecated +//! [`crate::Views`] trait: it dispatches through the same dispatch core and +//! covers standard codecs and custom protocol keys alike. +//! +//! The three steps for a standard key: +//! +//! ``` +//! use multi_codec::Codec; +//! use multi_key::{Builder, ViewBuilder}; +//! +//! let mut rng = rand::rng(); +//! let mk = Builder::new_from_random_bytes(Codec::Ed25519Priv, &mut rng) +//! .unwrap() +//! .try_build() +//! .unwrap(); +//! +//! let signer = ViewBuilder::new(&mk).sign().build().unwrap(); +//! let sig = signer.sign(b"message", false, None).unwrap(); +//! let verifier = ViewBuilder::new(&mk).verify().build().unwrap(); +//! verifier.verify(&sig, Some(b"message")).unwrap(); +//! ``` +//! +//! A custom protocol key (codec [`multi_codec::Codec::Identity`] carrying the +//! `AlgorithmName` and `KeyType` attributes from +//! cryptidtech/provenance-specifications#4) has no built-in views, so it +//! dispatches to the factory registered for its kind: +//! +//! ``` +//! use multi_codec::Codec; +//! use multi_key::{AttrId, Builder, Error, Multikey, SignView, ViewBuilder}; +//! use zeroize::Zeroizing; +//! +//! // a custom protocol key: codec Identity plus AlgorithmName/KeyType +//! let mut custom = Builder::new(Codec::Identity) +//! .with_key_bytes(b"custom".as_slice()) +//! .try_build() +//! .unwrap(); +//! custom +//! .attributes +//! .insert(AttrId::AlgorithmName, Zeroizing::new(b"example-protocol".to_vec())); +//! custom +//! .attributes +//! .insert(AttrId::KeyType, Zeroizing::new(vec![1])); +//! // decode the key from wire bytes, the way a custom key usually arrives +//! let bytes: Vec = custom.clone().into(); +//! let decoded = Multikey::try_from(bytes.as_ref()).unwrap(); +//! +//! struct CustomSign { +//! algorithm_name: Vec, +//! } +//! +//! impl SignView for CustomSign { +//! fn sign( +//! &self, +//! _: &[u8], +//! _: bool, +//! _: Option, +//! ) -> Result { +//! Err(Error::UnsupportedAlgorithm("custom".into())) +//! } +//! } +//! +//! let view = ViewBuilder::new(&decoded) +//! .sign() +//! .with_local_codec(Codec::Identity, |mk| { +//! let name = mk +//! .attributes +//! .get(&AttrId::AlgorithmName) +//! .ok_or(Error::UnsupportedAlgorithm("AlgorithmName missing".into()))?; +//! Ok(Box::new(CustomSign { +//! algorithm_name: name.to_vec(), +//! })) +//! }) +//! .build() +//! .unwrap(); +//! let err = view.sign(b"message", false, None).err().unwrap(); +//! assert_eq!(err.to_string(), "Unsupported key algorithm: custom"); +//! ``` +//! +//! # Custom keys that share a codec +//! +//! Custom protocol keys of different protocols share the +//! [`multi_codec::Codec::Identity`] codec, so the codec alone cannot say +//! which protocol a key serves. The `AlgorithmName` attribute does: a +//! local-codec factory reads it from the passed-in Multikey and branches on +//! it, serving its own protocol and erroring for other protocols (or +//! branching internally between view structs). +//! `Builder::with_algorithm_name` stamps the attribute and +//! `Builder::with_key_type` stamps the key type byte convention of +//! provenance-specifications#4: absent or 0 means public, 1 means secret. +//! +//! # Differences from the issuing report +//! +//! The requesting report sketched a single callback, +//! `|mk, view_id| -> Box`. This crate has 17 distinct view traits +//! and no unified `View` trait, so an erased single callback cannot be +//! expressed type-safely. The builder realizes the same steps with a fluent +//! kind selector and a registration typed by the selected kind instead; each +//! factory receives the viewed Multikey (plus the attached second key for the +//! `cipher` and `kdf` kinds). See [`ViewKind`] for the reachable kinds. +//! +//! # Second key lifetime +//! +//! The `cipher` and `kdf` kinds store their second key by reference for the +//! builder's lifetime (`'mk`). This is slightly narrower than +//! [`crate::Views::cipher_view`]/[`crate::Views::kdf_view`], which let the +//! caller choose the overlap of the two borrows at the call site. + +use crate::{ + AttrView, CipherAttrView, CipherView, ConvView, DataView, Error, FingerprintView, KdfAttrView, + KdfView, MerkleStateView, Multikey, OpenView, SealView, SignView, ThresholdAttrView, + ThresholdDisclosureView, ThresholdKeyView, ThresholdView, VerifyView, + error::{AttributesError, CipherError, ConversionsError, KdfError, SealError}, + views::dispatch::{ + dispatch_attr_view, dispatch_cipher_attr_view, dispatch_cipher_view, dispatch_conv_view, + dispatch_data_view, dispatch_disclosure_view, dispatch_fingerprint_view, + dispatch_kdf_attr_view, dispatch_kdf_view, dispatch_merkle_state_view, dispatch_open_view, + dispatch_seal_view, dispatch_sign_view, dispatch_threshold_attr_view, + dispatch_threshold_key_view, dispatch_threshold_view, dispatch_verify_view, + }, +}; +use multi_codec::Codec; +use std::collections::BTreeMap; + +mod sealed { + /// Types outside this module cannot implement the `ViewKind` trait. + pub trait Sealed {} +} + +/// The view kinds a [`ViewBuilder`] can produce. +/// +/// One marker type implements this trait per view kind. The kind markers are +/// sealed: code outside this module cannot implement the trait. +/// +/// This trait's associated type is implementation plumbing for the builder's +/// local factory table; callers select kinds with the fluent methods and never +/// name or work with this associated type. +pub trait ViewKind: sealed::Sealed { + /// The local factory table held by a [`ViewBuilder`] for this kind. + type Factories; +} + +/// Marker for a [`ViewBuilder`] with no kind selected yet. +/// +/// The kind selectors ([`ViewBuilder::attr`], [`ViewBuilder::sign`], +/// [`ViewBuilder::cipher`], ...) transform a builder of this kind into a +/// builder of the selected kind. Only kind-selected builders can register +/// local codec factories or build. +pub struct Unselected; + +/// Marker for the basic-attributes view kind (`attr`), producing +/// [`Box`]. +pub struct AttrKind; + +/// Marker for the cipher-attributes view kind (`cipher_attr`), producing +/// [`Box`]. +pub struct CipherAttrKind; + +/// Marker for the key-data view kind (`data`), producing +/// [`Box`]. +pub struct DataKind; + +/// Marker for the kdf-attributes view kind (`kdf_attr`), producing +/// [`Box`]. +pub struct KdfAttrKind; + +/// Marker for the threshold-attributes view kind (`threshold_attr`), +/// producing [`Box`]. +pub struct ThresholdAttrKind; + +/// Marker for the threshold-key-metadata view kind (`threshold_key`), +/// producing [`Box`]. +pub struct ThresholdKeyKind; + +/// Marker for the key-conversion view kind (`conv`), producing +/// [`Box`]. +pub struct ConvKind; + +/// Marker for the fingerprint view kind (`fingerprint`), producing +/// [`Box`]. +pub struct FingerprintKind; + +/// Marker for the signing view kind (`sign`), producing [`Box`]. +pub struct SignKind; + +/// Marker for the verification view kind (`verify`), producing +/// [`Box`]. +pub struct VerifyKind; + +/// Marker for the seal (encrypt) view kind (`seal`), producing +/// [`Box`]. +pub struct SealKind; + +/// Marker for the open (decrypt) view kind (`open`), producing +/// [`Box`]. +pub struct OpenKind; + +/// Marker for the cipher view kind (`cipher`), producing +/// [`Box`]. This kind needs a cipher key attached with +/// [`ViewBuilder::cipher`]. +pub struct CipherKind; + +/// Marker for the kdf view kind (`kdf`), producing [`Box`]. This +/// kind needs a kdf key attached with [`ViewBuilder::kdf`]. +pub struct KdfKind; + +/// Marker for the threshold view kind (`threshold`), producing +/// [`Box`]. +pub struct ThresholdKind; + +/// Marker for the threshold-disclosure view kind (`disclosure`), producing +/// [`Box`]. +pub struct DisclosureKind; + +/// Marker for the merkle-tree state view kind (`merkle_state`), producing +/// [`Box`]. +pub struct MerkleStateKind; + +impl sealed::Sealed for Unselected {} +impl ViewKind for Unselected { + type Factories = (); +} +impl sealed::Sealed for AttrKind {} +impl ViewKind for AttrKind { + type Factories = AttrFactories; +} +impl sealed::Sealed for CipherAttrKind {} +impl ViewKind for CipherAttrKind { + type Factories = CipherAttrFactories; +} +impl sealed::Sealed for DataKind {} +impl ViewKind for DataKind { + type Factories = DataFactories; +} +impl sealed::Sealed for KdfAttrKind {} +impl ViewKind for KdfAttrKind { + type Factories = KdfAttrFactories; +} +impl sealed::Sealed for ThresholdAttrKind {} +impl ViewKind for ThresholdAttrKind { + type Factories = ThresholdAttrFactories; +} +impl sealed::Sealed for ThresholdKeyKind {} +impl ViewKind for ThresholdKeyKind { + type Factories = ThresholdKeyFactories; +} +impl sealed::Sealed for ConvKind {} +impl ViewKind for ConvKind { + type Factories = ConvFactories; +} +impl sealed::Sealed for FingerprintKind {} +impl ViewKind for FingerprintKind { + type Factories = FingerprintFactories; +} +impl sealed::Sealed for SignKind {} +impl ViewKind for SignKind { + type Factories = SignFactories; +} +impl sealed::Sealed for VerifyKind {} +impl ViewKind for VerifyKind { + type Factories = VerifyFactories; +} +impl sealed::Sealed for SealKind {} +impl ViewKind for SealKind { + type Factories = SealFactories; +} +impl sealed::Sealed for OpenKind {} +impl ViewKind for OpenKind { + type Factories = OpenFactories; +} +impl sealed::Sealed for CipherKind {} +impl ViewKind for CipherKind { + type Factories = CipherFactories; +} +impl sealed::Sealed for KdfKind {} +impl ViewKind for KdfKind { + type Factories = KdfFactories; +} +impl sealed::Sealed for ThresholdKind {} +impl ViewKind for ThresholdKind { + type Factories = ThresholdFactories; +} +impl sealed::Sealed for DisclosureKind {} +impl ViewKind for DisclosureKind { + type Factories = ThresholdDisclosureFactories; +} +impl sealed::Sealed for MerkleStateKind {} +impl ViewKind for MerkleStateKind { + type Factories = MerkleStateFactories; +} + +/// A local-codec factory for the attr view kind. +type AttrFactory = + Box Fn(&'a Multikey) -> Result, Error> + Send + Sync>; +/// Local codec factories registered for the attr view kind, keyed by codec. +type AttrFactories = BTreeMap; +/// A local-codec factory for the cipher-attributes view kind. +type CipherAttrFactory = + Box Fn(&'a Multikey) -> Result, Error> + Send + Sync>; +/// Local codec factories registered for the cipher-attributes view kind, keyed by codec. +type CipherAttrFactories = BTreeMap; +/// A local-codec factory for the key-data view kind. +type DataFactory = + Box Fn(&'a Multikey) -> Result, Error> + Send + Sync>; +/// Local codec factories registered for the key-data view kind, keyed by codec. +type DataFactories = BTreeMap; +/// A local-codec factory for the kdf-attributes view kind. +type KdfAttrFactory = + Box Fn(&'a Multikey) -> Result, Error> + Send + Sync>; +/// Local codec factories registered for the kdf-attributes view kind, keyed by codec. +type KdfAttrFactories = BTreeMap; +/// A local-codec factory for the threshold-attributes view kind. +type ThresholdAttrFactory = Box< + dyn for<'a> Fn(&'a Multikey) -> Result, Error> + Send + Sync, +>; +/// Local codec factories registered for the threshold-attributes view kind, keyed by codec. +type ThresholdAttrFactories = BTreeMap; +/// A local-codec factory for the threshold-key-metadata view kind. +type ThresholdKeyFactory = Box< + dyn for<'a> Fn(&'a Multikey) -> Result, Error> + Send + Sync, +>; +/// Local codec factories registered for the threshold-key-metadata view kind, keyed by codec. +type ThresholdKeyFactories = BTreeMap; +/// A local-codec factory for the key-conversion view kind. +type ConvFactory = + Box Fn(&'a Multikey) -> Result, Error> + Send + Sync>; +/// Local codec factories registered for the key-conversion view kind, keyed by codec. +type ConvFactories = BTreeMap; +/// A local-codec factory for the fingerprint view kind. +type FingerprintFactory = + Box Fn(&'a Multikey) -> Result, Error> + Send + Sync>; +/// Local codec factories registered for the fingerprint view kind, keyed by codec. +type FingerprintFactories = BTreeMap; +/// A local-codec factory for the signing view kind. +type SignFactory = + Box Fn(&'a Multikey) -> Result, Error> + Send + Sync>; +/// Local codec factories registered for the signing view kind, keyed by codec. +type SignFactories = BTreeMap; +/// A local-codec factory for the verification view kind. +type VerifyFactory = + Box Fn(&'a Multikey) -> Result, Error> + Send + Sync>; +/// Local codec factories registered for the verification view kind, keyed by codec. +type VerifyFactories = BTreeMap; +/// A local-codec factory for the seal view kind. +type SealFactory = + Box Fn(&'a Multikey) -> Result, Error> + Send + Sync>; +/// Local codec factories registered for the seal view kind, keyed by codec. +type SealFactories = BTreeMap; +/// A local-codec factory for the open view kind. +type OpenFactory = + Box Fn(&'a Multikey) -> Result, Error> + Send + Sync>; +/// Local codec factories registered for the open view kind, keyed by codec. +type OpenFactories = BTreeMap; +/// A local-codec factory for the cipher view kind. The factory receives the +/// viewed Multikey and the attached cipher key. +type CipherFactory = Box< + dyn for<'a> Fn(&'a Multikey, &'a Multikey) -> Result, Error> + + Send + + Sync, +>; +/// Local codec factories registered for the cipher view kind, keyed by codec. +type CipherFactories = BTreeMap; +/// A local-codec factory for the kdf view kind. The factory receives the +/// viewed Multikey and the attached kdf key. +type KdfFactory = Box< + dyn for<'a> Fn(&'a Multikey, &'a Multikey) -> Result, Error> + + Send + + Sync, +>; +/// Local codec factories registered for the kdf view kind, keyed by codec. +type KdfFactories = BTreeMap; +/// A local-codec factory for the threshold view kind. +type ThresholdFactory = + Box Fn(&'a Multikey) -> Result, Error> + Send + Sync>; +/// Local codec factories registered for the threshold view kind, keyed by codec. +type ThresholdFactories = BTreeMap; +/// A local-codec factory for the threshold-disclosure view kind. +type ThresholdDisclosureFactory = Box< + dyn for<'a> Fn(&'a Multikey) -> Result, Error> + + Send + + Sync, +>; +/// Local codec factories registered for the threshold-disclosure view kind, keyed by codec. +type ThresholdDisclosureFactories = BTreeMap; +/// A local-codec factory for the merkle-tree state view kind. +type MerkleStateFactory = + Box Fn(&'a Multikey) -> Result, Error> + Send + Sync>; +/// Local codec factories registered for the merkle-tree state view kind, keyed by codec. +type MerkleStateFactories = BTreeMap; + +/// Builder-pattern view creation over a Multikey. +/// +/// Create the builder with [`ViewBuilder::new`], select a view kind with one +/// of the fluent kind selectors, optionally register local codec factories +/// with [`with_local_codec`](Self::with_local_codec), then build with +/// [`build`](Self::build): +/// +/// - [`ViewBuilder::new`] + `.attr()` + `.build()` (and 14 more no-argument +/// kinds: `cipher_attr`, `data`, `kdf_attr`, `threshold_attr`, +/// `threshold_key`, `conv`, `fingerprint`, `sign`, `verify`, `seal`, +/// `open`, `threshold`, `disclosure`, `merkle_state`) +/// - `.cipher(&cipher_key)` and `.kdf(&kdf_key)` additionally attach the +/// second key their kind dispatches on. The attached key is stored by +/// reference for the builder's lifetime (`'mk`). +/// +/// # Dispatch order +/// +/// 1. The built-in views dispatch first, exactly as the `Views` trait +/// methods do: on the Multikey's codec, on the second key's codec for the +/// `cipher` and `kdf` kinds, and on the attribute-derived codec (the +/// `CipherCodec`/`KdfCodec` attribute value, falling back to the key's +/// codec) for the `cipher_attr` and `kdf_attr` kinds. +/// 2. A local factory registered for the codec that has no built-in view +/// applies on fallthrough only. The factory lookup key is the codec the +/// built-in dispatch actually failed on: the codec carried by the +/// fallthrough error, or the viewed Multikey's codec for the `seal` and +/// `open` kinds whose fallthrough error (`SealError::NotEncryptionKey`) +/// carries no codec. Built-in views always win: a factory registered for a +/// codec that the built-in dispatch does support is never called. +/// 3. Without a matching factory, the built-in fallthrough error propagates +/// unchanged (same variant, same codec value). +/// 4. Factory errors propagate unchanged. +/// +/// A repeat `with_local_codec` call for the same kind and codec replaces the +/// earlier factory. The `disclosure` kind is codec-independent (its built-in +/// view applies to every key), so a factory registered for that kind is never +/// consulted. +/// +/// # Factory contract +/// +/// Factories satisfy a higher-ranked closure shape so the built view can bind +/// to the viewed (and second) key's lifetime: +/// `for<'a> Fn(&'a Multikey) -> Result, Error>` for the +/// single-key kinds and +/// `for<'a> Fn(&'a Multikey, &'a Multikey) -> Result, Error>` +/// for the `cipher` and `kdf` kinds. Factories must additionally be +/// `Send + Sync + 'static`, so a factory cannot capture the builder's borrows; +/// it works from owned state (a clone, an `Arc`, static data). +/// +/// Note that the returned view boxes carry no `Send`/`Sync` supertrait, same +/// as the `Views` trait today. The builder itself is `Send + Sync` with and +/// without factories. +pub struct ViewBuilder<'mk, K: ViewKind = Unselected> { + /// the Multikey being viewed + mk: &'mk Multikey, + /// the cipher/kdf second key; aliases the viewed Multikey until + /// [`ViewBuilder::cipher`] or [`ViewBuilder::kdf`] attaches the real + /// second key, which only those two kinds read + second: &'mk Multikey, + /// local codec factories registered for the selected kind + local: Option, +} + +impl<'mk> ViewBuilder<'mk, Unselected> { + /// Create an unselected view builder over `mk`. + pub fn new(mk: &'mk Multikey) -> Self { + Self { + mk, + second: mk, + local: None, + } + } + + /// Select the basic-attributes view kind. + pub fn attr(self) -> ViewBuilder<'mk, AttrKind> { + ViewBuilder { + mk: self.mk, + second: self.second, + local: None, + } + } + + /// Select the cipher-attributes view kind. + pub fn cipher_attr(self) -> ViewBuilder<'mk, CipherAttrKind> { + ViewBuilder { + mk: self.mk, + second: self.second, + local: None, + } + } + + /// Select the key-data view kind. + pub fn data(self) -> ViewBuilder<'mk, DataKind> { + ViewBuilder { + mk: self.mk, + second: self.second, + local: None, + } + } + + /// Select the kdf-attributes view kind. + pub fn kdf_attr(self) -> ViewBuilder<'mk, KdfAttrKind> { + ViewBuilder { + mk: self.mk, + second: self.second, + local: None, + } + } + + /// Select the threshold-attributes view kind. + pub fn threshold_attr(self) -> ViewBuilder<'mk, ThresholdAttrKind> { + ViewBuilder { + mk: self.mk, + second: self.second, + local: None, + } + } + + /// Select the threshold-key-metadata view kind. + pub fn threshold_key(self) -> ViewBuilder<'mk, ThresholdKeyKind> { + ViewBuilder { + mk: self.mk, + second: self.second, + local: None, + } + } + + /// Select the key-conversion view kind. + pub fn conv(self) -> ViewBuilder<'mk, ConvKind> { + ViewBuilder { + mk: self.mk, + second: self.second, + local: None, + } + } + + /// Select the fingerprint view kind. + pub fn fingerprint(self) -> ViewBuilder<'mk, FingerprintKind> { + ViewBuilder { + mk: self.mk, + second: self.second, + local: None, + } + } + + /// Select the signing view kind. + pub fn sign(self) -> ViewBuilder<'mk, SignKind> { + ViewBuilder { + mk: self.mk, + second: self.second, + local: None, + } + } + + /// Select the verification view kind. + pub fn verify(self) -> ViewBuilder<'mk, VerifyKind> { + ViewBuilder { + mk: self.mk, + second: self.second, + local: None, + } + } + + /// Select the seal (encrypt) view kind. + pub fn seal(self) -> ViewBuilder<'mk, SealKind> { + ViewBuilder { + mk: self.mk, + second: self.second, + local: None, + } + } + + /// Select the open (decrypt) view kind. + pub fn open(self) -> ViewBuilder<'mk, OpenKind> { + ViewBuilder { + mk: self.mk, + second: self.second, + local: None, + } + } + + /// Select the threshold view kind. + pub fn threshold(self) -> ViewBuilder<'mk, ThresholdKind> { + ViewBuilder { + mk: self.mk, + second: self.second, + local: None, + } + } + + /// Select the threshold-disclosure view kind. + pub fn disclosure(self) -> ViewBuilder<'mk, DisclosureKind> { + ViewBuilder { + mk: self.mk, + second: self.second, + local: None, + } + } + + /// Select the merkle-tree state view kind. + pub fn merkle_state(self) -> ViewBuilder<'mk, MerkleStateKind> { + ViewBuilder { + mk: self.mk, + second: self.second, + local: None, + } + } + + /// Select the cipher view kind and attach the cipher key this kind + /// encrypts and decrypts with. The key is stored by reference for the + /// builder's lifetime. + pub fn cipher(self, cipher: &'mk Multikey) -> ViewBuilder<'mk, CipherKind> { + ViewBuilder { + mk: self.mk, + second: cipher, + local: None, + } + } + + /// Select the kdf view kind and attach the kdf parameter key this kind + /// derives with. The key is stored by reference for the builder's + /// lifetime. + pub fn kdf(self, kdf: &'mk Multikey) -> ViewBuilder<'mk, KdfKind> { + ViewBuilder { + mk: self.mk, + second: kdf, + local: None, + } + } +} + +impl<'mk> ViewBuilder<'mk, AttrKind> { + /// Set a local-codec factory for this kind, replacing any factory already + /// registered for `codec`. + /// + /// The factory applies only when the built-in attr dispatch reports + /// [`AttributesError::UnsupportedCodec`] for a codec. + pub fn with_local_codec(mut self, codec: Codec, factory: F) -> Self + where + F: for<'a> Fn(&'a Multikey) -> Result, Error> + + Send + + Sync + + 'static, + { + self.local + .get_or_insert_with(BTreeMap::new) + .insert(codec, Box::new(factory)); + self + } + + /// Build the basic-attributes view for the viewed Multikey. + pub fn build(self) -> Result, Error> { + match dispatch_attr_view(self.mk) { + Ok(view) => Ok(view), + Err(Error::Attributes(AttributesError::UnsupportedCodec(codec))) => { + match self.local.as_ref().and_then(|f| f.get(&codec)) { + Some(factory) => factory(self.mk), + None => Err(Error::Attributes(AttributesError::UnsupportedCodec(codec))), + } + } + Err(err) => Err(err), + } + } +} + +impl<'mk> ViewBuilder<'mk, CipherAttrKind> { + /// Set a local-codec factory for this kind, replacing any factory already + /// registered for `codec`. + /// + /// The factory applies only when the built-in cipher-attr dispatch reports + /// [`CipherError::UnsupportedCodec`]. The dispatch (and the factory + /// lookup) key on the codec derived from the `CipherCodec` attribute, + /// falling back to the viewed Multikey's codec. + pub fn with_local_codec(mut self, codec: Codec, factory: F) -> Self + where + F: for<'a> Fn(&'a Multikey) -> Result, Error> + + Send + + Sync + + 'static, + { + self.local + .get_or_insert_with(BTreeMap::new) + .insert(codec, Box::new(factory)); + self + } + + /// Build the cipher-attributes view for the viewed Multikey. + pub fn build(self) -> Result, Error> { + match dispatch_cipher_attr_view(self.mk) { + Ok(view) => Ok(view), + Err(Error::Cipher(CipherError::UnsupportedCodec(codec))) => { + match self.local.as_ref().and_then(|f| f.get(&codec)) { + Some(factory) => factory(self.mk), + None => Err(Error::Cipher(CipherError::UnsupportedCodec(codec))), + } + } + Err(err) => Err(err), + } + } +} + +impl<'mk> ViewBuilder<'mk, DataKind> { + /// Set a local-codec factory for this kind, replacing any factory already + /// registered for `codec`. + /// + /// The factory applies only when the built-in data dispatch reports + /// [`ConversionsError::UnsupportedCodec`] for a codec. + pub fn with_local_codec(mut self, codec: Codec, factory: F) -> Self + where + F: for<'a> Fn(&'a Multikey) -> Result, Error> + + Send + + Sync + + 'static, + { + self.local + .get_or_insert_with(BTreeMap::new) + .insert(codec, Box::new(factory)); + self + } + + /// Build the key-data view for the viewed Multikey. + pub fn build(self) -> Result, Error> { + match dispatch_data_view(self.mk) { + Ok(view) => Ok(view), + Err(Error::Conversions(ConversionsError::UnsupportedCodec(codec))) => { + match self.local.as_ref().and_then(|f| f.get(&codec)) { + Some(factory) => factory(self.mk), + None => Err(Error::Conversions(ConversionsError::UnsupportedCodec( + codec, + ))), + } + } + Err(err) => Err(err), + } + } +} + +impl<'mk> ViewBuilder<'mk, KdfAttrKind> { + /// Set a local-codec factory for this kind, replacing any factory already + /// registered for `codec`. + /// + /// The factory applies only when the built-in kdf-attr dispatch reports + /// [`KdfError::UnsupportedCodec`]. The dispatch (and the factory lookup) + /// key on the codec derived from the `KdfCodec` attribute, falling back to + /// the viewed Multikey's codec. + pub fn with_local_codec(mut self, codec: Codec, factory: F) -> Self + where + F: for<'a> Fn(&'a Multikey) -> Result, Error> + + Send + + Sync + + 'static, + { + self.local + .get_or_insert_with(BTreeMap::new) + .insert(codec, Box::new(factory)); + self + } + + /// Build the kdf-attributes view for the viewed Multikey. + pub fn build(self) -> Result, Error> { + match dispatch_kdf_attr_view(self.mk) { + Ok(view) => Ok(view), + Err(Error::Kdf(KdfError::UnsupportedCodec(codec))) => { + match self.local.as_ref().and_then(|f| f.get(&codec)) { + Some(factory) => factory(self.mk), + None => Err(Error::Kdf(KdfError::UnsupportedCodec(codec))), + } + } + Err(err) => Err(err), + } + } +} + +impl<'mk> ViewBuilder<'mk, ThresholdAttrKind> { + /// Set a local-codec factory for this kind, replacing any factory already + /// registered for `codec`. + /// + /// The factory applies only when the built-in threshold-attr dispatch + /// reports [`ConversionsError::UnsupportedCodec`] for a codec. + pub fn with_local_codec(mut self, codec: Codec, factory: F) -> Self + where + F: for<'a> Fn(&'a Multikey) -> Result, Error> + + Send + + Sync + + 'static, + { + self.local + .get_or_insert_with(BTreeMap::new) + .insert(codec, Box::new(factory)); + self + } + + /// Build the threshold-attributes view for the viewed Multikey. + pub fn build(self) -> Result, Error> { + match dispatch_threshold_attr_view(self.mk) { + Ok(view) => Ok(view), + Err(Error::Conversions(ConversionsError::UnsupportedCodec(codec))) => { + match self.local.as_ref().and_then(|f| f.get(&codec)) { + Some(factory) => factory(self.mk), + None => Err(Error::Conversions(ConversionsError::UnsupportedCodec( + codec, + ))), + } + } + Err(err) => Err(err), + } + } +} + +impl<'mk> ViewBuilder<'mk, ThresholdKeyKind> { + /// Set a local-codec factory for this kind, replacing any factory already + /// registered for `codec`. + /// + /// The factory applies only when the built-in threshold-key dispatch + /// reports [`ConversionsError::UnsupportedCodec`] for a codec. + pub fn with_local_codec(mut self, codec: Codec, factory: F) -> Self + where + F: for<'a> Fn(&'a Multikey) -> Result, Error> + + Send + + Sync + + 'static, + { + self.local + .get_or_insert_with(BTreeMap::new) + .insert(codec, Box::new(factory)); + self + } + + /// Build the threshold-key metadata view for the viewed Multikey. + pub fn build(self) -> Result, Error> { + match dispatch_threshold_key_view(self.mk) { + Ok(view) => Ok(view), + Err(Error::Conversions(ConversionsError::UnsupportedCodec(codec))) => { + match self.local.as_ref().and_then(|f| f.get(&codec)) { + Some(factory) => factory(self.mk), + None => Err(Error::Conversions(ConversionsError::UnsupportedCodec( + codec, + ))), + } + } + Err(err) => Err(err), + } + } +} + +impl<'mk> ViewBuilder<'mk, ConvKind> { + /// Set a local-codec factory for this kind, replacing any factory already + /// registered for `codec`. + /// + /// The factory applies only when the built-in conversion dispatch reports + /// [`ConversionsError::UnsupportedCodec`] for a codec. + pub fn with_local_codec(mut self, codec: Codec, factory: F) -> Self + where + F: for<'a> Fn(&'a Multikey) -> Result, Error> + + Send + + Sync + + 'static, + { + self.local + .get_or_insert_with(BTreeMap::new) + .insert(codec, Box::new(factory)); + self + } + + /// Build the key-conversion view for the viewed Multikey. + pub fn build(self) -> Result, Error> { + match dispatch_conv_view(self.mk) { + Ok(view) => Ok(view), + Err(Error::Conversions(ConversionsError::UnsupportedCodec(codec))) => { + match self.local.as_ref().and_then(|f| f.get(&codec)) { + Some(factory) => factory(self.mk), + None => Err(Error::Conversions(ConversionsError::UnsupportedCodec( + codec, + ))), + } + } + Err(err) => Err(err), + } + } +} + +impl<'mk> ViewBuilder<'mk, FingerprintKind> { + /// Set a local-codec factory for this kind, replacing any factory already + /// registered for `codec`. + /// + /// The factory applies only when the built-in fingerprint dispatch reports + /// [`ConversionsError::UnsupportedCodec`] for a codec. + pub fn with_local_codec(mut self, codec: Codec, factory: F) -> Self + where + F: for<'a> Fn(&'a Multikey) -> Result, Error> + + Send + + Sync + + 'static, + { + self.local + .get_or_insert_with(BTreeMap::new) + .insert(codec, Box::new(factory)); + self + } + + /// Build the fingerprint view for the viewed Multikey. + pub fn build(self) -> Result, Error> { + match dispatch_fingerprint_view(self.mk) { + Ok(view) => Ok(view), + Err(Error::Conversions(ConversionsError::UnsupportedCodec(codec))) => { + match self.local.as_ref().and_then(|f| f.get(&codec)) { + Some(factory) => factory(self.mk), + None => Err(Error::Conversions(ConversionsError::UnsupportedCodec( + codec, + ))), + } + } + Err(err) => Err(err), + } + } +} + +impl<'mk> ViewBuilder<'mk, SignKind> { + /// Set a local-codec factory for this kind, replacing any factory already + /// registered for `codec`. + /// + /// The factory applies only when the built-in signing dispatch reports + /// [`ConversionsError::UnsupportedCodec`] for a codec. + pub fn with_local_codec(mut self, codec: Codec, factory: F) -> Self + where + F: for<'a> Fn(&'a Multikey) -> Result, Error> + + Send + + Sync + + 'static, + { + self.local + .get_or_insert_with(BTreeMap::new) + .insert(codec, Box::new(factory)); + self + } + + /// Build the signing view for the viewed Multikey. + pub fn build(self) -> Result, Error> { + match dispatch_sign_view(self.mk) { + Ok(view) => Ok(view), + Err(Error::Conversions(ConversionsError::UnsupportedCodec(codec))) => { + match self.local.as_ref().and_then(|f| f.get(&codec)) { + Some(factory) => factory(self.mk), + None => Err(Error::Conversions(ConversionsError::UnsupportedCodec( + codec, + ))), + } + } + Err(err) => Err(err), + } + } +} + +impl<'mk> ViewBuilder<'mk, VerifyKind> { + /// Set a local-codec factory for this kind, replacing any factory already + /// registered for `codec`. + /// + /// The factory applies only when the built-in verify dispatch reports + /// [`ConversionsError::UnsupportedCodec`] for a codec. + pub fn with_local_codec(mut self, codec: Codec, factory: F) -> Self + where + F: for<'a> Fn(&'a Multikey) -> Result, Error> + + Send + + Sync + + 'static, + { + self.local + .get_or_insert_with(BTreeMap::new) + .insert(codec, Box::new(factory)); + self + } + + /// Build the verification view for the viewed Multikey. + pub fn build(self) -> Result, Error> { + match dispatch_verify_view(self.mk) { + Ok(view) => Ok(view), + Err(Error::Conversions(ConversionsError::UnsupportedCodec(codec))) => { + match self.local.as_ref().and_then(|f| f.get(&codec)) { + Some(factory) => factory(self.mk), + None => Err(Error::Conversions(ConversionsError::UnsupportedCodec( + codec, + ))), + } + } + Err(err) => Err(err), + } + } +} + +impl<'mk> ViewBuilder<'mk, SealKind> { + /// Set a local-codec factory for this kind, replacing any factory already + /// registered for `codec`. + /// + /// The factory applies only when the built-in seal dispatch reports + /// [`SealError::NotEncryptionKey`], whose fallthrough error carries no + /// codec, so the factory lookup keys on the viewed Multikey's codec. + pub fn with_local_codec(mut self, codec: Codec, factory: F) -> Self + where + F: for<'a> Fn(&'a Multikey) -> Result, Error> + + Send + + Sync + + 'static, + { + self.local + .get_or_insert_with(BTreeMap::new) + .insert(codec, Box::new(factory)); + self + } + + /// Build the seal (encrypt) view for the viewed Multikey. + pub fn build(self) -> Result, Error> { + match dispatch_seal_view(self.mk) { + Ok(view) => Ok(view), + Err(Error::Seal(SealError::NotEncryptionKey)) => { + match self.local.as_ref().and_then(|f| f.get(&self.mk.codec)) { + Some(factory) => factory(self.mk), + None => Err(Error::Seal(SealError::NotEncryptionKey)), + } + } + Err(err) => Err(err), + } + } +} + +impl<'mk> ViewBuilder<'mk, OpenKind> { + /// Set a local-codec factory for this kind, replacing any factory already + /// registered for `codec`. + /// + /// The factory applies only when the built-in open dispatch reports + /// [`SealError::NotEncryptionKey`], whose fallthrough error carries no + /// codec, so the factory lookup keys on the viewed Multikey's codec. + pub fn with_local_codec(mut self, codec: Codec, factory: F) -> Self + where + F: for<'a> Fn(&'a Multikey) -> Result, Error> + + Send + + Sync + + 'static, + { + self.local + .get_or_insert_with(BTreeMap::new) + .insert(codec, Box::new(factory)); + self + } + + /// Build the open (decrypt) view for the viewed Multikey. + pub fn build(self) -> Result, Error> { + match dispatch_open_view(self.mk) { + Ok(view) => Ok(view), + Err(Error::Seal(SealError::NotEncryptionKey)) => { + match self.local.as_ref().and_then(|f| f.get(&self.mk.codec)) { + Some(factory) => factory(self.mk), + None => Err(Error::Seal(SealError::NotEncryptionKey)), + } + } + Err(err) => Err(err), + } + } +} + +impl<'mk> ViewBuilder<'mk, CipherKind> { + /// Set a local-codec factory for this kind, replacing any factory already + /// registered for `codec`. + /// + /// The factory applies only when the built-in cipher dispatch reports + /// [`CipherError::UnsupportedCodec`] for the attached cipher key's codec. + /// The factory receives the viewed Multikey and the attached cipher key. + pub fn with_local_codec(mut self, codec: Codec, factory: F) -> Self + where + F: for<'a> Fn(&'a Multikey, &'a Multikey) -> Result, Error> + + Send + + Sync + + 'static, + { + self.local + .get_or_insert_with(BTreeMap::new) + .insert(codec, Box::new(factory)); + self + } + + /// Build the cipher view for the viewed Multikey and the attached second + /// key. + pub fn build(self) -> Result, Error> { + match dispatch_cipher_view(self.mk, self.second) { + Ok(view) => Ok(view), + Err(Error::Cipher(CipherError::UnsupportedCodec(codec))) => { + match self.local.as_ref().and_then(|f| f.get(&codec)) { + Some(factory) => factory(self.mk, self.second), + None => Err(Error::Cipher(CipherError::UnsupportedCodec(codec))), + } + } + Err(err) => Err(err), + } + } +} + +impl<'mk> ViewBuilder<'mk, KdfKind> { + /// Set a local-codec factory for this kind, replacing any factory already + /// registered for `codec`. + /// + /// The factory applies only when the built-in kdf dispatch reports + /// [`KdfError::UnsupportedCodec`] for the attached kdf key's codec. The + /// factory receives the viewed Multikey and the attached kdf key. + pub fn with_local_codec(mut self, codec: Codec, factory: F) -> Self + where + F: for<'a> Fn(&'a Multikey, &'a Multikey) -> Result, Error> + + Send + + Sync + + 'static, + { + self.local + .get_or_insert_with(BTreeMap::new) + .insert(codec, Box::new(factory)); + self + } + + /// Build the kdf view for the viewed Multikey and the attached second + /// key. + pub fn build(self) -> Result, Error> { + match dispatch_kdf_view(self.mk, self.second) { + Ok(view) => Ok(view), + Err(Error::Kdf(KdfError::UnsupportedCodec(codec))) => { + match self.local.as_ref().and_then(|f| f.get(&codec)) { + Some(factory) => factory(self.mk, self.second), + None => Err(Error::Kdf(KdfError::UnsupportedCodec(codec))), + } + } + Err(err) => Err(err), + } + } +} + +impl<'mk> ViewBuilder<'mk, ThresholdKind> { + /// Set a local-codec factory for this kind, replacing any factory already + /// registered for `codec`. + /// + /// The factory applies only when the built-in threshold dispatch reports + /// [`ConversionsError::UnsupportedCodec`] for a codec. + pub fn with_local_codec(mut self, codec: Codec, factory: F) -> Self + where + F: for<'a> Fn(&'a Multikey) -> Result, Error> + + Send + + Sync + + 'static, + { + self.local + .get_or_insert_with(BTreeMap::new) + .insert(codec, Box::new(factory)); + self + } + + /// Build the threshold view for the viewed Multikey. + pub fn build(self) -> Result, Error> { + match dispatch_threshold_view(self.mk) { + Ok(view) => Ok(view), + Err(Error::Conversions(ConversionsError::UnsupportedCodec(codec))) => { + match self.local.as_ref().and_then(|f| f.get(&codec)) { + Some(factory) => factory(self.mk), + None => Err(Error::Conversions(ConversionsError::UnsupportedCodec( + codec, + ))), + } + } + Err(err) => Err(err), + } + } +} + +impl<'mk> ViewBuilder<'mk, DisclosureKind> { + /// Set a local-codec factory for this kind, replacing any factory already + /// registered for `codec`. + /// + /// The built-in disclosure view is codec-independent, so the factory is + /// never consulted. Registration is accepted to keep the builder API + /// uniform across kinds; it has no effect. + pub fn with_local_codec(mut self, codec: Codec, factory: F) -> Self + where + F: for<'a> Fn(&'a Multikey) -> Result, Error> + + Send + + Sync + + 'static, + { + self.local + .get_or_insert_with(BTreeMap::new) + .insert(codec, Box::new(factory)); + self + } + + /// Build the threshold-disclosure view for the viewed Multikey. + /// + /// The disclosure view is codec-independent: the built-in view applies to + /// every key and local factories are never consulted. + pub fn build(self) -> Result, Error> { + Ok(dispatch_disclosure_view(self.mk)) + } +} + +impl<'mk> ViewBuilder<'mk, MerkleStateKind> { + /// Set a local-codec factory for this kind, replacing any factory already + /// registered for `codec`. + /// + /// The factory applies only when the built-in merkle-state dispatch + /// reports [`ConversionsError::UnsupportedCodec`] for a codec. + pub fn with_local_codec(mut self, codec: Codec, factory: F) -> Self + where + F: for<'a> Fn(&'a Multikey) -> Result, Error> + + Send + + Sync + + 'static, + { + self.local + .get_or_insert_with(BTreeMap::new) + .insert(codec, Box::new(factory)); + self + } + + /// Build the merkle-tree state view for the viewed Multikey. + pub fn build(self) -> Result, Error> { + match dispatch_merkle_state_view(self.mk) { + Ok(view) => Ok(view), + Err(Error::Conversions(ConversionsError::UnsupportedCodec(codec))) => { + match self.local.as_ref().and_then(|f| f.get(&codec)) { + Some(factory) => factory(self.mk), + None => Err(Error::Conversions(ConversionsError::UnsupportedCodec( + codec, + ))), + } + } + Err(err) => Err(err), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{AttrId, Builder, KEY_CODECS, ThresholdDisclosure}; + use multi_sig::Multisig; + use std::sync::{Arc, Mutex}; + use zeroize::Zeroizing; + + type Recorder = Arc>>; + + fn recorder() -> Recorder { + Arc::new(Mutex::new(Vec::new())) + } + + fn record(rec: &Recorder, entry: &str) { + rec.lock().unwrap().push(entry.to_string()); + } + + /// A custom protocol key: codec `Identity` plus the `AlgorithmName` and + /// `KeyType` attributes, decoded from wire bytes the way such keys arrive. + fn custom_key(algorithm_name: &str) -> Multikey { + let mut mk = Builder::new(Codec::Identity) + .with_key_bytes("custom-key-material") + .try_build() + .unwrap(); + mk.attributes.insert( + AttrId::AlgorithmName, + Zeroizing::new(algorithm_name.as_bytes().to_vec()), + ); + mk.attributes + .insert(AttrId::KeyType, Zeroizing::new(vec![1])); + let bytes: Vec = mk.clone().into(); + Multikey::try_from(bytes.as_ref()).unwrap() + } + + fn ed25519_key() -> Multikey { + let mut rng = rand::rng(); + Builder::new_from_random_bytes(Codec::Ed25519Priv, &mut rng) + .unwrap() + .try_build() + .unwrap() + } + + fn chacha_key() -> Multikey { + let mut mk = Builder::new(Codec::Chacha20Poly1305) + .with_key_bytes("chacha-key-material-32-bytes-x") + .try_build() + .unwrap(); + mk.attributes + .insert(AttrId::CipherNonce, Zeroizing::new(vec![0u8; 12])); + mk + } + + struct NoSign; + impl SignView for NoSign { + fn sign(&self, _: &[u8], _: bool, _: Option) -> Result { + Err(Error::UnsupportedAlgorithm("NoSign".into())) + } + } + + struct NoCipher; + impl CipherView for NoCipher { + fn decrypt(&self) -> Result { + Err(Error::UnsupportedAlgorithm("NoCipher.decrypt".into())) + } + fn encrypt(&self) -> Result { + Err(Error::UnsupportedAlgorithm("NoCipher.encrypt".into())) + } + } + + struct NoKdf; + impl KdfView for NoKdf { + fn derive_key(&self, _: &[u8]) -> Result { + Err(Error::UnsupportedAlgorithm("NoKdf.derive_key".into())) + } + } + + struct NoCipherAttr; + impl CipherAttrView for NoCipherAttr { + fn cipher_codec(&self) -> Result { + Err(Error::UnsupportedAlgorithm( + "NoCipherAttr.cipher_codec".into(), + )) + } + fn nonce_bytes(&self) -> Result>, Error> { + Err(Error::UnsupportedAlgorithm( + "NoCipherAttr.nonce_bytes".into(), + )) + } + fn key_length(&self) -> Result { + Err(Error::UnsupportedAlgorithm( + "NoCipherAttr.key_length".into(), + )) + } + } + + struct NoKdfAttr; + impl KdfAttrView for NoKdfAttr { + fn kdf_codec(&self) -> Result { + Err(Error::UnsupportedAlgorithm("NoKdfAttr.kdf_codec".into())) + } + fn salt_bytes(&self) -> Result>, Error> { + Err(Error::UnsupportedAlgorithm("NoKdfAttr.salt_bytes".into())) + } + fn rounds(&self) -> Result { + Err(Error::UnsupportedAlgorithm("NoKdfAttr.rounds".into())) + } + } + + struct NoDisclosure; + impl ThresholdDisclosureView for NoDisclosure { + fn disclosure_mode(&self) -> Result { + Err(Error::UnsupportedAlgorithm( + "NoDisclosure.disclosure_mode".into(), + )) + } + fn read_threshold_params(&self, _: Option<&Multikey>) -> Result<(usize, usize), Error> { + Err(Error::UnsupportedAlgorithm( + "NoDisclosure.read_threshold_params".into(), + )) + } + fn to_disclosure( + &self, + _: ThresholdDisclosure, + _: Option<&Multikey>, + _: Option<&Multikey>, + ) -> Result { + Err(Error::UnsupportedAlgorithm( + "NoDisclosure.to_disclosure".into(), + )) + } + } + + struct NoSeal; + impl SealView for NoSeal { + fn seal(&self, _: &[u8], _: Codec, _: &[u8]) -> Result<(Vec, Option), Error> { + Err(Error::UnsupportedAlgorithm("NoSeal.seal".into())) + } + } + + struct NoOpen; + impl OpenView for NoOpen { + fn open( + &self, + _: &[u8], + _: Option<&Multikey>, + _: &[u8], + ) -> Result>, Error> { + Err(Error::UnsupportedAlgorithm("NoOpen.open".into())) + } + } + + #[test] + fn test_builder_matches_shim() { + for codec in KEY_CODECS { + let mut rng = rand::rng(); + let mk = Builder::new_from_random_bytes(codec, &mut rng) + .unwrap() + .try_build() + .unwrap(); + + // the basic-attributes view is identical to the shim (which + // delegates to the dispatch core) + let shim = dispatch_attr_view(&mk).unwrap(); + let built = ViewBuilder::new(&mk).attr().build().unwrap(); + assert_eq!(shim.is_encrypted(), built.is_encrypted()); + assert_eq!(shim.is_public_key(), built.is_public_key()); + assert_eq!(shim.is_secret_key(), built.is_secret_key()); + assert_eq!(shim.is_secret_key_share(), built.is_secret_key_share()); + + // the key-data view is identical to the shim + let shim = dispatch_data_view(&mk).unwrap(); + let built = ViewBuilder::new(&mk).data().build().unwrap(); + assert_eq!(shim.key_bytes().unwrap(), built.key_bytes().unwrap()); + assert_eq!(shim.secret_bytes().unwrap(), built.secret_bytes().unwrap()); + } + } + + #[test] + fn test_cipher_attr_matches_shim() { + let mk = chacha_key(); + + // built-in path: both construct the chacha20 view + let shim = dispatch_cipher_attr_view(&mk).unwrap(); + let built = ViewBuilder::new(&mk).cipher_attr().build().unwrap(); + assert_eq!(shim.cipher_codec().unwrap(), built.cipher_codec().unwrap()); + assert_eq!( + shim.nonce_bytes().unwrap().len(), + built.nonce_bytes().unwrap().len() + ); + assert_eq!(shim.key_length().unwrap(), built.key_length().unwrap()); + + // a signing codec has no cipher-attributes view: same error as the shim + let mk = ed25519_key(); + let shim = dispatch_cipher_attr_view(&mk).err().unwrap(); + let built = ViewBuilder::new(&mk).cipher_attr().build().err().unwrap(); + assert_eq!(shim.to_string(), built.to_string()); + + // a signing codec has no kdf-attributes view: same error as the shim + let shim = dispatch_kdf_attr_view(&mk).err().unwrap(); + let built = ViewBuilder::new(&mk).kdf_attr().build().err().unwrap(); + assert_eq!(shim.to_string(), built.to_string()); + } + + #[test] + fn test_unsupported_matches_shim_error() { + // a DKG share codec has no built-in sign view + let mk = Builder::new(Codec::Ed25519ThreshPrivShare) + .with_key_bytes("share") + .try_build() + .unwrap(); + let shim = dispatch_sign_view(&mk).err().unwrap(); + let built = ViewBuilder::new(&mk).sign().build().err().unwrap(); + assert_eq!(shim.to_string(), built.to_string()); + assert!(matches!( + built, + Error::Conversions(ConversionsError::UnsupportedCodec(_)) + )); + + // the same key also has no threshold view + let shim = dispatch_threshold_view(&mk).err().unwrap(); + let built = ViewBuilder::new(&mk).threshold().build().err().unwrap(); + assert_eq!(shim.to_string(), built.to_string()); + + // a signing codec is not a seal/open key: exact variant preserved + let mk = ed25519_key(); + let shim = dispatch_seal_view(&mk).err().unwrap(); + let built = ViewBuilder::new(&mk).seal().build().err().unwrap(); + assert_eq!(shim.to_string(), built.to_string()); + assert!(matches!(built, Error::Seal(SealError::NotEncryptionKey))); + + // a signing codec has no merkle state either + let shim = dispatch_merkle_state_view(&mk).err().unwrap(); + let built = ViewBuilder::new(&mk).merkle_state().build().err().unwrap(); + assert_eq!(shim.to_string(), built.to_string()); + } + + #[test] + fn test_custom_key_factory() { + let mk = custom_key("example-protocol"); + let rec = recorder(); + let factory_rec = rec.clone(); + let view = ViewBuilder::new(&mk) + .sign() + .with_local_codec(Codec::Identity, move |key: &Multikey| { + let name = key + .attributes + .get(&AttrId::AlgorithmName) + .map(|v| v.as_slice().to_vec()) + .unwrap_or_default(); + record(&factory_rec, &String::from_utf8(name).unwrap_or_default()); + Ok(Box::new(NoSign)) + }) + .build() + .unwrap(); + + // the factory saw the decoded custom key's AlgorithmName attribute + assert_eq!(rec.lock().unwrap().as_slice(), ["example-protocol"]); + + // the built view is the factory's view + let err = view.sign(b"msg", false, None).err().unwrap(); + assert_eq!(err.to_string(), "Unsupported key algorithm: NoSign"); + } + + #[test] + fn test_cipher_factory_gets_second_key() { + let mk = custom_key("viewed-key"); + let ck = custom_key("cipher-key"); + let rec = recorder(); + let factory_rec = rec.clone(); + let view = ViewBuilder::new(&mk) + .cipher(&ck) + .with_local_codec(Codec::Identity, move |mk: &Multikey, cipher: &Multikey| { + let viewed = mk + .attributes + .get(&AttrId::AlgorithmName) + .map(|v| v.as_slice().to_vec()) + .unwrap_or_default(); + let attached = cipher + .attributes + .get(&AttrId::AlgorithmName) + .map(|v| v.as_slice().to_vec()) + .unwrap_or_default(); + record( + &factory_rec, + &format!( + "{}|{}", + String::from_utf8(viewed).unwrap_or_default(), + String::from_utf8(attached).unwrap_or_default() + ), + ); + Ok(Box::new(NoCipher)) + }) + .build() + .unwrap(); + + // the factory received the viewed key and the attached cipher key + assert_eq!(rec.lock().unwrap().as_slice(), ["viewed-key|cipher-key"]); + + // the built view is the factory's view + let err = view.decrypt().err().unwrap(); + assert_eq!( + err.to_string(), + "Unsupported key algorithm: NoCipher.decrypt" + ); + } + + #[test] + fn test_kdf_factory_gets_second_key() { + let mk = custom_key("viewed-key"); + let kk = custom_key("kdf-key"); + let rec = recorder(); + let factory_rec = rec.clone(); + let view = ViewBuilder::new(&mk) + .kdf(&kk) + .with_local_codec(Codec::Identity, move |mk: &Multikey, kdf: &Multikey| { + let viewed = mk + .attributes + .get(&AttrId::AlgorithmName) + .map(|v| v.as_slice().to_vec()) + .unwrap_or_default(); + let attached = kdf + .attributes + .get(&AttrId::AlgorithmName) + .map(|v| v.as_slice().to_vec()) + .unwrap_or_default(); + record( + &factory_rec, + &format!( + "{}|{}", + String::from_utf8(viewed).unwrap_or_default(), + String::from_utf8(attached).unwrap_or_default() + ), + ); + Ok(Box::new(NoKdf)) + }) + .build() + .unwrap(); + + // the factory received the viewed key and the attached kdf key + assert_eq!(rec.lock().unwrap().as_slice(), ["viewed-key|kdf-key"]); + + // the built view is the factory's view + let err = view.derive_key(b"passphrase").err().unwrap(); + assert_eq!( + err.to_string(), + "Unsupported key algorithm: NoKdf.derive_key" + ); + } + + #[test] + fn test_builtin_wins() { + let mk = ed25519_key(); + let rec = recorder(); + let factory_rec = rec.clone(); + let signer = ViewBuilder::new(&mk) + .sign() + .with_local_codec(Codec::Ed25519Priv, move |_: &Multikey| { + record(&factory_rec, "called"); + Ok(Box::new(NoSign)) + }) + .build() + .unwrap(); + + // a factory registered for a supported standard codec is never called + assert!(rec.lock().unwrap().is_empty()); + + // the built-in view works: sign with the builder, verify with the + // shim's dispatch path + let sig = signer.sign(b"hello", false, None).unwrap(); + dispatch_verify_view(&mk) + .unwrap() + .verify(&sig, Some(b"hello")) + .unwrap(); + + // and the other way around + let sig = dispatch_sign_view(&mk) + .unwrap() + .sign(b"hello", false, None) + .unwrap(); + ViewBuilder::new(&mk) + .verify() + .build() + .unwrap() + .verify(&sig, Some(b"hello")) + .unwrap(); + } + + #[test] + fn test_factory_error_propagates() { + let mk = custom_key("propagates"); + let built = ViewBuilder::new(&mk) + .sign() + .with_local_codec(Codec::Identity, |_: &Multikey| { + Err(Error::UnsupportedAlgorithm("factory failure".into())) + }) + .build(); + assert!(matches!(built, Err(Error::UnsupportedAlgorithm(s)) if s == "factory failure")); + } + + #[test] + fn test_last_registration_wins() { + let mk = custom_key("replaces"); + let rec = recorder(); + let first = rec.clone(); + let second = rec.clone(); + let view = ViewBuilder::new(&mk) + .sign() + .with_local_codec(Codec::Identity, move |_: &Multikey| { + record(&first, "first"); + Ok(Box::new(NoSign)) + }) + .with_local_codec(Codec::Identity, move |_: &Multikey| { + record(&second, "second"); + Ok(Box::new(NoSign)) + }) + .build() + .unwrap(); + + // the second factory replaced the first for the same kind and codec + assert_eq!(rec.lock().unwrap().as_slice(), ["second"]); + let err = view.sign(b"msg", false, None).err().unwrap(); + assert_eq!(err.to_string(), "Unsupported key algorithm: NoSign"); + } + + #[test] + fn test_cipher_attr_factory_lookup_key() { + // the key's CipherCodec attribute names Ed25519Pub: that derived codec + // is what the built-in dispatch matches on and what the factory + // lookup keys on + let mut mk = custom_key("attribute-derived"); + mk.attributes.insert( + AttrId::CipherCodec, + Zeroizing::new(Vec::from(Codec::Ed25519Pub)), + ); + + // a factory registered for the attribute-derived codec applies + let rec = recorder(); + let factory_rec = rec.clone(); + let view = ViewBuilder::new(&mk) + .cipher_attr() + .with_local_codec(Codec::Ed25519Pub, move |_: &Multikey| { + record(&factory_rec, "called"); + Ok(Box::new(NoCipherAttr)) + }) + .build() + .unwrap(); + assert_eq!(rec.lock().unwrap().as_slice(), ["called"]); + let err = view.cipher_codec().err().unwrap(); + assert_eq!( + err.to_string(), + "Unsupported key algorithm: NoCipherAttr.cipher_codec" + ); + + // a factory registered for the key's own codec is not consulted + let rec = recorder(); + let factory_rec = rec.clone(); + let built = ViewBuilder::new(&mk) + .cipher_attr() + .with_local_codec(Codec::Identity, move |_: &Multikey| { + record(&factory_rec, "called"); + Ok(Box::new(NoCipherAttr)) + }) + .build(); + assert!(rec.lock().unwrap().is_empty()); + // the fallthrough error matches the shim and names the derived codec + assert!(matches!( + &built, + Err(Error::Cipher(CipherError::UnsupportedCodec( + Codec::Ed25519Pub + ))) + )); + let shim = dispatch_cipher_attr_view(&mk).err().unwrap(); + assert_eq!(shim.to_string(), built.err().unwrap().to_string()); + } + + #[test] + fn test_kdf_attr_factory_lookup_key() { + // the key's KdfCodec attribute names Ed25519Priv: that derived codec + // is what the built-in dispatch matches on and what the factory + // lookup keys on + let mut mk = custom_key("attribute-derived"); + mk.attributes.insert( + AttrId::KdfCodec, + Zeroizing::new(Vec::from(Codec::Ed25519Priv)), + ); + + // no factory: the fallthrough error matches the shim and names the + // derived codec + let shim = dispatch_kdf_attr_view(&mk).err().unwrap(); + let built = ViewBuilder::new(&mk).kdf_attr().build().err().unwrap(); + assert!(matches!( + built, + Error::Kdf(KdfError::UnsupportedCodec(Codec::Ed25519Priv)) + )); + assert_eq!(shim.to_string(), built.to_string()); + + // a factory registered for the derived codec applies + let rec = recorder(); + let factory_rec = rec.clone(); + let view = ViewBuilder::new(&mk) + .kdf_attr() + .with_local_codec(Codec::Ed25519Priv, move |_: &Multikey| { + record(&factory_rec, "called"); + Ok(Box::new(NoKdfAttr)) + }) + .build() + .unwrap(); + assert_eq!(rec.lock().unwrap().as_slice(), ["called"]); + let err = view.rounds().err().unwrap(); + assert_eq!( + err.to_string(), + "Unsupported key algorithm: NoKdfAttr.rounds" + ); + + // a factory registered for the key's own codec is not consulted + let rec = recorder(); + let factory_rec = rec.clone(); + let built = ViewBuilder::new(&mk) + .kdf_attr() + .with_local_codec(Codec::Identity, move |_: &Multikey| { + record(&factory_rec, "called"); + Ok(Box::new(NoKdfAttr)) + }) + .build(); + assert!(rec.lock().unwrap().is_empty()); + assert!(matches!( + built, + Err(Error::Kdf(KdfError::UnsupportedCodec(Codec::Ed25519Priv))) + )); + } + + #[test] + fn test_cipher_attr_factory_lookup_falls_back_to_key_codec() { + // without a CipherCodec attribute the derived codec falls back to the + // key's own codec, and that is what the factory lookup keys on + let mk = custom_key("no-cipher-attr"); + let rec = recorder(); + let factory_rec = rec.clone(); + let view = ViewBuilder::new(&mk) + .cipher_attr() + .with_local_codec(Codec::Identity, move |_: &Multikey| { + record(&factory_rec, "called"); + Ok(Box::new(NoCipherAttr)) + }) + .build() + .unwrap(); + assert_eq!(rec.lock().unwrap().as_slice(), ["called"]); + let err = view.key_length().err().unwrap(); + assert_eq!( + err.to_string(), + "Unsupported key algorithm: NoCipherAttr.key_length" + ); + } + + #[test] + fn test_disclosure_factory_never_consulted() { + let mk = ed25519_key(); + let rec = recorder(); + let factory_rec = rec.clone(); + let view = ViewBuilder::new(&mk) + .disclosure() + .with_local_codec(Codec::Ed25519Priv, move |_: &Multikey| { + record(&factory_rec, "called"); + Ok(Box::new(NoDisclosure)) + }) + .build() + .unwrap(); + + // the disclosure view is codec-independent: the factory is never + // consulted and the built-in view applies + assert!(rec.lock().unwrap().is_empty()); + let mode = view.disclosure_mode().unwrap(); + assert_eq!(mode, ThresholdDisclosure::Full); + } + + #[test] + fn test_send_sync() { + fn assert_send() {} + fn assert_sync() {} + + // the type is Send + Sync with or without factories: the factory + // table is bounded Send + Sync and only ever holds the same type + assert_send::>(); + assert_sync::>(); + assert_send::>(); + assert_sync::>(); + assert_send::>(); + assert_sync::>(); + + // a builder that borrows keys and holds a registered factory moves + // across threads too + let mk = custom_key("threaded"); + let builder = ViewBuilder::new(&mk) + .sign() + .with_local_codec(Codec::Identity, |_: &Multikey| Ok(Box::new(NoSign))); + std::thread::scope(|scope| { + scope.spawn(move || { + let view = builder.build().unwrap(); + assert!(view.sign(b"msg", false, None).is_err()); + }); + }); + + // and so does a factory-less builder + let mk = ed25519_key(); + let builder = ViewBuilder::new(&mk).attr(); + std::thread::scope(|scope| { + scope.spawn(move || { + assert!(builder.build().is_ok()); + }); + }); + } + + #[test] + fn test_seal_open_factory_keyed_on_key_codec() { + // a signing codec is not an encryption key: seal and open fall + // through with NotEncryptionKey, whose error carries no codec, so the + // factory lookup keys on the viewed Multikey's codec + let mk = ed25519_key(); + let rec = recorder(); + let factory_rec = rec.clone(); + let view = ViewBuilder::new(&mk) + .seal() + .with_local_codec(Codec::Ed25519Priv, move |_: &Multikey| { + record(&factory_rec, "seal-called"); + Ok(Box::new(NoSeal)) + }) + .build() + .unwrap(); + assert_eq!(rec.lock().unwrap().as_slice(), ["seal-called"]); + let err = view + .seal(b"plaintext", Codec::Chacha20Poly1305, b"aad") + .err() + .unwrap(); + assert_eq!(err.to_string(), "Unsupported key algorithm: NoSeal.seal"); + + let rec = recorder(); + let factory_rec = rec.clone(); + let view = ViewBuilder::new(&mk) + .open() + .with_local_codec(Codec::Ed25519Priv, move |_: &Multikey| { + record(&factory_rec, "open-called"); + Ok(Box::new(NoOpen)) + }) + .build() + .unwrap(); + assert_eq!(rec.lock().unwrap().as_slice(), ["open-called"]); + let err = view.open(b"sealed", None, b"aad").err().unwrap(); + assert_eq!(err.to_string(), "Unsupported key algorithm: NoOpen.open"); + + // a factory registered for a codec the key does not carry is not + // consulted; the exact fallthrough error returns + let built = ViewBuilder::new(&mk) + .seal() + .with_local_codec(Codec::Ed25519Pub, |_: &Multikey| Ok(Box::new(NoSeal))) + .build(); + assert!(matches!( + built, + Err(Error::Seal(SealError::NotEncryptionKey)) + )); + } + + #[test] + fn test_cipher_kdf_factory_lookup_key_is_second_key_codec() { + // the cipher and kdf kinds dispatch on the SECOND key's codec, so the + // factory lookup keys on that codec, not the viewed key's codec + let mk = custom_key("viewed-key"); + let ck = ed25519_key(); // a signing codec: no built-in cipher or kdf view + let rec = recorder(); + let factory_rec = rec.clone(); + let built = ViewBuilder::new(&mk) + .cipher(&ck) + .with_local_codec(Codec::Ed25519Priv, move |_: &Multikey, _: &Multikey| { + record(&factory_rec, "called"); + Err(Error::UnsupportedAlgorithm("second-key factory".into())) + }) + .build(); + assert_eq!(rec.lock().unwrap().as_slice(), ["called"]); + assert!(matches!(built, Err(Error::UnsupportedAlgorithm(s)) if s == "second-key factory")); + + // a factory keyed on the VIEWED key's codec is never consulted; the + // fallthrough error matches the shim and names the second key's codec + let rec = recorder(); + let factory_rec = rec.clone(); + let built = ViewBuilder::new(&mk) + .cipher(&ck) + .with_local_codec(Codec::Identity, move |_: &Multikey, _: &Multikey| { + record(&factory_rec, "called"); + Err(Error::UnsupportedAlgorithm("viewed-key factory".into())) + }) + .build(); + assert!(rec.lock().unwrap().is_empty()); + assert!(matches!( + &built, + Err(Error::Cipher(CipherError::UnsupportedCodec( + Codec::Ed25519Priv + ))) + )); + let shim = dispatch_cipher_view(&mk, &ck).err().unwrap(); + assert_eq!(shim.to_string(), built.err().unwrap().to_string()); + + // the kdf kind keys on the kdf key's codec the same way + let rec = recorder(); + let factory_rec = rec.clone(); + let built = ViewBuilder::new(&mk) + .kdf(&ck) + .with_local_codec(Codec::Ed25519Priv, move |_: &Multikey, _: &Multikey| { + record(&factory_rec, "called"); + Err(Error::UnsupportedAlgorithm("kdf second-key factory".into())) + }) + .build(); + assert_eq!(rec.lock().unwrap().as_slice(), ["called"]); + assert!( + matches!(built, Err(Error::UnsupportedAlgorithm(s)) if s == "kdf second-key factory") + ); + } +} diff --git a/src/views/chacha20.rs b/src/views/chacha20.rs index cdafb6e..a117ebb 100644 --- a/src/views/chacha20.rs +++ b/src/views/chacha20.rs @@ -1,7 +1,10 @@ // SPDX-License-Identifier: Apache-2.0 +use crate::views::dispatch::{ + dispatch_attr_view, dispatch_cipher_attr_view, dispatch_data_view, dispatch_kdf_attr_view, +}; use crate::{ AttrId, AttrView, CipherAttrView, CipherView, DataView, Error, FingerprintView, KdfAttrView, - Multikey, Views, + Multikey, error::{AttributesError, CipherError, KdfError}, }; #[cfg(feature = "legacy_chacha20_fallback")] @@ -155,14 +158,14 @@ impl<'a> CipherView for View<'a> { fn decrypt(&self) -> Result { let cipher = self.cipher.ok_or(CipherError::MissingCodec)?; // make sure the viewed key is an encrypted secret key - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if !attr.is_encrypted() || !attr.is_secret_key() { return Err(CipherError::DecryptionFailed.into()); } // get the nonce data from the passed-in Multikey let nonce = { - let cattr = cipher.cipher_attr_view()?; + let cattr = dispatch_cipher_attr_view(cipher)?; cattr.nonce_bytes()? }; @@ -172,7 +175,7 @@ impl<'a> CipherView for View<'a> { // get the key data from the passed-in Multikey let key = { - let kd = cipher.data_view()?; + let kd = dispatch_data_view(cipher)?; let key = kd.secret_bytes()?; if key.len() != self.key_length()? { return Err(CipherError::InvalidKey.into()); @@ -186,7 +189,7 @@ impl<'a> CipherView for View<'a> { // get the encrypted key bytes from the viewed Multikey (self) let msg = { - let attr = self.mk.data_view()?; + let attr = dispatch_data_view(self.mk)?; attr.key_bytes()? }; @@ -249,7 +252,7 @@ impl<'a> CipherView for View<'a> { fn encrypt(&self) -> Result { let cipher = self.cipher.ok_or(CipherError::MissingCodec)?; // make sure the viewed key is not encrypted - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if attr.is_encrypted() { return Err( CipherError::EncryptionFailed("key is encrypted already".to_string()).into(), @@ -258,13 +261,13 @@ impl<'a> CipherView for View<'a> { // get the nonce data from the passed-in Multikey let nonce = { - let cattr = cipher.cipher_attr_view()?; + let cattr = dispatch_cipher_attr_view(cipher)?; cattr.nonce_bytes()? }; // get the key data from the passed-in Multikey let key = { - let kd = cipher.data_view()?; + let kd = dispatch_data_view(cipher)?; let key = kd.secret_bytes()?; if key.len() != self.key_length()? { return Err(CipherError::InvalidKey.into()); @@ -274,7 +277,7 @@ impl<'a> CipherView for View<'a> { // get the secret bytes from the viewed Multikey let msg = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -294,13 +297,13 @@ impl<'a> CipherView for View<'a> { }; // prepare the cipher attributes - let cattr = cipher.cipher_attr_view()?; + let cattr = dispatch_cipher_attr_view(cipher)?; let cipher_codec: Vec = cipher.codec.into(); let key_length: Vec = Varuint(cattr.key_length()?).into(); let is_encrypted: Vec = Varuint(true).into(); // get a view on the kdf attributes - let kattr = cipher.kdf_attr_view()?; + let kattr = dispatch_kdf_attr_view(cipher)?; let kdf_codec: Vec = kattr.kdf_codec()?.into(); let salt = kattr.salt_bytes()?; let rounds: Vec = Varuint(kattr.rounds()?).into(); @@ -328,7 +331,7 @@ impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { // get the key bytes let bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.key_bytes()? }; // hash the key bytes using the given codec diff --git a/src/views/classic_mceliece.rs b/src/views/classic_mceliece.rs index aa48c5d..5c2ae80 100644 --- a/src/views/classic_mceliece.rs +++ b/src/views/classic_mceliece.rs @@ -13,11 +13,12 @@ //! mutually exclusive feature flags per variant, preventing both from being //! compiled in a single dependency graph. +use crate::views::dispatch::dispatch_data_view; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, OpenView, SealView, error::{AttributesError, ConversionsError, SealError}, - views::{Views, aead}, + views::aead, }; use multi_codec::Codec; use multi_hash::{Multihash, mh}; @@ -95,7 +96,7 @@ impl<'a> DataView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -135,7 +136,7 @@ impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { let pub_bytes = if self.is_secret_key() { let pk = self.to_public_key()?; - let dv = pk.data_view()?; + let dv = dispatch_data_view(&pk)?; dv.key_bytes()? } else { self.key_bytes()? @@ -227,7 +228,7 @@ impl<'a> OpenView for View<'a> { } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -263,8 +264,8 @@ impl<'a> OpenView for View<'a> { #[cfg(all(test, feature = "slow-tests"))] mod tests { use super::*; + use crate::ViewBuilder; use crate::mk::MCELIECE_KEY_CODECS; - use crate::views::Views; #[test] fn test_mceliece_key_gen_roundtrip() { @@ -276,11 +277,11 @@ mod tests { .try_build() .unwrap(); - let attr = mk.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk).attr().build().unwrap(); assert!(attr.is_secret_key()); assert!(!attr.is_public_key()); - let kd = mk.data_view().unwrap(); + let kd = ViewBuilder::new(&mk).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); @@ -300,10 +301,10 @@ mod tests { .try_build() .unwrap(); - let conv = mk.conv_view().unwrap(); + let conv = ViewBuilder::new(&mk).conv().build().unwrap(); let pk = conv.to_public_key().unwrap(); - let attr = pk.attr_view().unwrap(); + let attr = ViewBuilder::new(&pk).attr().build().unwrap(); assert!(attr.is_public_key()); assert!(!attr.is_secret_key()); @@ -322,9 +323,15 @@ mod tests { .try_build() .unwrap(); - let pk = mk.conv_view().unwrap().to_public_key().unwrap(); - let fp = pk - .fingerprint_view() + let pk = ViewBuilder::new(&mk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); + let fp = ViewBuilder::new(&pk) + .fingerprint() + .build() .unwrap() .fingerprint(Codec::Sha3256) .unwrap(); @@ -347,17 +354,28 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); for aead_codec in &aead_codecs { let plaintext = b"hello classic mceliece world!"; - let (sealed, _) = pk - .seal_view() + let (sealed, _) = ViewBuilder::new(&pk) + .seal() + .build() .unwrap() .seal(plaintext, *aead_codec, b"") .unwrap(); - let opened = sk.open_view().unwrap().open(&sealed, None, b"").unwrap(); + let opened = ViewBuilder::new(&sk) + .open() + .build() + .unwrap() + .open(&sealed, None, b"") + .unwrap(); assert_eq!(plaintext.as_slice(), opened.as_slice()); } } @@ -370,21 +388,34 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk1 = sk1.conv_view().unwrap().to_public_key().unwrap(); + let pk1 = ViewBuilder::new(&sk1) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let sk2 = Builder::new_from_random_bytes(Codec::Mceliece348864Priv, &mut rng) .unwrap() .try_build() .unwrap(); - let (sealed, _) = pk1 - .seal_view() + let (sealed, _) = ViewBuilder::new(&pk1) + .seal() + .build() .unwrap() .seal(b"secret data", Codec::Xchacha20Poly1305, b"") .unwrap(); // Opening with wrong key should fail - assert!(sk2.open_view().unwrap().open(&sealed, None, b"").is_err()); + assert!( + ViewBuilder::new(&sk2) + .open() + .build() + .unwrap() + .open(&sealed, None, b"") + .is_err() + ); } #[test] @@ -397,7 +428,9 @@ mod tests { // seal with private key should fail assert!( - sk.seal_view() + ViewBuilder::new(&sk) + .seal() + .build() .unwrap() .seal(b"data", Codec::Xchacha20Poly1305, b"") .is_err() @@ -411,16 +444,29 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); - let (sealed, _) = pk - .seal_view() + let (sealed, _) = ViewBuilder::new(&pk) + .seal() + .build() .unwrap() .seal(b"data", Codec::Xchacha20Poly1305, b"") .unwrap(); // open with public key should fail - assert!(pk.open_view().unwrap().open(&sealed, None, b"").is_err()); + assert!( + ViewBuilder::new(&pk) + .open() + .build() + .unwrap() + .open(&sealed, None, b"") + .is_err() + ); } #[test] @@ -430,11 +476,18 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); // AES-128-GCM is not allowed for mceliece (not PQ-safe) assert!( - pk.seal_view() + ViewBuilder::new(&pk) + .seal() + .build() .unwrap() .seal(b"data", Codec::AesGcm128, b"") .is_err() diff --git a/src/views/dispatch.rs b/src/views/dispatch.rs new file mode 100644 index 0000000..074a1c5 --- /dev/null +++ b/src/views/dispatch.rs @@ -0,0 +1,1565 @@ +// SPDX-License-Identifier: Apache-2.0 + +//! The shared Multikey view dispatch core. +//! +//! One constructor function per view kind dispatches on the Multikey codec +//! exactly as the original `impl Views for Multikey` bodies did, so the +//! deprecated `Views` shim and the `ViewBuilder` share one implementation. + +#[cfg(feature = "xmss")] +use crate::views::xmss; +#[cfg(feature = "deprecated")] +use crate::views::{classic_mceliece, x25519_mceliece348864}; +#[cfg(feature = "lamport")] +use crate::views::{lamport, lamport_merkle}; +use crate::{ + AttrId, AttrView, CipherAttrView, CipherView, ConvView, DataView, Error, FingerprintView, + KdfAttrView, KdfView, MerkleStateView, Multikey, OpenView, SealView, SignView, + ThresholdAttrView, ThresholdDisclosureView, ThresholdKeyView, ThresholdView, VerifyView, + error::{AttributesError, CipherError, ConversionsError, KdfError, SealError}, + views::{ + bcrypt, bls12381, bls12381_g1_fndsa512, bls12381_g1_mayo1, bls12381_g1_mayo2, + bls12381_g1_mldsa65, chacha20, ed25519, ed25519_fndsa512, ed25519_mayo2, ed25519_mldsa65, + fn_dsa, frodokem, mayo, ml_dsa, ml_kem, nist_p, rsa, secp256k1, slh_dsa, sntrup, + threshold_meta, x25519, x25519_frodokem640, x25519_mlkem768, x25519_sntrup761, xeddsa, + }, +}; +use multi_codec::Codec; +/// Builds the basic-attributes view for the viewed Multikey. +pub(crate) fn dispatch_attr_view<'a>(mk: &'a Multikey) -> Result, Error> { + match mk.codec { + Codec::Bls12381G1PrivShare + | Codec::Bls12381G1Priv + | Codec::Bls12381G1Pub + | Codec::Bls12381G1PubShare + | Codec::Bls12381G2PrivShare + | Codec::Bls12381G2Priv + | Codec::Bls12381G2Pub + | Codec::Bls12381G2PubShare => Ok(Box::new(bls12381::View::try_from(mk)?)), + Codec::Ed25519Pub | Codec::Ed25519Priv => Ok(Box::new(ed25519::View::try_from(mk)?)), + Codec::Secp256K1Pub | Codec::Secp256K1Priv => Ok(Box::new(secp256k1::View::try_from(mk)?)), + Codec::Chacha20Poly1305 => Ok(Box::new(chacha20::View::try_from(mk)?)), + Codec::SlhDsaSha2128FPub + | Codec::SlhDsaSha2128SPub + | Codec::SlhDsaSha2192FPub + | Codec::SlhDsaSha2192SPub + | Codec::SlhDsaSha2256FPub + | Codec::SlhDsaSha2256SPub + | Codec::SlhDsaShake128FPub + | Codec::SlhDsaShake128SPub + | Codec::SlhDsaShake192FPub + | Codec::SlhDsaShake192SPub + | Codec::SlhDsaShake256FPub + | Codec::SlhDsaShake256SPub + | Codec::SlhDsaSha2128FPriv + | Codec::SlhDsaSha2128SPriv + | Codec::SlhDsaSha2192FPriv + | Codec::SlhDsaSha2192SPriv + | Codec::SlhDsaSha2256FPriv + | Codec::SlhDsaSha2256SPriv + | Codec::SlhDsaShake128FPriv + | Codec::SlhDsaShake128SPriv + | Codec::SlhDsaShake192FPriv + | Codec::SlhDsaShake192SPriv + | Codec::SlhDsaShake256FPriv + | Codec::SlhDsaShake256SPriv => Ok(Box::new(slh_dsa::View::try_from(mk)?)), + Codec::MlDsa65Pub | Codec::MlDsa65Priv | Codec::MlDsa87Pub | Codec::MlDsa87Priv => { + Ok(Box::new(ml_dsa::View::try_from(mk)?)) + } + Codec::Mayo1Pub + | Codec::Mayo1Priv + | Codec::Mayo2Pub + | Codec::Mayo2Priv + | Codec::Mayo3Pub + | Codec::Mayo3Priv + | Codec::Mayo5Pub + | Codec::Mayo5Priv => Ok(Box::new(mayo::View::try_from(mk)?)), + Codec::FnDsa512Pub | Codec::FnDsa512Priv | Codec::FnDsa1024Pub | Codec::FnDsa1024Priv => { + Ok(Box::new(fn_dsa::View::try_from(mk)?)) + } + Codec::Mlkem768Pub | Codec::Mlkem768Priv | Codec::Mlkem1024Pub | Codec::Mlkem1024Priv => { + Ok(Box::new(ml_kem::View::try_from(mk)?)) + } + Codec::Sntrup761Pub + | Codec::Sntrup761Priv + | Codec::Sntrup857Pub + | Codec::Sntrup857Priv + | Codec::Sntrup953Pub + | Codec::Sntrup953Priv + | Codec::Sntrup1013Pub + | Codec::Sntrup1013Priv + | Codec::Sntrup1277Pub + | Codec::Sntrup1277Priv => Ok(Box::new(sntrup::View::try_from(mk)?)), + #[cfg(feature = "deprecated")] + #[allow(deprecated)] + Codec::Mceliece348864Pub | Codec::Mceliece348864Priv => { + Ok(Box::new(classic_mceliece::View::try_from(mk)?)) + } + Codec::FrodoKem640AesPub + | Codec::FrodoKem640AesPriv + | Codec::FrodoKem976AesPub + | Codec::FrodoKem976AesPriv + | Codec::FrodoKem1344AesPub + | Codec::FrodoKem1344AesPriv + | Codec::FrodoKem640ShakePub + | Codec::FrodoKem640ShakePriv + | Codec::FrodoKem976ShakePub + | Codec::FrodoKem976ShakePriv + | Codec::FrodoKem1344ShakePub + | Codec::FrodoKem1344ShakePriv => Ok(Box::new(frodokem::View::try_from(mk)?)), + Codec::X25519Pub | Codec::X25519Priv => Ok(Box::new(x25519::View::try_from(mk)?)), + Codec::X25519Sntrup761Pub | Codec::X25519Sntrup761Priv => { + Ok(Box::new(x25519_sntrup761::View::try_from(mk)?)) + } + Codec::X25519Frodokem640AesPub + | Codec::X25519Frodokem640AesPriv + | Codec::X25519Frodokem640ShakePub + | Codec::X25519Frodokem640ShakePriv => { + Ok(Box::new(x25519_frodokem640::View::try_from(mk)?)) + } + #[cfg(feature = "deprecated")] + #[allow(deprecated)] + Codec::X25519Mceliece348864Pub | Codec::X25519Mceliece348864Priv => { + Ok(Box::new(x25519_mceliece348864::View::try_from(mk)?)) + } + Codec::X25519Mlkem768Pub | Codec::X25519Mlkem768Priv => { + Ok(Box::new(x25519_mlkem768::View::try_from(mk)?)) + } + Codec::Ed25519Mayo2Pub | Codec::Ed25519Mayo2Priv => { + Ok(Box::new(ed25519_mayo2::View::try_from(mk)?)) + } + Codec::Ed25519Mldsa65Pub | Codec::Ed25519Mldsa65Priv => { + Ok(Box::new(ed25519_mldsa65::View::try_from(mk)?)) + } + Codec::Bls12381G1Mldsa65Pub | Codec::Bls12381G1Mldsa65Priv => { + Ok(Box::new(bls12381_g1_mldsa65::View::try_from(mk)?)) + } + Codec::Bls12381G1Fndsa512Pub | Codec::Bls12381G1Fndsa512Priv => { + Ok(Box::new(bls12381_g1_fndsa512::View::try_from(mk)?)) + } + Codec::Bls12381G1Mayo1Pub | Codec::Bls12381G1Mayo1Priv => { + Ok(Box::new(bls12381_g1_mayo1::View::try_from(mk)?)) + } + Codec::Bls12381G1Mayo2Pub | Codec::Bls12381G1Mayo2Priv => { + Ok(Box::new(bls12381_g1_mayo2::View::try_from(mk)?)) + } + Codec::Ed25519Fndsa512Pub | Codec::Ed25519Fndsa512Priv => { + Ok(Box::new(ed25519_fndsa512::View::try_from(mk)?)) + } + Codec::P256Pub + | Codec::P256Priv + | Codec::P384Pub + | Codec::P384Priv + | Codec::P521Pub + | Codec::P521Priv => Ok(Box::new(nist_p::View::try_from(mk)?)), + Codec::Rsa2048Pub + | Codec::Rsa2048Priv + | Codec::Rsa3072Pub + | Codec::Rsa3072Priv + | Codec::Rsa4096Pub + | Codec::Rsa4096Priv => Ok(Box::new(rsa::View::try_from(mk)?)), + #[cfg(feature = "lamport")] + Codec::LamportSha3256Pub + | Codec::LamportSha3256Priv + | Codec::LamportSha3256PrivShare + | Codec::LamportSha3384Pub + | Codec::LamportSha3384Priv + | Codec::LamportSha3384PrivShare + | Codec::LamportSha3512Pub + | Codec::LamportSha3512Priv + | Codec::LamportSha3512PrivShare + | Codec::LamportSha2256Pub + | Codec::LamportSha2256Priv + | Codec::LamportSha2256PrivShare + | Codec::LamportSha2384Pub + | Codec::LamportSha2384Priv + | Codec::LamportSha2384PrivShare + | Codec::LamportSha2512Pub + | Codec::LamportSha2512Priv + | Codec::LamportSha2512PrivShare + | Codec::LamportBlake2B512Pub + | Codec::LamportBlake2B512Priv + | Codec::LamportBlake2B512PrivShare + | Codec::LamportBlake2S256Pub + | Codec::LamportBlake2S256Priv + | Codec::LamportBlake2S256PrivShare + | Codec::LamportBlake3256Pub + | Codec::LamportBlake3256Priv + | Codec::LamportBlake3256PrivShare + | Codec::LamportShake128Pub + | Codec::LamportShake128Priv + | Codec::LamportShake128PrivShare + | Codec::LamportShake256Pub + | Codec::LamportShake256Priv + | Codec::LamportShake256PrivShare => Ok(Box::new(lamport::View::try_from(mk)?)), + #[cfg(feature = "lamport")] + Codec::LamportMerkleSha3256Pub + | Codec::LamportMerkleSha3256Priv + | Codec::LamportMerkleSha3256PrivShare + | Codec::LamportMerkleSha3384Pub + | Codec::LamportMerkleSha3384Priv + | Codec::LamportMerkleSha3384PrivShare + | Codec::LamportMerkleSha3512Pub + | Codec::LamportMerkleSha3512Priv + | Codec::LamportMerkleSha3512PrivShare + | Codec::LamportMerkleSha2256Pub + | Codec::LamportMerkleSha2256Priv + | Codec::LamportMerkleSha2256PrivShare + | Codec::LamportMerkleSha2384Pub + | Codec::LamportMerkleSha2384Priv + | Codec::LamportMerkleSha2384PrivShare + | Codec::LamportMerkleSha2512Pub + | Codec::LamportMerkleSha2512Priv + | Codec::LamportMerkleSha2512PrivShare + | Codec::LamportMerkleBlake2B512Pub + | Codec::LamportMerkleBlake2B512Priv + | Codec::LamportMerkleBlake2B512PrivShare + | Codec::LamportMerkleBlake2S256Pub + | Codec::LamportMerkleBlake2S256Priv + | Codec::LamportMerkleBlake2S256PrivShare + | Codec::LamportMerkleBlake3256Pub + | Codec::LamportMerkleBlake3256Priv + | Codec::LamportMerkleBlake3256PrivShare + | Codec::LamportMerkleShake128Pub + | Codec::LamportMerkleShake128Priv + | Codec::LamportMerkleShake128PrivShare + | Codec::LamportMerkleShake256Pub + | Codec::LamportMerkleShake256Priv + | Codec::LamportMerkleShake256PrivShare => { + Ok(Box::new(lamport_merkle::View::try_from(mk)?)) + } + #[cfg(feature = "xmss")] + Codec::XmssSha210256Pub + | Codec::XmssSha210256Priv + | Codec::XmssSha216256Pub + | Codec::XmssSha216256Priv + | Codec::XmssSha220256Pub + | Codec::XmssSha220256Priv => Ok(Box::new(xmss::View::try_from(mk)?)), + _ => Err(AttributesError::UnsupportedCodec(mk.codec).into()), + } +} +/// Builds the cipher-attributes view for the viewed Multikey. +pub(crate) fn dispatch_cipher_attr_view<'a>( + mk: &'a Multikey, +) -> Result, Error> { + let codec = if let Some(bytes) = mk.attributes.get(&AttrId::CipherCodec) { + Codec::try_from(bytes.as_slice())? + } else { + mk.codec + }; + match codec { + Codec::Chacha20Poly1305 => Ok(Box::new(chacha20::View::try_from(mk)?)), + _ => Err(CipherError::UnsupportedCodec(codec).into()), + } +} +/// Builds the key-data view for the viewed Multikey. +pub(crate) fn dispatch_data_view<'a>(mk: &'a Multikey) -> Result, Error> { + match mk.codec { + Codec::Bls12381G1PrivShare + | Codec::Bls12381G1Priv + | Codec::Bls12381G1Pub + | Codec::Bls12381G1PubShare + | Codec::Bls12381G2PrivShare + | Codec::Bls12381G2Priv + | Codec::Bls12381G2Pub + | Codec::Bls12381G2PubShare => Ok(Box::new(bls12381::View::try_from(mk)?)), + Codec::Ed25519Pub | Codec::Ed25519Priv => Ok(Box::new(ed25519::View::try_from(mk)?)), + Codec::Secp256K1Pub | Codec::Secp256K1Priv => Ok(Box::new(secp256k1::View::try_from(mk)?)), + Codec::Chacha20Poly1305 => Ok(Box::new(chacha20::View::try_from(mk)?)), + Codec::SlhDsaSha2128FPub + | Codec::SlhDsaSha2128SPub + | Codec::SlhDsaSha2192FPub + | Codec::SlhDsaSha2192SPub + | Codec::SlhDsaSha2256FPub + | Codec::SlhDsaSha2256SPub + | Codec::SlhDsaShake128FPub + | Codec::SlhDsaShake128SPub + | Codec::SlhDsaShake192FPub + | Codec::SlhDsaShake192SPub + | Codec::SlhDsaShake256FPub + | Codec::SlhDsaShake256SPub + | Codec::SlhDsaSha2128FPriv + | Codec::SlhDsaSha2128SPriv + | Codec::SlhDsaSha2192FPriv + | Codec::SlhDsaSha2192SPriv + | Codec::SlhDsaSha2256FPriv + | Codec::SlhDsaSha2256SPriv + | Codec::SlhDsaShake128FPriv + | Codec::SlhDsaShake128SPriv + | Codec::SlhDsaShake192FPriv + | Codec::SlhDsaShake192SPriv + | Codec::SlhDsaShake256FPriv + | Codec::SlhDsaShake256SPriv => Ok(Box::new(slh_dsa::View::try_from(mk)?)), + Codec::MlDsa65Pub | Codec::MlDsa65Priv | Codec::MlDsa87Pub | Codec::MlDsa87Priv => { + Ok(Box::new(ml_dsa::View::try_from(mk)?)) + } + Codec::Mayo1Pub + | Codec::Mayo1Priv + | Codec::Mayo2Pub + | Codec::Mayo2Priv + | Codec::Mayo3Pub + | Codec::Mayo3Priv + | Codec::Mayo5Pub + | Codec::Mayo5Priv => Ok(Box::new(mayo::View::try_from(mk)?)), + Codec::FnDsa512Pub | Codec::FnDsa512Priv | Codec::FnDsa1024Pub | Codec::FnDsa1024Priv => { + Ok(Box::new(fn_dsa::View::try_from(mk)?)) + } + Codec::Mlkem768Pub | Codec::Mlkem768Priv | Codec::Mlkem1024Pub | Codec::Mlkem1024Priv => { + Ok(Box::new(ml_kem::View::try_from(mk)?)) + } + Codec::Sntrup761Pub + | Codec::Sntrup761Priv + | Codec::Sntrup857Pub + | Codec::Sntrup857Priv + | Codec::Sntrup953Pub + | Codec::Sntrup953Priv + | Codec::Sntrup1013Pub + | Codec::Sntrup1013Priv + | Codec::Sntrup1277Pub + | Codec::Sntrup1277Priv => Ok(Box::new(sntrup::View::try_from(mk)?)), + #[cfg(feature = "deprecated")] + #[allow(deprecated)] + Codec::Mceliece348864Pub | Codec::Mceliece348864Priv => { + Ok(Box::new(classic_mceliece::View::try_from(mk)?)) + } + Codec::FrodoKem640AesPub + | Codec::FrodoKem640AesPriv + | Codec::FrodoKem976AesPub + | Codec::FrodoKem976AesPriv + | Codec::FrodoKem1344AesPub + | Codec::FrodoKem1344AesPriv + | Codec::FrodoKem640ShakePub + | Codec::FrodoKem640ShakePriv + | Codec::FrodoKem976ShakePub + | Codec::FrodoKem976ShakePriv + | Codec::FrodoKem1344ShakePub + | Codec::FrodoKem1344ShakePriv => Ok(Box::new(frodokem::View::try_from(mk)?)), + Codec::X25519Pub | Codec::X25519Priv => Ok(Box::new(x25519::View::try_from(mk)?)), + Codec::X25519Sntrup761Pub | Codec::X25519Sntrup761Priv => { + Ok(Box::new(x25519_sntrup761::View::try_from(mk)?)) + } + Codec::X25519Frodokem640AesPub + | Codec::X25519Frodokem640AesPriv + | Codec::X25519Frodokem640ShakePub + | Codec::X25519Frodokem640ShakePriv => { + Ok(Box::new(x25519_frodokem640::View::try_from(mk)?)) + } + #[cfg(feature = "deprecated")] + #[allow(deprecated)] + Codec::X25519Mceliece348864Pub | Codec::X25519Mceliece348864Priv => { + Ok(Box::new(x25519_mceliece348864::View::try_from(mk)?)) + } + Codec::X25519Mlkem768Pub | Codec::X25519Mlkem768Priv => { + Ok(Box::new(x25519_mlkem768::View::try_from(mk)?)) + } + Codec::Ed25519Mayo2Pub | Codec::Ed25519Mayo2Priv => { + Ok(Box::new(ed25519_mayo2::View::try_from(mk)?)) + } + Codec::Ed25519Mldsa65Pub | Codec::Ed25519Mldsa65Priv => { + Ok(Box::new(ed25519_mldsa65::View::try_from(mk)?)) + } + Codec::Bls12381G1Mldsa65Pub | Codec::Bls12381G1Mldsa65Priv => { + Ok(Box::new(bls12381_g1_mldsa65::View::try_from(mk)?)) + } + Codec::Bls12381G1Fndsa512Pub | Codec::Bls12381G1Fndsa512Priv => { + Ok(Box::new(bls12381_g1_fndsa512::View::try_from(mk)?)) + } + Codec::Bls12381G1Mayo1Pub | Codec::Bls12381G1Mayo1Priv => { + Ok(Box::new(bls12381_g1_mayo1::View::try_from(mk)?)) + } + Codec::Bls12381G1Mayo2Pub | Codec::Bls12381G1Mayo2Priv => { + Ok(Box::new(bls12381_g1_mayo2::View::try_from(mk)?)) + } + Codec::Ed25519Fndsa512Pub | Codec::Ed25519Fndsa512Priv => { + Ok(Box::new(ed25519_fndsa512::View::try_from(mk)?)) + } + Codec::P256Pub + | Codec::P256Priv + | Codec::P384Pub + | Codec::P384Priv + | Codec::P521Pub + | Codec::P521Priv => Ok(Box::new(nist_p::View::try_from(mk)?)), + Codec::Rsa2048Pub + | Codec::Rsa2048Priv + | Codec::Rsa3072Pub + | Codec::Rsa3072Priv + | Codec::Rsa4096Pub + | Codec::Rsa4096Priv => Ok(Box::new(rsa::View::try_from(mk)?)), + #[cfg(feature = "lamport")] + Codec::LamportSha3256Pub + | Codec::LamportSha3256Priv + | Codec::LamportSha3256PrivShare + | Codec::LamportSha3384Pub + | Codec::LamportSha3384Priv + | Codec::LamportSha3384PrivShare + | Codec::LamportSha3512Pub + | Codec::LamportSha3512Priv + | Codec::LamportSha3512PrivShare + | Codec::LamportSha2256Pub + | Codec::LamportSha2256Priv + | Codec::LamportSha2256PrivShare + | Codec::LamportSha2384Pub + | Codec::LamportSha2384Priv + | Codec::LamportSha2384PrivShare + | Codec::LamportSha2512Pub + | Codec::LamportSha2512Priv + | Codec::LamportSha2512PrivShare + | Codec::LamportBlake2B512Pub + | Codec::LamportBlake2B512Priv + | Codec::LamportBlake2B512PrivShare + | Codec::LamportBlake2S256Pub + | Codec::LamportBlake2S256Priv + | Codec::LamportBlake2S256PrivShare + | Codec::LamportBlake3256Pub + | Codec::LamportBlake3256Priv + | Codec::LamportBlake3256PrivShare + | Codec::LamportShake128Pub + | Codec::LamportShake128Priv + | Codec::LamportShake128PrivShare + | Codec::LamportShake256Pub + | Codec::LamportShake256Priv + | Codec::LamportShake256PrivShare => Ok(Box::new(lamport::View::try_from(mk)?)), + #[cfg(feature = "lamport")] + Codec::LamportMerkleSha3256Pub + | Codec::LamportMerkleSha3256Priv + | Codec::LamportMerkleSha3256PrivShare + | Codec::LamportMerkleSha3384Pub + | Codec::LamportMerkleSha3384Priv + | Codec::LamportMerkleSha3384PrivShare + | Codec::LamportMerkleSha3512Pub + | Codec::LamportMerkleSha3512Priv + | Codec::LamportMerkleSha3512PrivShare + | Codec::LamportMerkleSha2256Pub + | Codec::LamportMerkleSha2256Priv + | Codec::LamportMerkleSha2256PrivShare + | Codec::LamportMerkleSha2384Pub + | Codec::LamportMerkleSha2384Priv + | Codec::LamportMerkleSha2384PrivShare + | Codec::LamportMerkleSha2512Pub + | Codec::LamportMerkleSha2512Priv + | Codec::LamportMerkleSha2512PrivShare + | Codec::LamportMerkleBlake2B512Pub + | Codec::LamportMerkleBlake2B512Priv + | Codec::LamportMerkleBlake2B512PrivShare + | Codec::LamportMerkleBlake2S256Pub + | Codec::LamportMerkleBlake2S256Priv + | Codec::LamportMerkleBlake2S256PrivShare + | Codec::LamportMerkleBlake3256Pub + | Codec::LamportMerkleBlake3256Priv + | Codec::LamportMerkleBlake3256PrivShare + | Codec::LamportMerkleShake128Pub + | Codec::LamportMerkleShake128Priv + | Codec::LamportMerkleShake128PrivShare + | Codec::LamportMerkleShake256Pub + | Codec::LamportMerkleShake256Priv + | Codec::LamportMerkleShake256PrivShare => { + Ok(Box::new(lamport_merkle::View::try_from(mk)?)) + } + #[cfg(feature = "xmss")] + Codec::XmssSha210256Pub + | Codec::XmssSha210256Priv + | Codec::XmssSha216256Pub + | Codec::XmssSha216256Priv + | Codec::XmssSha220256Pub + | Codec::XmssSha220256Priv => Ok(Box::new(xmss::View::try_from(mk)?)), + _ => Err(ConversionsError::UnsupportedCodec(mk.codec).into()), + } +} +/// Builds the kdf-attributes view for the viewed Multikey. +pub(crate) fn dispatch_kdf_attr_view<'a>( + mk: &'a Multikey, +) -> Result, Error> { + let codec = if let Some(bytes) = mk.attributes.get(&AttrId::KdfCodec) { + Codec::try_from(bytes.as_slice())? + } else { + mk.codec + }; + match codec { + Codec::BcryptPbkdf => Ok(Box::new(bcrypt::View::try_from(mk)?)), + _ => Err(KdfError::UnsupportedCodec(codec).into()), + } +} +/// Builds the threshold-attributes view for the viewed Multikey. +pub(crate) fn dispatch_threshold_attr_view<'a>( + mk: &'a Multikey, +) -> Result, Error> { + match mk.codec { + Codec::Bls12381G1PrivShare + | Codec::Bls12381G1Priv + | Codec::Bls12381G1Pub + | Codec::Bls12381G1PubShare + | Codec::Bls12381G2PrivShare + | Codec::Bls12381G2Priv + | Codec::Bls12381G2Pub + | Codec::Bls12381G2PubShare => Ok(Box::new(bls12381::View::try_from(mk)?)), + Codec::Ed25519ThreshPrivShare + | Codec::P256ThreshPrivShare + | Codec::P384ThreshPrivShare + | Codec::Secp256K1ThreshPrivShare + | Codec::Bls12381ThreshPrivShare + | Codec::Ed448ThreshPrivShare => { + Ok(Box::new(crate::views::dkg_threshold::View::try_from(mk)?)) + } + _ => Err(ConversionsError::UnsupportedCodec(mk.codec).into()), + } +} +/// Builds the threshold-key metadata view for the viewed Multikey. +pub(crate) fn dispatch_threshold_key_view<'a>( + mk: &'a Multikey, +) -> Result, Error> { + match mk.codec { + Codec::Ed25519ThreshPrivShare + | Codec::P256ThreshPrivShare + | Codec::P384ThreshPrivShare + | Codec::Secp256K1ThreshPrivShare + | Codec::Bls12381ThreshPrivShare + | Codec::Ed448ThreshPrivShare => { + Ok(Box::new(crate::views::dkg_threshold::View::try_from(mk)?)) + } + _ => Err(ConversionsError::UnsupportedCodec(mk.codec).into()), + } +} +/// Builds the cipher view for the viewed Multikey using the cipher key. +pub(crate) fn dispatch_cipher_view<'a>( + mk: &'a Multikey, + cipher: &'a Multikey, +) -> Result, Error> { + match cipher.codec { + Codec::Chacha20Poly1305 => Ok(Box::new(chacha20::View::new(mk, cipher))), + _ => Err(CipherError::UnsupportedCodec(cipher.codec).into()), + } +} +/// Builds the key-conversion view for the viewed Multikey. +pub(crate) fn dispatch_conv_view<'a>(mk: &'a Multikey) -> Result, Error> { + match mk.codec { + Codec::Bls12381G1PrivShare + | Codec::Bls12381G1Priv + | Codec::Bls12381G1Pub + | Codec::Bls12381G1PubShare + | Codec::Bls12381G2PrivShare + | Codec::Bls12381G2Priv + | Codec::Bls12381G2Pub + | Codec::Bls12381G2PubShare => Ok(Box::new(bls12381::View::try_from(mk)?)), + Codec::Ed25519Pub | Codec::Ed25519Priv => Ok(Box::new(ed25519::View::try_from(mk)?)), + Codec::Secp256K1Pub | Codec::Secp256K1Priv => Ok(Box::new(secp256k1::View::try_from(mk)?)), + Codec::SlhDsaSha2128FPub + | Codec::SlhDsaSha2128SPub + | Codec::SlhDsaSha2192FPub + | Codec::SlhDsaSha2192SPub + | Codec::SlhDsaSha2256FPub + | Codec::SlhDsaSha2256SPub + | Codec::SlhDsaShake128FPub + | Codec::SlhDsaShake128SPub + | Codec::SlhDsaShake192FPub + | Codec::SlhDsaShake192SPub + | Codec::SlhDsaShake256FPub + | Codec::SlhDsaShake256SPub + | Codec::SlhDsaSha2128FPriv + | Codec::SlhDsaSha2128SPriv + | Codec::SlhDsaSha2192FPriv + | Codec::SlhDsaSha2192SPriv + | Codec::SlhDsaSha2256FPriv + | Codec::SlhDsaSha2256SPriv + | Codec::SlhDsaShake128FPriv + | Codec::SlhDsaShake128SPriv + | Codec::SlhDsaShake192FPriv + | Codec::SlhDsaShake192SPriv + | Codec::SlhDsaShake256FPriv + | Codec::SlhDsaShake256SPriv => Ok(Box::new(slh_dsa::View::try_from(mk)?)), + Codec::MlDsa65Pub | Codec::MlDsa65Priv | Codec::MlDsa87Pub | Codec::MlDsa87Priv => { + Ok(Box::new(ml_dsa::View::try_from(mk)?)) + } + Codec::Mayo1Pub + | Codec::Mayo1Priv + | Codec::Mayo2Pub + | Codec::Mayo2Priv + | Codec::Mayo3Pub + | Codec::Mayo3Priv + | Codec::Mayo5Pub + | Codec::Mayo5Priv => Ok(Box::new(mayo::View::try_from(mk)?)), + Codec::FnDsa512Pub | Codec::FnDsa512Priv | Codec::FnDsa1024Pub | Codec::FnDsa1024Priv => { + Ok(Box::new(fn_dsa::View::try_from(mk)?)) + } + Codec::Mlkem768Pub | Codec::Mlkem768Priv | Codec::Mlkem1024Pub | Codec::Mlkem1024Priv => { + Ok(Box::new(ml_kem::View::try_from(mk)?)) + } + Codec::Sntrup761Pub + | Codec::Sntrup761Priv + | Codec::Sntrup857Pub + | Codec::Sntrup857Priv + | Codec::Sntrup953Pub + | Codec::Sntrup953Priv + | Codec::Sntrup1013Pub + | Codec::Sntrup1013Priv + | Codec::Sntrup1277Pub + | Codec::Sntrup1277Priv => Ok(Box::new(sntrup::View::try_from(mk)?)), + #[cfg(feature = "deprecated")] + #[allow(deprecated)] + Codec::Mceliece348864Pub | Codec::Mceliece348864Priv => { + Ok(Box::new(classic_mceliece::View::try_from(mk)?)) + } + Codec::FrodoKem640AesPub + | Codec::FrodoKem640AesPriv + | Codec::FrodoKem976AesPub + | Codec::FrodoKem976AesPriv + | Codec::FrodoKem1344AesPub + | Codec::FrodoKem1344AesPriv + | Codec::FrodoKem640ShakePub + | Codec::FrodoKem640ShakePriv + | Codec::FrodoKem976ShakePub + | Codec::FrodoKem976ShakePriv + | Codec::FrodoKem1344ShakePub + | Codec::FrodoKem1344ShakePriv => Ok(Box::new(frodokem::View::try_from(mk)?)), + Codec::X25519Pub | Codec::X25519Priv => Ok(Box::new(x25519::View::try_from(mk)?)), + Codec::X25519Sntrup761Pub | Codec::X25519Sntrup761Priv => { + Ok(Box::new(x25519_sntrup761::View::try_from(mk)?)) + } + Codec::X25519Frodokem640AesPub + | Codec::X25519Frodokem640AesPriv + | Codec::X25519Frodokem640ShakePub + | Codec::X25519Frodokem640ShakePriv => { + Ok(Box::new(x25519_frodokem640::View::try_from(mk)?)) + } + #[cfg(feature = "deprecated")] + #[allow(deprecated)] + Codec::X25519Mceliece348864Pub | Codec::X25519Mceliece348864Priv => { + Ok(Box::new(x25519_mceliece348864::View::try_from(mk)?)) + } + Codec::X25519Mlkem768Pub | Codec::X25519Mlkem768Priv => { + Ok(Box::new(x25519_mlkem768::View::try_from(mk)?)) + } + Codec::Ed25519Mayo2Pub | Codec::Ed25519Mayo2Priv => { + Ok(Box::new(ed25519_mayo2::View::try_from(mk)?)) + } + Codec::Ed25519Mldsa65Pub | Codec::Ed25519Mldsa65Priv => { + Ok(Box::new(ed25519_mldsa65::View::try_from(mk)?)) + } + Codec::Bls12381G1Mldsa65Pub | Codec::Bls12381G1Mldsa65Priv => { + Ok(Box::new(bls12381_g1_mldsa65::View::try_from(mk)?)) + } + Codec::Bls12381G1Fndsa512Pub | Codec::Bls12381G1Fndsa512Priv => { + Ok(Box::new(bls12381_g1_fndsa512::View::try_from(mk)?)) + } + Codec::Bls12381G1Mayo1Pub | Codec::Bls12381G1Mayo1Priv => { + Ok(Box::new(bls12381_g1_mayo1::View::try_from(mk)?)) + } + Codec::Bls12381G1Mayo2Pub | Codec::Bls12381G1Mayo2Priv => { + Ok(Box::new(bls12381_g1_mayo2::View::try_from(mk)?)) + } + Codec::Ed25519Fndsa512Pub | Codec::Ed25519Fndsa512Priv => { + Ok(Box::new(ed25519_fndsa512::View::try_from(mk)?)) + } + Codec::P256Pub + | Codec::P256Priv + | Codec::P384Pub + | Codec::P384Priv + | Codec::P521Pub + | Codec::P521Priv => Ok(Box::new(nist_p::View::try_from(mk)?)), + Codec::Rsa2048Pub + | Codec::Rsa2048Priv + | Codec::Rsa3072Pub + | Codec::Rsa3072Priv + | Codec::Rsa4096Pub + | Codec::Rsa4096Priv => Ok(Box::new(rsa::View::try_from(mk)?)), + #[cfg(feature = "lamport")] + Codec::LamportSha3256Pub + | Codec::LamportSha3256Priv + | Codec::LamportSha3256PrivShare + | Codec::LamportSha3384Pub + | Codec::LamportSha3384Priv + | Codec::LamportSha3384PrivShare + | Codec::LamportSha3512Pub + | Codec::LamportSha3512Priv + | Codec::LamportSha3512PrivShare + | Codec::LamportSha2256Pub + | Codec::LamportSha2256Priv + | Codec::LamportSha2256PrivShare + | Codec::LamportSha2384Pub + | Codec::LamportSha2384Priv + | Codec::LamportSha2384PrivShare + | Codec::LamportSha2512Pub + | Codec::LamportSha2512Priv + | Codec::LamportSha2512PrivShare + | Codec::LamportBlake2B512Pub + | Codec::LamportBlake2B512Priv + | Codec::LamportBlake2B512PrivShare + | Codec::LamportBlake2S256Pub + | Codec::LamportBlake2S256Priv + | Codec::LamportBlake2S256PrivShare + | Codec::LamportBlake3256Pub + | Codec::LamportBlake3256Priv + | Codec::LamportBlake3256PrivShare + | Codec::LamportShake128Pub + | Codec::LamportShake128Priv + | Codec::LamportShake128PrivShare + | Codec::LamportShake256Pub + | Codec::LamportShake256Priv + | Codec::LamportShake256PrivShare => Ok(Box::new(lamport::View::try_from(mk)?)), + #[cfg(feature = "lamport")] + Codec::LamportMerkleSha3256Pub + | Codec::LamportMerkleSha3256Priv + | Codec::LamportMerkleSha3256PrivShare + | Codec::LamportMerkleSha3384Pub + | Codec::LamportMerkleSha3384Priv + | Codec::LamportMerkleSha3384PrivShare + | Codec::LamportMerkleSha3512Pub + | Codec::LamportMerkleSha3512Priv + | Codec::LamportMerkleSha3512PrivShare + | Codec::LamportMerkleSha2256Pub + | Codec::LamportMerkleSha2256Priv + | Codec::LamportMerkleSha2256PrivShare + | Codec::LamportMerkleSha2384Pub + | Codec::LamportMerkleSha2384Priv + | Codec::LamportMerkleSha2384PrivShare + | Codec::LamportMerkleSha2512Pub + | Codec::LamportMerkleSha2512Priv + | Codec::LamportMerkleSha2512PrivShare + | Codec::LamportMerkleBlake2B512Pub + | Codec::LamportMerkleBlake2B512Priv + | Codec::LamportMerkleBlake2B512PrivShare + | Codec::LamportMerkleBlake2S256Pub + | Codec::LamportMerkleBlake2S256Priv + | Codec::LamportMerkleBlake2S256PrivShare + | Codec::LamportMerkleBlake3256Pub + | Codec::LamportMerkleBlake3256Priv + | Codec::LamportMerkleBlake3256PrivShare + | Codec::LamportMerkleShake128Pub + | Codec::LamportMerkleShake128Priv + | Codec::LamportMerkleShake128PrivShare + | Codec::LamportMerkleShake256Pub + | Codec::LamportMerkleShake256Priv + | Codec::LamportMerkleShake256PrivShare => { + Ok(Box::new(lamport_merkle::View::try_from(mk)?)) + } + #[cfg(feature = "xmss")] + Codec::XmssSha210256Pub + | Codec::XmssSha210256Priv + | Codec::XmssSha216256Pub + | Codec::XmssSha216256Priv + | Codec::XmssSha220256Pub + | Codec::XmssSha220256Priv => Ok(Box::new(xmss::View::try_from(mk)?)), + _ => Err(ConversionsError::UnsupportedCodec(mk.codec).into()), + } +} +/// Builds the fingerprint view for the viewed Multikey. +pub(crate) fn dispatch_fingerprint_view<'a>( + mk: &'a Multikey, +) -> Result, Error> { + match mk.codec { + Codec::Bls12381G1PrivShare + | Codec::Bls12381G1Priv + | Codec::Bls12381G1Pub + | Codec::Bls12381G1PubShare + | Codec::Bls12381G2PrivShare + | Codec::Bls12381G2Priv + | Codec::Bls12381G2Pub + | Codec::Bls12381G2PubShare => Ok(Box::new(bls12381::View::try_from(mk)?)), + Codec::Ed25519Pub | Codec::Ed25519Priv => Ok(Box::new(ed25519::View::try_from(mk)?)), + Codec::Secp256K1Pub | Codec::Secp256K1Priv => Ok(Box::new(secp256k1::View::try_from(mk)?)), + Codec::Chacha20Poly1305 => Ok(Box::new(chacha20::View::try_from(mk)?)), + Codec::SlhDsaSha2128FPub + | Codec::SlhDsaSha2128SPub + | Codec::SlhDsaSha2192FPub + | Codec::SlhDsaSha2192SPub + | Codec::SlhDsaSha2256FPub + | Codec::SlhDsaSha2256SPub + | Codec::SlhDsaShake128FPub + | Codec::SlhDsaShake128SPub + | Codec::SlhDsaShake192FPub + | Codec::SlhDsaShake192SPub + | Codec::SlhDsaShake256FPub + | Codec::SlhDsaShake256SPub + | Codec::SlhDsaSha2128FPriv + | Codec::SlhDsaSha2128SPriv + | Codec::SlhDsaSha2192FPriv + | Codec::SlhDsaSha2192SPriv + | Codec::SlhDsaSha2256FPriv + | Codec::SlhDsaSha2256SPriv + | Codec::SlhDsaShake128FPriv + | Codec::SlhDsaShake128SPriv + | Codec::SlhDsaShake192FPriv + | Codec::SlhDsaShake192SPriv + | Codec::SlhDsaShake256FPriv + | Codec::SlhDsaShake256SPriv => Ok(Box::new(slh_dsa::View::try_from(mk)?)), + Codec::MlDsa65Pub | Codec::MlDsa65Priv | Codec::MlDsa87Pub | Codec::MlDsa87Priv => { + Ok(Box::new(ml_dsa::View::try_from(mk)?)) + } + Codec::Mayo1Pub + | Codec::Mayo1Priv + | Codec::Mayo2Pub + | Codec::Mayo2Priv + | Codec::Mayo3Pub + | Codec::Mayo3Priv + | Codec::Mayo5Pub + | Codec::Mayo5Priv => Ok(Box::new(mayo::View::try_from(mk)?)), + Codec::FnDsa512Pub | Codec::FnDsa512Priv | Codec::FnDsa1024Pub | Codec::FnDsa1024Priv => { + Ok(Box::new(fn_dsa::View::try_from(mk)?)) + } + Codec::Mlkem768Pub | Codec::Mlkem768Priv | Codec::Mlkem1024Pub | Codec::Mlkem1024Priv => { + Ok(Box::new(ml_kem::View::try_from(mk)?)) + } + Codec::Sntrup761Pub + | Codec::Sntrup761Priv + | Codec::Sntrup857Pub + | Codec::Sntrup857Priv + | Codec::Sntrup953Pub + | Codec::Sntrup953Priv + | Codec::Sntrup1013Pub + | Codec::Sntrup1013Priv + | Codec::Sntrup1277Pub + | Codec::Sntrup1277Priv => Ok(Box::new(sntrup::View::try_from(mk)?)), + #[cfg(feature = "deprecated")] + #[allow(deprecated)] + Codec::Mceliece348864Pub | Codec::Mceliece348864Priv => { + Ok(Box::new(classic_mceliece::View::try_from(mk)?)) + } + Codec::FrodoKem640AesPub + | Codec::FrodoKem640AesPriv + | Codec::FrodoKem976AesPub + | Codec::FrodoKem976AesPriv + | Codec::FrodoKem1344AesPub + | Codec::FrodoKem1344AesPriv + | Codec::FrodoKem640ShakePub + | Codec::FrodoKem640ShakePriv + | Codec::FrodoKem976ShakePub + | Codec::FrodoKem976ShakePriv + | Codec::FrodoKem1344ShakePub + | Codec::FrodoKem1344ShakePriv => Ok(Box::new(frodokem::View::try_from(mk)?)), + Codec::X25519Pub | Codec::X25519Priv => Ok(Box::new(x25519::View::try_from(mk)?)), + Codec::X25519Sntrup761Pub | Codec::X25519Sntrup761Priv => { + Ok(Box::new(x25519_sntrup761::View::try_from(mk)?)) + } + Codec::X25519Frodokem640AesPub + | Codec::X25519Frodokem640AesPriv + | Codec::X25519Frodokem640ShakePub + | Codec::X25519Frodokem640ShakePriv => { + Ok(Box::new(x25519_frodokem640::View::try_from(mk)?)) + } + #[cfg(feature = "deprecated")] + #[allow(deprecated)] + Codec::X25519Mceliece348864Pub | Codec::X25519Mceliece348864Priv => { + Ok(Box::new(x25519_mceliece348864::View::try_from(mk)?)) + } + Codec::X25519Mlkem768Pub | Codec::X25519Mlkem768Priv => { + Ok(Box::new(x25519_mlkem768::View::try_from(mk)?)) + } + Codec::Ed25519Mayo2Pub | Codec::Ed25519Mayo2Priv => { + Ok(Box::new(ed25519_mayo2::View::try_from(mk)?)) + } + Codec::Ed25519Mldsa65Pub | Codec::Ed25519Mldsa65Priv => { + Ok(Box::new(ed25519_mldsa65::View::try_from(mk)?)) + } + Codec::Bls12381G1Mldsa65Pub | Codec::Bls12381G1Mldsa65Priv => { + Ok(Box::new(bls12381_g1_mldsa65::View::try_from(mk)?)) + } + Codec::Bls12381G1Fndsa512Pub | Codec::Bls12381G1Fndsa512Priv => { + Ok(Box::new(bls12381_g1_fndsa512::View::try_from(mk)?)) + } + Codec::Bls12381G1Mayo1Pub | Codec::Bls12381G1Mayo1Priv => { + Ok(Box::new(bls12381_g1_mayo1::View::try_from(mk)?)) + } + Codec::Bls12381G1Mayo2Pub | Codec::Bls12381G1Mayo2Priv => { + Ok(Box::new(bls12381_g1_mayo2::View::try_from(mk)?)) + } + Codec::Ed25519Fndsa512Pub | Codec::Ed25519Fndsa512Priv => { + Ok(Box::new(ed25519_fndsa512::View::try_from(mk)?)) + } + Codec::P256Pub + | Codec::P256Priv + | Codec::P384Pub + | Codec::P384Priv + | Codec::P521Pub + | Codec::P521Priv => Ok(Box::new(nist_p::View::try_from(mk)?)), + Codec::Rsa2048Pub + | Codec::Rsa2048Priv + | Codec::Rsa3072Pub + | Codec::Rsa3072Priv + | Codec::Rsa4096Pub + | Codec::Rsa4096Priv => Ok(Box::new(rsa::View::try_from(mk)?)), + #[cfg(feature = "lamport")] + Codec::LamportSha3256Pub + | Codec::LamportSha3256Priv + | Codec::LamportSha3256PrivShare + | Codec::LamportSha3384Pub + | Codec::LamportSha3384Priv + | Codec::LamportSha3384PrivShare + | Codec::LamportSha3512Pub + | Codec::LamportSha3512Priv + | Codec::LamportSha3512PrivShare + | Codec::LamportSha2256Pub + | Codec::LamportSha2256Priv + | Codec::LamportSha2256PrivShare + | Codec::LamportSha2384Pub + | Codec::LamportSha2384Priv + | Codec::LamportSha2384PrivShare + | Codec::LamportSha2512Pub + | Codec::LamportSha2512Priv + | Codec::LamportSha2512PrivShare + | Codec::LamportBlake2B512Pub + | Codec::LamportBlake2B512Priv + | Codec::LamportBlake2B512PrivShare + | Codec::LamportBlake2S256Pub + | Codec::LamportBlake2S256Priv + | Codec::LamportBlake2S256PrivShare + | Codec::LamportBlake3256Pub + | Codec::LamportBlake3256Priv + | Codec::LamportBlake3256PrivShare + | Codec::LamportShake128Pub + | Codec::LamportShake128Priv + | Codec::LamportShake128PrivShare + | Codec::LamportShake256Pub + | Codec::LamportShake256Priv + | Codec::LamportShake256PrivShare => Ok(Box::new(lamport::View::try_from(mk)?)), + #[cfg(feature = "lamport")] + Codec::LamportMerkleSha3256Pub + | Codec::LamportMerkleSha3256Priv + | Codec::LamportMerkleSha3256PrivShare + | Codec::LamportMerkleSha3384Pub + | Codec::LamportMerkleSha3384Priv + | Codec::LamportMerkleSha3384PrivShare + | Codec::LamportMerkleSha3512Pub + | Codec::LamportMerkleSha3512Priv + | Codec::LamportMerkleSha3512PrivShare + | Codec::LamportMerkleSha2256Pub + | Codec::LamportMerkleSha2256Priv + | Codec::LamportMerkleSha2256PrivShare + | Codec::LamportMerkleSha2384Pub + | Codec::LamportMerkleSha2384Priv + | Codec::LamportMerkleSha2384PrivShare + | Codec::LamportMerkleSha2512Pub + | Codec::LamportMerkleSha2512Priv + | Codec::LamportMerkleSha2512PrivShare + | Codec::LamportMerkleBlake2B512Pub + | Codec::LamportMerkleBlake2B512Priv + | Codec::LamportMerkleBlake2B512PrivShare + | Codec::LamportMerkleBlake2S256Pub + | Codec::LamportMerkleBlake2S256Priv + | Codec::LamportMerkleBlake2S256PrivShare + | Codec::LamportMerkleBlake3256Pub + | Codec::LamportMerkleBlake3256Priv + | Codec::LamportMerkleBlake3256PrivShare + | Codec::LamportMerkleShake128Pub + | Codec::LamportMerkleShake128Priv + | Codec::LamportMerkleShake128PrivShare + | Codec::LamportMerkleShake256Pub + | Codec::LamportMerkleShake256Priv + | Codec::LamportMerkleShake256PrivShare => { + Ok(Box::new(lamport_merkle::View::try_from(mk)?)) + } + #[cfg(feature = "xmss")] + Codec::XmssSha210256Pub + | Codec::XmssSha210256Priv + | Codec::XmssSha216256Pub + | Codec::XmssSha216256Priv + | Codec::XmssSha220256Pub + | Codec::XmssSha220256Priv => Ok(Box::new(xmss::View::try_from(mk)?)), + _ => Err(ConversionsError::UnsupportedCodec(mk.codec).into()), + } +} +/// Builds the kdf view for the viewed Multikey using the kdf key. +pub(crate) fn dispatch_kdf_view<'a>( + mk: &'a Multikey, + kdf: &'a Multikey, +) -> Result, Error> { + match kdf.codec { + Codec::BcryptPbkdf => Ok(Box::new(bcrypt::View::new(mk, kdf))), + _ => Err(KdfError::UnsupportedCodec(kdf.codec).into()), + } +} +/// Builds the seal view for the viewed Multikey. +pub(crate) fn dispatch_seal_view<'a>(mk: &'a Multikey) -> Result, Error> { + match mk.codec { + Codec::Bls12381G1Pub + | Codec::Bls12381G1Priv + | Codec::Bls12381G2Pub + | Codec::Bls12381G2Priv => Ok(Box::new(bls12381::View::try_from(mk)?)), + Codec::Mlkem768Pub | Codec::Mlkem768Priv | Codec::Mlkem1024Pub | Codec::Mlkem1024Priv => { + Ok(Box::new(ml_kem::View::try_from(mk)?)) + } + Codec::Sntrup761Pub + | Codec::Sntrup761Priv + | Codec::Sntrup857Pub + | Codec::Sntrup857Priv + | Codec::Sntrup953Pub + | Codec::Sntrup953Priv + | Codec::Sntrup1013Pub + | Codec::Sntrup1013Priv + | Codec::Sntrup1277Pub + | Codec::Sntrup1277Priv => Ok(Box::new(sntrup::View::try_from(mk)?)), + #[cfg(feature = "deprecated")] + #[allow(deprecated)] + Codec::Mceliece348864Pub | Codec::Mceliece348864Priv => { + Ok(Box::new(classic_mceliece::View::try_from(mk)?)) + } + Codec::FrodoKem640AesPub + | Codec::FrodoKem640AesPriv + | Codec::FrodoKem976AesPub + | Codec::FrodoKem976AesPriv + | Codec::FrodoKem1344AesPub + | Codec::FrodoKem1344AesPriv + | Codec::FrodoKem640ShakePub + | Codec::FrodoKem640ShakePriv + | Codec::FrodoKem976ShakePub + | Codec::FrodoKem976ShakePriv + | Codec::FrodoKem1344ShakePub + | Codec::FrodoKem1344ShakePriv => Ok(Box::new(frodokem::View::try_from(mk)?)), + Codec::X25519Pub | Codec::X25519Priv => Ok(Box::new(x25519::View::try_from(mk)?)), + Codec::X25519Sntrup761Pub | Codec::X25519Sntrup761Priv => { + Ok(Box::new(x25519_sntrup761::View::try_from(mk)?)) + } + Codec::X25519Frodokem640AesPub + | Codec::X25519Frodokem640AesPriv + | Codec::X25519Frodokem640ShakePub + | Codec::X25519Frodokem640ShakePriv => { + Ok(Box::new(x25519_frodokem640::View::try_from(mk)?)) + } + #[cfg(feature = "deprecated")] + #[allow(deprecated)] + Codec::X25519Mceliece348864Pub | Codec::X25519Mceliece348864Priv => { + Ok(Box::new(x25519_mceliece348864::View::try_from(mk)?)) + } + Codec::X25519Mlkem768Pub | Codec::X25519Mlkem768Priv => { + Ok(Box::new(x25519_mlkem768::View::try_from(mk)?)) + } + Codec::Rsa2048Pub + | Codec::Rsa2048Priv + | Codec::Rsa3072Pub + | Codec::Rsa3072Priv + | Codec::Rsa4096Pub + | Codec::Rsa4096Priv => Ok(Box::new(rsa::View::try_from(mk)?)), + Codec::P256Pub + | Codec::P256Priv + | Codec::P384Pub + | Codec::P384Priv + | Codec::P521Pub + | Codec::P521Priv => Ok(Box::new(nist_p::View::try_from(mk)?)), + Codec::Secp256K1Pub | Codec::Secp256K1Priv => Ok(Box::new(secp256k1::View::try_from(mk)?)), + _ => Err(SealError::NotEncryptionKey.into()), + } +} +/// Builds the open view for the viewed Multikey. +pub(crate) fn dispatch_open_view<'a>(mk: &'a Multikey) -> Result, Error> { + match mk.codec { + Codec::Bls12381G1Pub + | Codec::Bls12381G1Priv + | Codec::Bls12381G2Pub + | Codec::Bls12381G2Priv => Ok(Box::new(bls12381::View::try_from(mk)?)), + Codec::Mlkem768Pub | Codec::Mlkem768Priv | Codec::Mlkem1024Pub | Codec::Mlkem1024Priv => { + Ok(Box::new(ml_kem::View::try_from(mk)?)) + } + Codec::Sntrup761Pub + | Codec::Sntrup761Priv + | Codec::Sntrup857Pub + | Codec::Sntrup857Priv + | Codec::Sntrup953Pub + | Codec::Sntrup953Priv + | Codec::Sntrup1013Pub + | Codec::Sntrup1013Priv + | Codec::Sntrup1277Pub + | Codec::Sntrup1277Priv => Ok(Box::new(sntrup::View::try_from(mk)?)), + #[cfg(feature = "deprecated")] + #[allow(deprecated)] + Codec::Mceliece348864Pub | Codec::Mceliece348864Priv => { + Ok(Box::new(classic_mceliece::View::try_from(mk)?)) + } + Codec::FrodoKem640AesPub + | Codec::FrodoKem640AesPriv + | Codec::FrodoKem976AesPub + | Codec::FrodoKem976AesPriv + | Codec::FrodoKem1344AesPub + | Codec::FrodoKem1344AesPriv + | Codec::FrodoKem640ShakePub + | Codec::FrodoKem640ShakePriv + | Codec::FrodoKem976ShakePub + | Codec::FrodoKem976ShakePriv + | Codec::FrodoKem1344ShakePub + | Codec::FrodoKem1344ShakePriv => Ok(Box::new(frodokem::View::try_from(mk)?)), + Codec::X25519Pub | Codec::X25519Priv => Ok(Box::new(x25519::View::try_from(mk)?)), + Codec::X25519Sntrup761Pub | Codec::X25519Sntrup761Priv => { + Ok(Box::new(x25519_sntrup761::View::try_from(mk)?)) + } + Codec::X25519Frodokem640AesPub + | Codec::X25519Frodokem640AesPriv + | Codec::X25519Frodokem640ShakePub + | Codec::X25519Frodokem640ShakePriv => { + Ok(Box::new(x25519_frodokem640::View::try_from(mk)?)) + } + #[cfg(feature = "deprecated")] + #[allow(deprecated)] + Codec::X25519Mceliece348864Pub | Codec::X25519Mceliece348864Priv => { + Ok(Box::new(x25519_mceliece348864::View::try_from(mk)?)) + } + Codec::X25519Mlkem768Pub | Codec::X25519Mlkem768Priv => { + Ok(Box::new(x25519_mlkem768::View::try_from(mk)?)) + } + Codec::Rsa2048Pub + | Codec::Rsa2048Priv + | Codec::Rsa3072Pub + | Codec::Rsa3072Priv + | Codec::Rsa4096Pub + | Codec::Rsa4096Priv => Ok(Box::new(rsa::View::try_from(mk)?)), + Codec::P256Pub + | Codec::P256Priv + | Codec::P384Pub + | Codec::P384Priv + | Codec::P521Pub + | Codec::P521Priv => Ok(Box::new(nist_p::View::try_from(mk)?)), + Codec::Secp256K1Pub | Codec::Secp256K1Priv => Ok(Box::new(secp256k1::View::try_from(mk)?)), + _ => Err(SealError::NotEncryptionKey.into()), + } +} +/// Builds the signature view for the viewed Multikey. +pub(crate) fn dispatch_sign_view<'a>(mk: &'a Multikey) -> Result, Error> { + match mk.codec { + Codec::Bls12381G1PrivShare + | Codec::Bls12381G1Priv + | Codec::Bls12381G1Pub + | Codec::Bls12381G1PubShare + | Codec::Bls12381G2PrivShare + | Codec::Bls12381G2Priv + | Codec::Bls12381G2Pub + | Codec::Bls12381G2PubShare => Ok(Box::new(bls12381::View::try_from(mk)?)), + Codec::Ed25519Pub | Codec::Ed25519Priv => Ok(Box::new(ed25519::View::try_from(mk)?)), + Codec::Secp256K1Pub | Codec::Secp256K1Priv => Ok(Box::new(secp256k1::View::try_from(mk)?)), + Codec::SlhDsaSha2128FPub + | Codec::SlhDsaSha2128SPub + | Codec::SlhDsaSha2192FPub + | Codec::SlhDsaSha2192SPub + | Codec::SlhDsaSha2256FPub + | Codec::SlhDsaSha2256SPub + | Codec::SlhDsaShake128FPub + | Codec::SlhDsaShake128SPub + | Codec::SlhDsaShake192FPub + | Codec::SlhDsaShake192SPub + | Codec::SlhDsaShake256FPub + | Codec::SlhDsaShake256SPub + | Codec::SlhDsaSha2128FPriv + | Codec::SlhDsaSha2128SPriv + | Codec::SlhDsaSha2192FPriv + | Codec::SlhDsaSha2192SPriv + | Codec::SlhDsaSha2256FPriv + | Codec::SlhDsaSha2256SPriv + | Codec::SlhDsaShake128FPriv + | Codec::SlhDsaShake128SPriv + | Codec::SlhDsaShake192FPriv + | Codec::SlhDsaShake192SPriv + | Codec::SlhDsaShake256FPriv + | Codec::SlhDsaShake256SPriv => Ok(Box::new(slh_dsa::View::try_from(mk)?)), + Codec::MlDsa65Pub | Codec::MlDsa65Priv | Codec::MlDsa87Pub | Codec::MlDsa87Priv => { + Ok(Box::new(ml_dsa::View::try_from(mk)?)) + } + Codec::Mayo1Pub + | Codec::Mayo1Priv + | Codec::Mayo2Pub + | Codec::Mayo2Priv + | Codec::Mayo3Pub + | Codec::Mayo3Priv + | Codec::Mayo5Pub + | Codec::Mayo5Priv => Ok(Box::new(mayo::View::try_from(mk)?)), + Codec::FnDsa512Pub | Codec::FnDsa512Priv | Codec::FnDsa1024Pub | Codec::FnDsa1024Priv => { + Ok(Box::new(fn_dsa::View::try_from(mk)?)) + } + Codec::Ed25519Mayo2Pub | Codec::Ed25519Mayo2Priv => { + Ok(Box::new(ed25519_mayo2::View::try_from(mk)?)) + } + Codec::Ed25519Mldsa65Pub | Codec::Ed25519Mldsa65Priv => { + Ok(Box::new(ed25519_mldsa65::View::try_from(mk)?)) + } + Codec::Bls12381G1Mldsa65Pub | Codec::Bls12381G1Mldsa65Priv => { + Ok(Box::new(bls12381_g1_mldsa65::View::try_from(mk)?)) + } + Codec::Bls12381G1Fndsa512Pub | Codec::Bls12381G1Fndsa512Priv => { + Ok(Box::new(bls12381_g1_fndsa512::View::try_from(mk)?)) + } + Codec::Bls12381G1Mayo1Pub | Codec::Bls12381G1Mayo1Priv => { + Ok(Box::new(bls12381_g1_mayo1::View::try_from(mk)?)) + } + Codec::Bls12381G1Mayo2Pub | Codec::Bls12381G1Mayo2Priv => { + Ok(Box::new(bls12381_g1_mayo2::View::try_from(mk)?)) + } + Codec::Ed25519Fndsa512Pub | Codec::Ed25519Fndsa512Priv => { + Ok(Box::new(ed25519_fndsa512::View::try_from(mk)?)) + } + Codec::P256Pub + | Codec::P256Priv + | Codec::P384Pub + | Codec::P384Priv + | Codec::P521Pub + | Codec::P521Priv => Ok(Box::new(nist_p::View::try_from(mk)?)), + Codec::Rsa2048Pub + | Codec::Rsa2048Priv + | Codec::Rsa3072Pub + | Codec::Rsa3072Priv + | Codec::Rsa4096Pub + | Codec::Rsa4096Priv => Ok(Box::new(rsa::View::try_from(mk)?)), + #[cfg(feature = "lamport")] + Codec::LamportSha3256Pub + | Codec::LamportSha3256Priv + | Codec::LamportSha3256PrivShare + | Codec::LamportSha3384Pub + | Codec::LamportSha3384Priv + | Codec::LamportSha3384PrivShare + | Codec::LamportSha3512Pub + | Codec::LamportSha3512Priv + | Codec::LamportSha3512PrivShare + | Codec::LamportSha2256Pub + | Codec::LamportSha2256Priv + | Codec::LamportSha2256PrivShare + | Codec::LamportSha2384Pub + | Codec::LamportSha2384Priv + | Codec::LamportSha2384PrivShare + | Codec::LamportSha2512Pub + | Codec::LamportSha2512Priv + | Codec::LamportSha2512PrivShare + | Codec::LamportBlake2B512Pub + | Codec::LamportBlake2B512Priv + | Codec::LamportBlake2B512PrivShare + | Codec::LamportBlake2S256Pub + | Codec::LamportBlake2S256Priv + | Codec::LamportBlake2S256PrivShare + | Codec::LamportBlake3256Pub + | Codec::LamportBlake3256Priv + | Codec::LamportBlake3256PrivShare + | Codec::LamportShake128Pub + | Codec::LamportShake128Priv + | Codec::LamportShake128PrivShare + | Codec::LamportShake256Pub + | Codec::LamportShake256Priv + | Codec::LamportShake256PrivShare => Ok(Box::new(lamport::View::try_from(mk)?)), + #[cfg(feature = "lamport")] + Codec::LamportMerkleSha3256Pub + | Codec::LamportMerkleSha3256Priv + | Codec::LamportMerkleSha3256PrivShare + | Codec::LamportMerkleSha3384Pub + | Codec::LamportMerkleSha3384Priv + | Codec::LamportMerkleSha3384PrivShare + | Codec::LamportMerkleSha3512Pub + | Codec::LamportMerkleSha3512Priv + | Codec::LamportMerkleSha3512PrivShare + | Codec::LamportMerkleSha2256Pub + | Codec::LamportMerkleSha2256Priv + | Codec::LamportMerkleSha2256PrivShare + | Codec::LamportMerkleSha2384Pub + | Codec::LamportMerkleSha2384Priv + | Codec::LamportMerkleSha2384PrivShare + | Codec::LamportMerkleSha2512Pub + | Codec::LamportMerkleSha2512Priv + | Codec::LamportMerkleSha2512PrivShare + | Codec::LamportMerkleBlake2B512Pub + | Codec::LamportMerkleBlake2B512Priv + | Codec::LamportMerkleBlake2B512PrivShare + | Codec::LamportMerkleBlake2S256Pub + | Codec::LamportMerkleBlake2S256Priv + | Codec::LamportMerkleBlake2S256PrivShare + | Codec::LamportMerkleBlake3256Pub + | Codec::LamportMerkleBlake3256Priv + | Codec::LamportMerkleBlake3256PrivShare + | Codec::LamportMerkleShake128Pub + | Codec::LamportMerkleShake128Priv + | Codec::LamportMerkleShake128PrivShare + | Codec::LamportMerkleShake256Pub + | Codec::LamportMerkleShake256Priv + | Codec::LamportMerkleShake256PrivShare => { + Ok(Box::new(lamport_merkle::View::try_from(mk)?)) + } + #[cfg(feature = "xmss")] + Codec::XmssSha210256Pub + | Codec::XmssSha210256Priv + | Codec::XmssSha216256Pub + | Codec::XmssSha216256Priv + | Codec::XmssSha220256Pub + | Codec::XmssSha220256Priv => Ok(Box::new(xmss::View::try_from(mk)?)), + Codec::X25519Pub | Codec::X25519Priv => Ok(Box::new(xeddsa::View::try_from(mk)?)), + _ => Err(ConversionsError::UnsupportedCodec(mk.codec).into()), + } +} +/// Builds the threshold view for the viewed Multikey. +pub(crate) fn dispatch_threshold_view<'a>( + mk: &'a Multikey, +) -> Result, Error> { + match mk.codec { + Codec::Bls12381G1Priv | Codec::Bls12381G2Priv => { + Ok(Box::new(bls12381::View::try_from(mk)?)) + } + #[cfg(feature = "lamport")] + Codec::LamportSha3256Pub + | Codec::LamportSha3256Priv + | Codec::LamportSha3256PrivShare + | Codec::LamportSha3384Pub + | Codec::LamportSha3384Priv + | Codec::LamportSha3384PrivShare + | Codec::LamportSha3512Pub + | Codec::LamportSha3512Priv + | Codec::LamportSha3512PrivShare + | Codec::LamportSha2256Pub + | Codec::LamportSha2256Priv + | Codec::LamportSha2256PrivShare + | Codec::LamportSha2384Pub + | Codec::LamportSha2384Priv + | Codec::LamportSha2384PrivShare + | Codec::LamportSha2512Pub + | Codec::LamportSha2512Priv + | Codec::LamportSha2512PrivShare + | Codec::LamportBlake2B512Pub + | Codec::LamportBlake2B512Priv + | Codec::LamportBlake2B512PrivShare + | Codec::LamportBlake2S256Pub + | Codec::LamportBlake2S256Priv + | Codec::LamportBlake2S256PrivShare + | Codec::LamportBlake3256Pub + | Codec::LamportBlake3256Priv + | Codec::LamportBlake3256PrivShare + | Codec::LamportShake128Pub + | Codec::LamportShake128Priv + | Codec::LamportShake128PrivShare + | Codec::LamportShake256Pub + | Codec::LamportShake256Priv + | Codec::LamportShake256PrivShare => Ok(Box::new(lamport::View::try_from(mk)?)), + #[cfg(feature = "lamport")] + Codec::LamportMerkleSha3256Pub + | Codec::LamportMerkleSha3256Priv + | Codec::LamportMerkleSha3256PrivShare + | Codec::LamportMerkleSha3384Pub + | Codec::LamportMerkleSha3384Priv + | Codec::LamportMerkleSha3384PrivShare + | Codec::LamportMerkleSha3512Pub + | Codec::LamportMerkleSha3512Priv + | Codec::LamportMerkleSha3512PrivShare + | Codec::LamportMerkleSha2256Pub + | Codec::LamportMerkleSha2256Priv + | Codec::LamportMerkleSha2256PrivShare + | Codec::LamportMerkleSha2384Pub + | Codec::LamportMerkleSha2384Priv + | Codec::LamportMerkleSha2384PrivShare + | Codec::LamportMerkleSha2512Pub + | Codec::LamportMerkleSha2512Priv + | Codec::LamportMerkleSha2512PrivShare + | Codec::LamportMerkleBlake2B512Pub + | Codec::LamportMerkleBlake2B512Priv + | Codec::LamportMerkleBlake2B512PrivShare + | Codec::LamportMerkleBlake2S256Pub + | Codec::LamportMerkleBlake2S256Priv + | Codec::LamportMerkleBlake2S256PrivShare + | Codec::LamportMerkleBlake3256Pub + | Codec::LamportMerkleBlake3256Priv + | Codec::LamportMerkleBlake3256PrivShare + | Codec::LamportMerkleShake128Pub + | Codec::LamportMerkleShake128Priv + | Codec::LamportMerkleShake128PrivShare + | Codec::LamportMerkleShake256Pub + | Codec::LamportMerkleShake256Priv + | Codec::LamportMerkleShake256PrivShare => { + Ok(Box::new(lamport_merkle::View::try_from(mk)?)) + } + _ => Err(ConversionsError::UnsupportedCodec(mk.codec).into()), + } +} +/// Builds the verification view for the viewed Multikey. +pub(crate) fn dispatch_verify_view<'a>( + mk: &'a Multikey, +) -> Result, Error> { + match mk.codec { + Codec::Bls12381G1PrivShare + | Codec::Bls12381G1Priv + | Codec::Bls12381G1Pub + | Codec::Bls12381G1PubShare + | Codec::Bls12381G2PrivShare + | Codec::Bls12381G2Priv + | Codec::Bls12381G2Pub + | Codec::Bls12381G2PubShare => Ok(Box::new(bls12381::View::try_from(mk)?)), + Codec::Ed25519Pub | Codec::Ed25519Priv => Ok(Box::new(ed25519::View::try_from(mk)?)), + Codec::Secp256K1Pub | Codec::Secp256K1Priv => Ok(Box::new(secp256k1::View::try_from(mk)?)), + Codec::SlhDsaSha2128FPub + | Codec::SlhDsaSha2128SPub + | Codec::SlhDsaSha2192FPub + | Codec::SlhDsaSha2192SPub + | Codec::SlhDsaSha2256FPub + | Codec::SlhDsaSha2256SPub + | Codec::SlhDsaShake128FPub + | Codec::SlhDsaShake128SPub + | Codec::SlhDsaShake192FPub + | Codec::SlhDsaShake192SPub + | Codec::SlhDsaShake256FPub + | Codec::SlhDsaShake256SPub + | Codec::SlhDsaSha2128FPriv + | Codec::SlhDsaSha2128SPriv + | Codec::SlhDsaSha2192FPriv + | Codec::SlhDsaSha2192SPriv + | Codec::SlhDsaSha2256FPriv + | Codec::SlhDsaSha2256SPriv + | Codec::SlhDsaShake128FPriv + | Codec::SlhDsaShake128SPriv + | Codec::SlhDsaShake192FPriv + | Codec::SlhDsaShake192SPriv + | Codec::SlhDsaShake256FPriv + | Codec::SlhDsaShake256SPriv => Ok(Box::new(slh_dsa::View::try_from(mk)?)), + Codec::MlDsa65Pub | Codec::MlDsa65Priv | Codec::MlDsa87Pub | Codec::MlDsa87Priv => { + Ok(Box::new(ml_dsa::View::try_from(mk)?)) + } + Codec::Mayo1Pub + | Codec::Mayo1Priv + | Codec::Mayo2Pub + | Codec::Mayo2Priv + | Codec::Mayo3Pub + | Codec::Mayo3Priv + | Codec::Mayo5Pub + | Codec::Mayo5Priv => Ok(Box::new(mayo::View::try_from(mk)?)), + Codec::FnDsa512Pub | Codec::FnDsa512Priv | Codec::FnDsa1024Pub | Codec::FnDsa1024Priv => { + Ok(Box::new(fn_dsa::View::try_from(mk)?)) + } + Codec::Ed25519Mayo2Pub | Codec::Ed25519Mayo2Priv => { + Ok(Box::new(ed25519_mayo2::View::try_from(mk)?)) + } + Codec::Ed25519Mldsa65Pub | Codec::Ed25519Mldsa65Priv => { + Ok(Box::new(ed25519_mldsa65::View::try_from(mk)?)) + } + Codec::Bls12381G1Mldsa65Pub | Codec::Bls12381G1Mldsa65Priv => { + Ok(Box::new(bls12381_g1_mldsa65::View::try_from(mk)?)) + } + Codec::Bls12381G1Fndsa512Pub | Codec::Bls12381G1Fndsa512Priv => { + Ok(Box::new(bls12381_g1_fndsa512::View::try_from(mk)?)) + } + Codec::Bls12381G1Mayo1Pub | Codec::Bls12381G1Mayo1Priv => { + Ok(Box::new(bls12381_g1_mayo1::View::try_from(mk)?)) + } + Codec::Bls12381G1Mayo2Pub | Codec::Bls12381G1Mayo2Priv => { + Ok(Box::new(bls12381_g1_mayo2::View::try_from(mk)?)) + } + Codec::Ed25519Fndsa512Pub | Codec::Ed25519Fndsa512Priv => { + Ok(Box::new(ed25519_fndsa512::View::try_from(mk)?)) + } + Codec::P256Pub + | Codec::P256Priv + | Codec::P384Pub + | Codec::P384Priv + | Codec::P521Pub + | Codec::P521Priv => Ok(Box::new(nist_p::View::try_from(mk)?)), + Codec::Rsa2048Pub + | Codec::Rsa2048Priv + | Codec::Rsa3072Pub + | Codec::Rsa3072Priv + | Codec::Rsa4096Pub + | Codec::Rsa4096Priv => Ok(Box::new(rsa::View::try_from(mk)?)), + #[cfg(feature = "lamport")] + Codec::LamportSha3256Pub + | Codec::LamportSha3256Priv + | Codec::LamportSha3256PrivShare + | Codec::LamportSha3384Pub + | Codec::LamportSha3384Priv + | Codec::LamportSha3384PrivShare + | Codec::LamportSha3512Pub + | Codec::LamportSha3512Priv + | Codec::LamportSha3512PrivShare + | Codec::LamportSha2256Pub + | Codec::LamportSha2256Priv + | Codec::LamportSha2256PrivShare + | Codec::LamportSha2384Pub + | Codec::LamportSha2384Priv + | Codec::LamportSha2384PrivShare + | Codec::LamportSha2512Pub + | Codec::LamportSha2512Priv + | Codec::LamportSha2512PrivShare + | Codec::LamportBlake2B512Pub + | Codec::LamportBlake2B512Priv + | Codec::LamportBlake2B512PrivShare + | Codec::LamportBlake2S256Pub + | Codec::LamportBlake2S256Priv + | Codec::LamportBlake2S256PrivShare + | Codec::LamportBlake3256Pub + | Codec::LamportBlake3256Priv + | Codec::LamportBlake3256PrivShare + | Codec::LamportShake128Pub + | Codec::LamportShake128Priv + | Codec::LamportShake128PrivShare + | Codec::LamportShake256Pub + | Codec::LamportShake256Priv + | Codec::LamportShake256PrivShare => Ok(Box::new(lamport::View::try_from(mk)?)), + #[cfg(feature = "lamport")] + Codec::LamportMerkleSha3256Pub + | Codec::LamportMerkleSha3256Priv + | Codec::LamportMerkleSha3256PrivShare + | Codec::LamportMerkleSha3384Pub + | Codec::LamportMerkleSha3384Priv + | Codec::LamportMerkleSha3384PrivShare + | Codec::LamportMerkleSha3512Pub + | Codec::LamportMerkleSha3512Priv + | Codec::LamportMerkleSha3512PrivShare + | Codec::LamportMerkleSha2256Pub + | Codec::LamportMerkleSha2256Priv + | Codec::LamportMerkleSha2256PrivShare + | Codec::LamportMerkleSha2384Pub + | Codec::LamportMerkleSha2384Priv + | Codec::LamportMerkleSha2384PrivShare + | Codec::LamportMerkleSha2512Pub + | Codec::LamportMerkleSha2512Priv + | Codec::LamportMerkleSha2512PrivShare + | Codec::LamportMerkleBlake2B512Pub + | Codec::LamportMerkleBlake2B512Priv + | Codec::LamportMerkleBlake2B512PrivShare + | Codec::LamportMerkleBlake2S256Pub + | Codec::LamportMerkleBlake2S256Priv + | Codec::LamportMerkleBlake2S256PrivShare + | Codec::LamportMerkleBlake3256Pub + | Codec::LamportMerkleBlake3256Priv + | Codec::LamportMerkleBlake3256PrivShare + | Codec::LamportMerkleShake128Pub + | Codec::LamportMerkleShake128Priv + | Codec::LamportMerkleShake128PrivShare + | Codec::LamportMerkleShake256Pub + | Codec::LamportMerkleShake256Priv + | Codec::LamportMerkleShake256PrivShare => { + Ok(Box::new(lamport_merkle::View::try_from(mk)?)) + } + #[cfg(feature = "xmss")] + Codec::XmssSha210256Pub + | Codec::XmssSha210256Priv + | Codec::XmssSha216256Pub + | Codec::XmssSha216256Priv + | Codec::XmssSha220256Pub + | Codec::XmssSha220256Priv => Ok(Box::new(xmss::View::try_from(mk)?)), + Codec::X25519Pub | Codec::X25519Priv => Ok(Box::new(xeddsa::View::try_from(mk)?)), + _ => Err(ConversionsError::UnsupportedCodec(mk.codec).into()), + } +} +/// Builds the threshold-disclosure view for the viewed Multikey. +/// +/// Construction cannot fail: the built-in disclosure view applies to every +/// codec. +pub(crate) fn dispatch_disclosure_view<'a>( + mk: &'a Multikey, +) -> Box { + Box::new(threshold_meta::DisclosureView::new(mk)) +} +/// Builds the merkle-tree state view for the viewed Multikey. +pub(crate) fn dispatch_merkle_state_view<'a>( + mk: &'a Multikey, +) -> Result, Error> { + #[cfg(feature = "lamport")] + match mk.codec { + Codec::LamportMerkleSha3256Pub + | Codec::LamportMerkleSha3256Priv + | Codec::LamportMerkleSha3384Pub + | Codec::LamportMerkleSha3384Priv + | Codec::LamportMerkleSha3512Pub + | Codec::LamportMerkleSha3512Priv + | Codec::LamportMerkleSha2256Pub + | Codec::LamportMerkleSha2256Priv + | Codec::LamportMerkleSha2384Pub + | Codec::LamportMerkleSha2384Priv + | Codec::LamportMerkleSha2512Pub + | Codec::LamportMerkleSha2512Priv + | Codec::LamportMerkleBlake2B512Pub + | Codec::LamportMerkleBlake2B512Priv + | Codec::LamportMerkleBlake2S256Pub + | Codec::LamportMerkleBlake2S256Priv + | Codec::LamportMerkleBlake3256Pub + | Codec::LamportMerkleBlake3256Priv + | Codec::LamportMerkleShake128Pub + | Codec::LamportMerkleShake128Priv + | Codec::LamportMerkleShake256Pub + | Codec::LamportMerkleShake256Priv => Ok(Box::new(lamport_merkle::View::try_from(mk)?)), + _ => Err(ConversionsError::UnsupportedCodec(mk.codec).into()), + } + #[cfg(not(feature = "lamport"))] + Err(ConversionsError::UnsupportedCodec(mk.codec).into()) +} diff --git a/src/views/ed25519.rs b/src/views/ed25519.rs index 7d37d85..b30010a 100644 --- a/src/views/ed25519.rs +++ b/src/views/ed25519.rs @@ -1,7 +1,10 @@ // SPDX-License-Identifier: Apache-2.0 +use crate::views::dispatch::{ + dispatch_attr_view, dispatch_conv_view, dispatch_data_view, dispatch_fingerprint_view, +}; use crate::{ AttrId, AttrView, Builder, CipherAttrView, ConvView, DataView, Error, FingerprintView, - KdfAttrView, Multikey, SignView, VerifyView, Views, + KdfAttrView, Multikey, SignView, VerifyView, error::{AttributesError, CipherError, ConversionsError, KdfError, SignError, VerifyError}, }; use ed25519_dalek::{ @@ -137,19 +140,19 @@ impl<'a> KdfAttrView for View<'a> { impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if attr.is_secret_key() { // convert to a public key Multikey let pk = self.to_public_key()?; // get a conversions view on the public key - let fp = pk.fingerprint_view()?; + let fp = dispatch_fingerprint_view(&pk)?; // get the fingerprint let f = fp.fingerprint(codec)?; Ok(f) } else { // get the key bytes let bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.key_bytes()? }; @@ -164,7 +167,7 @@ impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { // get the secret key bytes let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -192,7 +195,7 @@ impl<'a> ConvView for View<'a> { } let key_bytes = { - let kd = pk.data_view()?; + let kd = dispatch_data_view(&pk)?; kd.key_bytes()? }; @@ -213,7 +216,7 @@ impl<'a> ConvView for View<'a> { /// try to convert a Multikey to an ssh_key::PrivateKey fn to_ssh_private_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -226,7 +229,7 @@ impl<'a> ConvView for View<'a> { })?; let pk = self.to_public_key()?; - let data = pk.data_view()?; + let data = dispatch_data_view(&pk)?; let public_bytes: [u8; PUBLIC_KEY_LENGTH] = data.key_bytes()?.as_slice() [..PUBLIC_KEY_LENGTH] .try_into() @@ -248,14 +251,14 @@ impl<'a> ConvView for View<'a> { impl<'a> SignView for View<'a> { /// try to create a Multisig by siging the passed-in data with the Multikey fn sign(&self, msg: &[u8], combined: bool, _scheme: Option) -> Result { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if !attr.is_secret_key() { return Err(SignError::NotSigningKey.into()); } // get the secret key bytes let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -287,9 +290,9 @@ impl<'a> SignView for View<'a> { impl<'a> VerifyView for View<'a> { /// try to verify a Multisig using the Multikey fn verify(&self, multisig: &Multisig, msg: Option<&[u8]>) -> Result<(), Error> { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; let pubmk = if attr.is_secret_key() { - let kc = self.mk.conv_view()?; + let kc = dispatch_conv_view(self.mk)?; kc.to_public_key()? } else { @@ -298,7 +301,7 @@ impl<'a> VerifyView for View<'a> { // get the secret key bytes let key_bytes = { - let kd = pubmk.data_view()?; + let kd = dispatch_data_view(&pubmk)?; kd.key_bytes()? }; diff --git a/src/views/ed25519_fndsa512.rs b/src/views/ed25519_fndsa512.rs index 3625eb2..a1a0f13 100644 --- a/src/views/ed25519_fndsa512.rs +++ b/src/views/ed25519_fndsa512.rs @@ -5,11 +5,11 @@ //! Secret layout: ed25519_seed (32) || fn_dsa_signing_key (sign_key_size). //! Public encoding is classical-first: ed25519_pub (32) || fn_dsa_verifying_key. +use crate::views::dispatch::{dispatch_attr_view, dispatch_conv_view, dispatch_data_view}; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, SignView, VerifyView, error::{AttributesError, ConversionsError, SignError, VerifyError}, - views::Views, }; use ed25519_dalek::{Signature as Ed25519Sig, SigningKey, VerifyingKey}; use fn_dsa::{ @@ -74,7 +74,7 @@ impl<'a> DataView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -127,7 +127,7 @@ impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { let pub_bytes = if self.is_secret_key() { let pk = self.to_public_key()?; - let dv = pk.data_view()?; + let dv = dispatch_data_view(&pk)?; dv.key_bytes()? } else { self.key_bytes()? @@ -138,13 +138,13 @@ impl<'a> FingerprintView for View<'a> { impl<'a> SignView for View<'a> { fn sign(&self, msg: &[u8], combined: bool, _scheme: Option) -> Result { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if !attr.is_secret_key() { return Err(SignError::NotSigningKey.into()); } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -202,16 +202,16 @@ impl<'a> VerifyView for View<'a> { return Err(VerifyError::MissingMessage.into()); }; - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; let pubmk = if attr.is_secret_key() { - let kc = self.mk.conv_view()?; + let kc = dispatch_conv_view(self.mk)?; kc.to_public_key()? } else { self.mk.clone() }; let key_bytes = { - let kd = pubmk.data_view()?; + let kd = dispatch_data_view(&pubmk)?; kd.key_bytes()? }; @@ -269,7 +269,7 @@ impl<'a> VerifyView for View<'a> { #[cfg(test)] mod tests { use super::*; - use crate::views::Views; + use crate::ViewBuilder; #[test] fn test_sign_verify_roundtrip() { @@ -278,15 +278,32 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let msg = b"hello Ed25519-FN-DSA-512 hybrid signing!"; - let sig = sk.sign_view().unwrap().sign(msg, false, None).unwrap(); - pk.verify_view().unwrap().verify(&sig, Some(msg)).unwrap(); + let sig = ViewBuilder::new(&sk) + .sign() + .build() + .unwrap() + .sign(msg, false, None) + .unwrap(); + ViewBuilder::new(&pk) + .verify() + .build() + .unwrap() + .verify(&sig, Some(msg)) + .unwrap(); // wrong message must fail assert!( - pk.verify_view() + ViewBuilder::new(&pk) + .verify() + .build() .unwrap() .verify(&sig, Some(b"wrong message")) .is_err() diff --git a/src/views/ed25519_mayo2.rs b/src/views/ed25519_mayo2.rs index def33af..34e4980 100644 --- a/src/views/ed25519_mayo2.rs +++ b/src/views/ed25519_mayo2.rs @@ -3,11 +3,11 @@ //! Sign: s1 = Ed25519(m), s2 = Mayo2(m || s1), sig = s1 || s2 //! Verify: verify Ed25519(m, s1) && verify Mayo2(m || s1, s2) +use crate::views::dispatch::{dispatch_attr_view, dispatch_conv_view, dispatch_data_view}; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, SignView, VerifyView, error::{AttributesError, ConversionsError, SignError, VerifyError}, - views::Views, }; use ed25519_dalek::{Signature as Ed25519Sig, SigningKey, VerifyingKey}; use multi_codec::Codec; @@ -75,7 +75,7 @@ impl<'a> DataView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -126,7 +126,7 @@ impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { let pub_bytes = if self.is_secret_key() { let pk = self.to_public_key()?; - let dv = pk.data_view()?; + let dv = dispatch_data_view(&pk)?; dv.key_bytes()? } else { self.key_bytes()? @@ -137,13 +137,13 @@ impl<'a> FingerprintView for View<'a> { impl<'a> SignView for View<'a> { fn sign(&self, msg: &[u8], combined: bool, _scheme: Option) -> Result { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if !attr.is_secret_key() { return Err(SignError::NotSigningKey.into()); } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -201,16 +201,16 @@ impl<'a> VerifyView for View<'a> { return Err(VerifyError::MissingMessage.into()); }; - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; let pubmk = if attr.is_secret_key() { - let kc = self.mk.conv_view()?; + let kc = dispatch_conv_view(self.mk)?; kc.to_public_key()? } else { self.mk.clone() }; let key_bytes = { - let kd = pubmk.data_view()?; + let kd = dispatch_data_view(&pubmk)?; kd.key_bytes()? }; @@ -269,8 +269,8 @@ impl<'a> VerifyView for View<'a> { #[cfg(test)] mod tests { use super::*; + use crate::ViewBuilder; use crate::mk::ED25519_MAYO2_KEY_CODECS; - use crate::views::Views; #[test] fn test_key_gen_roundtrip() { @@ -282,7 +282,7 @@ mod tests { .try_build() .unwrap(); - let attr = mk.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk).attr().build().unwrap(); assert!(attr.is_secret_key()); assert!(!attr.is_public_key()); @@ -301,10 +301,10 @@ mod tests { .try_build() .unwrap(); - let conv = mk.conv_view().unwrap(); + let conv = ViewBuilder::new(&mk).conv().build().unwrap(); let pk = conv.to_public_key().unwrap(); - let attr = pk.attr_view().unwrap(); + let attr = ViewBuilder::new(&pk).attr().build().unwrap(); assert!(attr.is_public_key()); assert!(!attr.is_secret_key()); @@ -313,7 +313,7 @@ mod tests { assert_eq!(pk, pk2); // check public key length - let dv = pk.data_view().unwrap(); + let dv = ViewBuilder::new(&pk).data().build().unwrap(); assert_eq!(dv.key_bytes().unwrap().len(), PUB_KEY_LEN); } @@ -326,16 +326,23 @@ mod tests { .unwrap(); // Fingerprint from private key (derives public key internally) - let fp1 = mk - .fingerprint_view() + let fp1 = ViewBuilder::new(&mk) + .fingerprint() + .build() .unwrap() .fingerprint(Codec::Sha3256) .unwrap(); // Fingerprint from public key - let pk = mk.conv_view().unwrap().to_public_key().unwrap(); - let fp2 = pk - .fingerprint_view() + let pk = ViewBuilder::new(&mk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); + let fp2 = ViewBuilder::new(&pk) + .fingerprint() + .build() .unwrap() .fingerprint(Codec::Sha3256) .unwrap(); @@ -353,16 +360,36 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let msg = b"hello Ed25519-MAYO2 hybrid signing!"; - let sig = sk.sign_view().unwrap().sign(msg, false, None).unwrap(); + let sig = ViewBuilder::new(&sk) + .sign() + .build() + .unwrap() + .sign(msg, false, None) + .unwrap(); // Verify with public key - pk.verify_view().unwrap().verify(&sig, Some(msg)).unwrap(); + ViewBuilder::new(&pk) + .verify() + .build() + .unwrap() + .verify(&sig, Some(msg)) + .unwrap(); // Verify with private key (auto-derives public key) - sk.verify_view().unwrap().verify(&sig, Some(msg)).unwrap(); + ViewBuilder::new(&sk) + .verify() + .build() + .unwrap() + .verify(&sig, Some(msg)) + .unwrap(); } #[test] @@ -372,13 +399,28 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let msg = b"combined message test"; - let sig = sk.sign_view().unwrap().sign(msg, true, None).unwrap(); + let sig = ViewBuilder::new(&sk) + .sign() + .build() + .unwrap() + .sign(msg, true, None) + .unwrap(); // Verify without explicit message (uses embedded message) - pk.verify_view().unwrap().verify(&sig, None).unwrap(); + ViewBuilder::new(&pk) + .verify() + .build() + .unwrap() + .verify(&sig, None) + .unwrap(); } #[test] @@ -388,14 +430,26 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let msg = b"tamper test"; - let sig = sk.sign_view().unwrap().sign(msg, false, None).unwrap(); + let sig = ViewBuilder::new(&sk) + .sign() + .build() + .unwrap() + .sign(msg, false, None) + .unwrap(); // Tamper with message assert!( - pk.verify_view() + ViewBuilder::new(&pk) + .verify() + .build() .unwrap() .verify(&sig, Some(b"wrong message")) .is_err() @@ -413,12 +467,29 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk2 = sk2.conv_view().unwrap().to_public_key().unwrap(); + let pk2 = ViewBuilder::new(&sk2) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let msg = b"wrong key test"; - let sig = sk1.sign_view().unwrap().sign(msg, false, None).unwrap(); + let sig = ViewBuilder::new(&sk1) + .sign() + .build() + .unwrap() + .sign(msg, false, None) + .unwrap(); // Verify with wrong key should fail - assert!(pk2.verify_view().unwrap().verify(&sig, Some(msg)).is_err()); + assert!( + ViewBuilder::new(&pk2) + .verify() + .build() + .unwrap() + .verify(&sig, Some(msg)) + .is_err() + ); } } diff --git a/src/views/ed25519_mldsa65.rs b/src/views/ed25519_mldsa65.rs index e36ff40..d1b0503 100644 --- a/src/views/ed25519_mldsa65.rs +++ b/src/views/ed25519_mldsa65.rs @@ -4,11 +4,11 @@ //! Verify: verify Ed25519(m, s1) && verify MlDsa65(m || s1, s2) //! Public encoding is classical-first: ed25519_pub (32) || mldsa65_pub. +use crate::views::dispatch::{dispatch_attr_view, dispatch_conv_view, dispatch_data_view}; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, SignView, VerifyView, error::{AttributesError, ConversionsError, SignError, VerifyError}, - views::Views, }; use ed25519_dalek::{Signature as Ed25519Sig, SigningKey, VerifyingKey}; use ml_dsa::{ @@ -80,7 +80,7 @@ impl<'a> DataView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -133,7 +133,7 @@ impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { let pub_bytes = if self.is_secret_key() { let pk = self.to_public_key()?; - let dv = pk.data_view()?; + let dv = dispatch_data_view(&pk)?; dv.key_bytes()? } else { self.key_bytes()? @@ -144,13 +144,13 @@ impl<'a> FingerprintView for View<'a> { impl<'a> SignView for View<'a> { fn sign(&self, msg: &[u8], combined: bool, _scheme: Option) -> Result { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if !attr.is_secret_key() { return Err(SignError::NotSigningKey.into()); } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -205,16 +205,16 @@ impl<'a> VerifyView for View<'a> { return Err(VerifyError::MissingMessage.into()); }; - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; let pubmk = if attr.is_secret_key() { - let kc = self.mk.conv_view()?; + let kc = dispatch_conv_view(self.mk)?; kc.to_public_key()? } else { self.mk.clone() }; let key_bytes = { - let kd = pubmk.data_view()?; + let kd = dispatch_data_view(&pubmk)?; kd.key_bytes()? }; @@ -279,7 +279,7 @@ impl<'a> VerifyView for View<'a> { #[cfg(test)] mod tests { use super::*; - use crate::views::Views; + use crate::ViewBuilder; #[test] fn test_sign_verify_roundtrip() { @@ -288,15 +288,32 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let msg = b"hello Ed25519-ML-DSA-65 hybrid signing!"; - let sig = sk.sign_view().unwrap().sign(msg, false, None).unwrap(); - pk.verify_view().unwrap().verify(&sig, Some(msg)).unwrap(); + let sig = ViewBuilder::new(&sk) + .sign() + .build() + .unwrap() + .sign(msg, false, None) + .unwrap(); + ViewBuilder::new(&pk) + .verify() + .build() + .unwrap() + .verify(&sig, Some(msg)) + .unwrap(); // wrong message must fail assert!( - pk.verify_view() + ViewBuilder::new(&pk) + .verify() + .build() .unwrap() .verify(&sig, Some(b"wrong message")) .is_err() @@ -310,8 +327,13 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); - let dv = pk.data_view().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); + let dv = ViewBuilder::new(&pk).data().build().unwrap(); assert_eq!(dv.key_bytes().unwrap().len(), PUB_KEY_LEN); } } diff --git a/src/views/fn_dsa.rs b/src/views/fn_dsa.rs index 721d3d7..3c4f977 100644 --- a/src/views/fn_dsa.rs +++ b/src/views/fn_dsa.rs @@ -1,11 +1,13 @@ // SPDX-License-Identifier: Apache-2.0 //! FN-DSA (Falcon) 512/1024 multikey view; FIPS 206 (draft). +use crate::views::dispatch::{ + dispatch_attr_view, dispatch_conv_view, dispatch_data_view, dispatch_fingerprint_view, +}; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, SignView, VerifyView, error::{AttributesError, ConversionsError, SignError, VerifyError}, - views::Views, }; use fn_dsa::{ DOMAIN_NONE, FN_DSA_LOGN_512, FN_DSA_LOGN_1024, HASH_ID_RAW, SigningKey, SigningKeyStandard, @@ -71,19 +73,19 @@ impl<'a> DataView for View<'a> { impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if attr.is_secret_key() { // convert to a public key Multikey let pk = self.to_public_key()?; // get a conversions view on the public key - let fp = pk.fingerprint_view()?; + let fp = dispatch_fingerprint_view(&pk)?; // get the fingerprint let f = fp.fingerprint(codec)?; Ok(f) } else { // get the key bytes let bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.key_bytes()? }; @@ -96,7 +98,7 @@ impl<'a> FingerprintView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -126,7 +128,7 @@ impl<'a> ConvView for View<'a> { } let key_bytes = { - let kd = pk.data_view()?; + let kd = dispatch_data_view(&pk)?; kd.key_bytes()? }; @@ -158,7 +160,7 @@ impl<'a> ConvView for View<'a> { fn to_ssh_private_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -179,7 +181,7 @@ impl<'a> ConvView for View<'a> { let pk = self.to_public_key()?; let key_bytes = { - let kd = pk.data_view()?; + let kd = dispatch_data_view(&pk)?; kd.key_bytes()? }; @@ -215,13 +217,13 @@ impl<'a> SignView for View<'a> { combined: bool, _scheme: Option, ) -> Result { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if !attr.is_secret_key() { return Err(SignError::NotSigningKey.into()); } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -254,16 +256,16 @@ impl<'a> SignView for View<'a> { impl<'a> VerifyView for View<'a> { fn verify(&self, multisig: &multi_sig::Multisig, msg: Option<&[u8]>) -> Result<(), Error> { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; let pubmk = if attr.is_secret_key() { - let kc = self.mk.conv_view()?; + let kc = dispatch_conv_view(self.mk)?; kc.to_public_key()? } else { self.mk.clone() }; let key_bytes = { - let kd = pubmk.data_view()?; + let kd = dispatch_data_view(&pubmk)?; kd.key_bytes()? }; diff --git a/src/views/frodokem.rs b/src/views/frodokem.rs index f3abdad..0414d22 100644 --- a/src/views/frodokem.rs +++ b/src/views/frodokem.rs @@ -1,11 +1,12 @@ // SPDX-License-Identifier: Apache-2.0 //! FrodoKEM AES/SHAKE multikey view. +use crate::views::dispatch::dispatch_data_view; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, OpenView, SealView, error::{AttributesError, ConversionsError, SealError}, - views::{Views, aead}, + views::aead, }; use multi_codec::Codec; use multi_hash::{Multihash, mh}; @@ -184,7 +185,7 @@ impl<'a> DataView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; let pub_bytes = public_from_private(self.mk.codec, secret_bytes.as_slice())?; @@ -212,7 +213,7 @@ impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { let pub_bytes = if self.is_secret_key() { let pk = self.to_public_key()?; - let dv = pk.data_view()?; + let dv = dispatch_data_view(&pk)?; dv.key_bytes()? } else { self.key_bytes()? @@ -284,7 +285,7 @@ impl<'a> OpenView for View<'a> { return Err(SealError::UnsupportedAeadCodec(aead_codec).into()); } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; let shared_secret = decap(self.mk.codec, secret_bytes.as_slice(), &kem_ct)?; diff --git a/src/views/lamport.rs b/src/views/lamport.rs index a8a57f1..984d01f 100644 --- a/src/views/lamport.rs +++ b/src/views/lamport.rs @@ -7,11 +7,11 @@ //! single use; the provenance-log / keystore layer must reject a second //! signature by the same Lamport public key. +use crate::views::dispatch::{dispatch_attr_view, dispatch_conv_view, dispatch_data_view}; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, SignView, ThresholdView, VerifyView, error::{AttributesError, ConversionsError, SignError, ThresholdError, VerifyError}, - views::Views, }; use blake2::{Blake2b512, Blake2s256}; use lamport_signature_plus::{ @@ -516,7 +516,7 @@ impl<'a> DataView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; let pub_bytes = public_from_private(self.mk.codec, secret_bytes.as_slice())?; @@ -544,7 +544,7 @@ impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { let pub_bytes = if self.is_secret_key() { let pk = self.to_public_key()?; - let dv = pk.data_view()?; + let dv = dispatch_data_view(&pk)?; dv.key_bytes()? } else { self.key_bytes()? @@ -557,7 +557,7 @@ impl<'a> SignView for View<'a> { fn sign(&self, msg: &[u8], combined: bool, _scheme: Option) -> Result { let codec = self.mk.codec; let key_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.key_bytes()? }; // A private-key SHARE signs to a signature SHARE; a full private key @@ -593,14 +593,14 @@ impl<'a> VerifyView for View<'a> { return Err(VerifyError::MissingMessage.into()); }; - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; let pubmk = if attr.is_secret_key() { - self.mk.conv_view()?.to_public_key()? + dispatch_conv_view(self.mk)?.to_public_key()? } else { self.mk.clone() }; let key_bytes = { - let kd = pubmk.data_view()?; + let kd = dispatch_data_view(&pubmk)?; kd.key_bytes()? }; let sv = multisig.data_view()?; @@ -614,11 +614,11 @@ impl<'a> ThresholdView for View<'a> { /// returned Multikey is a `Lamport*PrivShare` whose key data is a GF(256) /// Shamir share that can sign independently to a signature share. fn split(&self, threshold: usize, limit: usize) -> Result, Error> { - if !self.mk.attr_view()?.is_secret_key() { + if !dispatch_attr_view(self.mk)?.is_secret_key() { return Err(ThresholdError::NotASecretKey.into()); } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; let share_codec = priv_share_codec(self.mk.codec)?; @@ -690,6 +690,7 @@ pub(crate) fn generate_private_key(codec: Codec) -> Result>, E mod tests { use super::*; use crate::Builder as MkBuilder; + use crate::ViewBuilder; #[test] fn test_lamport_sign_verify_roundtrip() { @@ -711,13 +712,30 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let msg = b"lamport multikey message"; - let ms = sk.sign_view().unwrap().sign(msg, false, None).unwrap(); - pk.verify_view().unwrap().verify(&ms, Some(msg)).unwrap(); + let ms = ViewBuilder::new(&sk) + .sign() + .build() + .unwrap() + .sign(msg, false, None) + .unwrap(); + ViewBuilder::new(&pk) + .verify() + .build() + .unwrap() + .verify(&ms, Some(msg)) + .unwrap(); assert!( - pk.verify_view() + ViewBuilder::new(&pk) + .verify() + .build() .unwrap() .verify(&ms, Some(b"tampered")) .is_err() @@ -735,22 +753,40 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let msg = b"threshold lamport message"; // split the signing key into 2-of-3 key shares - let shares = sk.threshold_view().unwrap().split(2, 3).unwrap(); + let shares = ViewBuilder::new(&sk) + .threshold() + .build() + .unwrap() + .split(2, 3) + .unwrap(); assert_eq!(shares.len(), 3); - assert!(shares[0].attr_view().unwrap().is_secret_key_share()); + assert!( + ViewBuilder::new(&shares[0]) + .attr() + .build() + .unwrap() + .is_secret_key_share() + ); // any two shareholders each produce a signature share - let share_sig_a = shares[0] - .sign_view() + let share_sig_a = ViewBuilder::new(&shares[0]) + .sign() + .build() .unwrap() .sign(msg, false, None) .unwrap(); - let share_sig_c = shares[2] - .sign_view() + let share_sig_c = ViewBuilder::new(&shares[2]) + .sign() + .build() .unwrap() .sign(msg, false, None) .unwrap(); @@ -774,7 +810,9 @@ mod tests { assert_eq!(combined.codec(), Codec::LamportSha3256Sig); // the combined signature verifies under the ORIGINAL public key - pk.verify_view() + ViewBuilder::new(&pk) + .verify() + .build() .unwrap() .verify(&combined, Some(msg)) .unwrap(); diff --git a/src/views/lamport_merkle.rs b/src/views/lamport_merkle.rs index a162154..c10a216 100644 --- a/src/views/lamport_merkle.rs +++ b/src/views/lamport_merkle.rs @@ -15,11 +15,11 @@ //! and 1 respectively (`[depth, root]`, `[version, depth, next_index, …]`); //! `MtSignature`/`MtSignatureShare` embed depth at byte 0 (`[depth, index, …]`). +use crate::views::dispatch::{dispatch_attr_view, dispatch_conv_view, dispatch_data_view}; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, MerkleStateView, Multikey, SignView, ThresholdView, VerifyView, error::{AttributesError, ConversionsError, SignError, ThresholdError, VerifyError}, - views::Views, views::lamport::{ Blake2b512Digest, Blake2s256Digest, Blake3_256Digest, Sha2_256Digest, Sha2_384Digest, Sha2_512Digest, Sha3_256Digest, Sha3_384Digest, Sha3_512Digest, Shake128Digest, @@ -600,7 +600,7 @@ impl<'a> DataView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; let depth = wire_depth_at(secret_bytes.as_slice(), 1)?; @@ -631,7 +631,7 @@ impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { let pub_bytes = if self.is_secret_key() { let pk = self.to_public_key()?; - let dv = pk.data_view()?; + let dv = dispatch_data_view(&pk)?; dv.key_bytes()? } else { self.key_bytes()? @@ -667,7 +667,7 @@ impl<'a> SignView for View<'a> { ) -> Result<(ms::Multisig, Multikey), Error> { let codec = self.mk.codec; let key_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.key_bytes()? }; // A private-key SHARE signs to a signature SHARE (stateful, but the @@ -717,14 +717,14 @@ impl<'a> VerifyView for View<'a> { return Err(VerifyError::MissingMessage.into()); }; - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; let pubmk = if attr.is_secret_key() { - self.mk.conv_view()?.to_public_key()? + dispatch_conv_view(self.mk)?.to_public_key()? } else { self.mk.clone() }; let key_bytes = { - let kd = pubmk.data_view()?; + let kd = dispatch_data_view(&pubmk)?; kd.key_bytes()? }; // Cross-check the pubkey's depth attribute against the MtVerifyingKey @@ -760,11 +760,11 @@ impl<'a> ThresholdView for View<'a> { /// data is a GF(256) share of the whole tree state that can sign /// independently to a signature share. fn split(&self, threshold: usize, limit: usize) -> Result, Error> { - if !self.mk.attr_view()?.is_secret_key() { + if !dispatch_attr_view(self.mk)?.is_secret_key() { return Err(ThresholdError::NotASecretKey.into()); } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; let depth = state_depth(secret_bytes.as_slice())?; @@ -836,7 +836,7 @@ impl<'a> ThresholdView for View<'a> { impl<'a> MerkleStateView for View<'a> { fn depth(&self) -> Result { let key_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.key_bytes()? }; let depth = if self.is_secret_key() { @@ -858,7 +858,7 @@ impl<'a> MerkleStateView for View<'a> { return Err(AttributesError::NotSecretKey(self.mk.codec).into()); } let key_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; // [version, depth, next_index, …] @@ -870,7 +870,7 @@ impl<'a> MerkleStateView for View<'a> { return Err(AttributesError::NotSecretKey(self.mk.codec).into()); } let key_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; let sk = MtSigningKey::::from_bytes(key_bytes.as_slice()); @@ -1015,6 +1015,7 @@ const MAX_DECODED_SIZE: usize = crate::mk::MAX_DECODED_SIZE; mod tests { use super::*; use crate::LAMPORT_MERKLE_KEY_CODECS; + use crate::ViewBuilder; use multi_sig::AttrId as MsAttrId; fn build_priv(codec: Codec, depth: u8) -> Multikey { @@ -1047,33 +1048,51 @@ mod tests { fn test_merkle_roundtrip_all_digests_depth1() { for codec in merkle_priv_codecs() { let sk = build_priv(codec, 1); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); // depth attribute stamped and cross-checked assert_eq!(pk.attributes.get(&AttrId::Depth).map(|b| b[0]), Some(1)); let msg = b"merkle roundtrip"; - let (ms, advanced) = sk - .sign_view() + let (ms, advanced) = ViewBuilder::new(&sk) + .sign() + .build() .unwrap() .sign_advance(msg, false, None) .unwrap(); assert_eq!(ms.attributes.get(&MsAttrId::Depth).map(|b| b[0]), Some(1)); - pk.verify_view().unwrap().verify(&ms, Some(msg)).unwrap(); - sk.verify_view().unwrap().verify(&ms, Some(msg)).unwrap(); + ViewBuilder::new(&pk) + .verify() + .build() + .unwrap() + .verify(&ms, Some(msg)) + .unwrap(); + ViewBuilder::new(&sk) + .verify() + .build() + .unwrap() + .verify(&ms, Some(msg)) + .unwrap(); // tampered message must fail assert!( - pk.verify_view() + ViewBuilder::new(&pk) + .verify() + .build() .unwrap() .verify(&ms, Some(b"tampered")) .is_err() ); // advanced key has one fewer remaining signature - let sv = sk.merkle_state_view().unwrap(); - let av = advanced.merkle_state_view().unwrap(); + let sv = ViewBuilder::new(&sk).merkle_state().build().unwrap(); + let av = ViewBuilder::new(&advanced).merkle_state().build().unwrap(); assert_eq!(av.next_index().unwrap(), sv.next_index().unwrap() + 1); } } @@ -1081,7 +1100,14 @@ mod tests { #[test] fn test_merkle_sign_rejected_sign_advance_required() { let sk = build_priv(Codec::LamportMerkleSha3256Priv, 1); - assert!(sk.sign_view().unwrap().sign(b"x", false, None).is_err()); + assert!( + ViewBuilder::new(&sk) + .sign() + .build() + .unwrap() + .sign(b"x", false, None) + .is_err() + ); } #[test] @@ -1117,15 +1143,26 @@ mod tests { assert_eq!(sk.attributes.get(&AttrId::Depth).map(|b| b[0]), Some(1)); // the key must be fully usable: public derivation and signing - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let msg = b"plain ctor roundtrip"; - let (ms, advanced) = sk - .sign_view() + let (ms, advanced) = ViewBuilder::new(&sk) + .sign() + .build() .unwrap() .sign_advance(msg, false, None) .unwrap(); - pk.verify_view().unwrap().verify(&ms, Some(msg)).unwrap(); - let av = advanced.merkle_state_view().unwrap(); + ViewBuilder::new(&pk) + .verify() + .build() + .unwrap() + .verify(&ms, Some(msg)) + .unwrap(); + let av = ViewBuilder::new(&advanced).merkle_state().build().unwrap(); assert_eq!(av.next_index().unwrap(), 1); assert_eq!(av.remaining_signatures().unwrap(), 1); } @@ -1133,40 +1170,55 @@ mod tests { #[test] fn test_merkle_depth3_sha3_256() { let sk = build_priv(Codec::LamportMerkleSha3256Priv, 3); - let sv = sk.merkle_state_view().unwrap(); + let sv = ViewBuilder::new(&sk).merkle_state().build().unwrap(); assert_eq!(sv.depth().unwrap(), 3); assert_eq!(sv.capacity().unwrap(), 8); assert_eq!(sv.next_index().unwrap(), 0); assert_eq!(sv.remaining_signatures().unwrap(), 8); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let msg = b"depth three"; - let (ms, mut advanced) = sk - .sign_view() + let (ms, mut advanced) = ViewBuilder::new(&sk) + .sign() + .build() .unwrap() .sign_advance(msg, true, None) .unwrap(); assert_eq!(ms.message, msg.to_vec()); - pk.verify_view().unwrap().verify(&ms, Some(msg)).unwrap(); + ViewBuilder::new(&pk) + .verify() + .build() + .unwrap() + .verify(&ms, Some(msg)) + .unwrap(); // exhaust the tree for i in 1..8 { let m = format!("m{i}"); - let (m2, adv2) = advanced - .sign_view() + let (m2, adv2) = ViewBuilder::new(&advanced) + .sign() + .build() .unwrap() .sign_advance(m.as_bytes(), false, None) .unwrap(); - pk.verify_view() + ViewBuilder::new(&pk) + .verify() + .build() .unwrap() .verify(&m2, Some(m.as_bytes())) .unwrap(); advanced = adv2; } - let av = advanced.merkle_state_view().unwrap(); + let av = ViewBuilder::new(&advanced).merkle_state().build().unwrap(); assert_eq!(av.remaining_signatures().unwrap(), 0); assert!( - advanced - .sign_view() + ViewBuilder::new(&advanced) + .sign() + .build() .unwrap() .sign_advance(b"m9", false, None) .is_err() @@ -1181,12 +1233,27 @@ mod tests { tampered .attributes .insert(AttrId::Depth, Zeroizing::new(vec![3])); - assert!(tampered.conv_view().unwrap().to_public_key().is_err()); - assert!(tampered.merkle_state_view().unwrap().depth().is_err()); + assert!( + ViewBuilder::new(&tampered) + .conv() + .build() + .unwrap() + .to_public_key() + .is_err() + ); + assert!( + ViewBuilder::new(&tampered) + .merkle_state() + .build() + .unwrap() + .depth() + .is_err() + ); // sign_advance cross-checks too assert!( - tampered - .sign_view() + ViewBuilder::new(&tampered) + .sign() + .build() .unwrap() .sign_advance(b"m", false, None) .is_err() @@ -1196,26 +1263,44 @@ mod tests { #[test] fn test_merkle_threshold_flow() { let sk = build_priv(Codec::LamportMerkleSha3256Priv, 1); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let msg = b"threshold merkle"; // split 2-of-3 - let shares = sk.threshold_view().unwrap().split(2, 3).unwrap(); + let shares = ViewBuilder::new(&sk) + .threshold() + .build() + .unwrap() + .split(2, 3) + .unwrap(); assert_eq!(shares.len(), 3); for share in &shares { - assert!(share.attr_view().unwrap().is_secret_key_share()); + assert!( + ViewBuilder::new(share) + .attr() + .build() + .unwrap() + .is_secret_key_share() + ); assert_eq!(share.attributes.get(&AttrId::Depth).map(|b| b[0]), Some(1)); } // two participants sign to signature shares - let s1 = shares[0] - .sign_view() + let s1 = ViewBuilder::new(&shares[0]) + .sign() + .build() .unwrap() .sign_advance(msg, false, None) .unwrap() .0; - let s2 = shares[1] - .sign_view() + let s2 = ViewBuilder::new(&shares[1]) + .sign() + .build() .unwrap() .sign_advance(msg, false, None) .unwrap() @@ -1231,7 +1316,9 @@ mod tests { acc = next; } let combined = acc.threshold_view().unwrap().combine().unwrap(); - pk.verify_view() + ViewBuilder::new(&pk) + .verify() + .build() .unwrap() .verify(&combined, Some(msg)) .unwrap(); @@ -1240,15 +1327,20 @@ mod tests { #[test] fn test_merkle_introspection_depth1() { let sk = build_priv(Codec::LamportMerkleSha2256Priv, 1); - let sv = sk.merkle_state_view().unwrap(); + let sv = ViewBuilder::new(&sk).merkle_state().build().unwrap(); assert_eq!(sv.depth().unwrap(), 1); assert_eq!(sv.capacity().unwrap(), 2); assert_eq!(sv.next_index().unwrap(), 0); assert_eq!(sv.remaining_signatures().unwrap(), 2); // public keys expose depth/capacity only - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); - let pv = pk.merkle_state_view().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); + let pv = ViewBuilder::new(&pk).merkle_state().build().unwrap(); assert_eq!(pv.depth().unwrap(), 1); assert_eq!(pv.capacity().unwrap(), 2); assert!(pv.next_index().is_err()); @@ -1258,8 +1350,9 @@ mod tests { #[test] fn test_merkle_sig_index_absent() { let sk = build_priv(Codec::LamportMerkleBlake3256Priv, 1); - let (ms, _adv) = sk - .sign_view() + let (ms, _adv) = ViewBuilder::new(&sk) + .sign() + .build() .unwrap() .sign_advance(b"m", false, None) .unwrap(); diff --git a/src/views/mayo.rs b/src/views/mayo.rs index 0f948bd..0595fde 100644 --- a/src/views/mayo.rs +++ b/src/views/mayo.rs @@ -1,11 +1,11 @@ // SPDX-License-Identifier: Apache-2.0 //! MAYO-1/2/3/5 multikey view; post-quantum multivariate signature. +use crate::views::dispatch::{dispatch_attr_view, dispatch_conv_view, dispatch_data_view}; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, SignView, VerifyView, error::{AttributesError, ConversionsError, SignError, VerifyError}, - views::Views, }; use multi_codec::Codec; use multi_hash::{Multihash, mh}; @@ -99,7 +99,7 @@ impl<'a> DataView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -149,7 +149,7 @@ impl<'a> ConvView for View<'a> { } let key_bytes = { - let kd = pk.data_view()?; + let kd = dispatch_data_view(&pk)?; kd.key_bytes()? }; @@ -182,7 +182,7 @@ impl<'a> ConvView for View<'a> { fn to_ssh_private_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -204,7 +204,7 @@ impl<'a> ConvView for View<'a> { let pk = self.to_public_key()?; let key_bytes = { - let kd = pk.data_view()?; + let kd = dispatch_data_view(&pk)?; kd.key_bytes()? }; @@ -240,8 +240,8 @@ impl<'a> FingerprintView for View<'a> { // Ed25519 / BLS / SLH-DSA views. Earlier revisions returned an // "not yet implemented" error here even though `to_public_key()` // was implemented, which broke fingerprinting of secret keys. - let pk = self.mk.conv_view()?.to_public_key()?; - let dv = pk.data_view()?; + let pk = dispatch_conv_view(self.mk)?.to_public_key()?; + let dv = dispatch_data_view(&pk)?; dv.key_bytes()? } else { self.key_bytes()? @@ -259,13 +259,13 @@ impl<'a> SignView for View<'a> { ) -> Result { use ml_dsa::signature::Signer; - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if !attr.is_secret_key() { return Err(SignError::NotSigningKey.into()); } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; let (signature, codec) = match (self.mk.codec, secret_bytes.len()) { @@ -337,16 +337,16 @@ impl<'a> VerifyView for View<'a> { return Err(VerifyError::MissingMessage.into()); }; - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; let pubmk = if attr.is_secret_key() { - let kc = self.mk.conv_view()?; + let kc = dispatch_conv_view(self.mk)?; kc.to_public_key()? } else { self.mk.clone() }; let key_bytes = { - let kd = pubmk.data_view()?; + let kd = dispatch_data_view(&pubmk)?; kd.key_bytes()? }; @@ -404,7 +404,7 @@ impl<'a> VerifyView for View<'a> { #[cfg(test)] mod tests { use super::*; - use crate::views::Views; + use crate::ViewBuilder; fn sign_verify_roundtrip(priv_codec: Codec) { let mut rng = rand::rng(); @@ -413,15 +413,32 @@ mod tests { .with_comment("mayo test") .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let msg = b"hello MAYO multivariate signature"; - let sig = sk.sign_view().unwrap().sign(msg, false, None).unwrap(); - pk.verify_view().unwrap().verify(&sig, Some(msg)).unwrap(); + let sig = ViewBuilder::new(&sk) + .sign() + .build() + .unwrap() + .sign(msg, false, None) + .unwrap(); + ViewBuilder::new(&pk) + .verify() + .build() + .unwrap() + .verify(&sig, Some(msg)) + .unwrap(); // wrong message must fail assert!( - pk.verify_view() + ViewBuilder::new(&pk) + .verify() + .build() .unwrap() .verify(&sig, Some(b"tampered")) .is_err() diff --git a/src/views/ml_dsa.rs b/src/views/ml_dsa.rs index 7490296..d7cb0cc 100644 --- a/src/views/ml_dsa.rs +++ b/src/views/ml_dsa.rs @@ -1,11 +1,11 @@ // SPDX-License-Identifier: Apache-2.0 //! ML-DSA 65/87 multikey view; FIPS 204. +use crate::views::dispatch::{dispatch_attr_view, dispatch_conv_view, dispatch_data_view}; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, SignView, VerifyView, error::{AttributesError, ConversionsError, SignError, VerifyError}, - views::Views, }; use ml_dsa::{ EncodedSignature, EncodedVerifyingKey, MlDsa65, MlDsa87, Seed, Signature, SigningKey, @@ -86,7 +86,7 @@ impl<'a> DataView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -136,7 +136,7 @@ impl<'a> ConvView for View<'a> { } let key_bytes = { - let kd = pk.data_view()?; + let kd = dispatch_data_view(&pk)?; kd.key_bytes()? }; @@ -168,7 +168,7 @@ impl<'a> ConvView for View<'a> { fn to_ssh_private_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -189,7 +189,7 @@ impl<'a> ConvView for View<'a> { let pk = self.to_public_key()?; let key_bytes = { - let kd = pk.data_view()?; + let kd = dispatch_data_view(&pk)?; kd.key_bytes()? }; @@ -225,8 +225,8 @@ impl<'a> FingerprintView for View<'a> { // Ed25519 / BLS / SLH-DSA views. Earlier revisions returned an // "not yet implemented" error here even though `to_public_key()` // was implemented, which broke fingerprinting of secret keys. - let pk = self.mk.conv_view()?.to_public_key()?; - let dv = pk.data_view()?; + let pk = dispatch_conv_view(self.mk)?.to_public_key()?; + let dv = dispatch_data_view(&pk)?; dv.key_bytes()? } else { self.key_bytes()? @@ -242,13 +242,13 @@ impl<'a> SignView for View<'a> { combined: bool, _scheme: Option, ) -> Result { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if !attr.is_secret_key() { return Err(SignError::NotSigningKey.into()); } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; let (signature, codec) = match (self.mk.codec, secret_bytes.len()) { @@ -302,16 +302,16 @@ impl<'a> VerifyView for View<'a> { return Err(VerifyError::MissingMessage.into()); }; - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; let pubmk = if attr.is_secret_key() { - let kc = self.mk.conv_view()?; + let kc = dispatch_conv_view(self.mk)?; kc.to_public_key()? } else { self.mk.clone() }; let key_bytes = { - let kd = pubmk.data_view()?; + let kd = dispatch_data_view(&pubmk)?; kd.key_bytes()? }; diff --git a/src/views/ml_kem.rs b/src/views/ml_kem.rs index 346aabf..4426058 100644 --- a/src/views/ml_kem.rs +++ b/src/views/ml_kem.rs @@ -1,11 +1,12 @@ // SPDX-License-Identifier: Apache-2.0 //! ML-KEM 768/1024 multikey view; FIPS 203 key encapsulation. +use crate::views::dispatch::dispatch_data_view; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, OpenView, SealView, error::{AttributesError, ConversionsError, SealError}, - views::{Views, aead}, + views::aead, }; use ml_kem::{ MlKem768, MlKem1024, @@ -85,7 +86,7 @@ impl<'a> DataView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -138,7 +139,7 @@ impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { let pub_bytes = if self.is_secret_key() { let pk = self.to_public_key()?; - let dv = pk.data_view()?; + let dv = dispatch_data_view(&pk)?; dv.key_bytes()? } else { self.key_bytes()? @@ -247,7 +248,7 @@ impl<'a> OpenView for View<'a> { } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -294,8 +295,8 @@ impl<'a> OpenView for View<'a> { #[cfg(test)] mod tests { use super::*; + use crate::ViewBuilder; use crate::mk::ML_KEM_KEY_CODECS; - use crate::views::Views; #[test] fn test_ml_kem_key_gen_roundtrip() { @@ -307,11 +308,11 @@ mod tests { .try_build() .unwrap(); - let attr = mk.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk).attr().build().unwrap(); assert!(attr.is_secret_key()); assert!(!attr.is_public_key()); - let kd = mk.data_view().unwrap(); + let kd = ViewBuilder::new(&mk).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); @@ -331,10 +332,10 @@ mod tests { .try_build() .unwrap(); - let conv = mk.conv_view().unwrap(); + let conv = ViewBuilder::new(&mk).conv().build().unwrap(); let pk = conv.to_public_key().unwrap(); - let attr = pk.attr_view().unwrap(); + let attr = ViewBuilder::new(&pk).attr().build().unwrap(); assert!(attr.is_public_key()); assert!(!attr.is_secret_key()); @@ -353,9 +354,15 @@ mod tests { .try_build() .unwrap(); - let pk = mk.conv_view().unwrap().to_public_key().unwrap(); - let fp = pk - .fingerprint_view() + let pk = ViewBuilder::new(&mk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); + let fp = ViewBuilder::new(&pk) + .fingerprint() + .build() .unwrap() .fingerprint(Codec::Sha3256) .unwrap(); @@ -378,17 +385,28 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); for aead_codec in &aead_codecs { let plaintext = b"hello ML-KEM world!"; - let (sealed, _) = pk - .seal_view() + let (sealed, _) = ViewBuilder::new(&pk) + .seal() + .build() .unwrap() .seal(plaintext, *aead_codec, b"") .unwrap(); - let opened = sk.open_view().unwrap().open(&sealed, None, b"").unwrap(); + let opened = ViewBuilder::new(&sk) + .open() + .build() + .unwrap() + .open(&sealed, None, b"") + .unwrap(); assert_eq!(plaintext.as_slice(), opened.as_slice()); } } @@ -401,22 +419,35 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk1 = sk1.conv_view().unwrap().to_public_key().unwrap(); + let pk1 = ViewBuilder::new(&sk1) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let sk2 = Builder::new_from_random_bytes(Codec::Mlkem768Priv, &mut rng) .unwrap() .try_build() .unwrap(); - let (sealed, _) = pk1 - .seal_view() + let (sealed, _) = ViewBuilder::new(&pk1) + .seal() + .build() .unwrap() .seal(b"secret data", Codec::Xchacha20Poly1305, b"") .unwrap(); // Opening with wrong key should fail (decapsulation will produce different shared secret // and AEAD open will fail) - assert!(sk2.open_view().unwrap().open(&sealed, None, b"").is_err()); + assert!( + ViewBuilder::new(&sk2) + .open() + .build() + .unwrap() + .open(&sealed, None, b"") + .is_err() + ); } #[test] @@ -429,7 +460,9 @@ mod tests { // seal with private key should fail assert!( - sk.seal_view() + ViewBuilder::new(&sk) + .seal() + .build() .unwrap() .seal(b"data", Codec::Xchacha20Poly1305, b"") .is_err() @@ -443,16 +476,29 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); - let (sealed, _) = pk - .seal_view() + let (sealed, _) = ViewBuilder::new(&pk) + .seal() + .build() .unwrap() .seal(b"data", Codec::Xchacha20Poly1305, b"") .unwrap(); // open with public key should fail - assert!(pk.open_view().unwrap().open(&sealed, None, b"").is_err()); + assert!( + ViewBuilder::new(&pk) + .open() + .build() + .unwrap() + .open(&sealed, None, b"") + .is_err() + ); } #[test] @@ -462,11 +508,18 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); // AES-128-GCM is not allowed for ML-KEM (not PQ-safe) assert!( - pk.seal_view() + ViewBuilder::new(&pk) + .seal() + .build() .unwrap() .seal(b"data", Codec::AesGcm128, b"") .is_err() diff --git a/src/views/nist_p.rs b/src/views/nist_p.rs index 6ebcbb5..faa1613 100644 --- a/src/views/nist_p.rs +++ b/src/views/nist_p.rs @@ -1,9 +1,12 @@ // SPDX-License-Identifier: Apache-2.0 //! NIST P-256, P-384, P-521 ECDSA multikey view. +use crate::views::dispatch::{ + dispatch_attr_view, dispatch_conv_view, dispatch_data_view, dispatch_fingerprint_view, +}; use crate::{ AttrId, AttrView, Builder, CipherAttrView, ConvView, DataView, Error, FingerprintView, - KdfAttrView, Multikey, OpenView, SealView, SignView, VerifyView, Views, + KdfAttrView, Multikey, OpenView, SealView, SignView, VerifyView, error::{ AttributesError, CipherError, ConversionsError, KdfError, SealError, SignError, VerifyError, }, @@ -165,14 +168,14 @@ impl<'a> KdfAttrView for View<'a> { impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if attr.is_secret_key() { let pk = self.to_public_key()?; - let fp = pk.fingerprint_view()?; + let fp = dispatch_fingerprint_view(&pk)?; fp.fingerprint(codec) } else { let bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.key_bytes()? }; Ok(mh::Builder::new_from_bytes(codec, bytes)?.try_build()?) @@ -183,7 +186,7 @@ impl<'a> FingerprintView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -224,7 +227,7 @@ impl<'a> ConvView for View<'a> { } let key_bytes = { - let kd = pk.data_view()?; + let kd = dispatch_data_view(&pk)?; kd.key_bytes()? }; @@ -272,7 +275,7 @@ impl<'a> ConvView for View<'a> { fn to_ssh_private_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -332,13 +335,13 @@ impl<'a> ConvView for View<'a> { impl<'a> SignView for View<'a> { fn sign(&self, msg: &[u8], combined: bool, _scheme: Option) -> Result { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if !attr.is_secret_key() { return Err(SignError::NotSigningKey.into()); } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -384,16 +387,16 @@ impl<'a> SignView for View<'a> { impl<'a> VerifyView for View<'a> { fn verify(&self, multisig: &Multisig, msg: Option<&[u8]>) -> Result<(), Error> { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; let pubmk = if attr.is_secret_key() { - let kc = self.mk.conv_view()?; + let kc = dispatch_conv_view(self.mk)?; kc.to_public_key()? } else { self.mk.clone() }; let key_bytes = { - let kd = pubmk.data_view()?; + let kd = dispatch_data_view(&pubmk)?; kd.key_bytes()? }; @@ -610,9 +613,9 @@ impl<'a> OpenView for View<'a> { return Err(SealError::UnsupportedAeadCodec(aead_codec).into()); } - let eph_bytes = ephemeral_mk.data_view()?.key_bytes()?; + let eph_bytes = dispatch_data_view(ephemeral_mk)?.key_bytes()?; let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -672,7 +675,7 @@ impl<'a> OpenView for View<'a> { #[cfg(test)] mod ecies_tests { use super::*; - use crate::views::Views; + use crate::ViewBuilder; fn seal_open_roundtrip(priv_codec: Codec) { let mut rng = rand::rng(); @@ -681,7 +684,12 @@ mod ecies_tests { .with_comment("nistp ecies test") .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let plaintext = b"the quick brown fox jumps over the lazy dog"; for aead_codec in [ @@ -690,13 +698,15 @@ mod ecies_tests { Codec::AesGcm128, Codec::AesGcm256, ] { - let (sealed, ephemeral) = pk - .seal_view() + let (sealed, ephemeral) = ViewBuilder::new(&pk) + .seal() + .build() .unwrap() .seal(plaintext, aead_codec, b"") .unwrap(); - let opened = sk - .open_view() + let opened = ViewBuilder::new(&sk) + .open() + .build() .unwrap() .open(&sealed, ephemeral.as_ref(), b"") .unwrap(); @@ -730,15 +740,23 @@ mod ecies_tests { .unwrap() .try_build() .unwrap(); - let pk1 = sk1.conv_view().unwrap().to_public_key().unwrap(); + let pk1 = ViewBuilder::new(&sk1) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); - let (sealed, ephemeral) = pk1 - .seal_view() + let (sealed, ephemeral) = ViewBuilder::new(&pk1) + .seal() + .build() .unwrap() .seal(b"secret", Codec::Chacha20Poly1305, b"") .unwrap(); assert!( - sk2.open_view() + ViewBuilder::new(&sk2) + .open() + .build() .unwrap() .open(&sealed, ephemeral.as_ref(), b"") .is_err() diff --git a/src/views/rsa.rs b/src/views/rsa.rs index a61e4f8..cac9c44 100644 --- a/src/views/rsa.rs +++ b/src/views/rsa.rs @@ -1,13 +1,16 @@ // SPDX-License-Identifier: Apache-2.0 //! RSA-2048/3072/4096 multikey view — signing (PSS SHA-256) + encryption (RSA-OAEP + AEAD). +use crate::views::dispatch::{ + dispatch_attr_view, dispatch_conv_view, dispatch_data_view, dispatch_fingerprint_view, +}; use crate::{ AttrId, AttrView, Builder, CipherAttrView, ConvView, DataView, Error, FingerprintView, KdfAttrView, Multikey, OpenView, SealView, SignView, VerifyView, error::{ AttributesError, CipherError, ConversionsError, KdfError, SealError, SignError, VerifyError, }, - views::{Views, aead}, + views::aead, }; use ::sad_rsa::sha2::Sha256; @@ -224,14 +227,14 @@ impl<'a> KdfAttrView for View<'a> { impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if attr.is_secret_key() { let pk = self.to_public_key()?; - let fp = pk.fingerprint_view()?; + let fp = dispatch_fingerprint_view(&pk)?; fp.fingerprint(codec) } else { let bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.key_bytes()? }; Ok(mh::Builder::new_from_bytes(codec, bytes)?.try_build()?) @@ -242,7 +245,7 @@ impl<'a> FingerprintView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -266,7 +269,7 @@ impl<'a> ConvView for View<'a> { } let key_bytes = { - let kd = pk.data_view()?; + let kd = dispatch_data_view(&pk)?; kd.key_bytes()? }; @@ -291,7 +294,7 @@ impl<'a> ConvView for View<'a> { fn to_ssh_private_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -305,7 +308,7 @@ impl<'a> ConvView for View<'a> { let pk = self.to_public_key()?; let key_bytes = { - let kd = pk.data_view()?; + let kd = dispatch_data_view(&pk)?; kd.key_bytes()? }; @@ -336,13 +339,13 @@ impl<'a> ConvView for View<'a> { impl<'a> SignView for View<'a> { fn sign(&self, msg: &[u8], combined: bool, _scheme: Option) -> Result { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if !attr.is_secret_key() { return Err(SignError::NotSigningKey.into()); } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -382,16 +385,16 @@ impl<'a> SignView for View<'a> { impl<'a> VerifyView for View<'a> { fn verify(&self, multisig: &Multisig, msg: Option<&[u8]>) -> Result<(), Error> { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; let pubmk = if attr.is_secret_key() { - let kc = self.mk.conv_view()?; + let kc = dispatch_conv_view(self.mk)?; kc.to_public_key()? } else { self.mk.clone() }; let key_bytes = { - let kd = pubmk.data_view()?; + let kd = dispatch_data_view(&pubmk)?; kd.key_bytes()? }; @@ -527,7 +530,7 @@ impl<'a> OpenView for View<'a> { } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; diff --git a/src/views/secp256k1.rs b/src/views/secp256k1.rs index 0e2efdb..ef41b8b 100644 --- a/src/views/secp256k1.rs +++ b/src/views/secp256k1.rs @@ -1,7 +1,10 @@ // SPDX-License-Identifier: Apache-2.0 +use crate::views::dispatch::{ + dispatch_attr_view, dispatch_conv_view, dispatch_data_view, dispatch_fingerprint_view, +}; use crate::{ AttrId, AttrView, Builder, CipherAttrView, ConvView, DataView, Error, FingerprintView, - KdfAttrView, Multikey, OpenView, SealView, SignView, VerifyView, Views, + KdfAttrView, Multikey, OpenView, SealView, SignView, VerifyView, error::{ AttributesError, CipherError, ConversionsError, KdfError, SealError, SignError, VerifyError, }, @@ -152,19 +155,19 @@ impl<'a> KdfAttrView for View<'a> { impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if attr.is_secret_key() { // convert to a public key Multikey let pk = self.to_public_key()?; // get a conversions view on the public key - let fp = pk.fingerprint_view()?; + let fp = dispatch_fingerprint_view(&pk)?; // get the fingerprint let f = fp.fingerprint(codec)?; Ok(f) } else { // get the key bytes let bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.key_bytes()? }; @@ -179,7 +182,7 @@ impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { // get the secret key bytes let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -208,7 +211,7 @@ impl<'a> ConvView for View<'a> { } let key_bytes = { - let kd = pk.data_view()?; + let kd = dispatch_data_view(&pk)?; kd.key_bytes()? }; @@ -235,7 +238,7 @@ impl<'a> ConvView for View<'a> { /// try to convert a Multikey to an ssh_key::PrivateKey fn to_ssh_private_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -250,7 +253,7 @@ impl<'a> ConvView for View<'a> { let pk = self.to_public_key()?; let key_bytes = { - let kd = pk.data_view()?; + let kd = dispatch_data_view(&pk)?; kd.key_bytes()? }; @@ -283,14 +286,14 @@ impl<'a> ConvView for View<'a> { impl<'a> SignView for View<'a> { /// try to create a Multisig by siging the passed-in data with the Multikey fn sign(&self, msg: &[u8], combined: bool, _scheme: Option) -> Result { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if !attr.is_secret_key() { return Err(SignError::NotSigningKey.into()); } // get the secret key bytes let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -324,9 +327,9 @@ impl<'a> SignView for View<'a> { impl<'a> VerifyView for View<'a> { /// try to verify a Multisig using the Multikey fn verify(&self, multisig: &Multisig, msg: Option<&[u8]>) -> Result<(), Error> { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; let pubmk = if attr.is_secret_key() { - let kc = self.mk.conv_view()?; + let kc = dispatch_conv_view(self.mk)?; kc.to_public_key()? } else { @@ -335,7 +338,7 @@ impl<'a> VerifyView for View<'a> { // get the secret key bytes let key_bytes = { - let kd = pubmk.data_view()?; + let kd = dispatch_data_view(&pubmk)?; kd.key_bytes()? }; @@ -479,9 +482,9 @@ impl<'a> OpenView for View<'a> { return Err(SealError::UnsupportedAeadCodec(aead_codec).into()); } - let eph_bytes = ephemeral_mk.data_view()?.key_bytes()?; + let eph_bytes = dispatch_data_view(ephemeral_mk)?.key_bytes()?; let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -507,7 +510,7 @@ impl<'a> OpenView for View<'a> { #[cfg(test)] mod ecies_tests { use super::*; - use crate::views::Views; + use crate::ViewBuilder; #[test] fn test_secp256k1_seal_open_roundtrip() { @@ -517,7 +520,12 @@ mod ecies_tests { .with_comment("secp256k1 ecies test") .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let plaintext = b"the quick brown fox jumps over the lazy dog"; for aead_codec in [ @@ -526,13 +534,15 @@ mod ecies_tests { Codec::AesGcm128, Codec::AesGcm256, ] { - let (sealed, ephemeral) = pk - .seal_view() + let (sealed, ephemeral) = ViewBuilder::new(&pk) + .seal() + .build() .unwrap() .seal(plaintext, aead_codec, b"") .unwrap(); - let opened = sk - .open_view() + let opened = ViewBuilder::new(&sk) + .open() + .build() .unwrap() .open(&sealed, ephemeral.as_ref(), b"") .unwrap(); @@ -551,15 +561,23 @@ mod ecies_tests { .unwrap() .try_build() .unwrap(); - let pk1 = sk1.conv_view().unwrap().to_public_key().unwrap(); + let pk1 = ViewBuilder::new(&sk1) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); - let (sealed, ephemeral) = pk1 - .seal_view() + let (sealed, ephemeral) = ViewBuilder::new(&pk1) + .seal() + .build() .unwrap() .seal(b"secret", Codec::Chacha20Poly1305, b"") .unwrap(); assert!( - sk2.open_view() + ViewBuilder::new(&sk2) + .open() + .build() .unwrap() .open(&sealed, ephemeral.as_ref(), b"") .is_err() diff --git a/src/views/slh_dsa.rs b/src/views/slh_dsa.rs index d72c908..a90f26e 100644 --- a/src/views/slh_dsa.rs +++ b/src/views/slh_dsa.rs @@ -1,9 +1,12 @@ // SPDX-License-Identifier: Apache-2.0 //! SLH-DSA multikey view; FIPS 205. Supports all 12 parameter sets (Sha2_128f/s through Shake256f/s). +use crate::views::dispatch::{ + dispatch_attr_view, dispatch_conv_view, dispatch_data_view, dispatch_fingerprint_view, +}; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, SignView, - VerifyView, Views, + VerifyView, error::{AttributesError, ConversionsError, SignError, VerifyError}, }; use multi_codec::Codec; @@ -138,7 +141,7 @@ impl<'a> DataView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -270,7 +273,7 @@ impl<'a> ConvView for View<'a> { } let key_bytes = { - let kd = pk.data_view()?; + let kd = dispatch_data_view(&pk)?; kd.key_bytes()? }; @@ -312,7 +315,7 @@ impl<'a> ConvView for View<'a> { fn to_ssh_private_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -343,7 +346,7 @@ impl<'a> ConvView for View<'a> { let pk = self.to_public_key()?; let key_bytes = { - let kd = pk.data_view()?; + let kd = dispatch_data_view(&pk)?; kd.key_bytes()? }; @@ -374,10 +377,10 @@ impl<'a> ConvView for View<'a> { impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if attr.is_secret_key() { - let pk = self.mk.conv_view()?.to_public_key()?; - return pk.fingerprint_view()?.fingerprint(codec); + let pk = dispatch_conv_view(self.mk)?.to_public_key()?; + return dispatch_fingerprint_view(&pk)?.fingerprint(codec); } let bytes = self.key_bytes()?; Ok(mh::Builder::new_from_bytes(codec, bytes.as_slice())?.try_build()?) @@ -391,12 +394,12 @@ impl<'a> SignView for View<'a> { combined: bool, _scheme: Option, ) -> Result { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if !attr.is_secret_key() { return Err(SignError::NotSigningKey.into()); } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; let (signature, codec) = match self.mk.codec { @@ -507,15 +510,15 @@ impl<'a> VerifyView for View<'a> { } else { return Err(VerifyError::MissingMessage.into()); }; - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; let pubmk = if attr.is_secret_key() { - let kc = self.mk.conv_view()?; + let kc = dispatch_conv_view(self.mk)?; kc.to_public_key()? } else { self.mk.clone() }; let key_bytes = { - let kd = pubmk.data_view()?; + let kd = dispatch_data_view(&pubmk)?; kd.key_bytes()? }; diff --git a/src/views/sntrup.rs b/src/views/sntrup.rs index bec7ab1..94d8611 100644 --- a/src/views/sntrup.rs +++ b/src/views/sntrup.rs @@ -3,11 +3,12 @@ //! //! Supports sntrup761, sntrup857, sntrup953, sntrup1013, and sntrup1277. +use crate::views::dispatch::dispatch_data_view; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, OpenView, SealView, error::{AttributesError, ConversionsError, SealError}, - views::{Views, aead}, + views::aead, }; use multi_codec::Codec; use multi_hash::{Multihash, mh}; @@ -248,7 +249,7 @@ impl<'a> DataView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -287,7 +288,7 @@ impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { let pub_bytes = if self.is_secret_key() { let pk = self.to_public_key()?; - let dv = pk.data_view()?; + let dv = dispatch_data_view(&pk)?; dv.key_bytes()? } else { self.key_bytes()? @@ -372,7 +373,7 @@ impl<'a> OpenView for View<'a> { } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -401,8 +402,8 @@ impl<'a> OpenView for View<'a> { #[cfg(test)] mod tests { use super::*; + use crate::ViewBuilder; use crate::mk::SNTRUP_KEY_CODECS; - use crate::views::Views; #[test] fn test_sntrup_key_gen_roundtrip() { @@ -414,11 +415,11 @@ mod tests { .try_build() .unwrap(); - let attr = mk.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk).attr().build().unwrap(); assert!(attr.is_secret_key()); assert!(!attr.is_public_key()); - let kd = mk.data_view().unwrap(); + let kd = ViewBuilder::new(&mk).data().build().unwrap(); assert!(kd.key_bytes().is_ok()); assert!(kd.secret_bytes().is_ok()); @@ -438,10 +439,10 @@ mod tests { .try_build() .unwrap(); - let conv = mk.conv_view().unwrap(); + let conv = ViewBuilder::new(&mk).conv().build().unwrap(); let pk = conv.to_public_key().unwrap(); - let attr = pk.attr_view().unwrap(); + let attr = ViewBuilder::new(&pk).attr().build().unwrap(); assert!(attr.is_public_key()); assert!(!attr.is_secret_key()); @@ -460,9 +461,15 @@ mod tests { .try_build() .unwrap(); - let pk = mk.conv_view().unwrap().to_public_key().unwrap(); - let fp = pk - .fingerprint_view() + let pk = ViewBuilder::new(&mk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); + let fp = ViewBuilder::new(&pk) + .fingerprint() + .build() .unwrap() .fingerprint(Codec::Sha3256) .unwrap(); @@ -485,17 +492,28 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); for aead_codec in &aead_codecs { let plaintext = b"hello sntrup world!"; - let (sealed, _) = pk - .seal_view() + let (sealed, _) = ViewBuilder::new(&pk) + .seal() + .build() .unwrap() .seal(plaintext, *aead_codec, b"") .unwrap(); - let opened = sk.open_view().unwrap().open(&sealed, None, b"").unwrap(); + let opened = ViewBuilder::new(&sk) + .open() + .build() + .unwrap() + .open(&sealed, None, b"") + .unwrap(); assert_eq!(plaintext.as_slice(), opened.as_slice()); } } @@ -508,22 +526,35 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk1 = sk1.conv_view().unwrap().to_public_key().unwrap(); + let pk1 = ViewBuilder::new(&sk1) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let sk2 = Builder::new_from_random_bytes(Codec::Sntrup761Priv, &mut rng) .unwrap() .try_build() .unwrap(); - let (sealed, _) = pk1 - .seal_view() + let (sealed, _) = ViewBuilder::new(&pk1) + .seal() + .build() .unwrap() .seal(b"secret data", Codec::Xchacha20Poly1305, b"") .unwrap(); // Opening with wrong key should fail (decapsulation will produce different shared secret // and AEAD open will fail) - assert!(sk2.open_view().unwrap().open(&sealed, None, b"").is_err()); + assert!( + ViewBuilder::new(&sk2) + .open() + .build() + .unwrap() + .open(&sealed, None, b"") + .is_err() + ); } #[test] @@ -536,7 +567,9 @@ mod tests { // seal with private key should fail assert!( - sk.seal_view() + ViewBuilder::new(&sk) + .seal() + .build() .unwrap() .seal(b"data", Codec::Xchacha20Poly1305, b"") .is_err() @@ -550,16 +583,29 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); - let (sealed, _) = pk - .seal_view() + let (sealed, _) = ViewBuilder::new(&pk) + .seal() + .build() .unwrap() .seal(b"data", Codec::Xchacha20Poly1305, b"") .unwrap(); // open with public key should fail - assert!(pk.open_view().unwrap().open(&sealed, None, b"").is_err()); + assert!( + ViewBuilder::new(&pk) + .open() + .build() + .unwrap() + .open(&sealed, None, b"") + .is_err() + ); } #[test] @@ -569,11 +615,18 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); // AES-128-GCM is not allowed for sntrup (not PQ-safe) assert!( - pk.seal_view() + ViewBuilder::new(&pk) + .seal() + .build() .unwrap() .seal(b"data", Codec::AesGcm128, b"") .is_err() diff --git a/src/views/threshold_marker.rs b/src/views/threshold_marker.rs index 76ea55f..0923dda 100644 --- a/src/views/threshold_marker.rs +++ b/src/views/threshold_marker.rs @@ -245,9 +245,8 @@ pub fn canonical_marker_bytes(mk: &Multikey) -> Result, Error> { /// threshold/limit are set. `signer` is normally the controller's own signing /// key; the verifier must hold an independently-trusted copy of its public key. pub fn sign_marker(mk: &mut Multikey, signer: &Multikey, scheme: Option) -> Result<(), Error> { - use crate::Views; let bytes = canonical_marker_bytes(mk)?; - let sig = signer.sign_view()?.sign(&bytes, false, scheme)?; + let sig = crate::views::dispatch::dispatch_sign_view(signer)?.sign(&bytes, false, scheme)?; let sig_bytes: Vec = sig.into(); mk.attributes .insert(AttrId::ThresholdMarkerSig, sig_bytes.into()); @@ -260,7 +259,6 @@ pub fn sign_marker(mk: &mut Multikey, signer: &Multikey, scheme: Option) -> /// public key). Returns an error if the signature is absent or does not verify /// over the recomputed canonical bytes — defeating TSIG-1 marker tampering. pub fn verify_marker(mk: &Multikey, verifier_pubkey: &Multikey) -> Result<(), Error> { - use crate::Views; let sig_bytes = mk .attributes .get(&AttrId::ThresholdMarkerSig) @@ -268,8 +266,7 @@ pub fn verify_marker(mk: &Multikey, verifier_pubkey: &Multikey) -> Result<(), Er let sig = multi_sig::Multisig::try_from(sig_bytes.as_slice()) .map_err(|_| AttributesError::ThresholdMarkerSigInvalid)?; let bytes = canonical_marker_bytes(mk)?; - verifier_pubkey - .verify_view()? + crate::views::dispatch::dispatch_verify_view(verifier_pubkey)? .verify(&sig, Some(&bytes)) .map_err(|_| AttributesError::ThresholdMarkerSigInvalid.into()) } @@ -277,7 +274,7 @@ pub fn verify_marker(mk: &Multikey, verifier_pubkey: &Multikey) -> Result<(), Er #[cfg(test)] mod tests { use super::*; - use crate::{Builder, Views}; + use crate::{Builder, ViewBuilder}; fn g1_priv() -> Multikey { Builder::new_from_random_bytes(Codec::Bls12381G1Priv, &mut rand::rng()) .unwrap() @@ -288,7 +285,12 @@ mod tests { #[test] fn classify_split_share_g1() { let mk = g1_priv(); - let shares = mk.threshold_view().unwrap().split(3, 5).unwrap(); + let shares = ViewBuilder::new(&mk) + .threshold() + .build() + .unwrap() + .split(3, 5) + .unwrap(); let share = &shares[0]; assert_eq!(threshold_kind(share), Some(ThresholdScheme::ShamirSplit)); assert_eq!(threshold_params(share), Some((3, 5))); @@ -297,8 +299,18 @@ mod tests { #[test] fn classify_split_pub_share_g1() { let mk = g1_priv(); - let shares = mk.threshold_view().unwrap().split(2, 4).unwrap(); - let pub_share = shares[0].conv_view().unwrap().to_public_key().unwrap(); + let shares = ViewBuilder::new(&mk) + .threshold() + .build() + .unwrap() + .split(2, 4) + .unwrap(); + let pub_share = ViewBuilder::new(&shares[0]) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); assert_eq!(pub_share.codec, Codec::Bls12381G1PubShare); assert_eq!( threshold_kind(&pub_share), diff --git a/src/views/threshold_meta.rs b/src/views/threshold_meta.rs index 0a500e5..1fd0a68 100644 --- a/src/views/threshold_meta.rs +++ b/src/views/threshold_meta.rs @@ -22,7 +22,7 @@ pub use multi_sig::{ }; use crate::{ - AttrId, Error, Multikey, Views, + AttrId, Error, Multikey, error::{AttributesError, ThresholdError}, mk::Attributes, }; @@ -35,7 +35,7 @@ use zeroize::Zeroizing; /// `Multikey` at-rest encryption infrastructure to the threshold metadata /// encryption helpers in `multi_sig`. fn extract_meta_key(meta_key: &Multikey) -> Result>, Error> { - let dv = meta_key.data_view()?; + let dv = crate::views::dispatch::dispatch_data_view(meta_key)?; let key = dv.key_bytes()?; if key.len() != 32 { return Err(Error::Threshold(ThresholdError::MetaEncryption(format!( @@ -277,6 +277,7 @@ impl<'a> ThresholdDisclosureView for DisclosureView<'a> { mod tests { use super::*; use crate::Builder; + use crate::ViewBuilder; use multi_codec::Codec; fn make_meta_key() -> Multikey { @@ -381,14 +382,16 @@ mod tests { let meta_key = make_meta_key(); // convert to Partial - let partial = share - .disclosure_view() + let partial = ViewBuilder::new(&share) + .disclosure() + .build() .unwrap() .to_disclosure(ThresholdDisclosure::Partial, Some(&meta_key), None) .unwrap(); assert_eq!( - partial - .disclosure_view() + ViewBuilder::new(&partial) + .disclosure() + .build() .unwrap() .disclosure_mode() .unwrap(), @@ -401,13 +404,19 @@ mod tests { assert_eq!(n, 5); // convert back to Full - let full = partial - .disclosure_view() + let full = ViewBuilder::new(&partial) + .disclosure() + .build() .unwrap() .to_disclosure(ThresholdDisclosure::Full, None, Some(&meta_key)) .unwrap(); assert_eq!( - full.disclosure_view().unwrap().disclosure_mode().unwrap(), + ViewBuilder::new(&full) + .disclosure() + .build() + .unwrap() + .disclosure_mode() + .unwrap(), ThresholdDisclosure::Full ); let (t, n) = read_threshold_params(&full, None).unwrap(); @@ -421,8 +430,9 @@ mod tests { let meta_key = make_meta_key(); // convert to FullConfidentialial - let encrypted = share - .disclosure_view() + let encrypted = ViewBuilder::new(&share) + .disclosure() + .build() .unwrap() .to_disclosure( ThresholdDisclosure::FullConfidentialial, @@ -431,8 +441,9 @@ mod tests { ) .unwrap(); assert_eq!( - encrypted - .disclosure_view() + ViewBuilder::new(&encrypted) + .disclosure() + .build() .unwrap() .disclosure_mode() .unwrap(), @@ -445,8 +456,9 @@ mod tests { assert_eq!(n, 5); // convert back to Full - let full = encrypted - .disclosure_view() + let full = ViewBuilder::new(&encrypted) + .disclosure() + .build() .unwrap() .to_disclosure(ThresholdDisclosure::Full, None, Some(&meta_key)) .unwrap(); @@ -459,8 +471,9 @@ mod tests { fn test_read_encrypted_without_meta_key() { let share = make_share(3, 5); let meta_key = make_meta_key(); - let encrypted = share - .disclosure_view() + let encrypted = ViewBuilder::new(&share) + .disclosure() + .build() .unwrap() .to_disclosure( ThresholdDisclosure::FullConfidentialial, @@ -474,11 +487,11 @@ mod tests { #[test] fn test_convert_to_partial_without_meta_key() { let share = make_share(3, 5); - let result = share.disclosure_view().unwrap().to_disclosure( - ThresholdDisclosure::Partial, - None, - None, - ); + let result = ViewBuilder::new(&share) + .disclosure() + .build() + .unwrap() + .to_disclosure(ThresholdDisclosure::Partial, None, None); assert!(result.is_err()); } diff --git a/src/views/x25519.rs b/src/views/x25519.rs index e3677f3..7d787b9 100644 --- a/src/views/x25519.rs +++ b/src/views/x25519.rs @@ -1,11 +1,12 @@ // SPDX-License-Identifier: Apache-2.0 //! X25519 ECIES multikey view; Curve25519 Diffie-Hellman key agreement + AEAD. +use crate::views::dispatch::dispatch_data_view; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, OpenView, SealView, error::{AttributesError, ConversionsError, SealError}, - views::{Views, aead}, + views::aead, }; use multi_codec::Codec; use multi_hash::{Multihash, mh}; @@ -76,7 +77,7 @@ impl<'a> DataView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -119,7 +120,7 @@ impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { let pub_bytes = if self.is_secret_key() { let pk = self.to_public_key()?; - let dv = pk.data_view()?; + let dv = dispatch_data_view(&pk)?; dv.key_bytes()? } else { self.key_bytes()? @@ -221,7 +222,7 @@ impl<'a> OpenView for View<'a> { return Err(SealError::UnsupportedAeadCodec(aead_codec).into()); } - let ephemeral_pub_bytes = ephemeral_mk.data_view()?.key_bytes()?; + let ephemeral_pub_bytes = dispatch_data_view(ephemeral_mk)?.key_bytes()?; if ephemeral_pub_bytes.len() != X25519_PUBLIC_LENGTH { return Err( SealError::InvalidFormat("invalid ephemeral public key length".into()).into(), @@ -229,7 +230,7 @@ impl<'a> OpenView for View<'a> { } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -262,8 +263,8 @@ impl<'a> OpenView for View<'a> { #[cfg(test)] mod tests { use super::*; + use crate::ViewBuilder; use crate::mk::X25519_KEY_CODECS; - use crate::views::Views; #[test] fn test_x25519_key_gen_roundtrip() { @@ -275,7 +276,7 @@ mod tests { .try_build() .unwrap(); - let attr = mk.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk).attr().build().unwrap(); assert!(attr.is_secret_key()); assert!(!attr.is_public_key()); @@ -294,10 +295,10 @@ mod tests { .try_build() .unwrap(); - let conv = mk.conv_view().unwrap(); + let conv = ViewBuilder::new(&mk).conv().build().unwrap(); let pk = conv.to_public_key().unwrap(); - let attr = pk.attr_view().unwrap(); + let attr = ViewBuilder::new(&pk).attr().build().unwrap(); assert!(attr.is_public_key()); assert!(!attr.is_secret_key()); @@ -314,9 +315,15 @@ mod tests { .try_build() .unwrap(); - let pk = mk.conv_view().unwrap().to_public_key().unwrap(); - let fp = pk - .fingerprint_view() + let pk = ViewBuilder::new(&mk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); + let fp = ViewBuilder::new(&pk) + .fingerprint() + .build() .unwrap() .fingerprint(Codec::Sha3256) .unwrap(); @@ -337,18 +344,25 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); for aead_codec in &aead_codecs { let plaintext = b"hello X25519 ECIES world!"; - let (sealed, ephemeral) = pk - .seal_view() + let (sealed, ephemeral) = ViewBuilder::new(&pk) + .seal() + .build() .unwrap() .seal(plaintext, *aead_codec, b"") .unwrap(); - let opened = sk - .open_view() + let opened = ViewBuilder::new(&sk) + .open() + .build() .unwrap() .open(&sealed, ephemeral.as_ref(), b"") .unwrap(); @@ -363,22 +377,30 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk1 = sk1.conv_view().unwrap().to_public_key().unwrap(); + let pk1 = ViewBuilder::new(&sk1) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let sk2 = Builder::new_from_random_bytes(Codec::X25519Priv, &mut rng) .unwrap() .try_build() .unwrap(); - let (sealed, ephemeral) = pk1 - .seal_view() + let (sealed, ephemeral) = ViewBuilder::new(&pk1) + .seal() + .build() .unwrap() .seal(b"secret data", Codec::Chacha20Poly1305, b"") .unwrap(); // Opening with wrong key should fail assert!( - sk2.open_view() + ViewBuilder::new(&sk2) + .open() + .build() .unwrap() .open(&sealed, ephemeral.as_ref(), b"") .is_err() @@ -394,7 +416,9 @@ mod tests { .unwrap(); assert!( - sk.seal_view() + ViewBuilder::new(&sk) + .seal() + .build() .unwrap() .seal(b"data", Codec::Chacha20Poly1305, b"") .is_err() @@ -408,16 +432,24 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); - let (sealed, ephemeral) = pk - .seal_view() + let (sealed, ephemeral) = ViewBuilder::new(&pk) + .seal() + .build() .unwrap() .seal(b"data", Codec::Chacha20Poly1305, b"") .unwrap(); assert!( - pk.open_view() + ViewBuilder::new(&pk) + .open() + .build() .unwrap() .open(&sealed, ephemeral.as_ref(), b"") .is_err() diff --git a/src/views/x25519_frodokem640.rs b/src/views/x25519_frodokem640.rs index 18beb8e..94e473d 100644 --- a/src/views/x25519_frodokem640.rs +++ b/src/views/x25519_frodokem640.rs @@ -6,11 +6,12 @@ //! Private key layout: `x25519_seed (32) || frodokem_secret_key`. //! Public key layout (classical-first): `x25519_pub (32) || frodokem_public_key`. +use crate::views::dispatch::dispatch_data_view; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, OpenView, SealView, error::{AttributesError, ConversionsError, SealError}, - views::{Views, aead}, + views::aead, }; use multi_codec::Codec; use multi_hash::{Multihash, mh}; @@ -154,7 +155,7 @@ impl<'a> DataView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -203,7 +204,7 @@ impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { let pub_bytes = if self.is_secret_key() { let pk = self.to_public_key()?; - let dv = pk.data_view()?; + let dv = dispatch_data_view(&pk)?; dv.key_bytes()? } else { self.key_bytes()? @@ -330,7 +331,7 @@ impl<'a> OpenView for View<'a> { } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; if secret_bytes.len() <= X25519_SEED_LEN { @@ -383,7 +384,7 @@ impl<'a> OpenView for View<'a> { #[cfg(test)] mod tests { use super::*; - use crate::views::Views; + use crate::ViewBuilder; fn roundtrip(priv_codec: Codec) { let mut rng = rand::rng(); @@ -392,15 +393,26 @@ mod tests { .with_comment("x25519-frodokem hybrid test") .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let plaintext = b"hello X25519-FrodoKEM-640 hybrid KEM!"; - let (sealed, _) = pk - .seal_view() + let (sealed, _) = ViewBuilder::new(&pk) + .seal() + .build() .unwrap() .seal(plaintext, Codec::Chacha20Poly1305, b"") .unwrap(); - let opened = sk.open_view().unwrap().open(&sealed, None, b"").unwrap(); + let opened = ViewBuilder::new(&sk) + .open() + .build() + .unwrap() + .open(&sealed, None, b"") + .unwrap(); assert_eq!(plaintext.as_slice(), opened.as_slice()); // wrong key fails @@ -408,7 +420,14 @@ mod tests { .unwrap() .try_build() .unwrap(); - assert!(sk2.open_view().unwrap().open(&sealed, None, b"").is_err()); + assert!( + ViewBuilder::new(&sk2) + .open() + .build() + .unwrap() + .open(&sealed, None, b"") + .is_err() + ); } #[test] diff --git a/src/views/x25519_mceliece348864.rs b/src/views/x25519_mceliece348864.rs index bd8d55f..bc04beb 100644 --- a/src/views/x25519_mceliece348864.rs +++ b/src/views/x25519_mceliece348864.rs @@ -12,11 +12,12 @@ //! challenges; see for the //! recovered keys. It compiles only with the `deprecated` feature. +use crate::views::dispatch::dispatch_data_view; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, OpenView, SealView, error::{AttributesError, ConversionsError, SealError}, - views::{Views, aead}, + views::aead, }; use multi_codec::Codec; use multi_hash::{Multihash, mh}; @@ -87,7 +88,7 @@ impl<'a> DataView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -140,7 +141,7 @@ impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { let pub_bytes = if self.is_secret_key() { let pk = self.to_public_key()?; - let dv = pk.data_view()?; + let dv = dispatch_data_view(&pk)?; dv.key_bytes()? } else { self.key_bytes()? @@ -275,7 +276,7 @@ impl<'a> OpenView for View<'a> { } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; if secret_bytes.len() != PRIV_SEED_LEN { @@ -335,7 +336,7 @@ impl<'a> OpenView for View<'a> { #[cfg(all(test, feature = "slow-tests"))] mod tests { use super::*; - use crate::views::Views; + use crate::ViewBuilder; #[test] fn test_seal_open_roundtrip() { @@ -345,15 +346,26 @@ mod tests { .with_comment("x25519-mceliece hybrid test") .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let plaintext = b"hello X25519-Classic-McEliece-348864 hybrid KEM!"; - let (sealed, _) = pk - .seal_view() + let (sealed, _) = ViewBuilder::new(&pk) + .seal() + .build() .unwrap() .seal(plaintext, Codec::Chacha20Poly1305, b"") .unwrap(); - let opened = sk.open_view().unwrap().open(&sealed, None, b"").unwrap(); + let opened = ViewBuilder::new(&sk) + .open() + .build() + .unwrap() + .open(&sealed, None, b"") + .unwrap(); assert_eq!(plaintext.as_slice(), opened.as_slice()); } @@ -364,17 +376,30 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk1 = sk1.conv_view().unwrap().to_public_key().unwrap(); + let pk1 = ViewBuilder::new(&sk1) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let sk2 = Builder::new_from_random_bytes(Codec::X25519Mceliece348864Priv, &mut rng) .unwrap() .try_build() .unwrap(); - let (sealed, _) = pk1 - .seal_view() + let (sealed, _) = ViewBuilder::new(&pk1) + .seal() + .build() .unwrap() .seal(b"secret", Codec::Chacha20Poly1305, b"") .unwrap(); - assert!(sk2.open_view().unwrap().open(&sealed, None, b"").is_err()); + assert!( + ViewBuilder::new(&sk2) + .open() + .build() + .unwrap() + .open(&sealed, None, b"") + .is_err() + ); } } diff --git a/src/views/x25519_mlkem768.rs b/src/views/x25519_mlkem768.rs index 08d2ce1..e8c8f96 100644 --- a/src/views/x25519_mlkem768.rs +++ b/src/views/x25519_mlkem768.rs @@ -2,11 +2,12 @@ //! X25519-ML-KEM-768 hybrid KEM multikey view; combines X25519 ECDH with ML-KEM-768 KEM, //! ChaCha20-Poly1305 AEAD, and SHA-512 KDF. +use crate::views::dispatch::dispatch_data_view; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, OpenView, SealView, error::{AttributesError, ConversionsError, SealError}, - views::{Views, aead}, + views::aead, }; use ml_kem::{ MlKem768, @@ -83,7 +84,7 @@ impl<'a> DataView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -135,7 +136,7 @@ impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { let pub_bytes = if self.is_secret_key() { let pk = self.to_public_key()?; - let dv = pk.data_view()?; + let dv = dispatch_data_view(&pk)?; dv.key_bytes()? } else { self.key_bytes()? @@ -282,7 +283,7 @@ impl<'a> OpenView for View<'a> { } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -342,8 +343,8 @@ impl<'a> OpenView for View<'a> { #[cfg(test)] mod tests { use super::*; + use crate::ViewBuilder; use crate::mk::X25519_MLKEM768_KEY_CODECS; - use crate::views::Views; #[test] fn test_key_gen_roundtrip() { @@ -355,7 +356,7 @@ mod tests { .try_build() .unwrap(); - let attr = mk.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk).attr().build().unwrap(); assert!(attr.is_secret_key()); assert!(!attr.is_public_key()); @@ -374,10 +375,10 @@ mod tests { .try_build() .unwrap(); - let conv = mk.conv_view().unwrap(); + let conv = ViewBuilder::new(&mk).conv().build().unwrap(); let pk = conv.to_public_key().unwrap(); - let attr = pk.attr_view().unwrap(); + let attr = ViewBuilder::new(&pk).attr().build().unwrap(); assert!(attr.is_public_key()); assert!(!attr.is_secret_key()); @@ -386,7 +387,7 @@ mod tests { assert_eq!(pk, pk2); // check public key length - let dv = pk.data_view().unwrap(); + let dv = ViewBuilder::new(&pk).data().build().unwrap(); assert_eq!(dv.key_bytes().unwrap().len(), PUB_KEY_LEN); } @@ -398,9 +399,15 @@ mod tests { .try_build() .unwrap(); - let pk = mk.conv_view().unwrap().to_public_key().unwrap(); - let fp = pk - .fingerprint_view() + let pk = ViewBuilder::new(&mk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); + let fp = ViewBuilder::new(&pk) + .fingerprint() + .build() .unwrap() .fingerprint(Codec::Sha3256) .unwrap(); @@ -415,16 +422,27 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let plaintext = b"hello X25519-ML-KEM-768 hybrid KEM!"; - let (sealed, _) = pk - .seal_view() + let (sealed, _) = ViewBuilder::new(&pk) + .seal() + .build() .unwrap() .seal(plaintext, Codec::Chacha20Poly1305, b"") .unwrap(); - let opened = sk.open_view().unwrap().open(&sealed, None, b"").unwrap(); + let opened = ViewBuilder::new(&sk) + .open() + .build() + .unwrap() + .open(&sealed, None, b"") + .unwrap(); assert_eq!(plaintext.as_slice(), opened.as_slice()); } @@ -435,20 +453,33 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk1 = sk1.conv_view().unwrap().to_public_key().unwrap(); + let pk1 = ViewBuilder::new(&sk1) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let sk2 = Builder::new_from_random_bytes(Codec::X25519Mlkem768Priv, &mut rng) .unwrap() .try_build() .unwrap(); - let (sealed, _) = pk1 - .seal_view() + let (sealed, _) = ViewBuilder::new(&pk1) + .seal() + .build() .unwrap() .seal(b"secret data", Codec::Chacha20Poly1305, b"") .unwrap(); - assert!(sk2.open_view().unwrap().open(&sealed, None, b"").is_err()); + assert!( + ViewBuilder::new(&sk2) + .open() + .build() + .unwrap() + .open(&sealed, None, b"") + .is_err() + ); } #[test] @@ -460,7 +491,9 @@ mod tests { .unwrap(); assert!( - sk.seal_view() + ViewBuilder::new(&sk) + .seal() + .build() .unwrap() .seal(b"data", Codec::Chacha20Poly1305, b"") .is_err() @@ -474,15 +507,28 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); - let (sealed, _) = pk - .seal_view() + let (sealed, _) = ViewBuilder::new(&pk) + .seal() + .build() .unwrap() .seal(b"data", Codec::Chacha20Poly1305, b"") .unwrap(); - assert!(pk.open_view().unwrap().open(&sealed, None, b"").is_err()); + assert!( + ViewBuilder::new(&pk) + .open() + .build() + .unwrap() + .open(&sealed, None, b"") + .is_err() + ); } #[test] @@ -492,16 +538,25 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); assert!( - pk.seal_view() + ViewBuilder::new(&pk) + .seal() + .build() .unwrap() .seal(b"data", Codec::AesGcm128, b"") .is_err() ); assert!( - pk.seal_view() + ViewBuilder::new(&pk) + .seal() + .build() .unwrap() .seal(b"data", Codec::Xchacha20Poly1305, b"") .is_err() diff --git a/src/views/x25519_sntrup761.rs b/src/views/x25519_sntrup761.rs index 4b436b3..a3378bb 100644 --- a/src/views/x25519_sntrup761.rs +++ b/src/views/x25519_sntrup761.rs @@ -2,11 +2,12 @@ //! X25519-sntrup761 hybrid KEM multikey view; combines X25519 ECDH with sntrup761 KEM, //! ChaCha20-Poly1305 AEAD, and a BLAKE3 combiner feeding HKDF-SHA512. +use crate::views::dispatch::dispatch_data_view; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, OpenView, SealView, error::{AttributesError, ConversionsError, SealError}, - views::{Views, aead}, + views::aead, }; use multi_codec::Codec; use multi_hash::{Multihash, mh}; @@ -98,7 +99,7 @@ impl<'a> DataView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -150,7 +151,7 @@ impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { let pub_bytes = if self.is_secret_key() { let pk = self.to_public_key()?; - let dv = pk.data_view()?; + let dv = dispatch_data_view(&pk)?; dv.key_bytes()? } else { self.key_bytes()? @@ -290,7 +291,7 @@ impl<'a> OpenView for View<'a> { } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -349,8 +350,8 @@ impl<'a> OpenView for View<'a> { #[cfg(test)] mod tests { use super::*; + use crate::ViewBuilder; use crate::mk::X25519_SNTRUP761_KEY_CODECS; - use crate::views::Views; #[test] fn test_key_gen_roundtrip() { @@ -362,7 +363,7 @@ mod tests { .try_build() .unwrap(); - let attr = mk.attr_view().unwrap(); + let attr = ViewBuilder::new(&mk).attr().build().unwrap(); assert!(attr.is_secret_key()); assert!(!attr.is_public_key()); @@ -381,10 +382,10 @@ mod tests { .try_build() .unwrap(); - let conv = mk.conv_view().unwrap(); + let conv = ViewBuilder::new(&mk).conv().build().unwrap(); let pk = conv.to_public_key().unwrap(); - let attr = pk.attr_view().unwrap(); + let attr = ViewBuilder::new(&pk).attr().build().unwrap(); assert!(attr.is_public_key()); assert!(!attr.is_secret_key()); @@ -393,7 +394,7 @@ mod tests { assert_eq!(pk, pk2); // check public key length - let dv = pk.data_view().unwrap(); + let dv = ViewBuilder::new(&pk).data().build().unwrap(); assert_eq!(dv.key_bytes().unwrap().len(), PUB_KEY_LEN); } @@ -405,9 +406,15 @@ mod tests { .try_build() .unwrap(); - let pk = mk.conv_view().unwrap().to_public_key().unwrap(); - let fp = pk - .fingerprint_view() + let pk = ViewBuilder::new(&mk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); + let fp = ViewBuilder::new(&pk) + .fingerprint() + .build() .unwrap() .fingerprint(Codec::Sha3256) .unwrap(); @@ -422,16 +429,27 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let plaintext = b"hello X25519-sntrup761 hybrid KEM!"; - let (sealed, _) = pk - .seal_view() + let (sealed, _) = ViewBuilder::new(&pk) + .seal() + .build() .unwrap() .seal(plaintext, Codec::Chacha20Poly1305, b"") .unwrap(); - let opened = sk.open_view().unwrap().open(&sealed, None, b"").unwrap(); + let opened = ViewBuilder::new(&sk) + .open() + .build() + .unwrap() + .open(&sealed, None, b"") + .unwrap(); assert_eq!(plaintext.as_slice(), opened.as_slice()); } @@ -442,20 +460,33 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk1 = sk1.conv_view().unwrap().to_public_key().unwrap(); + let pk1 = ViewBuilder::new(&sk1) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let sk2 = Builder::new_from_random_bytes(Codec::X25519Sntrup761Priv, &mut rng) .unwrap() .try_build() .unwrap(); - let (sealed, _) = pk1 - .seal_view() + let (sealed, _) = ViewBuilder::new(&pk1) + .seal() + .build() .unwrap() .seal(b"secret data", Codec::Chacha20Poly1305, b"") .unwrap(); - assert!(sk2.open_view().unwrap().open(&sealed, None, b"").is_err()); + assert!( + ViewBuilder::new(&sk2) + .open() + .build() + .unwrap() + .open(&sealed, None, b"") + .is_err() + ); } #[test] @@ -467,7 +498,9 @@ mod tests { .unwrap(); assert!( - sk.seal_view() + ViewBuilder::new(&sk) + .seal() + .build() .unwrap() .seal(b"data", Codec::Chacha20Poly1305, b"") .is_err() @@ -481,15 +514,28 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); - let (sealed, _) = pk - .seal_view() + let (sealed, _) = ViewBuilder::new(&pk) + .seal() + .build() .unwrap() .seal(b"data", Codec::Chacha20Poly1305, b"") .unwrap(); - assert!(pk.open_view().unwrap().open(&sealed, None, b"").is_err()); + assert!( + ViewBuilder::new(&pk) + .open() + .build() + .unwrap() + .open(&sealed, None, b"") + .is_err() + ); } #[test] @@ -499,17 +545,26 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); // Only ChaCha20-Poly1305 is allowed assert!( - pk.seal_view() + ViewBuilder::new(&pk) + .seal() + .build() .unwrap() .seal(b"data", Codec::AesGcm128, b"") .is_err() ); assert!( - pk.seal_view() + ViewBuilder::new(&pk) + .seal() + .build() .unwrap() .seal(b"data", Codec::Xchacha20Poly1305, b"") .is_err() diff --git a/src/views/xeddsa.rs b/src/views/xeddsa.rs index 0e20778..e6c6a42 100644 --- a/src/views/xeddsa.rs +++ b/src/views/xeddsa.rs @@ -21,9 +21,10 @@ //! 4. The verification equation is standard Ed25519 over the lifted point, //! checked with strict verification. +use crate::views::dispatch::{dispatch_attr_view, dispatch_conv_view, dispatch_data_view}; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, SignView, - VerifyView, Views, + VerifyView, error::{AttributesError, ConversionsError, SignError, VerifyError}, }; use curve25519_dalek::{edwards::EdwardsPoint, montgomery::MontgomeryPoint, scalar::Scalar}; @@ -222,7 +223,7 @@ impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { let pub_bytes = if self.is_secret_key() { let pk = self.to_public_key()?; - let dv = pk.data_view()?; + let dv = dispatch_data_view(&pk)?; dv.key_bytes()? } else { self.key_bytes()? @@ -239,7 +240,7 @@ impl<'a> ConvView for View<'a> { /// point, so the derived key verifies under the XEdDSA verifier. fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; let public = x25519_dalek::PublicKey::from(&x25519_dalek::StaticSecret::from( @@ -275,7 +276,7 @@ impl<'a> SignView for View<'a> { /// is the only authorized caller class, and its messages are 32-byte /// transcript challenges. fn sign(&self, msg: &[u8], combined: bool, _scheme: Option) -> Result { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if !attr.is_secret_key() { return Err(SignError::NotSigningKey.into()); } @@ -286,7 +287,7 @@ impl<'a> SignView for View<'a> { } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; @@ -337,9 +338,9 @@ impl<'a> VerifyView for View<'a> { /// non-canonical u encoding, a small-order key, and a mixed-order key. /// Signature verification is strict: a non-canonical R or S is rejected. fn verify(&self, multisig: &Multisig, msg: Option<&[u8]>) -> Result<(), Error> { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; let pubmk = if attr.is_secret_key() { - self.mk.conv_view()?.to_public_key()? + dispatch_conv_view(self.mk)?.to_public_key()? } else { self.mk.clone() }; @@ -349,7 +350,7 @@ impl<'a> VerifyView for View<'a> { } let key_bytes = { - let kd = pubmk.data_view()?; + let kd = dispatch_data_view(&pubmk)?; kd.key_bytes()? }; @@ -399,7 +400,7 @@ impl<'a> VerifyView for View<'a> { #[cfg(test)] mod tests { use super::*; - use crate::{Builder, Views}; + use crate::{Builder, ViewBuilder}; fn key_pair_mks() -> (Multikey, Multikey) { let mut rng = rand::rng(); @@ -407,7 +408,12 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); (sk, pk) } @@ -422,15 +428,25 @@ mod tests { for _ in 0..8 { let (sk, pk) = key_pair_mks(); let msg = message32(); - let sig = sk.sign_view().unwrap().sign(&msg, false, None).unwrap(); - pk.verify_view().unwrap().verify(&sig, Some(&msg)).unwrap(); + let sig = ViewBuilder::new(&sk) + .sign() + .build() + .unwrap() + .sign(&msg, false, None) + .unwrap(); + ViewBuilder::new(&pk) + .verify() + .build() + .unwrap() + .verify(&sig, Some(&msg)) + .unwrap(); } } #[test] fn test_xeddsa_rejects_non_32_byte_message() { let (sk, _pk) = key_pair_mks(); - let sv = sk.sign_view().unwrap(); + let sv = ViewBuilder::new(&sk).sign().build().unwrap(); assert!(sv.sign(b"short", false, None).is_err()); assert!(sv.sign(&[0u8; 33], false, None).is_err()); assert!(sv.sign(&[0u8; 31], false, None).is_err()); @@ -441,9 +457,16 @@ mod tests { let (sk, pk) = key_pair_mks(); let msg = message32(); let other = message32(); - let sig = sk.sign_view().unwrap().sign(&msg, false, None).unwrap(); + let sig = ViewBuilder::new(&sk) + .sign() + .build() + .unwrap() + .sign(&msg, false, None) + .unwrap(); assert!( - pk.verify_view() + ViewBuilder::new(&pk) + .verify() + .build() .unwrap() .verify(&sig, Some(&other)) .is_err() @@ -455,22 +478,46 @@ mod tests { let (sk, _pk) = key_pair_mks(); let (_sk2, pk2) = key_pair_mks(); let msg = message32(); - let sig = sk.sign_view().unwrap().sign(&msg, false, None).unwrap(); - assert!(pk2.verify_view().unwrap().verify(&sig, Some(&msg)).is_err()); + let sig = ViewBuilder::new(&sk) + .sign() + .build() + .unwrap() + .sign(&msg, false, None) + .unwrap(); + assert!( + ViewBuilder::new(&pk2) + .verify() + .build() + .unwrap() + .verify(&sig, Some(&msg)) + .is_err() + ); } #[test] fn test_xeddsa_public_key_cannot_sign() { let (_sk, pk) = key_pair_mks(); let msg = message32(); - assert!(pk.sign_view().unwrap().sign(&msg, false, None).is_err()); + assert!( + ViewBuilder::new(&pk) + .sign() + .build() + .unwrap() + .sign(&msg, false, None) + .is_err() + ); } #[test] fn test_xeddsa_tampered_signature_fails() { let (sk, pk) = key_pair_mks(); let msg = message32(); - let sig = sk.sign_view().unwrap().sign(&msg, false, None).unwrap(); + let sig = ViewBuilder::new(&sk) + .sign() + .build() + .unwrap() + .sign(&msg, false, None) + .unwrap(); let view = sig.data_view().unwrap(); let mut bytes = view.sig_bytes().unwrap(); bytes[0] ^= 1; @@ -479,7 +526,9 @@ mod tests { .try_build() .unwrap(); assert!( - pk.verify_view() + ViewBuilder::new(&pk) + .verify() + .build() .unwrap() .verify(&tampered, Some(&msg)) .is_err() @@ -499,7 +548,12 @@ mod tests { .with_key_bytes(&seed) .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); // The published u must be the standard RFC 7748 ladder output. let expected_u: [u8; 32] = [ @@ -507,7 +561,7 @@ mod tests { 0xf7, 0x5a, 0x0d, 0xbf, 0x3a, 0x0d, 0x26, 0x38, 0x1a, 0xf4, 0xeb, 0xa4, 0xa9, 0x8e, 0xaa, 0x9b, 0x4e, 0x6a, ]; - let dv = pk.data_view().unwrap(); + let dv = ViewBuilder::new(&pk).data().build().unwrap(); let pub_bytes = dv.key_bytes().unwrap(); let mut pub_arr = [0u8; 32]; pub_arr.copy_from_slice(pub_bytes.as_slice()); @@ -525,8 +579,18 @@ mod tests { // Sign and verify a fixed 32-byte message. The nonce is derived per // the spec with a fresh random Z, so only verify is deterministic. let msg: [u8; 32] = core::array::from_fn(|i| (i as u8).wrapping_mul(7)); - let sig = sk.sign_view().unwrap().sign(&msg, false, None).unwrap(); - pk.verify_view().unwrap().verify(&sig, Some(&msg)).unwrap(); + let sig = ViewBuilder::new(&sk) + .sign() + .build() + .unwrap() + .sign(&msg, false, None) + .unwrap(); + ViewBuilder::new(&pk) + .verify() + .build() + .unwrap() + .verify(&sig, Some(&msg)) + .unwrap(); // Every signature is 64 bytes of R || s. let dv = sig.data_view().unwrap(); @@ -540,7 +604,7 @@ mod tests { // deterministic and returns sign 0. for _ in 0..16 { let (sk, pk) = key_pair_mks(); - let dv = pk.data_view().unwrap(); + let dv = ViewBuilder::new(&pk).data().build().unwrap(); let mut u = [0u8; 32]; u.copy_from_slice(dv.key_bytes().unwrap().as_slice()); let lift = canonical_lift(&u).unwrap(); @@ -554,7 +618,12 @@ mod tests { fn test_xeddsa_small_order_keys_rejected() { let (sk, _pk) = key_pair_mks(); let msg = message32(); - let sig = sk.sign_view().unwrap().sign(&msg, false, None).unwrap(); + let sig = ViewBuilder::new(&sk) + .sign() + .build() + .unwrap() + .sign(&msg, false, None) + .unwrap(); for small in &SMALL_ORDER_PUBKEYS { let pk = Builder::new(Codec::X25519Pub) @@ -562,7 +631,12 @@ mod tests { .try_build() .unwrap(); assert!( - pk.verify_view().unwrap().verify(&sig, Some(&msg)).is_err(), + ViewBuilder::new(&pk) + .verify() + .build() + .unwrap() + .verify(&sig, Some(&msg)) + .is_err(), "small-order key {small:?} must not verify" ); } diff --git a/src/views/xmss.rs b/src/views/xmss.rs index a31609a..dacbbb0 100644 --- a/src/views/xmss.rs +++ b/src/views/xmss.rs @@ -13,11 +13,11 @@ //! [`SignView::sign_advance`] to obtain both the signature and the advanced //! secret key in one step. +use crate::views::dispatch::{dispatch_attr_view, dispatch_conv_view, dispatch_data_view}; use crate::{ AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, SignView, VerifyView, error::{AttributesError, ConversionsError, SignError, VerifyError}, - views::Views, }; use multi_codec::Codec; use multi_hash::{Multihash, mh}; @@ -285,7 +285,7 @@ impl<'a> DataView for View<'a> { impl<'a> ConvView for View<'a> { fn to_public_key(&self) -> Result { let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; let pub_bytes = public_from_private(self.mk.codec, secret_bytes.as_slice())?; @@ -313,7 +313,7 @@ impl<'a> FingerprintView for View<'a> { fn fingerprint(&self, codec: Codec) -> Result { let pub_bytes = if self.is_secret_key() { let pk = self.to_public_key()?; - let dv = pk.data_view()?; + let dv = dispatch_data_view(&pk)?; dv.key_bytes()? } else { self.key_bytes()? @@ -329,12 +329,12 @@ impl<'a> SignView for View<'a> { combined: bool, _scheme: Option, ) -> Result { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if !attr.is_secret_key() { return Err(SignError::NotSigningKey.into()); } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; let sig = sign_bytes(self.mk.codec, secret_bytes.as_slice(), msg)?; @@ -352,12 +352,12 @@ impl<'a> SignView for View<'a> { combined: bool, _scheme: Option, ) -> Result<(multi_sig::Multisig, Multikey), Error> { - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; if !attr.is_secret_key() || !is_xmss_priv(self.mk.codec) { return Err(SignError::NotSigningKey.into()); } let secret_bytes = { - let kd = self.mk.data_view()?; + let kd = dispatch_data_view(self.mk)?; kd.secret_bytes()? }; let sig = sign_bytes(self.mk.codec, secret_bytes.as_slice(), msg)?; @@ -377,16 +377,16 @@ impl<'a> VerifyView for View<'a> { return Err(VerifyError::MissingMessage.into()); }; - let attr = self.mk.attr_view()?; + let attr = dispatch_attr_view(self.mk)?; let pubmk = if attr.is_secret_key() { - let kc = self.mk.conv_view()?; + let kc = dispatch_conv_view(self.mk)?; kc.to_public_key()? } else { self.mk.clone() }; let key_bytes = { - let kd = pubmk.data_view()?; + let kd = dispatch_data_view(&pubmk)?; kd.key_bytes()? }; let sv = sig.data_view()?; @@ -407,6 +407,8 @@ mod tests { use super::*; #[cfg(feature = "slow-tests")] use crate::Builder; + #[cfg(feature = "slow-tests")] + use crate::ViewBuilder; #[cfg(feature = "slow-tests")] #[test] @@ -417,20 +419,42 @@ mod tests { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); let msg = b"provenance entry bytes"; - let ms = sk.sign_view().unwrap().sign(msg, false, None).unwrap(); + let ms = ViewBuilder::new(&sk) + .sign() + .build() + .unwrap() + .sign(msg, false, None) + .unwrap(); // the consumed leaf index (0) travels with the signature assert_eq!(ms.sig_index(), Some(0)); // verify with the public key and with the secret key's derived public key - pk.verify_view().unwrap().verify(&ms, Some(msg)).unwrap(); - sk.verify_view().unwrap().verify(&ms, Some(msg)).unwrap(); + ViewBuilder::new(&pk) + .verify() + .build() + .unwrap() + .verify(&ms, Some(msg)) + .unwrap(); + ViewBuilder::new(&sk) + .verify() + .build() + .unwrap() + .verify(&ms, Some(msg)) + .unwrap(); // wrong message must fail assert!( - pk.verify_view() + ViewBuilder::new(&pk) + .verify() + .build() .unwrap() .verify(&ms, Some(b"tampered")) .is_err() @@ -446,14 +470,20 @@ mod tests { .try_build() .unwrap(); - let (ms0, advanced) = sk - .sign_view() + let (ms0, advanced) = ViewBuilder::new(&sk) + .sign() + .build() .unwrap() .sign_advance(b"first", false, None) .unwrap(); assert_eq!(ms0.sig_index(), Some(0)); // advanced secret key now points at index 1 - let advanced_bytes = advanced.data_view().unwrap().secret_bytes().unwrap(); + let advanced_bytes = ViewBuilder::new(&advanced) + .data() + .build() + .unwrap() + .secret_bytes() + .unwrap(); assert_eq!(xmss_wrapper::current_index(&advanced_bytes).unwrap(), 1); } } diff --git a/tests/custom_codec_tests.rs b/tests/custom_codec_tests.rs new file mode 100644 index 0000000..cbbf6bd --- /dev/null +++ b/tests/custom_codec_tests.rs @@ -0,0 +1,463 @@ +// SPDX-License-Identifier: Apache-2.0 +//! End-to-end tests for custom protocol keys (sigil-0, the +//! `Codec::Identity` codec carrying `AlgorithmName` and `KeyType` +//! attributes): public construction through the `Builder` attribute +//! setters, wire and serde roundtrips, and `ViewBuilder` local-codec +//! factory dispatch including the `AlgorithmName` disambiguation +//! convention for keys that share a codec. + +use multi_codec::Codec; +use multi_hash::{Multihash, mh}; +use multi_key::{ + AttrId, AttrView, Builder, ConvView, DataView, Error, FingerprintView, Multikey, SignView, + VerifyView, ViewBuilder, +}; +use multi_sig::Multisig; +use multi_trait::Null; +use multi_util::CodecInfo; +use ssh_key::{PrivateKey, PublicKey}; +use zeroize::Zeroizing; + +/// The raw `KeyType` attribute byte of `mk`, or 0 (public) when the +/// attribute is absent or empty, per the byte convention from +/// provenance-specifications#4. +fn key_type_byte(mk: &Multikey) -> u8 { + mk.attributes + .get(&AttrId::KeyType) + .and_then(|t| t.first().copied()) + .unwrap_or(0) +} + +/// The `AlgorithmName` attribute value of `mk`, or an error if the +/// attribute is missing or not UTF-8. +fn algorithm_name(mk: &Multikey) -> Result<&str, Error> { + let bytes = mk + .attributes + .get(&AttrId::AlgorithmName) + .ok_or_else(|| Error::UnsupportedAlgorithm("AlgorithmName missing".into()))?; + std::str::from_utf8(bytes) + .map_err(|_| Error::UnsupportedAlgorithm("AlgorithmName not UTF-8".into())) +} + +/// An attributes view over a custom key, reporting the key type byte +/// convention: absent or 0 means public, 1 means secret. +struct CustomAttrs { + key_type: u8, +} + +impl AttrView for CustomAttrs { + fn is_encrypted(&self) -> bool { + false + } + + fn is_public_key(&self) -> bool { + self.key_type == 0 + } + + fn is_secret_key(&self) -> bool { + self.key_type == 1 + } + + fn is_secret_key_share(&self) -> bool { + false + } +} + +/// A view over the key data of a custom key. +struct CustomData { + key_bytes: Vec, +} + +impl DataView for CustomData { + fn key_bytes(&self) -> Result>, Error> { + Ok(Zeroizing::new(self.key_bytes.clone())) + } + + fn secret_bytes(&self) -> Result>, Error> { + Ok(Zeroizing::new(self.key_bytes.clone())) + } +} + +/// A conversion view over a custom key. Per the byte convention from +/// provenance-specifications#4, a secret (key type 1) custom key converts +/// to its public form by storing key type 0. +struct CustomConv { + key_type: u8, + key: Multikey, +} + +impl ConvView for CustomConv { + fn to_public_key(&self) -> Result { + if self.key_type != 1 { + return Err(Error::UnsupportedAlgorithm( + "custom public key has no public form".into(), + )); + } + let mut pk = self.key.clone(); + pk.attributes + .insert(AttrId::KeyType, Zeroizing::new(vec![0])); + Ok(pk) + } + + fn to_ssh_public_key(&self) -> Result { + Err(Error::UnsupportedAlgorithm( + "custom keys have no ssh form".into(), + )) + } + + fn to_ssh_private_key(&self) -> Result { + Err(Error::UnsupportedAlgorithm( + "custom keys have no ssh form".into(), + )) + } +} + +/// A fingerprint view over a custom key: the hash of the key data bytes. +struct CustomFingerprint { + key_bytes: Vec, +} + +impl FingerprintView for CustomFingerprint { + fn fingerprint(&self, hash: Codec) -> Result { + Ok(mh::Builder::new_from_bytes(hash, &self.key_bytes)?.try_build()?) + } +} + +/// A signing view that reports the protocol it serves through its error, +/// so tests can assert which factory produced the view. +struct NamedSign { + name: &'static str, +} + +impl SignView for NamedSign { + fn sign(&self, _: &[u8], _: bool, _: Option) -> Result { + Err(Error::UnsupportedAlgorithm(format!("{} sign", self.name))) + } +} + +/// A verification view that reports the protocol it serves through its +/// error, so tests can assert which factory produced the view. +struct NamedVerify { + name: String, +} + +impl VerifyView for NamedVerify { + fn verify(&self, _: &Multisig, _: Option<&[u8]>) -> Result<(), Error> { + Err(Error::UnsupportedAlgorithm(format!("{} verify", self.name))) + } +} + +/// A factory for the basic-attributes view kind. It serves the key's +/// protocol, which it identifies through the `AlgorithmName` attribute. +fn attr_factory<'a>(mk: &'a Multikey) -> Result, Error> { + algorithm_name(mk)?; + Ok(Box::new(CustomAttrs { + key_type: key_type_byte(mk), + })) +} + +/// A factory for the key-data view kind. +fn data_factory<'a>(mk: &'a Multikey) -> Result, Error> { + let key_bytes = mk + .attributes + .get(&AttrId::KeyData) + .ok_or_else(|| Error::UnsupportedAlgorithm("KeyData missing".into()))? + .to_vec(); + Ok(Box::new(CustomData { key_bytes })) +} + +/// A factory for the key-conversion view kind. It serves the key's +/// protocol, which it identifies through the `AlgorithmName` attribute. +fn conv_factory<'a>(mk: &'a Multikey) -> Result, Error> { + algorithm_name(mk)?; + Ok(Box::new(CustomConv { + key_type: key_type_byte(mk), + key: mk.clone(), + })) +} + +/// A factory for the fingerprint view kind. +fn fingerprint_factory<'a>(mk: &'a Multikey) -> Result, Error> { + let key_bytes = mk + .attributes + .get(&AttrId::KeyData) + .ok_or_else(|| Error::UnsupportedAlgorithm("KeyData missing".into()))? + .to_vec(); + Ok(Box::new(CustomFingerprint { key_bytes })) +} + +/// A factory for the verification view kind, named after the key's +/// `AlgorithmName`. +fn verify_factory<'a>(mk: &'a Multikey) -> Result, Error> { + Ok(Box::new(NamedVerify { + name: algorithm_name(mk)?.to_string(), + })) +} + +/// A factory for the signing view kind that serves only the `serves` +/// protocol: keys sharing `Codec::Identity` are disambiguated by their +/// `AlgorithmName` attribute, not by the codec. +fn sign_factory( + serves: &'static str, +) -> impl for<'a> Fn(&'a Multikey) -> Result, Error> + Send + Sync + 'static +{ + move |mk| { + if algorithm_name(mk)? == serves { + Ok(Box::new(NamedSign { name: serves })) + } else { + Err(Error::UnsupportedAlgorithm(format!( + "this factory serves {serves} only" + ))) + } + } +} + +/// Build a custom protocol key through the public `Builder` setters and +/// decode it back from its wire encoding, so tests exercise the decoded +/// form the way custom keys usually arrive. +fn decoded_custom_key(name: &str, key_type: u8) -> Multikey { + let built = Builder::new(Codec::Identity) + .with_comment("custom protocol key") + .with_key_bytes(b"custom-key-seed".as_slice()) + .with_algorithm_name(name) + .with_key_type(key_type) + .try_build() + .unwrap(); + let bytes: Vec = built.into(); + Multikey::try_from(bytes.as_ref()).unwrap() +} + +/// Test that the setters stamp the `AlgorithmName` (code 27) and `KeyType` +/// (code 28) attributes on the built key. +#[test] +fn test_custom_key_builder_stamps_attributes() { + let built = Builder::new(Codec::Identity) + .with_key_bytes(b"custom-key-seed".as_slice()) + .with_algorithm_name("my-protocol") + .with_key_type(1) + .try_build() + .unwrap(); + assert_eq!(built.codec(), Codec::Identity); + assert_eq!( + built + .attributes + .get(&AttrId::AlgorithmName) + .unwrap() + .as_slice(), + b"my-protocol".as_slice() + ); + assert_eq!( + built.attributes.get(&AttrId::KeyType).unwrap().as_slice(), + b"\x01".as_slice() + ); +} + +/// Test that a custom key built through the public setters roundtrips +/// through the wire encoding unchanged. +#[test] +fn test_custom_key_wire_roundtrip() { + let built = Builder::new(Codec::Identity) + .with_comment("custom protocol key") + .with_key_bytes(b"custom-key-seed".as_slice()) + .with_algorithm_name("my-protocol") + .with_key_type(1) + .try_build() + .unwrap(); + let bytes: Vec = built.clone().into(); + let decoded = Multikey::try_from(bytes.as_ref()).unwrap(); + assert_eq!(decoded, built); + assert_eq!(decoded.codec(), Codec::Identity); + assert_eq!( + decoded + .attributes + .get(&AttrId::AlgorithmName) + .unwrap() + .as_slice(), + b"my-protocol".as_slice() + ); + assert_eq!( + decoded.attributes.get(&AttrId::KeyType).unwrap().as_slice(), + b"\x01".as_slice() + ); +} + +/// Test that the empty algorithm-name string is accepted and stored +/// unvalidated, and survives the wire roundtrip. +#[test] +fn test_empty_algorithm_name_is_accepted() { + let built = Builder::new(Codec::Identity) + .with_key_bytes(b"custom-key-seed".as_slice()) + .with_algorithm_name("") + .with_key_type(0) + .try_build() + .unwrap(); + assert_eq!( + built + .attributes + .get(&AttrId::AlgorithmName) + .unwrap() + .as_slice(), + b"".as_slice() + ); + let bytes: Vec = built.into(); + let decoded = Multikey::try_from(bytes.as_ref()).unwrap(); + assert_eq!( + decoded + .attributes + .get(&AttrId::AlgorithmName) + .unwrap() + .as_slice(), + b"".as_slice() + ); +} + +/// Test that custom-key views build through the `ViewBuilder` with a +/// registered local factory per kind: attr, data, conv, and fingerprint. +#[test] +fn test_custom_key_factory_dispatch() { + let mk = decoded_custom_key("my-protocol", 1); + + // the attributes view reports the key type byte convention + let attrs = ViewBuilder::new(&mk) + .attr() + .with_local_codec(Codec::Identity, attr_factory) + .build() + .unwrap(); + assert!(attrs.is_secret_key()); + assert!(!attrs.is_public_key()); + assert!(!attrs.is_encrypted()); + assert!(!attrs.is_secret_key_share()); + + // the same factory reports public for a key type 0 custom key + let public = decoded_custom_key("my-protocol", 0); + let attrs = ViewBuilder::new(&public) + .attr() + .with_local_codec(Codec::Identity, attr_factory) + .build() + .unwrap(); + assert!(attrs.is_public_key()); + assert!(!attrs.is_secret_key()); + + // the key-data view returns the key bytes + let data = ViewBuilder::new(&mk) + .data() + .with_local_codec(Codec::Identity, data_factory) + .build() + .unwrap(); + assert_eq!( + data.key_bytes().unwrap().as_slice(), + b"custom-key-seed".as_slice() + ); + + // a secret (key type 1) custom key converts to a public key storing + // key type 0 + let conv = ViewBuilder::new(&mk) + .conv() + .with_local_codec(Codec::Identity, conv_factory) + .build() + .unwrap(); + let public_key = conv.to_public_key().unwrap(); + assert_eq!( + public_key + .attributes + .get(&AttrId::KeyType) + .unwrap() + .as_slice(), + b"\x00".as_slice() + ); + assert_eq!( + conv.to_ssh_public_key().err().unwrap().to_string(), + "Unsupported key algorithm: custom keys have no ssh form" + ); + + // the fingerprint view hashes the key data bytes + let fp = ViewBuilder::new(&mk) + .fingerprint() + .with_local_codec(Codec::Identity, fingerprint_factory) + .build() + .unwrap(); + let expected = mh::Builder::new_from_bytes(Codec::Blake2S256, b"custom-key-seed".as_slice()) + .unwrap() + .try_build() + .unwrap(); + assert_eq!(fp.fingerprint(Codec::Blake2S256).unwrap(), expected); +} + +/// Test that two custom keys sharing the `Codec::Identity` codec but +/// carrying different `AlgorithmName` values each reach the factory that +/// serves their protocol through caller-side branching. +#[test] +fn test_algorithm_name_branching() { + let alpha = decoded_custom_key("alpha-protocol", 1); + let beta = decoded_custom_key("beta-protocol", 1); + + // each key reaches the factory serving its protocol + let sign = ViewBuilder::new(&alpha) + .sign() + .with_local_codec(Codec::Identity, sign_factory("alpha-protocol")) + .build() + .unwrap(); + assert_eq!( + sign.sign(b"message", false, None) + .err() + .unwrap() + .to_string(), + "Unsupported key algorithm: alpha-protocol sign" + ); + + let sign = ViewBuilder::new(&beta) + .sign() + .with_local_codec(Codec::Identity, sign_factory("beta-protocol")) + .build() + .unwrap(); + assert_eq!( + sign.sign(b"message", false, None) + .err() + .unwrap() + .to_string(), + "Unsupported key algorithm: beta-protocol sign" + ); + + // the factory serving alpha rejects beta's key: the built-in dispatch + // never supports a custom codec, so the factory's error propagates + let err = ViewBuilder::new(&beta) + .sign() + .with_local_codec(Codec::Identity, sign_factory("alpha-protocol")) + .build() + .err() + .unwrap(); + assert_eq!( + err.to_string(), + "Unsupported key algorithm: this factory serves alpha-protocol only" + ); + + // the same branching works over the verification view kind + let verify = ViewBuilder::new(&beta) + .verify() + .with_local_codec(Codec::Identity, verify_factory) + .build() + .unwrap(); + let sig = Multisig::null(); + assert_eq!( + verify.verify(&sig, None).err().unwrap().to_string(), + "Unsupported key algorithm: beta-protocol verify" + ); +} + +/// Test that a custom key passes through serde roundtrips unchanged. +#[cfg(feature = "serde")] +#[test] +fn test_custom_key_serde_roundtrip() { + let mk = decoded_custom_key("my-protocol", 1); + + // human-readable format + let json = serde_json::to_string(&mk).unwrap(); + let back: Multikey = serde_json::from_str(&json).unwrap(); + assert_eq!(back, mk); + + // binary format + let mut cbor = Vec::new(); + ciborium::into_writer(&mk, &mut cbor).unwrap(); + let back: Multikey = ciborium::from_reader(cbor.as_slice()).unwrap(); + assert_eq!(back, mk); +} diff --git a/tests/merkle_lamport_tests.rs b/tests/merkle_lamport_tests.rs index d037dcf..29128a2 100644 --- a/tests/merkle_lamport_tests.rs +++ b/tests/merkle_lamport_tests.rs @@ -4,9 +4,8 @@ #![cfg(feature = "lamport")] #![allow(clippy::uninlined_format_args)] - use multi_codec::Codec; -use multi_key::{Builder, Multikey, Views as _}; +use multi_key::{Builder, Multikey, ViewBuilder}; use multi_sig::{Multisig, Views as _}; #[test] @@ -17,16 +16,22 @@ fn test_merkle_sign_advance_and_verify() { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); - let sv = sk.merkle_state_view().unwrap(); + let sv = ViewBuilder::new(&sk).merkle_state().build().unwrap(); assert_eq!(sv.depth().unwrap(), 1); assert_eq!(sv.capacity().unwrap(), 2); assert_eq!(sv.remaining_signatures().unwrap(), 2); let msg = b"integration merkle message"; - let (ms, advanced) = sk - .sign_view() + let (ms, advanced) = ViewBuilder::new(&sk) + .sign() + .build() .unwrap() .sign_advance(msg, true, None) .unwrap(); @@ -34,10 +39,15 @@ fn test_merkle_sign_advance_and_verify() { // depth attribute travels on the signature assert_eq!(ms.depth(), Some(1)); - pk.verify_view().unwrap().verify(&ms, Some(msg)).unwrap(); + ViewBuilder::new(&pk) + .verify() + .build() + .unwrap() + .verify(&ms, Some(msg)) + .unwrap(); // advanced key must be persisted and reflect the consumed leaf - let av = advanced.merkle_state_view().unwrap(); + let av = ViewBuilder::new(&advanced).merkle_state().build().unwrap(); assert_eq!(av.next_index().unwrap(), 1); assert_eq!(av.remaining_signatures().unwrap(), 1); @@ -45,7 +55,7 @@ fn test_merkle_sign_advance_and_verify() { let bytes: Vec = advanced.clone().into(); let restored = Multikey::try_from(bytes.as_slice()).unwrap(); assert_eq!(restored, advanced); - let rv = restored.merkle_state_view().unwrap(); + let rv = ViewBuilder::new(&restored).merkle_state().build().unwrap(); assert_eq!(rv.next_index().unwrap(), 1); } @@ -57,10 +67,20 @@ fn test_merkle_threshold_split_sign_combine_verify() { .unwrap() .try_build() .unwrap(); - let pk = sk.conv_view().unwrap().to_public_key().unwrap(); + let pk = ViewBuilder::new(&sk) + .conv() + .build() + .unwrap() + .to_public_key() + .unwrap(); // split 2-of-3 - let shares = sk.threshold_view().unwrap().split(2, 3).unwrap(); + let shares = ViewBuilder::new(&sk) + .threshold() + .build() + .unwrap() + .split(2, 3) + .unwrap(); assert_eq!(shares.len(), 3); let msg = b"threshold integration message"; @@ -70,8 +90,9 @@ fn test_merkle_threshold_split_sign_combine_verify() { .iter() .take(2) .map(|share| { - let (ms, _adv) = share - .sign_view() + let (ms, _adv) = ViewBuilder::new(share) + .sign() + .build() .unwrap() .sign_advance(msg, false, None) .unwrap(); @@ -94,7 +115,9 @@ fn test_merkle_threshold_split_sign_combine_verify() { assert_eq!(combined.depth(), Some(1)); // verify under the tree-root public key - pk.verify_view() + ViewBuilder::new(&pk) + .verify() + .build() .unwrap() .verify(&combined, Some(msg)) .unwrap(); diff --git a/tests/security_tests.rs b/tests/security_tests.rs index 27d6da6..e501911 100644 --- a/tests/security_tests.rs +++ b/tests/security_tests.rs @@ -1,7 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 //! Security-focused tests for multi-key #![allow(clippy::redundant_clone, clippy::doc_markdown)] - use multi_codec::Codec; use multi_key::{Builder, Error, Multikey}; @@ -87,7 +86,7 @@ fn test_private_key_zeroization() { #[cfg(not(feature = "legacy_chacha20_fallback"))] #[test] fn test_chacha20_aead_tamper_rejected() { - use multi_key::{Builder, Views, cipher, kdf}; + use multi_key::{Builder, ViewBuilder, cipher, kdf}; let plain = hex::decode("7e48467029ffb9f6282b56e9ce131cead6e4bd061a3500697c57ac7034cf86f2").unwrap(); @@ -109,15 +108,27 @@ fn test_chacha20_aead_tamper_rejected() { .with_nonce(&nonce) .try_build() .unwrap(); - let ciphermk = ciphermk - .kdf_view(&kdfmk) + let ciphermk = ViewBuilder::new(&ciphermk) + .kdf(&kdfmk) + .build() .unwrap() .derive_key(b"for great justice, move every zig!") .unwrap(); // encrypt → authenticated ciphertext (plaintext || 16-byte tag) - let enc = mk1.cipher_view(&ciphermk).unwrap().encrypt().unwrap(); - assert!(enc.attr_view().unwrap().is_encrypted()); + let enc = ViewBuilder::new(&mk1) + .cipher(&ciphermk) + .build() + .unwrap() + .encrypt() + .unwrap(); + assert!( + ViewBuilder::new(&enc) + .attr() + .build() + .unwrap() + .is_encrypted() + ); // tamper with the stored ciphertext attribute so AEAD verification fails let mut tampered = enc.clone(); @@ -133,7 +144,11 @@ fn test_chacha20_aead_tamper_rejected() { .attributes .insert(multi_key::AttrId::KeyData, corrupted.into()); - let result = tampered.cipher_view(&ciphermk).unwrap().decrypt(); + let result = ViewBuilder::new(&tampered) + .cipher(&ciphermk) + .build() + .unwrap() + .decrypt(); assert!( result.is_err(), "decrypting tampered AEAD ciphertext must fail, not silently fall back \ @@ -148,7 +163,7 @@ fn test_chacha20_aead_tamper_rejected() { #[cfg(feature = "legacy_chacha20_fallback")] #[test] fn test_chacha20_legacy_fallback_downgrades_on_tamper() { - use multi_key::{Builder, Views, cipher, kdf}; + use multi_key::{Builder, ViewBuilder, cipher, kdf}; let plain = hex::decode("7e48467029ffb9f6282b56e9ce131cead6e4bd061a3500697c57ac7034cf86f2").unwrap(); @@ -170,13 +185,19 @@ fn test_chacha20_legacy_fallback_downgrades_on_tamper() { .with_nonce(&nonce) .try_build() .unwrap(); - let ciphermk = ciphermk - .kdf_view(&kdfmk) + let ciphermk = ViewBuilder::new(&ciphermk) + .kdf(&kdfmk) + .build() .unwrap() .derive_key(b"for great justice, move every zig!") .unwrap(); - let enc = mk1.cipher_view(&ciphermk).unwrap().encrypt().unwrap(); + let enc = ViewBuilder::new(&mk1) + .cipher(&ciphermk) + .build() + .unwrap() + .encrypt() + .unwrap(); // tamper with the ciphertext body so AEAD verification fails let mut tampered = enc.clone(); @@ -193,9 +214,20 @@ fn test_chacha20_legacy_fallback_downgrades_on_tamper() { // With the legacy fallback enabled, the bare-ChaCha20 path is taken and // decrypt "succeeds" — but the output is garbage, NOT the original key. - let dec = tampered.cipher_view(&ciphermk).unwrap().decrypt().unwrap(); + let dec = ViewBuilder::new(&tampered) + .cipher(&ciphermk) + .build() + .unwrap() + .decrypt() + .unwrap(); assert_ne!( - dec.data_view().unwrap().secret_bytes().unwrap().as_slice(), + ViewBuilder::new(&dec) + .data() + .build() + .unwrap() + .secret_bytes() + .unwrap() + .as_slice(), plain.as_slice(), "legacy fallback must not be used to authenticate; it yields garbage \ on tampered ciphertext"