Skip to main content

jam_std_common/crypto/
ed25519.rs

1use super::concat;
2use crate::{availability::AvailabilityStatementHash, finality, simple::TrancheIndex};
3use codec::{ConstEncodedLen, Decode, Encode, MaxEncodedLen};
4use ed25519_consensus::{Error as SignatureError, VerificationKey};
5use jam_types::{hex::HexDisplay, CoreIndex, HeaderHash, OpaqueEd25519Public, WorkReportHash};
6
7pub const SIGNATURE_LEN: usize = 64;
8#[derive(Clone, Copy, Eq, PartialEq)]
9pub struct Signature(pub ed25519_consensus::Signature);
10
11pub const PUBLIC_LEN: usize = 32;
12#[derive(Clone, Copy, Eq, Ord, PartialEq, PartialOrd)]
13pub struct Public(pub ed25519_consensus::VerificationKeyBytes);
14impl Default for Public {
15	fn default() -> Self {
16		Self([0u8; PUBLIC_LEN].into())
17	}
18}
19impl From<OpaqueEd25519Public> for Public {
20	fn from(opaque: OpaqueEd25519Public) -> Self {
21		opaque.0.into()
22	}
23}
24impl From<Public> for OpaqueEd25519Public {
25	fn from(real: Public) -> Self {
26		Self(real.to_bytes())
27	}
28}
29
30pub const SECRET_LEN: usize = 32;
31#[derive(Clone)]
32pub struct Secret(pub ed25519_consensus::SigningKey);
33
34impl Signature {
35	pub fn null() -> Self {
36		Self([0u8; SIGNATURE_LEN].into())
37	}
38	pub fn to_bytes(self) -> [u8; SIGNATURE_LEN] {
39		self.0.to_bytes()
40	}
41	pub fn to_vec(self) -> Vec<u8> {
42		self.to_bytes().to_vec()
43	}
44}
45
46impl Encode for Signature {
47	fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
48		self.0.to_bytes().encode_to(dest)
49	}
50	fn size_hint(&self) -> usize {
51		self.0.to_bytes().size_hint()
52	}
53}
54impl MaxEncodedLen for Signature {
55	fn max_encoded_len() -> usize {
56		SIGNATURE_LEN
57	}
58}
59impl ConstEncodedLen for Signature {}
60impl Decode for Signature {
61	fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
62		Ok(Self(<[u8; SIGNATURE_LEN]>::decode(input)?.into()))
63	}
64}
65impl std::fmt::Debug for Signature {
66	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67		write!(f, "Signature")
68	}
69}
70impl From<Signature> for [u8; SIGNATURE_LEN] {
71	fn from(sig: Signature) -> Self {
72		sig.0.to_bytes()
73	}
74}
75
76impl Signature {
77	pub fn verify(&self, message: &Message, signer: &Public) -> Result<(), SignatureError> {
78		signer.verify(message, self)
79	}
80}
81
82impl std::fmt::Debug for Public {
83	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84		write!(f, "Pub({})", HexDisplay::from(self.as_bytes()))
85	}
86}
87impl std::hash::Hash for Public {
88	fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
89		self.0.as_ref().hash(state)
90	}
91}
92impl Encode for Public {
93	fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
94		AsRef::<[u8; PUBLIC_LEN]>::as_ref(&self).encode_to(dest)
95	}
96	fn size_hint(&self) -> usize {
97		PUBLIC_LEN
98	}
99}
100impl MaxEncodedLen for Public {
101	fn max_encoded_len() -> usize {
102		PUBLIC_LEN
103	}
104}
105impl ConstEncodedLen for Public {}
106
107impl Decode for Public {
108	fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
109		Ok(<[u8; PUBLIC_LEN]>::decode(input)?.into())
110	}
111}
112impl From<[u8; PUBLIC_LEN]> for Public {
113	fn from(bytes: [u8; PUBLIC_LEN]) -> Self {
114		Self(ed25519_consensus::VerificationKeyBytes::from(bytes))
115	}
116}
117impl AsRef<[u8; PUBLIC_LEN]> for Public {
118	fn as_ref(&self) -> &[u8; PUBLIC_LEN] {
119		self.as_bytes()
120	}
121}
122impl From<Public> for [u8; PUBLIC_LEN] {
123	fn from(p: Public) -> Self {
124		p.0.into()
125	}
126}
127impl Public {
128	pub fn as_bytes(&self) -> &[u8; PUBLIC_LEN] {
129		self.0.as_bytes()
130	}
131
132	pub fn to_bytes(self) -> [u8; PUBLIC_LEN] {
133		*self.as_bytes()
134	}
135
136	pub fn verify(&self, message: &Message, signature: &Signature) -> Result<(), SignatureError> {
137		let key: VerificationKey = self.0.try_into()?;
138		message.using_encoded(|enc| key.verify(&signature.0, enc))
139	}
140}
141
142/// A message signed by a node's Ed25519 key.
143pub enum Message<'a> {
144	/// Availability assurance.
145	Assurance(AvailabilityStatementHash),
146	/// Guarantee statement.
147	Guarantee(WorkReportHash),
148	/// Audit announcement statement.
149	Announcement(HeaderHash, TrancheIndex, &'a [(CoreIndex, WorkReportHash)]),
150	/// Work-report judgement.
151	Judgement(bool, WorkReportHash),
152	/// X.509 certificate.
153	X509(&'a [u8]),
154	/// TLS handshake transcript.
155	Tls(&'a [u8]),
156	/// Grandpa message
157	Grandpa(finality::GrandpaRoundNumber, finality::GrandpaSetId, finality::GrandpaMessage),
158}
159
160impl Message<'_> {
161	/// Call `f` with the encoded message.
162	pub fn using_encoded<R>(&self, f: impl FnOnce(&[u8]) -> R) -> R {
163		match self {
164			Self::Assurance(hash) => f(concat(&mut [0; 13 + 32], &[b"jam_available", &hash.0])),
165			Self::Guarantee(hash) => f(concat(&mut [0; 13 + 32], &[b"jam_guarantee", &hash.0])),
166			Self::Announcement(anchor, tranche, reports) => {
167				let mut buf = Vec::with_capacity(13 + reports.len() * 34 + 32);
168				buf.extend_from_slice(b"jam_announce");
169				buf.extend_from_slice(&[*tranche]);
170				reports.iter().for_each(|t| {
171					t.encode_to(&mut buf);
172				});
173				buf.extend_from_slice(&anchor.0);
174				f(&buf)
175			},
176			Self::Judgement(true, hash) => f(concat(&mut [0; 9 + 32], &[b"jam_valid", &hash.0])),
177			Self::Judgement(false, hash) =>
178				f(concat(&mut [0; 11 + 32], &[b"jam_invalid", &hash.0])),
179			Self::X509(enc) | Self::Tls(enc) => {
180				// These messages never begin with "jam":
181				//
182				// - TLS messages begin with a context string consisting of 64 spaces followed by
183				//   "TLS 1.3, ..."; see https://www.rfc-editor.org/rfc/rfc8446.html#section-4.4.3.
184				// - X.509 certificates are not signed with a context string, however they always
185				//   begin with the byte 0x30 ("0").
186				assert!(!enc.starts_with(b"jam"));
187				f(enc)
188			},
189			Self::Grandpa(round_number, set_id, message) => {
190				let mut buf = Vec::new();
191				buf.extend_from_slice(b"jam_grandpa_vote");
192				(message, round_number, set_id).encode_to(&mut buf);
193				f(&buf)
194			},
195		}
196	}
197}
198
199#[cfg(any(test, feature = "rand"))]
200impl rand::distr::Distribution<Public> for rand::distr::StandardUniform {
201	fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Public {
202		let bytes: [u8; PUBLIC_LEN] = rng.random();
203		bytes.into()
204	}
205}
206
207impl Eq for Secret {}
208impl PartialEq for Secret {
209	fn eq(&self, other: &Self) -> bool {
210		self.0.as_ref() == other.0.as_ref()
211	}
212}
213impl Encode for Secret {
214	fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
215		self.0.as_bytes().encode_to(dest)
216	}
217	fn size_hint(&self) -> usize {
218		SECRET_LEN
219	}
220}
221impl MaxEncodedLen for Secret {
222	fn max_encoded_len() -> usize {
223		SECRET_LEN
224	}
225}
226impl Decode for Secret {
227	fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
228		Ok(Self(ed25519_consensus::SigningKey::from(<[u8; SECRET_LEN]>::decode(input)?)))
229	}
230}
231impl AsRef<[u8; SECRET_LEN]> for Secret {
232	fn as_ref(&self) -> &[u8; SECRET_LEN] {
233		self.as_bytes()
234	}
235}
236impl From<Secret> for [u8; SECRET_LEN] {
237	fn from(s: Secret) -> Self {
238		s.to_bytes()
239	}
240}
241
242impl Secret {
243	pub fn new(rng: &mut impl rand::CryptoRng) -> Self {
244		Self::from_seed(rand::Rng::random(rng))
245	}
246	pub fn from_seed(seed: [u8; SECRET_LEN]) -> Self {
247		Self(seed.into())
248	}
249	pub fn as_bytes(&self) -> &[u8; SECRET_LEN] {
250		self.0.as_bytes()
251	}
252	pub fn to_bytes(&self) -> [u8; SECRET_LEN] {
253		*self.as_bytes()
254	}
255	pub fn generate() -> Self {
256		Self::new(&mut rand::rng())
257	}
258	pub fn sign(&self, message: &Message) -> Signature {
259		message.using_encoded(|enc| Signature(self.0.sign(enc)))
260	}
261	pub fn public(&self) -> Public {
262		Public((&self.0).into())
263	}
264}