Skip to content

Commit e1c1ade

Browse files
valkryptonAli Tariq
authored andcommitted
implement jwa algorithms
1 parent 766bbc6 commit e1c1ade

File tree

4 files changed

+255
-5
lines changed

4 files changed

+255
-5
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rama-crypto/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ rustls-pki-types = { workspace = true }
3030
serde = { workspace = true }
3131
serde_json = { workspace = true }
3232
x509-parser = { workspace = true }
33+
byteorder = { workspace = true }
3334

3435
[dev-dependencies]
3536
tokio = { workspace = true, features = ["full"] }

rama-crypto/src/jose/jwa.rs

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
11
use std::ops::Deref;
22

33
use aws_lc_rs::signature::{
4-
ECDSA_P256_SHA256_FIXED_SIGNING, ECDSA_P384_SHA384_FIXED_SIGNING, EcdsaSigningAlgorithm,
5-
EcdsaVerificationAlgorithm,
4+
RSA_PKCS1_2048_8192_SHA256, RSA_PKCS1_2048_8192_SHA384, RSA_PKCS1_2048_8192_SHA512,
5+
RSA_PSS_2048_8192_SHA256, RSA_PSS_2048_8192_SHA384, RSA_PSS_2048_8192_SHA512, RsaParameters,
6+
VerificationAlgorithm,
7+
};
8+
use aws_lc_rs::{
9+
hmac,
10+
hmac::{HMAC_SHA256, HMAC_SHA384, HMAC_SHA512},
11+
signature,
12+
signature::{
13+
ECDSA_P256_SHA256_FIXED_SIGNING, ECDSA_P384_SHA384_FIXED_SIGNING, EcdsaSigningAlgorithm,
14+
EcdsaVerificationAlgorithm, RSA_PKCS1_SHA256, RSA_PKCS1_SHA384, RSA_PKCS1_SHA512,
15+
RSA_PSS_SHA256, RSA_PSS_SHA384, RSA_PSS_SHA512, RsaEncoding,
16+
},
617
};
718
use rama_core::error::OpaqueError;
819
use serde::{Deserialize, Serialize};
@@ -99,3 +110,58 @@ impl TryFrom<JWA> for &'static EcdsaVerificationAlgorithm {
99110
Ok(signing_algo.deref())
100111
}
101112
}
113+
114+
impl TryFrom<JWA> for &'static hmac::Algorithm {
115+
type Error = OpaqueError;
116+
117+
fn try_from(value: JWA) -> Result<Self, Self::Error> {
118+
match value {
119+
JWA::HS256 => Ok(&HMAC_SHA256),
120+
JWA::HS384 => Ok(&HMAC_SHA384),
121+
JWA::HS512 => Ok(&HMAC_SHA512),
122+
_ => Err(OpaqueError::from_display(
123+
"Non-Hmac algorithm cannot be converted to hmac types",
124+
)),
125+
}
126+
}
127+
}
128+
129+
impl TryFrom<JWA> for &'static dyn RsaEncoding {
130+
type Error = OpaqueError;
131+
132+
fn try_from(value: JWA) -> Result<Self, Self::Error> {
133+
match value {
134+
JWA::RS256 => Ok(&RSA_PKCS1_SHA256),
135+
JWA::RS384 => Ok(&RSA_PKCS1_SHA384),
136+
JWA::RS512 => Ok(&RSA_PKCS1_SHA512),
137+
JWA::PS256 => Ok(&RSA_PSS_SHA256),
138+
JWA::PS384 => Ok(&RSA_PSS_SHA384),
139+
JWA::PS512 => Ok(&RSA_PSS_SHA512),
140+
_ => Err(OpaqueError::from_display(
141+
"Non-RSA algorithm cannot be converted to rsa types",
142+
)),
143+
}
144+
}
145+
}
146+
147+
impl TryFrom<JWA> for &'static dyn VerificationAlgorithm {
148+
type Error = OpaqueError;
149+
150+
fn try_from(value: JWA) -> Result<Self, Self::Error> {
151+
match value {
152+
JWA::RS256 => Ok(&RSA_PKCS1_2048_8192_SHA256),
153+
JWA::RS384 => Ok(&RSA_PKCS1_2048_8192_SHA384),
154+
JWA::RS512 => Ok(&RSA_PKCS1_2048_8192_SHA512),
155+
JWA::PS256 => Ok(&RSA_PSS_2048_8192_SHA256),
156+
JWA::PS384 => Ok(&RSA_PSS_2048_8192_SHA384),
157+
JWA::PS512 => Ok(&RSA_PSS_2048_8192_SHA512),
158+
JWA::ES256 | JWA::ES384 | JWA::ES512 => {
159+
let signing_algo: &'static EcdsaSigningAlgorithm = value.try_into()?;
160+
Ok(signing_algo.deref())
161+
}
162+
_ => Err(OpaqueError::from_display(
163+
"Verification algorithm is not supported",
164+
)),
165+
}
166+
}
167+
}

