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

feat(crypto): support importKey in SPKI format #12921

Merged
merged 21 commits into from
Dec 9, 2021

Conversation

yacinehmito
Copy link
Contributor

Work towards #11690.

This is a good milestone. Now, there is a key format that can be both imported and exported for RSA. This wasn't the case before, and it was a real blocker to implement an authorization server in Deno.

@yacinehmito
Copy link
Contributor Author

cc @littledivy @lucacasonato

@yacinehmito
Copy link
Contributor Author

There was a lot of copy/pasting involved. Shall I refactor and introduce a more generic function for key imports, at least for RSA?

@yacinehmito
Copy link
Contributor Author

yacinehmito commented Nov 28, 2021

I'm investigating why the Web platform tests are passing locally but failing in the CI.

@yacinehmito
Copy link
Contributor Author

Thank you @littledivy. I fixed the code according to you suggestions.

I found that the wpt were not passing because the public keys were deserialized as if they were private keys.

There was also a couple of unexpected errors in the wpt due to checks on the emptiness of keyUsages, which is not part of the spec. I removed them.

I am only left with fixing some web platform tests related to decryption using RSA-OAEP with a non-empty label.
I don't exactly understand why it fails, as the error is generated inside the RSA library, and only gives "decryption error". The current implementation seems correct, but my nose tells me that it might be related to the encoding of the label. I'm not sure though, and I feel kind of stuck, so any help would be appreciated here.

@yacinehmito
Copy link
Contributor Author

I also need to change the implementation of generateKey.
At the moment, Deno assumes that the key store contains PKCS1 private keys, from which the public key is derived.
However, when importing, the user only provides the public key. Therefore, we need to differentiate between these two cases in the key store.

@yacinehmito
Copy link
Contributor Author

yacinehmito commented Nov 29, 2021

At the moment, Deno assumes that the key store contains PKCS1 private keys, from which the public key is derived.
However, when importing, the user only provides the public key. Therefore, we need to differentiate between these two cases in the key store.

This has been implemented by adding a kind property to the key in the key store, then matching against it when performing operations using RSA keys. Effectively, an RSA key pair is represented only by the private key in the store, if it has been created using generateKey.
I originally wanted to have generateKey return both the private and public keys pkcs1 buffers, but I don't know how to do that with a Deno op.

Also, I noticed that we always put the type raw, and that it is not used on the Rust side. @littledivy Shall I remove the type property?

I am only left with fixing some web platform tests related to decryption using RSA-OAEP with a non-empty label.
I don't exactly understand why it fails, as the error is generated inside the RSA library, and only gives "decryption error".

It was a simple issue. The label was not passed to the OP. It is fixed.

Also, I took this opportunity to factor common code across all methods that copy buffers into new Uint8Arrays.

The PR is ready for review @littledivy @bartlomieju.

@littledivy
Copy link
Member

Also, I noticed that we always put the type raw, and that it is not used on the Rust side. @littledivy Shall remove the type property?

Good catch, sgtm. (I think I added that to be used later on but I can't recall)

@yacinehmito yacinehmito marked this pull request as ready for review November 30, 2021 10:14
Copy link
Contributor

@bnoordhuis bnoordhuis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Rust side of things mostly LGTM. Bartek or Luca are probably more qualified to review the JS part. (The code itself looks okay but I can't really judge whether it's up to spec.)

Minor aside (and cc @littledivy): ext/crypto/lib.rs calls custom_error("Foo", "some string".to_string()) a lot but that's inefficient because it allocates the string on the heap. You can just custom_error("Foo", "some string").

Copy link
Member

@littledivy littledivy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@yacinehmito
Copy link
Contributor Author

Minor aside (and cc @littledivy): ext/crypto/lib.rs calls custom_error("Foo", "some string".to_string()) a lot but that's inefficient because it allocates the string on the heap. You can just custom_error("Foo", "some string").

I'll submit another PR that changes this.

@yacinehmito
Copy link
Contributor Author

I originally wanted to have generateKey return both the private and public keys pkcs1 buffers, but I don't know how to do that with a Deno op.

I managed to find a way, but I can't replicate it with ECDSA as ring doesn't really let us generate the public key and private key already separated. It only provides a pkcs8-formatted buffer, which would then need to be parsed again to get the raw keys.
RustCrypto can help as their implementation doesn't have the same limitations, but it doesn't support P384 natively.
I'll try an implementation in a future PR.

@bartlomieju bartlomieju added this to the 1.17.0 milestone Dec 6, 2021
Copy link
Member

@bartlomieju bartlomieju left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM to me too, cleanly implemented!

Before landing, I will wait for @lucacasonato review.

));
}

hash
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a check missing here:

  1. If the parameters field of the maskGenAlgorithm field of params is not an instance of the HashAlgorithm ASN.1 type that is identical in content to the hashAlgorithm field of params, throw a NotSupportedError.

@lucacasonato
Copy link
Member

This is very close @yacinehmito. Thanks for working on this :-)

Copy link
Member

@lucacasonato lucacasonato left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Incredible work, thank you @yacinehmito. At least 700 more passing tests!

Copy link
Member

@bartlomieju bartlomieju left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thank you @yacinehmito!

@lucacasonato lucacasonato merged commit a3d024a into denoland:main Dec 9, 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
Development

Successfully merging this pull request may close these issues.

5 participants