Skip to main content

bssl_crypto/
ed25519.rs

1// Copyright 2023 The BoringSSL Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Ed25519, a signature scheme.
16//!
17//! Ed25519 builds a signature scheme over a curve that is isogenous to
18//! curve25519. This module provides the "pure" signature scheme described in
19//! <https://datatracker.ietf.org/doc/html/rfc8032>.
20//!
21//! ```
22//! use bssl_crypto::ed25519;
23//!
24//! let key = ed25519::PrivateKey::generate();
25//! // Publish your public key.
26//! let public_key_bytes = *key.to_public().as_bytes();
27//!
28//! // Sign and publish some message.
29//! let signed_message = b"hello world";
30//! let mut sig = key.sign(signed_message);
31//!
32//! // Anyone with the public key can verify it.
33//! let public_key = ed25519::PublicKey::from_bytes(&public_key_bytes);
34//! assert!(public_key.verify(signed_message, &sig).is_ok());
35//! ```
36
37use crate::{
38    cbb_to_buffer, scoped, with_output_array, Buffer, FfiMutSlice, FfiSlice, InvalidSignatureError,
39};
40
41/// The length in bytes of an Ed25519 public key.
42pub const PUBLIC_KEY_LEN: usize = bssl_sys::ED25519_PUBLIC_KEY_LEN as usize;
43
44/// The length in bytes of an Ed25519 seed which is the 32-byte private key
45/// representation defined in RFC 8032.
46pub const SEED_LEN: usize =
47    (bssl_sys::ED25519_PRIVATE_KEY_LEN - bssl_sys::ED25519_PUBLIC_KEY_LEN) as usize;
48
49/// The length in bytes of an Ed25519 signature.
50pub const SIGNATURE_LEN: usize = bssl_sys::ED25519_SIGNATURE_LEN as usize;
51
52// The length in bytes of an Ed25519 keypair. In BoringSSL, the private key is suffixed with the
53// public key, so the keypair length is the same as the private key length.
54const KEYPAIR_LEN: usize = bssl_sys::ED25519_PRIVATE_KEY_LEN as usize;
55
56/// An Ed25519 private key.
57#[derive(Clone)]
58pub struct PrivateKey([u8; KEYPAIR_LEN]);
59
60/// An Ed25519 public key used to verify a signature + message.
61pub struct PublicKey([u8; PUBLIC_KEY_LEN]);
62
63/// An Ed25519 signature created by signing a message with a private key.
64pub type Signature = [u8; SIGNATURE_LEN];
65
66impl PrivateKey {
67    /// Generates a new Ed25519 keypair.
68    pub fn generate() -> Self {
69        let mut public_key = [0u8; PUBLIC_KEY_LEN];
70        let mut private_key = [0u8; KEYPAIR_LEN];
71
72        // Safety:
73        // - Public key and private key are the correct length.
74        unsafe {
75            bssl_sys::ED25519_keypair(public_key.as_mut_ffi_ptr(), private_key.as_mut_ffi_ptr())
76        }
77
78        PrivateKey(private_key)
79    }
80
81    /// Returns the "seed" of this private key, as defined in RFC 8032.
82    pub fn to_seed(&self) -> [u8; SEED_LEN] {
83        // This code will never panic because a length 32 slice will always fit into a
84        // size 32 byte array. The private key is the first 32 bytes of the keypair.
85        #[allow(clippy::expect_used)]
86        self.0[..SEED_LEN]
87            .try_into()
88            .expect("A slice of length SEED_LEN will always fit into an array of length SEED_LEN")
89    }
90
91    /// Derives a key-pair from `seed`, which is the 32-byte private key representation defined
92    /// in RFC 8032.
93    pub fn from_seed(seed: &[u8; SEED_LEN]) -> Self {
94        let mut public_key = [0u8; PUBLIC_KEY_LEN];
95        let mut private_key = [0u8; KEYPAIR_LEN];
96
97        // Safety:
98        // - Public key, private key, and seed are the correct lengths.
99        unsafe {
100            bssl_sys::ED25519_keypair_from_seed(
101                public_key.as_mut_ffi_ptr(),
102                private_key.as_mut_ffi_ptr(),
103                seed.as_ffi_ptr(),
104            )
105        }
106        PrivateKey(private_key)
107    }
108
109    /// Signs the given message and returns the signature.
110    pub fn sign(&self, msg: &[u8]) -> Signature {
111        let mut sig_bytes = [0u8; SIGNATURE_LEN];
112
113        // Safety:
114        // - On allocation failure we panic.
115        // - Signature and private keys are always the correct length.
116        let result = unsafe {
117            bssl_sys::ED25519_sign(
118                sig_bytes.as_mut_ffi_ptr(),
119                msg.as_ffi_ptr(),
120                msg.len(),
121                self.0.as_ffi_ptr(),
122            )
123        };
124        assert_eq!(result, 1, "allocation failure in bssl_sys::ED25519_sign");
125
126        sig_bytes
127    }
128
129    /// Returns the [`PublicKey`] corresponding to this private key.
130    pub fn to_public(&self) -> PublicKey {
131        let keypair_bytes = &self.0;
132
133        // This code will never panic because a length 32 slice will always fit into a
134        // size 32 byte array. The public key is the last 32 bytes of the keypair.
135        #[allow(clippy::expect_used)]
136        PublicKey(
137            keypair_bytes[PUBLIC_KEY_LEN..]
138                .try_into()
139                .expect("The slice is always the correct size for a public key"),
140        )
141    }
142
143    // Safety: caller must make sure that the key type is ED25519
144    pub(crate) unsafe fn from_evp_pkey(mut pkey: scoped::EvpPkey) -> Self {
145        let mut seed = [0; SEED_LEN];
146        let len = &mut { SEED_LEN };
147        // Safety: pkey is now owned and len is set
148        let ret = unsafe {
149            bssl_sys::EVP_PKEY_get_raw_private_key(
150                pkey.as_ffi_ptr(),
151                seed.as_mut_ptr(),
152                len as *mut _,
153            )
154        };
155        // Sanity check, in case the seed is not as long as expected.
156        assert_eq!(ret, 1);
157        assert_eq!(*len, SEED_LEN);
158        Self::from_seed(&seed)
159    }
160}
161
162impl PublicKey {
163    /// Builds the public key from an array of bytes.
164    pub fn from_bytes(bytes: &[u8; PUBLIC_KEY_LEN]) -> Self {
165        PublicKey(*bytes)
166    }
167
168    /// Returns the bytes of the public key.
169    pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_LEN] {
170        &self.0
171    }
172
173    /// Parse a public key in SubjectPublicKeyInfo format.
174    pub fn from_der_subject_public_key_info(spki: &[u8]) -> Option<Self> {
175        // Safety: `EVP_pkey_ed25519` is always safe to call.
176        let alg = unsafe { bssl_sys::EVP_pkey_ed25519() };
177        let mut pkey =
178            scoped::EvpPkey::from_der_subject_public_key_info(spki, core::slice::from_ref(&alg))?;
179        let raw_pkey: [u8; PUBLIC_KEY_LEN] = unsafe {
180            with_output_array(|out, mut out_len| {
181                // We only passed one key type, so `pkey` must be an Ed25519
182                // key. The raw public key then must be available, and must be
183                // `PUBLIC_KEY_LEN` bytes.
184                assert_eq!(
185                    1,
186                    bssl_sys::EVP_PKEY_get_raw_public_key(pkey.as_ffi_ptr(), out, &mut out_len)
187                );
188                assert_eq!(out_len, PUBLIC_KEY_LEN);
189            })
190        };
191        Some(PublicKey(raw_pkey))
192    }
193
194    /// Serialize this key in SubjectPublicKeyInfo format.
195    pub fn to_der_subject_public_key_info(&self) -> Buffer {
196        // Safety: this only copies from the `self.0` buffer.
197        let mut pkey = scoped::EvpPkey::from_ptr(unsafe {
198            bssl_sys::EVP_PKEY_from_raw_public_key(
199                bssl_sys::EVP_pkey_ed25519(),
200                self.0.as_ffi_ptr(),
201                PUBLIC_KEY_LEN,
202            )
203        });
204        // Safety: we are only testing pointer nullness, we do not mutate the data
205        assert!(!pkey.as_ffi_ptr().is_null());
206
207        cbb_to_buffer(PUBLIC_KEY_LEN + 32, |cbb| unsafe {
208            // The arguments are valid so this will only fail if out of memory,
209            // which this crate doesn't handle.
210            assert_eq!(1, bssl_sys::EVP_marshal_public_key(cbb, pkey.as_ffi_ptr()));
211        })
212    }
213
214    /// Verifies that `signature` is a valid signature, by this key, of `msg`.
215    pub fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), InvalidSignatureError> {
216        let ret = unsafe {
217            // Safety: `self.0` is the correct length and other buffers are valid.
218            bssl_sys::ED25519_verify(
219                msg.as_ffi_ptr(),
220                msg.len(),
221                signature.as_ffi_ptr(),
222                self.0.as_ffi_ptr(),
223            )
224        };
225        if ret == 1 {
226            Ok(())
227        } else {
228            Err(InvalidSignatureError)
229        }
230    }
231}
232
233#[cfg(test)]
234mod test {
235    use super::*;
236    use crate::test_helpers;
237
238    #[test]
239    fn gen_roundtrip() {
240        let private_key = PrivateKey::generate();
241        assert_ne!([0u8; 64], private_key.0);
242        let seed = private_key.to_seed();
243        let new_private_key = PrivateKey::from_seed(&seed);
244        assert_eq!(private_key.0, new_private_key.0);
245    }
246
247    #[test]
248    fn der_subject_public_key_info() {
249        let priv_key = PrivateKey::generate();
250        let msg = [0u8; 0];
251        let sig = priv_key.sign(&msg);
252
253        let pub_key = priv_key.to_public();
254        assert!(pub_key.verify(&msg, &sig).is_ok());
255
256        let pub_key_der = pub_key.to_der_subject_public_key_info();
257        let pub_key_from_der =
258            PublicKey::from_der_subject_public_key_info(pub_key_der.as_ref()).unwrap();
259        assert_eq!(pub_key.as_bytes(), pub_key_from_der.as_bytes());
260        assert!(pub_key_from_der.verify(&msg, &sig).is_ok());
261
262        assert!(PublicKey::from_der_subject_public_key_info(
263            &pub_key_from_der.as_bytes()[0..PUBLIC_KEY_LEN / 2]
264        )
265        .is_none());
266
267        assert!(PublicKey::from_der_subject_public_key_info(b"").is_none());
268    }
269
270    #[test]
271    fn der_subject_public_key_info_wrong_type() {
272        // This is an X25519 key, not an Ed25519 key.
273        let spki = test_helpers::decode_hex_into_vec("302a300506032b656e032100e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c");
274        // `from_der_subject_public_key_info` should reject it.
275        assert!(PublicKey::from_der_subject_public_key_info(&spki).is_none());
276    }
277
278    #[test]
279    fn empty_msg() {
280        // Test Case 1 from RFC test vectors: https://www.rfc-editor.org/rfc/rfc8032#section-7.1
281        let pk = test_helpers::decode_hex(
282            "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
283        );
284        let seed = test_helpers::decode_hex(
285            "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
286        );
287        let msg = [0u8; 0];
288        let sig_expected  = test_helpers::decode_hex("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b");
289        let kp = PrivateKey::from_seed(&seed);
290        let sig = kp.sign(&msg);
291        assert_eq!(sig_expected, sig);
292
293        let pub_key = PublicKey::from_bytes(&pk);
294        assert_eq!(pub_key.as_bytes(), kp.to_public().as_bytes());
295        assert!(pub_key.verify(&msg, &sig).is_ok());
296    }
297
298    #[test]
299    fn ed25519_sign_and_verify() {
300        // Test Case 15 from RFC test vectors: https://www.rfc-editor.org/rfc/rfc8032#section-7.1
301        let pk = test_helpers::decode_hex(
302            "cf3af898467a5b7a52d33d53bc037e2642a8da996903fc252217e9c033e2f291",
303        );
304        let sk = test_helpers::decode_hex(
305            "9acad959d216212d789a119252ebfe0c96512a23c73bd9f3b202292d6916a738",
306        );
307        let msg: [u8; 14] = test_helpers::decode_hex("55c7fa434f5ed8cdec2b7aeac173");
308        let sig_expected  = test_helpers::decode_hex("6ee3fe81e23c60eb2312b2006b3b25e6838e02106623f844c44edb8dafd66ab0671087fd195df5b8f58a1d6e52af42908053d55c7321010092748795ef94cf06");
309        let kp = PrivateKey::from_seed(&sk);
310
311        let sig = kp.sign(&msg);
312        assert_eq!(sig_expected, sig);
313
314        let pub_key = PublicKey::from_bytes(&pk);
315        assert_eq!(pub_key.as_bytes(), kp.to_public().as_bytes());
316        assert!(pub_key.verify(&msg, &sig).is_ok());
317    }
318}