rama-crypto/src/jose/jwk.rs

Lines changed: 185 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
use aws_lc_rs::encoding::{AsDer, Pkcs8V1Der};
2+
use aws_lc_rs::rsa::KeySize;
3+
use aws_lc_rs::signature::{RsaEncoding, RsaKeyPair, RsaParameters};
14
use aws_lc_rs::{
25
digest::{Digest, SHA256, digest},
36
pkcs8::Document,
@@ -156,7 +159,51 @@ impl JWK {
156159
&self,
157160
) -> Result<signature::UnparsedPublicKey<Vec<u8>>, OpaqueError> {
158161
match &self.key_type {
159-
JWKType::RSA { .. } => Err(OpaqueError::from_display("currently not supported")),
162+
JWKType::RSA { n, e } => {
163+
let n_bytes = BASE64_URL_SAFE_NO_PAD
164+
.decode(n)
165+
.context("decode RSA modulus (n)")?;
166+
let e_bytes = BASE64_URL_SAFE_NO_PAD
167+
.decode(e)
168+
.context("decode RSA exponent (e)")?;
169+
let n_der_encoded = utils::encode_integer(n_bytes);
170+
let e_der_encoded = utils::encode_integer(e_bytes);
171+
172+
let mut rsa_public_key_sequence = Vec::new();
173+
rsa_public_key_sequence.extend_from_slice(&n_der_encoded);
174+
rsa_public_key_sequence.extend_from_slice(&e_der_encoded);
175+
176+
let rsa_key_len = utils::encode_der_length(rsa_public_key_sequence.len());
177+
let mut rsa_key_der = vec![0x30];
178+
rsa_key_der.extend_from_slice(&rsa_key_len);
179+
rsa_key_der.extend_from_slice(&rsa_public_key_sequence);
180+
181+
let mut bit_string_payload = vec![0x00];
182+
bit_string_payload.extend_from_slice(&rsa_key_der);
183+
let bit_string_len = utils::encode_der_length(bit_string_payload.len());
184+
let mut bit_string = vec![0x03];
185+
bit_string.extend_from_slice(&bit_string_len);
186+
bit_string.extend_from_slice(&bit_string_payload);
187+
188+
let algorithm_identifier = [
189+
0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
190+
0x05, 0x00,
191+
];
192+
193+
let mut final_sequence = Vec::new();
194+
final_sequence.extend_from_slice(&algorithm_identifier);
195+
final_sequence.extend_from_slice(&bit_string);
196+
197+
let final_len = utils::encode_der_length(final_sequence.len());
198+
let mut result = vec![0x30];
199+
result.extend_from_slice(&final_len);
200+
result.extend_from_slice(&final_sequence);
201+
202+
Ok(signature::UnparsedPublicKey::new(
203+
self.alg.try_into()?,
204+
result,
205+
))
206+
}
160207
JWKType::OCT { .. } => Err(OpaqueError::from_display(
161208
"Symmetric key cannot be converted to public key",
162209
)),
@@ -180,6 +227,27 @@ impl JWK {
180227
}
181228
}
182229
}
230+
231+
/// Creates a new [`JWK`] from a given [`RSAKeyPair`]
232+
pub fn new_from_rsa_key_pair(rsa_key_pair: &RsaKeyPair, alg: JWA) -> Result<Self, OpaqueError> {
233+
let n = rsa_key_pair.public_key().modulus();
234+
let e = rsa_key_pair.public_key().exponent();
235+
Ok(Self {
236+
alg,
237+
key_type: JWKType::RSA {
238+
n: String::try_from(n.big_endian_without_leading_zero().to_vec())
239+
.map_err(|e| OpaqueError::from_display(format!("{:?}", e)))?,
240+
e: String::try_from(e.big_endian_without_leading_zero().to_vec())
241+
.map_err(|e| OpaqueError::from_display(format!("{:?}", e)))?,
242+
},
243+
244+
r#use: Some(JWKUse::Signature),
245+
key_ops: None,
246+
x5c: None,
247+
x5t: None,
248+
x5t_sha256: None,
249+
})
250+
}
183251
}
184252

185253
/// [`EcdsaKey`] which is used to identify and authenticate our requests
@@ -252,7 +320,7 @@ impl EcdsaKey {
252320
}
253321

