qp_dilithium_crypto/
traits.rs

1use super::types::{
2	DilithiumPair, DilithiumPublic, DilithiumSignatureScheme, DilithiumSigner, Error,
3	WrappedPublicBytes, WrappedSignatureBytes,
4};
5
6use crate::{DilithiumSignature, DilithiumSignatureWithPublic};
7use alloc::vec::Vec;
8use qp_poseidon::PoseidonHasher;
9use sp_core::{
10	crypto::{Derive, Public, PublicBytes, Signature, SignatureBytes},
11	ByteArray, Hasher, H256,
12};
13use sp_runtime::{
14	traits::{IdentifyAccount, Verify},
15	AccountId32, CryptoType,
16};
17
18/// Verifies a Dilithium ML-DSA-87 signature
19///
20/// This function performs signature verification using the Dilithium post-quantum
21/// cryptographic signature scheme (ML-DSA-87). It validates that the given signature
22/// was created by the holder of the private key corresponding to the public key.
23///
24/// # Arguments
25/// * `pub_key` - The public key bytes (must be valid Dilithium public key)
26/// * `msg` - The message that was signed
27/// * `sig` - The signature bytes to verify
28///
29/// # Returns
30/// `true` if the signature is valid and verification succeeds, `false` otherwise
31///
32/// # Examples
33/// ```ignore
34/// use qp_dilithium_crypto::verify;
35///
36/// let valid = verify(&public_key_bytes, &message, &signature_bytes);
37/// if valid {
38///     println!("Signature is valid!");
39/// }
40/// ```
41pub fn verify(pub_key: &[u8], msg: &[u8], sig: &[u8]) -> bool {
42	use qp_rusty_crystals_dilithium::ml_dsa_87::PublicKey;
43	match PublicKey::from_bytes(pub_key) {
44		Ok(pk) => pk.verify(msg, sig, None),
45		Err(e) => {
46			log::warn!("public key failed to deserialize {:?}", e);
47			false
48		},
49	}
50}
51
52//
53// Trait implementations for WrappedPublicBytes
54//
55
56impl<const N: usize, SubTag> Derive for WrappedPublicBytes<N, SubTag> {}
57impl<const N: usize, SubTag> AsMut<[u8]> for WrappedPublicBytes<N, SubTag> {
58	fn as_mut(&mut self) -> &mut [u8] {
59		self.0.as_mut()
60	}
61}
62impl<const N: usize, SubTag> AsRef<[u8]> for WrappedPublicBytes<N, SubTag> {
63	fn as_ref(&self) -> &[u8] {
64		self.0.as_slice()
65	}
66}
67impl<const N: usize, SubTag> TryFrom<&[u8]> for WrappedPublicBytes<N, SubTag> {
68	type Error = ();
69	fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
70		PublicBytes::from_slice(data)
71			.map(|bytes| WrappedPublicBytes(bytes))
72			.map_err(|_| ())
73	}
74}
75impl<const N: usize, SubTag> ByteArray for WrappedPublicBytes<N, SubTag> {
76	fn as_slice(&self) -> &[u8] {
77		self.0.as_slice()
78	}
79	const LEN: usize = N;
80	fn from_slice(data: &[u8]) -> Result<Self, ()> {
81		PublicBytes::from_slice(data)
82			.map(|bytes| WrappedPublicBytes(bytes))
83			.map_err(|_| ())
84	}
85	fn to_raw_vec(&self) -> Vec<u8> {
86		self.0.as_slice().to_vec()
87	}
88}
89impl<const N: usize, SubTag> CryptoType for WrappedPublicBytes<N, SubTag> {
90	type Pair = DilithiumPair;
91}
92impl<const N: usize, SubTag: Clone + Eq> Public for WrappedPublicBytes<N, SubTag> {}
93
94impl<const N: usize, SubTag> Default for WrappedPublicBytes<N, SubTag> {
95	fn default() -> Self {
96		WrappedPublicBytes(PublicBytes::default())
97	}
98}
99impl<const N: usize, SubTag> alloc::fmt::Debug for WrappedPublicBytes<N, SubTag> {
100	#[cfg(feature = "std")]
101	fn fmt(&self, f: &mut alloc::fmt::Formatter) -> alloc::fmt::Result {
102		write!(f, "{}", sp_core::hexdisplay::HexDisplay::from(&self.0.as_ref()))
103	}
104
105	#[cfg(not(feature = "std"))]
106	fn fmt(&self, _: &mut alloc::fmt::Formatter) -> alloc::fmt::Result {
107		Ok(())
108	}
109}
110
111impl IdentifyAccount for DilithiumPublic {
112	type AccountId = AccountId32;
113	fn into_account(self) -> Self::AccountId {
114		AccountId32::new(PoseidonHasher::hash(self.0.as_slice()).0)
115	}
116}
117
118pub struct WormholeAddress(pub H256);
119
120// AccountID32 for a wormhole address is the same as the address itself
121impl IdentifyAccount for WormholeAddress {
122	type AccountId = AccountId32;
123	fn into_account(self) -> Self::AccountId {
124		AccountId32::new(self.0.as_bytes().try_into().unwrap())
125	}
126}
127
128//
129// Trait implementations for WrappedSignatureBytes
130//
131impl<const N: usize, SubTag> Derive for WrappedSignatureBytes<N, SubTag> {}
132impl<const N: usize, SubTag> AsMut<[u8]> for WrappedSignatureBytes<N, SubTag> {
133	fn as_mut(&mut self) -> &mut [u8] {
134		self.0.as_mut()
135	}
136}
137impl<const N: usize, SubTag> AsRef<[u8]> for WrappedSignatureBytes<N, SubTag> {
138	fn as_ref(&self) -> &[u8] {
139		self.0.as_slice()
140	}
141}
142impl<const N: usize, SubTag> TryFrom<&[u8]> for WrappedSignatureBytes<N, SubTag> {
143	type Error = ();
144	fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
145		SignatureBytes::from_slice(data)
146			.map(|bytes| WrappedSignatureBytes(bytes))
147			.map_err(|_| ())
148	}
149}
150impl<const N: usize, SubTag> ByteArray for WrappedSignatureBytes<N, SubTag> {
151	fn as_slice(&self) -> &[u8] {
152		self.0.as_slice()
153	}
154	const LEN: usize = N;
155	fn from_slice(data: &[u8]) -> Result<Self, ()> {
156		SignatureBytes::from_slice(data)
157			.map(|bytes| WrappedSignatureBytes(bytes))
158			.map_err(|_| ())
159	}
160	fn to_raw_vec(&self) -> Vec<u8> {
161		self.0.as_slice().to_vec()
162	}
163}
164impl<const N: usize, SubTag> CryptoType for WrappedSignatureBytes<N, SubTag> {
165	type Pair = DilithiumPair;
166}
167impl<const N: usize, SubTag: Clone + Eq> Signature for WrappedSignatureBytes<N, SubTag> {}
168
169impl<const N: usize, SubTag> Default for WrappedSignatureBytes<N, SubTag> {
170	fn default() -> Self {
171		WrappedSignatureBytes(SignatureBytes::default())
172	}
173}
174
175impl<const N: usize, SubTag> alloc::fmt::Debug for WrappedSignatureBytes<N, SubTag> {
176	#[cfg(feature = "std")]
177	fn fmt(&self, f: &mut alloc::fmt::Formatter) -> alloc::fmt::Result {
178		write!(f, "{}", sp_core::hexdisplay::HexDisplay::from(&self.0.as_ref()))
179	}
180
181	#[cfg(not(feature = "std"))]
182	fn fmt(&self, _: &mut alloc::fmt::Formatter) -> alloc::fmt::Result {
183		Ok(())
184	}
185}
186
187//
188// Trait implementations for DilithiumPair
189//
190
191impl CryptoType for DilithiumPair {
192	type Pair = Self;
193}
194
195//
196// Trait implementations for DilithiumSignatureScheme
197//
198
199impl Verify for DilithiumSignatureScheme {
200	type Signer = DilithiumSigner;
201
202	fn verify<L: sp_runtime::traits::Lazy<[u8]>>(
203		&self,
204		mut msg: L,
205		signer: &<Self::Signer as IdentifyAccount>::AccountId,
206	) -> bool {
207		let Self::Dilithium(sig_public) = self;
208		let account = sig_public.public().clone().into_account();
209		if account != *signer {
210			return false;
211		}
212		let result =
213			verify(sig_public.public().as_ref(), msg.get(), sig_public.signature().as_ref());
214		result
215	}
216}
217
218//
219// Trait implementations for DilithiumSigner
220//
221impl From<DilithiumPublic> for DilithiumSigner {
222	fn from(x: DilithiumPublic) -> Self {
223		Self::Dilithium(x)
224	}
225}
226
227impl IdentifyAccount for DilithiumSigner {
228	type AccountId = AccountId32;
229
230	fn into_account(self) -> AccountId32 {
231		let Self::Dilithium(who) = self;
232		PoseidonHasher::hash(who.as_ref()).0.into()
233	}
234}
235
236impl From<DilithiumPublic> for AccountId32 {
237	fn from(public: DilithiumPublic) -> Self {
238		public.into_account()
239	}
240}
241
242//
243// Implementation methods for DilithiumPair
244//
245
246impl DilithiumPair {
247	pub fn from_seed(seed: &[u8]) -> Result<Self, Error> {
248		let keypair = crate::pair::generate(Some(seed))?;
249		Ok(DilithiumPair { secret: keypair.secret.to_bytes(), public: keypair.public.to_bytes() })
250	}
251	pub fn public(&self) -> DilithiumPublic {
252		DilithiumPublic::from_slice(&self.public).expect("Valid public key bytes")
253	}
254}
255
256impl alloc::fmt::Debug for DilithiumSignatureWithPublic {
257	#[cfg(feature = "std")]
258	fn fmt(&self, f: &mut alloc::fmt::Formatter) -> alloc::fmt::Result {
259		write!(
260			f,
261			"ResonanceSignatureWithPublic {{ signature: {:?}, public: {:?} }}",
262			self.signature(),
263			self.public()
264		)
265	}
266
267	#[cfg(not(feature = "std"))]
268	fn fmt(&self, f: &mut alloc::fmt::Formatter) -> alloc::fmt::Result {
269		write!(f, "ResonanceSignatureWithPublic")
270	}
271}
272
273impl From<DilithiumSignatureWithPublic> for DilithiumSignatureScheme {
274	fn from(x: DilithiumSignatureWithPublic) -> Self {
275		Self::Dilithium(x)
276	}
277}
278
279impl TryFrom<DilithiumSignatureScheme> for DilithiumSignatureWithPublic {
280	type Error = ();
281	fn try_from(m: DilithiumSignatureScheme) -> Result<Self, Self::Error> {
282		let DilithiumSignatureScheme::Dilithium(sig_with_public) = m;
283		Ok(sig_with_public)
284	}
285}
286
287impl AsMut<[u8]> for DilithiumSignatureWithPublic {
288	fn as_mut(&mut self) -> &mut [u8] {
289		self.bytes.as_mut()
290	}
291}
292impl TryFrom<&[u8]> for DilithiumSignatureWithPublic {
293	type Error = ();
294	fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
295		if data.len() != Self::TOTAL_LEN {
296			return Err(());
297		}
298		let (sig_bytes, pub_bytes) = data.split_at(<DilithiumSignature as ByteArray>::LEN);
299		let signature = DilithiumSignature::from_slice(sig_bytes).map_err(|_| ())?;
300		let public = DilithiumPublic::from_slice(pub_bytes).map_err(|_| ())?;
301		Ok(Self::new(signature, public))
302	}
303}
304
305impl ByteArray for DilithiumSignatureWithPublic {
306	const LEN: usize = Self::TOTAL_LEN;
307
308	fn to_raw_vec(&self) -> Vec<u8> {
309		self.to_bytes().to_vec()
310	}
311
312	fn from_slice(data: &[u8]) -> Result<Self, ()> {
313		if data.len() != Self::LEN {
314			return Err(());
315		}
316		let bytes = <[u8; Self::LEN]>::try_from(data).map_err(|_| ())?;
317		Self::from_bytes(&bytes).map_err(|_| ())
318	}
319
320	fn as_slice(&self) -> &[u8] {
321		self.bytes.as_slice()
322	}
323}
324impl AsRef<[u8; Self::LEN]> for DilithiumSignatureWithPublic {
325	fn as_ref(&self) -> &[u8; Self::LEN] {
326		&self.bytes
327	}
328}
329
330impl AsRef<[u8]> for DilithiumSignatureWithPublic {
331	fn as_ref(&self) -> &[u8] {
332		&self.bytes
333	}
334}
335impl Signature for DilithiumSignatureWithPublic {}
336
337impl CryptoType for DilithiumSignatureWithPublic {
338	type Pair = DilithiumPair;
339}