Skip to content

Commit d9e4f81

Browse files
committed
Implement ES256K using secp256k1
1 parent 53a3fc2 commit d9e4f81

File tree

8 files changed

+87
-7
lines changed

8 files changed

+87
-7
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ ed25519-dalek = { version = "2.1.1", optional = true, features = ["pkcs8"] }
4040
hmac = { version = "0.12.1", optional = true }
4141
p256 = { version = "0.13.2", optional = true, features = ["ecdsa"] }
4242
p384 = { version = "0.13.0", optional = true, features = ["ecdsa"] }
43+
k256 = { version = "0.13.4", optional = true, features = ["ecdsa"] }
4344
rand = { version = "0.8.5", optional = true, features = ["std"], default-features = false }
4445
rsa = { version = "0.9.6", optional = true }
4546
sha2 = { version = "0.10.7", optional = true, features = ["oid"] }
@@ -66,7 +67,7 @@ criterion = { version = "0.4", default-features = false }
6667
[features]
6768
default = ["use_pem"]
6869
use_pem = ["pem", "simple_asn1"]
69-
rust_crypto = ["ed25519-dalek", "hmac", "p256", "p384", "rand", "rsa", "sha2"]
70+
rust_crypto = ["ed25519-dalek", "hmac", "p256", "p384", "k256", "rand", "rsa", "sha2"]
7071
aws_lc_rs = ["aws-lc-rs"]
7172

7273
[[bench]]

