1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
use penumbra_proto::{custody::v1 as pb, DomainType};
use serde::{Deserialize, Serialize};

/// A pre-authorization packet.  This allows a custodian to delegate (partial)
/// signing authority to other authorization mechanisms.  Details of how a
/// custodian manages those keys are out-of-scope for the custody protocol and
/// are custodian-specific.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(try_from = "pb::PreAuthorization", into = "pb::PreAuthorization")]
pub enum PreAuthorization {
    Ed25519(Ed25519),
}

/// An Ed25519-based preauthorization, containing an Ed25519 signature over the
/// `TransactionPlan`.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(
    try_from = "pb::pre_authorization::Ed25519",
    into = "pb::pre_authorization::Ed25519"
)]
pub struct Ed25519 {
    /// The verification key used to pre-authorize the `TransactionPlan`.
    pub vk: ed25519_consensus::VerificationKey,
    /// An Ed25519 signature over the `TransactionPlan`.
    pub sig: ed25519_consensus::Signature,
}

impl Ed25519 {
    /// Verifies the provided `TransactionPlan`.
    pub fn verify(&self, message: impl AsRef<[u8]>) -> anyhow::Result<()> {
        let bytes = message.as_ref();
        self.vk.verify(&self.sig, &bytes).map_err(Into::into)
    }
}

impl DomainType for PreAuthorization {
    type Proto = pb::PreAuthorization;
}

impl TryFrom<pb::PreAuthorization> for PreAuthorization {
    type Error = anyhow::Error;
    fn try_from(value: pb::PreAuthorization) -> Result<Self, Self::Error> {
        Ok(match value.pre_authorization {
            Some(pb::pre_authorization::PreAuthorization::Ed25519(ed)) => {
                Self::Ed25519(ed.try_into()?)
            }
            None => {
                anyhow::bail!("missing pre-authorization");
            }
        })
    }
}

impl From<PreAuthorization> for pb::PreAuthorization {
    fn from(value: PreAuthorization) -> pb::PreAuthorization {
        Self {
            pre_authorization: Some(match value {
                PreAuthorization::Ed25519(ed) => {
                    pb::pre_authorization::PreAuthorization::Ed25519(ed.into())
                }
            }),
        }
    }
}

impl DomainType for Ed25519 {
    type Proto = pb::pre_authorization::Ed25519;
}

impl TryFrom<pb::pre_authorization::Ed25519> for Ed25519 {
    type Error = anyhow::Error;
    fn try_from(value: pb::pre_authorization::Ed25519) -> Result<Self, Self::Error> {
        Ok(Self {
            vk: value.vk.as_slice().try_into()?,
            sig: value.sig.as_slice().try_into()?,
        })
    }
}

impl From<Ed25519> for pb::pre_authorization::Ed25519 {
    fn from(value: Ed25519) -> pb::pre_authorization::Ed25519 {
        Self {
            vk: value.vk.to_bytes().into(),
            sig: value.sig.to_bytes().into(),
        }
    }
}