1use crate::{
4 AffinePoint, Curve, Error, NonZeroScalar, ProjectiveArithmetic, ProjectivePoint, Result,
5};
6use core::fmt::Debug;
7use group::{Curve as _, Group};
8
9#[cfg(feature = "jwk")]
10use crate::{JwkEcKey, JwkParameters};
11
12#[cfg(all(feature = "sec1", feature = "pkcs8"))]
13use crate::{
14 pkcs8::{self, AssociatedOid, DecodePublicKey},
15 ALGORITHM_OID,
16};
17
18#[cfg(feature = "pem")]
19use core::str::FromStr;
20
21#[cfg(feature = "sec1")]
22use {
23 crate::{
24 sec1::{EncodedPoint, FromEncodedPoint, ModulusSize, ToEncodedPoint},
25 FieldSize, PointCompression,
26 },
27 core::cmp::Ordering,
28 subtle::CtOption,
29};
30
31#[cfg(feature = "serde")]
32use serdect::serde::{de, ser, Deserialize, Serialize};
33
34#[cfg(all(feature = "alloc", feature = "pkcs8"))]
35use pkcs8::EncodePublicKey;
36
37#[cfg(any(feature = "jwk", feature = "pem"))]
38use alloc::string::{String, ToString};
39
40#[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))]
77#[derive(Clone, Debug, Eq, PartialEq)]
78pub struct PublicKey<C>
79where
80 C: Curve + ProjectiveArithmetic,
81{
82 point: AffinePoint<C>,
83}
84
85impl<C> PublicKey<C>
86where
87 C: Curve + ProjectiveArithmetic,
88{
89 pub fn from_affine(point: AffinePoint<C>) -> Result<Self> {
91 if ProjectivePoint::<C>::from(point).is_identity().into() {
92 Err(Error)
93 } else {
94 Ok(Self { point })
95 }
96 }
97
98 pub fn from_secret_scalar(scalar: &NonZeroScalar<C>) -> Self {
101 Self {
103 point: (C::ProjectivePoint::generator() * scalar.as_ref()).to_affine(),
104 }
105 }
106
107 #[cfg(feature = "sec1")]
114 pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self>
115 where
116 C: Curve,
117 FieldSize<C>: ModulusSize,
118 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
119 {
120 let point = EncodedPoint::<C>::from_bytes(bytes).map_err(|_| Error)?;
121 Option::from(Self::from_encoded_point(&point)).ok_or(Error)
122 }
123
124 pub fn as_affine(&self) -> &AffinePoint<C> {
128 &self.point
129 }
130
131 pub fn to_projective(&self) -> ProjectivePoint<C> {
133 self.point.into()
134 }
135
136 #[cfg(feature = "jwk")]
138 #[cfg_attr(docsrs, doc(cfg(feature = "jwk")))]
139 pub fn from_jwk(jwk: &JwkEcKey) -> Result<Self>
140 where
141 C: Curve + JwkParameters,
142 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
143 FieldSize<C>: ModulusSize,
144 {
145 jwk.to_public_key::<C>()
146 }
147
148 #[cfg(feature = "jwk")]
150 #[cfg_attr(docsrs, doc(cfg(feature = "jwk")))]
151 pub fn from_jwk_str(jwk: &str) -> Result<Self>
152 where
153 C: Curve + JwkParameters,
154 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
155 FieldSize<C>: ModulusSize,
156 {
157 jwk.parse::<JwkEcKey>().and_then(|jwk| Self::from_jwk(&jwk))
158 }
159
160 #[cfg(feature = "jwk")]
162 #[cfg_attr(docsrs, doc(cfg(feature = "jwk")))]
163 pub fn to_jwk(&self) -> JwkEcKey
164 where
165 C: Curve + JwkParameters,
166 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
167 FieldSize<C>: ModulusSize,
168 {
169 self.into()
170 }
171
172 #[cfg(feature = "jwk")]
174 #[cfg_attr(docsrs, doc(cfg(feature = "jwk")))]
175 pub fn to_jwk_string(&self) -> String
176 where
177 C: Curve + JwkParameters,
178 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
179 FieldSize<C>: ModulusSize,
180 {
181 self.to_jwk().to_string()
182 }
183}
184
185impl<C> AsRef<AffinePoint<C>> for PublicKey<C>
186where
187 C: Curve + ProjectiveArithmetic,
188{
189 fn as_ref(&self) -> &AffinePoint<C> {
190 self.as_affine()
191 }
192}
193
194impl<C> Copy for PublicKey<C> where C: Curve + ProjectiveArithmetic {}
195
196#[cfg(feature = "sec1")]
197#[cfg_attr(docsrs, doc(cfg(feature = "sec1")))]
198impl<C> FromEncodedPoint<C> for PublicKey<C>
199where
200 C: Curve + ProjectiveArithmetic,
201 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
202 FieldSize<C>: ModulusSize,
203{
204 fn from_encoded_point(encoded_point: &EncodedPoint<C>) -> CtOption<Self> {
206 AffinePoint::<C>::from_encoded_point(encoded_point).and_then(|point| {
207 let is_identity = ProjectivePoint::<C>::from(point).is_identity();
208 CtOption::new(PublicKey { point }, !is_identity)
209 })
210 }
211}
212
213#[cfg(feature = "sec1")]
214#[cfg_attr(docsrs, doc(cfg(feature = "sec1")))]
215impl<C> ToEncodedPoint<C> for PublicKey<C>
216where
217 C: Curve + ProjectiveArithmetic,
218 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
219 FieldSize<C>: ModulusSize,
220{
221 fn to_encoded_point(&self, compress: bool) -> EncodedPoint<C> {
224 self.point.to_encoded_point(compress)
225 }
226}
227
228#[cfg(feature = "sec1")]
229#[cfg_attr(docsrs, doc(cfg(feature = "sec1")))]
230impl<C> From<PublicKey<C>> for EncodedPoint<C>
231where
232 C: Curve + ProjectiveArithmetic + PointCompression,
233 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
234 FieldSize<C>: ModulusSize,
235{
236 fn from(public_key: PublicKey<C>) -> EncodedPoint<C> {
237 EncodedPoint::<C>::from(&public_key)
238 }
239}
240
241#[cfg(feature = "sec1")]
242#[cfg_attr(docsrs, doc(cfg(feature = "sec1")))]
243impl<C> From<&PublicKey<C>> for EncodedPoint<C>
244where
245 C: Curve + ProjectiveArithmetic + PointCompression,
246 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
247 FieldSize<C>: ModulusSize,
248{
249 fn from(public_key: &PublicKey<C>) -> EncodedPoint<C> {
250 public_key.to_encoded_point(C::COMPRESS_POINTS)
251 }
252}
253
254#[cfg(feature = "sec1")]
255#[cfg_attr(docsrs, doc(cfg(feature = "sec1")))]
256impl<C> PartialOrd for PublicKey<C>
257where
258 C: Curve + ProjectiveArithmetic,
259 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
260 FieldSize<C>: ModulusSize,
261{
262 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
263 Some(self.cmp(other))
264 }
265}
266
267#[cfg(feature = "sec1")]
268#[cfg_attr(docsrs, doc(cfg(feature = "sec1")))]
269impl<C> Ord for PublicKey<C>
270where
271 C: Curve + ProjectiveArithmetic,
272 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
273 FieldSize<C>: ModulusSize,
274{
275 fn cmp(&self, other: &Self) -> Ordering {
276 self.to_encoded_point(false)
279 .cmp(&other.to_encoded_point(false))
280 }
281}
282
283#[cfg(all(feature = "pkcs8", feature = "sec1"))]
284#[cfg_attr(docsrs, doc(cfg(all(feature = "pkcs8", feature = "sec1"))))]
285impl<C> TryFrom<pkcs8::SubjectPublicKeyInfo<'_>> for PublicKey<C>
286where
287 C: Curve + AssociatedOid + ProjectiveArithmetic,
288 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
289 FieldSize<C>: ModulusSize,
290{
291 type Error = pkcs8::spki::Error;
292
293 fn try_from(spki: pkcs8::SubjectPublicKeyInfo<'_>) -> pkcs8::spki::Result<Self> {
294 spki.algorithm.assert_oids(ALGORITHM_OID, C::OID)?;
295 Self::from_sec1_bytes(spki.subject_public_key)
296 .map_err(|_| der::Tag::BitString.value_error().into())
297 }
298}
299
300#[cfg(all(feature = "pkcs8", feature = "sec1"))]
301#[cfg_attr(docsrs, doc(cfg(all(feature = "pkcs8", feature = "sec1"))))]
302impl<C> DecodePublicKey for PublicKey<C>
303where
304 C: Curve + AssociatedOid + ProjectiveArithmetic,
305 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
306 FieldSize<C>: ModulusSize,
307{
308}
309
310#[cfg(all(feature = "alloc", feature = "pkcs8"))]
311#[cfg_attr(docsrs, doc(cfg(all(feature = "alloc", feature = "pkcs8"))))]
312impl<C> EncodePublicKey for PublicKey<C>
313where
314 C: Curve + AssociatedOid + ProjectiveArithmetic,
315 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
316 FieldSize<C>: ModulusSize,
317{
318 fn to_public_key_der(&self) -> pkcs8::spki::Result<der::Document> {
319 let algorithm = pkcs8::AlgorithmIdentifier {
320 oid: ALGORITHM_OID,
321 parameters: Some((&C::OID).into()),
322 };
323
324 let public_key_bytes = self.to_encoded_point(false);
325
326 pkcs8::SubjectPublicKeyInfo {
327 algorithm,
328 subject_public_key: public_key_bytes.as_ref(),
329 }
330 .try_into()
331 }
332}
333
334#[cfg(feature = "pem")]
335#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
336impl<C> FromStr for PublicKey<C>
337where
338 C: Curve + AssociatedOid + ProjectiveArithmetic,
339 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
340 FieldSize<C>: ModulusSize,
341{
342 type Err = Error;
343
344 fn from_str(s: &str) -> Result<Self> {
345 Self::from_public_key_pem(s).map_err(|_| Error)
346 }
347}
348
349#[cfg(feature = "pem")]
350#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
351impl<C> ToString for PublicKey<C>
352where
353 C: Curve + AssociatedOid + ProjectiveArithmetic,
354 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
355 FieldSize<C>: ModulusSize,
356{
357 fn to_string(&self) -> String {
358 self.to_public_key_pem(Default::default())
359 .expect("PEM encoding error")
360 }
361}
362
363#[cfg(feature = "serde")]
364#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
365impl<C> Serialize for PublicKey<C>
366where
367 C: Curve + AssociatedOid + ProjectiveArithmetic,
368 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
369 FieldSize<C>: ModulusSize,
370{
371 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
372 where
373 S: ser::Serializer,
374 {
375 let der = self.to_public_key_der().map_err(ser::Error::custom)?;
376 serdect::slice::serialize_hex_upper_or_bin(&der, serializer)
377 }
378}
379
380#[cfg(feature = "serde")]
381#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
382impl<'de, C> Deserialize<'de> for PublicKey<C>
383where
384 C: Curve + AssociatedOid + ProjectiveArithmetic,
385 AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
386 FieldSize<C>: ModulusSize,
387{
388 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
389 where
390 D: de::Deserializer<'de>,
391 {
392 let der_bytes = serdect::slice::deserialize_hex_or_bin_vec(deserializer)?;
393 Self::from_public_key_der(&der_bytes).map_err(de::Error::custom)
394 }
395}
396
397#[cfg(all(feature = "dev", test))]
398mod tests {
399 use crate::{dev::MockCurve, sec1::FromEncodedPoint};
400
401 type EncodedPoint = crate::sec1::EncodedPoint<MockCurve>;
402 type PublicKey = super::PublicKey<MockCurve>;
403
404 #[test]
405 fn from_encoded_point_rejects_identity() {
406 let identity = EncodedPoint::identity();
407 assert!(bool::from(
408 PublicKey::from_encoded_point(&identity).is_none()
409 ));
410 }
411}