Skip to content

Commit

Permalink
feat: add hkdf 256
Browse files Browse the repository at this point in the history
  • Loading branch information
nikgraf committed Nov 28, 2023
1 parent fac49dc commit fdd7cb6
Show file tree
Hide file tree
Showing 11 changed files with 205 additions and 2 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ import {
to_string,
ready, // only needed for react-native-web
loadSumoVersion, // only relevant for react-native-web
_unstable_crypto_kdf_hkdf_sha256_BYTES_MAX, // has no counterpart in libsodium-wrappers yet
_unstable_crypto_kdf_hkdf_sha256_BYTES_MIN, // has no counterpart in libsodium-wrappers yet
_unstable_crypto_kdf_hkdf_sha256_KEYBYTES, // has no counterpart in libsodium-wrappers yet
_unstable_crypto_kdf_hkdf_sha256_extract, // has no counterpart in libsodium-wrappers yet
_unstable_crypto_kdf_hkdf_sha256_expand, // has no counterpart in libsodium-wrappers yet
} from 'react-native-libsodium';

// ...
Expand Down
78 changes: 78 additions & 0 deletions cpp/react-native-libsodium.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,9 @@ namespace ReactNativeLibsodium
jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_sign_SEEDBYTES", static_cast<int>(crypto_sign_SEEDBYTES));
jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_auth_BYTES", static_cast<int>(crypto_auth_BYTES));
jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_auth_KEYBYTES", static_cast<int>(crypto_auth_KEYBYTES));
jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_kdf_hkdf_sha256_BYTES_MAX", static_cast<int>(crypto_kdf_hkdf_sha256_BYTES_MAX));
jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_kdf_hkdf_sha256_BYTES_MIN", static_cast<int>(crypto_kdf_hkdf_sha256_BYTES_MIN));
jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_kdf_hkdf_sha256_KEYBYTES", static_cast<int>(crypto_kdf_hkdf_sha256_KEYBYTES));

auto jsi_from_base64_to_arraybuffer = jsi::Function::createFromHostFunction(
jsiRuntime,
Expand Down Expand Up @@ -1341,5 +1344,80 @@ namespace ReactNativeLibsodium
});

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

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

std::string keyArgumentName = "key";
unsigned int keyArgumentPosition = 0;
validateIsArrayBuffer(functionName, runtime, arguments[keyArgumentPosition], keyArgumentName, true);

std::string saltArgumentName = "salt";
unsigned int saltArgumentPosition = 1;
validateIsArrayBuffer(functionName, runtime, arguments[saltArgumentPosition], saltArgumentName, true);

std::vector<uint8_t> subkey(crypto_kdf_hkdf_sha256_KEYBYTES);
int result = -1;

auto keyArrayBuffer = arguments[keyArgumentPosition].asObject(runtime).getArrayBuffer(runtime);
auto saltArrayBuffer = arguments[saltArgumentPosition].asObject(runtime).getArrayBuffer(runtime);

result = crypto_kdf_hkdf_sha256_extract(
subkey.data(),
saltArrayBuffer.data(runtime),
saltArrayBuffer.length(runtime),
keyArrayBuffer.data(runtime),
keyArrayBuffer.length(runtime));

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

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

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

std::string keyArgumentName = "key";
unsigned int keyArgumentPosition = 0;
validateIsArrayBuffer(functionName, runtime, arguments[keyArgumentPosition], keyArgumentName, true);

std::string infoArgumentName = "info";
unsigned int infoArgumentPosition = 1;
validateIsString(functionName, runtime, arguments[infoArgumentPosition], infoArgumentName, true);

std::string subkeyLengthArgumentName = "subkeyLength";
unsigned int subkeyLengthArgumentPosition = 2;
validateIsNumber(functionName, runtime, arguments[subkeyLengthArgumentPosition], subkeyLengthArgumentName, true);

