Skip to content

Commit 93012d2

Browse files
committed
feat: add more KeyObjectHandle implementation
1 parent 1209bfb commit 93012d2

File tree

21 files changed

+421
-54
lines changed

21 files changed

+421
-54
lines changed

bun.lock

Lines changed: 7 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

example/ios/Podfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ target 'QuickCryptoExample' do
3939
installer.pods_project.targets.each do |target|
4040
target.build_configurations.each do |config|
4141
config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '16.0'
42+
config.build_settings['CLANG_CXX_LANGUAGE_STANDARD'] = 'c++20'
4243
end
4344
end
4445
end

example/ios/Podfile.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1752,7 +1752,7 @@ DEPENDENCIES:
17521752
- fmt (from `../../node_modules/react-native/third-party-podspecs/fmt.podspec`)
17531753
- glog (from `../../node_modules/react-native/third-party-podspecs/glog.podspec`)
17541754
- hermes-engine (from `../../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
1755-
- NitroModules (from `../../node_modules/react-native-nitro-modules`)
1755+
- NitroModules (from `../node_modules/react-native-nitro-modules`)
17561756
- QuickCrypto (from `../../node_modules/react-native-quick-crypto`)
17571757
- RCT-Folly (from `../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
17581758
- RCT-Folly/Fabric (from `../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
@@ -1840,7 +1840,7 @@ EXTERNAL SOURCES:
18401840
:podspec: "../../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec"
18411841
:tag: hermes-2024-11-12-RNv0.76.2-5b4aa20c719830dcf5684832b89a6edb95ac3d64
18421842
NitroModules:
1843-
:path: "../../node_modules/react-native-nitro-modules"
1843+
:path: "../node_modules/react-native-nitro-modules"
18441844
QuickCrypto:
18451845
:path: "../../node_modules/react-native-quick-crypto"
18461846
RCT-Folly:
@@ -2040,6 +2040,6 @@ SPEC CHECKSUMS:
20402040
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
20412041
Yoga: feb4910aba9742cfedc059e2b2902e22ffe9954a
20422042

2043-
PODFILE CHECKSUM: 63c4c1ca9d3ee6394f468b17e19efd3e548b5357
2043+
PODFILE CHECKSUM: 7f292eb16e0483a44b89e782c3639f979b8ad29c
20442044

20452045
COCOAPODS: 1.15.2
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
CompileFlags:
2+
Add:
3+
- -std=c++20
4+
- -Wall
5+
- -Wextra
6+
# Project includes
7+
- -I${COMPILE_COMMANDS_DIR}/cpp
8+
- -I${COMPILE_COMMANDS_DIR}/cpp/cipher
9+
- -I${COMPILE_COMMANDS_DIR}/cpp/ed25519
10+
- -I${COMPILE_COMMANDS_DIR}/cpp/hash
11+
- -I${COMPILE_COMMANDS_DIR}/cpp/hmac
12+
- -I${COMPILE_COMMANDS_DIR}/cpp/keys
13+
- -I${COMPILE_COMMANDS_DIR}/cpp/pbkdf2
14+
- -I${COMPILE_COMMANDS_DIR}/cpp/random
15+
- -I${COMPILE_COMMANDS_DIR}/cpp/utils
16+
- -I${COMPILE_COMMANDS_DIR}/deps/fastpbkdf2
17+
- -I${COMPILE_COMMANDS_DIR}/deps/ncrypto/include
18+
# Nitrogen generated includes
19+
- -I${COMPILE_COMMANDS_DIR}/nitrogen/generated/shared/c++
20+
- -I${COMPILE_COMMANDS_DIR}/nitrogen/generated/android/c++
21+
- -I${COMPILE_COMMANDS_DIR}/nitrogen/generated/android
22+
# React Native includes
23+
- -I${COMPILE_COMMANDS_DIR}/node_modules/react-native/ReactAndroid/src/main/jni/first-party/fbjni/headers
24+
- -I${COMPILE_COMMANDS_DIR}/node_modules/react-native/ReactCommon
25+
- -I${COMPILE_COMMANDS_DIR}/node_modules/react-native/ReactCommon/jsi
26+
- -I${COMPILE_COMMANDS_DIR}/node_modules/react-native/ReactCommon/callinvoker
27+
- -I${COMPILE_COMMANDS_DIR}/node_modules/react-native/ReactCommon/react/nativemodule/core
28+
- -I${COMPILE_COMMANDS_DIR}/node_modules/react-native/ReactAndroid/src/main/jni/react/turbomodule
29+
# Nitro modules
30+
- -I${COMPILE_COMMANDS_DIR}/node_modules/react-native-nitro-modules/cpp
31+
# OpenSSL
32+
- -I/opt/homebrew/include
33+
Remove: [-W*, -std=*]
34+
35+
Diagnostics:
36+
UnusedIncludes: Strict
37+
Suppress:
38+
- unused-includes
39+
- unknown-warning-option
40+
41+
Index:
42+
Background: Build
43+
44+
InlayHints:
45+
Enabled: Yes
46+
ParameterNames: Yes
47+
DeducedTypes: Yes
48+
49+
Hover:
50+
ShowAKA: Yes

packages/react-native-quick-crypto/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,6 @@
22
README.md
33

44
ios/**
5+
6+
.cache/**
7+
build-clangd/**

packages/react-native-quick-crypto/android/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ include_directories(
3939
"../cpp/random"
4040
"../cpp/utils"
4141
"../deps/fastpbkdf2"
42+
"../deps/ncrypto"
4243
)
4344

4445
# Third party libraries (Prefabs)

packages/react-native-quick-crypto/cpp/keys/HybridKeyObjectHandle.cpp

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
#include "HybridKeyObjectHandle.hpp"
2-
31
#include <stdexcept>
42

3+
#include "HybridKeyObjectHandle.hpp"
4+
#include "Utils.hpp"
5+
56
namespace margelo::nitro::crypto {
67

78
std::shared_ptr<ArrayBuffer> HybridKeyObjectHandle::exportKey(std::optional<KFormatType> format, std::optional<KeyEncoding> type,
@@ -21,7 +22,33 @@ CFRGKeyPairType HybridKeyObjectHandle::getAsymmetricKeyType() {
2122
bool HybridKeyObjectHandle::init(KeyType keyType, const std::variant<std::string, std::shared_ptr<ArrayBuffer>>& key,
2223
std::optional<KFormatType> format, std::optional<KeyEncoding> type,
2324
const std::optional<std::shared_ptr<ArrayBuffer>>& passphrase) {
24-
throw std::runtime_error("Not yet implemented");
25+
// get ArrayBuffer from key
26+
std::shared_ptr<ArrayBuffer> ab;
27+
if (std::holds_alternative<std::string>(key)) {
28+
ab = ToNativeArrayBuffer(std::get<std::string>(key));
29+
} else {
30+
ab = std::get<std::shared_ptr<ArrayBuffer>>(key);
31+
}
32+
33+
switch (keyType) {
34+
case KeyType::SECRET: {
35+
this->data_ = KeyObjectData::CreateSecret(ab);
36+
break;
37+
}
38+
case KeyType::PUBLIC: {
39+
auto data = KeyObjectData::GetPublicOrPrivateKey(ab, format, type, passphrase);
40+
if (!data) return false;
41+
this->data_ = data.addRefWithType(KeyType::PUBLIC);
42+
break;
43+
}
44+
case KeyType::PRIVATE: {
45+
if (auto data = KeyObjectData::GetPrivateKey(ab, format, type, passphrase, false)) {
46+
this->data_ = std::move(data);
47+
}
48+
break;
49+
}
50+
}
51+
return true;
2552
}
2653

2754
bool HybridKeyObjectHandle::initECRaw(const std::string& curveName, const std::shared_ptr<ArrayBuffer>& keyData) {

packages/react-native-quick-crypto/cpp/keys/HybridKeyObjectHandle.hpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
#include "HybridKeyObjectHandleSpec.hpp"
88
#include "JWK.hpp"
99
#include "KeyDetail.hpp"
10+
#include "KeyObjectData.hpp"
11+
#include "KeyType.hpp"
1012
#include "NamedCurve.hpp"
1113

1214
namespace margelo::nitro::crypto {
1315

14-
using namespace facebook;
15-
1616
class HybridKeyObjectHandle : public HybridKeyObjectHandleSpec {
1717
public:
1818
HybridKeyObjectHandle() : HybridObject(TAG) {}
@@ -21,13 +21,22 @@ class HybridKeyObjectHandle : public HybridKeyObjectHandleSpec {
2121
std::shared_ptr<ArrayBuffer> exportKey(std::optional<KFormatType> format, std::optional<KeyEncoding> type,
2222
const std::optional<std::string>& cipher,
2323
const std::optional<std::shared_ptr<ArrayBuffer>>& passphrase) override;
24+
2425
JWK exportJwk(const JWK& key, bool handleRsaPss) override;
26+
2527
CFRGKeyPairType getAsymmetricKeyType() override;
28+
2629
bool init(KeyType keyType, const std::variant<std::string, std::shared_ptr<ArrayBuffer>>& key, std::optional<KFormatType> format,
2730
std::optional<KeyEncoding> type, const std::optional<std::shared_ptr<ArrayBuffer>>& passphrase) override;
31+
2832
bool initECRaw(const std::string& curveName, const std::shared_ptr<ArrayBuffer>& keyData) override;
33+
2934
std::optional<KeyType> initJwk(const JWK& keyData, std::optional<NamedCurve> namedCurve) override;
35+
3036
KeyDetail keyDetail() override;
37+
38+
private:
39+
KeyObjectData data_;
3140
};
3241

3342
} // namespace margelo::nitro::crypto
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#include "KeyObjectData.hpp"
2+
3+
namespace margelo {
4+
5+
KeyObjectData::KeyObjectData(std::nullptr_t)
6+
: key_type_(KeyType::SECRET) {}
7+
8+
KeyObjectData::KeyObjectData(std::shared_ptr<ArrayBuffer> symmetric_key)
9+
: key_type_(KeyType::SECRET),
10+
data_(std::make_shared<Data>(std::move(symmetric_key))) {}
11+
12+
KeyObjectData::KeyObjectData(KeyType type, ncrypto::EVPKeyPointer&& pkey)
13+
: key_type_(type), data_(std::make_shared<Data>(std::move(pkey))) {}
14+
15+
KeyObjectData KeyObjectData::CreateSecret(std::shared_ptr<ArrayBuffer> key) {
16+
return KeyObjectData(std::move(key));
17+
}
18+
19+
KeyObjectData KeyObjectData::CreateAsymmetric(KeyType key_type,
20+
ncrypto::EVPKeyPointer&& pkey) {
21+
CHECK(pkey);
22+
return KeyObjectData(key_type, std::move(pkey));
23+
}
24+
25+
KeyType KeyObjectData::GetKeyType() const {
26+
CHECK(data_);
27+
return key_type_;
28+
}
29+
30+
const ncrypto::EVPKeyPointer& KeyObjectData::GetAsymmetricKey() const {
31+
CHECK_NE(key_type_, KeyType::SECRET);
32+
CHECK(data_);
33+
return data_->asymmetric_key;
34+
}
35+
36+
std::shared_ptr<ArrayBuffer> KeyObjectData::GetSymmetricKey() const {
37+
CHECK_EQ(key_type_, KeyType::SECRET);
38+
CHECK(data_);
39+
return data_->symmetric_key;
40+
}
41+
42+
size_t KeyObjectData::GetSymmetricKeySize() const {
43+
CHECK_EQ(key_type_, KeyType::SECRET);
44+
CHECK(data_);
45+
return data_->symmetric_key->size();
46+
}
47+
48+
KeyObjectData KeyObjectData::GetPublicOrPrivateKey(std::shared_ptr<ArrayBuffer> key, std::optional<KFormatType> format,
49+
std::optional<KeyEncoding> type,
50+
const std::optional<std::shared_ptr<ArrayBuffer>>& passphrase) {
51+
if (!CheckIsInt32(key->size())) {
52+
throw std::runtime_error("key is too big (int32)");
53+
}
54+
55+
if (format.has_value() && format.value() == KFormatType::PEM) {
56+
// For PEM, we can easily determine whether it is a public or private key
57+
// by looking for the respective PEM tags.
58+
auto res = EVPKeyPointer::TryParsePublicKeyPEM(key);
59+
if (res) {
60+
return CreateAsymmetric(KeyType::PUBLIC, std::move(res.value));
61+
}
62+
63+
if (res.error.value() == EVPKeyPointer::PKParseError::NOT_RECOGNIZED) {
64+
return TryParsePrivateKey(key, format, type, passphrase);
65+
}
66+
throw std::runtime_error("Failed to read asymmetric key");
67+
}
68+
}
69+
70+
KeyObjectData KeyObjectData::GetPrivateKey(std::shared_ptr<ArrayBuffer> key, std::optional<KFormatType> format,
71+
std::optional<KeyEncoding> type,
72+
const std::optional<std::shared_ptr<ArrayBuffer>>& passphrase,
73+
bool isPublic) {
74+
throw std::runtime_error("Not yet implemented");
75+
}
76+
77+
KeyObjectData TryParsePrivateKey(std::shared_ptr<ArrayBuffer> key, std::optional<KFormatType> format,
78+
std::optional<KeyEncoding> type,
79+
const std::optional<std::shared_ptr<ArrayBuffer>>& passphrase) {
80+
auto res = EVPKeyPointer::TryParsePrivateKey(config, buffer);
81+
if (res) {
82+
return KeyObjectData::CreateAsymmetric(KeyType::kKeyTypePrivate,
83+
std::move(res.value));
84+
}
85+
86+
if (res.error.value() == EVPKeyPointer::PKParseError::NEED_PASSPHRASE) {
87+
THROW_ERR_MISSING_PASSPHRASE(env, "Passphrase required for encrypted key");
88+
} else {
89+
ThrowCryptoError(
90+
env, res.openssl_error.value_or(0), "Failed to read private key");
91+
}
92+
return {};
93+
}
94+
95+
} // namespace margelo

0 commit comments

Comments
 (0)