Skip to content

Commit

Permalink
Merge pull request #60 from plneple/main
Browse files Browse the repository at this point in the history
add crypto_box_seal
  • Loading branch information
nikgraf authored Jan 22, 2024
2 parents 6d8da13 + 9bfc689 commit 035a053
Show file tree
Hide file tree
Showing 4 changed files with 234 additions and 0 deletions.
120 changes: 120 additions & 0 deletions cpp/react-native-libsodium.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -968,6 +968,126 @@ namespace ReactNativeLibsodium

jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_box_open_easy", std::move(jsi_crypto_box_open_easy));

auto jsi_crypto_box_seal = jsi::Function::createFromHostFunction(
jsiRuntime,
jsi::PropNameID::forUtf8(jsiRuntime, "jsi_crypto_box_seal"),
2,
[](jsi::Runtime &runtime, const jsi::Value &thisValue, const jsi::Value *arguments, size_t count) -> jsi::Value
{
const std::string functionName = "crypto_box_seal";

std::string messageArgumentName = "message";
unsigned int messageArgumentPosition = 0;
JsiArgType messageArgType = validateIsStringOrArrayBuffer(functionName, runtime, arguments[messageArgumentPosition], messageArgumentName, true);

std::string publicKeyArgumentName = "publicKey";
unsigned int publicKeyArgumentPosition = 1;
validateIsArrayBuffer(functionName, runtime, arguments[publicKeyArgumentPosition], publicKeyArgumentName, true);

auto publicKey = arguments[publicKeyArgumentPosition].asObject(runtime).getArrayBuffer(runtime);

if (publicKey.length(runtime) != crypto_box_PUBLICKEYBYTES)
{
throw jsi::JSError(runtime, "invalid publicKey length");
}

std::vector<uint8_t> ciphertext;
int result = -1;

if (messageArgType == JsiArgType::string)
{
std::string messageString = arguments[messageArgumentPosition].asString(runtime).utf8(runtime);
ciphertext.resize(messageString.length() + crypto_box_SEALBYTES);
result = crypto_box_seal(
ciphertext.data(),
reinterpret_cast<const unsigned char *>(messageString.data()),
messageString.length(),
publicKey.data(runtime));
}
else
{
auto messageArrayBuffer = arguments[messageArgumentPosition].asObject(runtime).getArrayBuffer(runtime);
ciphertext.resize(messageArrayBuffer.length(runtime) + crypto_box_SEALBYTES);
result = crypto_box_seal(
ciphertext.data(),
messageArrayBuffer.data(runtime),
messageArrayBuffer.length(runtime),
publicKey.data(runtime));
}

throwOnBadResult(functionName, runtime, result);
return arrayBufferAsObject(runtime, ciphertext);
});

jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_box_seal", std::move(jsi_crypto_box_seal));

auto jsi_crypto_box_seal_open = jsi::Function::createFromHostFunction(
jsiRuntime,
jsi::PropNameID::forUtf8(jsiRuntime, "jsi_crypto_box_seal_open"),
3,
[](jsi::Runtime &runtime, const jsi::Value &thisValue, const jsi::Value *arguments, size_t count) -> jsi::Value
{
const std::string functionName = "crypto_box_seal_open";

std::string ciphertextArgumentName = "ciphertext";
unsigned int ciphertextArgumentPosition = 0;
JsiArgType ciphertextArgType = validateIsStringOrArrayBuffer(functionName, runtime, arguments[ciphertextArgumentPosition], ciphertextArgumentName, true);

std::string publicKeyArgumentName = "publicKey";
unsigned int publicKeyArgumentPosition = 1;
validateIsArrayBuffer(functionName, runtime, arguments[publicKeyArgumentPosition], publicKeyArgumentName, true);

std::string secretKeyArgumentName = "secretKey";
unsigned int secretKeyArgumentPosition = 2;
validateIsArrayBuffer(functionName, runtime, arguments[secretKeyArgumentPosition], secretKeyArgumentName, true);

auto publicKeyArrayBuffer =
arguments[1].asObject(runtime).getArrayBuffer(runtime);

auto secretKeyArrayBuffer =
arguments[2].asObject(runtime).getArrayBuffer(runtime);

if (publicKeyArrayBuffer.length(runtime) != crypto_box_PUBLICKEYBYTES)
{
throw jsi::JSError(runtime, "invalid publicKey length");
}
if (secretKeyArrayBuffer.length(runtime) != crypto_box_SECRETKEYBYTES)
{
throw jsi::JSError(runtime, "invalid privateKey length");
}

std::vector<uint8_t> message;
int result = -1;

if (ciphertextArgType == JsiArgType::string)
{
std::string ciphertextString = arguments[ciphertextArgumentPosition].asString(runtime).utf8(runtime);
message.resize(ciphertextString.length() - crypto_box_SEALBYTES);
result = crypto_box_seal_open(
message.data(),
reinterpret_cast<const unsigned char *>(ciphertextString.data()),
ciphertextString.length(),
publicKeyArrayBuffer.data(runtime),
secretKeyArrayBuffer.data(runtime));
}
else
{
auto ciphertextArrayBuffer = arguments[ciphertextArgumentPosition].asObject(runtime).getArrayBuffer(runtime);
message.resize(ciphertextArrayBuffer.length(runtime) - crypto_box_SEALBYTES);
result = crypto_box_seal_open(
message.data(),
ciphertextArrayBuffer.data(runtime),
ciphertextArrayBuffer.length(runtime),
publicKeyArrayBuffer.data(runtime),
secretKeyArrayBuffer.data(runtime));
}

throwOnBadResult(functionName, runtime, result);
return arrayBufferAsObject(runtime, message);
});

jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_box_seal_open", std::move(jsi_crypto_box_seal_open));

auto jsi_crypto_pwhash = jsi::Function::createFromHostFunction(
jsiRuntime,
jsi::PropNameID::forUtf8(jsiRuntime, "jsi_crypto_pwhash"),
Expand Down
1 change: 1 addition & 0 deletions example/src/components/TestResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import '../tests/crypto_auth_keygen_test';
import '../tests/crypto_auth_test';
import '../tests/crypto_auth_verify_test';
import '../tests/crypto_box_easy_test';
import '../tests/crypto_box_seal_test';
import '../tests/crypto_box_keypair_test';
import '../tests/crypto_box_open_easy_test';
import '../tests/crypto_generichash_test';
Expand Down
53 changes: 53 additions & 0 deletions example/src/tests/crypto_box_seal_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { crypto_box_seal, crypto_box_seal_open } from 'react-native-libsodium';
import { expect, test } from '../utils/testRunner';

test('crypto_box_seal', () => {
const receiverKeyPair = {
keyType: 'x25519',
privateKey: new Uint8Array([
232, 167, 21, 228, 54, 165, 143, 50, 85, 27, 167, 176, 163, 211, 176, 7,
159, 111, 77, 250, 19, 16, 169, 199, 109, 135, 21, 253, 184, 239, 207,
172,
]),
publicKey: new Uint8Array([
142, 127, 163, 61, 43, 54, 5, 80, 178, 86, 18, 245, 253, 136, 7, 152, 90,
152, 194, 31, 23, 253, 243, 87, 53, 66, 15, 42, 18, 238, 19, 12,
]),
};

const message = 'Hello, world!';
const messageSealed = crypto_box_seal(message, receiverKeyPair.publicKey);
const messageOpen = crypto_box_seal_open(
messageSealed,
receiverKeyPair.publicKey,
receiverKeyPair.privateKey
);
expect(messageOpen).toEqual(
new Uint8Array([
72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33,
])
);

const message2 = new Uint8Array([
8, 231, 240, 41, 106, 138, 234, 14, 38, 102, 70, 86, 168, 115, 93, 238, 3,
95, 224, 157, 125, 40, 151, 150, 147, 223, 7, 153, 132, 32, 92, 36,
]);
const message2Sealed = crypto_box_seal(message2, receiverKeyPair.publicKey);
const message2Open = crypto_box_seal_open(
message2Sealed,
receiverKeyPair.publicKey,
receiverKeyPair.privateKey
);
expect(message2Open).toEqual(message2);

expect(() => {
crypto_box_seal(
message,
new Uint8Array([
232, 167, 21, 228, 54, 165, 143, 50, 85, 27, 167, 176, 163, 211, 176, 7,
159, 111, 77, 250, 19, 16, 169, 199, 109, 135, 21, 253, 184, 239, 207,
172, 100,
])
);
}).toThrow();
});
60 changes: 60 additions & 0 deletions src/lib.native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,15 @@ declare global {
publicKey: ArrayBuffer,
secretKey: ArrayBuffer
): ArrayBuffer;
function jsi_crypto_box_seal(
message: string | ArrayBuffer,
publicKey: ArrayBuffer
): ArrayBuffer;
function jsi_crypto_box_seal_open(
ciphertext: string | ArrayBuffer,
publicKey: ArrayBuffer,
secretKey: ArrayBuffer
): ArrayBuffer;
function jsi_crypto_generichash(
hashLength: number,
message: string | ArrayBuffer,
Expand Down Expand Up @@ -529,6 +538,57 @@ export function crypto_box_open_easy(
return convertToOutputFormat(result, outputFormat);
}

export function crypto_box_seal(
ciphertext: string | Uint8Array,
publicKey: Uint8Array,
outputFormat?: Uint8ArrayOutputFormat | null
): Uint8Array;
export function crypto_box_seal(
ciphertext: string | Uint8Array,
publicKey: Uint8Array,
outputFormat: StringOutputFormat
): string;
export function crypto_box_seal(
ciphertext: string | Uint8Array,
publicKey: Uint8Array,
outputFormat: OutputFormat
) {
let result: ArrayBuffer;
const ciphertextParam =
typeof ciphertext === 'string' ? ciphertext : ciphertext.buffer;
result = global.jsi_crypto_box_seal(ciphertextParam, publicKey.buffer);
return convertToOutputFormat(result, outputFormat);
}

export function crypto_box_seal_open(
ciphertext: string | Uint8Array,
publicKey: Uint8Array,
privateKey: Uint8Array,
outputFormat?: Uint8ArrayOutputFormat | null
): Uint8Array;
export function crypto_box_seal_open(
ciphertext: string | Uint8Array,
publicKey: Uint8Array,
privateKey: Uint8Array,
outputFormat: StringOutputFormat
): string;
export function crypto_box_seal_open(
ciphertext: string | Uint8Array,
publicKey: Uint8Array,
privateKey: Uint8Array,
outputFormat: OutputFormat
) {
let result: ArrayBuffer;
const ciphertextParam =
typeof ciphertext === 'string' ? ciphertext : ciphertext.buffer;
result = global.jsi_crypto_box_seal_open(
ciphertextParam,
publicKey.buffer,
privateKey.buffer
);
return convertToOutputFormat(result, outputFormat);
}

export function crypto_generichash(
hash_length: number,
message: string | Uint8Array,
Expand Down

0 comments on commit 035a053

Please sign in to comment.