playready/crypto/
ecc_p256.rs

1use elastic_elgamal::{Ciphertext, group::Generic};
2use p256::{
3    AffinePoint, EncodedPoint, NistP256, ProjectivePoint,
4    ecdsa::{
5        Signature, SigningKey, VerifyingKey,
6        signature::{Signer, Verifier},
7    },
8    elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint},
9};
10
11use std::{ptr::copy_nonoverlapping, sync::OnceLock};
12
13pub type PublicKey = elastic_elgamal::PublicKey<Generic<NistP256>>;
14pub type SecretKey = elastic_elgamal::SecretKey<Generic<NistP256>>;
15/// Keypair used for encryption key
16pub type Keypair = elastic_elgamal::Keypair<Generic<NistP256>>;
17
18pub const SCALAR_SIZE: usize = 32;
19pub const SIGNATURE_SIZE: usize = 64;
20
21pub trait ToUntaggedBytes {
22    fn to_untagged_bytes(&self) -> Box<[u8]>;
23}
24
25impl<T: ToEncodedPoint<NistP256>> ToUntaggedBytes for T {
26    fn to_untagged_bytes(&self) -> Box<[u8]> {
27        self.to_encoded_point(false)
28            .as_bytes()
29            .iter()
30            .copied()
31            .skip(1) // skip tag
32            .collect()
33    }
34}
35
36pub trait FromBytes
37where
38    Self: Sized,
39{
40    type Error;
41
42    fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error>;
43}
44
45impl FromBytes for Keypair {
46    type Error = crate::Error;
47
48    fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> {
49        Ok(Keypair::from(
50            SecretKey::from_bytes(bytes).ok_or(crate::Error::P256DecodeError)?,
51        ))
52    }
53}
54
55impl FromBytes for VerifyingKey {
56    type Error = crate::Error;
57
58    fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> {
59        if bytes.len() != 64 {
60            return Err(crate::Error::P256DecodeError);
61        }
62
63        let point = EncodedPoint::from_untagged_bytes(bytes.into());
64        let point = AffinePoint::from_encoded_point(&point)
65            .into_option()
66            .ok_or(crate::Error::P256DecodeError)?;
67
68        VerifyingKey::from_affine(point).or(Err(crate::Error::P256DecodeError))
69    }
70}
71
72pub fn wmrm_public_key() -> &'static PublicKey {
73    static CELL: OnceLock<PublicKey> = OnceLock::new();
74
75    CELL.get_or_init(|| {
76        const WMRM_KEY: [u8; 33] = [
77            0x02, 0xc8, 0xb6, 0xaf, 0x16, 0xee, 0x94, 0x1a, 0xad, 0xaa, 0x53, 0x89, 0xb4, 0xaf,
78            0x2c, 0x10, 0xe3, 0x56, 0xbe, 0x42, 0xaf, 0x17, 0x5e, 0xf3, 0xfa, 0xce, 0x93, 0x25,
79            0x4e, 0x7b, 0x0b, 0x3d, 0x9b,
80        ];
81
82        PublicKey::from_bytes(&WMRM_KEY).unwrap()
83    })
84}
85
86pub fn encrypt(public_key: &PublicKey, plaintext: ProjectivePoint) -> Vec<u8> {
87    let mut rng = rand::thread_rng();
88    let ciphertext = public_key.encrypt_element(plaintext, &mut rng);
89
90    let point1 = ciphertext.random_element().to_untagged_bytes();
91    let point2 = ciphertext.blinded_element().to_untagged_bytes();
92
93    [point1, point2].concat()
94}
95
96pub fn decrypt(private_key: &SecretKey, ciphertext: &[u8]) -> Result<Vec<u8>, crate::Error> {
97    let random_element = EncodedPoint::from_untagged_bytes(
98        ciphertext
99            .get(..64)
100            .ok_or(crate::Error::SliceOutOfBoundsError(
101                "ciphertext",
102                ciphertext.len(),
103            ))?
104            .into(),
105    );
106    let random_element = AffinePoint::from_encoded_point(&random_element)
107        .into_option()
108        .ok_or(crate::Error::P256DecodeError)?;
109
110    let blinded_element = EncodedPoint::from_untagged_bytes(
111        ciphertext
112            .get(64..128)
113            .ok_or(crate::Error::SliceOutOfBoundsError(
114                "ciphertext",
115                ciphertext.len(),
116            ))?
117            .into(),
118    );
119    let blinded_element = AffinePoint::from_encoded_point(&blinded_element)
120        .into_option()
121        .ok_or(crate::Error::P256DecodeError)?;
122
123    let encrypted: Ciphertext<Generic<NistP256>> = Ciphertext::zero();
124
125    // TODO: Remove unsafe code once https://github.com/slowli/elastic-elgamal/pull/157 is merged
126    unsafe {
127        copy_nonoverlapping(
128            &random_element.into(),
129            encrypted.random_element() as *const ProjectivePoint as *mut ProjectivePoint,
130            1,
131        );
132        copy_nonoverlapping(
133            &blinded_element.into(),
134            encrypted.blinded_element() as *const ProjectivePoint as *mut ProjectivePoint,
135            1,
136        );
137    };
138
139    Ok(private_key
140        .decrypt_to_element(encrypted)
141        .to_untagged_bytes()
142        .to_vec())
143}
144
145pub fn verify(
146    verifying_key: &VerifyingKey,
147    msg: &[u8],
148    signature: &[u8],
149) -> Result<(), p256::ecdsa::Error> {
150    let signature = Signature::from_slice(signature)?;
151    verifying_key.verify(msg, &signature)
152}
153
154pub fn sign(signing_key: &SigningKey, msg: &[u8]) -> Vec<u8> {
155    let signature: Signature = signing_key.sign(msg);
156    signature.to_bytes().to_vec()
157}
158
159#[cfg(test)]
160mod test {
161    use crate::{
162        Keypair,
163        crypto::ecc_p256::{FromBytes, SecretKey, ToUntaggedBytes, decrypt},
164    };
165    use p256::ecdsa::VerifyingKey;
166
167    #[test]
168    fn create_invalid_verifying_key() {
169        assert!(matches!(
170            VerifyingKey::from_bytes(&[1]),
171            Err(crate::Error::P256DecodeError)
172        ));
173    }
174
175    #[test]
176    fn try_to_decrypt_too_short_ciphertext() {
177        let sk = SecretKey::from_bytes(&[1u8; 32]).unwrap();
178
179        assert!(matches!(
180            decrypt(&sk, &[1]),
181            Err(crate::Error::SliceOutOfBoundsError(_, _))
182        ));
183    }
184
185    #[test]
186    fn decrypt_long_ciphertext() {
187        let sk = SecretKey::from_bytes(&[1u8; 32]).unwrap();
188        let kp = Keypair::from(sk.clone());
189
190        let mut point = kp.public().as_element().to_untagged_bytes().to_vec();
191        point.append(&mut point.clone());
192
193        let expected = vec![
194            172, 161, 83, 0, 40, 73, 212, 190, 29, 204, 157, 78, 217, 152, 216, 235, 155, 39, 228,
195            115, 176, 146, 44, 117, 228, 161, 45, 97, 177, 206, 222, 39, 112, 171, 121, 124, 145,
196            154, 37, 179, 108, 74, 77, 247, 132, 44, 68, 197, 162, 252, 131, 221, 46, 117, 145, 82,
197            33, 147, 21, 83, 103, 237, 70, 93,
198        ];
199
200        assert_eq!(decrypt(&sk, &point).unwrap(), expected);
201        point.append(&mut vec![1u8; 32]);
202        assert_eq!(decrypt(&sk, &point).unwrap(), expected);
203    }
204}