pezsp_statement_store/
lib.rs

1// This file is part of Bizinikiwi.
2
3// Copyright (C) Parity Technologies (UK) Ltd. and Dijital Kurdistan Tech Institute
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18#![cfg_attr(not(feature = "std"), no_std)]
19#![warn(missing_docs)]
20
21//! A crate which contains statement-store primitives.
22
23extern crate alloc;
24
25use alloc::vec::Vec;
26use codec::{Decode, DecodeWithMemTracking, Encode};
27use pezsp_application_crypto::RuntimeAppPublic;
28#[cfg(feature = "std")]
29use pezsp_core::Pair;
30use scale_info::TypeInfo;
31
32/// Statement topic.
33pub type Topic = [u8; 32];
34/// Decryption key identifier.
35pub type DecryptionKey = [u8; 32];
36/// Statement hash.
37pub type Hash = [u8; 32];
38/// Block hash.
39pub type BlockHash = [u8; 32];
40/// Account id
41pub type AccountId = [u8; 32];
42/// Statement channel.
43pub type Channel = [u8; 32];
44
45/// Total number of topic fields allowed.
46pub const MAX_TOPICS: usize = 4;
47
48#[cfg(feature = "std")]
49pub use store_api::{
50	Error, NetworkPriority, Result, StatementSource, StatementStore, SubmitResult,
51};
52
53#[cfg(feature = "std")]
54mod ecies;
55pub mod runtime_api;
56#[cfg(feature = "std")]
57mod store_api;
58
59mod sr25519 {
60	mod app_sr25519 {
61		use pezsp_application_crypto::{app_crypto, key_types::STATEMENT, sr25519};
62		app_crypto!(sr25519, STATEMENT);
63	}
64	pub type Public = app_sr25519::Public;
65}
66
67/// Statement-store specific ed25519 crypto primitives.
68pub mod ed25519 {
69	mod app_ed25519 {
70		use pezsp_application_crypto::{app_crypto, ed25519, key_types::STATEMENT};
71		app_crypto!(ed25519, STATEMENT);
72	}
73	/// Statement-store specific ed25519 public key.
74	pub type Public = app_ed25519::Public;
75	/// Statement-store specific ed25519 key pair.
76	#[cfg(feature = "std")]
77	pub type Pair = app_ed25519::Pair;
78}
79
80mod ecdsa {
81	mod app_ecdsa {
82		use pezsp_application_crypto::{app_crypto, ecdsa, key_types::STATEMENT};
83		app_crypto!(ecdsa, STATEMENT);
84	}
85	pub type Public = app_ecdsa::Public;
86}
87
88/// Returns blake2-256 hash for the encoded statement.
89#[cfg(feature = "std")]
90pub fn hash_encoded(data: &[u8]) -> [u8; 32] {
91	pezsp_crypto_hashing::blake2_256(data)
92}
93
94/// Statement proof.
95#[derive(
96	Encode, Decode, DecodeWithMemTracking, TypeInfo, pezsp_core::RuntimeDebug, Clone, PartialEq, Eq,
97)]
98pub enum Proof {
99	/// Sr25519 Signature.
100	Sr25519 {
101		/// Signature.
102		signature: [u8; 64],
103		/// Public key.
104		signer: [u8; 32],
105	},
106	/// Ed25519 Signature.
107	Ed25519 {
108		/// Signature.
109		signature: [u8; 64],
110		/// Public key.
111		signer: [u8; 32],
112	},
113	/// Secp256k1 Signature.
114	Secp256k1Ecdsa {
115		/// Signature.
116		signature: [u8; 65],
117		/// Public key.
118		signer: [u8; 33],
119	},
120	/// On-chain event proof.
121	OnChain {
122		/// Account identifier associated with the event.
123		who: AccountId,
124		/// Hash of block that contains the event.
125		block_hash: BlockHash,
126		/// Index of the event in the event list.
127		event_index: u64,
128	},
129}
130
131impl Proof {
132	/// Return account id for the proof creator.
133	pub fn account_id(&self) -> AccountId {
134		match self {
135			Proof::Sr25519 { signer, .. } => *signer,
136			Proof::Ed25519 { signer, .. } => *signer,
137			Proof::Secp256k1Ecdsa { signer, .. } => {
138				<pezsp_runtime::traits::BlakeTwo256 as pezsp_core::Hasher>::hash(signer).into()
139			},
140			Proof::OnChain { who, .. } => *who,
141		}
142	}
143}
144
145/// Statement attributes. Each statement is a list of 0 or more fields. Fields may only appear once
146/// and in the order declared here.
147#[derive(Encode, Decode, TypeInfo, pezsp_core::RuntimeDebug, Clone, PartialEq, Eq)]
148#[repr(u8)]
149pub enum Field {
150	/// Statement proof.
151	AuthenticityProof(Proof) = 0,
152	/// An identifier for the key that `Data` field may be decrypted with.
153	DecryptionKey(DecryptionKey) = 1,
154	/// Priority when competing with other messages from the same sender.
155	Priority(u32) = 2,
156	/// Account channel to use. Only one message per `(account, channel)` pair is allowed.
157	Channel(Channel) = 3,
158	/// First statement topic.
159	Topic1(Topic) = 4,
160	/// Second statement topic.
161	Topic2(Topic) = 5,
162	/// Third statement topic.
163	Topic3(Topic) = 6,
164	/// Fourth statement topic.
165	Topic4(Topic) = 7,
166	/// Additional data.
167	Data(Vec<u8>) = 8,
168}
169
170impl Field {
171	fn discriminant(&self) -> u8 {
172		// This is safe for repr(u8)
173		// see https://doc.rust-lang.org/reference/items/enumerations.html#pointer-casting
174		unsafe { *(self as *const Self as *const u8) }
175	}
176}
177
178/// Statement structure.
179#[derive(
180	DecodeWithMemTracking, TypeInfo, pezsp_core::RuntimeDebug, Clone, PartialEq, Eq, Default,
181)]
182pub struct Statement {
183	proof: Option<Proof>,
184	decryption_key: Option<DecryptionKey>,
185	channel: Option<Channel>,
186	priority: Option<u32>,
187	num_topics: u8,
188	topics: [Topic; MAX_TOPICS],
189	data: Option<Vec<u8>>,
190}
191
192impl Decode for Statement {
193	fn decode<I: codec::Input>(input: &mut I) -> core::result::Result<Self, codec::Error> {
194		// Encoding matches that of Vec<Field>. Basically this just means accepting that there
195		// will be a prefix of vector length.
196		let num_fields: codec::Compact<u32> = Decode::decode(input)?;
197		let mut tag = 0;
198		let mut statement = Statement::new();
199		for i in 0..num_fields.into() {
200			let field: Field = Decode::decode(input)?;
201			if i > 0 && field.discriminant() <= tag {
202				return Err("Invalid field order or duplicate fields".into());
203			}
204			tag = field.discriminant();
205			match field {
206				Field::AuthenticityProof(p) => statement.set_proof(p),
207				Field::DecryptionKey(key) => statement.set_decryption_key(key),
208				Field::Priority(p) => statement.set_priority(p),
209				Field::Channel(c) => statement.set_channel(c),
210				Field::Topic1(t) => statement.set_topic(0, t),
211				Field::Topic2(t) => statement.set_topic(1, t),
212				Field::Topic3(t) => statement.set_topic(2, t),
213				Field::Topic4(t) => statement.set_topic(3, t),
214				Field::Data(data) => statement.set_plain_data(data),
215			}
216		}
217		Ok(statement)
218	}
219}
220
221impl Encode for Statement {
222	fn encode(&self) -> Vec<u8> {
223		self.encoded(false)
224	}
225}
226
227#[derive(Clone, Copy, PartialEq, Eq, Debug)]
228/// Result returned by `Statement::verify_signature`
229pub enum SignatureVerificationResult {
230	/// Signature is valid and matches this account id.
231	Valid(AccountId),
232	/// Signature has failed verification.
233	Invalid,
234	/// No signature in the proof or no proof.
235	NoSignature,
236}
237
238impl Statement {
239	/// Create a new empty statement with no proof.
240	pub fn new() -> Statement {
241		Default::default()
242	}
243
244	/// Create a new statement with a proof.
245	pub fn new_with_proof(proof: Proof) -> Statement {
246		let mut statement = Self::new();
247		statement.set_proof(proof);
248		statement
249	}
250
251	/// Sign with a key that matches given public key in the keystore.
252	///
253	/// Returns `true` if signing worked (private key present etc).
254	///
255	/// NOTE: This can only be called from the runtime.
256	pub fn sign_sr25519_public(&mut self, key: &sr25519::Public) -> bool {
257		let to_sign = self.signature_material();
258		if let Some(signature) = key.sign(&to_sign) {
259			let proof = Proof::Sr25519 {
260				signature: signature.into_inner().into(),
261				signer: key.clone().into_inner().into(),
262			};
263			self.set_proof(proof);
264			true
265		} else {
266			false
267		}
268	}
269
270	/// Sign with a given private key and add the signature proof field.
271	#[cfg(feature = "std")]
272	pub fn sign_sr25519_private(&mut self, key: &pezsp_core::sr25519::Pair) {
273		let to_sign = self.signature_material();
274		let proof =
275			Proof::Sr25519 { signature: key.sign(&to_sign).into(), signer: key.public().into() };
276		self.set_proof(proof);
277	}
278
279	/// Sign with a key that matches given public key in the keystore.
280	///
281	/// Returns `true` if signing worked (private key present etc).
282	///
283	/// NOTE: This can only be called from the runtime.
284	pub fn sign_ed25519_public(&mut self, key: &ed25519::Public) -> bool {
285		let to_sign = self.signature_material();
286		if let Some(signature) = key.sign(&to_sign) {
287			let proof = Proof::Ed25519 {
288				signature: signature.into_inner().into(),
289				signer: key.clone().into_inner().into(),
290			};
291			self.set_proof(proof);
292			true
293		} else {
294			false
295		}
296	}
297
298	/// Sign with a given private key and add the signature proof field.
299	#[cfg(feature = "std")]
300	pub fn sign_ed25519_private(&mut self, key: &pezsp_core::ed25519::Pair) {
301		let to_sign = self.signature_material();
302		let proof =
303			Proof::Ed25519 { signature: key.sign(&to_sign).into(), signer: key.public().into() };
304		self.set_proof(proof);
305	}
306
307	/// Sign with a key that matches given public key in the keystore.
308	///
309	/// Returns `true` if signing worked (private key present etc).
310	///
311	/// NOTE: This can only be called from the runtime.
312	///
313	/// Returns `true` if signing worked (private key present etc).
314	///
315	/// NOTE: This can only be called from the runtime.
316	pub fn sign_ecdsa_public(&mut self, key: &ecdsa::Public) -> bool {
317		let to_sign = self.signature_material();
318		if let Some(signature) = key.sign(&to_sign) {
319			let proof = Proof::Secp256k1Ecdsa {
320				signature: signature.into_inner().into(),
321				signer: key.clone().into_inner().0,
322			};
323			self.set_proof(proof);
324			true
325		} else {
326			false
327		}
328	}
329
330	/// Sign with a given private key and add the signature proof field.
331	#[cfg(feature = "std")]
332	pub fn sign_ecdsa_private(&mut self, key: &pezsp_core::ecdsa::Pair) {
333		let to_sign = self.signature_material();
334		let proof =
335			Proof::Secp256k1Ecdsa { signature: key.sign(&to_sign).into(), signer: key.public().0 };
336		self.set_proof(proof);
337	}
338
339	/// Check proof signature, if any.
340	pub fn verify_signature(&self) -> SignatureVerificationResult {
341		use pezsp_runtime::traits::Verify;
342
343		match self.proof() {
344			Some(Proof::OnChain { .. }) | None => SignatureVerificationResult::NoSignature,
345			Some(Proof::Sr25519 { signature, signer }) => {
346				let to_sign = self.signature_material();
347				let signature = pezsp_core::sr25519::Signature::from(*signature);
348				let public = pezsp_core::sr25519::Public::from(*signer);
349				if signature.verify(to_sign.as_slice(), &public) {
350					SignatureVerificationResult::Valid(*signer)
351				} else {
352					SignatureVerificationResult::Invalid
353				}
354			},
355			Some(Proof::Ed25519 { signature, signer }) => {
356				let to_sign = self.signature_material();
357				let signature = pezsp_core::ed25519::Signature::from(*signature);
358				let public = pezsp_core::ed25519::Public::from(*signer);
359				if signature.verify(to_sign.as_slice(), &public) {
360					SignatureVerificationResult::Valid(*signer)
361				} else {
362					SignatureVerificationResult::Invalid
363				}
364			},
365			Some(Proof::Secp256k1Ecdsa { signature, signer }) => {
366				let to_sign = self.signature_material();
367				let signature = pezsp_core::ecdsa::Signature::from(*signature);
368				let public = pezsp_core::ecdsa::Public::from(*signer);
369				if signature.verify(to_sign.as_slice(), &public) {
370					let sender_hash =
371						<pezsp_runtime::traits::BlakeTwo256 as pezsp_core::Hasher>::hash(signer);
372					SignatureVerificationResult::Valid(sender_hash.into())
373				} else {
374					SignatureVerificationResult::Invalid
375				}
376			},
377		}
378	}
379
380	/// Calculate statement hash.
381	#[cfg(feature = "std")]
382	pub fn hash(&self) -> [u8; 32] {
383		self.using_encoded(hash_encoded)
384	}
385
386	/// Returns a topic by topic index.
387	pub fn topic(&self, index: usize) -> Option<Topic> {
388		if index < self.num_topics as usize {
389			Some(self.topics[index])
390		} else {
391			None
392		}
393	}
394
395	/// Returns decryption key if any.
396	pub fn decryption_key(&self) -> Option<DecryptionKey> {
397		self.decryption_key
398	}
399
400	/// Convert to internal data.
401	pub fn into_data(self) -> Option<Vec<u8>> {
402		self.data
403	}
404
405	/// Get a reference to the statement proof, if any.
406	pub fn proof(&self) -> Option<&Proof> {
407		self.proof.as_ref()
408	}
409
410	/// Get proof account id, if any
411	pub fn account_id(&self) -> Option<AccountId> {
412		self.proof.as_ref().map(Proof::account_id)
413	}
414
415	/// Get plain data.
416	pub fn data(&self) -> Option<&Vec<u8>> {
417		self.data.as_ref()
418	}
419
420	/// Get plain data len.
421	pub fn data_len(&self) -> usize {
422		self.data().map_or(0, Vec::len)
423	}
424
425	/// Get channel, if any.
426	pub fn channel(&self) -> Option<Channel> {
427		self.channel
428	}
429
430	/// Get priority, if any.
431	pub fn priority(&self) -> Option<u32> {
432		self.priority
433	}
434
435	/// Return encoded fields that can be signed to construct or verify a proof
436	fn signature_material(&self) -> Vec<u8> {
437		self.encoded(true)
438	}
439
440	/// Remove the proof of this statement.
441	pub fn remove_proof(&mut self) {
442		self.proof = None;
443	}
444
445	/// Set statement proof. Any existing proof is overwritten.
446	pub fn set_proof(&mut self, proof: Proof) {
447		self.proof = Some(proof)
448	}
449
450	/// Set statement priority.
451	pub fn set_priority(&mut self, priority: u32) {
452		self.priority = Some(priority)
453	}
454
455	/// Set statement channel.
456	pub fn set_channel(&mut self, channel: Channel) {
457		self.channel = Some(channel)
458	}
459
460	/// Set topic by index. Does noting if index is over `MAX_TOPICS`.
461	pub fn set_topic(&mut self, index: usize, topic: Topic) {
462		if index < MAX_TOPICS {
463			self.topics[index] = topic;
464			self.num_topics = self.num_topics.max(index as u8 + 1);
465		}
466	}
467
468	/// Set decryption key.
469	pub fn set_decryption_key(&mut self, key: DecryptionKey) {
470		self.decryption_key = Some(key);
471	}
472
473	/// Set unencrypted statement data.
474	pub fn set_plain_data(&mut self, data: Vec<u8>) {
475		self.data = Some(data)
476	}
477
478	fn encoded(&self, for_signing: bool) -> Vec<u8> {
479		// Encoding matches that of Vec<Field>. Basically this just means accepting that there
480		// will be a prefix of vector length.
481		let num_fields = if !for_signing && self.proof.is_some() { 1 } else { 0 }
482			+ if self.decryption_key.is_some() { 1 } else { 0 }
483			+ if self.priority.is_some() { 1 } else { 0 }
484			+ if self.channel.is_some() { 1 } else { 0 }
485			+ if self.data.is_some() { 1 } else { 0 }
486			+ self.num_topics as u32;
487
488		let mut output = Vec::new();
489		// When encoding signature payload, the length prefix is omitted.
490		// This is so that the signature for encoded statement can potentially be derived without
491		// needing to re-encode the statement.
492		if !for_signing {
493			let compact_len = codec::Compact::<u32>(num_fields);
494			compact_len.encode_to(&mut output);
495
496			if let Some(proof) = &self.proof {
497				0u8.encode_to(&mut output);
498				proof.encode_to(&mut output);
499			}
500		}
501		if let Some(decryption_key) = &self.decryption_key {
502			1u8.encode_to(&mut output);
503			decryption_key.encode_to(&mut output);
504		}
505		if let Some(priority) = &self.priority {
506			2u8.encode_to(&mut output);
507			priority.encode_to(&mut output);
508		}
509		if let Some(channel) = &self.channel {
510			3u8.encode_to(&mut output);
511			channel.encode_to(&mut output);
512		}
513		for t in 0..self.num_topics {
514			(4u8 + t).encode_to(&mut output);
515			self.topics[t as usize].encode_to(&mut output);
516		}
517		if let Some(data) = &self.data {
518			8u8.encode_to(&mut output);
519			data.encode_to(&mut output);
520		}
521		output
522	}
523
524	/// Encrypt give data with given key and store both in the statements.
525	#[cfg(feature = "std")]
526	pub fn encrypt(
527		&mut self,
528		data: &[u8],
529		key: &pezsp_core::ed25519::Public,
530	) -> core::result::Result<(), ecies::Error> {
531		let encrypted = ecies::encrypt_ed25519(key, data)?;
532		self.data = Some(encrypted);
533		self.decryption_key = Some((*key).into());
534		Ok(())
535	}
536
537	/// Decrypt data (if any) with the given private key.
538	#[cfg(feature = "std")]
539	pub fn decrypt_private(
540		&self,
541		key: &pezsp_core::ed25519::Pair,
542	) -> core::result::Result<Option<Vec<u8>>, ecies::Error> {
543		self.data.as_ref().map(|d| ecies::decrypt_ed25519(key, d)).transpose()
544	}
545}
546
547#[cfg(test)]
548mod test {
549	use crate::{hash_encoded, Field, Proof, SignatureVerificationResult, Statement};
550	use codec::{Decode, Encode};
551	use pezsp_application_crypto::Pair;
552
553	#[test]
554	fn statement_encoding_matches_vec() {
555		let mut statement = Statement::new();
556		assert!(statement.proof().is_none());
557		let proof = Proof::OnChain { who: [42u8; 32], block_hash: [24u8; 32], event_index: 66 };
558
559		let decryption_key = [0xde; 32];
560		let topic1 = [0x01; 32];
561		let topic2 = [0x02; 32];
562		let data = vec![55, 99];
563		let priority = 999;
564		let channel = [0xcc; 32];
565
566		statement.set_proof(proof.clone());
567		statement.set_decryption_key(decryption_key);
568		statement.set_priority(priority);
569		statement.set_channel(channel);
570		statement.set_topic(0, topic1);
571		statement.set_topic(1, topic2);
572		statement.set_plain_data(data.clone());
573
574		statement.set_topic(5, [0x55; 32]);
575		assert_eq!(statement.topic(5), None);
576
577		let fields = vec![
578			Field::AuthenticityProof(proof.clone()),
579			Field::DecryptionKey(decryption_key),
580			Field::Priority(priority),
581			Field::Channel(channel),
582			Field::Topic1(topic1),
583			Field::Topic2(topic2),
584			Field::Data(data.clone()),
585		];
586
587		let encoded = statement.encode();
588		assert_eq!(statement.hash(), hash_encoded(&encoded));
589		assert_eq!(encoded, fields.encode());
590
591		let decoded = Statement::decode(&mut encoded.as_slice()).unwrap();
592		assert_eq!(decoded, statement);
593	}
594
595	#[test]
596	fn decode_checks_fields() {
597		let topic1 = [0x01; 32];
598		let topic2 = [0x02; 32];
599		let priority = 999;
600
601		let fields = vec![
602			Field::Priority(priority),
603			Field::Topic1(topic1),
604			Field::Topic1(topic1),
605			Field::Topic2(topic2),
606		]
607		.encode();
608
609		assert!(Statement::decode(&mut fields.as_slice()).is_err());
610
611		let fields =
612			vec![Field::Topic1(topic1), Field::Priority(priority), Field::Topic2(topic2)].encode();
613
614		assert!(Statement::decode(&mut fields.as_slice()).is_err());
615	}
616
617	#[test]
618	fn sign_and_verify() {
619		let mut statement = Statement::new();
620		statement.set_plain_data(vec![42]);
621
622		let sr25519_kp = pezsp_core::sr25519::Pair::from_string("//Alice", None).unwrap();
623		let ed25519_kp = pezsp_core::ed25519::Pair::from_string("//Alice", None).unwrap();
624		let secp256k1_kp = pezsp_core::ecdsa::Pair::from_string("//Alice", None).unwrap();
625
626		statement.sign_sr25519_private(&sr25519_kp);
627		assert_eq!(
628			statement.verify_signature(),
629			SignatureVerificationResult::Valid(sr25519_kp.public().0)
630		);
631
632		statement.sign_ed25519_private(&ed25519_kp);
633		assert_eq!(
634			statement.verify_signature(),
635			SignatureVerificationResult::Valid(ed25519_kp.public().0)
636		);
637
638		statement.sign_ecdsa_private(&secp256k1_kp);
639		assert_eq!(
640			statement.verify_signature(),
641			SignatureVerificationResult::Valid(pezsp_crypto_hashing::blake2_256(
642				&secp256k1_kp.public().0
643			))
644		);
645
646		// set an invalid signature
647		statement.set_proof(Proof::Sr25519 { signature: [0u8; 64], signer: [0u8; 32] });
648		assert_eq!(statement.verify_signature(), SignatureVerificationResult::Invalid);
649
650		statement.remove_proof();
651		assert_eq!(statement.verify_signature(), SignatureVerificationResult::NoSignature);
652	}
653
654	#[test]
655	fn encrypt_decrypt() {
656		let mut statement = Statement::new();
657		let (pair, _) = pezsp_core::ed25519::Pair::generate();
658		let plain = b"test data".to_vec();
659
660		//let sr25519_kp = pezsp_core::sr25519::Pair::from_string("//Alice", None).unwrap();
661		statement.encrypt(&plain, &pair.public()).unwrap();
662		assert_ne!(plain.as_slice(), statement.data().unwrap().as_slice());
663
664		let decrypted = statement.decrypt_private(&pair).unwrap();
665		assert_eq!(decrypted, Some(plain));
666	}
667}