Skip to content

Commit 8bfdb48

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

File tree

5 files changed

+277
-6
lines changed

5 files changed

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

rama-crypto/src/jose/jwk.rs

Lines changed: 207 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::RsaKeyPair;
14
use aws_lc_rs::{
25
digest::{Digest, SHA256, digest},
36
pkcs8::Document,
@@ -156,7 +159,60 @@ 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 =
173+
Vec::with_capacity(n_der_encoded.len() + e_der_encoded.len());
174+
rsa_public_key_sequence.extend(n_der_encoded);
175+
rsa_public_key_sequence.extend(e_der_encoded);
176+
177+
let rsa_key_len = utils::encode_der_length(rsa_public_key_sequence.len());
178+
let mut rsa_key_der =
179+
Vec::with_capacity(1 + rsa_key_len.len() + rsa_public_key_sequence.len());
180+
rsa_key_der.push(0x30);
181+
rsa_key_der.extend(rsa_key_len);
182+
rsa_key_der.extend(rsa_public_key_sequence);
183+
184+
let mut bit_string_payload = Vec::with_capacity(1 + rsa_key_der.len());
185+
bit_string_payload.push(0x00);
186+
bit_string_payload.extend(rsa_key_der);
187+
let bit_string_len = utils::encode_der_length(bit_string_payload.len());
188+
let mut bit_string =
189+
Vec::with_capacity(1 + bit_string_len.len() + bit_string_payload.len());
190+
bit_string.push(0x03);
191+
bit_string.extend(bit_string_len);
192+
bit_string.extend(bit_string_payload);
193+
194+
let algorithm_identifier = [
195+
0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
196+
0x05, 0x00,
197+
];
198+
199+
let mut final_sequence =
200+
Vec::with_capacity(algorithm_identifier.len() + bit_string.len());
201+
final_sequence.extend(algorithm_identifier);
202+
final_sequence.extend(bit_string);
203+
204+
let final_sequence_len = utils::encode_der_length(final_sequence.len());
205+
let mut result =
206+
Vec::with_capacity(final_sequence_len.len() + final_sequence.len());
207+
result.push(0x30);
208+
result.extend(final_sequence_len);
209+
result.extend(final_sequence);
210+
211+
Ok(signature::UnparsedPublicKey::new(
212+
self.alg.try_into()?,
213+
result,
214+
))
215+
}
160216
JWKType::OCT { .. } => Err(OpaqueError::from_display(
161217
"Symmetric key cannot be converted to public key",
162218
)),
@@ -180,6 +236,25 @@ impl JWK {
180236
}
181237
}
182238
}
239+
240+
/// Creates a new [`JWK`] from a given [`RSAKeyPair`]
241+
pub fn new_from_rsa_key_pair(rsa_key_pair: &RsaKeyPair, alg: JWA) -> Result<Self, OpaqueError> {
242+
let n = rsa_key_pair.public_key().modulus();
243+
let e = rsa_key_pair.public_key().exponent();
244+
Ok(Self {
245+
alg,
246+
key_type: JWKType::RSA {
247+
n: BASE64_URL_SAFE_NO_PAD.encode(n.big_endian_without_leading_zero()),
248+
e: BASE64_URL_SAFE_NO_PAD.encode(e.big_endian_without_leading_zero()),
249+
},
250+
251+
r#use: Some(JWKUse::Signature),
252+
key_ops: None,
253+
x5c: None,
254+
x5t: None,
255+
x5t_sha256: None,
256+
})
257+
}
183258
}
184259

185260
/// [`EcdsaKey`] which is used to identify and authenticate our requests
@@ -252,7 +327,7 @@ impl EcdsaKey {
252327
}
253328

254329
#[derive(Serialize)]
255-
struct EcdsaKeySigningHeaders<'a> {
330+
struct SigningHeaders<'a> {
256331
alg: JWA,
257332
jwk: &'a JWK,
258333
}
@@ -267,7 +342,7 @@ impl Signer for EcdsaKey {
267342
_unprotected_headers: &mut super::jws::Headers,
268343
) -> Result<(), Self::Error> {
269344
let jwk = self.create_jwk();
270-
protected_headers.try_set_headers(EcdsaKeySigningHeaders {
345+
protected_headers.try_set_headers(SigningHeaders {
271346
alg: jwk.alg,
272347
jwk: &jwk,
273348
})?;
@@ -284,6 +359,123 @@ impl Signer for EcdsaKey {
284359
}
285360
}
286361

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

323515
assert_eq!(key.create_jwk(), recreated_key.create_jwk())
324516
}
517+
518+
#[test]
519+
fn generate_jwk_from_rsa_key_pair() {
520+
let rsa_key_pair = RsaKey::generate(KeySize::Rsa4096).unwrap();
521+
let jwk = JWK::new_from_rsa_key_pair(&rsa_key_pair.inner, JWA::PS512).unwrap();
522+
let (n, e) = match jwk.key_type {
523+
JWKType::RSA { n, e } => (n, e),
524+
_ => panic!("JWK type not RSA"),
525+
};
526+
assert!(BASE64_URL_SAFE_NO_PAD.decode(n).is_ok());
527+
assert!(BASE64_URL_SAFE_NO_PAD.decode(e).is_ok());
528+
}
325529
}

rama-crypto/src/jose/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ mod jwa;
3333
pub use jwa::JWA;
3434

3535
mod jwk;
36-
pub use jwk::{EcdsaKey, JWK, JWKEllipticCurves, JWKType, JWKUse};
36+
pub use jwk::{EcdsaKey, JWK, JWKEllipticCurves, JWKType, JWKUse, RsaKey};
3737

3838
mod jws;
3939
pub use jws::{

0 commit comments

Comments
 (0)