jwt_compact/alg/
es256k.rs1use core::{marker::PhantomData, num::NonZeroUsize};
4use std::sync::LazyLock;
5
6use secp256k1::{
7 All, Message, PublicKey, Secp256k1, SecretKey,
8 constants::{
9 COMPACT_SIGNATURE_SIZE, FIELD_SIZE, SECRET_KEY_SIZE, UNCOMPRESSED_PUBLIC_KEY_SIZE,
10 },
11 ecdsa::Signature,
12};
13use sha2::{
14 Digest, Sha256,
15 digest::{
16 FixedOutputReset, HashMarker,
17 crypto_common::{BlockSizeUser, typenum::U32},
18 },
19};
20
21use crate::{
22 Algorithm, AlgorithmSignature,
23 alg::{SecretBytes, SigningKey, VerifyingKey},
24 alloc::Cow,
25 jwk::{JsonWebKey, JwkError, KeyType},
26};
27
28const COORDINATE_SIZE: usize = FIELD_SIZE.len();
30
31impl AlgorithmSignature for Signature {
32 const LENGTH: Option<NonZeroUsize> = NonZeroUsize::new(COMPACT_SIGNATURE_SIZE);
33
34 fn try_from_slice(slice: &[u8]) -> anyhow::Result<Self> {
35 Signature::from_compact(slice).map_err(Into::into)
36 }
37
38 fn as_bytes(&self) -> Cow<'_, [u8]> {
39 Cow::Owned(self.serialize_compact()[..].to_vec())
40 }
41}
42
43#[derive(Debug)]
50#[cfg_attr(docsrs, doc(cfg(any(feature = "es256k", feature = "k256"))))]
51pub struct Es256k<D = Sha256> {
52 context: Secp256k1<All>,
53 _digest: PhantomData<D>,
54}
55
56impl<D> Default for Es256k<D>
57where
58 D: FixedOutputReset<OutputSize = U32> + BlockSizeUser + Clone + Default + HashMarker,
59{
60 fn default() -> Self {
61 Es256k {
62 context: Secp256k1::new(),
63 _digest: PhantomData,
64 }
65 }
66}
67
68impl<D> Es256k<D>
69where
70 D: FixedOutputReset<OutputSize = U32> + BlockSizeUser + Clone + Default + HashMarker,
71{
72 #[cfg_attr(docsrs, doc(cfg(feature = "es256k")))]
76 pub fn new(context: Secp256k1<All>) -> Self {
77 Es256k {
78 context,
79 _digest: PhantomData,
80 }
81 }
82}
83
84impl<D> Algorithm for Es256k<D>
85where
86 D: FixedOutputReset<OutputSize = U32> + BlockSizeUser + Clone + Default + HashMarker,
87{
88 type SigningKey = SecretKey;
89 type VerifyingKey = PublicKey;
90 type Signature = Signature;
91
92 fn name(&self) -> Cow<'static, str> {
93 Cow::Borrowed("ES256K")
94 }
95
96 fn sign(&self, signing_key: &Self::SigningKey, message: &[u8]) -> Self::Signature {
97 let mut digest = D::default();
98 digest.update(message);
99 let message = Message::from_digest(digest.finalize().into());
100
101 self.context.sign_ecdsa(message, signing_key)
102 }
103
104 fn verify_signature(
105 &self,
106 signature: &Self::Signature,
107 verifying_key: &Self::VerifyingKey,
108 message: &[u8],
109 ) -> bool {
110 let mut digest = D::default();
111 digest.update(message);
112 let message = Message::from_digest(digest.finalize().into());
113
114 let mut normalized_signature = *signature;
119 normalized_signature.normalize_s();
120
121 self.context
122 .verify_ecdsa(message, &normalized_signature, verifying_key)
123 .is_ok()
124 }
125}
126
127impl SigningKey<Es256k> for SecretKey {
130 fn from_slice(raw: &[u8]) -> anyhow::Result<Self> {
131 let raw = <[u8; SECRET_KEY_SIZE]>::try_from(raw)?;
132 Self::from_byte_array(raw).map_err(From::from)
133 }
134
135 fn to_verifying_key(&self) -> PublicKey {
136 static CONTEXT: LazyLock<Secp256k1<All>> = LazyLock::new(Secp256k1::new);
137
138 PublicKey::from_secret_key(&CONTEXT, self)
139 }
140
141 fn as_bytes(&self) -> SecretBytes<'_> {
142 SecretBytes::borrowed(&self[..])
143 }
144}
145
146impl VerifyingKey<Es256k> for PublicKey {
147 fn from_slice(raw: &[u8]) -> anyhow::Result<Self> {
148 Self::from_slice(raw).map_err(From::from)
149 }
150
151 fn as_bytes(&self) -> Cow<'_, [u8]> {
153 Cow::Owned(self.serialize().to_vec())
154 }
155}
156
157fn create_jwk<'a>(pk: &PublicKey, sk: Option<&'a SecretKey>) -> JsonWebKey<'a> {
158 let uncompressed = pk.serialize_uncompressed();
159 JsonWebKey::EllipticCurve {
160 curve: "secp256k1".into(),
161 x: Cow::Owned(uncompressed[1..=COORDINATE_SIZE].to_vec()),
162 y: Cow::Owned(uncompressed[(1 + COORDINATE_SIZE)..].to_vec()),
163 secret: sk.map(|sk| SecretBytes::borrowed(&sk.as_ref()[..])),
164 }
165}
166
167impl<'a> From<&'a PublicKey> for JsonWebKey<'a> {
168 fn from(key: &'a PublicKey) -> JsonWebKey<'a> {
169 create_jwk(key, None)
170 }
171}
172
173impl TryFrom<&JsonWebKey<'_>> for PublicKey {
174 type Error = JwkError;
175
176 fn try_from(jwk: &JsonWebKey<'_>) -> Result<Self, Self::Error> {
177 let JsonWebKey::EllipticCurve { curve, x, y, .. } = jwk else {
178 return Err(JwkError::key_type(jwk, KeyType::EllipticCurve));
179 };
180 JsonWebKey::ensure_curve(curve, "secp256k1")?;
181 JsonWebKey::ensure_len("x", x, COORDINATE_SIZE)?;
182 JsonWebKey::ensure_len("y", y, COORDINATE_SIZE)?;
183
184 let mut key_bytes = [0_u8; UNCOMPRESSED_PUBLIC_KEY_SIZE];
185 key_bytes[0] = 4; key_bytes[1..=COORDINATE_SIZE].copy_from_slice(x);
187 key_bytes[(1 + COORDINATE_SIZE)..].copy_from_slice(y);
188 PublicKey::from_slice(&key_bytes[..]).map_err(JwkError::custom)
189 }
190}
191
192impl<'a> From<&'a SecretKey> for JsonWebKey<'a> {
193 fn from(key: &'a SecretKey) -> JsonWebKey<'a> {
194 create_jwk(&key.to_verifying_key(), Some(key))
195 }
196}
197
198impl TryFrom<&JsonWebKey<'_>> for SecretKey {
199 type Error = JwkError;
200
201 fn try_from(jwk: &JsonWebKey<'_>) -> Result<Self, Self::Error> {
202 let JsonWebKey::EllipticCurve { secret, .. } = jwk else {
203 return Err(JwkError::key_type(jwk, KeyType::EllipticCurve));
204 };
205 let sk_bytes = secret.as_deref();
206 let sk_bytes = sk_bytes.ok_or_else(|| JwkError::NoField("d".into()))?;
207 let sk_bytes: [u8; SECRET_KEY_SIZE] =
208 sk_bytes.try_into().map_err(|_| JwkError::UnexpectedLen {
209 field: "d".to_owned(),
210 expected: SECRET_KEY_SIZE,
211 actual: sk_bytes.len(),
212 })?;
213
214 let sk = SecretKey::from_byte_array(sk_bytes).map_err(JwkError::custom)?;
215 jwk.ensure_key_match(sk)
216 }
217}