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: generateKeyPairSync for x25519 #173

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions android/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ add_library(
"../cpp/MGLKeys.cpp"
"../cpp/Utils/MGLUtils.cpp"
"../cpp/Cipher/MGLRsa.cpp"
"../cpp/Cipher/MGLX25519.cpp"
"../cpp/Cipher/MGLGenerateKeyPairInstaller.cpp"
"../cpp/Cipher/MGLGenerateKeyPairSyncInstaller.cpp"
"../cpp/Sig/MGLSignInstaller.cpp"
Expand Down
2 changes: 2 additions & 0 deletions cpp/Cipher/MGLGenerateKeyPairInstaller.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@

#ifdef ANDROID
#include "Cipher/MGLRsa.h"
#include "Cipher/MGLX25519.h"
#include "JSIUtils/MGLSmartHostObject.h"
#include "Utils/MGLUtils.h"
#else
#include "MGLRsa.h"
#include "MGLSmartHostObject.h"
#include "MGLUtils.h"
#include "MGLX25519.h"
#endif

namespace margelo {
Expand Down
25 changes: 19 additions & 6 deletions cpp/Cipher/MGLGenerateKeyPairSyncInstaller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <utility>

#include "MGLRsa.h"
#include "MGLX25519.h"

#ifdef ANDROID
#include "JSIUtils/MGLJSIMacros.h"
Expand All @@ -27,17 +28,29 @@ using namespace facebook;

namespace margelo {

// Current implementation only supports RSA schemes (check line config.variant =
// ) As more encryption schemes are added this will require an abstraction that
// supports more schemes
// Current implementation only supports RSA & X25519 schemes (check line
// config.variant = ) As more encryption schemes are added this will require an
// abstraction that supports more schemes
FieldDefinition getGenerateKeyPairSyncFieldDefinition(
std::shared_ptr<react::CallInvoker> jsCallInvoker,
std::shared_ptr<DispatchQueue::dispatch_queue> workerQueue) {
return buildPair(
"generateKeyPairSync", JSIF([=]) {
auto config = std::make_shared<RsaKeyPairGenConfig>(
prepareRsaKeyGenConfig(runtime, arguments));
auto keys = generateRSAKeyPair(runtime, std::move(config));
KeyVariant variant =
static_cast<KeyVariant>((int)arguments[0].asNumber());

std::pair<StringOrBuffer, StringOrBuffer> keys;

if (variant == kKeyVariantX25519) {
auto config = std::make_shared<X25519KeyPairGenConfig>(
prepareX25519KeyGenConfig(runtime, arguments));
keys = generateX25519KeyPair(runtime, std::move(config));
} else {
auto config = std::make_shared<RsaKeyPairGenConfig>(
prepareRsaKeyGenConfig(runtime, arguments));
keys = generateRSAKeyPair(runtime, std::move(config));
}

if (keys.first.isString && keys.second.isString) {
auto publicKey =
jsi::String::createFromUtf8(runtime, keys.first.stringValue);
Expand Down
2 changes: 2 additions & 0 deletions cpp/Cipher/MGLGenerateKeyPairSyncInstaller.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@

#ifdef ANDROID
#include "Cipher/MGLRsa.h"
#include "Cipher/MGLX25519.h"
#include "JSIUtils/MGLSmartHostObject.h"
#include "Utils/MGLUtils.h"
#else
#include "MGLRsa.h"
#include "MGLSmartHostObject.h"
#include "MGLUtils.h"
#include "MGLX25519.h"
#endif
#include "MGLKeys.h"

Expand Down
2 changes: 1 addition & 1 deletion cpp/Cipher/MGLRsa.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ RsaKeyPairGenConfig prepareRsaKeyGenConfig(jsi::Runtime& runtime,
// CHECK(args[*offset + 1]->IsUint32()); // Modulus bits
// CHECK(args[*offset + 2]->IsUint32()); // Exponent
config.variant =
static_cast<RSAKeyVariant>((int)arguments[offset].asNumber());
static_cast<KeyVariant>((int)arguments[offset].asNumber());

// TODO(osp)
// CHECK_IMPLIES(params->params.variant != kKeyVariantRSA_PSS,
Expand Down
8 changes: 1 addition & 7 deletions cpp/Cipher/MGLRsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,6 @@ namespace margelo {

namespace jsi = facebook::jsi;

enum RSAKeyVariant {
kKeyVariantRSA_SSA_PKCS1_v1_5,
kKeyVariantRSA_PSS,
kKeyVariantRSA_OAEP
};

// On node there is a complete madness of structs/classes that encapsulate and
// initialize the data in a generic manner this is to be later be used to
// generate the keys in a thread-safe manner (I think) I'm however too dumb and
Expand All @@ -43,7 +37,7 @@ struct RsaKeyPairGenConfig {
PrivateKeyEncodingConfig private_key_encoding;
ManagedEVPPKey key;

RSAKeyVariant variant;
KeyVariant variant;
unsigned int modulus_bits;
unsigned int exponent;

Expand Down
80 changes: 80 additions & 0 deletions cpp/Cipher/MGLX25519.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//
// MGLX25519.cpp
// react-native-quick-crypto
//
// Created by Samuel on 22.06.22.
//

#include "MGLX25519.h"

namespace margelo {

namespace jsi = facebook::jsi;

X25519KeyPairGenConfig prepareX25519KeyGenConfig(jsi::Runtime& runtime,
const jsi::Value* arguments) {
X25519KeyPairGenConfig config;
unsigned int offset = 0;

config.variant = static_cast<KeyVariant>((int)arguments[offset].asNumber());
offset++;

config.public_key_encoding = ManagedEVPPKey::GetPublicKeyEncodingFromJs(
runtime, arguments, &offset, kKeyContextGenerate);

auto private_key_encoding = ManagedEVPPKey::GetPrivateKeyEncodingFromJs(
runtime, arguments, &offset, kKeyContextGenerate);

if (!private_key_encoding.IsEmpty()) {
config.private_key_encoding = private_key_encoding.Release();
}

return config;
}

EVPKeyCtxPointer setupX25519(std::shared_ptr<X25519KeyPairGenConfig> config) {
EVPKeyCtxPointer ctx(EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, nullptr));

if (!ctx) {
return nullptr;
}

if (!EVP_PKEY_keygen_init(ctx.get())) {
return nullptr;
}

return ctx;
}

std::pair<StringOrBuffer, StringOrBuffer> generateX25519KeyPair(
jsi::Runtime& runtime, std::shared_ptr<X25519KeyPairGenConfig> config) {
CheckEntropy();

EVPKeyCtxPointer ctx = setupX25519(config);

if (!ctx) {
throw new jsi::JSError(runtime, "Error on key generation job");
}

EVP_PKEY* pkey = nullptr;
if (!EVP_PKEY_keygen(ctx.get(), &pkey)) {
throw new jsi::JSError(runtime, "Error generating key");
}

config->key = ManagedEVPPKey(EVPKeyPointer(pkey));

std::optional<StringOrBuffer> publicBuffer =
ManagedEVPPKey::ToEncodedPublicKey(runtime, std::move(config->key),
config->public_key_encoding);
std::optional<StringOrBuffer> privateBuffer =
ManagedEVPPKey::ToEncodedPrivateKey(runtime, std::move(config->key),
config->private_key_encoding);

if (!publicBuffer.has_value() || !privateBuffer.has_value()) {
throw jsi::JSError(runtime, "Failed to encode public and/or private key");
}

return std::make_pair(publicBuffer.value(), privateBuffer.value());
}

} // namespace margelo
44 changes: 44 additions & 0 deletions cpp/Cipher/MGLX25519.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// MGLX25519.h
// react-native-quick-crypto
//
// Created by Samuel on 22.06.22.
//

#ifndef REACT_NATIVE_QUICK_CRYPTO_MGLX25519_H
#define REACT_NATIVE_QUICK_CRYPTO_MGLX25519_H

#include <jsi/jsi.h>

#include <memory>
#include <optional>
#include <utility>

#include "MGLKeys.h"
#ifdef ANDROID
#include "Utils/MGLUtils.h"
#else
#include "MGLUtils.h"
#endif

namespace margelo {

namespace jsi = facebook::jsi;

struct X25519KeyPairGenConfig {
PublicKeyEncodingConfig public_key_encoding;
PrivateKeyEncodingConfig private_key_encoding;
ManagedEVPPKey key;

KeyVariant variant;
};

X25519KeyPairGenConfig prepareX25519KeyGenConfig(jsi::Runtime& runtime,
const jsi::Value* arguments);

std::pair<StringOrBuffer, StringOrBuffer> generateX25519KeyPair(
jsi::Runtime& runtime, std::shared_ptr<X25519KeyPairGenConfig> config);

} // namespace margelo

#endif // REACT_NATIVE_QUICK_CRYPTO_MGLX25519_H
7 changes: 7 additions & 0 deletions cpp/MGLKeys.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ enum PKFormatType { kKeyFormatDER, kKeyFormatPEM, kKeyFormatJWK };

enum KeyType { kKeyTypeSecret, kKeyTypePublic, kKeyTypePrivate };

enum KeyVariant {
kKeyVariantRSA_SSA_PKCS1_v1_5,
kKeyVariantRSA_PSS,
kKeyVariantRSA_OAEP,
kKeyVariantX25519,
};

enum KeyEncodingContext {
kKeyContextInput,
kKeyContextExport,
Expand Down
4 changes: 2 additions & 2 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ PODS:
- glog
- react-native-quick-base64 (2.0.5):
- React-Core
- react-native-quick-crypto (0.5.0):
- react-native-quick-crypto (0.6.1):
- OpenSSL-Universal
- React
- React-callinvoker
Expand Down Expand Up @@ -639,7 +639,7 @@ SPEC CHECKSUMS:
React-jsinspector: d5ce2ef3eb8fd30c28389d0bc577918c70821bd6
React-logger: 9332c3e7b4ef007a0211c0a9868253aac3e1da82
react-native-quick-base64: e657e9197e61b60a9dec49807843052b830da254
react-native-quick-crypto: a903ff39ac8fea78bb9d37cccc5cc0596afc9670
react-native-quick-crypto: 455c1b411db006dba1026a30681ececb19180187
react-native-safe-area-context: 39c2d8be3328df5d437ac1700f4f3a4f75716acc
React-perflogger: 43392072a5b867a504e2b4857606f8fc5a403d7f
React-RCTActionSheet: c7b67c125bebeda9fb19fc7b200d85cb9d6899c4
Expand Down
12 changes: 12 additions & 0 deletions example/src/testing/Tests/CipherTests/GenerateKeyPairTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,4 +131,16 @@ export function registerGenerateKeyPairTests() {
}
);
});

it('Sync X25519: spki - pkcs8', () => {
const ret = crypto.generateKeyPairSync('x25519', {
publicKeyEncoding: { format: 'der', type: 'spki' },
privateKeyEncoding: { format: 'der', type: 'pkcs8' },
});

const { publicKey, privateKey } = ret;

chai.assert.strictEqual(privateKey.length, 48);
chai.assert.strictEqual(publicKey.length, 44);
});
}
16 changes: 12 additions & 4 deletions src/Cipher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
validateUint32,
validateInt32,
} from './Utils';
import { InternalCipher, RSAKeyVariant } from './NativeQuickCrypto/Cipher';
import { InternalCipher, KeyVariant } from './NativeQuickCrypto/Cipher';
// TODO(osp) re-enable type specific constructors
// They are nice to have but not absolutely necessary
// import type {
Expand Down Expand Up @@ -530,7 +530,7 @@ function internalGenerateKeyPair(
if (type === 'rsa') {
if (isAsync) {
NativeQuickCrypto.generateKeyPair(
RSAKeyVariant.kKeyVariantRSA_SSA_PKCS1_v1_5,
KeyVariant.kKeyVariantRSA_SSA_PKCS1_v1_5,
modulusLength,
publicExponent,
...encoding
Expand All @@ -551,7 +551,7 @@ function internalGenerateKeyPair(
} else {
let [err, publicKey, privateKey] =
NativeQuickCrypto.generateKeyPairSync(
RSAKeyVariant.kKeyVariantRSA_SSA_PKCS1_v1_5,
KeyVariant.kKeyVariantRSA_SSA_PKCS1_v1_5,
modulusLength,
publicExponent,
...encoding
Expand Down Expand Up @@ -604,7 +604,7 @@ function internalGenerateKeyPair(
}

return NativeQuickCrypto.generateKeyPairSync(
RSAKeyVariant.kKeyVariantRSA_PSS,
KeyVariant.kKeyVariantRSA_PSS,
modulusLength,
publicExponent,
hashAlgorithm || hash,
Expand Down Expand Up @@ -644,6 +644,14 @@ function internalGenerateKeyPair(

// // return new EcKeyPairGenJob(mode, namedCurve, paramEncoding, ...encoding);
// }
case 'x25519': {
validateObject<GenerateKeyPairOptions>(options, 'options');

return NativeQuickCrypto.generateKeyPairSync(
KeyVariant.kKeyVariantX25519,
...encoding
);
}
// case 'ed25519':
// case 'ed448':
// case 'x25519':
Expand Down
9 changes: 4 additions & 5 deletions src/NativeQuickCrypto/Cipher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import type { Buffer } from '@craftzdog/react-native-buffer';

// TODO(osp) on node this is defined on the native side
// Need to do the same so that values are always in sync
export enum RSAKeyVariant {
export enum KeyVariant {
kKeyVariantRSA_SSA_PKCS1_v1_5,
kKeyVariantRSA_PSS,
kKeyVariantRSA_OAEP,
kKeyVariantX25519,
}

export type InternalCipher = {
Expand Down Expand Up @@ -55,15 +56,13 @@ export type PrivateDecryptMethod = (
) => Buffer;

export type GenerateKeyPairMethod = (
keyVariant: RSAKeyVariant,
keyVariant: KeyVariant,
modulusLength: number,
publicExponent: number,
...rest: any[]
) => Promise<[error: unknown, publicBuffer: any, privateBuffer: any]>;

export type GenerateKeyPairSyncMethod = (
keyVariant: RSAKeyVariant,
modulusLength: number,
publicExponent: number,
keyVariant: KeyVariant,
...rest: any[]
) => [error: unknown, publicBuffer: any, privateBuffer: any];
Loading