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