sp_core/proof_of_possession.rs
1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
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//! Utilities for proving possession of a particular public key
19
20use crate::crypto::{CryptoType, Pair};
21use sp_std::vec::Vec;
22
23/// Pair which is able to generate proof of possession.
24///
25/// This is implemented in different trait to provide default behavior.
26pub trait ProofOfPossessionGenerator: Pair
27where
28 Self::Public: CryptoType,
29{
30 /// Generate proof of possession.
31 ///
32 /// The proof of possession generator is supposed to
33 /// produce a "signature" with unique hash context that should
34 /// never be used in other signatures. This proves that
35 /// the secret key is known to the prover. While prevent
36 /// malicious actors to trick an honest party to sign an
37 /// unpossessed public key resulting in a rogue key attack (See: Section 4.3 of
38 /// - Ristenpart, T., & Yilek, S. (2007). The power of proofs-of-possession: Securing multiparty
39 /// signatures against rogue-key attacks. In , Annual {{International Conference}} on the
40 /// {{Theory}} and {{Applications}} of {{Cryptographic Techniques} (pp. 228–245). : Springer).
41 #[cfg(feature = "full_crypto")]
42 fn generate_proof_of_possession(&mut self) -> Self::Signature;
43}
44
45/// Pair which is able to verify proof of possession.
46///
47/// While you don't need a keypair to verify a proof of possession (you only need a public key)
48/// we constrain on Pair to use the Public and Signature types associated to Pair.
49/// This is implemented in different trait (than Public Key) to provide default behavior.
50pub trait ProofOfPossessionVerifier: Pair
51where
52 Self::Public: CryptoType,
53{
54 /// Verify proof of possession.
55 ///
56 /// The proof of possession verifier is supposed to to verify a signature with unique hash
57 /// context that is produced solely for this reason. This proves that that the secret key is
58 /// known to the prover.
59 fn verify_proof_of_possession(
60 proof_of_possession: &Self::Signature,
61 allegedly_possessesd_pubkey: &Self::Public,
62 ) -> bool;
63}
64
65/// Marker trait to identify whether the scheme is not aggregatable.
66///
67/// Aggregatable schemes may change/optimize implementation parts such as Proof Of Possession
68/// or other specifics.
69///
70/// This is specifically because implementation of proof of possession for aggregatable schemes
71/// is security critical.
72///
73/// We would like to prevent aggregatable scheme from unknowingly generating signatures
74/// which aggregate to false albeit valid proof of possession aka rogue key attack.
75/// We ensure that by separating signing and generating proof_of_possession at the API level.
76///
77/// Rogue key attack however is not immediately applicable to non-aggregatable scheme
78/// when even if an honest signing oracle is tricked to sign a rogue proof_of_possession, it is not
79/// possible to aggregate it to generate a valid proof for a key the attack does not
80/// possess. Therefore we do not require non-aggregatable schemes to prevent proof_of_possession
81/// confirming signatures at API level
82pub trait NonAggregatable: Pair {
83 /// Default proof_of_possession statement.
84 fn proof_of_possession_statement(pk: &impl crate::Public) -> Vec<u8> {
85 /// The context which attached to pop message to attest its purpose.
86 const PROOF_OF_POSSESSION_CONTEXT_TAG: &[u8; 4] = b"POP_";
87 [PROOF_OF_POSSESSION_CONTEXT_TAG, pk.to_raw_vec().as_slice()].concat()
88 }
89}
90
91impl<T> ProofOfPossessionVerifier for T
92where
93 T: NonAggregatable,
94{
95 /// Default implementation for non-aggregatable signatures.
96 ///
97 /// While we enforce hash context separation at the library level in aggregatable schemes,
98 /// it remains as an advisory for the default implementation using signature API used for
99 /// non-aggregatable schemes
100 fn verify_proof_of_possession(
101 proof_of_possession: &Self::Signature,
102 allegedly_possessesd_pubkey: &Self::Public,
103 ) -> bool {
104 let proof_of_possession_statement =
105 Self::proof_of_possession_statement(allegedly_possessesd_pubkey);
106 Self::verify(
107 &proof_of_possession,
108 proof_of_possession_statement,
109 allegedly_possessesd_pubkey,
110 )
111 }
112}
113
114impl<T> ProofOfPossessionGenerator for T
115where
116 T: NonAggregatable,
117{
118 /// Default implementation for non-aggregatable signatures.
119 ///
120 /// While we enforce hash context separation at the library level in aggregatable schemes,
121 /// it remains as an advisory for the default implementation using signature API used for
122 /// non-aggregatable schemes
123 #[cfg(feature = "full_crypto")]
124 fn generate_proof_of_possession(&mut self) -> Self::Signature {
125 let proof_of_possession_statement = Self::proof_of_possession_statement(&self.public());
126 self.sign(proof_of_possession_statement.as_slice())
127 }
128}