sp_core/proof_of_possession.rs
1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd. SPDX-License-Identifier: Apache-2.0
4
5// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
6// in compliance with the License. You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software distributed under the License
11// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12// or implied. See the License for the specific language governing permissions and limitations
13// under the License.
14
15//! Utilities for proving possession of a particular public key
16
17use crate::crypto::{CryptoType, Pair, Signature};
18use sp_std::vec::Vec;
19
20/// Pair which is able to generate proof of possession.
21///
22/// This is implemented in different trait to provide default behavior.
23pub trait ProofOfPossessionGenerator: Pair
24where
25 Self::Public: CryptoType,
26 Self::ProofOfPossession: Signature,
27{
28 /// Generate proof of possession.
29 ///
30 /// This is usually done by signing the owner's identifier, this is prevent front runner to
31 /// claim ownership of public keys of other entities.
32 ///
33 /// However, for aggregatable signature the proof of possession generator is supposed to
34 /// produce a "signature" with unique hash context that should never be used in other
35 /// signatures. While this proves that the secret key is known to the prover, it prevents
36 /// malicious actors to trick an honest party to sign an unpossessed public key resulting in
37 /// a rogue key attack (See: Section 4.3 of - Ristenpart, T., & Yilek, S. (2007). The power
38 /// of proofs-of-possession: Securing multiparty signatures against rogue-key attacks. In ,
39 /// Annual {{International Conference}} on the {{Theory}} and {{Applications}} of
40 /// {{Cryptographic Techniques} (pp. 228–245). : Springer).
41 ///
42 /// As such, for aggregatable signatures, proof of possession consists of two signatures one
43 /// regular signature signing the owner identity and the second one with unique context
44 /// which signs the correspoding public key (and nothing else).
45 #[cfg(feature = "full_crypto")]
46 fn generate_proof_of_possession(&mut self, owner: &[u8]) -> Self::ProofOfPossession;
47}
48
49/// Pair which is able to verify proof of possession.
50///
51/// While you don't need a keypair to verify a proof of possession (you only need a public key) we
52/// constrain on Pair to use the Public and Signature types associated to Pair. This is implemented
53/// in different trait (than Public Key) to provide default behavior.
54pub trait ProofOfPossessionVerifier: Pair
55where
56 Self::Public: CryptoType,
57 Self::ProofOfPossession: Signature,
58{
59 /// Verify proof of possession.
60 ///
61 /// The proof of possession verifier is supposed to to verify a signature with unique hash
62 /// context that is produced solely for this reason. This proves that that the secret key is
63 /// known to the prover.
64 fn verify_proof_of_possession(
65 owner: &[u8],
66 proof_of_possession: &Self::ProofOfPossession,
67 allegedly_possessesd_pubkey: &Self::Public,
68 ) -> bool;
69}
70
71/// Simply returns the owner prefixed with proof of possession context.
72pub fn statement_of_ownership(owner: &[u8]) -> Vec<u8> {
73 /// The context which attached to pop message to attest its purpose.
74 const PROOF_OF_POSSESSION_CONTEXT_TAG: &[u8; 4] = b"POP_";
75 [PROOF_OF_POSSESSION_CONTEXT_TAG, owner].concat()
76}
77
78/// Marker trait to identify whether the scheme is not aggregatable.
79///
80/// Aggregatable schemes may change/optimize implementation parts such as Proof Of Possession or
81/// other specifics.
82///
83/// This is specifically because implementation of proof of possession for aggregatable schemes is
84/// security critical.
85///
86/// We would like to prevent aggregatable scheme from unknowingly generating signatures which
87/// aggregate to false albeit valid proof of possession aka rogue key attack. We ensure that by
88/// separating signing and generating proof_of_possession at the API level.
89///
90/// Rogue key attack however is not immediately applicable to non-aggregatable scheme when even if
91/// an honest signing oracle is tricked to sign a rogue proof_of_possession, it is not possible to
92/// aggregate it to generate a valid proof for a key the attack does not possess. Therefore we do
93/// not require non-aggregatable schemes to prevent proof_of_possession confirming signatures at API
94/// level
95pub trait NonAggregatable: Pair {
96 /// Default proof_of_possession statement.
97 fn proof_of_possession_statement(owner: &[u8]) -> Vec<u8> {
98 statement_of_ownership(owner)
99 }
100}
101
102impl<T> ProofOfPossessionVerifier for T
103where
104 T: NonAggregatable<ProofOfPossession = Self::Signature>,
105{
106 /// Default implementation for non-aggregatable signatures.
107 ///
108 /// While we enforce hash context separation at the library level in aggregatable schemes,
109 /// it remains as an advisory for the default implementation using signature API used for
110 /// non-aggregatable schemes
111 fn verify_proof_of_possession(
112 owner: &[u8],
113 proof_of_possession: &Self::ProofOfPossession,
114 allegedly_possessesd_pubkey: &Self::Public,
115 ) -> bool {
116 let proof_of_possession_statement = statement_of_ownership(owner);
117 Self::verify(
118 &proof_of_possession,
119 proof_of_possession_statement,
120 allegedly_possessesd_pubkey,
121 )
122 }
123}
124
125impl<T> ProofOfPossessionGenerator for T
126where
127 T: NonAggregatable<ProofOfPossession = Self::Signature>,
128{
129 /// Default implementation for non-aggregatable signatures.
130 ///
131 /// While we enforce hash context separation at the library level in aggregatable schemes,
132 /// it remains as an advisory for the default implementation using signature API used for
133 /// non-aggregatable schemes
134 #[cfg(feature = "full_crypto")]
135 fn generate_proof_of_possession(&mut self, owner: &[u8]) -> Self::ProofOfPossession {
136 let proof_of_possession_statement = statement_of_ownership(owner);
137 self.sign(proof_of_possession_statement.as_slice())
138 }
139}