Skip to main content

playready/crypto/
ecc_p256.rs

1use elastic_elgamal::{Ciphertext, group::Generic};
2use p256::{
3    AffinePoint, NistP256, ProjectivePoint, Sec1Point,
4    ecdsa::{
5        Signature, SigningKey, VerifyingKey,
6        signature::{Signer, Verifier},
7    },
8    elliptic_curve::sec1::{FromSec1Point, ToSec1Point},
9};
10
11use std::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: ToSec1Point<NistP256>> ToUntaggedBytes for T {
26    fn to_untagged_bytes(&self) -> Box<[u8]> {
27        self.to_sec1_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        let point = Sec1Point::from_untagged_bytes(
60            bytes.try_into().or(Err(crate::Error::P256DecodeError))?,
61        );
62        let point = AffinePoint::from_sec1_point(&point)
63            .into_option()
64            .ok_or(crate::Error::P256DecodeError)?;
65
66        VerifyingKey::from_affine(point).or(Err(crate::Error::P256DecodeError))
67    }
68}
69
70pub fn wmrm_public_key() -> &'static PublicKey {
71    static CELL: OnceLock<PublicKey> = OnceLock::new();
72
73    CELL.get_or_init(|| {
74        const WMRM_KEY: [u8; 33] = [
75            0x02, 0xc8, 0xb6, 0xaf, 0x16, 0xee, 0x94, 0x1a, 0xad, 0xaa, 0x53, 0x89, 0xb4, 0xaf,
76            0x2c, 0x10, 0xe3, 0x56, 0xbe, 0x42, 0xaf, 0x17, 0x5e, 0xf3, 0xfa, 0xce, 0x93, 0x25,
77            0x4e, 0x7b, 0x0b, 0x3d, 0x9b,
78        ];
79
80        PublicKey::from_bytes(&WMRM_KEY).unwrap()
81    })
82}
83
84pub fn encrypt(public_key: &PublicKey, plaintext: ProjectivePoint) -> Vec<u8> {
85    let mut rng = rand::rng();
86    let ciphertext = public_key.encrypt_element(plaintext, &mut rng);
87
88    let point1 = ciphertext.random_element().to_untagged_bytes();
89    let point2 = ciphertext.blinded_element().to_untagged_bytes();
90
91    [point1, point2].concat()
92}
93
94pub fn decrypt(private_key: &SecretKey, ciphertext: &[u8]) -> Result<Vec<u8>, crate::Error> {
95    let random_element = Sec1Point::from_untagged_bytes(
96        ciphertext
97            .get(..64)
98            .ok_or(crate::Error::SliceOutOfBoundsError(
99                "ciphertext",
100                ciphertext.len(),
101            ))?
102            .try_into()
103            .or(Err(crate::Error::P256DecodeError))?,
104    );
105    let random_element = AffinePoint::from_sec1_point(&random_element)
106        .into_option()
107        .ok_or(crate::Error::P256DecodeError)?;
108
109    let blinded_element = Sec1Point::from_untagged_bytes(
110        ciphertext
111            .get(64..128)
112            .ok_or(crate::Error::SliceOutOfBoundsError(
113                "ciphertext",
114                ciphertext.len(),
115            ))?
116            .try_into()
117            .or(Err(crate::Error::P256DecodeError))?,
118    );
119    let blinded_element = AffinePoint::from_sec1_point(&blinded_element)
120        .into_option()
121        .ok_or(crate::Error::P256DecodeError)?;
122
123    let encrypted = Ciphertext::from_elements(random_element.into(), blinded_element.into());
124
125    Ok(private_key
126        .decrypt_to_element(encrypted)
127        .to_untagged_bytes()
128        .to_vec())
129}
130
131pub fn verify(
132    verifying_key: &VerifyingKey,
133    msg: &[u8],
134    signature: &[u8],
135) -> Result<(), p256::ecdsa::Error> {
136    let signature = Signature::from_slice(signature)?;
137    verifying_key.verify(msg, &signature)
138}
139
140pub fn sign(signing_key: &SigningKey, msg: &[u8]) -> Vec<u8> {
141    let signature: Signature = signing_key.sign(msg);
142    signature.to_bytes().to_vec()
143}
144
145#[cfg(test)]
146mod test {
147    use crate::{
148        Keypair,
149        crypto::ecc_p256::{FromBytes, SecretKey, ToUntaggedBytes, decrypt},
150    };
151    use p256::ecdsa::VerifyingKey;
152
153    #[test]
154    fn create_invalid_verifying_key() {
155        assert!(matches!(
156            VerifyingKey::from_bytes(&[1]),
157            Err(crate::Error::P256DecodeError)
158        ));
159    }
160
161    #[test]
162    fn try_to_decrypt_too_short_ciphertext() {
163        let sk = SecretKey::from_bytes(&[1u8; 32]).unwrap();
164
165        assert!(matches!(
166            decrypt(&sk, &[1]),
167            Err(crate::Error::SliceOutOfBoundsError(_, _))
168        ));
169    }
170
171    #[test]
172    fn decrypt_long_ciphertext() {
173        let sk = SecretKey::from_bytes(&[1u8; 32]).unwrap();
174        let kp = Keypair::from(sk.clone());
175
176        let mut point = kp.public().as_element().to_untagged_bytes().to_vec();
177        point.append(&mut point.clone());
178
179        let expected = vec![
180            172, 161, 83, 0, 40, 73, 212, 190, 29, 204, 157, 78, 217, 152, 216, 235, 155, 39, 228,
181            115, 176, 146, 44, 117, 228, 161, 45, 97, 177, 206, 222, 39, 112, 171, 121, 124, 145,
182            154, 37, 179, 108, 74, 77, 247, 132, 44, 68, 197, 162, 252, 131, 221, 46, 117, 145, 82,
183            33, 147, 21, 83, 103, 237, 70, 93,
184        ];
185
186        assert_eq!(decrypt(&sk, &point).unwrap(), expected);
187        point.append(&mut vec![1u8; 32]);
188        assert_eq!(decrypt(&sk, &point).unwrap(), expected);
189    }
190}