radicle/node/
device.rs

1use std::fmt;
2use std::ops::Deref;
3
4use crypto::{
5    signature::{Signer, Verifier},
6    ssh::ExtendedSignature,
7    Signature,
8};
9
10use crate::crypto;
11
12use super::NodeId;
13
14/// A `Device` identifies the local node through its [`NodeId`], and carries its
15/// signing mechanism.
16///
17/// The signing mechanism is for node specific cryptography, e.g. signing
18/// `rad/sigrefs`, COB commits, node messages, etc.
19///
20/// Note that a `Device` can create [`Signature`]s and [`ExtendedSignature`]s.
21/// It can achieve this as long as `S` implements `Signer<Signature>`.
22#[derive(Clone)]
23pub struct Device<S> {
24    node: NodeId,
25    signer: S,
26}
27
28impl<S> fmt::Debug for Device<S> {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        f.debug_struct("Device")
31            .field("node", &self.node.to_human())
32            .finish()
33    }
34}
35
36impl<S: crypto::Signer + Default> Default for Device<S> {
37    fn default() -> Self {
38        Self::from(S::default())
39    }
40}
41
42impl<S> Device<S> {
43    /// Construct a new `Device`.
44    pub fn new(node: NodeId, signer: S) -> Self {
45        Self { node, signer }
46    }
47
48    /// Return the [`NodeId`] of the `Device.`
49    pub fn node_id(&self) -> &NodeId {
50        &self.node
51    }
52
53    /// Return the [`crypto::PublicKey`] of the `Device.`
54    pub fn public_key(&self) -> &crypto::PublicKey {
55        &self.node
56    }
57
58    /// Convert the `Device` into its signer.
59    ///
60    /// This consumes the `Device`.
61    pub fn into_inner(self) -> S {
62        self.signer
63    }
64}
65
66#[cfg(any(test, feature = "test"))]
67impl Device<crypto::test::signer::MockSigner> {
68    /// Construct a `Device` using a default `MockSigner` as the device signer.
69    pub fn mock() -> Self {
70        Device::from(crypto::test::signer::MockSigner::default())
71    }
72
73    /// Construct a `Device`, constructing an RNG'd `MockSigner` for the signer.
74    pub fn mock_rng(rng: &mut fastrand::Rng) -> Self {
75        Device::from(crypto::test::signer::MockSigner::new(rng))
76    }
77
78    /// Construct a `Device`, constructing a seeded `MockSigner` for the signer.
79    pub fn mock_from_seed(seed: [u8; 32]) -> Self {
80        Device::from(crypto::test::signer::MockSigner::from_seed(seed))
81    }
82}
83
84impl<S: Signer<Signature> + 'static> Device<S> {
85    /// Construct a [`BoxedDevice`] from a given `Device`.
86    pub fn boxed(self) -> BoxedDevice {
87        BoxedDevice(Device {
88            node: self.node,
89            signer: BoxedSigner(Box::new(self.signer)),
90        })
91    }
92}
93
94impl<S> Verifier<Signature> for Device<S> {
95    fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), crypto::signature::Error> {
96        self.node
97            .verify(msg, signature)
98            .map_err(crypto::signature::Error::from_source)
99    }
100}
101
102impl<S: crypto::Signer> From<S> for Device<S> {
103    fn from(signer: S) -> Self {
104        Self {
105            node: *signer.public_key(),
106            signer,
107        }
108    }
109}
110
111impl<S: crypto::Signer + Clone> From<&S> for Device<S> {
112    fn from(signer: &S) -> Self {
113        Self::from(signer.clone())
114    }
115}
116
117impl<S: Signer<Signature>> Signer<Signature> for Device<S> {
118    fn try_sign(&self, msg: &[u8]) -> Result<Signature, crypto::signature::Error> {
119        self.signer.try_sign(msg)
120    }
121}
122
123impl<S: Signer<Signature>> Signer<ExtendedSignature> for Device<S> {
124    fn try_sign(&self, msg: &[u8]) -> Result<ExtendedSignature, crypto::signature::Error> {
125        Ok(ExtendedSignature {
126            key: *self.public_key(),
127            sig: self.signer.try_sign(msg)?,
128        })
129    }
130}
131
132/// A `Signer<Signature>` that is packed in a [`Box`] for dynamic dispatch.
133pub struct BoxedSigner(Box<dyn Signer<Signature> + 'static>);
134
135impl Signer<Signature> for BoxedSigner {
136    fn try_sign(&self, msg: &[u8]) -> Result<Signature, crypto::signature::Error> {
137        self.0.try_sign(msg)
138    }
139}
140
141/// A `Device` where the signer is a dynamic `Signer<Signature>`, in the form of
142/// a [`BoxedSigner`].
143///
144/// This can be constructed via [`Device::boxed`].
145pub struct BoxedDevice(Device<BoxedSigner>);
146
147impl AsRef<Device<BoxedSigner>> for BoxedDevice {
148    fn as_ref(&self) -> &Device<BoxedSigner> {
149        &self.0
150    }
151}
152
153impl Deref for BoxedDevice {
154    type Target = Device<BoxedSigner>;
155
156    fn deref(&self) -> &Self::Target {
157        &self.0
158    }
159}
160
161impl Signer<Signature> for BoxedDevice {
162    fn try_sign(&self, msg: &[u8]) -> Result<Signature, crypto::signature::Error> {
163        self.0.signer.try_sign(msg)
164    }
165}
166
167impl Signer<ExtendedSignature> for BoxedDevice {
168    fn try_sign(&self, msg: &[u8]) -> Result<ExtendedSignature, crypto::signature::Error> {
169        Ok(ExtendedSignature {
170            key: *self.0.public_key(),
171            sig: self.0.signer.try_sign(msg)?,
172        })
173    }
174}