std::string info = arguments[infoArgumentPosition].asString(runtime).utf8(runtime);
int subkeyLength = arguments[subkeyLengthArgumentPosition].asNumber();
std::vector<uint8_t> subkey(subkeyLength);
int result = -1;

auto keyArrayBuffer = arguments[keyArgumentPosition].asObject(runtime).getArrayBuffer(runtime);

result = crypto_kdf_hkdf_sha256_expand(
subkey.data(),
subkey.size(),
reinterpret_cast<const char *>(info.data()),
info.length(),
keyArrayBuffer.data(runtime));

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

jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_kdf_hkdf_sha256_expand", std::move(jsi_crypto_kdf_hkdf_sha256_expand));
}
} // namespace ReactNativeLibsodium
4 changes: 2 additions & 2 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ PODS:
- React-jsinspector (0.71.6)
- React-logger (0.71.6):
- glog
- react-native-libsodium (0.7.0):
- react-native-libsodium (1.1.2):
- React-Core
- React-perflogger (0.71.6)
- React-RCTActionSheet (0.71.6):
Expand Down Expand Up @@ -606,7 +606,7 @@ SPEC CHECKSUMS:
React-jsiexecutor: 7894956638ff3e00819dd3f9f6f4a84da38f2409
React-jsinspector: d5ce2ef3eb8fd30c28389d0bc577918c70821bd6
React-logger: 9332c3e7b4ef007a0211c0a9868253aac3e1da82
react-native-libsodium: 532974631ce56ac3309c9e40040582982d624cc7
react-native-libsodium: 070bd408c7ccf5ca931077aeb4277f0929299bc1
React-perflogger: 43392072a5b867a504e2b4857606f8fc5a403d7f
React-RCTActionSheet: c7b67c125bebeda9fb19fc7b200d85cb9d6899c4
React-RCTAnimation: c2de79906f607986633a7114bee44854e4c7e2f5
Expand Down
2 changes: 2 additions & 0 deletions example/src/components/TestResults.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React, { useEffect, useState } from 'react';
import { Text, View } from 'react-native';
import '../tests/_unstable_crypto_kdf_hkdf_sha256_expand_test';
import '../tests/_unstable_crypto_kdf_hkdf_sha256_extract_test';
import '../tests/constants_test';
import '../tests/crypto_aead_xchacha20poly1305_ietf_decrypt_test';
import '../tests/crypto_aead_xchacha20poly1305_ietf_encrypt_test';
Expand Down
18 changes: 18 additions & 0 deletions example/src/tests/_unstable_crypto_kdf_hkdf_sha256_expand_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { _unstable_crypto_kdf_hkdf_sha256_expand } from 'react-native-libsodium';
import { expect, test } from '../utils/testRunner';

test('_unstable_crypto_kdf_hkdf_sha256_expand_test', () => {
const key = new Uint8Array([
75, 222, 64, 255, 217, 41, 81, 229, 21, 194, 0, 72, 125, 254, 2, 182, 113,
28, 24, 1, 227, 2, 226, 196, 127, 221, 56, 72, 15, 126, 128, 30,
]);

expect(
_unstable_crypto_kdf_hkdf_sha256_expand(key, 'some_context', 32)
).toEqual(
new Uint8Array([
94, 202, 158, 208, 160, 172, 67, 223, 68, 62, 180, 53, 79, 68, 173, 141,
136, 4, 177, 112, 84, 31, 14, 18, 40, 35, 230, 251, 53, 81, 81, 151,
])
);
});
21 changes: 21 additions & 0 deletions example/src/tests/_unstable_crypto_kdf_hkdf_sha256_extract_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { _unstable_crypto_kdf_hkdf_sha256_extract } from 'react-native-libsodium';
import { expect, test } from '../utils/testRunner';

