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: subtle.generateKey support for ecdsa, ecdh #304

Open
wants to merge 14 commits into
base: main
Choose a base branch
from

Conversation

boorad
Copy link
Collaborator

@boorad boorad commented May 6, 2024

Add support for subtle.generateKey() for EC formats (ECDSA, ECDH)

closes #59
closes #80
closes #288

@boorad boorad self-assigned this May 6, 2024
@boorad boorad marked this pull request as draft May 6, 2024 19:43
@boorad boorad force-pushed the feat/subtle-generatekey-ecdsa branch from 81a6e23 to 1237592 Compare May 11, 2024 02:08
@boorad boorad changed the title feat: subtle.generateKey support for ECDSA feat: subtle.generateKey support for ecdsa, ecdh May 14, 2024
@boorad boorad force-pushed the feat/subtle-generatekey-ecdsa branch from 9016f81 to a2032f9 Compare May 16, 2024 00:32
@boorad
Copy link
Collaborator Author

boorad commented May 21, 2024

304_tests_before
304_tests_after

@boorad boorad marked this pull request as ready for review May 21, 2024 19:26
Copy link
Member

@mrousavy mrousavy left a comment

Choose a reason for hiding this comment

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

great work dude.

left some comments about C++ code style, JSI specifics, and threading concerns.

cpp/Cipher/MGLGenerateKeyPairInstaller.cpp Show resolved Hide resolved
Comment on lines +43 to +61
std::shared_ptr<RsaKeyPairGenConfig> rsaConfig;
std::shared_ptr<EcKeyPairGenConfig> ecConfig;

// switch on variant to get proper config from arguments
// outside of lambda 🤮
if (variant == kvRSA_SSA_PKCS1_v1_5 ||
variant == kvRSA_PSS ||
variant == kvRSA_OAEP
) {
rsaConfig = std::make_shared<RsaKeyPairGenConfig>(
prepareRsaKeyGenConfig(runtime, arguments));
} else
if (variant == kvEC) {
ecConfig = std::make_shared<EcKeyPairGenConfig>(
prepareEcKeyGenConfig(runtime, arguments));
} else {
throw std::runtime_error("KeyVariant not implemented"
+ std::to_string((int)variant));
}
Copy link
Member

Choose a reason for hiding this comment

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

Hm, can't we make a single abstract base class and only construct the type we need? Do those things share logic?

Something like

class KeyPairGenConfig { ... }

class RsaKeyPairGenConfig: public KeyPairGenConfig { ... }
class EcKeyPairGenConfig: public KeyPairGenConfig { ... }

Inheritance/abstraction is better here.

auto keys = generateRSAKeyPair(runtime, config);
auto publicKey = toJSI(runtime, keys.first);
auto privateKey = toJSI(runtime, keys.second);
jsCallInvoker->invokeAsync([&runtime, resolve,
Copy link
Member

Choose a reason for hiding this comment

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

Wait what? What's the point of this Thread? It will immediately jump back to the JS Thread because of callInvoker->invokeAsync, why do we do that??

Nothing is running on a separate Thread here.

We probably want to call generateKeyPair(...) on the Thread, and instead of constructing jsi::Values we construct an intermediate type first, then call back to JS using jsCallInvoker->invokeAsync, and then create the jsi::Values and resolve the promise.

Comment on lines +89 to +97
if (variant == kvRSA_SSA_PKCS1_v1_5 ||
variant == kvRSA_PSS ||
variant == kvRSA_OAEP
) {
keys = generateRsaKeyPair(runtime, rsaConfig);
} else
if (variant == kvEC) {
keys = generateEcKeyPair(runtime, ecConfig);
} else {
Copy link
Member

Choose a reason for hiding this comment

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

Again - duplicated logic, and an unnecessary pre-allocation. Just do a single abstract class which has generateKeyPair - both RSA and EC have different implementations. Much cleaner and less code.

cpp/Cipher/MGLGenerateKeyPairInstaller.cpp Show resolved Hide resolved
Comment on lines +41 to +58
// switch on variant to get proper config/genKeyPair
if (variant == kvRSA_SSA_PKCS1_v1_5 ||
variant == kvRSA_PSS ||
variant == kvRSA_OAEP
) {
auto config = std::make_shared<RsaKeyPairGenConfig>(
prepareRsaKeyGenConfig(runtime, arguments));
keys = generateRsaKeyPair(runtime, config);
} else
if (variant == kvEC) {
auto config = std::make_shared<EcKeyPairGenConfig>(
prepareEcKeyGenConfig(runtime, arguments));
keys = generateEcKeyPair(runtime, config);
} else {
throw std::runtime_error("KeyVariant not implemented: " +
std::to_string((int)variant));
}
// keys.first = publicKey keys.second = privateKey
Copy link
Member

Choose a reason for hiding this comment

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

Again, abstraction. Code on interface level.

cpp/JSIUtils/MGLJSIUtils.h Show resolved Hide resolved
return key_ctx;
}

std::pair<jsi::Value, jsi::Value> generateEcKeyPair(jsi::Runtime& runtime,
Copy link
Member

Choose a reason for hiding this comment

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

I like that we go away from JSVariant, but I wonder if it makes sense to use C++ types instead of jsi::Value here.

If we do that, we could avoid Runtime specific calls and allow this method to run on any Thread.

Since we use jsi::Runtime here, we can only call this method on the JS-thread. So this cannot be called async.

cpp/webcrypto/crypto_ec.cpp Show resolved Hide resolved
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants