diff --git a/README.md b/README.md
index be4385f9..41612eb8 100644
--- a/README.md
+++ b/README.md
@@ -61,7 +61,7 @@ Creating a Wallet took 289 ms
```sh
-yarn add react-native-quick-crypto
+bun add react-native-quick-crypto react-native-nitro-modules
cd ios && pod install
```
diff --git a/bun.lockb b/bun.lockb
index f9de01b6..1bd2c97f 100755
Binary files a/bun.lockb and b/bun.lockb differ
diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock
index 51fedc84..8747dc43 100644
--- a/example/ios/Podfile.lock
+++ b/example/ios/Podfile.lock
@@ -7,7 +7,7 @@ PODS:
- hermes-engine (0.76.1):
- hermes-engine/Pre-built (= 0.76.1)
- hermes-engine/Pre-built (0.76.1)
- - NitroModules (0.18.1):
+ - NitroModules (0.18.2):
- DoubleConversion
- glog
- hermes-engine
@@ -1938,7 +1938,7 @@ SPEC CHECKSUMS:
fmt: 10c6e61f4be25dc963c36bd73fc7b1705fe975be
glog: 08b301085f15bcbb6ff8632a8ebaf239aae04e6a
hermes-engine: 46f1ffbf0297f4298862068dd4c274d4ac17a1fd
- NitroModules: 55f64932b4581a7d02103bc35b84c7bd3204106b
+ NitroModules: 47399393665e69228b29a17f501c7b453679ccc0
OpenSSL-Universal: b60a3702c9fea8b3145549d421fdb018e53ab7b4
QuickCrypto: e68316432823f70bdae0bf1486dc5e9afdfdff4d
RCT-Folly: 84578c8756030547307e4572ab1947de1685c599
diff --git a/example/package.json b/example/package.json
index f0b6cf6d..6ceeb482 100644
--- a/example/package.json
+++ b/example/package.json
@@ -34,7 +34,7 @@
"react": "18.3.1",
"react-native": "0.76.1",
"react-native-bouncy-checkbox": "4.0.1",
- "react-native-nitro-modules": "0.18.1",
+ "react-native-nitro-modules": "0.18.2",
"react-native-quick-base64": "2.1.2",
"react-native-quick-crypto": "workspace:*",
"react-native-safe-area-context": "4.14.0",
diff --git a/example/src/navigators/children/TestDetailsScreen.tsx b/example/src/navigators/children/TestDetailsScreen.tsx
index 90e9d8e4..caab031f 100644
--- a/example/src/navigators/children/TestDetailsScreen.tsx
+++ b/example/src/navigators/children/TestDetailsScreen.tsx
@@ -30,6 +30,7 @@ export const TestDetailsScreen = ({ route }) => {
onPress={() => setShowFailed(!showFailed)}
disableText={true}
fillColor="red"
+ style={styles.checkbox}
/>
Show Failed
@@ -39,6 +40,7 @@ export const TestDetailsScreen = ({ route }) => {
onPress={() => setShowPassed(!showPassed)}
disableText={true}
fillColor={colors.green}
+ style={styles.checkbox}
/>
Show Passed
@@ -101,4 +103,7 @@ const styles = StyleSheet.create({
scrollContent: {
paddingHorizontal: 5,
},
+ checkbox: {
+ transform: [{ scaleX: 0.8 }, { scaleY: 0.8 }],
+ },
});
diff --git a/example/src/tests/ed25519/ed25519_tests.ts b/example/src/tests/ed25519/ed25519_tests.ts
index d498e108..e27f8241 100644
--- a/example/src/tests/ed25519/ed25519_tests.ts
+++ b/example/src/tests/ed25519/ed25519_tests.ts
@@ -46,17 +46,17 @@ types.map((type) => {
});
*/
+const data1 = Buffer.from('hello world');
+
test(SUITE, 'sign/verify - round trip happy', async () => {
- const data = Buffer.from('hello world');
const ed = new Ed('ed25519', {});
await ed.generateKeyPair();
- const signature = await ed.sign(data.buffer);
- const verified = await ed.verify(signature, data.buffer);
+ const signature = await ed.sign(data1.buffer);
+ const verified = await ed.verify(signature, data1.buffer);
expect(verified).to.be.true;
});
test(SUITE, 'sign/verify - round trip sad', async () => {
- const data1 = Buffer.from('hello world');
const data2 = Buffer.from('goodbye cruel world');
const ed = new Ed('ed25519', {});
await ed.generateKeyPair();
@@ -66,12 +66,45 @@ test(SUITE, 'sign/verify - round trip sad', async () => {
});
test(SUITE, 'sign/verify - bad signature does not verify', async () => {
- const data = Buffer.from('hello world');
const ed = new Ed('ed25519', {});
await ed.generateKeyPair();
- const signature = await ed.sign(data.buffer);
+ const signature = await ed.sign(data1.buffer);
const signature2 = randomBytes(64).buffer;
expect(ab2str(signature2)).not.to.equal(ab2str(signature));
- const verified = await ed.verify(signature2, data.buffer);
+ const verified = await ed.verify(signature2, data1.buffer);
expect(verified).to.be.false;
});
+
+test(
+ SUITE,
+ 'sign/verify with non-internally generated private key',
+ async () => {
+ let ed1: Ed | null = new Ed('ed25519', {});
+ await ed1.generateKeyPair();
+ const priv = ed1.getPrivateKey();
+ ed1 = null;
+
+ const ed2 = new Ed('ed25519', {});
+ const signature = await ed2.sign(data1.buffer, priv);
+ const verified = await ed2.verify(signature, data1.buffer, priv);
+ expect(verified).to.be.true;
+ },
+);
+
+test(
+ SUITE,
+ 'sign/verify with bad non-internally generated private key',
+ async () => {
+ let ed1: Ed | null = new Ed('ed25519', {});
+ await ed1.generateKeyPair();
+ const priv = ed1.getPrivateKey();
+ ed1 = null;
+
+ const ed2 = new Ed('ed25519', {});
+ const signature = await ed2.sign(data1.buffer, priv);
+ const signature2 = randomBytes(64).buffer;
+ expect(ab2str(signature2)).not.to.equal(ab2str(signature));
+ const verified = await ed2.verify(signature2, data1.buffer, priv);
+ expect(verified).to.be.false;
+ },
+);
diff --git a/packages/react-native-quick-crypto/cpp/ed25519/HybridEdKeyPair.cpp b/packages/react-native-quick-crypto/cpp/ed25519/HybridEdKeyPair.cpp
index 2db3f6a2..6d9f8a32 100644
--- a/packages/react-native-quick-crypto/cpp/ed25519/HybridEdKeyPair.cpp
+++ b/packages/react-native-quick-crypto/cpp/ed25519/HybridEdKeyPair.cpp
@@ -72,28 +72,36 @@ HybridEdKeyPair::generateKeyPairSync(
std::shared_ptr>>
HybridEdKeyPair::sign(
- const std::shared_ptr& message
+ const std::shared_ptr& message,
+ const std::optional>& key
) {
// get owned NativeArrayBuffer before passing to sync function
auto nativeMessage = ToNativeArrayBuffer(message);
+ std::optional> nativeKey = std::nullopt;
+ if (key.has_value()) {
+ nativeKey = ToNativeArrayBuffer(key.value());
+ }
- return Promise>::async([this, nativeMessage]() {
- return this->signSync(nativeMessage);
+ return Promise>::async([this, nativeMessage, nativeKey]() {
+ return this->signSync(nativeMessage, nativeKey);
}
);
}
std::shared_ptr
HybridEdKeyPair::signSync(
- const std::shared_ptr& message
+ const std::shared_ptr& message,
+ const std::optional>& key
) {
- this->checkKeyPair();
size_t sig_len = 0;
uint8_t* sig = NULL;
EVP_MD_CTX* md_ctx = nullptr;
EVP_PKEY_CTX* pkey_ctx = nullptr;
+ // get key to use for signing
+ EVP_PKEY* pkey = this->importPrivateKey(key);
+
// key context
md_ctx = EVP_MD_CTX_new();
if (md_ctx == nullptr) {
@@ -107,7 +115,7 @@ HybridEdKeyPair::signSync(
throw std::runtime_error("Error creating signing context: " + this->curve);
}
- if (EVP_DigestSignInit(md_ctx, &pkey_ctx, NULL, NULL, this->pkey) <= 0) {
+ if (EVP_DigestSignInit(md_ctx, &pkey_ctx, NULL, NULL, pkey) <= 0) {
EVP_MD_CTX_free(md_ctx);
char* err = ERR_error_string(ERR_get_error(), NULL);
throw std::runtime_error("Failed to initialize signing: " + std::string(err));
@@ -142,14 +150,19 @@ HybridEdKeyPair::signSync(
std::shared_ptr>
HybridEdKeyPair::verify(
const std::shared_ptr& signature,
- const std::shared_ptr& message
+ const std::shared_ptr& message,
+ const std::optional>& key
) {
// get owned NativeArrayBuffers before passing to sync function
auto nativeSignature = ToNativeArrayBuffer(signature);
auto nativeMessage = ToNativeArrayBuffer(message);
+ std::optional> nativeKey = std::nullopt;
+ if (key.has_value()) {
+ nativeKey = ToNativeArrayBuffer(key.value());
+ }
- return Promise::async([this, nativeSignature, nativeMessage]() {
- return this->verifySync(nativeSignature, nativeMessage);
+ return Promise::async([this, nativeSignature, nativeMessage, nativeKey]() {
+ return this->verifySync(nativeSignature, nativeMessage, nativeKey);
}
);
}
@@ -157,9 +170,11 @@ HybridEdKeyPair::verify(
bool
HybridEdKeyPair::verifySync(
const std::shared_ptr& signature,
- const std::shared_ptr& message
+ const std::shared_ptr& message,
+ const std::optional>& key
) {
- this->checkKeyPair();
+ // get key to use for verifying
+ EVP_PKEY* pkey = this->importPrivateKey(key);
EVP_MD_CTX* md_ctx = nullptr;
EVP_PKEY_CTX* pkey_ctx = nullptr;
@@ -177,7 +192,7 @@ HybridEdKeyPair::verifySync(
throw std::runtime_error("Error creating verify context: " + this->curve);
}
- if (EVP_DigestVerifyInit(md_ctx, &pkey_ctx, NULL, NULL, this->pkey) <= 0) {
+ if (EVP_DigestVerifyInit(md_ctx, &pkey_ctx, NULL, NULL, pkey) <= 0) {
EVP_MD_CTX_free(md_ctx);
char* err = ERR_error_string(ERR_get_error(), NULL);
throw std::runtime_error("Failed to initialize verify: " + std::string(err));
@@ -230,4 +245,24 @@ HybridEdKeyPair::setCurve(const std::string& curve) {
this->curve = curve;
}
+EVP_PKEY*
+HybridEdKeyPair::importPrivateKey(const std::optional>& key) {
+ EVP_PKEY* pkey = nullptr;
+ if (key.has_value()) {
+ pkey = EVP_PKEY_new_raw_private_key(
+ EVP_PKEY_ED25519, // TODO: use this->curve somehow
+ NULL,
+ key.value()->data(),
+ 32
+ );
+ if (pkey == nullptr) {
+ throw std::runtime_error("Failed to read private key");
+ }
+ } else {
+ this->checkKeyPair();
+ pkey = this->pkey;
+ }
+ return pkey;
+}
+
} // namespace margelo::nitro::crypto
diff --git a/packages/react-native-quick-crypto/cpp/ed25519/HybridEdKeyPair.hpp b/packages/react-native-quick-crypto/cpp/ed25519/HybridEdKeyPair.hpp
index b8cd8520..42eb14d9 100644
--- a/packages/react-native-quick-crypto/cpp/ed25519/HybridEdKeyPair.hpp
+++ b/packages/react-native-quick-crypto/cpp/ed25519/HybridEdKeyPair.hpp
@@ -37,36 +37,49 @@ class HybridEdKeyPair : public HybridEdKeyPairSpec {
) override;
std::shared_ptr>>
- sign(const std::shared_ptr& message) override;
+ sign(
+ const std::shared_ptr& message,
+ const std::optional>& key
+ ) override;
std::shared_ptr
- signSync(const std::shared_ptr& message) override;
+ signSync(
+ const std::shared_ptr& message,
+ const std::optional>& key
+ ) override;
std::shared_ptr>
verify(
const std::shared_ptr& signature,
- const std::shared_ptr& message
+ const std::shared_ptr& message,
+ const std::optional>& key
) override;
bool
verifySync(
const std::shared_ptr& signature,
- const std::shared_ptr& message
+ const std::shared_ptr& message,
+ const std::optional>& key
) override;
protected:
std::shared_ptr
getPublicKey() override;
- std::shared_ptr getPrivateKey();
+ std::shared_ptr
+ getPrivateKey() override;
void checkKeyPair();
- void setCurve(const std::string& curve);
+ void setCurve(const std::string& curve) override;
private:
std::string curve;
EVP_PKEY* pkey = nullptr;
+
+ EVP_PKEY* importPrivateKey(
+ const std::optional>& key
+ );
};
} // namespace margelo::nitro::crypto
diff --git a/packages/react-native-quick-crypto/nitrogen/generated/shared/c++/HybridEdKeyPairSpec.cpp b/packages/react-native-quick-crypto/nitrogen/generated/shared/c++/HybridEdKeyPairSpec.cpp
index 35ee9ab5..fb0f5dc5 100644
--- a/packages/react-native-quick-crypto/nitrogen/generated/shared/c++/HybridEdKeyPairSpec.cpp
+++ b/packages/react-native-quick-crypto/nitrogen/generated/shared/c++/HybridEdKeyPairSpec.cpp
@@ -17,6 +17,7 @@ namespace margelo::nitro::crypto {
prototype.registerHybridMethod("generateKeyPair", &HybridEdKeyPairSpec::generateKeyPair);
prototype.registerHybridMethod("generateKeyPairSync", &HybridEdKeyPairSpec::generateKeyPairSync);
prototype.registerHybridMethod("getPublicKey", &HybridEdKeyPairSpec::getPublicKey);
+ prototype.registerHybridMethod("getPrivateKey", &HybridEdKeyPairSpec::getPrivateKey);
prototype.registerHybridMethod("sign", &HybridEdKeyPairSpec::sign);
prototype.registerHybridMethod("signSync", &HybridEdKeyPairSpec::signSync);
prototype.registerHybridMethod("verify", &HybridEdKeyPairSpec::verify);
diff --git a/packages/react-native-quick-crypto/nitrogen/generated/shared/c++/HybridEdKeyPairSpec.hpp b/packages/react-native-quick-crypto/nitrogen/generated/shared/c++/HybridEdKeyPairSpec.hpp
index 25c2425b..294df208 100644
--- a/packages/react-native-quick-crypto/nitrogen/generated/shared/c++/HybridEdKeyPairSpec.hpp
+++ b/packages/react-native-quick-crypto/nitrogen/generated/shared/c++/HybridEdKeyPairSpec.hpp
@@ -55,10 +55,11 @@ namespace margelo::nitro::crypto {
virtual std::shared_ptr> generateKeyPair(double publicFormat, double publicType, double privateFormat, double privateType, const std::optional& cipher, const std::optional>& passphrase) = 0;
virtual void generateKeyPairSync(double publicFormat, double publicType, double privateFormat, double privateType, const std::optional& cipher, const std::optional>& passphrase) = 0;
virtual std::shared_ptr getPublicKey() = 0;
- virtual std::shared_ptr>> sign(const std::shared_ptr& message) = 0;
- virtual std::shared_ptr signSync(const std::shared_ptr& message) = 0;
- virtual std::shared_ptr> verify(const std::shared_ptr& signature, const std::shared_ptr& message) = 0;
- virtual bool verifySync(const std::shared_ptr& signature, const std::shared_ptr& message) = 0;
+ virtual std::shared_ptr getPrivateKey() = 0;
+ virtual std::shared_ptr>> sign(const std::shared_ptr& message, const std::optional>& key) = 0;
+ virtual std::shared_ptr signSync(const std::shared_ptr& message, const std::optional>& key) = 0;
+ virtual std::shared_ptr> verify(const std::shared_ptr& message, const std::shared_ptr& signature, const std::optional>& key) = 0;
+ virtual bool verifySync(const std::shared_ptr& message, const std::shared_ptr& signature, const std::optional>& key) = 0;
virtual void setCurve(const std::string& curve) = 0;
protected:
diff --git a/packages/react-native-quick-crypto/package.json b/packages/react-native-quick-crypto/package.json
index e75f3e49..ac6c28b1 100644
--- a/packages/react-native-quick-crypto/package.json
+++ b/packages/react-native-quick-crypto/package.json
@@ -70,7 +70,7 @@
"dependencies": {
"@craftzdog/react-native-buffer": "6.0.5",
"events": "3.3.0",
- "react-native-nitro-modules": "0.18.1",
+ "react-native-nitro-modules": "0.18.2",
"react-native-quick-base64": "2.1.2",
"readable-stream": "4.5.2",
"string_decoder": "1.3.0",
@@ -88,7 +88,7 @@
"eslint": "9.9.0",
"eslint-plugin-react-native": "^4.1.0",
"jest": "29.7.0",
- "nitro-codegen": "0.18.1",
+ "nitro-codegen": "0.18.2",
"prettier": "3.3.3",
"react-native-builder-bob": "0.33.3",
"release-it": "17.6.0",
diff --git a/packages/react-native-quick-crypto/src/ed.ts b/packages/react-native-quick-crypto/src/ed.ts
index 57e97106..34863327 100644
--- a/packages/react-native-quick-crypto/src/ed.ts
+++ b/packages/react-native-quick-crypto/src/ed.ts
@@ -40,19 +40,37 @@ export class Ed {
return this.native.getPublicKey();
}
- async sign(message: ArrayBuffer): Promise {
- return this.native.sign(message);
+ getPrivateKey(): ArrayBuffer {
+ return this.native.getPrivateKey();
}
- signSync(message: ArrayBuffer): ArrayBuffer {
- return this.native.signSync(message);
+ async sign(message: ArrayBuffer, key?: ArrayBuffer): Promise {
+ return key ? this.native.sign(message, key) : this.native.sign(message);
}
- async verify(signature: ArrayBuffer, message: ArrayBuffer): Promise {
- return this.native.verify(signature, message);
+ signSync(message: ArrayBuffer, key?: ArrayBuffer): ArrayBuffer {
+ return key
+ ? this.native.signSync(message, key)
+ : this.native.signSync(message);
}
- verifySync(signature: ArrayBuffer, message: ArrayBuffer): boolean {
- return this.native.verifySync(signature, message);
+ async verify(
+ message: ArrayBuffer,
+ signature: ArrayBuffer,
+ key?: ArrayBuffer,
+ ): Promise {
+ return key
+ ? this.native.verify(message, signature, key)
+ : this.native.verify(message, signature);
+ }
+
+ verifySync(
+ message: ArrayBuffer,
+ signature: ArrayBuffer,
+ key?: ArrayBuffer,
+ ): boolean {
+ return key
+ ? this.native.verifySync(message, signature, key)
+ : this.native.verifySync(message, signature);
}
}
diff --git a/packages/react-native-quick-crypto/src/specs/edKeyPair.nitro.ts b/packages/react-native-quick-crypto/src/specs/edKeyPair.nitro.ts
index dc6aba6f..5e14d71c 100644
--- a/packages/react-native-quick-crypto/src/specs/edKeyPair.nitro.ts
+++ b/packages/react-native-quick-crypto/src/specs/edKeyPair.nitro.ts
@@ -21,12 +21,21 @@ export interface EdKeyPair
): void;
getPublicKey(): ArrayBuffer;
+ getPrivateKey(): ArrayBuffer;
- sign(message: ArrayBuffer): Promise;
- signSync(message: ArrayBuffer): ArrayBuffer;
+ sign(message: ArrayBuffer, key?: ArrayBuffer): Promise;
+ signSync(message: ArrayBuffer, key?: ArrayBuffer): ArrayBuffer;
- verify(signature: ArrayBuffer, message: ArrayBuffer): Promise;
- verifySync(signature: ArrayBuffer, message: ArrayBuffer): boolean;
+ verify(
+ message: ArrayBuffer,
+ signature: ArrayBuffer,
+ key?: ArrayBuffer,
+ ): Promise;
+ verifySync(
+ message: ArrayBuffer,
+ signature: ArrayBuffer,
+ key?: ArrayBuffer,
+ ): boolean;
setCurve(curve: string): void;
}