playready/crypto/
ecc_p256.rs

1use elastic_elgamal::{group::Generic, Ciphertext};
2use p256::{
3    ecdsa::{
4        signature::{Signer, Verifier},
5        Signature, SigningKey, VerifyingKey,
6    },
7    elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint},
8    AffinePoint, EncodedPoint, NistP256, ProjectivePoint,
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        let point = EncodedPoint::from_untagged_bytes(bytes.into());
60        let point = AffinePoint::from_encoded_point(&point)
61            .into_option()
62            .ok_or(crate::Error::P256DecodeError)?;
63
64        VerifyingKey::from_affine(point).or(Err(crate::Error::P256DecodeError))
65    }
66}
67
68pub fn wmrm_public_key() -> &'static PublicKey {
69    static CELL: OnceLock<PublicKey> = OnceLock::new();
70
71    CELL.get_or_init(|| {
72        const WMRM_KEY: [u8; 33] = [
73            0x02, 0xc8, 0xb6, 0xaf, 0x16, 0xee, 0x94, 0x1a, 0xad, 0xaa, 0x53, 0x89, 0xb4, 0xaf,
74            0x2c, 0x10, 0xe3, 0x56, 0xbe, 0x42, 0xaf, 0x17, 0x5e, 0xf3, 0xfa, 0xce, 0x93, 0x25,
75            0x4e, 0x7b, 0x0b, 0x3d, 0x9b,
76        ];
77
78        PublicKey::from_bytes(&WMRM_KEY).unwrap()
79    })
80}
81
82pub fn encrypt(public_key: &PublicKey, plaintext: ProjectivePoint) -> Vec<u8> {
83    let mut rng = rand::thread_rng();
84    let ciphertext = public_key.encrypt_element(plaintext, &mut rng);
85
86    let point1 = ciphertext.random_element().to_untagged_bytes();
87    let point2 = ciphertext.blinded_element().to_untagged_bytes();
88
89    [point1, point2].concat()
90}
91
92pub fn decrypt(private_key: &SecretKey, ciphertext: &[u8]) -> Result<Vec<u8>, crate::Error> {
93    let random_element = EncodedPoint::from_untagged_bytes(
94        ciphertext
95            .get(..64)
96            .ok_or(crate::Error::SliceOutOfBoundsError(
97                "ciphertext",
98                ciphertext.len(),
99            ))?
100            .into(),
101    );
102    let random_element = AffinePoint::from_encoded_point(&random_element)
103        .into_option()
104        .ok_or(crate::Error::P256DecodeError)?;
105
106    let blinded_element = EncodedPoint::from_untagged_bytes(
107        ciphertext
108            .get(64..)
109            .ok_or(crate::Error::SliceOutOfBoundsError(
110                "ciphertext",
111                ciphertext.len(),
112            ))?
113            .into(),
114    );
115    let blinded_element = AffinePoint::from_encoded_point(&blinded_element)
116        .into_option()
117        .ok_or(crate::Error::P256DecodeError)?;
118
119    let encrypted: Ciphertext<Generic<NistP256>> = Ciphertext::zero();
120
121    // TODO: Remove unsafe code once https://github.com/slowli/elastic-elgamal/pull/157 is merged
122    unsafe {
123        copy_nonoverlapping(
124            &random_element.into(),
125            encrypted.random_element() as *const ProjectivePoint as *mut ProjectivePoint,
126            1,
127        );
128        copy_nonoverlapping(
129            &blinded_element.into(),
130            encrypted.blinded_element() as *const ProjectivePoint as *mut ProjectivePoint,
131            1,
132        );
133    };
134
135    Ok(private_key
136        .decrypt_to_element(encrypted)
137        .to_untagged_bytes()
138        .to_vec())
139}
140
141pub fn verify(
142    verifying_key: &VerifyingKey,
143    msg: &[u8],
144    signature: &[u8],
145) -> Result<(), p256::ecdsa::Error> {
146    let signature = Signature::from_slice(signature)?;
147    verifying_key.verify(msg, &signature)
148}
149
150pub fn sign(signing_key: &SigningKey, msg: &[u8]) -> Vec<u8> {
151    let signature: Signature = signing_key.sign(msg);
152    signature.to_bytes().to_vec()
153}