254322
#[derive(Serialize)]
255-
struct EcdsaKeySigningHeaders<'a> {
323+
struct SigningHeaders<'a> {
256324
alg: JWA,
257325
jwk: &'a JWK,
258326
}
@@ -267,7 +335,7 @@ impl Signer for EcdsaKey {
267335
_unprotected_headers: &mut super::jws::Headers,
268336
) -> Result<(), Self::Error> {
269337
let jwk = self.create_jwk();
270-
protected_headers.try_set_headers(EcdsaKeySigningHeaders {
338+
protected_headers.try_set_headers(SigningHeaders {
271339
alg: jwk.alg,
272340
jwk: &jwk,
273341
})?;
@@ -284,6 +352,120 @@ impl Signer for EcdsaKey {
284352
}
285353
}
286354

355+
pub struct RsaKey {
356+
rng: SystemRandom,
357+
alg: JWA,
358+
inner: RsaKeyPair,
359+
}
360+
361+
impl RsaKey {
362+
/// Create a new [`RsaKey`] from the given [`RsaKeyPairK`]
363+
pub fn new(key_pair: RsaKeyPair, alg: JWA, rng: SystemRandom) -> Result<Self, OpaqueError> {
364+
Ok(Self {
365+
rng,
366+
alg,
367+
inner: key_pair,
368+
})
369+
}
370+
371+
/// Generate a new [`RsaKey`] from a newly generated [`RsaKeyPair`]
372+
pub fn generate(key_size: KeySize) -> Result<Self, OpaqueError> {
373+
let key_pair = RsaKeyPair::generate(key_size).context("error generating rsa key pair")?;
374+
375+
Self::new(key_pair, JWA::RS256, SystemRandom::new())
376+
}
377+
378+
/// Generate a new [`RsaKey`] from the given pkcs8 der
379+
pub fn from_pkcs8_der(
380+
pkcs8_der: &[u8],
381+
alg: JWA,
382+
rng: SystemRandom,
383+
) -> Result<Self, OpaqueError> {
384+
let key_pair = RsaKeyPair::from_pkcs8(pkcs8_der).context("create RSAKeyPair from pkcs8")?;
385+
386+
Self::new(key_pair, alg, rng)
387+
}
388+
389+
/// Create pkcs8 der for the current [`RsaKeyPair`]
390+
pub fn pkcs8_der(&self) -> Result<(JWA, Pkcs8V1Der<'_>), OpaqueError> {
391+
let doc = self
392+
.inner
393+
.as_der()
394+
.context("error creating pkcs8 der from rsa keypair")?;
395+
Ok((self.alg, doc))
396+
}
397+
398+
/// Create a [`JWK`] for this [`RsaKey`]
399+
#[must_use]
400+
pub fn create_jwk(&self) -> JWK {
401+
JWK::new_from_rsa_key_pair(&self.inner, self.alg)
402+
.expect("error creating jwa from rsa keypair")
403+
}
404+
405+
#[must_use]
406+
pub fn rng(&self) -> &SystemRandom {
407+
&self.rng
408+
}
409+
}
410+
411+
impl Signer for RsaKey {
412+
type Signature = Vec<u8>;
413+
type Error = OpaqueError;
414+
415+
fn set_headers(
416+
&self,
417+
protected_headers: &mut super::jws::Headers,
418+
_unprotected_headers: &mut super::jws::Headers,
419+
) -> Result<(), Self::Error> {
420+
let jwk = self.create_jwk();
421+
protected_headers.try_set_headers(SigningHeaders {
422+
alg: jwk.alg,
423+
jwk: &jwk,
424+
})?;
425+
Ok(())
426+
}
427+
428+
fn sign(&self, data: &str) -> Result<Self::Signature, Self::Error> {
429+
let mut sig: Vec<u8> = Vec::new();
430+
let alg = <&'static dyn RsaEncoding as TryFrom<JWA>>::try_from(self.alg)?;
431+
self.inner
432+
.sign(alg, self.rng(), data.as_bytes(), &mut sig)
433+
.context("sign protected data")?;
434+
Ok(sig)
435+
}
436+
}
437+
438+
mod utils {
439+
pub(super) fn encode_der_length(len: usize) -> Vec<u8> {
440+
if len < 128 {
441+
vec![len as u8]
442+
} else {
443+
let mut len_bytes = len.to_be_bytes().to_vec();
444+
while len_bytes[0] == 0 {
445+
len_bytes.remove(0);
446+
}
447+
let first_byte = 0x80 | len_bytes.len() as u8;
448+
let mut result = vec![first_byte];
449+
result.extend_from_slice(&len_bytes);
450+
result
451+
}
452+
}
453+
454+
pub(super) fn encode_integer(mut bytes: Vec<u8>) -> Vec<u8> {
455+
let needs_leading_zero = bytes[0] & 0x80 != 0;
456+
let value_len = bytes.len() + needs_leading_zero as usize;
457+
let len_bytes = encode_der_length(value_len);
458+
let mut result = Vec::with_capacity(1 + len_bytes.len() + value_len);
459+
result.push(0x02);
460+
result.extend_from_slice(&len_bytes);
461+
if needs_leading_zero {
462+
result.push(0);
463+
}
464+
result.extend(bytes.into_iter());
465+
result
466+
}
467+
}
468+
287469
#[cfg(test)]
288470
mod tests {
289471
use super::*;

0 commit comments

Comments
 (0)