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