schnorrkel/cert.rs
1// -*- mode: rust; -*-
2//
3// This file is part of schnorrkel.
4// Copyright (c) 2019 Web 3 Foundation
5// See LICENSE for licensing information.
6//
7// Authors:
8// - Jeffrey Burdges <jeff@web3.foundation>
9
10//! ### Adaptor signature-based implicit certificate scheme for Ristretto
11//!
12//! [Implicit certificates](https://en.wikipedia.org/wiki/Implicit_certificate)
13//! provide an extremely space efficient public key certificate scheme.
14//!
15//! As a rule, implicit certificates do not prove possession of the
16//! private key. We thus worry more about fear rogue key attack when
17//! using them, but all protocols here should provide strong defenses
18//! against then.
19//!
20//! We implement an implicit certificate scheme based on adaptor
21//! signatures as recommended by [1] and [2], which appears useful for
22//! "scriptless script" applications like [3] and [4].
23//!
24//! We should eventually place this into some more generally usable adaptor
25//! signature framework, but doing this safely this requires more work.
26//! We have not actually done security arguments for this code yet either,
27//! but we expect to find such results in the paymet channel literature [3].
28//! We might find arguments around Elliptic curve Qu-Vanstone (ECQV)
29//! helpful too [5,6].
30//!
31//! [1] "Schnorr Signatures for secp256k1"
32//! by Pieter Wuille, Jonas Nick, and Tim Ruffing
33//! https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki#Adaptor_Signatures
34//! [2] Ruben Somsen. "Schnorr signatures, adaptor signatures and statechains"
35//! https://bitcoinedge.org/transcript/telaviv2019/statechains
36//! [3] Giulio Malavolta and Pedro Moreno-Sanchez and Clara Schneidewind and Aniket Kate and Matteo Maffei
37//! "Anonymous Multi-Hop Locks for Blockchain Scalability and Interoperability"
38//! https://eprint.iacr.org/2018/472
39//! [4] Jonas Nick. "Scriptless Scripts [Using Adaptor Signatures]"
40//! https://github.com/ElementsProject/scriptless-scripts
41//! [5] "Standards for efficient cryptography, SEC 4: Elliptic Curve
42//! Qu-Vanstone Implicit Certificate Scheme (ECQV)".
43//! http://www.secg.org/sec4-1.0.pdf
44//! [6] Daniel R. L. Brown, Robert P. Gallant, and Scott A. Vanstone.
45//! "Provably Secure Implicit Certificate Schemes". Financial
46//! Cryptography 2001. Lecture Notes in Computer Science.
47//! Springer Berlin Heidelberg. 2339 (1): 156–165. doi:10.1007/3-540-46088-8_15.
48//! http://www.cacr.math.uwaterloo.ca/techreports/2000/corr2000-55.ps
49//
50// Found [4] via https://download.wpsoftware.net/bitcoin/wizardry/mw-slides/2018-05-18-l2/slides.pdf via [1]
51
52use curve25519_dalek::constants;
53use curve25519_dalek::ristretto::{CompressedRistretto};
54use curve25519_dalek::scalar::Scalar;
55
56use super::*;
57use crate::context::SigningTranscript;
58
59/// Adaptor Implicit Certificate Secret
60///
61/// Issuing an Adaptor implicit certificate requires producing
62/// this and securely sending it to the certificate holder.
63#[derive(Clone, Copy)] // Debug, Eq, PartialEq
64pub struct AdaptorCertSecret(pub [u8; 64]);
65/// TODO: Serde serialization/deserialization
66
67impl From<AdaptorCertSecret> for AdaptorCertPublic {
68 fn from(secret: AdaptorCertSecret) -> AdaptorCertPublic {
69 let mut public = AdaptorCertPublic([0u8; 32]);
70 public.0.copy_from_slice(&secret.0[0..32]);
71 public
72 }
73}
74
75/// Adaptor Implicit Certificate Public Key Reconstruction Data
76///
77/// Identifying the public key of, and implicitly verifying, an Adaptor
78/// implicit certificate requires this data, which is produced
79/// when the certificate holder accepts the implicit certificate.
80#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
81pub struct AdaptorCertPublic(pub [u8; 32]);
82/// TODO: Serde serialization/deserialization
83
84impl AdaptorCertPublic {
85 fn derive_e<T: SigningTranscript>(&self, mut t: T) -> Scalar {
86 t.challenge_scalar(b"adaptor-e")
87 }
88}
89
90impl Keypair {
91 /// Issue an Adaptor implicit certificate
92 ///
93 /// Aside from the issuing `Keypair` supplied as `self`, you provide both
94 /// (1) a `SigningTranscript` called `t` that incorporates both the context
95 /// and the certificate requester's identity, and
96 /// (2) the `seed_public_key` supplied by the certificate recipient
97 /// in their certificate request.
98 /// We return an `AdaptorCertSecret` which the issuer sent to the
99 /// certificate requester, ans from which the certificate requester
100 /// derives their certified key pair.
101 pub fn issue_adaptor_cert<T>(&self, mut t: T, seed_public_key: &PublicKey) -> AdaptorCertSecret
102 where
103 T: SigningTranscript,
104 {
105 t.proto_name(b"Adaptor");
106 t.commit_point(b"issuer-pk", self.public.as_compressed());
107
108 // We cannot commit the `seed_public_key` to the transcript
109 // because the whole point is to keep the transcript minimal.
110 // Instead we consume it as witness datathat influences only k.
111 let k = t.witness_scalar(
112 b"issuing",
113 &[&self.secret.nonce, seed_public_key.as_compressed().as_bytes()],
114 );
115
116 // Compute the public key reconstruction data
117 let gamma = seed_public_key.as_point() + &k * constants::RISTRETTO_BASEPOINT_TABLE;
118 let gamma = gamma.compress();
119 t.commit_point(b"gamma", &gamma);
120 let cert_public = AdaptorCertPublic(gamma.0);
121
122 // Compute the secret key reconstruction data
123 let s = k + cert_public.derive_e(t) * self.secret.key;
124
125 let mut cert_secret = AdaptorCertSecret([0u8; 64]);
126 cert_secret.0[0..32].copy_from_slice(&cert_public.0[..]);
127 cert_secret.0[32..64].copy_from_slice(s.as_bytes());
128 cert_secret
129 }
130}
131
132impl PublicKey {
133 /// Accept an Adaptor implicit certificate
134 ///
135 /// We request an Adaptor implicit certificate by first creating an
136 /// ephemeral `Keypair` and sending the public portion to the issuer
137 /// as `seed_public_key`. An issuer issues the certificate by replying
138 /// with the `AdaptorCertSecret` created by `issue_adaptor_cert`.
139 ///
140 /// Aside from the issuer `PublicKey` supplied as `self`, you provide
141 /// (1) a `SigningTranscript` called `t` that incorporates both the context
142 /// and the certificate requester's identity,
143 /// (2) the `seed_secret_key` corresponding to the `seed_public_key`
144 /// they sent to the issuer by the certificate recipient in their
145 /// certificate request, and
146 /// (3) the `AdaptorCertSecret` send by the issuer to the certificate
147 /// requester.
148 /// We return both your certificate's new `SecretKey` as well as
149 /// an `AdaptorCertPublic` from which third parties may derive
150 /// corresponding public key from `h` and the issuer's public key.
151 pub fn accept_adaptor_cert<T>(
152 &self,
153 mut t: T,
154 seed_secret_key: &SecretKey,
155 cert_secret: AdaptorCertSecret,
156 ) -> SignatureResult<(AdaptorCertPublic, SecretKey)>
157 where
158 T: SigningTranscript,
159 {
160 t.proto_name(b"Adaptor");
161 t.commit_point(b"issuer-pk", self.as_compressed());
162
163 // Again we cannot commit much to the transcript, but we again
164 // treat anything relevant as a witness when defining the
165 let mut nonce = [0u8; 32];
166 t.witness_bytes(b"accepting", &mut nonce, &[&cert_secret.0[..], &seed_secret_key.nonce]);
167
168 let mut s = [0u8; 32];
169 s.copy_from_slice(&cert_secret.0[32..64]);
170 let s = crate::scalar_from_canonical_bytes(s).ok_or(SignatureError::ScalarFormatError)?;
171 let cert_public: AdaptorCertPublic = cert_secret.into();
172 let gamma = CompressedRistretto(cert_public.0);
173 t.commit_point(b"gamma", &gamma);
174
175 let key = s + seed_secret_key.key;
176 Ok((cert_public, SecretKey { key, nonce }))
177 }
178}
179
180impl Keypair {
181 /// Issue an Adaptor Implicit Certificate for yourself
182 ///
183 /// We can issue an implicit certificate to ourselves if we merely
184 /// want to certify an associated public key. We should prefer
185 /// this option over "hierarchical deterministic" key derivation
186 /// because compromising the resulting secret key does not
187 /// compromise the issuer's secret key.
188 ///
189 /// In this case, we avoid the entire interactive protocol described
190 /// by `issue_adaptor_cert` and `accept_adaptor_cert` by hiding it an all
191 /// management of the ephemeral `Keypair` inside this function.
192 ///
193 /// Aside from the issuing secret key supplied as `self`, you provide
194 /// only a digest `h` that incorporates any context and metadata
195 /// pertaining to the issued key.
196 pub fn issue_self_adaptor_cert<T>(&self, t: T) -> (AdaptorCertPublic, SecretKey)
197 where
198 T: SigningTranscript + Clone,
199 {
200 let mut bytes = [0u8; 96];
201 t.witness_bytes(
202 b"issue_self_adaptor_cert",
203 &mut bytes,
204 &[&self.secret.nonce, &self.secret.to_bytes() as &[u8]],
205 );
206
207 let mut nonce = [0u8; 32];
208 nonce.copy_from_slice(&bytes[64..96]);
209
210 let mut key = [0u8; 64];
211 key.copy_from_slice(&bytes[0..64]);
212 let key = Scalar::from_bytes_mod_order_wide(&key);
213
214 let seed = SecretKey { key, nonce }.to_keypair();
215 let cert_secret = self.issue_adaptor_cert(t.clone(), &seed.public);
216 self.public
217 .accept_adaptor_cert(t, &seed.secret, cert_secret)
218 .expect("Cert issued above and known to produce signature errors; qed")
219 }
220}
221
222impl PublicKey {
223 /// Extract the certified pulic key from an adaptor certificate
224 ///
225 /// We've no confirmation that this public key was certified
226 /// until we use it in some authenticated setting, like an AEAD
227 /// or another signature.
228 pub fn open_adaptor_cert<T>(
229 &self,
230 mut t: T,
231 cert_public: &AdaptorCertPublic,
232 ) -> SignatureResult<PublicKey>
233 where
234 T: SigningTranscript,
235 {
236 t.proto_name(b"Adaptor");
237 t.commit_point(b"issuer-pk", self.as_compressed());
238
239 let gamma = CompressedRistretto(cert_public.0);
240 t.commit_point(b"gamma", &gamma);
241 let gamma = gamma.decompress().ok_or(SignatureError::PointDecompressionError)?;
242
243 let point = cert_public.derive_e(t) * self.as_point() + gamma;
244 Ok(PublicKey::from_point(point))
245 }
246}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251
252 #[cfg(feature = "getrandom")]
253 #[test]
254 fn adaptor_cert_public_vs_private_paths() {
255 let t = signing_context(b"").bytes(b"MrMeow!");
256
257 let mut csprng = rand_core::OsRng;
258 let issuer = Keypair::generate_with(&mut csprng);
259
260 let (cert_public, secret_key) = issuer.issue_self_adaptor_cert(t.clone());
261 let public_key = issuer.public.open_adaptor_cert(t, &cert_public).unwrap();
262 assert_eq!(secret_key.to_public(), public_key);
263 }
264}