From f0b2314553c0796806bbd3877db60f65ed2972ac Mon Sep 17 00:00:00 2001 From: Nik Graf Date: Tue, 28 Nov 2023 09:56:06 +0100 Subject: [PATCH] feat: add hkdf 256 --- README.md | 5 +++ cpp/react-native-libsodium.cpp | 78 ++++++++++++++++++++++++++++++++++ example/ios/Podfile.lock | 4 +- package.json | 1 + src/lib.native.ts | 44 +++++++++++++++++++ yarn.lock | 5 +++ 6 files changed, 135 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 317a5be..e640e44 100644 --- a/README.md +++ b/README.md @@ -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'; // ... diff --git a/cpp/react-native-libsodium.cpp b/cpp/react-native-libsodium.cpp index 69bcd80..8d55303 100644 --- a/cpp/react-native-libsodium.cpp +++ b/cpp/react-native-libsodium.cpp @@ -209,6 +209,9 @@ namespace ReactNativeLibsodium jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_sign_SEEDBYTES", static_cast(crypto_sign_SEEDBYTES)); jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_auth_BYTES", static_cast(crypto_auth_BYTES)); jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_auth_KEYBYTES", static_cast(crypto_auth_KEYBYTES)); + jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_kdf_hkdf_sha256_BYTES_MAX", static_cast(crypto_kdf_hkdf_sha256_BYTES_MAX)); + jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_kdf_hkdf_sha256_BYTES_MIN", static_cast(crypto_kdf_hkdf_sha256_BYTES_MIN)); + jsiRuntime.global().setProperty(jsiRuntime, "jsi_crypto_kdf_hkdf_sha256_KEYBYTES", static_cast(crypto_kdf_hkdf_sha256_KEYBYTES)); auto jsi_from_base64_to_arraybuffer = jsi::Function::createFromHostFunction( jsiRuntime, @@ -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 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 = 1; + validateIsNumber(functionName, runtime, arguments[subkeyLengthArgumentPosition], subkeyLengthArgumentName, true); + + std::string info = arguments[infoArgumentPosition].asString(runtime).utf8(runtime); + int subkeyLength = arguments[subkeyLengthArgumentPosition].asNumber(); + std::vector 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(info.data()), + info.length(), + keyArrayBuffer.length(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 diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index e3cdeda..fb189f0 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -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): @@ -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 diff --git a/package.json b/package.json index 8ad768c..a1e73a1 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/lib.native.ts b/src/lib.native.ts index f244d51..e8a6aae 100644 --- a/src/lib.native.ts +++ b/src/lib.native.ts @@ -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, @@ -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; @@ -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, @@ -692,6 +716,21 @@ export function crypto_aead_xchacha20poly1305_ietf_decrypt( return convertToOutputFormat(result, outputFormat); } +export function _unstable_crypto_kdf_hkdf_sha256_extract( + key: Uint8Array, + salt: Uint8Array +) { + return 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 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 = new Promise((resolve) => resolve()); @@ -748,4 +787,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, }; diff --git a/yarn.lock b/yarn.lock index da351aa..a539aa3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"