1use crate::{
4 AffinePoint, CurveArithmetic, CurveGroup, Error, NonZeroScalar, ProjectivePoint, Result,
5 SecretKey, point::NonIdentity,
6};
7use core::fmt::Debug;
8use group::Group;
9
10#[cfg(feature = "pkcs8")]
11use pkcs8::spki::{AlgorithmIdentifier, AssociatedAlgorithmIdentifier, ObjectIdentifier};
12
13#[cfg(feature = "pem")]
14use {
15 alloc::string::{String, ToString},
16 core::str::FromStr,
17};
18
19#[cfg(feature = "sec1")]
20use {
21 crate::{
22 FieldBytesSize,
23 ctutils::{Choice, CtOption},
24 point::PointCompression,
25 sec1::{CompressedPoint, FromSec1Point, ModulusSize, Sec1Point, ToSec1Point},
26 },
27 core::cmp::Ordering,
28};
29
30#[cfg(feature = "serde")]
31use serdect::serde::{Deserialize, Serialize, de, ser};
32
33#[cfg(all(feature = "alloc", feature = "pkcs8"))]
34use pkcs8::EncodePublicKey;
35
36#[cfg(all(feature = "alloc", feature = "sec1"))]
37use alloc::boxed::Box;
38
39#[cfg(any(feature = "pem", feature = "serde"))]
40use pkcs8::DecodePublicKey;
41
42#[cfg(all(feature = "sec1", feature = "pkcs8"))]
43use {
44 crate::{ALGORITHM_OID, pkcs8::AssociatedOid},
45 pkcs8::der,
46};
47
48#[derive(Clone, Debug, Eq, PartialEq)]
83pub struct PublicKey<C>
84where
85 C: CurveArithmetic,
86{
87 point: AffinePoint<C>,
88}
89
90impl<C> PublicKey<C>
91where
92 C: CurveArithmetic,
93{
94 pub fn from_affine(point: AffinePoint<C>) -> Result<Self> {
99 if ProjectivePoint::<C>::from(point).is_identity().into() {
100 Err(Error)
101 } else {
102 Ok(Self { point })
103 }
104 }
105
106 pub fn from_secret_scalar(scalar: &NonZeroScalar<C>) -> Self {
109 Self {
111 point: ProjectivePoint::<C>::mul_by_generator(scalar).to_affine(),
112 }
113 }
114
115 #[cfg(feature = "sec1")]
126 pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self>
127 where
128 FieldBytesSize<C>: ModulusSize,
129 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
130 {
131 let point = Sec1Point::<C>::from_bytes(bytes).map_err(|_| Error)?;
132 Self::from_sec1_point(&point).into_option().ok_or(Error)
133 }
134
135 #[cfg(all(feature = "alloc", feature = "sec1"))]
142 pub fn to_sec1_bytes(&self) -> Box<[u8]>
143 where
144 C: PointCompression,
145 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
146 FieldBytesSize<C>: ModulusSize,
147 {
148 Sec1Point::<C>::from(self).to_bytes()
149 }
150
151 pub fn as_affine(&self) -> &AffinePoint<C> {
155 &self.point
156 }
157
158 pub fn to_projective(&self) -> ProjectivePoint<C> {
160 self.point.into()
161 }
162
163 pub fn to_nonidentity(&self) -> NonIdentity<AffinePoint<C>> {
165 NonIdentity::new_unchecked(self.point)
166 }
167}
168
169impl<C> AsRef<AffinePoint<C>> for PublicKey<C>
170where
171 C: CurveArithmetic,
172{
173 fn as_ref(&self) -> &AffinePoint<C> {
174 self.as_affine()
175 }
176}
177
178impl<C> Copy for PublicKey<C> where C: CurveArithmetic {}
179
180#[cfg(feature = "sec1")]
181impl<C> FromSec1Point<C> for PublicKey<C>
182where
183 C: CurveArithmetic,
184 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
185 FieldBytesSize<C>: ModulusSize,
186{
187 fn from_sec1_point(encoded_point: &Sec1Point<C>) -> CtOption<Self> {
189 AffinePoint::<C>::from_sec1_point(encoded_point).and_then(|point| {
190 let is_identity = Choice::from_u8_lsb(u8::from(encoded_point.is_identity()));
192 CtOption::new(PublicKey { point }, !is_identity)
193 })
194 }
195}
196
197#[cfg(feature = "sec1")]
198impl<C> ToSec1Point<C> for PublicKey<C>
199where
200 C: CurveArithmetic,
201 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
202 FieldBytesSize<C>: ModulusSize,
203{
204 fn to_sec1_point(&self, compress: bool) -> Sec1Point<C> {
207 self.point.to_sec1_point(compress)
208 }
209}
210
211#[cfg(feature = "sec1")]
212impl<C> From<PublicKey<C>> for CompressedPoint<C>
213where
214 C: CurveArithmetic + PointCompression,
215 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
216 FieldBytesSize<C>: ModulusSize,
217{
218 fn from(public_key: PublicKey<C>) -> CompressedPoint<C> {
219 CompressedPoint::<C>::from(&public_key)
220 }
221}
222
223#[cfg(feature = "sec1")]
224impl<C> From<&PublicKey<C>> for CompressedPoint<C>
225where
226 C: CurveArithmetic + PointCompression,
227 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
228 FieldBytesSize<C>: ModulusSize,
229{
230 fn from(public_key: &PublicKey<C>) -> CompressedPoint<C> {
231 public_key
232 .to_sec1_point(true)
233 .as_bytes()
234 .try_into()
235 .expect("wrong compressed point size")
236 }
237}
238
239#[cfg(feature = "sec1")]
240impl<C> From<PublicKey<C>> for Sec1Point<C>
241where
242 C: CurveArithmetic + PointCompression,
243 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
244 FieldBytesSize<C>: ModulusSize,
245{
246 fn from(public_key: PublicKey<C>) -> Sec1Point<C> {
247 Sec1Point::<C>::from(&public_key)
248 }
249}
250
251#[cfg(feature = "sec1")]
252impl<C> From<&PublicKey<C>> for Sec1Point<C>
253where
254 C: CurveArithmetic + PointCompression,
255 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
256 FieldBytesSize<C>: ModulusSize,
257{
258 fn from(public_key: &PublicKey<C>) -> Sec1Point<C> {
259 public_key.to_sec1_point(C::COMPRESS_POINTS)
260 }
261}
262
263impl<C, P> From<NonIdentity<P>> for PublicKey<C>
264where
265 C: CurveArithmetic,
266 P: Copy + Into<AffinePoint<C>>,
267{
268 fn from(value: NonIdentity<P>) -> Self {
269 Self::from(&value)
270 }
271}
272
273impl<C, P> From<&NonIdentity<P>> for PublicKey<C>
274where
275 C: CurveArithmetic,
276 P: Copy + Into<AffinePoint<C>>,
277{
278 fn from(value: &NonIdentity<P>) -> Self {
279 Self {
280 point: value.to_point().into(),
281 }
282 }
283}
284
285impl<C> From<PublicKey<C>> for NonIdentity<AffinePoint<C>>
286where
287 C: CurveArithmetic,
288{
289 fn from(value: PublicKey<C>) -> Self {
290 Self::from(&value)
291 }
292}
293
294impl<C> From<&PublicKey<C>> for NonIdentity<AffinePoint<C>>
295where
296 C: CurveArithmetic,
297{
298 fn from(value: &PublicKey<C>) -> Self {
299 PublicKey::to_nonidentity(value)
300 }
301}
302
303impl<C> From<SecretKey<C>> for PublicKey<C>
304where
305 C: CurveArithmetic,
306{
307 fn from(secret_key: SecretKey<C>) -> Self {
308 secret_key.public_key()
309 }
310}
311
312impl<C> From<&SecretKey<C>> for PublicKey<C>
313where
314 C: CurveArithmetic,
315{
316 fn from(secret_key: &SecretKey<C>) -> PublicKey<C> {
317 secret_key.public_key()
318 }
319}
320
321#[cfg(feature = "sec1")]
322impl<C> PartialOrd for PublicKey<C>
323where
324 C: CurveArithmetic,
325 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
326 FieldBytesSize<C>: ModulusSize,
327{
328 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
329 Some(self.cmp(other))
330 }
331}
332
333#[cfg(feature = "sec1")]
334impl<C> Ord for PublicKey<C>
335where
336 C: CurveArithmetic,
337 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
338 FieldBytesSize<C>: ModulusSize,
339{
340 fn cmp(&self, other: &Self) -> Ordering {
341 self.to_sec1_point(false).cmp(&other.to_sec1_point(false))
344 }
345}
346
347#[cfg(feature = "sec1")]
348impl<C> TryFrom<CompressedPoint<C>> for PublicKey<C>
349where
350 C: CurveArithmetic,
351 FieldBytesSize<C>: ModulusSize,
352 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
353{
354 type Error = Error;
355
356 fn try_from(point: CompressedPoint<C>) -> Result<Self> {
357 Self::from_sec1_bytes(&point)
358 }
359}
360
361#[cfg(feature = "sec1")]
362impl<C> TryFrom<&CompressedPoint<C>> for PublicKey<C>
363where
364 C: CurveArithmetic,
365 FieldBytesSize<C>: ModulusSize,
366 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
367{
368 type Error = Error;
369
370 fn try_from(point: &CompressedPoint<C>) -> Result<Self> {
371 Self::from_sec1_bytes(point)
372 }
373}
374
375#[cfg(feature = "sec1")]
376impl<C> TryFrom<Sec1Point<C>> for PublicKey<C>
377where
378 C: CurveArithmetic,
379 FieldBytesSize<C>: ModulusSize,
380 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
381{
382 type Error = Error;
383
384 fn try_from(point: Sec1Point<C>) -> Result<Self> {
385 Self::from_sec1_bytes(point.as_bytes())
386 }
387}
388
389#[cfg(feature = "sec1")]
390impl<C> TryFrom<&Sec1Point<C>> for PublicKey<C>
391where
392 C: CurveArithmetic,
393 FieldBytesSize<C>: ModulusSize,
394 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
395{
396 type Error = Error;
397
398 fn try_from(point: &Sec1Point<C>) -> Result<Self> {
399 Self::from_sec1_bytes(point.as_bytes())
400 }
401}
402
403#[cfg(feature = "pkcs8")]
404impl<C> AssociatedAlgorithmIdentifier for PublicKey<C>
405where
406 C: AssociatedOid + CurveArithmetic,
407{
408 type Params = ObjectIdentifier;
409
410 const ALGORITHM_IDENTIFIER: AlgorithmIdentifier<ObjectIdentifier> = AlgorithmIdentifier {
411 oid: ALGORITHM_OID,
412 parameters: Some(C::OID),
413 };
414}
415
416#[cfg(feature = "pkcs8")]
417impl<C> TryFrom<pkcs8::SubjectPublicKeyInfoRef<'_>> for PublicKey<C>
418where
419 C: AssociatedOid + CurveArithmetic,
420 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
421 FieldBytesSize<C>: ModulusSize,
422{
423 type Error = pkcs8::spki::Error;
424
425 fn try_from(spki: pkcs8::SubjectPublicKeyInfoRef<'_>) -> pkcs8::spki::Result<Self> {
426 Self::try_from(&spki)
427 }
428}
429
430#[cfg(feature = "pkcs8")]
431impl<C> TryFrom<&pkcs8::SubjectPublicKeyInfoRef<'_>> for PublicKey<C>
432where
433 C: AssociatedOid + CurveArithmetic,
434 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
435 FieldBytesSize<C>: ModulusSize,
436{
437 type Error = pkcs8::spki::Error;
438
439 fn try_from(spki: &pkcs8::SubjectPublicKeyInfoRef<'_>) -> pkcs8::spki::Result<Self> {
440 spki.algorithm.assert_oids(ALGORITHM_OID, C::OID)?;
441
442 let public_key_bytes = spki
443 .subject_public_key
444 .as_bytes()
445 .ok_or_else(|| der::Tag::BitString.value_error().to_error())?;
446
447 Self::from_sec1_bytes(public_key_bytes)
448 .map_err(|_| der::Tag::BitString.value_error().to_error().into())
449 }
450}
451
452#[cfg(all(feature = "alloc", feature = "pkcs8"))]
453impl<C> EncodePublicKey for PublicKey<C>
454where
455 C: AssociatedOid + CurveArithmetic,
456 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
457 FieldBytesSize<C>: ModulusSize,
458{
459 fn to_public_key_der(&self) -> pkcs8::spki::Result<der::Document> {
460 let public_key_bytes = self.to_sec1_point(false);
461 let subject_public_key = der::asn1::BitStringRef::new(0, public_key_bytes.as_bytes())?;
462
463 pkcs8::SubjectPublicKeyInfo {
464 algorithm: Self::ALGORITHM_IDENTIFIER,
465 subject_public_key,
466 }
467 .try_into()
468 }
469}
470
471#[cfg(feature = "pem")]
472impl<C> FromStr for PublicKey<C>
473where
474 C: AssociatedOid + CurveArithmetic,
475 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
476 FieldBytesSize<C>: ModulusSize,
477{
478 type Err = Error;
479
480 fn from_str(s: &str) -> Result<Self> {
481 Self::from_public_key_pem(s).map_err(|_| Error)
482 }
483}
484
485#[cfg(feature = "pem")]
486#[allow(clippy::to_string_trait_impl)]
487impl<C> ToString for PublicKey<C>
488where
489 C: AssociatedOid + CurveArithmetic,
490 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
491 FieldBytesSize<C>: ModulusSize,
492{
493 fn to_string(&self) -> String {
494 self.to_public_key_pem(Default::default())
495 .expect("PEM encoding error")
496 }
497}
498
499#[cfg(feature = "serde")]
500impl<C> Serialize for PublicKey<C>
501where
502 C: AssociatedOid + CurveArithmetic,
503 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
504 FieldBytesSize<C>: ModulusSize,
505{
506 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
507 where
508 S: ser::Serializer,
509 {
510 let der = self.to_public_key_der().map_err(ser::Error::custom)?;
511 serdect::slice::serialize_hex_upper_or_bin(&der, serializer)
512 }
513}
514
515#[cfg(feature = "serde")]
516impl<'de, C> Deserialize<'de> for PublicKey<C>
517where
518 C: AssociatedOid + CurveArithmetic,
519 AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
520 FieldBytesSize<C>: ModulusSize,
521{
522 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
523 where
524 D: de::Deserializer<'de>,
525 {
526 let der_bytes = serdect::slice::deserialize_hex_or_bin_vec(deserializer)?;
527 Self::from_public_key_der(&der_bytes).map_err(de::Error::custom)
528 }
529}
530
531#[cfg(all(feature = "dev", test))]
532mod tests {
533 use crate::{dev::MockCurve, sec1::FromSec1Point};
534
535 type Sec1Point = crate::sec1::Sec1Point<MockCurve>;
536 type PublicKey = super::PublicKey<MockCurve>;
537
538 #[test]
539 fn from_sec1_point_rejects_identity() {
540 let identity = Sec1Point::identity();
541 assert!(bool::from(PublicKey::from_sec1_point(&identity).is_none()));
542 }
543}