1use digest::{array::Array, typenum::Unsigned};
5use elliptic_curve::point::AffineCoordinates;
6use p256::NistP256;
7use p384::NistP384;
8use p521::NistP521;
9use sha2::{Digest as _, Sha256};
10use signature::hazmat::{PrehashVerifier, RandomizedPrehashSigner};
11
12use crate::Digest;
13
14pub enum EllipticVerifyingKey {
16 Prime256(p256::ecdsa::VerifyingKey),
18 Prime384(p384::ecdsa::VerifyingKey),
20 Prime521(p521::ecdsa::VerifyingKey),
22}
23
24pub enum EllipticSigningKey {
26 Prime256(p256::ecdsa::SigningKey),
28 Prime384(p384::ecdsa::SigningKey),
30 Prime521(p521::ecdsa::SigningKey),
32}
33
34impl EllipticVerifyingKey {
35 pub fn x(&self) -> Vec<u8> {
37 match self {
38 Self::Prime256(key) => key.as_affine().x().to_vec(),
39 Self::Prime384(key) => key.as_affine().x().to_vec(),
40 Self::Prime521(key) => key.as_affine().x().to_vec(),
41 }
42 }
43
44 pub fn y(&self) -> Vec<u8> {
46 match self {
47 Self::Prime256(key) => key.as_affine().y().to_vec(),
48 Self::Prime384(key) => key.as_affine().y().to_vec(),
49 Self::Prime521(key) => key.as_affine().y().to_vec(),
50 }
51 }
52
53 pub fn verifies<D: Digest>(&self, signature: &[u8], message: &[u8]) -> bool {
55 match &self {
56 Self::Prime256(key) => {
57 let Ok(signature) = p256::ecdsa::Signature::from_slice(signature)
58 .or_else(|_| p256::ecdsa::Signature::from_der(signature))
59 else {
60 return false;
61 };
62 let digest = D::digest(message);
63 key.verify_prehash(&digest, &signature).is_ok()
64 }
65 Self::Prime384(key) => {
66 let Ok(signature) = p384::ecdsa::Signature::from_slice(signature)
67 .or_else(|_| p384::ecdsa::Signature::from_der(signature))
68 else {
69 return false;
70 };
71 let digest = D::digest(message);
72 key.verify_prehash(&digest, &signature).is_ok()
73 }
74 Self::Prime521(key) => {
75 let Ok(signature) = p521::ecdsa::Signature::from_slice(signature)
76 .or_else(|_| p521::ecdsa::Signature::from_der(signature))
77 else {
78 return false;
79 };
80 let digest = D::digest(message);
81
82 key.verify_prehash(&digest, &signature).is_ok()
83 }
84 }
85 }
86
87 pub fn from_coordinates(x: &[u8], y: &[u8]) -> Option<Self> {
89 if x.len() != y.len() {
90 return None;
91 }
92 match x.len() {
93 <NistP256 as elliptic_curve::Curve>::FieldBytesSize::USIZE => {
94 let x = Array::try_from(x).ok()?;
95 let y = Array::try_from(y).ok()?;
96 let point = p256::AffinePoint::from_coordinates(&x, &y).into_option()?;
97 let key = p256::ecdsa::VerifyingKey::from_affine(point).ok()?;
98 Some(Self::Prime256(key))
99 }
100 <NistP384 as elliptic_curve::Curve>::FieldBytesSize::USIZE => {
101 let x = Array::try_from(x).ok()?;
102 let y = Array::try_from(y).ok()?;
103 let point = p384::AffinePoint::from_coordinates(&x, &y).into_option()?;
104 let key = p384::ecdsa::VerifyingKey::from_affine(point).ok()?;
105 Some(Self::Prime384(key))
106 }
107 <NistP521 as elliptic_curve::Curve>::FieldBytesSize::USIZE => {
108 let x = Array::try_from(x).ok()?;
109 let y = Array::try_from(y).ok()?;
110 let point = p521::AffinePoint::from_coordinates(&x, &y).into_option()?;
111 let key = p521::ecdsa::VerifyingKey::from_affine(point).ok()?;
112 Some(Self::Prime521(key))
113 }
114 _ => None,
115 }
116 }
117
118 pub fn key_id(&self) -> Vec<u8> {
120 let x = self.x();
121 let y = self.y();
122 let digest = Sha256::new().chain_update(x).chain_update(y).finalize();
123 digest.to_vec()
124 }
125}
126
127impl EllipticSigningKey {
128 pub fn sign<D: Digest>(&self, message: &[u8]) -> Vec<u8> {
134 let digest = D::digest(message);
135 match &self {
136 Self::Prime256(key) => {
137 let signature: p256::ecdsa::Signature =
138 key.sign_prehash_with_rng(&mut rand::rng(), &digest).expect(
139 "the digest algorithm should produce a hash that is valid for this curve",
140 );
141 signature.to_vec()
142 }
143 Self::Prime384(key) => {
144 let signature: p384::ecdsa::Signature =
145 key.sign_prehash_with_rng(&mut rand::rng(), &digest).expect(
146 "the digest algorithm should produce a hash that is valid for this curve",
147 );
148 signature.to_vec()
149 }
150 Self::Prime521(key) => {
151 let signature: p521::ecdsa::Signature =
152 key.sign_prehash_with_rng(&mut rand::rng(), &digest).expect(
153 "the digest algorithm should produce a hash that is valid for this curve",
154 );
155 signature.to_vec()
156 }
157 }
158 }
159
160 pub fn verifying_key(&self) -> EllipticVerifyingKey {
162 match &self {
163 Self::Prime256(key) => EllipticVerifyingKey::Prime256(*key.verifying_key()),
164 Self::Prime384(key) => EllipticVerifyingKey::Prime384(*key.verifying_key()),
165 Self::Prime521(key) => EllipticVerifyingKey::Prime521(*key.verifying_key()),
166 }
167 }
168}