test('_unstable_crypto_kdf_hkdf_sha256_extract_test', () => {
const key = new Uint8Array([
75, 222, 64, 255, 217, 41, 81, 229, 21, 194, 0, 72, 125, 254, 2, 182, 113,
28, 24, 1, 227, 2, 226, 196, 127, 221, 56, 72, 15, 126, 128, 30,
]);

const salt = new Uint8Array([
75, 222, 64, 255, 217, 41, 81, 229, 21, 194, 0, 72, 125, 254, 2, 182, 113,
28, 24, 1, 227, 2, 226, 196, 127, 221, 56, 72, 15, 126, 128, 30,
]);

expect(_unstable_crypto_kdf_hkdf_sha256_extract(key, salt)).toEqual(
new Uint8Array([
96, 198, 77, 78, 235, 17, 136, 179, 112, 240, 235, 48, 24, 198, 36, 27,
180, 72, 174, 165, 180, 1, 95, 228, 38, 21, 51, 60, 114, 37, 20, 108,
])
);
});
6 changes: 6 additions & 0 deletions example/src/tests/constants_test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import {
_unstable_crypto_kdf_hkdf_sha256_BYTES_MAX,
_unstable_crypto_kdf_hkdf_sha256_BYTES_MIN,
_unstable_crypto_kdf_hkdf_sha256_KEYBYTES,
crypto_aead_xchacha20poly1305_ietf_KEYBYTES,
crypto_auth_BYTES,
crypto_auth_KEYBYTES,
Expand All @@ -23,4 +26,7 @@ test('constants', () => {
expect(crypto_kdf_KEYBYTES).toEqual(32);
expect(crypto_kdf_CONTEXTBYTES).toEqual(8);
expect(crypto_sign_SEEDBYTES).toEqual(32);
expect(_unstable_crypto_kdf_hkdf_sha256_KEYBYTES).toEqual(32);
expect(_unstable_crypto_kdf_hkdf_sha256_BYTES_MIN).toEqual(0);
expect(_unstable_crypto_kdf_hkdf_sha256_BYTES_MAX).toEqual(8160);
});
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@
]
},
"dependencies": {
"@noble/hashes": "^1.3.2",
"@types/libsodium-wrappers": "^0.7.13",
"@types/libsodium-wrappers-sumo": "^0.7.8",
"libsodium-wrappers": "^0.7.13",
Expand Down
48 changes: 48 additions & 0 deletions src/lib.native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ declare global {
var jsi_crypto_pwhash_MEMLIMIT_INTERACTIVE: number;
var jsi_crypto_pwhash_BYTES_MIN: number;
var jsi_crypto_pwhash_BYTES_MAX: number;
var jsi_crypto_kdf_hkdf_sha256_BYTES_MAX: number;
var jsi_crypto_kdf_hkdf_sha256_BYTES_MIN: number;
var jsi_crypto_kdf_hkdf_sha256_KEYBYTES: number;

function jsi_crypto_auth(
message: string | ArrayBuffer,
Expand Down Expand Up @@ -146,6 +149,21 @@ declare global {
public_nonce: ArrayBuffer,
key: ArrayBuffer
): ArrayBuffer;
function jsi_crypto_aead_xchacha20poly1305_ietf_decrypt(
ciphertext: string | ArrayBuffer,
additionalData: string,
public_nonce: ArrayBuffer,
key: ArrayBuffer
): ArrayBuffer;
function jsi_crypto_kdf_hkdf_sha256_extract(
key: ArrayBuffer,
salt: ArrayBuffer
): ArrayBuffer;
function jsi_crypto_kdf_hkdf_sha256_expand(
key: ArrayBuffer,
info: string,
length: number
): ArrayBuffer;
}

export const crypto_auth_BYTES = global.jsi_crypto_auth_BYTES;
Expand Down Expand Up @@ -182,6 +200,12 @@ export const crypto_pwhash_MEMLIMIT_INTERACTIVE =
global.jsi_crypto_pwhash_MEMLIMIT_INTERACTIVE;
export const crypto_pwhash_BYTES_MIN = global.jsi_crypto_pwhash_BYTES_MIN;
export const crypto_pwhash_BYTES_MAX = global.jsi_crypto_pwhash_BYTES_MAX;
export const _unstable_crypto_kdf_hkdf_sha256_BYTES_MAX =
global.jsi_crypto_kdf_hkdf_sha256_BYTES_MAX;
export const _unstable_crypto_kdf_hkdf_sha256_BYTES_MIN =
global.jsi_crypto_kdf_hkdf_sha256_BYTES_MIN;
export const _unstable_crypto_kdf_hkdf_sha256_KEYBYTES =
global.jsi_crypto_kdf_hkdf_sha256_KEYBYTES;

export const from_base64 = (
input: string,
Expand Down Expand Up @@ -692,6 +716,25 @@ export function crypto_aead_xchacha20poly1305_ietf_decrypt(
return convertToOutputFormat(result, outputFormat);
}

export function _unstable_crypto_kdf_hkdf_sha256_extract(
key: Uint8Array,
salt: Uint8Array
) {
return new Uint8Array(
global.jsi_crypto_kdf_hkdf_sha256_extract(key.buffer, salt.buffer)
);
}

export function _unstable_crypto_kdf_hkdf_sha256_expand(
key: Uint8Array,
info: string,
length: number
) {
return new Uint8Array(
global.jsi_crypto_kdf_hkdf_sha256_expand(key.buffer, info, length)
);
}

// add no-op ready to match the libsodium-wrappers API
export const ready: Promise<void> = new Promise((resolve) => resolve());

Expand Down Expand Up @@ -748,4 +791,9 @@ export default {
to_base64,
to_hex,
to_string,
_unstable_crypto_kdf_hkdf_sha256_BYTES_MAX,
_unstable_crypto_kdf_hkdf_sha256_BYTES_MIN,
_unstable_crypto_kdf_hkdf_sha256_KEYBYTES,
_unstable_crypto_kdf_hkdf_sha256_extract,
_unstable_crypto_kdf_hkdf_sha256_expand,
};
19 changes: 19 additions & 0 deletions src/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export type {
StringSecretBox,
Uint8ArrayOutputFormat,
} from 'libsodium-wrappers';
import * as hkdf from '@noble/hashes/hkdf';
import { sha256 } from '@noble/hashes/sha256';

type SodiumPackage = typeof import('libsodium-wrappers');

Expand Down Expand Up @@ -818,3 +820,20 @@ export let crypto_sign_SECRETKEYBYTES = sodium.crypto_sign_SECRETKEYBYTES;
export let crypto_sign_SEEDBYTES = sodium.crypto_sign_SEEDBYTES;
export let SODIUM_VERSION_STRING = sodium.SODIUM_VERSION_STRING;
export let crypto_pwhash_STRPREFIX = sodium.crypto_pwhash_STRPREFIX;

export let _unstable_crypto_kdf_hkdf_sha256_BYTES_MAX = 8160;
export let _unstable_crypto_kdf_hkdf_sha256_BYTES_MIN = 0;
export let _unstable_crypto_kdf_hkdf_sha256_KEYBYTES = 32;
export let _unstable_crypto_kdf_hkdf_sha256_extract = (
key: Uint8Array,
salt: Uint8Array
) => {
return hkdf.extract(sha256, key, salt);
};
export let _unstable_crypto_kdf_hkdf_sha256_expand = (
key: Uint8Array,
info: string,
length: number
) => {
return hkdf.expand(sha256, key, info, length);
};
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1754,6 +1754,11 @@
dependencies:
eslint-scope "5.1.1"

"@noble/hashes@^1.3.2":
version "1.3.2"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39"
integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==

"@nodelib/[email protected]":
version "2.1.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
Expand Down

0 comments on commit fdd7cb6

Please sign in to comment.