src/algorithms.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ impl AlgorithmFamily {
2525
Algorithm::PS384,
2626
Algorithm::PS512,
2727
],
28-
Self::Ec => &[Algorithm::ES256, Algorithm::ES384],
28+
Self::Ec => &[Algorithm::ES256, Algorithm::ES384, Algorithm::ES256K],
2929
Self::Ed => &[Algorithm::EdDSA],
3030
}
3131
}
@@ -48,6 +48,9 @@ pub enum Algorithm {
4848
/// ECDSA using SHA-384
4949
ES384,
5050

51+
/// ECDSA using secp256k1
52+
ES256K,
53+
5154
/// RSASSA-PKCS1-v1_5 using SHA-256
5255
RS256,
5356
/// RSASSA-PKCS1-v1_5 using SHA-384
@@ -74,6 +77,7 @@ impl FromStr for Algorithm {
7477
"HS384" => Ok(Algorithm::HS384),
7578
"HS512" => Ok(Algorithm::HS512),
7679
"ES256" => Ok(Algorithm::ES256),
80+
"ES256K" => Ok(Algorithm::ES256K),
7781
"ES384" => Ok(Algorithm::ES384),
7882
"RS256" => Ok(Algorithm::RS256),
7983
"RS384" => Ok(Algorithm::RS384),
@@ -97,7 +101,7 @@ impl Algorithm {
97101
| Algorithm::PS256
98102
| Algorithm::PS384
99103
| Algorithm::PS512 => AlgorithmFamily::Rsa,
100-
Algorithm::ES256 | Algorithm::ES384 => AlgorithmFamily::Ec,
104+
Algorithm::ES256 | Algorithm::ES384 | Algorithm::ES256K => AlgorithmFamily::Ec,
101105
Algorithm::EdDSA => AlgorithmFamily::Ed,
102106
}
103107
}
@@ -112,6 +116,7 @@ mod tests {
112116
#[test]
113117
#[wasm_bindgen_test]
114118
fn generate_algorithm_enum_from_str() {
119+
assert!(Algorithm::from_str("ES256K").is_ok());
115120
assert!(Algorithm::from_str("HS256").is_ok());
116121
assert!(Algorithm::from_str("HS384").is_ok());
117122
assert!(Algorithm::from_str("HS512").is_ok());

src/crypto/rust_crypto/ecdsa.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ use p256::ecdsa::{
1111
use p384::ecdsa::{
1212
Signature as Signature384, SigningKey as SigningKey384, VerifyingKey as VerifyingKey384,
1313
};
14+
use k256::ecdsa::{
15+
Signature as Signature256K, SigningKey as SigningKey256K, VerifyingKey as VerifyingKey256K
16+
};
1417
use rsa::pkcs8::DecodePrivateKey;
1518
use signature::{Error, Signer, Verifier};
1619

@@ -82,6 +85,8 @@ macro_rules! define_ecdsa_verifier {
8285

8386
define_ecdsa_signer!(Es256Signer, Algorithm::ES256, SigningKey256);
8487
define_ecdsa_signer!(Es384Signer, Algorithm::ES384, SigningKey384);
88+
define_ecdsa_signer!(Es256KSigner, Algorithm::ES256K, SigningKey256K);
8589

8690
define_ecdsa_verifier!(Es256Verifier, Algorithm::ES256, VerifyingKey256, Signature256);
8791
define_ecdsa_verifier!(Es384Verifier, Algorithm::ES384, VerifyingKey384, Signature384);
92+
define_ecdsa_verifier!(Es256KVerifier, Algorithm::ES256K, VerifyingKey256K, Signature256K);

src/decoding.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use crate::crypto::aws_lc::{
2626
};
2727
#[cfg(feature = "rust_crypto")]
2828
use crate::crypto::rust_crypto::{
29-
ecdsa::{Es256Verifier, Es384Verifier},
29+
ecdsa::{Es256Verifier, Es384Verifier, Es256KVerifier},
3030
eddsa::EdDSAVerifier,
3131
hmac::{Hs256Verifier, Hs384Verifier, Hs512Verifier},
3232
rsa::{
@@ -326,6 +326,7 @@ pub fn jwt_verifier_factory(
326326
Algorithm::HS512 => Box::new(Hs512Verifier::new(key)?) as Box<dyn JwtVerifier>,
327327
Algorithm::ES256 => Box::new(Es256Verifier::new(key)?) as Box<dyn JwtVerifier>,
328328
Algorithm::ES384 => Box::new(Es384Verifier::new(key)?) as Box<dyn JwtVerifier>,
329+
Algorithm::ES256K => Box::new(Es256KVerifier::new(key)?) as Box<dyn JwtVerifier>,
329330
Algorithm::RS256 => Box::new(Rsa256Verifier::new(key)?) as Box<dyn JwtVerifier>,
330331
Algorithm::RS384 => Box::new(Rsa384Verifier::new(key)?) as Box<dyn JwtVerifier>,
331332
Algorithm::RS512 => Box::new(Rsa512Verifier::new(key)?) as Box<dyn JwtVerifier>,

src/encoding.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use crate::crypto::aws_lc::{
2626
};
2727
#[cfg(feature = "rust_crypto")]
2828
use crate::crypto::rust_crypto::{
29-
ecdsa::{Es256Signer, Es384Signer},
29+
ecdsa::{Es256Signer, Es384Signer, Es256KSigner},
3030
eddsa::EdDSASigner,
3131
hmac::{Hs256Signer, Hs384Signer, Hs512Signer},
3232
rsa::{
@@ -202,6 +202,7 @@ pub(crate) fn jwt_signer_factory(
202202
Algorithm::HS512 => Box::new(Hs512Signer::new(key)?) as Box<dyn JwtSigner>,
203203
Algorithm::ES256 => Box::new(Es256Signer::new(key)?) as Box<dyn JwtSigner>,
204204
Algorithm::ES384 => Box::new(Es384Signer::new(key)?) as Box<dyn JwtSigner>,
205+
Algorithm::ES256K => Box::new(Es256KSigner::new(key)?) as Box<dyn JwtSigner>,
205206
Algorithm::RS256 => Box::new(Rsa256Signer::new(key)?) as Box<dyn JwtSigner>,
206207
Algorithm::RS384 => Box::new(Rsa384Signer::new(key)?) as Box<dyn JwtSigner>,
207208
Algorithm::RS512 => Box::new(Rsa512Signer::new(key)?) as Box<dyn JwtSigner>,

src/jwk.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ use p256::{ecdsa::SigningKey as P256SigningKey, pkcs8::DecodePrivateKey};
2323
#[cfg(feature = "rust_crypto")]
2424
use p384::ecdsa::SigningKey as P384SigningKey;
2525
#[cfg(feature = "rust_crypto")]
26+
use k256::ecdsa::SigningKey as K256SigningKey;
27+
#[cfg(feature = "rust_crypto")]
2628
use rsa::{RsaPrivateKey, pkcs1::DecodeRsaPrivateKey, traits::PublicKeyParts};
2729
#[cfg(feature = "rust_crypto")]
2830
use sha2::{Digest, Sha256, Sha384, Sha512};
@@ -177,6 +179,9 @@ pub enum KeyAlgorithm {
177179
/// ECDSA using SHA-384
178180
ES384,
179181

182+
/// ECDSA using secp256k
183+
ES256K,
184+
180185
/// RSASSA-PKCS1-v1_5 using SHA-256
181186
RS256,
182187
/// RSASSA-PKCS1-v1_5 using SHA-384
@@ -219,6 +224,7 @@ impl FromStr for KeyAlgorithm {
219224
"HS512" => Ok(KeyAlgorithm::HS512),
220225
"ES256" => Ok(KeyAlgorithm::ES256),
221226
"ES384" => Ok(KeyAlgorithm::ES384),
227+
"ES256K" => Ok(KeyAlgorithm::ES256K),
222228
"RS256" => Ok(KeyAlgorithm::RS256),
223229
"RS384" => Ok(KeyAlgorithm::RS384),
224230
"PS256" => Ok(KeyAlgorithm::PS256),
@@ -319,6 +325,9 @@ pub enum EllipticCurve {
319325
/// P-521 curve -- unsupported by `ring`.
320326
#[serde(rename = "P-521")]
321327
P521,
328+
/// K-256 curve
329+
#[serde(rename = "secp256k1")]
330+
Secp256k1,
322331
/// Ed25519 curve
323332
#[serde(rename = "Ed25519")]
324333
Ed25519,
@@ -501,6 +510,18 @@ fn extract_ec_public_key_coordinates(
501510
_ => Err(ErrorKind::InvalidEcdsaKey.into()),
502511
}
503512
}
513+
Algorithm::ES256K => {
514+
let signing_key = K256SigningKey::from_pkcs8_der(key_content)
515+
.map_err(|_| ErrorKind::InvalidEcdsaKey)?;
516+
let public_key = signing_key.verifying_key();
517+
let encoded = public_key.to_encoded_point(false);
518+
match encoded.coordinates() {
519+
k256::elliptic_curve::sec1::Coordinates::Uncompressed { x, y } => {
520+
Ok((EllipticCurve::Secp256k1, x.to_vec(), y.to_vec()))
521+
}
522+
_ => Err(ErrorKind::InvalidEcdsaKey.into())
523+
}
524+
}
504525
Algorithm::ES384 => {
505526
let signing_key = P384SigningKey::from_pkcs8_der(key_content)
506527
.map_err(|_| ErrorKind::InvalidEcdsaKey)?;
@@ -553,6 +574,7 @@ impl Jwk {
553574
Algorithm::HS512 => KeyAlgorithm::HS512,
554575
Algorithm::ES256 => KeyAlgorithm::ES256,
555576
Algorithm::ES384 => KeyAlgorithm::ES384,
577+
Algorithm::ES256K => KeyAlgorithm::ES256K,
556578
Algorithm::RS256 => KeyAlgorithm::RS256,
557579
Algorithm::RS384 => KeyAlgorithm::RS384,
558580
Algorithm::RS512 => KeyAlgorithm::RS512,
@@ -600,7 +622,7 @@ impl Jwk {
600622
pub fn thumbprint(&self, hash_function: ThumbprintHash) -> String {
601623
let pre = match &self.algorithm {
602624
AlgorithmParameters::EllipticCurve(a) => match a.curve {
603-
EllipticCurve::P256 | EllipticCurve::P384 | EllipticCurve::P521 => {
625+
EllipticCurve::P256 | EllipticCurve::P384 | EllipticCurve::P521 | EllipticCurve::Secp256k1 => {
604626
format!(
605627
r#"{{"crv":{},"kty":{},"x":"{}","y":"{}"}}"#,
606628
serde_json::to_string(&a.curve).unwrap(),
@@ -627,7 +649,7 @@ impl Jwk {
627649
)
628650
}
629651
AlgorithmParameters::OctetKeyPair(a) => match a.curve {
630-
EllipticCurve::P256 | EllipticCurve::P384 | EllipticCurve::P521 => {
652+
EllipticCurve::P256 | EllipticCurve::P384 | EllipticCurve::P521 | EllipticCurve::Secp256k1 => {
631653
panic!("OctetKeyPair can't contain this curve type")
632654
}
633655
EllipticCurve::Ed25519 => {

tests/ecdsa/mod.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,46 @@ fn ec_x_y() {
103103
assert!(res.is_ok());
104104
}
105105

106+
#[cfg(feature = "use_pem")]
107+
#[test]
108+
#[wasm_bindgen_test]
109+
fn es256k() {
110+
use jsonwebtoken::jwk::Jwk;
111+
use k256::schnorr::SigningKey;
112+
use serde_json::json;
113+
114+
// Private key errors out with invalid format or something?
115+
let privkey = include_str!("private_es256k_key.pem");
116+
let my_claims = Claims {
117+
sub: "[email protected]".to_string(),
118+
company: "ACME".to_string(),
119+
exp: OffsetDateTime::now_utc().unix_timestamp() + 10000,
120+
};
121+
122+
let jwk: Jwk = serde_json::from_value(json!({
123+
"kty": "EC",
124+
"x": "PIfJJc3sLXFLWsO6M-Hu7EUpdR56biBrImow8kA4SzY",
125+
"y": "iwxBRgkMP8Pi9cBtINdqictnRuswTOzYiwS4l53x9Rk",
126+
"crv": "secp256k1",
127+
"kid": "ec01",
128+
"alg": "ES256K",
129+
"use": "sig"
130+
})).unwrap();
131+
132+
let encrypted = encode(
133+
&Header::new(Algorithm::ES256K),
134+
&my_claims,
135+
&EncodingKey::from_ec_pem(privkey.as_ref()).unwrap(),
136+
)
137+
.unwrap();
138+
let res = decode::<Claims>(
139+
&encrypted,
140+
&DecodingKey::from_jwk(&jwk).unwrap(),
141+
&Validation::new(Algorithm::ES256),
142+
);
143+
assert!(res.is_ok());
144+
}
145+
106146
#[cfg(feature = "use_pem")]
107147
#[test]
108148
#[wasm_bindgen_test]

tests/ecdsa/private_es256k_key.pem

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-----BEGIN EC PRIVATE KEY-----
2+
MHQCAQEEIGNBj1dX7BBxpmxSx/lhf6BN/lNwEwZjeU0FUNFbQPs6oAcGBSuBBAAK
3+
oUQDQgAEPIfJJc3sLXFLWsO6M+Hu7EUpdR56biBrImow8kA4SzaLDEFGCQw/w+L1
4+
wG0g12qJy2dG6zBM7NiLBLiXnfH1GQ==
5+
-----END EC PRIVATE KEY-----

0 commit comments

Comments
 (0)