Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decrypt keys JWEs with ring directly #95

Merged
merged 2 commits into from Jul 11, 2018
Merged

Decrypt keys JWEs with ring directly #95

merged 2 commits into from Jul 11, 2018

Conversation

@eoger
Copy link
Contributor

@eoger eoger commented Jul 5, 2018

This allows us to get rid of the dependency on jansson and cjose, and unblocks us in pursuing a complete removal of the openssl dependency.
I don't mind dropping the 2nd commit if people feel the ScopedKeysFlow abstraction is confusing.
Many thanks to @linuxwolf for his assistance!

@eoger eoger requested a review from rfk Jul 5, 2018
@eoger eoger changed the title Manual jwe decrypt Decrypt keys JWEs with ring directly Jul 5, 2018
@eoger eoger requested a review from vladikoff Jul 5, 2018
@vladikoff vladikoff requested a review from linuxwolf Jul 5, 2018
Copy link

@linuxwolf linuxwolf left a comment

🎉 very nice!

my comments are suggestions, I don't think they have to be addressed before merging this.

peer_pub_key.extend_from_slice(&y);
let peer_pub_key = Input::from(&peer_pub_key);
let secret = agreement::agree_ephemeral(self.private_key, &agreement::ECDH_P256, peer_pub_key, ring::error::Unspecified, |z| {
// ConcatKDF

This comment has been minimized.

@linuxwolf

linuxwolf Jul 6, 2018

I think it might help future us to have a comment that this is an optimization of ConcatKDF when keyLen <= hashLen.

// ConcatKDF
let counter = 1;
let alg = protected_header["enc"].as_str().unwrap();
let apu = "";

This comment has been minimized.

@linuxwolf

linuxwolf Jul 6, 2018

To be a little more resilient, this ought look for protected_header["apu"] and use "" if it's missing/empty.

let counter = 1;
let alg = protected_header["enc"].as_str().unwrap();
let apu = "";
let apv = "";

This comment has been minimized.

@linuxwolf

linuxwolf Jul 6, 2018

Same as above, but protected_header["apv"].

let mut buf: Vec<u8> = vec![];
buf.extend_from_slice(&to_32b_buf(counter));
buf.extend_from_slice(&z);
buf.extend_from_slice(&to_32b_buf(alg.len() as u32));

This comment has been minimized.

@linuxwolf

linuxwolf Jul 6, 2018

maybe include a comment this is ConcatKDF's "otherinfo" -- again just thinking about future us.

pub fn generate_keys_jwk(&self) -> Result<String> {
let mut pub_key = vec![0u8; self.private_key.public_key_len()];
&self.private_key.compute_public_key(&mut pub_key)?;
// First byte is 4, then 32 bytes for x, and 32 bytes for y.

This comment has been minimized.

@linuxwolf

linuxwolf Jul 6, 2018

This format (0x04 + public.x + public.y) is the "uncompressed representation" -- might be worth a note if there's ever a discrepancy in the future.

@eoger eoger force-pushed the manual-jwe-decrypt branch from a462cf6 to a4e685f Jul 6, 2018
@eoger eoger mentioned this pull request Jul 6, 2018
@rfk
rfk approved these changes Jul 10, 2018
Copy link
Member

@rfk rfk left a comment

This is great, nice work @eoger! I think my main suggestion would be to add more docs and tests - anytime you have what looks like hand-rolled crypto code, it's worth investing in re-assuring the reader that everything is in order.

  • Can we add a comment about why we're doing this ourselves? e.g. because it's a nice code-size win for our very specific needs.
  • Can we provide more description of the formats that we're intending to produce?
  • Are there some existing testcases we can import to make sure things work correctly?
}

impl ScopedKeysFlow {
pub fn with_random_ecdh_key() -> Result<ScopedKeysFlow> {

This comment has been minimized.

@rfk

rfk Jul 10, 2018
Member

nit: is the fact that it's "ecdh" important enough to expose in the name, or would something like "with_random_key" be sufficient?

@@ -497,7 +499,7 @@ mod tests {
}

pub struct OAuthFlow {
pub jwk: Option<JWK>,
pub scoped_keys_flow: Option<ScopedKeysFlow>,

This comment has been minimized.

@rfk

rfk Jul 10, 2018
Member

I like the way this encapsulates the state FWIW.

let mut pub_key = vec![0u8; self.private_key.public_key_len()];
&self.private_key.compute_public_key(&mut pub_key)?;
// Uncompressed representation:
// First byte is 4, then 32 bytes for x, and 32 bytes for y.

This comment has been minimized.

@rfk

rfk Jul 10, 2018
Member

It would be useful to link to external documentation on the format you're encoding into here, for reference.

let x = base64::decode_config(&protected_header["epk"]["x"].as_str().unwrap(), base64::URL_SAFE_NO_PAD)?;
let y = base64::decode_config(&protected_header["epk"]["y"].as_str().unwrap(), base64::URL_SAFE_NO_PAD)?;
let mut peer_pub_key: Vec<u8> = vec![0x04];
peer_pub_key.extend_from_slice(&x);

This comment has been minimized.

@rfk

rfk Jul 10, 2018
Member

Do we know min/max length of x and y here? If so, it may be worth asserting them explicitly as an extra sanity-check.

peer_pub_key.extend_from_slice(&y);
let peer_pub_key = Input::from(&peer_pub_key);
let secret = agreement::agree_ephemeral(self.private_key, &agreement::ECDH_P256, peer_pub_key, ring::error::Unspecified, |z| {
// ConcatKDF (1 iteration since keyLen <= hashLen).

This comment has been minimized.

@rfk

rfk Jul 10, 2018
Member

As above, it's probably worth linking to the relevant parts of the JWA spec, for reference of future readers.

Copy link
Contributor

@vladikoff vladikoff left a comment

Amazing work!! My only wish , just like Ryan already mentioned, some kind of a test that will make it easier for us to maintain this JWE code.

@eoger eoger force-pushed the manual-jwe-decrypt branch 2 times, most recently from 3086a79 to fa6aec8 Jul 10, 2018
@eoger
Copy link
Contributor Author

@eoger eoger commented Jul 10, 2018

Thank you for the reviews everyone, I've added tests that rely on briansmith/ring#674, let's see how it goes on the other side.

#[fail(display = "JOSE error: {}", _0)]
JoseError(#[fail(cause)] SyncFailure<jose::error::Error>),
#[fail(display = "Unspecified ring error: {}", _0)]
UnspecifiedRingError(#[fail(cause)] ring::error::Unspecified),

This comment has been minimized.

@briansmith

briansmith Jul 10, 2018

I don't recommend that people map ring's Unspecified error to another Unspecified error. Instead I would create separate errors, e.g. KeyGenerationFailed, KeyAgreementFailed, and map Unspecified to these based on the context.

@eoger eoger force-pushed the manual-jwe-decrypt branch from fa6aec8 to 6855879 Jul 10, 2018
@eoger eoger force-pushed the manual-jwe-decrypt branch from 6855879 to be1410f Jul 11, 2018
@eoger eoger merged commit 5a471e2 into master Jul 11, 2018
2 checks passed
2 checks passed
continuous-integration/travis-ci/pr The Travis CI build passed
Details
continuous-integration/travis-ci/push The Travis CI build passed
Details
@eoger eoger deleted the manual-jwe-decrypt branch Jul 11, 2018
@eoger
Copy link
Contributor Author

@eoger eoger commented Jul 11, 2018

Thanks everyone for your comments!

@eoger eoger mentioned this pull request Jul 11, 2018
dmose pushed a commit to dmose/application-services that referenced this pull request Mar 15, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

5 participants