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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use crate::{SchnorrPublicKey, ECPublicKey, tags, ECKeyBase, Signature};
use anyhow::{bail, Result, Error};
use bc_ur::prelude::*;

/// A public key that can be used for signing. Supports both ECDSA and Schnorr.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum SigningPublicKey {
    Schnorr(SchnorrPublicKey),
    ECDSA(ECPublicKey),
}

impl SigningPublicKey {
    /// Restores a `SigningPublicKey` from a `SchnorrPublicKey`.
    pub fn from_schnorr(key: SchnorrPublicKey) -> Self {
        Self::Schnorr(key)
    }

    /// Restores a `SigningPublicKey` from an `ECPublicKey`.
    pub fn from_ecdsa(key: ECPublicKey) -> Self {
        Self::ECDSA(key)
    }

    /// Returns the `SchnorrPublicKey` of this `SigningPublicKey`, if it is a Schnorr key.
    pub fn schnorr(&self) -> Option<&SchnorrPublicKey> {
        match self {
            Self::Schnorr(key) => Some(key),
            _ => None,
        }
    }

    /// Returns the `ECPublicKey` of this `SigningPublicKey`, if it is an ECDSA key.
    pub fn ecdsa(&self) -> Option<&ECPublicKey> {
        match self {
            Self::ECDSA(key) => Some(key),
            _ => None,
        }
    }

    /// Verifies a signature against a message.
    ///
    /// The type of signature must match the type of this key, and the
    /// signature must be valid for the message, or the verification
    /// will fail.
    pub fn verify(&self, signature: &Signature, message: impl AsRef<[u8]>) -> bool {
        match self {
            SigningPublicKey::Schnorr(key) => {
                match signature {
                    Signature::Schnorr { sig, tag } => key.schnorr_verify(sig, message, tag),
                    Signature::ECDSA(_) => false,
                }
            },
            SigningPublicKey::ECDSA(key) => {
                match signature {
                    Signature::Schnorr { .. } => false,
                    Signature::ECDSA(sig) => key.verify(sig, message),
                }
            },
        }
    }
}

impl AsRef<SigningPublicKey> for SigningPublicKey {
    fn as_ref(&self) -> &SigningPublicKey {
        self
    }
}

impl CBORTagged for SigningPublicKey {
    fn cbor_tags() -> Vec<Tag> {
        vec![tags::SIGNING_PUBLIC_KEY]
    }
}

impl From<SigningPublicKey> for CBOR {
    fn from(value: SigningPublicKey) -> Self {
        value.tagged_cbor()
    }
}

impl CBORTaggedEncodable for SigningPublicKey {
    fn untagged_cbor(&self) -> CBOR {
        match self {
            SigningPublicKey::Schnorr(key) => {
                CBOR::to_byte_string(key.data())
            },
            SigningPublicKey::ECDSA(key) => {
                vec![
                    1.into(),
                    CBOR::to_byte_string(key.data()),
                ].into()
            },
        }
    }
}

impl TryFrom<CBOR> for SigningPublicKey {
    type Error = Error;

    fn try_from(cbor: CBOR) -> Result<Self, Self::Error> {
        Self::from_tagged_cbor(cbor)
    }
}

impl CBORTaggedDecodable for SigningPublicKey {
    fn from_untagged_cbor(untagged_cbor: CBOR) -> Result<Self> {
        match untagged_cbor.into_case() {
            CBORCase::ByteString(data) => {
                Ok(Self::Schnorr(SchnorrPublicKey::from_data_ref(data)?))
            },
            CBORCase::Array(mut elements) => {
                if elements.len() == 2 {
                    let mut drain = elements.drain(0..);
                    let ele_0 = drain.next().unwrap().into_case();
                    let ele_1 = drain.next().unwrap().into_case();
                    if let CBORCase::Unsigned(1) = ele_0 {
                        if let CBORCase::ByteString(data) = ele_1 {
                            return Ok(Self::ECDSA(ECPublicKey::from_data_ref(data)?));
                        }
                    }
                }
                bail!("invalid ECDSA public key");
            },
            _ => bail!("invalid ECDSA public key"),
        }
    }
}