1use libcrux_p256::{
4 compressed_to_raw, ecdsa_sign_p256_sha2, ecdsa_sign_p256_sha384, ecdsa_sign_p256_sha512,
5 ecdsa_verif_p256_sha2, ecdsa_verif_p256_sha384, ecdsa_verif_p256_sha512, uncompressed_to_raw,
6 validate_private_key, validate_public_key,
7};
8
9use crate::DigestAlgorithm;
10
11use super::Error;
12
13#[derive(Clone, Default)]
15pub struct Signature {
16 r: [u8; 32],
17 s: [u8; 32],
18}
19
20pub struct Nonce([u8; 32]);
22
23pub struct PrivateKey([u8; 32]);
25
26#[derive(Debug)]
28pub struct PublicKey(pub [u8; 64]);
29
30mod conversions {
31 use super::*;
32
33 impl Signature {
34 pub fn from_raw(r: [u8; 32], s: [u8; 32]) -> Self {
36 Self { r, s }
37 }
38
39 pub fn from_bytes(signature_bytes: [u8; 64]) -> Self {
41 Self {
42 r: signature_bytes[0..32].try_into().unwrap(),
43 s: signature_bytes[32..].try_into().unwrap(),
44 }
45 }
46
47 pub fn as_bytes(&self) -> (&[u8; 32], &[u8; 32]) {
49 (&self.r, &self.s)
50 }
51 }
52
53 impl TryFrom<&[u8; 32]> for PrivateKey {
54 type Error = Error;
55
56 fn try_from(value: &[u8; 32]) -> Result<Self, Self::Error> {
57 validate_private_key_slice(value)
58 }
59 }
60
61 impl TryFrom<&[u8]> for PrivateKey {
62 type Error = Error;
63
64 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
65 validate_private_key_slice(value)
66 }
67 }
68
69 impl AsRef<[u8]> for PrivateKey {
70 fn as_ref(&self) -> &[u8] {
71 &self.0
72 }
73 }
74
75 impl AsRef<[u8; 32]> for PrivateKey {
76 fn as_ref(&self) -> &[u8; 32] {
77 &self.0
78 }
79 }
80
81 impl TryFrom<&[u8; 64]> for PublicKey {
82 type Error = Error;
83
84 fn try_from(value: &[u8; 64]) -> Result<Self, Self::Error> {
85 validate_pk(value)
86 }
87 }
88
89 impl TryFrom<&[u8]> for PublicKey {
90 type Error = Error;
91
92 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
93 validate_pk(value)
94 }
95 }
96
97 impl AsRef<[u8]> for PublicKey {
98 fn as_ref(&self) -> &[u8] {
99 &self.0
100 }
101 }
102
103 impl AsRef<[u8; 64]> for PublicKey {
104 fn as_ref(&self) -> &[u8; 64] {
105 &self.0
106 }
107 }
108}
109
110pub fn uncompressed_to_coordinates(point: &[u8]) -> Result<[u8; 64], Error> {
113 let mut concat_point = [0u8; 64];
114 if point.len() >= 65 {
115 if uncompressed_to_raw(point, &mut concat_point) {
116 Ok(concat_point)
117 } else {
118 Err(Error::InvalidInput)
119 }
120 } else {
121 Err(Error::NoCompressedPoint)
122 }
123}
124
125pub fn compressed_to_coordinates(point: &[u8]) -> Result<[u8; 64], Error> {
128 let mut concat_point = [0u8; 64];
129 if point.len() >= 33 {
130 if compressed_to_raw(point, &mut concat_point) {
131 Ok(concat_point)
132 } else {
133 Err(Error::InvalidInput)
134 }
135 } else {
136 Err(Error::NoUnCompressedPoint)
137 }
138}
139
140pub fn validate_point(point: &[u8]) -> Result<(), Error> {
145 if validate_public_key(point) {
146 Ok(())
147 } else {
148 Err(Error::InvalidPoint)
149 }
150}
151
152pub fn validate_scalar(scalar: &impl AsRef<[u8; 32]>) -> Result<(), Error> {
156 validate_scalar_(scalar.as_ref())
157}
158
159fn validate_scalar_(scalar: &[u8; 32]) -> Result<(), Error> {
163 if scalar.as_ref().iter().all(|b| *b == 0) {
164 return Err(Error::InvalidScalar);
165 }
166
167 if validate_private_key(scalar.as_ref()) {
169 Ok(())
170 } else {
171 Err(Error::InvalidScalar)
172 }
173}
174
175fn validate_scalar_slice(scalar: &[u8]) -> Result<[u8; 32], Error> {
177 if scalar.is_empty() {
178 return Err(Error::InvalidScalar);
179 }
180
181 let mut private = [0u8; 32];
182 let sk_len = if scalar.len() >= 32 { 32 } else { scalar.len() };
184 for i in 0..sk_len {
185 private[31 - i] = scalar[scalar.len() - 1 - i];
186 }
187
188 validate_scalar_(&private).map(|_| private)
189}
190
191fn validate_private_key_slice(scalar: &[u8]) -> Result<PrivateKey, Error> {
192 validate_scalar_slice(scalar).map(|a| PrivateKey(a))
193}
194
195#[cfg(feature = "rand")]
197pub mod rand {
198 use crate::RAND_LIMIT;
199
200 use super::*;
201 use ::rand::{CryptoRng, RngCore, TryRngCore};
202
203 pub fn random_scalar(rng: &mut (impl CryptoRng + RngCore)) -> Result<[u8; 32], Error> {
210 let mut value = [0u8; 32];
211 for _ in 0..RAND_LIMIT {
212 rng.try_fill_bytes(&mut value)
213 .map_err(|_| Error::RandError)?;
214
215 if validate_scalar_slice(&value).is_ok() {
217 return Ok(value);
218 }
219 }
220 Err(Error::RandError)
221 }
222
223 impl Nonce {
224 pub fn random(rng: &mut (impl CryptoRng + RngCore)) -> Result<Self, Error> {
226 random_scalar(rng).map(|s| Self(s))
227 }
228 }
229
230 impl PrivateKey {
231 pub fn random(rng: &mut (impl CryptoRng + RngCore)) -> Result<Self, Error> {
233 random_scalar(rng).map(|s| Self(s))
234 }
235 }
236
237 pub fn sign(
239 hash: DigestAlgorithm,
240 payload: &[u8],
241 private_key: &PrivateKey,
242 rng: &mut (impl CryptoRng + RngCore),
243 ) -> Result<Signature, Error> {
244 let nonce = Nonce(random_scalar(rng)?);
245
246 super::_sign(hash, payload, private_key, &nonce)
247 }
248}
249
250pub fn sign(
254 hash: DigestAlgorithm,
255 payload: &[u8],
256 private_key: &PrivateKey,
257 nonce: &Nonce,
258) -> Result<Signature, Error> {
259 _sign(hash, payload, private_key, nonce)
260}
261
262fn _sign(
264 hash: DigestAlgorithm,
265 payload: &[u8],
266 private_key: &PrivateKey,
267 nonce: &Nonce,
268) -> Result<Signature, Error> {
269 let mut signature = [0u8; 64];
270 let len = u32_len(payload)?;
271
272 let success = match hash {
273 DigestAlgorithm::Sha256 => {
274 ecdsa_sign_p256_sha2(&mut signature, len, payload, private_key.as_ref(), &nonce.0)
275 }
276 DigestAlgorithm::Sha384 => {
277 ecdsa_sign_p256_sha384(&mut signature, len, payload, private_key.as_ref(), &nonce.0)
278 }
279 DigestAlgorithm::Sha512 => {
280 ecdsa_sign_p256_sha512(&mut signature, len, payload, private_key.as_ref(), &nonce.0)
281 }
282 libcrux_sha2::Algorithm::Sha224 => return Err(Error::UnsupportedHash),
283 };
284
285 if !success {
286 return Err(Error::SigningError);
287 }
288
289 Ok(Signature {
290 r: signature[..32]
291 .try_into()
292 .map_err(|_| Error::SigningError)?,
293 s: signature[32..]
294 .try_into()
295 .map_err(|_| Error::SigningError)?,
296 })
297}
298
299fn u32_len(bytes: &[u8]) -> Result<u32, Error> {
300 if bytes.len() > u32::MAX as usize {
301 return Err(Error::InvalidInput);
302 } else {
303 Ok(bytes.len() as u32)
304 }
305}
306
307fn validate_pk(public_key: &[u8]) -> Result<PublicKey, Error> {
309 if public_key.is_empty() {
310 return Err(Error::SigningError);
311 }
312
313 let pk = if let Ok(pk) = uncompressed_to_coordinates(public_key) {
315 pk
316 } else {
317 if let Ok(pk) = compressed_to_coordinates(public_key) {
319 pk
320 } else {
321 public_key.try_into().map_err(|_| Error::InvalidSignature)?
323 }
324 };
325
326 let pk = PublicKey(pk);
327 validate_point(&pk.0).map(|_| pk)
328}
329
330pub fn verify(
334 hash: DigestAlgorithm,
335 payload: &[u8],
336 signature: &Signature,
337 public_key: &PublicKey,
338) -> Result<(), Error> {
339 let len = u32_len(payload)?;
340
341 let success = match hash {
342 libcrux_sha2::Algorithm::Sha256 => {
343 ecdsa_verif_p256_sha2(len, payload, &public_key.0, &signature.r, &signature.s)
344 }
345 libcrux_sha2::Algorithm::Sha384 => {
346 ecdsa_verif_p256_sha384(len, payload, &public_key.0, &signature.r, &signature.s)
347 }
348 libcrux_sha2::Algorithm::Sha512 => {
349 ecdsa_verif_p256_sha512(len, payload, &public_key.0, &signature.r, &signature.s)
350 }
351 libcrux_sha2::Algorithm::Sha224 => return Err(Error::UnsupportedHash),
352 };
353
354 if success {
355 Ok(())
356 } else {
357 Err(Error::InvalidSignature)
358 }
359}