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 {
pub trait Sealed {}
}
pub trait ViewKind: sealed::Sealed {
type Factories;
}
pub struct Unselected;
pub struct AttrKind;
pub struct CipherAttrKind;
pub struct DataKind;
pub struct KdfAttrKind;
pub struct ThresholdAttrKind;
pub struct ThresholdKeyKind;
pub struct ConvKind;
pub struct FingerprintKind;
pub struct SignKind;
pub struct VerifyKind;
pub struct SealKind;
pub struct OpenKind;
pub struct CipherKind;
pub struct KdfKind;
pub struct ThresholdKind;
pub struct DisclosureKind;
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;
}
type AttrFactory =
Box<dyn for<'a> Fn(&'a Multikey) -> Result<Box<dyn AttrView + 'a>, Error> + Send + Sync>;
type AttrFactories = BTreeMap<Codec, AttrFactory>;
type CipherAttrFactory =
Box<dyn for<'a> Fn(&'a Multikey) -> Result<Box<dyn CipherAttrView + 'a>, Error> + Send + Sync>;
type CipherAttrFactories = BTreeMap<Codec, CipherAttrFactory>;
type DataFactory =
Box<dyn for<'a> Fn(&'a Multikey) -> Result<Box<dyn DataView + 'a>, Error> + Send + Sync>;
type DataFactories = BTreeMap<Codec, DataFactory>;
type KdfAttrFactory =
Box<dyn for<'a> Fn(&'a Multikey) -> Result<Box<dyn KdfAttrView + 'a>, Error> + Send + Sync>;
type KdfAttrFactories = BTreeMap<Codec, KdfAttrFactory>;
type ThresholdAttrFactory = Box<
dyn for<'a> Fn(&'a Multikey) -> Result<Box<dyn ThresholdAttrView + 'a>, Error> + Send + Sync,
>;
type ThresholdAttrFactories = BTreeMap<Codec, ThresholdAttrFactory>;
type ThresholdKeyFactory = Box<
dyn for<'a> Fn(&'a Multikey) -> Result<Box<dyn ThresholdKeyView + 'a>, Error> + Send + Sync,
>;
type ThresholdKeyFactories = BTreeMap<Codec, ThresholdKeyFactory>;
type ConvFactory =
Box<dyn for<'a> Fn(&'a Multikey) -> Result<Box<dyn ConvView + 'a>, Error> + Send + Sync>;
type ConvFactories = BTreeMap<Codec, ConvFactory>;
type FingerprintFactory =
Box<dyn for<'a> Fn(&'a Multikey) -> Result<Box<dyn FingerprintView + 'a>, Error> + Send + Sync>;
type FingerprintFactories = BTreeMap<Codec, FingerprintFactory>;
type SignFactory =
Box<dyn for<'a> Fn(&'a Multikey) -> Result<Box<dyn SignView + 'a>, Error> + Send + Sync>;
type SignFactories = BTreeMap<Codec, SignFactory>;
type VerifyFactory =
Box<dyn for<'a> Fn(&'a Multikey) -> Result<Box<dyn VerifyView + 'a>, Error> + Send + Sync>;
type VerifyFactories = BTreeMap<Codec, VerifyFactory>;
type SealFactory =
Box<dyn for<'a> Fn(&'a Multikey) -> Result<Box<dyn SealView + 'a>, Error> + Send + Sync>;
type SealFactories = BTreeMap<Codec, SealFactory>;
type OpenFactory =
Box<dyn for<'a> Fn(&'a Multikey) -> Result<Box<dyn OpenView + 'a>, Error> + Send + Sync>;
type OpenFactories = BTreeMap<Codec, OpenFactory>;
type CipherFactory = Box<
dyn for<'a> Fn(&'a Multikey, &'a Multikey) -> Result<Box<dyn CipherView + 'a>, Error>
+ Send
+ Sync,
>;
type CipherFactories = BTreeMap<Codec, CipherFactory>;
type KdfFactory = Box<
dyn for<'a> Fn(&'a Multikey, &'a Multikey) -> Result<Box<dyn KdfView + 'a>, Error>
+ Send
+ Sync,
>;
type KdfFactories = BTreeMap<Codec, KdfFactory>;
type ThresholdFactory =
Box<dyn for<'a> Fn(&'a Multikey) -> Result<Box<dyn ThresholdView + 'a>, Error> + Send + Sync>;
type ThresholdFactories = BTreeMap<Codec, ThresholdFactory>;
type ThresholdDisclosureFactory = Box<
dyn for<'a> Fn(&'a Multikey) -> Result<Box<dyn ThresholdDisclosureView + 'a>, Error>
+ Send
+ Sync,
>;
type ThresholdDisclosureFactories = BTreeMap<Codec, ThresholdDisclosureFactory>;
type MerkleStateFactory =
Box<dyn for<'a> Fn(&'a Multikey) -> Result<Box<dyn MerkleStateView + 'a>, Error> + Send + Sync>;
type MerkleStateFactories = BTreeMap<Codec, MerkleStateFactory>;
pub struct ViewBuilder<'mk, K: ViewKind = Unselected> {
mk: &'mk Multikey,
second: &'mk Multikey,
local: Option<K::Factories>,
}
impl<'mk> ViewBuilder<'mk, Unselected> {
pub fn new(mk: &'mk Multikey) -> Self {
Self {
mk,
second: mk,
local: None,
}
}
pub fn attr(self) -> ViewBuilder<'mk, AttrKind> {
ViewBuilder {
mk: self.mk,
second: self.second,
local: None,
}
}
pub fn cipher_attr(self) -> ViewBuilder<'mk, CipherAttrKind> {
ViewBuilder {
mk: self.mk,
second: self.second,
local: None,
}
}
pub fn data(self) -> ViewBuilder<'mk, DataKind> {
ViewBuilder {
mk: self.mk,
second: self.second,
local: None,
}
}
pub fn kdf_attr(self) -> ViewBuilder<'mk, KdfAttrKind> {
ViewBuilder {
mk: self.mk,
second: self.second,
local: None,
}
}
pub fn threshold_attr(self) -> ViewBuilder<'mk, ThresholdAttrKind> {
ViewBuilder {
mk: self.mk,
second: self.second,
local: None,
}
}
pub fn threshold_key(self) -> ViewBuilder<'mk, ThresholdKeyKind> {
ViewBuilder {
mk: self.mk,
second: self.second,
local: None,
}
}
pub fn conv(self) -> ViewBuilder<'mk, ConvKind> {
ViewBuilder {
mk: self.mk,
second: self.second,
local: None,
}
}
pub fn fingerprint(self) -> ViewBuilder<'mk, FingerprintKind> {
ViewBuilder {
mk: self.mk,
second: self.second,
local: None,
}
}
pub fn sign(self) -> ViewBuilder<'mk, SignKind> {
ViewBuilder {
mk: self.mk,
second: self.second,
local: None,
}
}
pub fn verify(self) -> ViewBuilder<'mk, VerifyKind> {
ViewBuilder {
mk: self.mk,
second: self.second,
local: None,
}
}
pub fn seal(self) -> ViewBuilder<'mk, SealKind> {
ViewBuilder {
mk: self.mk,
second: self.second,
local: None,
}
}
pub fn open(self) -> ViewBuilder<'mk, OpenKind> {
ViewBuilder {
mk: self.mk,
second: self.second,
local: None,
}
}
pub fn threshold(self) -> ViewBuilder<'mk, ThresholdKind> {
ViewBuilder {
mk: self.mk,
second: self.second,
local: None,
}
}
pub fn disclosure(self) -> ViewBuilder<'mk, DisclosureKind> {
ViewBuilder {
mk: self.mk,
second: self.second,
local: None,
}
}
pub fn merkle_state(self) -> ViewBuilder<'mk, MerkleStateKind> {
ViewBuilder {
mk: self.mk,
second: self.second,
local: None,
}
}
pub fn cipher(self, cipher: &'mk Multikey) -> ViewBuilder<'mk, CipherKind> {
ViewBuilder {
mk: self.mk,
second: cipher,
local: None,
}
}
pub fn kdf(self, kdf: &'mk Multikey) -> ViewBuilder<'mk, KdfKind> {
ViewBuilder {
mk: self.mk,
second: kdf,
local: None,
}
}
}
impl<'mk> ViewBuilder<'mk, AttrKind> {
pub fn with_local_codec<F>(mut self, codec: Codec, factory: F) -> Self
where
F: for<'a> Fn(&'a Multikey) -> Result<Box<dyn AttrView + 'a>, Error>
+ Send
+ Sync
+ 'static,
{
self.local
.get_or_insert_with(BTreeMap::new)
.insert(codec, Box::new(factory));
self
}
pub fn build(self) -> Result<Box<dyn AttrView + 'mk>, 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> {
pub fn with_local_codec<F>(mut self, codec: Codec, factory: F) -> Self
where
F: for<'a> Fn(&'a Multikey) -> Result<Box<dyn CipherAttrView + 'a>, Error>
+ Send
+ Sync
+ 'static,
{
self.local
.get_or_insert_with(BTreeMap::new)
.insert(codec, Box::new(factory));
self
}
pub fn build(self) -> Result<Box<dyn CipherAttrView + 'mk>, 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> {
pub fn with_local_codec<F>(mut self, codec: Codec, factory: F) -> Self
where
F: for<'a> Fn(&'a Multikey) -> Result<Box<dyn DataView + 'a>, Error>
+ Send
+ Sync
+ 'static,
{
self.local
.get_or_insert_with(BTreeMap::new)
.insert(codec, Box::new(factory));
self
}
pub fn build(self) -> Result<Box<dyn DataView + 'mk>, 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> {
pub fn with_local_codec<F>(mut self, codec: Codec, factory: F) -> Self
where
F: for<'a> Fn(&'a Multikey) -> Result<Box<dyn KdfAttrView + 'a>, Error>
+ Send
+ Sync
+ 'static,
{
self.local
.get_or_insert_with(BTreeMap::new)
.insert(codec, Box::new(factory));
self
}
pub fn build(self) -> Result<Box<dyn KdfAttrView + 'mk>, 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> {
pub fn with_local_codec<F>(mut self, codec: Codec, factory: F) -> Self
where
F: for<'a> Fn(&'a Multikey) -> Result<Box<dyn ThresholdAttrView + 'a>, Error>
+ Send
+ Sync
+ 'static,
{
self.local
.get_or_insert_with(BTreeMap::new)
.insert(codec, Box::new(factory));
self
}
pub fn build(self) -> Result<Box<dyn ThresholdAttrView + 'mk>, 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> {
pub fn with_local_codec<F>(mut self, codec: Codec, factory: F) -> Self
where
F: for<'a> Fn(&'a Multikey) -> Result<Box<dyn ThresholdKeyView + 'a>, Error>
+ Send
+ Sync
+ 'static,
{
self.local
.get_or_insert_with(BTreeMap::new)
.insert(codec, Box::new(factory));
self
}
pub fn build(self) -> Result<Box<dyn ThresholdKeyView + 'mk>, 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> {
pub fn with_local_codec<F>(mut self, codec: Codec, factory: F) -> Self
where
F: for<'a> Fn(&'a Multikey) -> Result<Box<dyn ConvView + 'a>, Error>
+ Send
+ Sync
+ 'static,
{
self.local
.get_or_insert_with(BTreeMap::new)
.insert(codec, Box::new(factory));
self
}
pub fn build(self) -> Result<Box<dyn ConvView + 'mk>, 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> {
pub fn with_local_codec<F>(mut self, codec: Codec, factory: F) -> Self
where
F: for<'a> Fn(&'a Multikey) -> Result<Box<dyn FingerprintView + 'a>, Error>
+ Send
+ Sync
+ 'static,
{
self.local
.get_or_insert_with(BTreeMap::new)
.insert(codec, Box::new(factory));
self
}
pub fn build(self) -> Result<Box<dyn FingerprintView + 'mk>, 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> {
pub fn with_local_codec<F>(mut self, codec: Codec, factory: F) -> Self
where
F: for<'a> Fn(&'a Multikey) -> Result<Box<dyn SignView + 'a>, Error>
+ Send
+ Sync
+ 'static,
{
self.local
.get_or_insert_with(BTreeMap::new)
.insert(codec, Box::new(factory));
self
}
pub fn build(self) -> Result<Box<dyn SignView + 'mk>, 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> {
pub fn with_local_codec<F>(mut self, codec: Codec, factory: F) -> Self
where
F: for<'a> Fn(&'a Multikey) -> Result<Box<dyn VerifyView + 'a>, Error>
+ Send
+ Sync
+ 'static,
{
self.local
.get_or_insert_with(BTreeMap::new)
.insert(codec, Box::new(factory));
self
}
pub fn build(self) -> Result<Box<dyn VerifyView + 'mk>, 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> {
pub fn with_local_codec<F>(mut self, codec: Codec, factory: F) -> Self
where
F: for<'a> Fn(&'a Multikey) -> Result<Box<dyn SealView + 'a>, Error>
+ Send
+ Sync
+ 'static,
{
self.local
.get_or_insert_with(BTreeMap::new)
.insert(codec, Box::new(factory));
self
}
pub fn build(self) -> Result<Box<dyn SealView + 'mk>, 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> {
pub fn with_local_codec<F>(mut self, codec: Codec, factory: F) -> Self
where
F: for<'a> Fn(&'a Multikey) -> Result<Box<dyn OpenView + 'a>, Error>
+ Send
+ Sync
+ 'static,
{
self.local
.get_or_insert_with(BTreeMap::new)
.insert(codec, Box::new(factory));
self
}
pub fn build(self) -> Result<Box<dyn OpenView + 'mk>, 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> {
pub fn with_local_codec<F>(mut self, codec: Codec, factory: F) -> Self
where
F: for<'a> Fn(&'a Multikey, &'a Multikey) -> Result<Box<dyn CipherView + 'a>, Error>
+ Send
+ Sync
+ 'static,
{
self.local
.get_or_insert_with(BTreeMap::new)
.insert(codec, Box::new(factory));
self
}
pub fn build(self) -> Result<Box<dyn CipherView + 'mk>, 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> {
pub fn with_local_codec<F>(mut self, codec: Codec, factory: F) -> Self
where
F: for<'a> Fn(&'a Multikey, &'a Multikey) -> Result<Box<dyn KdfView + 'a>, Error>
+ Send
+ Sync
+ 'static,
{
self.local
.get_or_insert_with(BTreeMap::new)
.insert(codec, Box::new(factory));
self
}
pub fn build(self) -> Result<Box<dyn KdfView + 'mk>, 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> {
pub fn with_local_codec<F>(mut self, codec: Codec, factory: F) -> Self
where
F: for<'a> Fn(&'a Multikey) -> Result<Box<dyn ThresholdView + 'a>, Error>
+ Send
+ Sync
+ 'static,
{
self.local
.get_or_insert_with(BTreeMap::new)
.insert(codec, Box::new(factory));
self
}
pub fn build(self) -> Result<Box<dyn ThresholdView + 'mk>, 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> {
pub fn with_local_codec<F>(mut self, codec: Codec, factory: F) -> Self
where
F: for<'a> Fn(&'a Multikey) -> Result<Box<dyn ThresholdDisclosureView + 'a>, Error>
+ Send
+ Sync
+ 'static,
{
self.local
.get_or_insert_with(BTreeMap::new)
.insert(codec, Box::new(factory));
self
}
pub fn build(self) -> Result<Box<dyn ThresholdDisclosureView + 'mk>, Error> {
Ok(dispatch_disclosure_view(self.mk))
}
}
impl<'mk> ViewBuilder<'mk, MerkleStateKind> {
pub fn with_local_codec<F>(mut self, codec: Codec, factory: F) -> Self
where
F: for<'a> Fn(&'a Multikey) -> Result<Box<dyn MerkleStateView + 'a>, Error>
+ Send
+ Sync
+ 'static,
{
self.local
.get_or_insert_with(BTreeMap::new)
.insert(codec, Box::new(factory));
self
}
pub fn build(self) -> Result<Box<dyn MerkleStateView + 'mk>, 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<Mutex<Vec<String>>>;
fn recorder() -> Recorder {
Arc::new(Mutex::new(Vec::new()))
}
fn record(rec: &Recorder, entry: &str) {
rec.lock().unwrap().push(entry.to_string());
}
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<u8> = 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<u8>) -> Result<Multisig, Error> {
Err(Error::UnsupportedAlgorithm("NoSign".into()))
}
}
struct NoCipher;
impl CipherView for NoCipher {
fn decrypt(&self) -> Result<Multikey, Error> {
Err(Error::UnsupportedAlgorithm("NoCipher.decrypt".into()))
}
fn encrypt(&self) -> Result<Multikey, Error> {
Err(Error::UnsupportedAlgorithm("NoCipher.encrypt".into()))
}
}
struct NoKdf;
impl KdfView for NoKdf {
fn derive_key(&self, _: &[u8]) -> Result<Multikey, Error> {
Err(Error::UnsupportedAlgorithm("NoKdf.derive_key".into()))
}
}
struct NoCipherAttr;
impl CipherAttrView for NoCipherAttr {
fn cipher_codec(&self) -> Result<Codec, Error> {
Err(Error::UnsupportedAlgorithm(
"NoCipherAttr.cipher_codec".into(),
))
}
fn nonce_bytes(&self) -> Result<Zeroizing<Vec<u8>>, Error> {
Err(Error::UnsupportedAlgorithm(
"NoCipherAttr.nonce_bytes".into(),
))
}
fn key_length(&self) -> Result<usize, Error> {
Err(Error::UnsupportedAlgorithm(
"NoCipherAttr.key_length".into(),
))
}
}
struct NoKdfAttr;
impl KdfAttrView for NoKdfAttr {
fn kdf_codec(&self) -> Result<Codec, Error> {
Err(Error::UnsupportedAlgorithm("NoKdfAttr.kdf_codec".into()))
}
fn salt_bytes(&self) -> Result<Zeroizing<Vec<u8>>, Error> {
Err(Error::UnsupportedAlgorithm("NoKdfAttr.salt_bytes".into()))
}
fn rounds(&self) -> Result<usize, Error> {
Err(Error::UnsupportedAlgorithm("NoKdfAttr.rounds".into()))
}
}
struct NoDisclosure;
impl ThresholdDisclosureView for NoDisclosure {
fn disclosure_mode(&self) -> Result<ThresholdDisclosure, Error> {
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<Multikey, Error> {
Err(Error::UnsupportedAlgorithm(
"NoDisclosure.to_disclosure".into(),
))
}
}
struct NoSeal;
impl SealView for NoSeal {
fn seal(&self, _: &[u8], _: Codec, _: &[u8]) -> Result<(Vec<u8>, Option<Multikey>), Error> {
Err(Error::UnsupportedAlgorithm("NoSeal.seal".into()))
}
}
struct NoOpen;
impl OpenView for NoOpen {
fn open(
&self,
_: &[u8],
_: Option<&Multikey>,
_: &[u8],
) -> Result<Zeroizing<Vec<u8>>, 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();
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());
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();
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());
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());
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() {
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(_))
));
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());
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)));
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();
assert_eq!(rec.lock().unwrap().as_slice(), ["example-protocol"]);
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();
assert_eq!(rec.lock().unwrap().as_slice(), ["viewed-key|cipher-key"]);
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();
assert_eq!(rec.lock().unwrap().as_slice(), ["viewed-key|kdf-key"]);
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();
assert!(rec.lock().unwrap().is_empty());
let sig = signer.sign(b"hello", false, None).unwrap();
dispatch_verify_view(&mk)
.unwrap()
.verify(&sig, Some(b"hello"))
.unwrap();
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();
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() {
let mut mk = custom_key("attribute-derived");
mk.attributes.insert(
AttrId::CipherCodec,
Zeroizing::new(Vec::from(Codec::Ed25519Pub)),
);
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"
);
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());
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() {
let mut mk = custom_key("attribute-derived");
mk.attributes.insert(
AttrId::KdfCodec,
Zeroizing::new(Vec::from(Codec::Ed25519Priv)),
);
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());
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"
);
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() {
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();
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<T: Send>() {}
fn assert_sync<T: Sync>() {}
assert_send::<ViewBuilder<'static, Unselected>>();
assert_sync::<ViewBuilder<'static, Unselected>>();
assert_send::<ViewBuilder<'static, SignKind>>();
assert_sync::<ViewBuilder<'static, SignKind>>();
assert_send::<ViewBuilder<'static, CipherKind>>();
assert_sync::<ViewBuilder<'static, CipherKind>>();
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());
});
});
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() {
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");
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() {
let mk = custom_key("viewed-key");
let ck = ed25519_key(); 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"));
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());
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")
);
}
}