Skip to main content

bssl_crypto/
ed25519.rs

1/* Copyright 2023 The BoringSSL Authors
2 *
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14 */
15
16//! Ed25519, a signature scheme.
17//!
18//! Ed25519 builds a signature scheme over a curve that is isogenous to
19//! curve25519. This module provides the "pure" signature scheme described in
20//! <https://datatracker.ietf.org/doc/html/rfc8032>.
21//!
22//! ```
23//! use bssl_crypto::ed25519;
24//!
25//! let key = ed25519::PrivateKey::generate();
26//! // Publish your public key.
27//! let public_key_bytes = *key.to_public().as_bytes();
28//!
29//! // Sign and publish some message.
30//! let signed_message = b"hello world";
31//! let mut sig = key.sign(signed_message);
32//!
33//! // Anyone with the public key can verify it.
34//! let public_key = ed25519::PublicKey::from_bytes(&public_key_bytes);
35//! assert!(public_key.verify(signed_message, &sig).is_ok());
36//! ```
37
38use crate::{FfiMutSlice, FfiSlice, InvalidSignatureError};
39
40/// The length in bytes of an Ed25519 public key.
41pub const PUBLIC_KEY_LEN: usize = bssl_sys::ED25519_PUBLIC_KEY_LEN as usize;
42
43/// The length in bytes of an Ed25519 seed which is the 32-byte private key
44/// representation defined in RFC 8032.
45pub const SEED_LEN: usize =
46    (bssl_sys::ED25519_PRIVATE_KEY_LEN - bssl_sys::ED25519_PUBLIC_KEY_LEN) as usize;
47
48/// The length in bytes of an Ed25519 signature.
49pub const SIGNATURE_LEN: usize = bssl_sys::ED25519_SIGNATURE_LEN as usize;
50
51// The length in bytes of an Ed25519 keypair. In BoringSSL, the private key is suffixed with the
52// public key, so the keypair length is the same as the private key length.
53const KEYPAIR_LEN: usize = bssl_sys::ED25519_PRIVATE_KEY_LEN as usize;
54
55/// An Ed25519 private key.
56pub struct PrivateKey([u8; KEYPAIR_LEN]);
57
58/// An Ed25519 public key used to verify a signature + message.
59pub struct PublicKey([u8; PUBLIC_KEY_LEN]);
60
61/// An Ed25519 signature created by signing a message with a private key.
62pub type Signature = [u8; SIGNATURE_LEN];
63
64impl PrivateKey {
65    /// Generates a new Ed25519 keypair.
66    pub fn generate() -> Self {
67        let mut public_key = [0u8; PUBLIC_KEY_LEN];
68        let mut private_key = [0u8; KEYPAIR_LEN];
69
70        // Safety:
71        // - Public key and private key are the correct length.
72        unsafe {
73            bssl_sys::ED25519_keypair(public_key.as_mut_ffi_ptr(), private_key.as_mut_ffi_ptr())
74        }
75
76        PrivateKey(private_key)
77    }
78
79    /// Returns the "seed" of this private key, as defined in RFC 8032.
80    pub fn to_seed(&self) -> [u8; SEED_LEN] {
81        // This code will never panic because a length 32 slice will always fit into a
82        // size 32 byte array. The private key is the first 32 bytes of the keypair.
83        #[allow(clippy::expect_used)]
84        self.0[..SEED_LEN]
85            .try_into()
86            .expect("A slice of length SEED_LEN will always fit into an array of length SEED_LEN")
87    }
88
89    /// Derives a key-pair from `seed`, which is the 32-byte private key representation defined
90    /// in RFC 8032.
91    pub fn from_seed(seed: &[u8; SEED_LEN]) -> Self {
92        let mut public_key = [0u8; PUBLIC_KEY_LEN];
93        let mut private_key = [0u8; KEYPAIR_LEN];
94
95        // Safety:
96        // - Public key, private key, and seed are the correct lengths.
97        unsafe {
98            bssl_sys::ED25519_keypair_from_seed(
99                public_key.as_mut_ffi_ptr(),
100                private_key.as_mut_ffi_ptr(),
101                seed.as_ffi_ptr(),
102            )
103        }
104        PrivateKey(private_key)
105    }
106
107    /// Signs the given message and returns the signature.
108    pub fn sign(&self, msg: &[u8]) -> Signature {
109        let mut sig_bytes = [0u8; SIGNATURE_LEN];
110
111        // Safety:
112        // - On allocation failure we panic.
113        // - Signature and private keys are always the correct length.
114        let result = unsafe {
115            bssl_sys::ED25519_sign(
116                sig_bytes.as_mut_ffi_ptr(),
117                msg.as_ffi_ptr(),
118                msg.len(),
119                self.0.as_ffi_ptr(),
120            )
121        };
122        assert_eq!(result, 1, "allocation failure in bssl_sys::ED25519_sign");
123
124        sig_bytes
125    }
126
127    /// Returns the [`PublicKey`] corresponding to this private key.
128    pub fn to_public(&self) -> PublicKey {
129        let keypair_bytes = &self.0;
130
131        // This code will never panic because a length 32 slice will always fit into a
132        // size 32 byte array. The public key is the last 32 bytes of the keypair.
133        #[allow(clippy::expect_used)]
134        PublicKey(
135            keypair_bytes[PUBLIC_KEY_LEN..]
136                .try_into()
137                .expect("The slice is always the correct size for a public key"),
138        )
139    }
140}
141
142impl PublicKey {
143    /// Builds the public key from an array of bytes.
144    pub fn from_bytes(bytes: &[u8; PUBLIC_KEY_LEN]) -> Self {
145        PublicKey(*bytes)
146    }
147
148    /// Returns the bytes of the public key.
149    pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_LEN] {
150        &self.0
151    }
152
153    /// Verifies that `signature` is a valid signature, by this key, of `msg`.
154    pub fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), InvalidSignatureError> {
155        let ret = unsafe {
156            // Safety: `self.0` is the correct length and other buffers are valid.
157            bssl_sys::ED25519_verify(
158                msg.as_ffi_ptr(),
159                msg.len(),
160                signature.as_ffi_ptr(),
161                self.0.as_ffi_ptr(),
162            )
163        };
164        if ret == 1 {
165            Ok(())
166        } else {
167            Err(InvalidSignatureError)
168        }
169    }
170}
171
172#[cfg(test)]
173mod test {
174    use super::*;
175    use crate::test_helpers;
176
177    #[test]
178    fn gen_roundtrip() {
179        let private_key = PrivateKey::generate();
180        assert_ne!([0u8; 64], private_key.0);
181        let seed = private_key.to_seed();
182        let new_private_key = PrivateKey::from_seed(&seed);
183        assert_eq!(private_key.0, new_private_key.0);
184    }
185
186    #[test]
187    fn empty_msg() {
188        // Test Case 1 from RFC test vectors: https://www.rfc-editor.org/rfc/rfc8032#section-7.1
189        let pk = test_helpers::decode_hex(
190            "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
191        );
192        let seed = test_helpers::decode_hex(
193            "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
194        );
195        let msg = [0u8; 0];
196        let sig_expected  = test_helpers::decode_hex("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b");
197        let kp = PrivateKey::from_seed(&seed);
198        let sig = kp.sign(&msg);
199        assert_eq!(sig_expected, sig);
200
201        let pub_key = PublicKey::from_bytes(&pk);
202        assert_eq!(pub_key.as_bytes(), kp.to_public().as_bytes());
203        assert!(pub_key.verify(&msg, &sig).is_ok());
204    }
205
206    #[test]
207    fn ed25519_sign_and_verify() {
208        // Test Case 15 from RFC test vectors: https://www.rfc-editor.org/rfc/rfc8032#section-7.1
209        let pk = test_helpers::decode_hex(
210            "cf3af898467a5b7a52d33d53bc037e2642a8da996903fc252217e9c033e2f291",
211        );
212        let sk = test_helpers::decode_hex(
213            "9acad959d216212d789a119252ebfe0c96512a23c73bd9f3b202292d6916a738",
214        );
215        let msg: [u8; 14] = test_helpers::decode_hex("55c7fa434f5ed8cdec2b7aeac173");
216        let sig_expected  = test_helpers::decode_hex("6ee3fe81e23c60eb2312b2006b3b25e6838e02106623f844c44edb8dafd66ab0671087fd195df5b8f58a1d6e52af42908053d55c7321010092748795ef94cf06");
217        let kp = PrivateKey::from_seed(&sk);
218
219        let sig = kp.sign(&msg);
220        assert_eq!(sig_expected, sig);
221
222        let pub_key = PublicKey::from_bytes(&pk);
223        assert_eq!(pub_key.as_bytes(), kp.to_public().as_bytes());
224        assert!(pub_key.verify(&msg, &sig).is_ok());
225    }
226}