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