├── .clang-format ├── .gitattributes ├── .github └── workflows │ ├── prebuild.yml │ └── test.yml ├── .gitignore ├── CHANGELOG.md ├── CMakeLists.txt ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── binding.cc ├── binding.js ├── example.js ├── extensions ├── pbkdf2 │ ├── pbkdf2.c │ └── pbkdf2.h └── tweak │ ├── tweak.c │ └── tweak.h ├── index.js ├── package.json └── test ├── all.js ├── core_ed25519.js ├── crypto_aead_chacha20poly1305_ietf.js ├── crypto_aead_xchacha20poly1305_ietf.js ├── crypto_auth.js ├── crypto_box.js ├── crypto_generichash.js ├── crypto_hash.js ├── crypto_hash_sha256.js ├── crypto_hash_sha512.js ├── crypto_kdf.js ├── crypto_kx.js ├── crypto_onetimeauth.js ├── crypto_pwhash.js ├── crypto_pwhash_scryptsalsa208sha256.js ├── crypto_scalarmult.js ├── crypto_secretbox.js ├── crypto_secretstream.js ├── crypto_shorthash.js ├── crypto_sign.js ├── crypto_stream.js ├── crypto_stream_chacha20.js ├── crypto_stream_chacha20_ietf.js ├── extension_pbkdf2.js ├── extension_tweak_ed25519.js ├── fastcalls.js ├── fixtures ├── crypto_kdf.json ├── crypto_sign.json ├── crypto_tweak_ed25519_sign.js ├── mprotect_noaccess.js ├── mprotect_readonly.js ├── mprotect_readwrite.js └── pbkdf2.json ├── helpers.js ├── memory.js ├── padding.js ├── randombytes.js └── vectors.js /.clang-format: -------------------------------------------------------------------------------- 1 | AlignAfterOpenBracket: BlockIndent 2 | AlignConsecutiveMacros: Consecutive 3 | AlignEscapedNewlines: DontAlign 4 | AllowShortIfStatementsOnASingleLine: AllIfsAndElse 5 | AlwaysBreakAfterReturnType: All 6 | BinPackArguments: false 7 | BinPackParameters: false 8 | ContinuationIndentWidth: 2 9 | ColumnLimit: 0 10 | SpaceAfterCStyleCast: true 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/workflows/prebuild.yml: -------------------------------------------------------------------------------- 1 | name: Prebuild 2 | on: 3 | workflow_dispatch: 4 | jobs: 5 | prebuild: 6 | strategy: 7 | matrix: 8 | include: 9 | - os: ubuntu-22.04 10 | platform: linux 11 | arch: x64 12 | - os: ubuntu-22.04-arm 13 | platform: linux 14 | arch: arm64 15 | - os: ubuntu-22.04 16 | platform: android 17 | arch: x64 18 | flags: -D ANDROID_STL=c++_shared 19 | - os: ubuntu-22.04 20 | platform: android 21 | arch: ia32 22 | flags: -D ANDROID_STL=c++_shared 23 | - os: ubuntu-22.04 24 | platform: android 25 | arch: arm64 26 | flags: -D ANDROID_STL=c++_shared 27 | - os: ubuntu-22.04 28 | platform: android 29 | arch: arm 30 | flags: -D ANDROID_STL=c++_shared 31 | - os: macos-14 32 | platform: darwin 33 | arch: x64 34 | - os: macos-14 35 | platform: darwin 36 | arch: arm64 37 | - os: macos-14 38 | platform: ios 39 | arch: arm64 40 | - os: macos-14 41 | platform: ios 42 | arch: arm64 43 | tags: -simulator 44 | flags: --simulator 45 | - os: macos-14 46 | platform: ios 47 | arch: x64 48 | tags: -simulator 49 | flags: --simulator 50 | - os: windows-2022 51 | platform: win32 52 | arch: x64 53 | - os: windows-2022 54 | platform: win32 55 | arch: arm64 56 | runs-on: ${{ matrix.os }} 57 | name: ${{ matrix.platform }}-${{ matrix.arch }}${{ matrix.tags }} 58 | steps: 59 | - uses: actions/checkout@v4 60 | - uses: actions/setup-node@v4 61 | with: 62 | node-version: lts/* 63 | - run: choco upgrade llvm 64 | if: ${{ matrix.platform == 'win32' }} 65 | - run: npm install -g bare-make 66 | - run: npm install 67 | - run: bare-make generate --platform ${{ matrix.platform }} --arch ${{ matrix.arch }} ${{ matrix.flags }} 68 | - run: bare-make build 69 | - run: bare-make install 70 | - uses: actions/upload-artifact@v4 71 | with: 72 | name: ${{ matrix.platform }}-${{ matrix.arch }}${{ matrix.tags }} 73 | path: prebuilds/* 74 | merge: 75 | needs: prebuild 76 | runs-on: ubuntu-latest 77 | steps: 78 | - uses: actions/download-artifact@v4 79 | with: 80 | path: prebuilds 81 | merge-multiple: true 82 | - uses: actions/upload-artifact@v4 83 | with: 84 | name: prebuilds 85 | path: prebuilds 86 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | branches: 8 | - main 9 | jobs: 10 | test: 11 | strategy: 12 | matrix: 13 | include: 14 | - os: ubuntu-latest 15 | platform: linux 16 | arch: x64 17 | - os: macos-latest 18 | platform: darwin 19 | arch: arm64 20 | - os: windows-latest 21 | platform: win32 22 | arch: x64 23 | runs-on: ${{ matrix.os }} 24 | name: ${{ matrix.platform }}-${{ matrix.arch }} 25 | steps: 26 | - uses: actions/checkout@v4 27 | - uses: actions/setup-node@v4 28 | with: 29 | node-version: lts/* 30 | - run: choco upgrade llvm 31 | if: ${{ matrix.platform == 'win32' }} 32 | - run: npm install -g bare-runtime bare-make 33 | - run: npm install 34 | - run: bare-make generate --platform ${{ matrix.platform }} --arch ${{ matrix.arch }} --debug 35 | - run: bare-make build 36 | - run: bare-make install 37 | - run: npm test 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | prebuilds 3 | node_modules 4 | package-lock.json 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## Current 4 | 5 | ## V5.0.0 6 | 7 | * Changed native from `napi` to `libjs` 8 | * Fixed bug: no more double `close_handle_scope()` 9 | * Add asserts to all returns codes 10 | * Fix `SN_ASYNC_TASK` memleak 11 | * Moved deprecated `Buffer.slice()` calls to `Buffer.subarray()` in `test/*` 12 | * Optimized performance of most common calls 13 | 14 | ## v4.3.2 15 | * Update builds to fix msvc dependency. 16 | 17 | ## v4.3.1 18 | * Use cmake-fetch for fetching source on demand 19 | 20 | ## v4.3.0 21 | * Upgrade libsodium 22 | 23 | ## v4.2.2 24 | * Use bare-make for easy cross platform prebuilding. 25 | 26 | ## v4.2.1 27 | * Move to cmake for building. 28 | 29 | ## v4.2.0 30 | * Adds prebuilds for android, ios, and windows/linux arm64. 31 | 32 | ## v4.1.1 33 | * Missing extensions folder in build when building from source. 34 | 35 | ## v4.1.0 36 | * Refactor extensions into an `extension_*` namespace for clarity on the exports. 37 | * Add `pbkdf2` extension 38 | * async operations return promises, whilst maintaining callback compat. Callbacks scheduled for removal in v5. 39 | 40 | ## v4.0.10 41 | * Revert back to a static build for CMAKE. 42 | 43 | ## v4.0.9 44 | * With CMAKE only link the objects. 45 | 46 | ## v4.0.8 47 | * Fix pkg.addon to be just the boolean. 48 | 49 | ## v4.0.7 50 | * Add pkg.addon.target to explicitly know the CMAKE target. 51 | 52 | ## v4.0.6 53 | * Fix CMAKE flag for Windows. 54 | 55 | ## v4.0.5 56 | * Upgrade prebuildify to make named prebuilds. 57 | 58 | ## v4.0.4 59 | * Fix cmake file 60 | 61 | ## v4.0.3 62 | * Added missing cmake files. 63 | 64 | ## v4.0.2 65 | * Move build to support iOS/Android also. 66 | 67 | ## v4.0.1 68 | * Remove unneeded asserts. Also fixes an issue where they would get compiled out. 69 | 70 | ## v4.0.0 71 | * crypto_secretstream_xchacha20poly1305_push accepts an int instead of a buffer for the tag param. 72 | * crypto_secretstream_xchacha20poly1305_TAG_MESSAGE is now an int. 73 | * crypto_secretstream_xchacha20poly1305_TAG_PUSH is now an int. 74 | * crypto_secretstream_xchacha20poly1305_TAG_REKEY is now an int. 75 | * crypto_secretstream_xchacha20poly1305_TAG_FINAL is not an int. 76 | * Move to 1.0.18-stable instead of fixed 1.0.18 for easier build. 77 | * Fix memleak in secure buffers. 78 | * Moved to uv workers instead of n-api ones for async methods. 79 | * musl prebuilds (for alpine linux). 80 | * Update experimental tweak api. 81 | 82 | ## v3.4.1 83 | * Fixed intel prebuild to still support sse (performance enhancement) 84 | 85 | ## v3.4.0 86 | * Added experimental key tweaking api for signing. 87 | 88 | ## v3.3.0 89 | * Moved to a static build to reduce build complexity and allow more platforms for our prebuilds. 90 | 91 | ## v3.2.1 92 | * Normalised and typo fixed error messages (Thanks @martinheidegger) 93 | 94 | ## v3.2.0 95 | * Add missing `napi` prototype for Node v10 96 | * Make "missing" checks behave like Javascript (`x == null`) 97 | * Typo in error message (Thanks @christianbundy) 98 | * Add missing `crypto_stream_xchacha20_*` `crypto_stream_salsa20_*` APIs 99 | 100 | ## v3.1.1 101 | 102 | * Bump `prebuildify`. Electron no longer needs a custom napi build 103 | 104 | ## v3.1.0 105 | 106 | * Add explicit `sodium.sodium_free(buf)` to free the memory backed by a secure 107 | buffer. This uses the detach semantics known from `.transfer` in the browser and 108 | from Node worker threads. This is a no-op on older versions of Node and is 109 | currently pending backporting to Node 10.x 110 | * External memory book-keeping. For every secure buffer we now increment the 111 | external memory of node by 16 kb to better hint the garbage collector about the 112 | true consumption of secure buffers. This is not exactly representative if more 113 | than a page of system memory is allocated by the user nor if the system page 114 | size is not 4kb. 115 | * Throw an exception of `sodium_malloc` returns a NULL pointer (eg unable to 116 | allocate secure memory). 117 | * Expose new APIs and associated constants: `crypto_stream_chacha20`, 118 | `crypto_stream_chacha20_ietf`, `crypto_aead_chacha20`, `crypto_aead_chacha20_ietf`, 119 | * Expose `crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX`, `crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX` and `crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX` as `BigInt`s 120 | 121 | ## v3.0.1 122 | 123 | * Fixed an issue that caused an assert error if an async callback threw an exception. 124 | 125 | ## v3.0.0 126 | 127 | * Updated to use n-api (@chm-diederichs). 128 | * Removed object instance apis and replaced them with init, update, final methods. 129 | * Bumped dev dependencies. 130 | * Bumped libsodium to 1.0.18. 131 | 132 | ## v2.4.10 133 | 134 | * Prebuilds for Electron 8 135 | 136 | ## v2.4.9 137 | 138 | * Downgrades npm on travis to 6.11.x as we cannot build prebuilds with node-gyp@5.0.5. Can be upgraded again when npm ships node-gyp@6. 139 | 140 | ## v2.4.8 141 | 142 | * Removing Node 4 and 6 from Travis as the config does not work there. We still build for 4 and 6 though. 143 | 144 | ## v2.4.7 145 | 146 | * Prebuilds for Node 13 and new Electron 147 | 148 | ## v2.4.6 149 | 150 | * Prebuildify fixes 151 | 152 | ## v2.4.5 153 | 154 | * node-gyp-build was accidentally added as a dev dependency. 155 | 156 | ## v2.4.4 157 | 158 | * Fix issue with node-gyp using the node 6.0.0 headers for electron 6.0.0 when prebuilding 159 | 160 | ## v2.4.3 161 | 162 | * Add Node 12 and Electron 5 support (thanks @davedoesdev) 163 | 164 | ## v2.4.2 165 | 166 | * Do travis release on `lts/*` node version 167 | 168 | ## v2.4.1 169 | 170 | * We cannot yet support Node 12 or Electron 5, so explicit versions on prebuildify. 171 | 172 | ## v2.4.0 173 | 174 | * Fix documentation error (thanks @jedisct1) 175 | * Add `crypto_pwhash_scryptsalsa208sha256_*` functions and constants. 176 | 177 | ## v2.3.0 178 | 179 | * Upgrade to libsodium 1.0.17 180 | * Add new `sodium_sub` (opposite of `sodium_add`) 181 | * Add new finite field operations `crypto_core_ed25519_*` and constants 182 | * Add `crypto_sign_ed25519_sk_to_pk` 183 | 184 | ## v2.2.6 185 | 186 | * Rebuilding the electron prebuild to get 4.0.4 support to work. This has a fix 187 | for an ABI mismatch. 188 | 189 | ## v2.2.5 190 | 191 | * Rebuilding the electron prebuild to get 4.0.0 support to work. 192 | 193 | ## v2.2.4 194 | 195 | * Updated cross-references to libsodium documentation (thanks @stripedpajamas) 196 | * Fix documentation typo (thanks @ralphtheninja) 197 | * Fix [DEP0005] DeprecationWarning: `Buffer()` (thanks @ralphtheninja) 198 | * Upgrade and be compliant with standard@12 199 | * Improve robustness of Windows builds. This means that we now use the "best" 200 | possible MSBuild. Thank you for all the work @enko 201 | * Due to the previous effort we can now build all artefacts on Travis and their 202 | new Windows offering 203 | * Amend the new MSBuild finding algorithm to look for "Program Files (x86)" 204 | first, such that cross-compiling works 205 | 206 | ## v2.2.3 207 | 208 | * Add Node 11 to build matrix 209 | 210 | ## v2.2.2 211 | 212 | * Document release process 213 | * Wrong error messages wrt. `crypto_sign`. Thanks @jackschmidt 214 | * Build for Electron v3.0.0 215 | 216 | ## v2.2.1 217 | 218 | * Fix CHANGELOG 219 | 220 | ## v2.2.0 221 | 222 | * Register tags for `async_hook`s on `crypto_pwhash_*_async` functions 223 | * Add constants and methods for `crypto_aead_xchacha20poly1305_ietf_*`. Please 224 | note the special circumstances around the bindings of `MESSAGEBYTES_MAX` and 225 | `crypto_aead_xchacha20poly1305_ietf_*_detached`. 226 | * Improved error messages; now reports the constants and argument names 227 | documented from javascript. 228 | * Use `Buffer.alloc`/`Buffer.fill`/`Buffer.from` in tests and examples 229 | * Add more libsodium helpers; `sodium_memcmp`, `sodium_compare`, `sodium_add`, 230 | `sodium_increment`, `sodium_is_zero` 231 | * Make it possible to pass only one of `rx` or `tx` to `crypto_kx_*` 232 | * Add `crypto_scalarmult_ed25519_*` and `crypto_core_ed25519_*` operations 233 | 234 | ## v2.1.6 235 | 236 | * Additional check `x < 0` before cast on uint assert macro 237 | * Add prebuilds for Node 10 238 | 239 | ## v2.1.5 240 | 241 | Fixes a critical bug in `crypto_secretstream_xchacha20poly1305_init_push` where 242 | it would call `crypto_secretstream_xchacha20poly1305_init_pull` instead. 243 | 244 | ## v2.1.4 245 | 246 | Only use the constants that `libsodium` compiled with instead of the ones that 247 | `sodium-native` compiled with. This has caused bugs for some users and may have 248 | led to subtle bugs. 249 | 250 | ## v2.1.3 251 | 252 | Rework build process so it is more versatile on UNIX operating systems by 253 | parsing the libtool archive files for correct .so name. This fixes builds on 254 | OpenBSD (#54) 255 | 256 | ## v2.1.2 257 | 258 | Fix `armv7l` builds. 259 | 260 | ## v2.1.1 261 | 262 | A mistake was made in generating prebuilds for v2.1.0, this version resolves the 263 | issue. 264 | 265 | ## v2.1.0 266 | - Upgrade to libsodium 1.0.16 267 | - Expose the new `crypto_secretstream` API 268 | - Expose `crypto_kx` API 269 | - Expose `sodium_pad` and `sodium_unpad` APIs 270 | - Expose `crypto_pwhash_str_needs_rehash` 271 | - Expose `randombytes_SEEDBYTES` `randombytes_random`, `randombytes_uniform` and 272 | `randombytes_buf_deterministic` 273 | - Check for `NULL` on `sodium_malloc` 274 | - All "Secure Buffers" (created with `sodium_malloc`) now have an immutable 275 | `.secure = true` property 276 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.25) 2 | 3 | find_package(cmake-bare REQUIRED PATHS node_modules/cmake-bare) 4 | find_package(cmake-fetch REQUIRED PATHS node_modules/cmake-fetch) 5 | find_package(cmake-napi REQUIRED PATHS node_modules/cmake-napi) 6 | find_package(cmake-npm REQUIRED PATHS node_modules/cmake-npm) 7 | 8 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 9 | 10 | project(sodium_native C ASM CXX) 11 | 12 | fetch_package("github:jedisct1/libsodium#stable" SOURCE_DIR sodium) 13 | fetch_package("github:holepunchto/libjstl#d92f140c") 14 | 15 | bare_target(target) 16 | 17 | if(target MATCHES "win32") 18 | add_compile_options(/MT$<$:d>) 19 | endif() 20 | 21 | file(COPY_FILE "${sodium}/builds/msvc/version.h" "${sodium}/src/libsodium/include/sodium/version.h") 22 | 23 | file(GLOB_RECURSE sodium_headers CONFIGURE_DEPENDS "${sodium}/src/libsodium/**/*.h") 24 | file(GLOB_RECURSE sodium_sources CONFIGURE_DEPENDS "${sodium}/src/libsodium/**/*.c") 25 | file(GLOB_RECURSE sodium_asm_sources CONFIGURE_DEPENDS "${sodium}/src/libsodium/**/*.S") 26 | 27 | add_library(sodium OBJECT) 28 | 29 | target_sources( 30 | sodium 31 | INTERFACE 32 | ${sodium_headers} 33 | PRIVATE 34 | ${sodium_sources} 35 | ) 36 | 37 | target_include_directories( 38 | sodium 39 | INTERFACE 40 | "${sodium}/src/libsodium/include" 41 | PRIVATE 42 | "${sodium}/src/libsodium/include/sodium" 43 | ) 44 | 45 | if(NOT target MATCHES "win32") 46 | target_compile_options( 47 | sodium 48 | PRIVATE 49 | -fvisibility=hidden 50 | -fno-strict-aliasing 51 | -fwrapv 52 | -flax-vector-conversions 53 | ) 54 | endif() 55 | 56 | target_compile_definitions( 57 | sodium 58 | PUBLIC 59 | SODIUM_STATIC=1 60 | PRIVATE 61 | _GNU_SOURCE=1 62 | CONFIGURED=1 63 | DEV_MODE=1 64 | HAVE_ATOMIC_OPS=1 65 | HAVE_C11_MEMORY_FENCES=1 66 | HAVE_CET_H=1 67 | HAVE_GCC_MEMORY_FENCES=1 68 | HAVE_INLINE_ASM=1 69 | HAVE_INTTYPES_H=1 70 | HAVE_STDINT_H=1 71 | HAVE_TI_MODE=1 72 | ) 73 | 74 | if(target MATCHES "darwin|ios") 75 | target_compile_definitions( 76 | sodium 77 | PRIVATE 78 | ASM_HIDE_SYMBOL=.private_extern 79 | TLS=_Thread_local 80 | HAVE_ARC4RANDOM=1 81 | HAVE_ARC4RANDOM_BUF=1 82 | HAVE_CATCHABLE_ABRT=1 83 | HAVE_CATCHABLE_SEGV=1 84 | HAVE_CLOCK_GETTIME=1 85 | HAVE_GETPID=1 86 | HAVE_MADVISE=1 87 | HAVE_MEMSET_S=1 88 | HAVE_MLOCK=1 89 | HAVE_MMAP=1 90 | HAVE_MPROTECT=1 91 | HAVE_NANOSLEEP=1 92 | HAVE_POSIX_MEMALIGN=1 93 | HAVE_PTHREAD=1 94 | HAVE_PTHREAD_PRIO_INHERIT=1 95 | HAVE_RAISE=1 96 | HAVE_SYSCONF=1 97 | HAVE_SYS_MMAN_H=1 98 | HAVE_SYS_PARAM_H=1 99 | HAVE_WEAK_SYMBOLS=1 100 | ) 101 | 102 | if(NOT target MATCHES "ios") 103 | target_compile_definitions( 104 | sodium 105 | PRIVATE 106 | HAVE_GETENTROPY=1 107 | HAVE_SYS_RANDOM_H=1 108 | ) 109 | endif() 110 | endif() 111 | 112 | if(target MATCHES "linux") 113 | target_compile_definitions( 114 | sodium 115 | PRIVATE 116 | ASM_HIDE_SYMBOL=.hidden 117 | TLS=_Thread_local 118 | HAVE_CATCHABLE_ABRT=1 119 | HAVE_CATCHABLE_SEGV=1 120 | HAVE_CLOCK_GETTIME=1 121 | HAVE_GETPID=1 122 | HAVE_MADVISE=1 123 | HAVE_MLOCK=1 124 | HAVE_MMAP=1 125 | HAVE_MPROTECT=1 126 | HAVE_NANOSLEEP=1 127 | HAVE_POSIX_MEMALIGN=1 128 | HAVE_PTHREAD_PRIO_INHERIT=1 129 | HAVE_PTHREAD=1 130 | HAVE_RAISE=1 131 | HAVE_SYSCONF=1 132 | HAVE_SYS_AUXV_H=1 133 | HAVE_SYS_MMAN_H=1 134 | HAVE_SYS_PARAM_H=1 135 | HAVE_SYS_RANDOM_H=1 136 | HAVE_WEAK_SYMBOLS=1 137 | ) 138 | endif() 139 | 140 | if(target MATCHES "win32") 141 | target_compile_definitions( 142 | sodium 143 | PRIVATE 144 | _CRT_SECURE_NO_WARNINGS=1 145 | HAVE_RAISE=1 146 | ) 147 | endif() 148 | 149 | if(target MATCHES "x64") 150 | target_compile_definitions( 151 | sodium 152 | PRIVATE 153 | HAVE_CPUID=1 154 | HAVE_RDRAND=1 155 | HAVE_EMMINTRIN_H=1 # SSE2 156 | HAVE_PMMINTRIN_H=1 # SSE3 157 | HAVE_TMMINTRIN_H=1 # SSSE3 158 | HAVE_SMMINTRIN_H=1 # SSE4.1 159 | HAVE_WMMINTRIN_H=1 # AES 160 | HAVE_AVXINTRIN_H=1 # AVX 161 | HAVE_AVX2INTRIN_H=1 # AVX2 162 | HAVE_AVX512FINTRIN_H # AVX512F 163 | ) 164 | 165 | if(NOT target MATCHES "win32") 166 | target_compile_definitions( 167 | sodium 168 | PRIVATE 169 | HAVE_AMD64_ASM=1 170 | HAVE_AVX_ASM=1 171 | ) 172 | 173 | target_sources( 174 | sodium 175 | PRIVATE 176 | ${sodium_asm_sources} 177 | ) 178 | endif() 179 | endif() 180 | 181 | if(target MATCHES "arm64") 182 | target_compile_definitions( 183 | sodium 184 | PRIVATE 185 | HAVE_ARMCRYPTO=1 186 | ) 187 | endif() 188 | 189 | if(CMAKE_C_BYTE_ORDER MATCHES "BIG_ENDIAN") 190 | target_compile_definitions( 191 | sodium 192 | PRIVATE 193 | NATIVE_BIG_ENDIAN=1 194 | ) 195 | else() 196 | target_compile_definitions( 197 | sodium 198 | PRIVATE 199 | NATIVE_LITTLE_ENDIAN=1 200 | ) 201 | endif() 202 | 203 | if(target MATCHES "linux|android") 204 | target_link_options( 205 | sodium 206 | PUBLIC 207 | -Wl,-z,noexecstack 208 | ) 209 | endif() 210 | 211 | add_bare_module(sodium_native_bare) 212 | 213 | target_sources( 214 | ${sodium_native_bare} 215 | PRIVATE 216 | binding.cc 217 | extensions/tweak/tweak.c 218 | extensions/tweak/tweak.h 219 | extensions/pbkdf2/pbkdf2.c 220 | extensions/pbkdf2/pbkdf2.h 221 | ) 222 | 223 | target_link_libraries( 224 | ${sodium_native_bare} 225 | PRIVATE 226 | $ 227 | jstl 228 | PUBLIC 229 | sodium 230 | ) 231 | 232 | add_napi_module(sodium_native_node) 233 | 234 | target_sources( 235 | ${sodium_native_node} 236 | PRIVATE 237 | binding.cc 238 | extensions/tweak/tweak.c 239 | extensions/tweak/tweak.h 240 | extensions/pbkdf2/pbkdf2.c 241 | extensions/pbkdf2/pbkdf2.h 242 | ) 243 | 244 | target_compile_definitions( 245 | ${sodium_native_node} 246 | PRIVATE 247 | NAPI_VERSION=9 248 | ) 249 | 250 | target_link_libraries( 251 | ${sodium_native_node} 252 | PRIVATE 253 | $ 254 | jstl 255 | PUBLIC 256 | sodium 257 | ) 258 | 259 | resolve_node_module(bare-compat-napi compat) 260 | 261 | target_include_directories( 262 | ${sodium_native_node} 263 | PRIVATE 264 | "${compat}/include" 265 | ) 266 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Setup 4 | 5 | The node script in deps/bin.js handles fetching source code, windows prebuild DLL 6 | and building the initial setup on all platforms. To trigger either run `npm install`, 7 | or call it manually (it has no npm dependencies). 8 | 9 | ## Release 10 | 11 | * Change the title of "Next" to the next version in the changelog 12 | * Update the link to the current released docs version in the README file 13 | * Tag a new release and push to Github, triggering CI services to test and build 14 | artifacts for windows (32 and 64 bit), MacOS (64 bit) and Linux (64 bit). 15 | NOTE: for now this step is done manually as Github actions has disabled the ubuntu 16 runner we use for compat. 16 | * Produce `arm7l` artifacts. We use a Raspberry 3+ Model B with raspbian for 17 | this, following the steps from `.travis.yml` with stock version of `gcc`, 18 | `autotools` and `make`: 19 | ``` 20 | npm install 21 | npm test 22 | npm run prebuild 23 | tar --create --verbose --file="`git describe --tags`-linux-`uname -m`.tar" --directory "./prebuilds" . 24 | ``` 25 | This tar file should be uploaded to Github Release like the artifacts produced 26 | by CI services. 27 | * Clean out the repository on your local computer: 28 | ``` 29 | git clean -x -d -f 30 | ``` 31 | * Add prebuild artifacts: 32 | - `mkdir prebuilds` 33 | - Download all artifacts from Github Releases 34 | - Extract tar archives and zip files into `prebuilds` 35 | * `npm publish` 36 | * Enjoy! 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Mathias Buus and Emil Bay 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sodium-native 2 | 3 | Low level bindings for [libsodium](https://github.com/jedisct1/libsodium). 4 | 5 | ``` 6 | npm install sodium-native 7 | ``` 8 | 9 | The goal of this project is to be thin, stable, unopionated wrapper around libsodium. 10 | 11 | All methods exposed are more or less a direct translation of the libsodium c-api. 12 | This means that most data types are buffers and you have to manage allocating return values and passing them in as arguments instead of receiving them as return values. 13 | 14 | This makes this API harder to use than other libsodium wrappers out there, but also means that you'll be able to get a lot of perf / memory improvements as you can do stuff like inline encryption / decryption, reuse buffers etc. 15 | 16 | This also makes this library useful as a foundation for more high level crypto abstractions that you want to make. 17 | 18 | ## Usage 19 | 20 | ``` js 21 | var sodium = require('sodium-native') 22 | 23 | var nonce = Buffer.alloc(sodium.crypto_secretbox_NONCEBYTES) 24 | var key = sodium.sodium_malloc(sodium.crypto_secretbox_KEYBYTES) // secure buffer 25 | var message = Buffer.from('Hello, World!') 26 | var ciphertext = Buffer.alloc(message.length + sodium.crypto_secretbox_MACBYTES) 27 | 28 | sodium.randombytes_buf(nonce) // insert random data into nonce 29 | sodium.randombytes_buf(key) // insert random data into key 30 | 31 | // encrypted message is stored in ciphertext. 32 | sodium.crypto_secretbox_easy(ciphertext, message, nonce, key) 33 | 34 | console.log('Encrypted message:', ciphertext) 35 | 36 | var plainText = Buffer.alloc(ciphertext.length - sodium.crypto_secretbox_MACBYTES) 37 | 38 | if (!sodium.crypto_secretbox_open_easy(plainText, ciphertext, nonce, key)) { 39 | console.log('Decryption failed!') 40 | } else { 41 | console.log('Decrypted message:', plainText, '(' + plainText.toString() + ')') 42 | } 43 | ``` 44 | 45 | ## Documentation 46 | 47 | Complete documentation may be found on the [sodium-friends website](https://sodium-friends.github.io/docs/docs/getstarted) 48 | 49 | ## License 50 | 51 | MIT 52 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | ## Security contact information 2 | 3 | To report a security vulnerability, please use the 4 | [Tidelift security contact](https://tidelift.com/security). 5 | Tidelift will coordinate the fix and disclosure. 6 | -------------------------------------------------------------------------------- /binding.js: -------------------------------------------------------------------------------- 1 | require.addon = require('require-addon') 2 | module.exports = require.addon('.', __filename) 3 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | const sodium = require('.') 2 | 3 | const nonce = Buffer.alloc(sodium.crypto_secretbox_NONCEBYTES) 4 | const key = sodium.sodium_malloc(sodium.crypto_secretbox_KEYBYTES) 5 | const message = Buffer.from('Hello, World!') 6 | const cipher = Buffer.alloc(message.length + sodium.crypto_secretbox_MACBYTES) 7 | 8 | sodium.randombytes_buf(nonce) // insert random data into nonce 9 | sodium.randombytes_buf(key) // insert random data into key 10 | 11 | // encrypted message is stored in cipher. 12 | sodium.crypto_secretbox_easy(cipher, message, nonce, key) 13 | 14 | console.log('Encrypted message:', cipher) 15 | 16 | const plainText = Buffer.alloc(cipher.length - sodium.crypto_secretbox_MACBYTES) 17 | 18 | if (!sodium.crypto_secretbox_open_easy(plainText, cipher, nonce, key)) { 19 | console.log('Decryption failed!') 20 | } else { 21 | console.log('Decrypted message:', plainText, '(' + plainText.toString() + ')') 22 | } 23 | -------------------------------------------------------------------------------- /extensions/pbkdf2/pbkdf2.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright 2005,2007,2009 Colin Percival 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | */ 26 | 27 | /* 28 | * Adapted from libsodium/crypto_pwhash/scryptsalsa208sha256/pbkdf-sha256.c 29 | */ 30 | 31 | #include 32 | #include 33 | 34 | #include "pbkdf2.h" 35 | 36 | /** 37 | * pbkdf2_sha512(passwd, passwdlen, salt, saltlen, c, buf, dkLen): 38 | * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and 39 | * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). 40 | */ 41 | int 42 | sn__extension_pbkdf2_sha512(const unsigned char *passwd, size_t passwdlen, 43 | const unsigned char *salt, size_t saltlen, uint64_t c, 44 | unsigned char *buf, size_t dkLen) 45 | { 46 | crypto_auth_hmacsha512_state PShctx, hctx; 47 | size_t i; 48 | unsigned char ivec[4]; 49 | unsigned char U[64]; 50 | unsigned char T[64]; 51 | uint64_t j; 52 | unsigned int k; 53 | size_t clen; 54 | 55 | if (dkLen > sn__extension_pbkdf2_sha512_BYTES_MAX) { 56 | return -1; 57 | } 58 | 59 | crypto_auth_hmacsha512_init(&PShctx, passwd, passwdlen); 60 | crypto_auth_hmacsha512_update(&PShctx, salt, saltlen); 61 | 62 | for (i = 0; i * crypto_auth_hmacsha512_BYTES < dkLen; i++) { 63 | SN_PBKDF2_STORE32_BE(ivec, (uint32_t)(i + 1)); 64 | memcpy(&hctx, &PShctx, sizeof(crypto_auth_hmacsha512_state)); 65 | crypto_auth_hmacsha512_update(&hctx, ivec, 4); 66 | crypto_auth_hmacsha512_final(&hctx, U); 67 | 68 | memcpy(T, U, crypto_auth_hmacsha512_BYTES); 69 | /* LCOV_EXCL_START */ 70 | for (j = 2; j <= c; j++) { 71 | crypto_auth_hmacsha512_init(&hctx, passwd, passwdlen); 72 | crypto_auth_hmacsha512_update(&hctx, U, crypto_auth_hmacsha512_BYTES); 73 | crypto_auth_hmacsha512_final(&hctx, U); 74 | 75 | for (k = 0; k < crypto_auth_hmacsha512_BYTES; k++) { 76 | T[k] ^= U[k]; 77 | } 78 | } 79 | /* LCOV_EXCL_STOP */ 80 | 81 | clen = dkLen - i * crypto_auth_hmacsha512_BYTES; 82 | if (clen > crypto_auth_hmacsha512_BYTES) { 83 | clen = crypto_auth_hmacsha512_BYTES; 84 | } 85 | memcpy(&buf[i * crypto_auth_hmacsha512_BYTES], T, clen); 86 | } 87 | sodium_memzero((void *) &PShctx, sizeof PShctx); 88 | 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /extensions/pbkdf2/pbkdf2.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright 2005,2007,2009 Colin Percival 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 | * SUCH DAMAGE. 25 | * 26 | */ 27 | 28 | /* 29 | * Adapted from libsodium/crypto_pwhash/scryptsalsa208sha256/pbkdf-sha256.c 30 | */ 31 | #ifdef __cplusplus 32 | extern "C" { 33 | #endif 34 | 35 | #include 36 | 37 | #define SN_PBKDF2_STORE32_BE(buf, n32) \ 38 | buf[0] = n32 >> 24 & 0xff; \ 39 | buf[1] = n32 >> 16 & 0xff; \ 40 | buf[2] = n32 >> 8 & 0xff; \ 41 | buf[3] = n32 >> 0 & 0xff; 42 | 43 | #define sn__extension_pbkdf2_sha512_SALTBYTES 16U 44 | 45 | #define sn__extension_pbkdf2_sha512_HASHBYTES crypto_hash_sha512_BYTES 46 | 47 | #define sn__extension_pbkdf2_sha512_ITERATIONS_MIN 1U 48 | 49 | #define sn__extension_pbkdf2_sha512_BYTES_MAX 0x3fffffffc0ULL 50 | 51 | /** 52 | * extension_pbkdf2_sha512(passwd, passwdlen, salt, saltlen, c, buf, dkLen): 53 | * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and 54 | * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). 55 | */ 56 | int sn__extension_pbkdf2_sha512(const unsigned char *, size_t, const unsigned char *, size_t, 57 | uint64_t, unsigned char *, size_t); 58 | #ifdef __cplusplus 59 | }; 60 | #endif 61 | -------------------------------------------------------------------------------- /extensions/tweak/tweak.c: -------------------------------------------------------------------------------- 1 | #include "tweak.h" 2 | 3 | /* 4 | *EXPERIMENTAL API* 5 | 6 | This module is an experimental implementation of a key tweaking protocol 7 | over ed25519 keys. The signature algorithm has been reimplemented from 8 | libsodium, but the nonce generation algorithm is *non-standard*. 9 | 10 | Use at your own risk 11 | */ 12 | 13 | static void _extension_tweak_nonce (unsigned char *nonce, const unsigned char *n, 14 | const unsigned char *m, unsigned long long mlen) 15 | { 16 | // dom2(x, y) with x = 0 (not prehashed) and y = "crypto_tweak_ed25519" 17 | static const unsigned char TWEAK_PREFIX[32 + 2 + 20] = { 18 | 'S', 'i', 'g', 'E', 'd', '2', '5', '5', '1', '9', ' ', 19 | 'n', 'o', ' ', 'E', 'd', '2', '5', '5', '1', '9', ' ', 20 | 'c', 'o', 'l', 'l', 'i', 's', 'i', 'o', 'n', 's', 0, 21 | 20, 'c', 'r', 'y', 'p', 't', 'o', '_', 't', 'w', 'e', 22 | 'a', 'k', '_', 'e', 'd', '2', '5', '5', '1', '9' 23 | }; 24 | 25 | crypto_hash_sha512_state hs; 26 | 27 | crypto_hash_sha512_init(&hs); 28 | crypto_hash_sha512_update(&hs, TWEAK_PREFIX, sizeof TWEAK_PREFIX); 29 | crypto_hash_sha512_update(&hs, n, 32); 30 | crypto_hash_sha512_update(&hs, m, mlen); 31 | crypto_hash_sha512_final(&hs, nonce); 32 | } 33 | 34 | static inline void 35 | _crypto_sign_ed25519_clamp(unsigned char k[32]) 36 | { 37 | k[0] &= 248; 38 | k[31] &= 127; 39 | k[31] |= 64; 40 | } 41 | 42 | static void _extension_tweak_ed25519(unsigned char *q, unsigned char *n, 43 | const unsigned char *ns, unsigned long long nslen) 44 | { 45 | sodium_memzero(q, sizeof q); 46 | 47 | crypto_hash(n, ns, nslen); 48 | n[31] &= 127; // clear highest bit 49 | 50 | crypto_scalarmult_ed25519_base_noclamp(q, n); 51 | 52 | // hash tweak until we get a valid tweaked q 53 | while (crypto_core_ed25519_is_valid_point(q) != 1) { 54 | crypto_hash(n, n, 32); 55 | n[31] &= 127; // clear highest bit 56 | 57 | crypto_scalarmult_ed25519_base_noclamp(q, n); 58 | } 59 | } 60 | 61 | void sn__extension_tweak_ed25519_base(unsigned char *pk, unsigned char *scalar, 62 | const unsigned char *ns, unsigned long long nslen) 63 | { 64 | unsigned char n64[64]; 65 | 66 | _extension_tweak_ed25519(pk, n64, ns, nslen); 67 | 68 | SN_TWEAK_COPY_32(scalar, n64) 69 | } 70 | 71 | int sn__extension_tweak_ed25519_sign_detached(unsigned char *sig, unsigned long long *siglen_p, 72 | const unsigned char *m, unsigned long long mlen, 73 | const unsigned char *n, unsigned char *pk) 74 | { 75 | crypto_hash_sha512_state hs; 76 | 77 | unsigned char nonce[64]; 78 | unsigned char R[32]; 79 | unsigned char hram[64]; 80 | unsigned char _pk[32]; 81 | 82 | // check if pk was passed 83 | if (pk == NULL) { 84 | pk = _pk; 85 | 86 | // derive pk from scalar 87 | if (crypto_scalarmult_ed25519_base_noclamp(pk, n) != 0) { 88 | return -1; 89 | } 90 | } 91 | 92 | _extension_tweak_nonce(nonce, n, m, mlen); 93 | crypto_core_ed25519_scalar_reduce(nonce, nonce); 94 | 95 | // R = G ^ nonce : curve point from nonce 96 | if (crypto_scalarmult_ed25519_base_noclamp(R, nonce) != 0) { 97 | return -1; 98 | } 99 | 100 | // generate challenge as h(ram) = hash(R, pk, message) 101 | crypto_hash_sha512_init(&hs); 102 | crypto_hash_sha512_update(&hs, R, 32); 103 | crypto_hash_sha512_update(&hs, pk, 32); 104 | crypto_hash_sha512_update(&hs, m, mlen); 105 | 106 | crypto_hash_sha512_final(&hs, hram); 107 | 108 | crypto_core_ed25519_scalar_reduce(hram, hram); 109 | 110 | // sig = nonce + n * h(ram) 111 | crypto_core_ed25519_scalar_mul(sig, hram, n); 112 | crypto_core_ed25519_scalar_add(sig + 32, nonce, sig); 113 | 114 | SN_TWEAK_COPY_32(sig, R) 115 | 116 | if (siglen_p != NULL) { 117 | *siglen_p = 64U; 118 | } 119 | 120 | return 0; 121 | } 122 | 123 | // tweak a secret key 124 | void sn__extension_tweak_ed25519_sk_to_scalar(unsigned char *n, const unsigned char *sk) 125 | { 126 | unsigned char n64[64]; 127 | 128 | // get sk scalar from seed, cf. crypto_sign_keypair_seed 129 | crypto_hash(n64, sk, 32); 130 | _crypto_sign_ed25519_clamp(n64); 131 | 132 | SN_TWEAK_COPY_32(n, n64) 133 | } 134 | 135 | // tweak a secret key 136 | void sn__extension_tweak_ed25519_scalar(unsigned char *scalar_out, 137 | const unsigned char *scalar, 138 | const unsigned char *ns, 139 | unsigned long long nslen) 140 | { 141 | unsigned char n[64]; 142 | unsigned char q[32]; 143 | 144 | _extension_tweak_ed25519(q, n, ns, nslen); 145 | crypto_core_ed25519_scalar_add(scalar_out, scalar, n); 146 | } 147 | 148 | // tweak a public key 149 | int sn__extension_tweak_ed25519_pk(unsigned char *tpk, 150 | const unsigned char *pk, 151 | const unsigned char *ns, 152 | unsigned long long nslen) 153 | { 154 | unsigned char n[64]; 155 | unsigned char q[32]; 156 | 157 | _extension_tweak_ed25519(q, n, ns, nslen); 158 | return crypto_core_ed25519_add(tpk, q, pk); 159 | } 160 | 161 | 162 | void sn__extension_tweak_ed25519_keypair(unsigned char *pk, unsigned char *scalar_out, 163 | unsigned char *scalar, const unsigned char *ns, 164 | unsigned long long nslen) 165 | { 166 | unsigned char n64[64]; 167 | 168 | crypto_hash(n64, ns, nslen); 169 | n64[31] &= 127; // clear highest bit 170 | 171 | sn__extension_tweak_ed25519_scalar_add(scalar_out, scalar, n64); 172 | crypto_scalarmult_ed25519_base_noclamp(pk, scalar_out); 173 | 174 | // hash tweak until we get a valid tweaked point 175 | while (crypto_core_ed25519_is_valid_point(pk) != 1) { 176 | crypto_hash(n64, n64, 32); 177 | n64[31] &= 127; // clear highest bit 178 | 179 | sn__extension_tweak_ed25519_scalar_add(scalar_out, scalar, n64); 180 | crypto_scalarmult_ed25519_base_noclamp(pk, scalar_out); 181 | } 182 | } 183 | 184 | // add tweak to scalar 185 | void sn__extension_tweak_ed25519_scalar_add(unsigned char *scalar_out, 186 | const unsigned char *scalar, 187 | const unsigned char *n) 188 | { 189 | crypto_core_ed25519_scalar_add(scalar_out, scalar, n); 190 | } 191 | 192 | // add tweak point to public key 193 | int sn__extension_tweak_ed25519_pk_add(unsigned char *tpk, 194 | const unsigned char *pk, 195 | const unsigned char *q) 196 | { 197 | return crypto_core_ed25519_add(tpk, pk, q); 198 | } 199 | 200 | 201 | int sn__extension_tweak_ed25519_keypair_add(unsigned char *pk, unsigned char *scalar_out, 202 | unsigned char *scalar, const unsigned char *tweak) 203 | { 204 | sn__extension_tweak_ed25519_scalar_add(scalar_out, scalar, tweak); 205 | return crypto_scalarmult_ed25519_base_noclamp(pk, scalar_out); 206 | } -------------------------------------------------------------------------------- /extensions/tweak/tweak.h: -------------------------------------------------------------------------------- 1 | #ifdef __cplusplus 2 | extern "C" { 3 | #endif 4 | 5 | #include 6 | 7 | // copy 32 bytes using int64_t pointers 8 | #define SN_TWEAK_COPY_32(a, b) \ 9 | { \ 10 | long long *dst = (long long *) a; \ 11 | long long *src = (long long *) b; \ 12 | dst[0] = src[0]; \ 13 | dst[1] = src[1]; \ 14 | dst[2] = src[2]; \ 15 | dst[3] = src[3]; \ 16 | } 17 | 18 | #define sn__extension_tweak_ed25519_BYTES crypto_sign_ed25519_PUBLICKEYBYTES 19 | 20 | #define sn__extension_tweak_ed25519_SCALARBYTES crypto_scalarmult_ed25519_SCALARBYTES 21 | 22 | int sn__extension_tweak_ed25519_sign_detached(unsigned char *sig, unsigned long long *siglen_p, 23 | const unsigned char *m, unsigned long long mlen, 24 | const unsigned char *n, unsigned char *pk); 25 | 26 | void sn__extension_tweak_ed25519_base(unsigned char *pk, unsigned char *scalar, 27 | const unsigned char *ns, unsigned long long nslen); 28 | 29 | void sn__extension_tweak_ed25519_sk_to_scalar(unsigned char *scalar, const unsigned char *sk); 30 | 31 | // tweak a secret key 32 | void sn__extension_tweak_ed25519_scalar(unsigned char *scalar_out, 33 | const unsigned char *scalar, 34 | const unsigned char *ns, 35 | unsigned long long nslen); 36 | 37 | // tweak a public key 38 | int sn__extension_tweak_ed25519_pk(unsigned char *tpk, 39 | const unsigned char *pk, 40 | const unsigned char *ns, 41 | unsigned long long nslen); 42 | 43 | void sn__extension_tweak_ed25519_keypair(unsigned char *pk, unsigned char *scalar_out, 44 | unsigned char *scalar, const unsigned char *ns, 45 | unsigned long long nslen); 46 | 47 | // add tweak scalar to private key 48 | void sn__extension_tweak_ed25519_scalar_add(unsigned char *scalar_out, 49 | const unsigned char *scalar, 50 | const unsigned char *n); 51 | 52 | // add tweak point to public key 53 | int sn__extension_tweak_ed25519_pk_add(unsigned char *tpk, 54 | const unsigned char *pk, 55 | const unsigned char *q); 56 | 57 | int sn__extension_tweak_ed25519_keypair_add(unsigned char *pk, unsigned char *scalar_out, 58 | unsigned char *scalar, const unsigned char *tweak); 59 | 60 | #ifdef __cplusplus 61 | }; 62 | #endif 63 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sodium-native", 3 | "version": "5.0.6", 4 | "description": "Low level bindings for libsodium", 5 | "main": "index.js", 6 | "files": [ 7 | "index.js", 8 | "binding.cc", 9 | "binding.js", 10 | "extensions", 11 | "prebuilds", 12 | "CMakeLists.txt" 13 | ], 14 | "addon": true, 15 | "dependencies": { 16 | "require-addon": "^1.1.0", 17 | "which-runtime": "^1.2.1" 18 | }, 19 | "devDependencies": { 20 | "bare-compat-napi": "^1.3.5", 21 | "brittle": "^3.16.2", 22 | "cmake-bare": "^1.6.1", 23 | "cmake-fetch": "^1.4.3", 24 | "cmake-napi": "^1.2.1", 25 | "standard": "^17.1.2" 26 | }, 27 | "scripts": { 28 | "test": "standard && npm run test:node && npm run test:bare", 29 | "test:node": "node test/all.js", 30 | "test:bare": "bare test/all.js" 31 | }, 32 | "standard": { 33 | "ignore": [ 34 | "/test/fixtures/*.js" 35 | ] 36 | }, 37 | "engines": { 38 | "bare": ">=1.16.0" 39 | }, 40 | "repository": { 41 | "type": "git", 42 | "url": "git+https://github.com/holepunchto/sodium-native.git" 43 | }, 44 | "contributors": [ 45 | "Emil Bay (http://bayes.dk)", 46 | "Mathias Buus (https://mafinto.sh)", 47 | "Christophe Diederichs " 48 | ], 49 | "license": "MIT", 50 | "bugs": { 51 | "url": "https://github.com/holepunchto/sodium-native/issues" 52 | }, 53 | "homepage": "https://github.com/holepunchto/sodium-native" 54 | } 55 | -------------------------------------------------------------------------------- /test/all.js: -------------------------------------------------------------------------------- 1 | // This runner is auto-generated by Brittle 2 | 3 | runTests() 4 | 5 | async function runTests () { 6 | const test = (await import('brittle')).default 7 | 8 | test.pause() 9 | 10 | await import('./core_ed25519.js') 11 | await import('./crypto_aead_chacha20poly1305_ietf.js') 12 | await import('./crypto_aead_xchacha20poly1305_ietf.js') 13 | await import('./crypto_auth.js') 14 | await import('./crypto_box.js') 15 | await import('./crypto_generichash.js') 16 | await import('./crypto_hash.js') 17 | await import('./crypto_hash_sha256.js') 18 | await import('./crypto_hash_sha512.js') 19 | await import('./crypto_kdf.js') 20 | await import('./crypto_kx.js') 21 | await import('./crypto_onetimeauth.js') 22 | await import('./crypto_pwhash.js') 23 | await import('./crypto_pwhash_scryptsalsa208sha256.js') 24 | await import('./crypto_scalarmult.js') 25 | await import('./crypto_secretbox.js') 26 | await import('./crypto_secretstream.js') 27 | await import('./crypto_shorthash.js') 28 | await import('./crypto_sign.js') 29 | await import('./crypto_stream.js') 30 | await import('./crypto_stream_chacha20.js') 31 | await import('./crypto_stream_chacha20_ietf.js') 32 | await import('./extension_pbkdf2.js') 33 | await import('./extension_tweak_ed25519.js') 34 | await import('./helpers.js') 35 | await import('./memory.js') 36 | await import('./padding.js') 37 | await import('./randombytes.js') 38 | await import('./vectors.js') 39 | await import('./fastcalls.js') 40 | 41 | test.resume() 42 | } 43 | -------------------------------------------------------------------------------- /test/core_ed25519.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | 4 | const nonCanonicalP = Buffer.from([ 5 | 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 6 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f 7 | ]) 8 | 9 | const nonCanonicalInvalidP = Buffer.from([ 10 | 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 11 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f 12 | ]) 13 | 14 | const maxCanonicalP = Buffer.from([ 15 | 0xe4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 16 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f 17 | ]) 18 | 19 | function addP (S) { 20 | const P = Buffer.from([ 21 | 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 22 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 23 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 24 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f 25 | ]) 26 | 27 | sodium.sodium_add(S, P) 28 | } 29 | 30 | function addL64 (S) { 31 | const l = Buffer.alloc(sodium.crypto_core_ed25519_NONREDUCEDSCALARBYTES) 32 | l.set([0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 33 | 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 34 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 35 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 36 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) 38 | 39 | sodium.sodium_add(S, l) 40 | } 41 | 42 | test('ported libsodium test', function (t) { 43 | let i 44 | 45 | const h = sodium.sodium_malloc(sodium.crypto_core_ed25519_UNIFORMBYTES) 46 | const p = sodium.sodium_malloc(sodium.crypto_core_ed25519_BYTES) 47 | for (i = 0; i < 1000; i++) { 48 | sodium.randombytes_buf(h.subarray(0, sodium.crypto_core_ed25519_UNIFORMBYTES)) 49 | sodium.crypto_core_ed25519_from_uniform(p, h) 50 | if (sodium.crypto_core_ed25519_is_valid_point(p) === false) { 51 | t.fail('crypto_core_ed25519_from_uniform() returned an invalid point') 52 | } 53 | } 54 | 55 | const p2 = sodium.sodium_malloc(sodium.crypto_core_ed25519_BYTES) 56 | const p3 = sodium.sodium_malloc(sodium.crypto_core_ed25519_BYTES) 57 | sodium.randombytes_buf(h.subarray(0, sodium.crypto_core_ed25519_UNIFORMBYTES)) 58 | sodium.crypto_core_ed25519_from_uniform(p2, h) 59 | 60 | const j = 1 + sodium.randombytes_uniform(100) 61 | p.copy(p3, 0, 0, sodium.crypto_core_ed25519_BYTES) 62 | for (i = 0; i < j; i++) { 63 | sodium.crypto_core_ed25519_add(p, p, p2) 64 | if (sodium.crypto_core_ed25519_is_valid_point(p) === false) { 65 | t.fail('crypto_core_add() returned an invalid point\n') 66 | } 67 | } 68 | 69 | t.absent(sodium.sodium_memcmp(p.subarray(0, sodium.crypto_core_ed25519_BYTES), p3), 'crypto_core_add() failed') 70 | for (i = 0; i < j; i++) { 71 | sodium.crypto_core_ed25519_sub(p, p, p2) 72 | } 73 | t.ok(sodium.sodium_memcmp(p.subarray(0, sodium.crypto_core_ed25519_BYTES), p3)) 74 | const sc = sodium.sodium_malloc(sodium.crypto_scalarmult_ed25519_SCALARBYTES) 75 | sc.fill(0, 0, sodium.crypto_scalarmult_ed25519_SCALARBYTES) 76 | sc[0] = 8 77 | p.copy(p2, 0, 0, sodium.crypto_core_ed25519_BYTES) 78 | p.copy(p3, 0, 0, sodium.crypto_core_ed25519_BYTES) 79 | 80 | for (i = 0; i < 254; i++) { 81 | sodium.crypto_core_ed25519_add(p2, p2, p2) 82 | } 83 | for (i = 0; i < 8; i++) { 84 | sodium.crypto_core_ed25519_add(p2, p2, p) 85 | } 86 | sodium.crypto_scalarmult_ed25519(p3, sc, p) 87 | t.ok(sodium.sodium_memcmp(p2.subarray(0, sodium.crypto_core_ed25519_BYTES), p3)) 88 | 89 | t.ok(sodium.crypto_core_ed25519_is_valid_point(p)) 90 | 91 | p.fill(0, 0, sodium.crypto_core_ed25519_BYTES) 92 | t.absent(sodium.crypto_core_ed25519_is_valid_point(p)) 93 | 94 | p[0] = 1 95 | t.absent(sodium.crypto_core_ed25519_is_valid_point(p)) 96 | 97 | p[0] = 2 98 | t.absent(sodium.crypto_core_ed25519_is_valid_point(p)) 99 | 100 | p[0] = 9 101 | t.ok(sodium.crypto_core_ed25519_is_valid_point(p)) 102 | 103 | t.ok(sodium.crypto_core_ed25519_is_valid_point(maxCanonicalP)) 104 | t.absent(sodium.crypto_core_ed25519_is_valid_point(nonCanonicalInvalidP)) 105 | t.absent(sodium.crypto_core_ed25519_is_valid_point(nonCanonicalP)) 106 | 107 | p.copy(p2, 0, 0, sodium.crypto_core_ed25519_BYTES) 108 | addP(p2) 109 | sodium.crypto_core_ed25519_add(p3, p2, p2) 110 | sodium.crypto_core_ed25519_sub(p3, p3, p2) 111 | t.absent(sodium.sodium_memcmp(p2.subarray(0, sodium.crypto_core_ed25519_BYTES), p)) 112 | t.ok(sodium.sodium_memcmp(p3.subarray(0, sodium.crypto_core_ed25519_BYTES), p)) 113 | 114 | p[0] = 2 115 | t.exception.all(() => sodium.crypto_core_ed25519_add(p3, p2, p)) 116 | sodium.crypto_core_ed25519_add(p3, p2, nonCanonicalP) 117 | t.exception.all(() => sodium.crypto_core_ed25519_add(p3, p2, nonCanonicalInvalidP)) 118 | t.exception.all(() => sodium.crypto_core_ed25519_add(p3, p, p3)) 119 | sodium.crypto_core_ed25519_add(p3, nonCanonicalP, p3) 120 | t.exception.all(() => sodium.crypto_core_ed25519_add(p3, nonCanonicalInvalidP, p3)) 121 | 122 | t.exception.all(() => sodium.crypto_core_ed25519_sub(p3, p2, p)) 123 | sodium.crypto_core_ed25519_sub(p3, p2, nonCanonicalP) 124 | t.exception.all(() => sodium.crypto_core_ed25519_sub(p3, p2, nonCanonicalInvalidP)) 125 | t.exception.all(() => sodium.crypto_core_ed25519_sub(p3, p, p3)) 126 | sodium.crypto_core_ed25519_sub(p3, nonCanonicalP, p3) 127 | t.exception.all(() => sodium.crypto_core_ed25519_sub(p3, nonCanonicalInvalidP, p3)) 128 | 129 | for (i = 0; i < 1000; i++) { 130 | sodium.randombytes_buf(h.subarray(0, sodium.crypto_core_ed25519_UNIFORMBYTES)) 131 | sodium.crypto_core_ed25519_from_uniform(p, h) 132 | sodium.crypto_core_ed25519_scalar_random(sc) 133 | sodium.crypto_scalarmult_ed25519_noclamp(p2, sc, p) 134 | if (!sodium.crypto_core_ed25519_is_valid_point(p2)) t.fail() 135 | sodium.crypto_core_ed25519_scalar_invert(sc, sc) 136 | sodium.crypto_scalarmult_ed25519_noclamp(p3, sc, p2) 137 | if (sodium.sodium_memcmp(p3.subarray(0, sodium.crypto_core_ed25519_BYTES), p) === false) t.fail() 138 | } 139 | 140 | const sc64 = sodium.sodium_malloc(64) 141 | sodium.crypto_core_ed25519_scalar_random(sc) 142 | sc.copy(sc64, 0, 0, sodium.crypto_core_ed25519_BYTES) 143 | sc64.fill(0, sodium.crypto_core_ed25519_BYTES) 144 | i = sodium.randombytes_uniform(100) 145 | do { 146 | addL64(sc64) 147 | } while (i-- > 0) 148 | const reduced = sodium.sodium_malloc(sodium.crypto_core_ed25519_SCALARBYTES) 149 | sodium.crypto_core_ed25519_scalar_reduce(reduced, sc64) 150 | t.ok(sodium.sodium_memcmp(reduced, sc)) 151 | 152 | sodium.randombytes_buf(h.subarray(0, sodium.crypto_core_ed25519_UNIFORMBYTES)) 153 | sodium.crypto_core_ed25519_from_uniform(p, h) 154 | p.copy(p2, 0, 0, sodium.crypto_core_ed25519_BYTES) 155 | sodium.crypto_core_ed25519_scalar_random(sc) 156 | sodium.crypto_scalarmult_ed25519_noclamp(p, sc, p) 157 | sodium.crypto_core_ed25519_scalar_complement(sc, sc) 158 | sodium.crypto_scalarmult_ed25519_noclamp(p2, sc, p2) 159 | sodium.crypto_core_ed25519_add(p3, p, p2) 160 | sodium.crypto_core_ed25519_from_uniform(p, h) 161 | sodium.crypto_core_ed25519_sub(p, p, p3) 162 | if (p[0] !== 0x01) t.fail() 163 | for (i = 1; i < sodium.crypto_core_ed25519_BYTES; i++) { 164 | if (p[i] !== 0) t.fail() 165 | } 166 | 167 | sodium.randombytes_buf(h.subarray(0, sodium.crypto_core_ed25519_UNIFORMBYTES)) 168 | sodium.crypto_core_ed25519_from_uniform(p, h) 169 | p.copy(p2, 0, 0, sodium.crypto_core_ed25519_BYTES) 170 | sodium.crypto_core_ed25519_scalar_random(sc) 171 | sodium.crypto_scalarmult_ed25519_noclamp(p, sc, p) 172 | sodium.crypto_core_ed25519_scalar_negate(sc, sc) 173 | sodium.crypto_scalarmult_ed25519_noclamp(p2, sc, p2) 174 | sodium.crypto_core_ed25519_add(p, p, p2) 175 | if (p[0] !== 0x01) t.fail() 176 | for (i = 1; i < sodium.crypto_core_ed25519_BYTES; i++) { 177 | if (p[i] !== 0) t.fail() 178 | } 179 | 180 | for (i = 0; i < sodium.crypto_core_ed25519_SCALARBYTES; i++) { 181 | sc[i] = 255 - i 182 | } 183 | sodium.crypto_core_ed25519_scalar_invert(sc, sc) 184 | t.alike(sc.toString('hex'), '5858cdec40a044b1548b3bb08f8ce0d71103d1f887df84ebc502643dac4df40b', 'inv1') 185 | sodium.crypto_core_ed25519_scalar_invert(sc, sc) 186 | t.alike(sc.toString('hex'), '09688ce78a8ff8273f636b0bc748c0cceeeeedecebeae9e8e7e6e5e4e3e2e100', 'inv2') 187 | for (i = 0; i < sodium.crypto_core_ed25519_SCALARBYTES; i++) { 188 | sc[i] = 32 - i 189 | } 190 | sodium.crypto_core_ed25519_scalar_invert(sc, sc) 191 | 192 | t.alike(sc.toString('hex'), 'f70b4f272b47bd6a1015a511fb3c9fc1b9c21ca4ca2e17d5a225b4c410b9b60d', 'inv3') 193 | sodium.crypto_core_ed25519_scalar_invert(sc, sc) 194 | t.alike(sc.toString('hex'), '201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201', 'inv4') 195 | 196 | for (i = 0; i < sodium.crypto_core_ed25519_SCALARBYTES; i++) { 197 | sc[i] = 255 - i 198 | } 199 | sodium.crypto_core_ed25519_scalar_negate(sc, sc) 200 | t.alike(sc.toString('hex'), 'e46b69758fd3193097398c9717b11e48111112131415161718191a1b1c1d1e0f', 'neg1') 201 | sodium.crypto_core_ed25519_scalar_negate(sc, sc) 202 | t.alike(sc.toString('hex'), '09688ce78a8ff8273f636b0bc748c0cceeeeedecebeae9e8e7e6e5e4e3e2e100', 'neg2') 203 | for (i = 0; i < sodium.crypto_core_ed25519_SCALARBYTES; i++) { 204 | sc[i] = 32 - i 205 | } 206 | sodium.crypto_core_ed25519_scalar_negate(sc, sc) 207 | t.alike(sc.toString('hex'), 'cdb4d73ffe47f83ebe85e18dcae6cc03f0f0f1f2f3f4f5f6f7f8f9fafbfcfd0e', 'neg3') 208 | sodium.crypto_core_ed25519_scalar_negate(sc, sc) 209 | t.alike(sc.toString('hex'), '201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201', 'neg4') 210 | 211 | for (i = 0; i < sodium.crypto_core_ed25519_SCALARBYTES; i++) { 212 | sc[i] = 255 - i 213 | } 214 | sodium.crypto_core_ed25519_scalar_complement(sc, sc) 215 | t.alike(sc.toString('hex'), 'e56b69758fd3193097398c9717b11e48111112131415161718191a1b1c1d1e0f', 'comp1') 216 | sodium.crypto_core_ed25519_scalar_complement(sc, sc) 217 | t.alike(sc.toString('hex'), '09688ce78a8ff8273f636b0bc748c0cceeeeedecebeae9e8e7e6e5e4e3e2e100', 'comp2') 218 | for (i = 0; i < sodium.crypto_core_ed25519_SCALARBYTES; i++) { 219 | sc[i] = 32 - i 220 | } 221 | sodium.crypto_core_ed25519_scalar_complement(sc, sc) 222 | t.alike(sc.toString('hex'), 'ceb4d73ffe47f83ebe85e18dcae6cc03f0f0f1f2f3f4f5f6f7f8f9fafbfcfd0e', 'comp3') 223 | sodium.crypto_core_ed25519_scalar_complement(sc, sc) 224 | t.alike(sc.toString('hex'), '201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201', 'comp4') 225 | 226 | const sc2 = sodium.sodium_malloc(sodium.crypto_core_ed25519_SCALARBYTES) 227 | const sc3 = sodium.sodium_malloc(sodium.crypto_core_ed25519_SCALARBYTES) 228 | for (i = 0; i < 1000; i++) { 229 | sodium.randombytes_buf(sc.subarray(0, sodium.crypto_core_ed25519_SCALARBYTES)) 230 | sodium.randombytes_buf(sc2.subarray(0, sodium.crypto_core_ed25519_SCALARBYTES)) 231 | sc[sodium.crypto_core_ed25519_SCALARBYTES - 1] &= 0x7f 232 | sc2[sodium.crypto_core_ed25519_SCALARBYTES - 1] &= 0x7f 233 | sodium.crypto_core_ed25519_scalar_add(sc3, sc, sc2) 234 | if (sodium.sodium_is_zero(sc, sodium.crypto_core_ed25519_SCALARBYTES)) t.fail() 235 | sodium.crypto_core_ed25519_scalar_sub(sc3, sc3, sc2) 236 | if (sodium.sodium_is_zero(sc, sodium.crypto_core_ed25519_SCALARBYTES)) t.fail() 237 | sodium.crypto_core_ed25519_scalar_sub(sc3, sc3, sc) 238 | if (!sodium.sodium_is_zero(sc3, sodium.crypto_core_ed25519_SCALARBYTES)) t.fail() 239 | } 240 | 241 | sc.fill(0x69, 0, sodium.crypto_core_ed25519_UNIFORMBYTES) 242 | sc2.fill(0x42, 0, sodium.crypto_core_ed25519_UNIFORMBYTES) 243 | sodium.crypto_core_ed25519_scalar_add(sc, sc, sc2) 244 | sodium.crypto_core_ed25519_scalar_add(sc, sc2, sc) 245 | t.alike(sc.toString('hex'), 'f7567cd87c82ec1c355a6304c143bcc9ecedededededededededededededed0d', 'add1') 246 | 247 | sodium.crypto_core_ed25519_scalar_sub(sc, sc2, sc) 248 | sodium.crypto_core_ed25519_scalar_sub(sc, sc, sc2) 249 | t.alike(sc.toString('hex'), 'f67c79849de0253ba142949e1db6224b13121212121212121212121212121202', 'sub1') 250 | 251 | sc.fill(0xcd, 0, sodium.crypto_core_ed25519_UNIFORMBYTES) 252 | sc2.fill(0x42, 0, sodium.crypto_core_ed25519_UNIFORMBYTES) 253 | sodium.crypto_core_ed25519_scalar_add(sc, sc, sc2) 254 | sodium.crypto_core_ed25519_scalar_add(sc, sc2, sc) 255 | t.alike(sc.toString('hex'), 'b02e8581ce62f69922427c23f970f7e951525252525252525252525252525202', 'add2') 256 | 257 | sodium.crypto_core_ed25519_scalar_sub(sc, sc2, sc) 258 | sodium.crypto_core_ed25519_scalar_sub(sc, sc, sc2) 259 | t.alike(sc.toString('hex'), '3da570db4b001cbeb35a7b7fe588e72aaeadadadadadadadadadadadadadad0d', 'sub2') 260 | 261 | // sodium-native: We're hardcoding these in just so we can see if they change 262 | t.ok(sodium.crypto_core_ed25519_BYTES === 32) 263 | t.ok(sodium.crypto_core_ed25519_SCALARBYTES === 32) 264 | t.ok(sodium.crypto_core_ed25519_NONREDUCEDSCALARBYTES === 64) 265 | t.ok(sodium.crypto_core_ed25519_NONREDUCEDSCALARBYTES >= sodium.crypto_core_ed25519_SCALARBYTES) 266 | t.ok(sodium.crypto_core_ed25519_UNIFORMBYTES === 32) 267 | t.ok(sodium.crypto_core_ed25519_UNIFORMBYTES >= sodium.crypto_core_ed25519_BYTES) 268 | }) 269 | -------------------------------------------------------------------------------- /test/crypto_aead_chacha20poly1305_ietf.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | 4 | test('constants', function (t) { 5 | t.is(typeof sodium.crypto_aead_chacha20poly1305_ietf_ABYTES, 'number') 6 | t.is(typeof sodium.crypto_aead_chacha20poly1305_ietf_KEYBYTES, 'number') 7 | t.is(typeof sodium.crypto_aead_chacha20poly1305_ietf_NPUBBYTES, 'number') 8 | t.is(typeof sodium.crypto_aead_chacha20poly1305_ietf_NSECBYTES, 'number') 9 | t.is(sodium.crypto_aead_chacha20poly1305_ietf_NSECBYTES, 0) 10 | t.is(typeof sodium.crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX, 'number') 11 | t.is(sodium.crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX, 0x3fffffffc0) // to make sure, see note in binding.cc 12 | }) 13 | 14 | test('ported from libsodium', function (t) { 15 | const mlen = 114 16 | const adlen = 12 17 | const clen = mlen + sodium.crypto_aead_chacha20poly1305_ietf_ABYTES 18 | 19 | const firstkey = Buffer.from([ 20 | 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 21 | 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 22 | 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 23 | 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f 24 | ]) 25 | 26 | const message = Buffer.from('Ladies and Gentlemen of the class of \'99: If I could offer you only one tip for the future, sunscreen would be it.') 27 | 28 | const m = sodium.sodium_malloc(mlen) 29 | const nonce = Buffer.from([ 30 | 0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43, 31 | 0x44, 0x45, 0x46, 0x47 32 | ]) 33 | t.is(nonce.length, sodium.crypto_aead_chacha20poly1305_ietf_NPUBBYTES) 34 | 35 | const ad = Buffer.from([ 36 | 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 37 | 0xc4, 0xc5, 0xc6, 0xc7 38 | ]) 39 | t.is(ad.length, adlen) 40 | 41 | const c = sodium.sodium_malloc(clen) 42 | const detachedc = sodium.sodium_malloc(mlen) 43 | 44 | const mac = sodium.sodium_malloc(sodium.crypto_aead_chacha20poly1305_ietf_ABYTES) 45 | 46 | const m2 = sodium.sodium_malloc(mlen) 47 | 48 | let foundclen = 0 49 | let foundmaclen = 0 50 | let m2len = 0 51 | 52 | let i = 0 53 | 54 | t.is(message.length, mlen) 55 | message.copy(m) 56 | 57 | foundclen = sodium.crypto_aead_chacha20poly1305_ietf_encrypt(c, m, ad, null, nonce, firstkey) 58 | t.is(foundclen, mlen + sodium.crypto_aead_chacha20poly1305_ietf_ABYTES) 59 | 60 | const exp1 = Buffer.from([ 61 | 0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, 0x7b, 0x86, 0xaf, 0xbc, 62 | 0x53, 0xef, 0x7e, 0xc2, 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe, 63 | 0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6, 0x3d, 0xbe, 0xa4, 0x5e, 64 | 0x8c, 0xa9, 0x67, 0x12, 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b, 65 | 0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29, 0x05, 0xd6, 0xa5, 0xb6, 66 | 0x7e, 0xcd, 0x3b, 0x36, 0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c, 67 | 0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58, 0xfa, 0xb3, 0x24, 0xe4, 68 | 0xfa, 0xd6, 0x75, 0x94, 0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc, 69 | 0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d, 0xe5, 0x76, 0xd2, 0x65, 70 | 0x86, 0xce, 0xc6, 0x4b, 0x61, 0x16, 0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09, 71 | 0xe2, 0x6a, 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, 0x06, 0x91 72 | ]) 73 | exp1.secure = true 74 | 75 | t.alike(c, exp1) 76 | 77 | foundmaclen = sodium.crypto_aead_chacha20poly1305_ietf_encrypt_detached(detachedc, mac, m, ad, null, nonce, firstkey) 78 | 79 | t.is(foundmaclen, sodium.crypto_aead_chacha20poly1305_ietf_ABYTES) 80 | const exp0 = c.subarray(0, mlen) 81 | exp0.secure = true 82 | t.alike(detachedc, exp0) 83 | 84 | m2len = sodium.crypto_aead_chacha20poly1305_ietf_decrypt(m2, null, c, ad, nonce, firstkey) 85 | t.is(m2len, mlen) 86 | 87 | t.alike(m, m2) 88 | 89 | m2.fill(0) 90 | sodium.crypto_aead_chacha20poly1305_ietf_decrypt_detached(m2, null, c.subarray(0, mlen), mac, ad, nonce, firstkey) 91 | 92 | t.alike(m, m2) 93 | 94 | for (i = 0; i < clen; i++) { 95 | c[i] ^= (i + 1) 96 | t.exception.all(_ => sodium.crypto_aead_chacha20poly1305_ietf_decrypt(m2, null, c, ad, nonce, firstkey)) 97 | if (m.equals(m2)) t.fail() 98 | c[i] ^= (i + 1) 99 | } 100 | 101 | foundclen = sodium.crypto_aead_chacha20poly1305_ietf_encrypt(c, m, null, null, nonce, firstkey) 102 | t.is(foundclen, clen) 103 | 104 | const exp2 = Buffer.from([ 105 | 0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, 0x7b, 0x86, 0xaf, 0xbc, 106 | 0x53, 0xef, 0x7e, 0xc2, 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe, 107 | 0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6, 0x3d, 0xbe, 0xa4, 0x5e, 108 | 0x8c, 0xa9, 0x67, 0x12, 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b, 109 | 0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29, 0x05, 0xd6, 0xa5, 0xb6, 110 | 0x7e, 0xcd, 0x3b, 0x36, 0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c, 111 | 0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58, 0xfa, 0xb3, 0x24, 0xe4, 112 | 0xfa, 0xd6, 0x75, 0x94, 0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc, 113 | 0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d, 0xe5, 0x76, 0xd2, 0x65, 114 | 0x86, 0xce, 0xc6, 0x4b, 0x61, 0x16, 0x6a, 0x23, 0xa4, 0x68, 0x1f, 0xd5, 115 | 0x94, 0x56, 0xae, 0xa1, 0xd2, 0x9f, 0x82, 0x47, 0x72, 0x16 116 | ]) 117 | exp2.secure = true 118 | 119 | t.alike(c, exp2) 120 | 121 | m2len = sodium.crypto_aead_chacha20poly1305_ietf_decrypt(m2, null, c, null, nonce, firstkey) 122 | t.is(m2len, mlen) 123 | 124 | t.alike(m2, m) 125 | 126 | m.copy(c) 127 | 128 | foundclen = sodium.crypto_aead_chacha20poly1305_ietf_encrypt(c, c.subarray(0, mlen), null, null, nonce, firstkey) 129 | 130 | t.is(foundclen, clen, 'clen is properly set (adlen=0)') 131 | 132 | const exp3 = Buffer.from([ 133 | 0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, 0x7b, 0x86, 0xaf, 0xbc, 134 | 0x53, 0xef, 0x7e, 0xc2, 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe, 135 | 0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6, 0x3d, 0xbe, 0xa4, 0x5e, 136 | 0x8c, 0xa9, 0x67, 0x12, 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b, 137 | 0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29, 0x05, 0xd6, 0xa5, 0xb6, 138 | 0x7e, 0xcd, 0x3b, 0x36, 0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c, 139 | 0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58, 0xfa, 0xb3, 0x24, 0xe4, 140 | 0xfa, 0xd6, 0x75, 0x94, 0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc, 141 | 0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d, 0xe5, 0x76, 0xd2, 0x65, 142 | 0x86, 0xce, 0xc6, 0x4b, 0x61, 0x16, 0x6a, 0x23, 0xa4, 0x68, 0x1f, 0xd5, 143 | 0x94, 0x56, 0xae, 0xa1, 0xd2, 0x9f, 0x82, 0x47, 0x72, 0x16 144 | ]) 145 | exp3.secure = true 146 | 147 | t.alike(c, exp3) 148 | 149 | const decrypted = sodium.sodium_malloc(c.byteLength - sodium.crypto_aead_chacha20poly1305_ietf_ABYTES) 150 | m2len = sodium.crypto_aead_chacha20poly1305_ietf_decrypt(decrypted, null, c, null, nonce, firstkey) 151 | t.is(m2len, mlen, 'm2len is properly set (adlen=0)') 152 | 153 | t.alike(m, decrypted, 'm == c (adlen=0)') 154 | }) 155 | 156 | test('keygen', function (t) { 157 | const key1 = sodium.sodium_malloc(sodium.crypto_aead_chacha20poly1305_ietf_KEYBYTES) 158 | const key2 = sodium.sodium_malloc(sodium.crypto_aead_chacha20poly1305_ietf_KEYBYTES) 159 | 160 | sodium.crypto_aead_chacha20poly1305_ietf_keygen(key1) 161 | sodium.crypto_aead_chacha20poly1305_ietf_keygen(key2) 162 | 163 | t.unlike(key1, key2) 164 | }) 165 | 166 | test('different keys', function (t) { 167 | const m = Buffer.from('Ladies and Gentlemen of the class of \'99: If I could offer you only one tip for the future, sunscreen would be it.') 168 | 169 | const key1 = sodium.sodium_malloc(sodium.crypto_aead_chacha20poly1305_ietf_KEYBYTES) 170 | const key2 = sodium.sodium_malloc(sodium.crypto_aead_chacha20poly1305_ietf_KEYBYTES) 171 | sodium.crypto_aead_chacha20poly1305_ietf_keygen(key1) 172 | sodium.crypto_aead_chacha20poly1305_ietf_keygen(key2) 173 | 174 | const nonce = sodium.sodium_malloc(sodium.crypto_aead_chacha20poly1305_ietf_NPUBBYTES) 175 | sodium.randombytes_buf(nonce) 176 | 177 | const clen = m.byteLength + sodium.crypto_aead_chacha20poly1305_ietf_ABYTES 178 | const c1 = sodium.sodium_malloc(clen) 179 | const c2 = sodium.sodium_malloc(clen) 180 | 181 | const m1 = sodium.sodium_malloc(m.byteLength) 182 | const m2 = sodium.sodium_malloc(m.byteLength) 183 | 184 | t.is(sodium.crypto_aead_chacha20poly1305_ietf_encrypt(c1, m, null, null, nonce, key1), clen) 185 | t.absent(c1.equals(c2)) 186 | t.absent(c1.equals(m)) 187 | t.is(sodium.crypto_aead_chacha20poly1305_ietf_encrypt(c2, m, null, null, nonce, key2), clen) 188 | t.absent(c1.equals(c2)) 189 | t.absent(c2.equals(m)) 190 | 191 | t.exception.all(_ => sodium.crypto_aead_chacha20poly1305_ietf_decrypt(m1, null, c1, null, nonce, key2)) 192 | t.exception.all(_ => sodium.crypto_aead_chacha20poly1305_ietf_decrypt(m2, null, c2, null, nonce, key1)) 193 | 194 | t.is(sodium.crypto_aead_chacha20poly1305_ietf_decrypt(m1, null, c1, null, nonce, key1), m.byteLength) 195 | t.ok(m.equals(m1)) 196 | t.is(sodium.crypto_aead_chacha20poly1305_ietf_decrypt(m2, null, c2, null, nonce, key2), m.byteLength) 197 | t.ok(m.equals(m2)) 198 | }) 199 | 200 | test('different nonce', function (t) { 201 | const m = Buffer.from('Ladies and Gentlemen of the class of \'99: If I could offer you only one tip for the future, sunscreen would be it.') 202 | 203 | const key = sodium.sodium_malloc(sodium.crypto_aead_chacha20poly1305_ietf_KEYBYTES) 204 | sodium.crypto_aead_chacha20poly1305_ietf_keygen(key) 205 | 206 | const n1 = sodium.sodium_malloc(sodium.crypto_aead_chacha20poly1305_ietf_NPUBBYTES) 207 | const n2 = sodium.sodium_malloc(sodium.crypto_aead_chacha20poly1305_ietf_NPUBBYTES) 208 | sodium.randombytes_buf(n1) 209 | sodium.randombytes_buf(n2) 210 | 211 | const clen = m.byteLength + sodium.crypto_aead_chacha20poly1305_ietf_ABYTES 212 | const c1 = sodium.sodium_malloc(clen) 213 | const c2 = sodium.sodium_malloc(clen) 214 | 215 | const m1 = sodium.sodium_malloc(m.byteLength) 216 | const m2 = sodium.sodium_malloc(m.byteLength) 217 | 218 | t.is(sodium.crypto_aead_chacha20poly1305_ietf_encrypt(c1, m, null, null, n1, key), clen) 219 | t.absent(c1.equals(c2)) 220 | t.absent(c1.equals(m)) 221 | t.is(sodium.crypto_aead_chacha20poly1305_ietf_encrypt(c2, m, null, null, n2, key), clen) 222 | t.absent(c1.equals(c2)) 223 | t.absent(c2.equals(m)) 224 | 225 | t.exception.all(_ => sodium.crypto_aead_chacha20poly1305_ietf_decrypt(m1, null, c1, null, n2, key)) 226 | t.exception.all(_ => sodium.crypto_aead_chacha20poly1305_ietf_decrypt(m2, null, c2, null, n1, key)) 227 | 228 | t.is(sodium.crypto_aead_chacha20poly1305_ietf_decrypt(m1, null, c1, null, n1, key), m.byteLength) 229 | t.ok(m.equals(m1)) 230 | t.is(sodium.crypto_aead_chacha20poly1305_ietf_decrypt(m2, null, c2, null, n2, key), m.byteLength) 231 | t.ok(m.equals(m2)) 232 | }) 233 | 234 | test('detached -> non-detached', function (t) { 235 | const m = Buffer.from('Ladies and Gentlemen of the class of \'99: If I could offer you only one tip for the future, sunscreen would be it.') 236 | m.secure = true 237 | 238 | const key = sodium.sodium_malloc(sodium.crypto_aead_chacha20poly1305_ietf_KEYBYTES) 239 | sodium.crypto_aead_chacha20poly1305_ietf_keygen(key) 240 | 241 | const nonce = sodium.sodium_malloc(sodium.crypto_aead_chacha20poly1305_ietf_NPUBBYTES) 242 | sodium.randombytes_buf(nonce) 243 | 244 | const mac = sodium.sodium_malloc(sodium.crypto_aead_chacha20poly1305_ietf_ABYTES) 245 | const clen = m.byteLength 246 | const c = sodium.sodium_malloc(clen) 247 | 248 | t.is(sodium.crypto_aead_chacha20poly1305_ietf_encrypt_detached(c, mac, m, null, null, nonce, key), mac.byteLength) 249 | 250 | const m1 = sodium.sodium_malloc(m.byteLength) 251 | t.is(sodium.crypto_aead_chacha20poly1305_ietf_decrypt(m1, null, Buffer.concat([c, mac]), null, nonce, key), m.byteLength) 252 | 253 | t.alike(m, m1) 254 | }) 255 | 256 | test('non-detached -> detached', function (t) { 257 | const m = Buffer.from('Ladies and Gentlemen of the class of \'99: If I could offer you only one tip for the future, sunscreen would be it.') 258 | m.secure = true 259 | 260 | const key = sodium.sodium_malloc(sodium.crypto_aead_chacha20poly1305_ietf_KEYBYTES) 261 | sodium.crypto_aead_chacha20poly1305_ietf_keygen(key) 262 | 263 | const nonce = sodium.sodium_malloc(sodium.crypto_aead_chacha20poly1305_ietf_NPUBBYTES) 264 | sodium.randombytes_buf(nonce) 265 | 266 | const clen = m.byteLength + sodium.crypto_aead_chacha20poly1305_ietf_ABYTES 267 | const c = sodium.sodium_malloc(clen) 268 | 269 | t.is(sodium.crypto_aead_chacha20poly1305_ietf_encrypt(c, m, null, null, nonce, key), c.byteLength) 270 | 271 | const m1 = sodium.sodium_malloc(m.byteLength) 272 | const csub = c.subarray(0, clen - sodium.crypto_aead_chacha20poly1305_ietf_ABYTES) 273 | const macsub = c.subarray(csub.byteLength) 274 | sodium.crypto_aead_chacha20poly1305_ietf_decrypt_detached(m1, null, csub, macsub, null, nonce, key) 275 | 276 | t.alike(m, m1) 277 | }) 278 | 279 | /** 280 | * Need to test in-place encryption 281 | * detach can talk to non detach 282 | * encrypt - decrypt 283 | * different nonce 284 | * different key 285 | * return values 286 | */ 287 | -------------------------------------------------------------------------------- /test/crypto_aead_xchacha20poly1305_ietf.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | 4 | test('constants', function (t) { 5 | t.is(typeof sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES, 'number') 6 | t.is(typeof sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES, 'number') 7 | t.is(typeof sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES, 'number') 8 | t.is(typeof sodium.crypto_aead_xchacha20poly1305_ietf_NSECBYTES, 'number') 9 | t.is(sodium.crypto_aead_xchacha20poly1305_ietf_NSECBYTES, 0) 10 | t.is(typeof sodium.crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX, 'number') 11 | t.is(sodium.crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX, Number.MAX_SAFE_INTEGER) // to make sure, see note in binding.c 12 | }) 13 | 14 | test('ported from libsodium', function (t) { 15 | const mlen = 114 16 | const adlen = 12 17 | const clen = mlen + sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES 18 | 19 | const firstkey = Buffer.from([ 20 | 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 21 | 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 22 | 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 23 | 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f 24 | ]) 25 | 26 | const message = Buffer.from('Ladies and Gentlemen of the class of \'99: If I could offer you only one tip for the future, sunscreen would be it.') 27 | 28 | const m = sodium.sodium_malloc(mlen) 29 | const nonce = new Uint8Array([ 30 | 0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 31 | 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53 32 | ]) 33 | t.is(nonce.length, sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES) 34 | 35 | const ad = Buffer.from([ 36 | 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 37 | 0xc4, 0xc5, 0xc6, 0xc7 38 | ]) 39 | t.is(ad.length, adlen) 40 | 41 | const c = sodium.sodium_malloc(clen) 42 | const detachedc = sodium.sodium_malloc(mlen) 43 | 44 | const key2 = sodium.sodium_malloc(sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES) 45 | const mac = sodium.sodium_malloc(sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES) 46 | 47 | const m2 = sodium.sodium_malloc(mlen) 48 | 49 | let foundclen = 0 50 | let foundmaclen = 0 51 | let m2len = 0 52 | 53 | let i = 0 54 | 55 | t.is(message.length, mlen) 56 | message.copy(m) 57 | 58 | foundclen = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(c, m, ad, null, nonce, firstkey) 59 | t.is(foundclen, mlen + sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES) 60 | 61 | const exp1 = Buffer.from([ 62 | 0xf8, 0xeb, 0xea, 0x48, 0x75, 0x04, 0x40, 0x66, 63 | 0xfc, 0x16, 0x2a, 0x06, 0x04, 0xe1, 0x71, 0xfe, 64 | 0xec, 0xfb, 0x3d, 0x20, 0x42, 0x52, 0x48, 0x56, 65 | 0x3b, 0xcf, 0xd5, 0xa1, 0x55, 0xdc, 0xc4, 0x7b, 66 | 0xbd, 0xa7, 0x0b, 0x86, 0xe5, 0xab, 0x9b, 0x55, 67 | 0x00, 0x2b, 0xd1, 0x27, 0x4c, 0x02, 0xdb, 0x35, 68 | 0x32, 0x1a, 0xcd, 0x7a, 0xf8, 0xb2, 0xe2, 0xd2, 69 | 0x50, 0x15, 0xe1, 0x36, 0xb7, 0x67, 0x94, 0x58, 70 | 0xe9, 0xf4, 0x32, 0x43, 0xbf, 0x71, 0x9d, 0x63, 71 | 0x9b, 0xad, 0xb5, 0xfe, 0xac, 0x03, 0xf8, 0x0a, 72 | 0x19, 0xa9, 0x6e, 0xf1, 0x0c, 0xb1, 0xd1, 0x53, 73 | 0x33, 0xa8, 0x37, 0xb9, 0x09, 0x46, 0xba, 0x38, 74 | 0x54, 0xee, 0x74, 0xda, 0x3f, 0x25, 0x85, 0xef, 75 | 0xc7, 0xe1, 0xe1, 0x70, 0xe1, 0x7e, 0x15, 0xe5, 76 | 0x63, 0xe7, 0x76, 0x01, 0xf4, 0xf8, 0x5c, 0xaf, 77 | 0xa8, 0xe5, 0x87, 0x76, 0x14, 0xe1, 0x43, 0xe6, 78 | 0x84, 0x20 79 | ]) 80 | exp1.secure = true 81 | 82 | t.alike(c, exp1) 83 | 84 | foundmaclen = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt_detached(detachedc, mac, m, ad, null, nonce, firstkey) 85 | 86 | t.is(foundmaclen, sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES) 87 | const exp0 = c.subarray(0, mlen) 88 | exp0.secure = true 89 | t.alike(detachedc, exp0) 90 | 91 | m2len = sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(m2, null, c, ad, nonce, firstkey) 92 | t.is(m2len, mlen) 93 | 94 | t.alike(m, m2) 95 | 96 | m2.fill(0) 97 | sodium.crypto_aead_xchacha20poly1305_ietf_decrypt_detached(m2, null, c.subarray(0, mlen), mac, ad, nonce, firstkey) 98 | 99 | t.alike(m, m2) 100 | 101 | for (i = 0; i < clen; i++) { 102 | c[i] ^= (i + 1) 103 | t.exception.all(_ => sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(m2, null, c, ad, nonce, firstkey)) 104 | if (m.equals(m2)) t.fail() 105 | c[i] ^= (i + 1) 106 | } 107 | 108 | foundclen = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(c, m, null, null, nonce, firstkey) 109 | t.is(foundclen, clen) 110 | 111 | const exp2 = Buffer.from([ 112 | 0xf8, 0xeb, 0xea, 0x48, 0x75, 0x04, 0x40, 0x66, 113 | 0xfc, 0x16, 0x2a, 0x06, 0x04, 0xe1, 0x71, 0xfe, 114 | 0xec, 0xfb, 0x3d, 0x20, 0x42, 0x52, 0x48, 0x56, 115 | 0x3b, 0xcf, 0xd5, 0xa1, 0x55, 0xdc, 0xc4, 0x7b, 116 | 0xbd, 0xa7, 0x0b, 0x86, 0xe5, 0xab, 0x9b, 0x55, 117 | 0x00, 0x2b, 0xd1, 0x27, 0x4c, 0x02, 0xdb, 0x35, 118 | 0x32, 0x1a, 0xcd, 0x7a, 0xf8, 0xb2, 0xe2, 0xd2, 119 | 0x50, 0x15, 0xe1, 0x36, 0xb7, 0x67, 0x94, 0x58, 120 | 0xe9, 0xf4, 0x32, 0x43, 0xbf, 0x71, 0x9d, 0x63, 121 | 0x9b, 0xad, 0xb5, 0xfe, 0xac, 0x03, 0xf8, 0x0a, 122 | 0x19, 0xa9, 0x6e, 0xf1, 0x0c, 0xb1, 0xd1, 0x53, 123 | 0x33, 0xa8, 0x37, 0xb9, 0x09, 0x46, 0xba, 0x38, 124 | 0x54, 0xee, 0x74, 0xda, 0x3f, 0x25, 0x85, 0xef, 125 | 0xc7, 0xe1, 0xe1, 0x70, 0xe1, 0x7e, 0x15, 0xe5, 126 | 0x63, 0xe7, 0xe0, 0x96, 0xe0, 0x33, 0xd9, 0x1b, 127 | 0x63, 0xf7, 0xac, 0x92, 0xe9, 0x97, 0x2e, 0x0d, 128 | 0x43, 0xe5 129 | ]) 130 | exp2.secure = true 131 | 132 | t.alike(c, exp2) 133 | 134 | m2len = sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(m2, null, c, null, nonce, firstkey) 135 | t.is(m2len, mlen) 136 | 137 | t.alike(m2, m) 138 | 139 | m.copy(c) 140 | 141 | foundclen = sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(c, c.subarray(0, mlen), null, null, nonce, firstkey) 142 | 143 | t.is(foundclen, clen, 'clen is properly set (adlen=0)') 144 | 145 | const exp3 = Buffer.from([ 146 | 0xf8, 0xeb, 0xea, 0x48, 0x75, 0x04, 0x40, 0x66, 147 | 0xfc, 0x16, 0x2a, 0x06, 0x04, 0xe1, 0x71, 0xfe, 148 | 0xec, 0xfb, 0x3d, 0x20, 0x42, 0x52, 0x48, 0x56, 149 | 0x3b, 0xcf, 0xd5, 0xa1, 0x55, 0xdc, 0xc4, 0x7b, 150 | 0xbd, 0xa7, 0x0b, 0x86, 0xe5, 0xab, 0x9b, 0x55, 151 | 0x00, 0x2b, 0xd1, 0x27, 0x4c, 0x02, 0xdb, 0x35, 152 | 0x32, 0x1a, 0xcd, 0x7a, 0xf8, 0xb2, 0xe2, 0xd2, 153 | 0x50, 0x15, 0xe1, 0x36, 0xb7, 0x67, 0x94, 0x58, 154 | 0xe9, 0xf4, 0x32, 0x43, 0xbf, 0x71, 0x9d, 0x63, 155 | 0x9b, 0xad, 0xb5, 0xfe, 0xac, 0x03, 0xf8, 0x0a, 156 | 0x19, 0xa9, 0x6e, 0xf1, 0x0c, 0xb1, 0xd1, 0x53, 157 | 0x33, 0xa8, 0x37, 0xb9, 0x09, 0x46, 0xba, 0x38, 158 | 0x54, 0xee, 0x74, 0xda, 0x3f, 0x25, 0x85, 0xef, 159 | 0xc7, 0xe1, 0xe1, 0x70, 0xe1, 0x7e, 0x15, 0xe5, 160 | 0x63, 0xe7, 0xe0, 0x96, 0xe0, 0x33, 0xd9, 0x1b, 161 | 0x63, 0xf7, 0xac, 0x92, 0xe9, 0x97, 0x2e, 0x0d, 162 | 0x43, 0xe5 163 | ]) 164 | exp3.secure = true 165 | 166 | t.alike(c, exp3) 167 | 168 | const decrypted = sodium.sodium_malloc(c.byteLength - sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES) 169 | m2len = sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(decrypted, null, c, null, nonce, firstkey) 170 | t.is(m2len, mlen, 'm2len is properly set (adlen=0)') 171 | 172 | t.alike(m, decrypted, 'm == c (adlen=0)') 173 | 174 | sodium.crypto_aead_xchacha20poly1305_ietf_keygen(key2) 175 | t.exception.all(_ => (m2len = sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(decrypted, null, c, null, nonce, key2))) 176 | }) 177 | 178 | test('keygen', function (t) { 179 | const key1 = sodium.sodium_malloc(sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES) 180 | const key2 = sodium.sodium_malloc(sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES) 181 | 182 | sodium.crypto_aead_xchacha20poly1305_ietf_keygen(key1) 183 | sodium.crypto_aead_xchacha20poly1305_ietf_keygen(key2) 184 | 185 | t.unlike(key1, key2) 186 | }) 187 | 188 | test('different keys', function (t) { 189 | const m = Buffer.from('Ladies and Gentlemen of the class of \'99: If I could offer you only one tip for the future, sunscreen would be it.') 190 | 191 | const key1 = sodium.sodium_malloc(sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES) 192 | const key2 = sodium.sodium_malloc(sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES) 193 | sodium.crypto_aead_xchacha20poly1305_ietf_keygen(key1) 194 | sodium.crypto_aead_xchacha20poly1305_ietf_keygen(key2) 195 | 196 | const nonce = sodium.sodium_malloc(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES) 197 | sodium.randombytes_buf(nonce) 198 | 199 | const clen = m.byteLength + sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES 200 | const c1 = sodium.sodium_malloc(clen) 201 | const c2 = sodium.sodium_malloc(clen) 202 | 203 | const m1 = sodium.sodium_malloc(m.byteLength) 204 | const m2 = sodium.sodium_malloc(m.byteLength) 205 | 206 | t.is(sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(c1, m, null, null, nonce, key1), clen) 207 | t.absent(c1.equals(c2)) 208 | t.absent(c1.equals(m)) 209 | t.is(sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(c2, m, null, null, nonce, key2), clen) 210 | t.absent(c1.equals(c2)) 211 | t.absent(c2.equals(m)) 212 | 213 | t.exception.all(_ => sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(m1, null, c1, null, nonce, key2)) 214 | t.exception.all(_ => sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(m2, null, c2, null, nonce, key1)) 215 | 216 | t.is(sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(m1, null, c1, null, nonce, key1), m.byteLength) 217 | t.ok(m.equals(m1)) 218 | t.is(sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(m2, null, c2, null, nonce, key2), m.byteLength) 219 | t.ok(m.equals(m2)) 220 | }) 221 | 222 | test('different nonce', function (t) { 223 | const m = Buffer.from('Ladies and Gentlemen of the class of \'99: If I could offer you only one tip for the future, sunscreen would be it.') 224 | 225 | const key = sodium.sodium_malloc(sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES) 226 | sodium.crypto_aead_xchacha20poly1305_ietf_keygen(key) 227 | 228 | const n1 = sodium.sodium_malloc(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES) 229 | const n2 = sodium.sodium_malloc(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES) 230 | sodium.randombytes_buf(n1) 231 | sodium.randombytes_buf(n2) 232 | 233 | const clen = m.byteLength + sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES 234 | const c1 = sodium.sodium_malloc(clen) 235 | const c2 = sodium.sodium_malloc(clen) 236 | 237 | const m1 = sodium.sodium_malloc(m.byteLength) 238 | const m2 = sodium.sodium_malloc(m.byteLength) 239 | 240 | t.is(sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(c1, m, null, null, n1, key), clen) 241 | t.absent(c1.equals(c2)) 242 | t.absent(c1.equals(m)) 243 | t.is(sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(c2, m, null, null, n2, key), clen) 244 | t.absent(c1.equals(c2)) 245 | t.absent(c2.equals(m)) 246 | 247 | t.exception.all(_ => sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(m1, null, c1, null, n2, key)) 248 | t.exception.all(_ => sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(m2, null, c2, null, n1, key)) 249 | 250 | t.is(sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(m1, null, c1, null, n1, key), m.byteLength) 251 | t.ok(m.equals(m1)) 252 | t.is(sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(m2, null, c2, null, n2, key), m.byteLength) 253 | t.ok(m.equals(m2)) 254 | }) 255 | 256 | test('detached -> non-detached', function (t) { 257 | const m = Buffer.from('Ladies and Gentlemen of the class of \'99: If I could offer you only one tip for the future, sunscreen would be it.') 258 | m.secure = true 259 | 260 | const key = sodium.sodium_malloc(sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES) 261 | sodium.crypto_aead_xchacha20poly1305_ietf_keygen(key) 262 | 263 | const nonce = sodium.sodium_malloc(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES) 264 | sodium.randombytes_buf(nonce) 265 | 266 | const mac = sodium.sodium_malloc(sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES) 267 | const clen = m.byteLength 268 | const c = sodium.sodium_malloc(clen) 269 | 270 | t.is(sodium.crypto_aead_xchacha20poly1305_ietf_encrypt_detached(c, mac, m, null, null, nonce, key), mac.byteLength) 271 | 272 | const m1 = sodium.sodium_malloc(m.byteLength) 273 | t.is(sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(m1, null, Buffer.concat([c, mac]), null, nonce, key), m.byteLength) 274 | 275 | t.alike(m, m1) 276 | }) 277 | 278 | test('non-detached -> detached', function (t) { 279 | const m = Buffer.from('Ladies and Gentlemen of the class of \'99: If I could offer you only one tip for the future, sunscreen would be it.') 280 | m.secure = true 281 | 282 | const key = sodium.sodium_malloc(sodium.crypto_aead_xchacha20poly1305_ietf_KEYBYTES) 283 | sodium.crypto_aead_xchacha20poly1305_ietf_keygen(key) 284 | 285 | const nonce = sodium.sodium_malloc(sodium.crypto_aead_xchacha20poly1305_ietf_NPUBBYTES) 286 | sodium.randombytes_buf(nonce) 287 | 288 | const clen = m.byteLength + sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES 289 | const c = sodium.sodium_malloc(clen) 290 | 291 | t.is(sodium.crypto_aead_xchacha20poly1305_ietf_encrypt(c, m, null, null, nonce, key), c.byteLength) 292 | 293 | const m1 = sodium.sodium_malloc(m.byteLength) 294 | const csub = c.subarray(0, clen - sodium.crypto_aead_xchacha20poly1305_ietf_ABYTES) 295 | const macsub = c.subarray(csub.byteLength) 296 | sodium.crypto_aead_xchacha20poly1305_ietf_decrypt_detached(m1, null, csub, macsub, null, nonce, key) 297 | 298 | t.alike(m, m1) 299 | }) 300 | 301 | /** 302 | * Need to test in-place encryption 303 | * detach can talk to non detach 304 | * encrypt - decrypt 305 | * different nonce 306 | * different key 307 | * return values 308 | */ 309 | -------------------------------------------------------------------------------- /test/crypto_auth.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | 4 | test('crypto_auth', function (t) { 5 | const key = Buffer.alloc(sodium.crypto_auth_KEYBYTES) 6 | sodium.randombytes_buf(key) 7 | 8 | const mac = Buffer.alloc(sodium.crypto_auth_BYTES) 9 | const value = Buffer.from('Hej, Verden') 10 | 11 | sodium.crypto_auth(mac, value, key) 12 | 13 | t.not(mac, Buffer.alloc(mac.length), 'mac not blank') 14 | t.absent(sodium.crypto_auth_verify(Buffer.alloc(mac.length), value, key), 'does not verify') 15 | t.ok(sodium.crypto_auth_verify(mac, value, key), 'verifies') 16 | }) 17 | 18 | test('crypto_auth #1', t => { 19 | // "Test Case 2" from RFC 4231 20 | const key = stringToArray('Jefe', 32) 21 | const c = stringToArray('what do ya want for nothing?') 22 | const c1 = stringToArray('wwhat do ya want for nothing') 23 | 24 | const a = new Uint8Array(sodium.crypto_auth_BYTES) 25 | 26 | const exp = [ 27 | new Uint8Array([ 28 | 0x16, 0x4b, 0x7a, 0x7b, 0xfc, 0xf8, 0x19, 0xe2, 29 | 0xe3, 0x95, 0xfb, 0xe7, 0x3b, 0x56, 0xe0, 0xa3, 30 | 0x87, 0xbd, 0x64, 0x22, 0x2e, 0x83, 0x1f, 0xd6, 31 | 0x10, 0x27, 0x0c, 0xd7, 0xea, 0x25, 0x05, 0x54 32 | ]), 33 | new Uint8Array([ 34 | 0x7b, 0x9d, 0x83, 0x38, 0xeb, 0x1e, 0x3d, 0xdd, 35 | 0xba, 0x8a, 0x9a, 0x35, 0x08, 0xd0, 0x34, 0xa1, 36 | 0xec, 0xbe, 0x75, 0x11, 0x37, 0xfa, 0x1b, 0xcb, 37 | 0xa0, 0xf9, 0x2a, 0x3e, 0x6d, 0xfc, 0x79, 0x80 38 | ]), 39 | new Uint8Array([ 40 | 0xb9, 0xd1, 0x4c, 0x51, 0xa6, 0xd4, 0xdd, 0x41, 41 | 0x60, 0x4e, 0xb0, 0x6c, 0x9c, 0x24, 0x0f, 0x1f, 42 | 0x64, 0xf1, 0x43, 0xb5, 0xcf, 0xde, 0xa3, 0x71, 43 | 0x29, 0xb2, 0x8b, 0xb7, 0x5d, 0x13, 0x71, 0xd3 44 | ]) 45 | ] 46 | 47 | sodium.crypto_auth(a, c, key) 48 | t.alike(a, exp[0]) 49 | t.ok(sodium.crypto_auth_verify(exp[0], c, key)) 50 | 51 | a.fill(0) 52 | sodium.crypto_auth(a, c1, key) 53 | t.alike(a, exp[1]) 54 | t.ok(sodium.crypto_auth_verify(exp[1], c1, key)) 55 | 56 | // Empty message tests 57 | a.fill(0) 58 | sodium.crypto_auth(a, new Uint8Array(0), key) 59 | t.alike(a, exp[2]) 60 | t.ok(sodium.crypto_auth_verify(exp[2], new Uint8Array(0), key)) 61 | t.end() 62 | }) 63 | 64 | test('crypto_auth', function (t) { 65 | const key = stringToArray('Jefe', 32) 66 | const c = stringToArray('what do ya want for nothing?') 67 | const a = new Uint8Array(sodium.crypto_auth_BYTES) 68 | const expected = new Uint8Array([ 69 | 0x16, 0x4b, 0x7a, 0x7b, 0xfc, 0xf8, 0x19, 0xe2, 0xe3, 0x95, 0xfb, 0xe7, 70 | 0x3b, 0x56, 0xe0, 0xa3, 0x87, 0xbd, 0x64, 0x22, 0x2e, 0x83, 0x1f, 0xd6, 71 | 0x10, 0x27, 0x0c, 0xd7, 0xea, 0x25, 0x05, 0x54 72 | ]) 73 | 74 | sodium.crypto_auth(a, c, key) 75 | 76 | t.alike(a, expected) 77 | t.ok(sodium.crypto_auth_verify(a, c, key)) 78 | 79 | c[Math.floor(Math.random() * c.length)] += 1 80 | t.absent(sodium.crypto_auth_verify(a, c, key)) 81 | 82 | t.end() 83 | }) 84 | 85 | test('wrong keylength', t => { 86 | const a = new Uint8Array(32) 87 | const c = new Uint8Array(0) 88 | 89 | for (let i = 0; i < 128; i++) { 90 | if (i === 32) continue 91 | const k = new Uint8Array(i) 92 | try { 93 | sodium.crypto_auth(a, c, k) 94 | t.fail('failed on test #' + i) 95 | } catch (e) { 96 | try { 97 | sodium.crypto_auth_verify(a, c, k) 98 | t.fail('failed on test #' + i) 99 | } catch (e) { 100 | try { 101 | sodium.crypto_auth_verify(k, c, a) 102 | t.fail('failed on test #' + i) 103 | } catch {} 104 | } 105 | } 106 | } 107 | 108 | t.pass('should not accept bad input length') 109 | t.end() 110 | }) 111 | 112 | test('crypto_auth constants', t => { 113 | t.is(sodium.crypto_auth_BYTES, 32) 114 | t.is(sodium.crypto_auth_KEYBYTES, 32) 115 | t.end() 116 | }) 117 | 118 | test('rfc4231 test case #6', t => { 119 | const keys = [ 120 | new Uint8Array([ 121 | 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 122 | 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 123 | 0x0b, 0x0b, 0x0b, 0x0b, 0x00, 0x00, 0x00, 0x00, 124 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 125 | ]), 126 | new Uint8Array([ 127 | 0x4a, 0x65, 0x66, 0x65, 0x00, 0x00, 0x00, 0x00, 128 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 129 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 130 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 131 | ]), 132 | new Uint8Array([ 133 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 134 | 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 135 | 0xaa, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00, 136 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 137 | ]), 138 | new Uint8Array([ 139 | 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 140 | 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 141 | 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 142 | 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 143 | ]) 144 | ] 145 | 146 | const data = [ 147 | new Uint8Array([ 148 | 0x48, 0x69, 0x20, 0x54, 0x68, 0x65, 0x72, 0x65 149 | ]), 150 | new Uint8Array([ 151 | 0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20, 152 | 0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20, 153 | 0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68, 154 | 0x69, 0x6e, 0x67, 0x3f 155 | ]), 156 | new Uint8Array([ 157 | 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 158 | 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 159 | 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 160 | 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 161 | 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 162 | 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 163 | 0xdd, 0xdd 164 | ]), 165 | new Uint8Array([ 166 | 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 167 | 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 168 | 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 169 | 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 170 | 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 171 | 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 172 | 0xcd, 0xcd 173 | ]) 174 | ] 175 | 176 | const exp = [ 177 | new Uint8Array([ 178 | 0x87, 0xaa, 0x7c, 0xde, 0xa5, 0xef, 0x61, 0x9d, 179 | 0x4f, 0xf0, 0xb4, 0x24, 0x1a, 0x1d, 0x6c, 0xb0, 180 | 0x23, 0x79, 0xf4, 0xe2, 0xce, 0x4e, 0xc2, 0x78, 181 | 0x7a, 0xd0, 0xb3, 0x05, 0x45, 0xe1, 0x7c, 0xde 182 | ]), 183 | new Uint8Array([ 184 | 0x16, 0x4b, 0x7a, 0x7b, 0xfc, 0xf8, 0x19, 0xe2, 185 | 0xe3, 0x95, 0xfb, 0xe7, 0x3b, 0x56, 0xe0, 0xa3, 186 | 0x87, 0xbd, 0x64, 0x22, 0x2e, 0x83, 0x1f, 0xd6, 187 | 0x10, 0x27, 0x0c, 0xd7, 0xea, 0x25, 0x05, 0x54 188 | ]), 189 | new Uint8Array([ 190 | 0xfa, 0x73, 0xb0, 0x08, 0x9d, 0x56, 0xa2, 0x84, 191 | 0xef, 0xb0, 0xf0, 0x75, 0x6c, 0x89, 0x0b, 0xe9, 192 | 0xb1, 0xb5, 0xdb, 0xdd, 0x8e, 0xe8, 0x1a, 0x36, 193 | 0x55, 0xf8, 0x3e, 0x33, 0xb2, 0x27, 0x9d, 0x39 194 | ]), 195 | new Uint8Array([ 196 | 0xb0, 0xba, 0x46, 0x56, 0x37, 0x45, 0x8c, 0x69, 197 | 0x90, 0xe5, 0xa8, 0xc5, 0xf6, 0x1d, 0x4a, 0xf7, 198 | 0xe5, 0x76, 0xd9, 0x7f, 0xf9, 0x4b, 0x87, 0x2d, 199 | 0xe7, 0x6f, 0x80, 0x50, 0x36, 0x1e, 0xe3, 0xdb 200 | ]) 201 | ] 202 | 203 | const a = new Uint8Array(sodium.crypto_auth_BYTES) 204 | 205 | for (let i = 0; i < keys.length; i++) { 206 | sodium.crypto_auth(a, data[i], keys[i]) 207 | t.alike(a, exp[i]) 208 | t.ok(sodium.crypto_auth_verify(exp[i], data[i], keys[i])) 209 | } 210 | 211 | t.end() 212 | }) 213 | 214 | function stringToArray (s, size = s.length) { 215 | const array = new Uint8Array(size) 216 | array.set(s.split('').map((c) => c.charCodeAt(0))) 217 | return array 218 | } 219 | -------------------------------------------------------------------------------- /test/crypto_box.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | 4 | test('crypto_box_seed_keypair', function (t) { 5 | const pk = Buffer.alloc(sodium.crypto_box_PUBLICKEYBYTES) 6 | const sk = Buffer.alloc(sodium.crypto_box_SECRETKEYBYTES) 7 | const seed = Buffer.alloc(sodium.crypto_box_SEEDBYTES, 'lo') 8 | 9 | t.exception.all(function () { 10 | sodium.crypto_box_seed_keypair() 11 | }, 'should validate input') 12 | 13 | t.exception.all(function () { 14 | sodium.crypto_box_seed_keypair(Buffer.alloc(0), Buffer.alloc(0), Buffer.alloc(0)) 15 | }, 'should validate input length') 16 | 17 | sodium.crypto_box_seed_keypair(pk, sk, seed) 18 | 19 | const eSk = '8661a95d21b134adc02881022ad86d37f32a230d537b525b997bce27aa745afc' 20 | const ePk = '425c5ba523e70411c77300bb48dd846562e6c1fcf0142d81d2567d650ce76c3b' 21 | 22 | t.alike(pk.toString('hex'), ePk, 'seeded public key') 23 | t.alike(sk.toString('hex'), eSk, 'seeded secret key') 24 | }) 25 | 26 | test('crypto_box_keypair', function (t) { 27 | const pk = Buffer.alloc(sodium.crypto_box_PUBLICKEYBYTES) 28 | const sk = Buffer.alloc(sodium.crypto_box_SECRETKEYBYTES) 29 | 30 | sodium.crypto_box_keypair(pk, sk) 31 | 32 | t.not(pk, Buffer.alloc(pk.length), 'made public key') 33 | t.not(sk, Buffer.alloc(sk.length), 'made secret key') 34 | 35 | t.exception.all(function () { 36 | sodium.crypto_box_keypair() 37 | }, 'should validate input') 38 | 39 | t.exception.all(function () { 40 | sodium.crypto_box_keypair(Buffer.alloc(0), Buffer.alloc(0)) 41 | }, 'should validate input length') 42 | }) 43 | 44 | test('crypto_box_detached', function (t) { 45 | const pk = Buffer.alloc(sodium.crypto_box_PUBLICKEYBYTES) 46 | const sk = Buffer.alloc(sodium.crypto_box_SECRETKEYBYTES) 47 | const nonce = Buffer.alloc(sodium.crypto_box_NONCEBYTES) 48 | 49 | sodium.crypto_box_keypair(pk, sk) 50 | 51 | const message = Buffer.from('Hello, World!') 52 | const mac = Buffer.alloc(sodium.crypto_box_MACBYTES) 53 | const cipher = Buffer.alloc(message.length) 54 | 55 | sodium.crypto_box_detached(cipher, mac, message, nonce, pk, sk) 56 | 57 | t.not(cipher, Buffer.alloc(cipher.length), 'not blank') 58 | 59 | const plain = Buffer.alloc(cipher.length) 60 | t.absent(sodium.crypto_box_open_detached(plain, cipher, Buffer.alloc(mac.length), nonce, pk, sk), 'does not decrypt') 61 | t.ok(sodium.crypto_box_open_detached(plain, cipher, mac, nonce, pk, sk), 'decrypts') 62 | t.alike(plain, message, 'same message') 63 | }) 64 | 65 | test('crypto_box_easy', function (t) { 66 | const pk = Buffer.alloc(sodium.crypto_box_PUBLICKEYBYTES) 67 | const sk = Buffer.alloc(sodium.crypto_box_SECRETKEYBYTES) 68 | const nonce = Buffer.alloc(sodium.crypto_box_NONCEBYTES) 69 | 70 | sodium.crypto_box_keypair(pk, sk) 71 | 72 | const message = Buffer.from('Hello, World!') 73 | const cipher = Buffer.alloc(message.length + sodium.crypto_box_MACBYTES) 74 | 75 | sodium.crypto_box_easy(cipher, message, nonce, pk, sk) 76 | 77 | t.not(cipher, Buffer.alloc(cipher.length), 'not blank') 78 | 79 | const plain = Buffer.alloc(cipher.length - sodium.crypto_box_MACBYTES) 80 | t.absent(sodium.crypto_box_open_easy(plain, Buffer.alloc(cipher.length), nonce, pk, sk), 'does not decrypt') 81 | t.ok(sodium.crypto_box_open_easy(plain, cipher, nonce, pk, sk), 'decrypts') 82 | t.alike(plain, message, 'same message') 83 | }) 84 | 85 | test('crypto_box_seal', function (t) { 86 | const pk = Buffer.alloc(sodium.crypto_box_PUBLICKEYBYTES) 87 | const sk = Buffer.alloc(sodium.crypto_box_SECRETKEYBYTES) 88 | 89 | sodium.crypto_box_keypair(pk, sk) 90 | 91 | const pk2 = Buffer.alloc(sodium.crypto_box_PUBLICKEYBYTES) 92 | const sk2 = Buffer.alloc(sodium.crypto_box_SECRETKEYBYTES) 93 | 94 | sodium.crypto_box_keypair(pk2, sk2) 95 | 96 | const message = Buffer.from('Hello, sealed World!') 97 | const cipher = Buffer.alloc(message.length + sodium.crypto_box_SEALBYTES) 98 | 99 | sodium.crypto_box_seal(cipher, message, pk) 100 | t.not(cipher, message, 'did not encrypt!') 101 | 102 | t.not(cipher, Buffer.alloc(cipher.length), 'not blank') 103 | 104 | const plain = Buffer.alloc(cipher.length - sodium.crypto_box_SEALBYTES) 105 | t.absent(sodium.crypto_box_seal_open(plain, cipher, pk2, sk2), 'does not decrypt') 106 | t.ok(sodium.crypto_box_seal_open(plain, cipher, pk, sk), 'decrypts') 107 | t.alike(plain, message, 'same message') 108 | }) 109 | 110 | test('crypto_box_seal/crypto_box_seal_open self-decrypt', function (t) { 111 | const pubKey = Buffer.alloc(sodium.crypto_box_PUBLICKEYBYTES) 112 | const secret = Buffer.alloc(sodium.crypto_box_SECRETKEYBYTES) 113 | 114 | sodium.crypto_box_keypair(pubKey, secret) 115 | 116 | const msg = Buffer.from('hello world') 117 | const cipher = Buffer.alloc(sodium.crypto_box_SEALBYTES + msg.length) 118 | sodium.crypto_box_seal(cipher, msg, pubKey) 119 | 120 | const out = Buffer.alloc(cipher.length - sodium.crypto_box_SEALBYTES) 121 | sodium.crypto_box_seal_open(out, cipher, pubKey, secret) 122 | t.alike(out.toString(), msg.toString()) 123 | t.end() 124 | }) 125 | 126 | test('crypto_box_seal_open cross-decrypt', function (t) { 127 | const pubKey = Buffer.from( 128 | 'e0bb844ae3f48bb04323c8dfe7c34cf86608db2e2112f927953060c80506287f', 'hex') 129 | const secret = Buffer.from( 130 | '036a9de1ecc9d152cf39fed1b3e15bf761ae39a299031adc011cc9809041abfa', 'hex') 131 | const cipher = Buffer.from( 132 | '249912e916ad8bcf96a3f9b750da2703' + 133 | '2eccdf83b5cff0d6a59a8bbe0bcd5823' + 134 | '5de9fbca55bd5416c754e5e0e0fe2f0c' + 135 | '4e50df0cb302f1c4378f80', 'hex') 136 | 137 | const out = Buffer.alloc(cipher.length - sodium.crypto_box_SEALBYTES) 138 | sodium.crypto_box_seal_open(out, cipher, pubKey, secret) 139 | t.alike(out.toString(), 'hello world') 140 | t.end() 141 | }) 142 | 143 | test('crypto_box_seed_keypair', function (t) { 144 | const seed = Buffer.from([ 145 | 0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 146 | 0x7d, 0x3c, 0x16, 0xc1, 0x72, 0x51, 0xb2, 147 | 0x66, 0x45, 0xdf, 0x4c, 0x2f, 0x87, 0xeb, 148 | 0xc0, 0x99, 0x2a, 0xb1, 0x77, 0xfb, 0xa5, 149 | 0x1d, 0xb9, 0x2c, 0x2a 150 | ]) 151 | 152 | const expPk = Buffer.from([ 153 | 0xed, 0x77, 0x49, 0xb4, 0xd9, 0x89, 0xf6, 0x95, 154 | 0x7f, 0x3b, 0xfd, 0xe6, 0xc5, 0x67, 0x67, 0xe9, 155 | 0x88, 0xe2, 0x1c, 0x9f, 0x87, 0x84, 0xd9, 0x1d, 156 | 0x61, 0x00, 0x11, 0xcd, 0x55, 0x3f, 0x9b, 0x06 157 | ]) 158 | 159 | const expSk = Buffer.from([ 160 | 0xac, 0xcd, 0x44, 0xeb, 0x8e, 0x93, 0x31, 0x9c, 161 | 0x05, 0x70, 0xbc, 0x11, 0x00, 0x5c, 0x0e, 0x01, 162 | 0x89, 0xd3, 0x4f, 0xf0, 0x2f, 0x6c, 0x17, 0x77, 163 | 0x34, 0x11, 0xad, 0x19, 0x12, 0x93, 0xc9, 0x8f 164 | ]) 165 | 166 | const sk = Buffer.alloc(32) 167 | const pk = Buffer.alloc(32) 168 | 169 | sodium.crypto_box_seed_keypair(pk, sk, seed) 170 | 171 | t.alike(pk, expPk) 172 | t.alike(sk, expSk) 173 | 174 | t.end() 175 | }) 176 | 177 | test('crypto_box_easy', (t) => { 178 | const alicesk = new Uint8Array([ 179 | 0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d, 0x3c, 0x16, 0xc1, 0x72, 180 | 0x51, 0xb2, 0x66, 0x45, 0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0, 0x99, 0x2a, 181 | 0xb1, 0x77, 0xfb, 0xa5, 0x1d, 0xb9, 0x2c, 0x2a 182 | ]) 183 | const bobpk = new Uint8Array([ 184 | 0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4, 0xd3, 0x5b, 0x61, 0xc2, 185 | 0xec, 0xe4, 0x35, 0x37, 0x3f, 0x83, 0x43, 0xc8, 0x5b, 0x78, 0x67, 0x4d, 186 | 0xad, 0xfc, 0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f 187 | ]) 188 | const nonce = new Uint8Array([ 189 | 0x69, 0x69, 0x6e, 0xe9, 0x55, 0xb6, 0x2b, 0x73, 0xcd, 0x62, 0xbd, 0xa8, 190 | 0x75, 0xfc, 0x73, 0xd6, 0x82, 0x19, 0xe0, 0x03, 0x6b, 0x7a, 0x0b, 0x37 191 | ]) 192 | const m = new Uint8Array([ 193 | 0xbe, 0x07, 0x5f, 0xc5, 0x3c, 0x81, 0xf2, 0xd5, 0xcf, 0x14, 0x13, 0x16, 194 | 0xeb, 0xeb, 0x0c, 0x7b, 0x52, 0x28, 0xc5, 0x2a, 0x4c, 0x62, 0xcb, 0xd4, 195 | 0x4b, 0x66, 0x84, 0x9b, 0x64, 0x24, 0x4f, 0xfc, 0xe5, 0xec, 0xba, 0xaf, 196 | 0x33, 0xbd, 0x75, 0x1a, 0x1a, 0xc7, 0x28, 0xd4, 0x5e, 0x6c, 0x61, 0x29, 197 | 0x6c, 0xdc, 0x3c, 0x01, 0x23, 0x35, 0x61, 0xf4, 0x1d, 0xb6, 0x6c, 0xce, 198 | 0x31, 0x4a, 0xdb, 0x31, 0x0e, 0x3b, 0xe8, 0x25, 0x0c, 0x46, 0xf0, 0x6d, 199 | 0xce, 0xea, 0x3a, 0x7f, 0xa1, 0x34, 0x80, 0x57, 0xe2, 0xf6, 0x55, 0x6a, 200 | 0xd6, 0xb1, 0x31, 0x8a, 0x02, 0x4a, 0x83, 0x8f, 0x21, 0xaf, 0x1f, 0xde, 201 | 0x04, 0x89, 0x77, 0xeb, 0x48, 0xf5, 0x9f, 0xfd, 0x49, 0x24, 0xca, 0x1c, 202 | 0x60, 0x90, 0x2e, 0x52, 0xf0, 0xa0, 0x89, 0xbc, 0x76, 0x89, 0x70, 0x40, 203 | 0xe0, 0x82, 0xf9, 0x37, 0x76, 0x38, 0x48, 0x64, 0x5e, 0x07, 0x05 204 | ]) 205 | 206 | const c = new Uint8Array(147) 207 | sodium.crypto_box_easy(c, m, nonce, bobpk, alicesk) 208 | 209 | const expected1 = new Uint8Array([ 210 | 0xf3, 0xff, 0xc7, 0x70, 0x3f, 0x94, 0x00, 0xe5, 0x2a, 0x7d, 0xfb, 0x4b, 211 | 0x3d, 0x33, 0x05, 0xd9, 0x8e, 0x99, 0x3b, 0x9f, 0x48, 0x68, 0x12, 0x73, 212 | 0xc2, 0x96, 0x50, 0xba, 0x32, 0xfc, 0x76, 0xce, 0x48, 0x33, 0x2e, 0xa7, 213 | 0x16, 0x4d, 0x96, 0xa4, 0x47, 0x6f, 0xb8, 0xc5, 0x31, 0xa1, 0x18, 0x6a, 214 | 0xc0, 0xdf, 0xc1, 0x7c, 0x98, 0xdc, 0xe8, 0x7b, 0x4d, 0xa7, 0xf0, 0x11, 215 | 0xec, 0x48, 0xc9, 0x72, 0x71, 0xd2, 0xc2, 0x0f, 0x9b, 0x92, 0x8f, 0xe2, 216 | 0x27, 0x0d, 0x6f, 0xb8, 0x63, 0xd5, 0x17, 0x38, 0xb4, 0x8e, 0xee, 0xe3, 217 | 0x14, 0xa7, 0xcc, 0x8a, 0xb9, 0x32, 0x16, 0x45, 0x48, 0xe5, 0x26, 0xae, 218 | 0x90, 0x22, 0x43, 0x68, 0x51, 0x7a, 0xcf, 0xea, 0xbd, 0x6b, 0xb3, 0x73, 219 | 0x2b, 0xc0, 0xe9, 0xda, 0x99, 0x83, 0x2b, 0x61, 0xca, 0x01, 0xb6, 0xde, 220 | 0x56, 0x24, 0x4a, 0x9e, 0x88, 0xd5, 0xf9, 0xb3, 0x79, 0x73, 0xf6, 0x22, 221 | 0xa4, 0x3d, 0x14, 0xa6, 0x59, 0x9b, 0x1f, 0x65, 0x4c, 0xb4, 0x5a, 0x74, 222 | 0xe3, 0x55, 0xa5 223 | ]) 224 | 225 | t.alike(c, expected1, 'encrypts correctly') 226 | 227 | // This test isn't found upstream, but it seems necessary to have at least 228 | // one crypto_box_open_easy() working since the next test diverges. 229 | const o = new Uint8Array(131) 230 | t.ok(sodium.crypto_box_open_easy(o, expected1, nonce, bobpk, alicesk)) 231 | t.alike(o, m, 'decrypts correctly') 232 | 233 | const guardPage = new Uint8Array(0) 234 | 235 | t.execution(() => sodium.crypto_box_easy( 236 | c.subarray(0, sodium.crypto_box_MACBYTES), 237 | guardPage, 238 | nonce, 239 | bobpk, 240 | alicesk 241 | )) 242 | 243 | const expected2 = new Uint8Array([ 244 | 0x25, 0x39, 0x12, 0x1d, 0x8e, 0x23, 0x4e, 0x65, 0x2d, 0x65, 0x1f, 0xa4, 245 | 0xc8, 0xcf, 0xf8, 0x80, 0x8e 246 | ]) 247 | 248 | t.alike(c.subarray(0, expected2.length), expected2) 249 | 250 | t.ok(sodium.crypto_box_open_easy( 251 | new Uint8Array(0), 252 | c.subarray(0, sodium.crypto_box_MACBYTES), 253 | nonce, 254 | bobpk, 255 | alicesk 256 | )) 257 | 258 | c[Math.floor(Math.random() * sodium.crypto_box_MACBYTES)] += 1 259 | 260 | t.absent(sodium.crypto_box_open_easy(new Uint8Array(0), c.subarray(0, sodium.crypto_box_MACBYTES), nonce, bobpk, alicesk)) 261 | 262 | t.end() 263 | }) 264 | 265 | /* eslint-disable camelcase */ 266 | test('crypto_box_easy2', t => { 267 | const alicepk = new Uint8Array(sodium.crypto_box_PUBLICKEYBYTES) 268 | const alicesk = new Uint8Array(sodium.crypto_box_SECRETKEYBYTES) 269 | const bobpk = new Uint8Array(sodium.crypto_box_PUBLICKEYBYTES) 270 | const bobsk = new Uint8Array(sodium.crypto_box_SECRETKEYBYTES) 271 | const nonce = new Uint8Array(sodium.crypto_box_NONCEBYTES) 272 | const m_size = 7 + Math.floor(Math.random() * 1000) 273 | const m = new Uint8Array(m_size) 274 | const m2 = new Uint8Array(m_size) 275 | const c = new Uint8Array(sodium.crypto_box_MACBYTES + m_size) 276 | 277 | sodium.crypto_box_keypair(alicepk, alicesk) 278 | sodium.crypto_box_keypair(bobpk, bobsk) 279 | 280 | const mlen = Math.floor(Math.random() * m_size) + 1 281 | sodium.randombytes_buf(m.subarray(0, mlen)) 282 | sodium.randombytes_buf(nonce.subarray(0, sodium.crypto_box_NONCEBYTES)) 283 | 284 | t.execution(() => sodium.crypto_box_easy(c.subarray(0, mlen + sodium.crypto_box_MACBYTES), m.subarray(0, mlen), nonce, bobpk, alicesk)) 285 | 286 | t.ok(sodium.crypto_box_open_easy(m2.subarray(0, mlen), c.subarray(0, mlen + sodium.crypto_box_MACBYTES), nonce, alicepk, bobsk)) 287 | t.alike(m.subarray(0, mlen), m2.subarray(0, mlen)) 288 | 289 | for (let i = sodium.crypto_box_MACBYTES; i < mlen + sodium.crypto_box_MACBYTES - 1; i++) { 290 | if (sodium.crypto_box_open_easy(m2.subarray(0, i - sodium.crypto_box_MACBYTES), c.subarray(0, i), nonce, alicepk, bobsk)) { 291 | t.fail('short open() should fail.') 292 | } 293 | } 294 | 295 | c.set(m.subarray(0, mlen)) 296 | t.execution(() => sodium.crypto_box_easy(c.subarray(0, mlen + sodium.crypto_box_MACBYTES), c.subarray(0, mlen), nonce, bobpk, alicesk)) 297 | 298 | t.unlike(m.subarray(0, mlen), c.subarray(0, mlen)) 299 | t.unlike(m.subarray(0, mlen), c.subarray(sodium.crypto_box_MACBYTES, sodium.crypto_box_MACBYTES + mlen)) 300 | 301 | t.ok(sodium.crypto_box_open_easy(c.subarray(0, mlen), c.subarray(0, mlen + sodium.crypto_box_MACBYTES), nonce, alicepk, bobsk)) 302 | 303 | t.end() 304 | }) 305 | -------------------------------------------------------------------------------- /test/crypto_generichash.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | 4 | test('crypto_generichash', function (t) { 5 | const buf = Buffer.from('Hello, World!') 6 | 7 | const out = Buffer.alloc(sodium.crypto_generichash_BYTES) 8 | sodium.crypto_generichash(out, buf) 9 | 10 | t.alike(out.toString('hex'), '511bc81dde11180838c562c82bb35f3223f46061ebde4a955c27b3f489cf1e03', 'hashed buffer') 11 | 12 | const min = Buffer.alloc(sodium.crypto_generichash_BYTES_MIN) 13 | sodium.crypto_generichash(min, buf) 14 | 15 | t.alike(min.toString('hex'), '3895c59e4aeb0903396b5be3fbec69fe', 'hashed buffer min') 16 | 17 | const max = Buffer.alloc(sodium.crypto_generichash_BYTES_MAX) 18 | sodium.crypto_generichash(max, buf) 19 | 20 | const res = '7dfdb888af71eae0e6a6b751e8e3413d767ef4fa52a7993daa9ef097f7aa3d949199c113caa37c94f80cf3b22f7d9d6e4f5def4ff927830cffe4857c34be3d89' 21 | t.alike(max.toString('hex'), res, 'hashed buffer max') 22 | }) 23 | 24 | test('crypto_generichash with key', function (t) { 25 | const buf = Buffer.from('Hello, World!') 26 | const key = Buffer.alloc(sodium.crypto_generichash_KEYBYTES) 27 | 28 | key.fill('lo') 29 | 30 | const out = Buffer.alloc(sodium.crypto_generichash_BYTES) 31 | sodium.crypto_generichash(out, buf, key) 32 | 33 | t.alike(out.toString('hex'), 'f4113fe33d43c24c54627d40efa1a78838d4a6d689fd6e83c213848904fffa8c', 'hashed buffer') 34 | 35 | const min = Buffer.alloc(sodium.crypto_generichash_BYTES_MIN) 36 | sodium.crypto_generichash(min, buf, key) 37 | 38 | t.alike(min.toString('hex'), 'c8226257b0d1c3dcf4bbc3ef79574689', 'hashed buffer min') 39 | 40 | const max = Buffer.alloc(sodium.crypto_generichash_BYTES_MAX) 41 | sodium.crypto_generichash(max, buf, key) 42 | 43 | const res = '763eda46f4c6c61abb4310eb8a488950e9e0667b2fca03c463dc7489e94f065b7af6063fe86b0441c3eb9052800121d55730412abb2cbe0761b1d66f9b047c1c' 44 | t.alike(max.toString('hex'), res, 'hashed buffer max') 45 | }) 46 | 47 | test('crypto_generichash_state', function (t) { 48 | const state = Buffer.alloc(sodium.crypto_generichash_STATEBYTES) 49 | sodium.crypto_generichash_init(state, null, sodium.crypto_generichash_BYTES) 50 | 51 | const buf = Buffer.from('Hej, Verden') 52 | 53 | for (let i = 0; i < 10; i++) sodium.crypto_generichash_update(state, buf) 54 | 55 | const out = Buffer.alloc(sodium.crypto_generichash_BYTES) 56 | sodium.crypto_generichash_final(state, out) 57 | 58 | t.alike(out.toString('hex'), 'cbc20f347f5dfe37dc13231cbf7eaa4ec48e585ec055a96839b213f62bd8ce00', 'streaming hash') 59 | }) 60 | 61 | test('crypto_generichash state with key', function (t) { 62 | const key = Buffer.alloc(sodium.crypto_generichash_KEYBYTES) 63 | key.fill('lo') 64 | 65 | const state = Buffer.alloc(sodium.crypto_generichash_STATEBYTES) 66 | sodium.crypto_generichash_init(state, key, sodium.crypto_generichash_BYTES) 67 | 68 | const buf = Buffer.from('Hej, Verden') 69 | 70 | for (let i = 0; i < 10; i++) sodium.crypto_generichash_update(state, buf) 71 | 72 | const out = Buffer.alloc(sodium.crypto_generichash_BYTES) 73 | sodium.crypto_generichash_final(state, out) 74 | 75 | t.alike(out.toString('hex'), '405f14acbeeb30396b8030f78e6a84bab0acf08cb1376aa200a500f669f675dc', 'streaming keyed hash') 76 | }) 77 | 78 | test('crypto_generichash state with hash length', function (t) { 79 | const state = Buffer.alloc(sodium.crypto_generichash_STATEBYTES) 80 | sodium.crypto_generichash_init(state, null, sodium.crypto_generichash_BYTES_MIN) 81 | 82 | const buf = Buffer.from('Hej, Verden') 83 | 84 | for (let i = 0; i < 10; i++) sodium.crypto_generichash_update(state, buf) 85 | 86 | const out = Buffer.alloc(sodium.crypto_generichash_BYTES_MIN) 87 | sodium.crypto_generichash_final(state, out) 88 | 89 | t.alike(out.toString('hex'), 'decacdcc3c61948c79d9f8dee5b6aa99', 'streaming short hash') 90 | }) 91 | 92 | test('crypto_generichash state with key and hash length', function (t) { 93 | const key = Buffer.alloc(sodium.crypto_generichash_KEYBYTES) 94 | key.fill('lo') 95 | 96 | const state = Buffer.alloc(sodium.crypto_generichash_STATEBYTES) 97 | sodium.crypto_generichash_init(state, key, sodium.crypto_generichash_BYTES_MIN) 98 | 99 | const buf = Buffer.from('Hej, Verden') 100 | 101 | for (let i = 0; i < 10; i++) sodium.crypto_generichash_update(state, buf) 102 | 103 | const out = Buffer.alloc(sodium.crypto_generichash_BYTES_MIN) 104 | sodium.crypto_generichash_final(state, out) 105 | 106 | t.alike(out.toString('hex'), 'fb43f0ab6872cbfd39ec4f8a1bc6fb37', 'streaming short keyed hash') 107 | }) 108 | 109 | test('crypto_generichash_batch', function (t) { 110 | const buf = Buffer.from('Hej, Verden') 111 | const batch = [] 112 | for (let i = 0; i < 10; i++) batch.push(buf) 113 | 114 | const out = Buffer.alloc(sodium.crypto_generichash_BYTES) 115 | sodium.crypto_generichash_batch(out, batch) 116 | 117 | t.alike(out.toString('hex'), 'cbc20f347f5dfe37dc13231cbf7eaa4ec48e585ec055a96839b213f62bd8ce00', 'batch hash') 118 | }) 119 | 120 | test('crypto_generichash_batch with key', function (t) { 121 | const key = Buffer.alloc(sodium.crypto_generichash_KEYBYTES) 122 | key.fill('lo') 123 | 124 | const buf = Buffer.from('Hej, Verden') 125 | const batch = [] 126 | for (let i = 0; i < 10; i++) batch.push(buf) 127 | 128 | const out = Buffer.alloc(sodium.crypto_generichash_BYTES) 129 | sodium.crypto_generichash_batch(out, batch, key) 130 | 131 | t.alike(out.toString('hex'), '405f14acbeeb30396b8030f78e6a84bab0acf08cb1376aa200a500f669f675dc', 'batch keyed hash') 132 | }) 133 | -------------------------------------------------------------------------------- /test/crypto_hash.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | 4 | test('crypto_hash', function (t) { 5 | const out = Buffer.alloc(sodium.crypto_hash_BYTES) 6 | const inp = Buffer.from('Hej, Verden!') 7 | 8 | t.exception.all(function () { 9 | sodium.crypto_hash(Buffer.alloc(0), inp) 10 | }, 'throws on bad input') 11 | 12 | sodium.crypto_hash(out, inp) 13 | 14 | const result = 'bcf8e6d11dec2da6e93abb99a73c8e9c387886a5f84fbca5e25af85af26ee39161b7e0c9f9cf547f2aef40523f1aab80e26ec3c630db43ce78adc8c058dc5d16' 15 | t.alike(out.toString('hex'), result, 'hashed the string') 16 | }) 17 | -------------------------------------------------------------------------------- /test/crypto_hash_sha256.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | 4 | test('crypto_hash_sha256', function (t) { 5 | const out = Buffer.alloc(sodium.crypto_hash_sha256_BYTES) 6 | const inp = Buffer.from('Hej, Verden!') 7 | 8 | t.exception.all(function () { 9 | sodium.crypto_hash(Buffer.alloc(0), inp) 10 | }, 'throws on bad input') 11 | 12 | sodium.crypto_hash_sha256(out, inp) 13 | 14 | const result = 'f0704b1e832b05d01223952fb2512181af4f843ce7bb6b443afd5ea028010e6c' 15 | t.alike(out.toString('hex'), result, 'hashed the string') 16 | }) 17 | 18 | test('crypto_hash_sha256_state', function (t) { 19 | const state = Buffer.alloc(sodium.crypto_hash_sha256_STATEBYTES) 20 | sodium.crypto_hash_sha256_init(state) 21 | 22 | const buf = Buffer.from('Hej, Verden!') 23 | 24 | for (let i = 0; i < 10; i++) sodium.crypto_hash_sha256_update(state, buf) 25 | 26 | const out = Buffer.alloc(sodium.crypto_hash_sha256_BYTES) 27 | sodium.crypto_hash_sha256_final(state, out) 28 | 29 | const result = '14207db33c6ac7d39ca5fe0e74432fa7a2ed15caf7f6ab5ef68d24017a899974' 30 | t.alike(out.toString('hex'), result, 'hashed the string') 31 | }) 32 | -------------------------------------------------------------------------------- /test/crypto_hash_sha512.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | 4 | test('crypto_hash_sha512', function (t) { 5 | const out = Buffer.alloc(sodium.crypto_hash_sha512_BYTES) 6 | const inp = Buffer.from('Hej, Verden!') 7 | 8 | t.exception.all(function () { 9 | sodium.crypto_hash(Buffer.alloc(0), inp) 10 | }, 'throws on bad input') 11 | 12 | sodium.crypto_hash_sha512(out, inp) 13 | 14 | const result = 'bcf8e6d11dec2da6e93abb99a73c8e9c387886a5f84fbca5e25af85af26ee39161b7e0c9f9cf547f2aef40523f1aab80e26ec3c630db43ce78adc8c058dc5d16' 15 | t.alike(out.toString('hex'), result, 'hashed the string') 16 | }) 17 | 18 | test('crypto_hash_sha512_state', function (t) { 19 | const state = Buffer.alloc(sodium.crypto_hash_sha512_STATEBYTES) 20 | sodium.crypto_hash_sha512_init(state) 21 | 22 | const buf = Buffer.from('Hej, Verden!') 23 | 24 | for (let i = 0; i < 10; i++) sodium.crypto_hash_sha512_update(state, buf) 25 | 26 | const out = Buffer.alloc(sodium.crypto_hash_sha512_BYTES) 27 | sodium.crypto_hash_sha512_final(state, out) 28 | 29 | const result = 'a0a9b965c23be41fa8c344f483da39bedcf88b7f25cdc0bc9ea335fa264dc3db51f08c1d0f5f6f0ffb08a1d8643e2a1cd0ea8f03408ca03711c751d61787a229' 30 | t.alike(out.toString('hex'), result, 'hashed the string') 31 | }) 32 | -------------------------------------------------------------------------------- /test/crypto_kdf.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | 4 | test('crypto_kdf_keygen', function (t) { 5 | const key = Buffer.alloc(sodium.crypto_kdf_KEYBYTES) 6 | 7 | t.exception.all(function () { 8 | sodium.crypto_kdf_keygen(Buffer.alloc(1)) 9 | }) 10 | 11 | sodium.crypto_kdf_keygen(key) 12 | 13 | t.not(key, Buffer.alloc(key.length)) 14 | }) 15 | 16 | test('crypto_kdf_derive_from_key', function (t) { 17 | const key = Buffer.alloc(sodium.crypto_kdf_KEYBYTES) 18 | 19 | sodium.crypto_kdf_keygen(key) 20 | 21 | const subkey = Buffer.alloc(sodium.crypto_kdf_BYTES_MIN) 22 | 23 | sodium.crypto_kdf_derive_from_key(subkey, 0, Buffer.from('context_'), key) 24 | t.not(subkey, Buffer.alloc(subkey.length)) 25 | 26 | const subkey2 = Buffer.alloc(sodium.crypto_kdf_BYTES_MIN) 27 | 28 | sodium.crypto_kdf_derive_from_key(subkey2, 1, Buffer.from('context_'), key) 29 | t.not(subkey, subkey2) 30 | 31 | sodium.crypto_kdf_derive_from_key(subkey2, 0, Buffer.from('context_'), key) 32 | t.alike(subkey, subkey2) 33 | }) 34 | 35 | test('test vectors', function (assert) { 36 | const fixtures = require('./fixtures/crypto_kdf.json') 37 | 38 | for (let i = 0; i < fixtures.length; i++) { 39 | const key = Buffer.from(fixtures[i].key, 'hex') 40 | const subkeyLen = fixtures[i].subkey_len 41 | const id = fixtures[i].id 42 | const context = Buffer.from(fixtures[i].context, 'hex') 43 | 44 | const shouldError = fixtures[i].error 45 | 46 | const actual = Buffer.alloc(subkeyLen) 47 | 48 | try { 49 | sodium.crypto_kdf_derive_from_key(actual, id, context, key) 50 | const expected = Buffer.from(fixtures[i].subkey, 'hex') 51 | if (Buffer.compare(actual, expected) !== 0) { 52 | assert.fail('Failed on fixture #' + i) 53 | } 54 | } catch (ex) { 55 | if (shouldError === false) assert.fail('Failed on fixture #' + i) 56 | } 57 | } 58 | 59 | assert.pass('Passed all fixtures') 60 | assert.end() 61 | }) 62 | 63 | test('constants', function (t) { 64 | t.ok(sodium.crypto_kdf_PRIMITIVE) 65 | t.ok(sodium.crypto_kdf_BYTES_MAX > 0) 66 | t.ok(sodium.crypto_kdf_BYTES_MIN <= sodium.crypto_kdf_BYTES_MAX) 67 | t.ok(sodium.crypto_kdf_CONTEXTBYTES > 0) 68 | t.ok(sodium.crypto_kdf_KEYBYTES >= 16) 69 | t.end() 70 | }) 71 | -------------------------------------------------------------------------------- /test/crypto_kx.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | 4 | test('crypto_kx_seed_keypair', function (t) { 5 | const pk = Buffer.alloc(sodium.crypto_kx_PUBLICKEYBYTES) 6 | const sk = Buffer.alloc(sodium.crypto_kx_SECRETKEYBYTES) 7 | const seed = Buffer.alloc(sodium.crypto_kx_SEEDBYTES, 'lo') 8 | 9 | t.exception.all(function () { 10 | sodium.crypto_kx_seed_keypair() 11 | }, 'should validate input') 12 | 13 | t.exception.all(function () { 14 | sodium.crypto_kx_seed_keypair(Buffer.alloc(0), Buffer.alloc(0), Buffer.alloc(0)) 15 | }, 'should validate input length') 16 | 17 | sodium.crypto_kx_seed_keypair(pk, sk, seed) 18 | 19 | const eSk = '768475983073421d5b1676c4aabb24fdf17c3a5f19e6e9e9cdefbfeb45ceb153' 20 | const ePk = '0cd703bbd6b1d46dc431a1fc4f1f7724c64b1d4c471e8c17de4966c9e15bf85e' 21 | 22 | t.alike(pk.toString('hex'), ePk, 'seeded public key') 23 | t.alike(sk.toString('hex'), eSk, 'seeded secret key') 24 | }) 25 | 26 | test('crypto_kx_keypair', function (t) { 27 | const pk = Buffer.alloc(sodium.crypto_kx_PUBLICKEYBYTES) 28 | const sk = Buffer.alloc(sodium.crypto_kx_SECRETKEYBYTES) 29 | 30 | sodium.crypto_kx_keypair(pk, sk) 31 | 32 | t.not(pk, Buffer.alloc(pk.length), 'made public key') 33 | t.not(sk, Buffer.alloc(sk.length), 'made secret key') 34 | 35 | t.exception.all(function () { 36 | sodium.crypto_kx_keypair() 37 | }, 'should validate input') 38 | 39 | t.exception.all(function () { 40 | sodium.crypto_kx_keypair(Buffer.alloc(0), Buffer.alloc(0)) 41 | }, 'should validate input length') 42 | }) 43 | 44 | test('crypto_kx_client_session_keys', function (t) { 45 | const clientPk = Buffer.alloc(sodium.crypto_kx_PUBLICKEYBYTES) 46 | const clientSk = Buffer.alloc(sodium.crypto_kx_SECRETKEYBYTES) 47 | const serverPk = Buffer.alloc(sodium.crypto_kx_PUBLICKEYBYTES) 48 | const serverSk = Buffer.alloc(sodium.crypto_kx_SECRETKEYBYTES) 49 | 50 | const serverRx = Buffer.alloc(sodium.crypto_kx_SESSIONKEYBYTES) 51 | const serverTx = Buffer.alloc(sodium.crypto_kx_SESSIONKEYBYTES) 52 | 53 | const clientRx = Buffer.alloc(sodium.crypto_kx_SESSIONKEYBYTES) 54 | const clientTx = Buffer.alloc(sodium.crypto_kx_SESSIONKEYBYTES) 55 | 56 | sodium.crypto_kx_keypair(serverPk, serverSk) 57 | sodium.crypto_kx_keypair(clientPk, clientSk) 58 | 59 | t.exception.all(function () { 60 | sodium.crypto_kx_client_session_keys() 61 | }, 'should validate') 62 | 63 | t.exception.all(function () { 64 | sodium.crypto_kx_server_session_keys() 65 | }, 'should validate') 66 | 67 | sodium.crypto_kx_client_session_keys(clientRx, clientTx, clientPk, clientSk, serverPk) 68 | sodium.crypto_kx_server_session_keys(serverRx, serverTx, serverPk, serverSk, clientPk) 69 | 70 | t.alike(clientRx, serverTx) 71 | t.alike(clientTx, serverRx) 72 | }) 73 | 74 | test('crypto_kx_client_session_keys one NULL', function (t) { 75 | const clientPk = Buffer.alloc(sodium.crypto_kx_PUBLICKEYBYTES) 76 | const clientSk = Buffer.alloc(sodium.crypto_kx_SECRETKEYBYTES) 77 | const serverPk = Buffer.alloc(sodium.crypto_kx_PUBLICKEYBYTES) 78 | const serverSk = Buffer.alloc(sodium.crypto_kx_SECRETKEYBYTES) 79 | 80 | const serverRx = Buffer.alloc(sodium.crypto_kx_SESSIONKEYBYTES) 81 | const serverTx = Buffer.alloc(sodium.crypto_kx_SESSIONKEYBYTES) 82 | 83 | const clientRx = Buffer.alloc(sodium.crypto_kx_SESSIONKEYBYTES) 84 | const clientTx = Buffer.alloc(sodium.crypto_kx_SESSIONKEYBYTES) 85 | 86 | sodium.crypto_kx_keypair(serverPk, serverSk) 87 | sodium.crypto_kx_keypair(clientPk, clientSk) 88 | 89 | t.exception.all(function () { 90 | sodium.crypto_kx_client_session_keys() 91 | }, 'should validate') 92 | 93 | t.exception.all(function () { 94 | sodium.crypto_kx_server_session_keys() 95 | }, 'should validate') 96 | 97 | t.exception(function () { 98 | sodium.crypto_kx_server_session_keys(null, null, clientPk, clientSk, serverPk) 99 | }, 'should validate') 100 | 101 | t.exception(function () { 102 | sodium.crypto_kx_client_session_keys(null, null, clientPk, clientSk, serverPk) 103 | }, 'should validate') 104 | 105 | sodium.crypto_kx_client_session_keys(clientRx, null, clientPk, clientSk, serverPk) 106 | sodium.crypto_kx_server_session_keys(null, serverTx, serverPk, serverSk, clientPk) 107 | 108 | t.alike(clientRx, serverTx) 109 | 110 | sodium.crypto_kx_client_session_keys(null, clientTx, clientPk, clientSk, serverPk) 111 | sodium.crypto_kx_server_session_keys(serverRx, null, serverPk, serverSk, clientPk) 112 | t.alike(clientTx, serverRx) 113 | }) 114 | 115 | test('crypto_kx constants', function (t) { 116 | t.alike(typeof sodium.crypto_kx_SESSIONKEYBYTES, 'number') 117 | t.alike(typeof sodium.crypto_kx_PUBLICKEYBYTES, 'number') 118 | t.alike(typeof sodium.crypto_kx_SECRETKEYBYTES, 'number') 119 | t.alike(typeof sodium.crypto_kx_SEEDBYTES, 'number') 120 | t.alike(typeof sodium.crypto_kx_PRIMITIVE, 'string') 121 | 122 | t.is(sodium.crypto_kx_SEEDBYTES, 32) 123 | t.is(sodium.crypto_kx_PUBLICKEYBYTES, 32) 124 | t.is(sodium.crypto_kx_SESSIONKEYBYTES, 32) 125 | t.is(sodium.crypto_kx_SECRETKEYBYTES, 32) 126 | 127 | t.end() 128 | }) 129 | 130 | /* eslint-disable camelcase */ 131 | test('libsodium', function (t) { 132 | const small_order_p = new Uint8Array([ 133 | 0xe0, 0xeb, 0x7a, 0x7c, 0x3b, 0x41, 0xb8, 0xae, 0x16, 0x56, 0xe3, 134 | 0xfa, 0xf1, 0x9f, 0xc4, 0x6a, 0xda, 0x09, 0x8d, 0xeb, 0x9c, 0x32, 135 | 0xb1, 0xfd, 0x86, 0x62, 0x05, 0x16, 0x5f, 0x49, 0xb8, 0x00 136 | ]) 137 | 138 | const seed = new Uint8Array(sodium.crypto_kx_SEEDBYTES) 139 | const client_pk = new Uint8Array(sodium.crypto_kx_PUBLICKEYBYTES) 140 | const client_sk = new Uint8Array(sodium.crypto_kx_SECRETKEYBYTES) 141 | const client_rx = new Uint8Array(sodium.crypto_kx_SESSIONKEYBYTES) 142 | const client_tx = new Uint8Array(sodium.crypto_kx_SESSIONKEYBYTES) 143 | const server_pk = new Uint8Array(sodium.crypto_kx_PUBLICKEYBYTES) 144 | const server_sk = new Uint8Array(sodium.crypto_kx_SECRETKEYBYTES) 145 | const server_rx = new Uint8Array(sodium.crypto_kx_SESSIONKEYBYTES) 146 | const server_tx = new Uint8Array(sodium.crypto_kx_SESSIONKEYBYTES) 147 | 148 | for (let i = 0; i < sodium.crypto_kx_SEEDBYTES; i++) { 149 | seed[i] = i 150 | } 151 | 152 | sodium.crypto_kx_seed_keypair(client_pk, client_sk, seed) 153 | 154 | const exp1 = new Uint8Array([ 155 | 0x0e, 0x02, 0x16, 0x22, 0x3f, 0x14, 0x71, 0x43, 0xd3, 0x26, 0x15, 156 | 0xa9, 0x11, 0x89, 0xc2, 0x88, 0xc1, 0x72, 0x8c, 0xba, 0x3c, 0xc5, 157 | 0xf9, 0xf6, 0x21, 0xb1, 0x02, 0x6e, 0x03, 0xd8, 0x31, 0x29 158 | ]) 159 | 160 | const exp2 = new Uint8Array([ 161 | 0xcb, 0x2f, 0x51, 0x60, 0xfc, 0x1f, 0x7e, 0x05, 0xa5, 0x5e, 0xf4, 162 | 0x9d, 0x34, 0x0b, 0x48, 0xda, 0x2e, 0x5a, 0x78, 0x09, 0x9d, 0x53, 163 | 0x39, 0x33, 0x51, 0xcd, 0x57, 0x9d, 0xd4, 0x25, 0x03, 0xd6 164 | ]) 165 | 166 | t.alike(client_pk, exp1, 'client_pk') 167 | t.alike(client_sk, exp2, 'client_pk') 168 | 169 | sodium.crypto_kx_keypair(server_pk, server_sk) 170 | 171 | t.exception(() => { 172 | sodium.crypto_kx_client_session_keys(client_rx, client_tx, client_pk, client_sk, small_order_p) 173 | }) 174 | 175 | t.execution(() => { 176 | sodium.crypto_kx_client_session_keys(client_rx, client_tx, client_pk, client_sk, server_pk) 177 | }) 178 | 179 | t.exception(() => sodium.crypto_kx_server_session_keys(server_rx, server_tx, server_pk, server_sk, small_order_p)) 180 | t.execution(() => { 181 | sodium.crypto_kx_server_session_keys(server_rx, server_tx, server_pk, server_sk, client_pk) 182 | }) 183 | 184 | t.alike(server_rx, client_tx) 185 | t.alike(server_tx, client_rx) 186 | 187 | sodium.sodium_increment(client_pk) 188 | 189 | t.execution(() => { 190 | sodium.crypto_kx_server_session_keys(server_rx, server_tx, server_pk, server_sk, client_pk) 191 | }) 192 | 193 | t.unlike(server_rx, client_tx) 194 | t.unlike(server_tx, client_rx) 195 | 196 | sodium.crypto_kx_keypair(client_pk, client_sk) 197 | 198 | t.execution(() => { 199 | sodium.crypto_kx_server_session_keys(server_rx, server_tx, server_pk, server_sk, client_pk) 200 | }) 201 | 202 | t.unlike(server_rx, client_tx) 203 | t.unlike(server_tx, client_rx) 204 | 205 | sodium.crypto_kx_seed_keypair(client_pk, client_sk, seed) 206 | sodium.sodium_increment(seed) 207 | 208 | sodium.crypto_kx_seed_keypair(server_pk, server_sk, seed) 209 | t.execution(() => { 210 | sodium.crypto_kx_server_session_keys(server_rx, server_tx, server_pk, server_sk, client_pk) 211 | }) 212 | 213 | const exp3 = new Uint8Array([ 214 | 0x62, 0xc8, 0xf4, 0xfa, 0x81, 0x80, 0x0a, 0xbd, 0x05, 0x77, 0xd9, 215 | 0x99, 0x18, 0xd1, 0x29, 0xb6, 0x5d, 0xeb, 0x78, 0x9a, 0xf8, 0xc8, 216 | 0x35, 0x1f, 0x39, 0x1f, 0xeb, 0x0c, 0xbf, 0x23, 0x86, 0x04 217 | ]) 218 | 219 | const exp4 = new Uint8Array([ 220 | 0x74, 0x95, 0x19, 0xc6, 0x80, 0x59, 0xbc, 0xe6, 0x9f, 0x7c, 0xfc, 221 | 0xc7, 0xb3, 0x87, 0xa3, 0xde, 0x1a, 0x1e, 0x82, 0x37, 0xd1, 0x10, 222 | 0x99, 0x13, 0x23, 0xbf, 0x62, 0x87, 0x01, 0x15, 0x73, 0x1a 223 | ]) 224 | 225 | t.alike(server_rx, exp3) 226 | t.alike(server_tx, exp4) 227 | 228 | t.execution(() => { 229 | sodium.crypto_kx_client_session_keys(client_rx, client_tx, client_pk, client_sk, server_pk) 230 | }) 231 | 232 | const exp5 = new Uint8Array([ 233 | 0x74, 0x95, 0x19, 0xc6, 0x80, 0x59, 0xbc, 0xe6, 0x9f, 0x7c, 0xfc, 234 | 0xc7, 0xb3, 0x87, 0xa3, 0xde, 0x1a, 0x1e, 0x82, 0x37, 0xd1, 0x10, 235 | 0x99, 0x13, 0x23, 0xbf, 0x62, 0x87, 0x01, 0x15, 0x73, 0x1a 236 | ]) 237 | 238 | const exp6 = new Uint8Array([ 239 | 0x62, 0xc8, 0xf4, 0xfa, 0x81, 0x80, 0x0a, 0xbd, 0x05, 0x77, 0xd9, 240 | 0x99, 0x18, 0xd1, 0x29, 0xb6, 0x5d, 0xeb, 0x78, 0x9a, 0xf8, 0xc8, 241 | 0x35, 0x1f, 0x39, 0x1f, 0xeb, 0x0c, 0xbf, 0x23, 0x86, 0x04 242 | ]) 243 | 244 | t.alike(client_rx, exp5) 245 | t.alike(client_tx, exp6) 246 | 247 | sodium.randombytes_buf(client_rx) 248 | sodium.randombytes_buf(client_tx) 249 | sodium.randombytes_buf(server_rx) 250 | sodium.randombytes_buf(server_tx) 251 | 252 | t.execution(() => sodium.crypto_kx_client_session_keys(client_rx, null, 253 | client_pk, client_sk, server_pk)) 254 | t.execution(() => sodium.crypto_kx_client_session_keys(null, client_tx, 255 | client_pk, client_sk, server_pk)) 256 | t.execution(() => sodium.crypto_kx_server_session_keys(server_rx, null, 257 | server_pk, server_sk, client_pk)) 258 | t.execution(() => sodium.crypto_kx_server_session_keys(null, server_tx, 259 | server_pk, server_sk, client_pk)) 260 | 261 | t.alike(client_rx, client_tx) 262 | t.alike(client_tx, server_rx) 263 | t.alike(server_rx, server_tx) 264 | }) 265 | -------------------------------------------------------------------------------- /test/crypto_onetimeauth.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | 4 | test('crypto_onetimeauth', function (t) { 5 | const key = Buffer.alloc(sodium.crypto_onetimeauth_KEYBYTES) 6 | const mac = Buffer.alloc(sodium.crypto_onetimeauth_BYTES) 7 | const value = Buffer.from('Hello, World!') 8 | 9 | sodium.randombytes_buf(key) 10 | sodium.crypto_onetimeauth(mac, value, key) 11 | 12 | t.not(mac, Buffer.alloc(mac.length), 'not blank') 13 | t.absent(sodium.crypto_onetimeauth_verify(Buffer.alloc(mac.length), value, key), 'does not verify') 14 | t.ok(sodium.crypto_onetimeauth_verify(mac, value, key), 'verifies') 15 | }) 16 | 17 | test('crypto_onetimeauth_state', function (t) { 18 | const key = Buffer.alloc(sodium.crypto_onetimeauth_KEYBYTES, 'lo') 19 | const state = Buffer.alloc(sodium.crypto_onetimeauth_STATEBYTES) 20 | 21 | t.exception.all(function () { 22 | sodium.crypto_onetimeauth_init(state) 23 | }, 'key required') 24 | 25 | key[0] = 42 26 | 27 | sodium.crypto_onetimeauth_init(state, key) 28 | const value = Buffer.from('Hello, World!') 29 | 30 | for (let i = 0; i < 10; i++) sodium.crypto_onetimeauth_update(state, value) 31 | 32 | const mac = Buffer.alloc(sodium.crypto_onetimeauth_BYTES) 33 | sodium.crypto_onetimeauth_final(state, mac) 34 | 35 | t.alike(mac.toString('hex'), 'ac35df70e6b95051e015de11a6cbf4ab', 'streaming mac') 36 | }) 37 | -------------------------------------------------------------------------------- /test/crypto_pwhash.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | 4 | test('constants', function (t) { 5 | t.ok(sodium.crypto_pwhash_ALG_ARGON2I13 != null, 'crypto_pwhash_ALG_ARGON2I13 is defined') 6 | t.ok(sodium.crypto_pwhash_ALG_ARGON2ID13 != null, 'crypto_pwhash_ALG_ARGON2ID13 is defined') 7 | t.ok(sodium.crypto_pwhash_ALG_DEFAULT === sodium.crypto_pwhash_ALG_ARGON2ID13, 'crypto_pwhash_ALG_DEFAULT is crypto_pwhash_ALG_ARGON2ID13') 8 | t.ok(sodium.crypto_pwhash_BYTES_MIN != null, 'crypto_pwhash_BYTES_MIN is defined') 9 | t.ok(sodium.crypto_pwhash_BYTES_MAX != null, 'crypto_pwhash_BYTES_MAX is defined') 10 | t.ok(sodium.crypto_pwhash_PASSWD_MIN != null, 'crypto_pwhash_PASSWD_MIN is defined') 11 | t.ok(sodium.crypto_pwhash_PASSWD_MAX != null, 'crypto_pwhash_PASSWD_MAX is defined') 12 | t.ok(sodium.crypto_pwhash_SALTBYTES != null, 'crypto_pwhash_SALTBYTES is defined') 13 | t.ok(sodium.crypto_pwhash_STRBYTES != null, 'crypto_pwhash_STRBYTES is defined') 14 | 15 | t.ok(sodium.crypto_pwhash_OPSLIMIT_MIN != null, 'crypto_pwhash_OPSLIMIT_MIN is defined') 16 | t.ok(sodium.crypto_pwhash_OPSLIMIT_MAX != null, 'crypto_pwhash_OPSLIMIT_MAX is defined') 17 | t.ok(sodium.crypto_pwhash_MEMLIMIT_MIN != null, 'crypto_pwhash_MEMLIMIT_MIN is defined') 18 | t.ok(sodium.crypto_pwhash_MEMLIMIT_MAX != null, 'crypto_pwhash_MEMLIMIT_MAX is defined') 19 | t.ok(sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE != null, 'crypto_pwhash_OPSLIMIT_INTERACTIVE is defined') 20 | t.ok(sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE != null, 'crypto_pwhash_MEMLIMIT_INTERACTIVE is defined') 21 | t.ok(sodium.crypto_pwhash_OPSLIMIT_MODERATE != null, 'crypto_pwhash_OPSLIMIT_MODERATE is defined') 22 | t.ok(sodium.crypto_pwhash_MEMLIMIT_MODERATE != null, 'crypto_pwhash_MEMLIMIT_MODERATE is defined') 23 | t.ok(sodium.crypto_pwhash_OPSLIMIT_SENSITIVE != null, 'crypto_pwhash_OPSLIMIT_SENSITIVE is defined') 24 | t.ok(sodium.crypto_pwhash_MEMLIMIT_SENSITIVE != null, 'crypto_pwhash_MEMLIMIT_SENSITIVE is defined') 25 | }) 26 | 27 | test('crypto_pwhash', function (t) { 28 | const output = Buffer.alloc(32) // can be any size 29 | const passwd = Buffer.from('Hej, Verden!') 30 | const salt = Buffer.alloc(sodium.crypto_pwhash_SALTBYTES, 'lo') 31 | const opslimit = sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE 32 | const memlimit = sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE 33 | const algo = sodium.crypto_pwhash_ALG_DEFAULT 34 | 35 | sodium.crypto_pwhash(output, passwd, salt, opslimit, memlimit, algo) 36 | 37 | t.alike(output.toString('hex'), 'f0236e17ec70050fc989f19d8ce640301e8f912154b4f0afc1552cdf246e659f', 'hashes password') 38 | 39 | salt[0] = 0 40 | sodium.crypto_pwhash(output, passwd, salt, opslimit, memlimit, algo) 41 | 42 | t.alike(output.toString('hex'), 'df73f15d217196311d4b1aa6fba339905ffe581dee4bd3a95ec2bb7c52991d65', 'diff salt -> diff hash') 43 | }) 44 | 45 | test('crypto_pwhash_str', function (t) { 46 | const output = Buffer.alloc(sodium.crypto_pwhash_STRBYTES) 47 | const passwd = Buffer.from('Hej, Verden!') 48 | const opslimit = sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE 49 | const memlimit = sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE 50 | 51 | t.exception.all(function () { 52 | sodium.crypto_pwhash_str(output, passwd) 53 | }, 'should throw on missing args') 54 | 55 | sodium.crypto_pwhash_str(output, passwd, opslimit, memlimit) 56 | 57 | t.not(output, Buffer.alloc(output.length), 'not blank') 58 | t.absent(sodium.crypto_pwhash_str_verify(Buffer.alloc(output.length), passwd), 'does not verify') 59 | t.ok(sodium.crypto_pwhash_str_verify(output, passwd), 'verifies') 60 | }) 61 | 62 | test('crypto_pwhash_str_needs_rehash', function (t) { 63 | const passwd = Buffer.from('secret') 64 | const weakMem = Buffer.alloc(sodium.crypto_pwhash_STRBYTES) 65 | const weakOps = Buffer.alloc(sodium.crypto_pwhash_STRBYTES) 66 | const malformed = Buffer.alloc(sodium.crypto_pwhash_STRBYTES) 67 | const good = Buffer.alloc(sodium.crypto_pwhash_STRBYTES) 68 | const weakAlg = Buffer.alloc(sodium.crypto_pwhash_STRBYTES) 69 | weakAlg.set(Buffer.from('$argon2id$v=19$m=8,t=1,p=1$DF4Tce8BK5di0gKeMBb2Fw$uNE4oyvyA0z68RPUom2NXu/KyGvpFppyUoN6pwFBtRU')) 70 | 71 | sodium.crypto_pwhash_str(weakMem, passwd, sodium.crypto_pwhash_OPSLIMIT_MODERATE, sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE) 72 | sodium.crypto_pwhash_str(weakOps, passwd, sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE, sodium.crypto_pwhash_MEMLIMIT_MODERATE) 73 | sodium.crypto_pwhash_str(malformed, passwd, sodium.crypto_pwhash_OPSLIMIT_MODERATE, sodium.crypto_pwhash_MEMLIMIT_MODERATE) 74 | sodium.crypto_pwhash_str(good, passwd, sodium.crypto_pwhash_OPSLIMIT_MODERATE, sodium.crypto_pwhash_MEMLIMIT_MODERATE) 75 | 76 | const first$ = malformed.indexOf('$') 77 | const second$ = malformed.indexOf('$', first$ + 1) 78 | malformed.fill('p=,m=,', first$, second$, 'ascii') 79 | 80 | t.ok(sodium.crypto_pwhash_str_needs_rehash(weakMem, sodium.crypto_pwhash_OPSLIMIT_MODERATE, sodium.crypto_pwhash_MEMLIMIT_MODERATE)) 81 | t.ok(sodium.crypto_pwhash_str_needs_rehash(weakOps, sodium.crypto_pwhash_OPSLIMIT_MODERATE, sodium.crypto_pwhash_MEMLIMIT_MODERATE)) 82 | t.ok(sodium.crypto_pwhash_str_needs_rehash(weakAlg, sodium.crypto_pwhash_OPSLIMIT_MODERATE, sodium.crypto_pwhash_MEMLIMIT_MODERATE)) 83 | t.absent(sodium.crypto_pwhash_str_needs_rehash(good, sodium.crypto_pwhash_OPSLIMIT_MODERATE, sodium.crypto_pwhash_MEMLIMIT_MODERATE)) 84 | t.ok(sodium.crypto_pwhash_str_needs_rehash(malformed, sodium.crypto_pwhash_OPSLIMIT_MODERATE, sodium.crypto_pwhash_MEMLIMIT_MODERATE)) 85 | }) 86 | 87 | test('crypto_pwhash_async', function (t) { 88 | t.plan(4) 89 | 90 | const output = Buffer.alloc(32) // can be any size 91 | const passwd = Buffer.from('Hej, Verden!') 92 | const salt = Buffer.alloc(sodium.crypto_pwhash_SALTBYTES, 'lo') 93 | const opslimit = sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE 94 | const memlimit = sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE 95 | const algo = sodium.crypto_pwhash_ALG_DEFAULT 96 | 97 | sodium.crypto_pwhash_async(output, passwd, salt, opslimit, memlimit, algo, function (err) { 98 | t.absent(err) 99 | t.alike(output.toString('hex'), 'f0236e17ec70050fc989f19d8ce640301e8f912154b4f0afc1552cdf246e659f', 'hashes password') 100 | 101 | salt[0] = 0 102 | 103 | sodium.crypto_pwhash_async(output, passwd, salt, opslimit, memlimit, algo, function (err) { 104 | t.absent(err) 105 | t.alike(output.toString('hex'), 'df73f15d217196311d4b1aa6fba339905ffe581dee4bd3a95ec2bb7c52991d65', 'diff salt -> diff hash') 106 | }) 107 | }) 108 | }) 109 | 110 | test('crypto_pwhash_str_async', function (t) { 111 | t.plan(7) 112 | 113 | const output = Buffer.alloc(sodium.crypto_pwhash_STRBYTES) 114 | const passwd = Buffer.from('Hej, Verden!') 115 | const opslimit = sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE 116 | const memlimit = sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE 117 | 118 | t.exception.all(function () { 119 | return sodium.crypto_pwhash_str_async(output, passwd) 120 | }, 'should throw on missing args') 121 | 122 | sodium.crypto_pwhash_str_async(output, passwd, opslimit, memlimit, function (err) { 123 | t.absent(err) 124 | t.not(output, Buffer.alloc(output.length), 'not blank') 125 | 126 | sodium.crypto_pwhash_str_verify_async(Buffer.alloc(output.length), passwd, function (err, bool) { 127 | t.absent(err) 128 | t.ok(bool === false, 'does not verify') 129 | 130 | sodium.crypto_pwhash_str_verify_async(output, passwd, function (err, bool) { 131 | t.absent(err) 132 | t.ok(bool === true, 'verifies') 133 | }) 134 | }) 135 | }) 136 | }) 137 | 138 | test('crypto_pwhash_async promise', async function (t) { 139 | t.plan(4) 140 | 141 | const output = Buffer.alloc(32) // can be any size 142 | const passwd = Buffer.from('Hej, Verden!') 143 | const salt = Buffer.alloc(sodium.crypto_pwhash_SALTBYTES, 'lo') 144 | const opslimit = sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE 145 | const memlimit = sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE 146 | const algo = sodium.crypto_pwhash_ALG_DEFAULT 147 | 148 | await t.execution(sodium.crypto_pwhash_async(output, passwd, salt, opslimit, memlimit, algo)) 149 | t.alike(output.toString('hex'), 'f0236e17ec70050fc989f19d8ce640301e8f912154b4f0afc1552cdf246e659f', 'hashes password') 150 | 151 | salt[0] = 0 152 | 153 | await t.execution(sodium.crypto_pwhash_async(output, passwd, salt, opslimit, memlimit, algo)) 154 | t.alike(output.toString('hex'), 'df73f15d217196311d4b1aa6fba339905ffe581dee4bd3a95ec2bb7c52991d65', 'diff salt -> diff hash') 155 | }) 156 | 157 | test('crypto_pwhash_str_async promise', async function (t) { 158 | t.plan(7) 159 | 160 | const output = Buffer.alloc(sodium.crypto_pwhash_STRBYTES) 161 | const passwd = Buffer.from('Hej, Verden!') 162 | const opslimit = sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE 163 | const memlimit = sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE 164 | 165 | t.exception.all(function () { 166 | sodium.crypto_pwhash_str_async(output, passwd) 167 | }, 'should throw on missing args') 168 | 169 | await t.execution(sodium.crypto_pwhash_str_async(output, passwd, opslimit, memlimit)) 170 | t.not(output, Buffer.alloc(output.length), 'not blank') 171 | 172 | let p = sodium.crypto_pwhash_str_verify_async(Buffer.alloc(output.length), passwd) 173 | await t.execution(p) 174 | t.ok((await p) === false, 'does not verify') 175 | 176 | p = sodium.crypto_pwhash_str_verify_async(output, passwd) 177 | await t.execution(p) 178 | t.ok((await p) === true, 'verifies') 179 | }) 180 | 181 | test('crypto_pwhash limits', function (t) { 182 | const output = Buffer.alloc(sodium.crypto_pwhash_STRBYTES) 183 | const passwd = Buffer.from('Hej, Verden!') 184 | const opslimit = Number.MAX_SAFE_INTEGER 185 | const memlimit = Number.MAX_SAFE_INTEGER 186 | 187 | t.exception.all(function () { 188 | sodium.crypto_pwhash_str(output, passwd, opslimit, memlimit) 189 | }, 'should throw on large limits') 190 | t.exception.all(function () { 191 | sodium.crypto_pwhash_str(output, passwd, -1, -1) 192 | }, 'should throw on negative limits') 193 | }) 194 | 195 | test('crypto_pwhash_async uncaughtException', function (t) { 196 | t.plan(1) 197 | 198 | const output = Buffer.alloc(32) // can be any size 199 | const passwd = Buffer.from('Hej, Verden!') 200 | const salt = Buffer.alloc(sodium.crypto_pwhash_SALTBYTES, 'lo') 201 | const opslimit = sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE 202 | const memlimit = sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE 203 | const algo = sodium.crypto_pwhash_ALG_DEFAULT 204 | 205 | uncaught(listener) 206 | 207 | sodium.crypto_pwhash_async(output, passwd, salt, opslimit, memlimit, algo, exception) 208 | 209 | function exception () { 210 | throw new Error('caught') 211 | } 212 | 213 | function listener (err) { 214 | if (err.message !== 'caught') { 215 | t.fail() 216 | } else { 217 | t.pass() 218 | } 219 | } 220 | }) 221 | 222 | test('crypto_pwhash_str_async uncaughtException', function (t) { 223 | t.plan(1) 224 | 225 | const output = Buffer.alloc(sodium.crypto_pwhash_STRBYTES) // can be any size 226 | const passwd = Buffer.from('Hej, Verden!') 227 | const opslimit = sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE 228 | const memlimit = sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE 229 | 230 | uncaught(listener) 231 | 232 | sodium.crypto_pwhash_str_async(output, passwd, opslimit, memlimit, exception) 233 | 234 | function exception () { 235 | throw new Error('caught') 236 | } 237 | 238 | function listener (err) { 239 | if (err.message === 'caught') { 240 | t.pass() 241 | } else { 242 | t.fail() 243 | } 244 | } 245 | }) 246 | 247 | test('crypto_pwhash_str_verify_async uncaughtException', function (t) { 248 | t.plan(1) 249 | 250 | const output = Buffer.alloc(sodium.crypto_pwhash_STRBYTES) // can be any size 251 | const passwd = Buffer.from('Hej, Verden!') 252 | 253 | uncaught(listener) 254 | 255 | sodium.crypto_pwhash_str_verify_async(output, passwd, exception) 256 | 257 | function exception () { 258 | throw new Error('caught') 259 | } 260 | 261 | function listener (err) { 262 | if (err.message === 'caught') { 263 | t.pass() 264 | } else { 265 | t.fail() 266 | } 267 | } 268 | }) 269 | 270 | function uncaught (fn) { 271 | if (global.Bare) { 272 | global.Bare.once('uncaughtException', fn) 273 | } else { 274 | process.once('uncaughtException', fn) 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /test/crypto_pwhash_scryptsalsa208sha256.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | 4 | test('constants', function (t) { 5 | t.ok(sodium.crypto_pwhash_scryptsalsa208sha256_BYTES_MIN != null, 'crypto_pwhash_scryptsalsa208sha256_BYTES_MIN is defined') 6 | t.ok(sodium.crypto_pwhash_scryptsalsa208sha256_BYTES_MAX != null, 'crypto_pwhash_scryptsalsa208sha256_BYTES_MAX is defined') 7 | t.ok(sodium.crypto_pwhash_scryptsalsa208sha256_PASSWD_MIN != null, 'crypto_pwhash_scryptsalsa208sha256_PASSWD_MIN is defined') 8 | t.ok(sodium.crypto_pwhash_scryptsalsa208sha256_PASSWD_MAX != null, 'crypto_pwhash_scryptsalsa208sha256_PASSWD_MAX is defined') 9 | t.ok(sodium.crypto_pwhash_scryptsalsa208sha256_SALTBYTES != null, 'crypto_pwhash_scryptsalsa208sha256_SALTBYTES is defined') 10 | t.ok(sodium.crypto_pwhash_scryptsalsa208sha256_STRBYTES != null, 'crypto_pwhash_scryptsalsa208sha256_STRBYTES is defined') 11 | 12 | t.ok(sodium.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN != null, 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MIN is defined') 13 | t.ok(sodium.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX != null, 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_MAX is defined') 14 | t.ok(sodium.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN != null, 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MIN is defined') 15 | t.ok(sodium.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX != null, 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_MAX is defined') 16 | t.ok(sodium.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE != null, 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE is defined') 17 | t.ok(sodium.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE != null, 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE is defined') 18 | t.ok(sodium.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE != null, 'crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE is defined') 19 | t.ok(sodium.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE != null, 'crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE is defined') 20 | }) 21 | 22 | test('crypto_pwhash_scryptsalsa208sha256', function (t) { 23 | const output = Buffer.alloc(32) // can be any size 24 | const passwd = Buffer.from('Hej, Verden!') 25 | const salt = Buffer.alloc(sodium.crypto_pwhash_scryptsalsa208sha256_SALTBYTES, 'lo') 26 | const opslimit = sodium.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE 27 | const memlimit = sodium.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE 28 | 29 | sodium.crypto_pwhash_scryptsalsa208sha256(output, passwd, salt, opslimit, memlimit) 30 | 31 | t.alike(output.toString('hex'), 'c9d280362d495e494672e44a91b94b35bb295f62c823845dd19773ded5877c2b', 'hashes password') 32 | 33 | salt[0] = 0 34 | sodium.crypto_pwhash_scryptsalsa208sha256(output, passwd, salt, opslimit, memlimit) 35 | 36 | t.alike(output.toString('hex'), '3831bd383708c7aff661ab4f990b116c7287bafde9abd02db3174631c97042e6', 'diff salt -> diff hash') 37 | }) 38 | 39 | test('crypto_pwhash_scryptsalsa208sha256_str', function (t) { 40 | const output = Buffer.alloc(sodium.crypto_pwhash_scryptsalsa208sha256_STRBYTES) 41 | const passwd = Buffer.from('Hej, Verden!') 42 | const opslimit = sodium.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE 43 | const memlimit = sodium.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE 44 | 45 | t.exception.all(function () { 46 | sodium.crypto_pwhash_scryptsalsa208sha256_str(output, passwd) 47 | }, 'should throw on missing args') 48 | 49 | sodium.crypto_pwhash_scryptsalsa208sha256_str(output, passwd, opslimit, memlimit) 50 | 51 | t.not(output, Buffer.alloc(output.length), 'not blank') 52 | t.absent(sodium.crypto_pwhash_scryptsalsa208sha256_str_verify(Buffer.alloc(output.length), passwd), 'does not verify') 53 | t.ok(sodium.crypto_pwhash_scryptsalsa208sha256_str_verify(output, passwd), 'verifies') 54 | }) 55 | 56 | test('crypto_pwhash_scryptsalsa208sha256_str_needs_rehash', function (t) { 57 | const passwd = Buffer.from('secret') 58 | const weakMem = Buffer.alloc(sodium.crypto_pwhash_scryptsalsa208sha256_STRBYTES) 59 | const weakOps = Buffer.alloc(sodium.crypto_pwhash_scryptsalsa208sha256_STRBYTES) 60 | const malformed = Buffer.alloc(sodium.crypto_pwhash_scryptsalsa208sha256_STRBYTES) 61 | const good = Buffer.alloc(sodium.crypto_pwhash_scryptsalsa208sha256_STRBYTES) 62 | const weakAlg = Buffer.alloc(sodium.crypto_pwhash_scryptsalsa208sha256_STRBYTES) 63 | weakAlg.set('argon2i$p=2,v=19,m=1024$SGVsbG8=$SGVsbG8gd29ybA==') 64 | 65 | sodium.crypto_pwhash_scryptsalsa208sha256_str(weakMem, passwd, sodium.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE, sodium.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE) 66 | sodium.crypto_pwhash_scryptsalsa208sha256_str(weakOps, passwd, sodium.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE, sodium.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE) 67 | sodium.crypto_pwhash_scryptsalsa208sha256_str(malformed, passwd, sodium.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE, sodium.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE) 68 | sodium.crypto_pwhash_scryptsalsa208sha256_str(good, passwd, sodium.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE, sodium.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE) 69 | 70 | const first$ = malformed.indexOf('$') 71 | const second$ = malformed.indexOf('$', first$ + 1) 72 | malformed.fill('p=,m=,', first$, second$, 'ascii') 73 | 74 | t.ok(sodium.crypto_pwhash_scryptsalsa208sha256_str_needs_rehash(weakMem, sodium.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE, sodium.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE)) 75 | t.ok(sodium.crypto_pwhash_scryptsalsa208sha256_str_needs_rehash(weakOps, sodium.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE, sodium.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE)) 76 | t.ok(sodium.crypto_pwhash_scryptsalsa208sha256_str_needs_rehash(weakAlg, sodium.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE, sodium.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE)) 77 | t.absent(sodium.crypto_pwhash_scryptsalsa208sha256_str_needs_rehash(good, sodium.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE, sodium.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE)) 78 | t.ok(sodium.crypto_pwhash_scryptsalsa208sha256_str_needs_rehash(malformed, sodium.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE, sodium.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE)) 79 | }) 80 | 81 | test('crypto_pwhash_scryptsalsa208sha256_async', function (t) { 82 | t.plan(4) 83 | 84 | const output = Buffer.alloc(32) // can be any size 85 | const passwd = Buffer.from('Hej, Verden!') 86 | const salt = Buffer.alloc(sodium.crypto_pwhash_scryptsalsa208sha256_SALTBYTES, 'lo') 87 | const opslimit = sodium.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE 88 | const memlimit = sodium.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE 89 | 90 | sodium.crypto_pwhash_scryptsalsa208sha256_async(output, passwd, salt, opslimit, memlimit, function (err) { 91 | t.absent(err) 92 | t.alike(output.toString('hex'), 'c9d280362d495e494672e44a91b94b35bb295f62c823845dd19773ded5877c2b', 'hashes password') 93 | 94 | salt[0] = 0 95 | sodium.crypto_pwhash_scryptsalsa208sha256_async(output, passwd, salt, opslimit, memlimit, function (err) { 96 | t.absent(err) 97 | t.alike(output.toString('hex'), '3831bd383708c7aff661ab4f990b116c7287bafde9abd02db3174631c97042e6', 'diff salt -> diff hash') 98 | }) 99 | }) 100 | }) 101 | 102 | test('crypto_pwhash_scryptsalsa208sha256_str_async', function (t) { 103 | t.plan(7) 104 | 105 | const output = Buffer.alloc(sodium.crypto_pwhash_scryptsalsa208sha256_STRBYTES) 106 | const passwd = Buffer.from('Hej, Verden!') 107 | const opslimit = sodium.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE 108 | const memlimit = sodium.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE 109 | 110 | t.exception.all(function () { 111 | sodium.crypto_pwhash_scryptsalsa208sha256_str_async(output, passwd) 112 | }, 'should throw on missing args') 113 | 114 | sodium.crypto_pwhash_scryptsalsa208sha256_str_async(output, passwd, opslimit, memlimit, function (err) { 115 | t.absent(err) 116 | t.not(output, Buffer.alloc(output.length), 'not blank') 117 | 118 | sodium.crypto_pwhash_scryptsalsa208sha256_str_verify_async(Buffer.alloc(output.length), passwd, function (err, bool) { 119 | t.absent(err) 120 | t.ok(bool === false, 'does not verify') 121 | 122 | sodium.crypto_pwhash_scryptsalsa208sha256_str_verify_async(output, passwd, function (err, bool) { 123 | t.absent(err) 124 | t.ok(bool === true, 'verifies') 125 | }) 126 | }) 127 | }) 128 | }) 129 | 130 | test('crypto_pwhash_scryptsalsa208sha256 limits', function (t) { 131 | const output = Buffer.alloc(sodium.crypto_pwhash_scryptsalsa208sha256_STRBYTES) 132 | const passwd = Buffer.from('Hej, Verden!') 133 | const opslimit = Number.MAX_SAFE_INTEGER 134 | const memlimit = Number.MAX_SAFE_INTEGER 135 | 136 | t.exception.all(function () { 137 | sodium.crypto_pwhash_scryptsalsa208sha256_str(output, passwd, opslimit, memlimit) 138 | }, 'should throw on large limits') 139 | t.exception.all(function () { 140 | sodium.crypto_pwhash_scryptsalsa208sha256_str(output, passwd, -1, -1) 141 | }, 'should throw on negative limits') 142 | }) 143 | 144 | test('crypto_pwhash_scryptsalsa208sha256_async uncaughtException', function (t) { 145 | t.plan(1) 146 | 147 | const output = Buffer.alloc(32) // can be any size 148 | const passwd = Buffer.from('Hej, Verden!') 149 | const salt = Buffer.alloc(sodium.crypto_pwhash_scryptsalsa208sha256_SALTBYTES, 'lo') 150 | const opslimit = sodium.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE 151 | const memlimit = sodium.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE 152 | 153 | uncaught(listener) 154 | 155 | sodium.crypto_pwhash_scryptsalsa208sha256_async(output, passwd, salt, opslimit, memlimit, exception) 156 | 157 | function exception () { 158 | throw new Error('caught') 159 | } 160 | 161 | function listener (err) { 162 | if (err.message !== 'caught') { 163 | t.fail() 164 | } else { 165 | t.pass() 166 | } 167 | } 168 | }) 169 | 170 | test('crypto_pwhash_scryptsalsa208sha256_str_async uncaughtException', function (t) { 171 | t.plan(1) 172 | 173 | const output = Buffer.alloc(sodium.crypto_pwhash_scryptsalsa208sha256_STRBYTES) // can be any size 174 | const passwd = Buffer.from('Hej, Verden!') 175 | const opslimit = sodium.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE 176 | const memlimit = sodium.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE 177 | 178 | uncaught(listener) 179 | 180 | sodium.crypto_pwhash_scryptsalsa208sha256_str_async(output, passwd, opslimit, memlimit, exception) 181 | 182 | function exception () { 183 | throw new Error('caught') 184 | } 185 | 186 | function listener (err) { 187 | if (err.message === 'caught') { 188 | t.pass() 189 | } else { 190 | t.fail() 191 | } 192 | } 193 | }) 194 | 195 | test('crypto_pwhash_scryptsalsa208sha256_str_verify_async uncaughtException', function (t) { 196 | t.plan(1) 197 | 198 | const output = Buffer.alloc(sodium.crypto_pwhash_scryptsalsa208sha256_STRBYTES) // can be any size 199 | const passwd = Buffer.from('Hej, Verden!') 200 | 201 | uncaught(listener) 202 | 203 | sodium.crypto_pwhash_scryptsalsa208sha256_str_verify_async(output, passwd, exception) 204 | 205 | function exception () { 206 | throw new Error('caught') 207 | } 208 | 209 | function listener (err) { 210 | if (err.message === 'caught') { 211 | t.pass() 212 | } else { 213 | t.fail() 214 | } 215 | } 216 | }) 217 | 218 | test('crypto_pwhash_scryptsalsa208sha256_async promise', async function (t) { 219 | t.plan(4) 220 | 221 | const output = Buffer.alloc(32) // can be any size 222 | const passwd = Buffer.from('Hej, Verden!') 223 | const salt = Buffer.alloc(sodium.crypto_pwhash_scryptsalsa208sha256_SALTBYTES, 'lo') 224 | const opslimit = sodium.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE 225 | const memlimit = sodium.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE 226 | 227 | await t.execution(sodium.crypto_pwhash_scryptsalsa208sha256_async(output, passwd, salt, opslimit, memlimit)) 228 | t.alike(output.toString('hex'), 'c9d280362d495e494672e44a91b94b35bb295f62c823845dd19773ded5877c2b', 'hashes password') 229 | 230 | salt[0] = 0 231 | 232 | await t.execution(sodium.crypto_pwhash_scryptsalsa208sha256_async(output, passwd, salt, opslimit, memlimit)) 233 | t.alike(output.toString('hex'), '3831bd383708c7aff661ab4f990b116c7287bafde9abd02db3174631c97042e6', 'diff salt -> diff hash') 234 | }) 235 | 236 | test('crypto_pwhash_scryptsalsa208sha256_str_async promise', async function (t) { 237 | t.plan(6) 238 | 239 | const output = Buffer.alloc(sodium.crypto_pwhash_scryptsalsa208sha256_STRBYTES) 240 | const passwd = Buffer.from('Hej, Verden!') 241 | const opslimit = sodium.crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE 242 | const memlimit = sodium.crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE 243 | 244 | await t.exception.all(() => sodium.crypto_pwhash_scryptsalsa208sha256_str_async(output, passwd), 245 | 'should throw on missing args') 246 | 247 | await sodium.crypto_pwhash_scryptsalsa208sha256_str_async(output, passwd, opslimit, memlimit) 248 | t.not(output, Buffer.alloc(output.length), 'not blank') 249 | 250 | let p = await sodium.crypto_pwhash_scryptsalsa208sha256_str_verify_async(Buffer.alloc(output.length), passwd) 251 | await t.execution(p) 252 | t.ok(p === false, 'does not verify') 253 | 254 | p = await sodium.crypto_pwhash_scryptsalsa208sha256_str_verify_async(output, passwd) 255 | await t.execution(p) 256 | t.ok(p === true, 'verifies') 257 | }) 258 | 259 | function uncaught (fn) { 260 | if (global.Bare) { 261 | global.Bare.once('uncaughtException', fn) 262 | } else { 263 | process.once('uncaughtException', fn) 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /test/crypto_scalarmult.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | 4 | test('crypto_scalarmult_base', function (t) { 5 | const keys = keyPair() 6 | 7 | t.not(keys.secretKey, Buffer.alloc(keys.secretKey.length), 'secret key not blank') 8 | t.not(keys.publicKey, Buffer.alloc(keys.publicKey.length), 'public key not blank') 9 | }) 10 | 11 | test('crypto_scalarmult', function (t) { 12 | const peer1 = keyPair() 13 | const peer2 = keyPair() 14 | 15 | t.not(peer1.secretKey, peer2.secretKey, 'diff secret keys') 16 | t.not(peer1.publicKey, peer2.publicKey, 'diff public keys') 17 | 18 | const shared1 = Buffer.alloc(sodium.crypto_scalarmult_BYTES) 19 | const shared2 = Buffer.alloc(sodium.crypto_scalarmult_BYTES) 20 | 21 | sodium.crypto_scalarmult(shared1, peer1.secretKey, peer2.publicKey) 22 | sodium.crypto_scalarmult(shared2, peer2.secretKey, peer1.publicKey) 23 | 24 | t.alike(shared1, shared2, 'same shared secret') 25 | }) 26 | 27 | function keyPair () { 28 | const secretKey = Buffer.alloc(sodium.crypto_scalarmult_SCALARBYTES) 29 | sodium.randombytes_buf(secretKey) 30 | 31 | const publicKey = Buffer.alloc(sodium.crypto_scalarmult_BYTES) 32 | sodium.crypto_scalarmult_base(publicKey, secretKey) 33 | 34 | return { 35 | publicKey, 36 | secretKey 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test/crypto_secretbox.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | 4 | test('crypto_secretbox_easy', function (t) { 5 | const message = Buffer.from('Hej, Verden!') 6 | const output = Buffer.alloc(message.length + sodium.crypto_secretbox_MACBYTES) 7 | 8 | const key = Buffer.alloc(sodium.crypto_secretbox_KEYBYTES) 9 | sodium.randombytes_buf(key) 10 | 11 | const nonce = Buffer.alloc(sodium.crypto_secretbox_NONCEBYTES) 12 | sodium.randombytes_buf(nonce) 13 | 14 | t.exception.all(function () { 15 | sodium.crypto_secretbox_easy(Buffer.alloc(0), message, nonce, key) 16 | }, 'throws if output is too small') 17 | 18 | t.exception.all(function () { 19 | sodium.crypto_secretbox_easy(Buffer.alloc(message.length), message, nonce, key) 20 | }, 'throws if output is too small') 21 | 22 | sodium.crypto_secretbox_easy(output, message, nonce, key) 23 | t.not(output, Buffer.alloc(output.length)) 24 | 25 | const result = Buffer.alloc(output.length - sodium.crypto_secretbox_MACBYTES) 26 | t.absent(sodium.crypto_secretbox_open_easy(result, output, Buffer.alloc(sodium.crypto_secretbox_NONCEBYTES), key), 'could not decrypt') 27 | t.ok(sodium.crypto_secretbox_open_easy(result, output, nonce, key), 'could decrypt') 28 | 29 | t.alike(result, message, 'decrypted message is correct') 30 | }) 31 | 32 | test('crypto_secretbox_easy overwrite buffer', function (t) { 33 | const output = Buffer.alloc(Buffer.byteLength('Hej, Verden!') + sodium.crypto_secretbox_MACBYTES) 34 | output.write('Hej, Verden!', sodium.crypto_secretbox_MACBYTES) 35 | 36 | const key = Buffer.alloc(sodium.crypto_secretbox_KEYBYTES) 37 | sodium.randombytes_buf(key) 38 | 39 | const nonce = Buffer.alloc(sodium.crypto_secretbox_NONCEBYTES) 40 | sodium.randombytes_buf(nonce) 41 | 42 | sodium.crypto_secretbox_easy(output, output.subarray(sodium.crypto_secretbox_MACBYTES), nonce, key) 43 | t.not(output, Buffer.alloc(output.length)) 44 | 45 | t.ok(sodium.crypto_secretbox_open_easy(output.subarray(sodium.crypto_secretbox_MACBYTES), output, nonce, key), 'could decrypt') 46 | t.alike(output.subarray(sodium.crypto_secretbox_MACBYTES), Buffer.from('Hej, Verden!'), 'decrypted message is correct') 47 | }) 48 | 49 | test('crypto_secretbox_detached', function (t) { 50 | const message = Buffer.from('Hej, Verden!') 51 | const output = Buffer.alloc(message.length) 52 | const mac = Buffer.alloc(sodium.crypto_secretbox_MACBYTES) 53 | 54 | const key = Buffer.alloc(sodium.crypto_secretbox_KEYBYTES) 55 | sodium.randombytes_buf(key) 56 | 57 | const nonce = Buffer.alloc(sodium.crypto_secretbox_NONCEBYTES) 58 | sodium.randombytes_buf(nonce) 59 | 60 | sodium.crypto_secretbox_detached(output, mac, message, nonce, key) 61 | 62 | t.not(mac, Buffer.alloc(mac.length), 'mac not blank') 63 | t.not(output, Buffer.alloc(output.length), 'output not blank') 64 | 65 | const result = Buffer.alloc(output.length) 66 | 67 | t.absent(sodium.crypto_secretbox_open_detached(result, output, mac, nonce, Buffer.alloc(key.length)), 'could not decrypt') 68 | t.ok(sodium.crypto_secretbox_open_detached(result, output, mac, nonce, key), 'could decrypt') 69 | 70 | t.alike(result, message, 'decrypted message is correct') 71 | }) 72 | -------------------------------------------------------------------------------- /test/crypto_secretstream.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | 4 | test('constants', function (t) { 5 | t.alike(typeof sodium.crypto_secretstream_xchacha20poly1305_ABYTES, 'number', 'crypto_secretstream_xchacha20poly1305_ABYTES is number') 6 | t.alike(typeof sodium.crypto_secretstream_xchacha20poly1305_HEADERBYTES, 'number', 'crypto_secretstream_xchacha20poly1305_HEADERBYTES is number') 7 | t.alike(typeof sodium.crypto_secretstream_xchacha20poly1305_KEYBYTES, 'number', 'crypto_secretstream_xchacha20poly1305_KEYBYTES is number') 8 | t.alike(typeof sodium.crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX, 'number', 'crypto_secretstream_xchacha20poly1305_MESSAGEBYTES_MAX is number') 9 | 10 | t.ok(typeof sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE === 'number', 'crypto_secretstream_xchacha20poly1305_TAG_MESSAGE is Buffer') 11 | t.ok(typeof sodium.crypto_secretstream_xchacha20poly1305_TAG_PUSH === 'number', 'crypto_secretstream_xchacha20poly1305_TAG_PUSH is Buffer') 12 | t.ok(typeof sodium.crypto_secretstream_xchacha20poly1305_TAG_REKEY === 'number', 'crypto_secretstream_xchacha20poly1305_TAG_REKEY is Buffer') 13 | t.ok(typeof sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL === 'number', 'crypto_secretstream_xchacha20poly1305_TAG_FINAL is Buffer') 14 | }) 15 | 16 | test('crypto_secretstream', function (t) { 17 | const state = Buffer.alloc(sodium.crypto_secretstream_xchacha20poly1305_STATEBYTES) 18 | 19 | const header = Buffer.alloc(sodium.crypto_secretstream_xchacha20poly1305_HEADERBYTES) 20 | const ad = Buffer.alloc(sodium.randombytes_uniform(100)) 21 | sodium.randombytes_buf(ad) 22 | 23 | const m1 = Buffer.alloc(sodium.randombytes_uniform(1000)) 24 | sodium.randombytes_buf(m1) 25 | 26 | const m2 = Buffer.alloc(sodium.randombytes_uniform(1000)) 27 | sodium.randombytes_buf(m2) 28 | 29 | const m3 = Buffer.alloc(sodium.randombytes_uniform(1000)) 30 | sodium.randombytes_buf(m3) 31 | 32 | const m4 = Buffer.alloc(sodium.randombytes_uniform(1000)) 33 | sodium.randombytes_buf(m4) 34 | 35 | const m1_ = Buffer.from(m1) 36 | const m2_ = Buffer.from(m2) 37 | const m3_ = Buffer.from(m3) 38 | const m4_ = Buffer.from(m4) 39 | 40 | const c1 = Buffer.alloc(m1.length + sodium.crypto_secretstream_xchacha20poly1305_ABYTES) 41 | const c2 = Buffer.alloc(m2.length + sodium.crypto_secretstream_xchacha20poly1305_ABYTES) 42 | const c3 = Buffer.alloc(m3.length + sodium.crypto_secretstream_xchacha20poly1305_ABYTES) 43 | const c4 = Buffer.alloc(m4.length + sodium.crypto_secretstream_xchacha20poly1305_ABYTES) 44 | 45 | const key = Buffer.alloc(sodium.crypto_secretstream_xchacha20poly1305_KEYBYTES) 46 | let ret 47 | const tag = Buffer.alloc(sodium.crypto_secretstream_xchacha20poly1305_TAGBYTES, 0xdb) 48 | 49 | sodium.crypto_secretstream_xchacha20poly1305_keygen(key) 50 | 51 | sodium.crypto_secretstream_xchacha20poly1305_init_push(state, header, key) 52 | t.unlike(header.toString('hex'), '000000000000000000000000000000000000000000000000') 53 | ret = sodium.crypto_secretstream_xchacha20poly1305_push(state, c1, m1, null, sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE) 54 | t.alike(ret, m1.length + sodium.crypto_secretstream_xchacha20poly1305_ABYTES) 55 | ret = sodium.crypto_secretstream_xchacha20poly1305_push(state, c2, m2, ad.subarray(0, 0), sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE) 56 | t.alike(ret, m2.length + sodium.crypto_secretstream_xchacha20poly1305_ABYTES) 57 | ret = sodium.crypto_secretstream_xchacha20poly1305_push(state, c3, m3, ad, sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE) 58 | t.alike(ret, m3.length + sodium.crypto_secretstream_xchacha20poly1305_ABYTES) 59 | ret = sodium.crypto_secretstream_xchacha20poly1305_push(state, c4, m4, null, sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL) 60 | t.alike(ret, m4.length + sodium.crypto_secretstream_xchacha20poly1305_ABYTES) 61 | 62 | sodium.crypto_secretstream_xchacha20poly1305_init_pull(state, header, key) 63 | m1.fill(0) 64 | tag.fill(0xdb) 65 | ret = sodium.crypto_secretstream_xchacha20poly1305_pull(state, m1, tag, c1, null) 66 | t.alike(ret, c1.length - sodium.crypto_secretstream_xchacha20poly1305_ABYTES) 67 | t.alike(tag[0], sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE) 68 | t.ok(m1.equals(m1_)) 69 | 70 | m2.fill(0) 71 | tag.fill(0xdb) 72 | ret = sodium.crypto_secretstream_xchacha20poly1305_pull(state, m2, tag, c2, null) 73 | t.alike(ret, c2.length - sodium.crypto_secretstream_xchacha20poly1305_ABYTES) 74 | t.alike(tag[0], sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE) 75 | t.ok(m2.equals(m2_)) 76 | 77 | if (ad.length > 0) { 78 | t.exception.all(function () { 79 | sodium.crypto_secretstream_xchacha20poly1305_pull(state, m3, tag, c3, null) 80 | }) 81 | } 82 | 83 | m3.fill(0) 84 | tag.fill(0xdb) 85 | ret = sodium.crypto_secretstream_xchacha20poly1305_pull(state, m3, tag, c3, ad) 86 | t.alike(ret, c3.length - sodium.crypto_secretstream_xchacha20poly1305_ABYTES) 87 | t.alike(tag[0], sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE) 88 | t.ok(m3.equals(m3_)) 89 | 90 | m4.fill(0) 91 | tag.fill(0xdb) 92 | ret = sodium.crypto_secretstream_xchacha20poly1305_pull(state, m4, tag, c4, null) 93 | t.alike(ret, c4.length - sodium.crypto_secretstream_xchacha20poly1305_ABYTES) 94 | t.alike(tag[0], sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL) 95 | t.ok(m4.equals(m4_)) 96 | 97 | t.exception.all(function () { 98 | sodium.crypto_secretstream_xchacha20poly1305_pull(state, m4, tag, c4, null) 99 | }, 'previous with FINAL tag') 100 | 101 | t.exception.all(function () { 102 | sodium.crypto_secretstream_xchacha20poly1305_pull(state, m2, tag, c2, null) 103 | }, 'previous with without tag') 104 | 105 | t.exception.all(function () { 106 | sodium.crypto_secretstream_xchacha20poly1305_pull(state, m2, tag, c2.subarray(0, Math.random() * sodium.crypto_secretstream_xchacha20poly1305_ABYTES | 0), null) // fixme 107 | }, 'short ciphertext') 108 | 109 | t.exception.all(function () { 110 | sodium.crypto_secretstream_xchacha20poly1305_pull(state, m2, tag, c2.subarray(0, sodium.crypto_secretstream_xchacha20poly1305_ABYTES), null) 111 | }, 'empty ciphertext') 112 | 113 | /* without explicit rekeying */ 114 | 115 | sodium.crypto_secretstream_xchacha20poly1305_init_push(state, header, key) 116 | sodium.crypto_secretstream_xchacha20poly1305_push(state, c1, m1, null, sodium.crypto_secretstream_xchacha20poly1305_TAG_REKEY) 117 | sodium.crypto_secretstream_xchacha20poly1305_push(state, c2, m2, null, sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE) 118 | 119 | sodium.crypto_secretstream_xchacha20poly1305_init_pull(state, header, key) 120 | tag.fill(0xdb) 121 | sodium.crypto_secretstream_xchacha20poly1305_pull(state, m1, tag, c1, null) 122 | t.alike(tag[0], sodium.crypto_secretstream_xchacha20poly1305_TAG_REKEY) 123 | tag.fill(0xdb) 124 | sodium.crypto_secretstream_xchacha20poly1305_pull(state, m2, tag, c2, null) 125 | t.alike(tag[0], sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE) 126 | 127 | /* with explicit rekeying */ 128 | 129 | sodium.crypto_secretstream_xchacha20poly1305_init_push(state, header, key) 130 | sodium.crypto_secretstream_xchacha20poly1305_push(state, c1, m1, null, sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE) 131 | sodium.crypto_secretstream_xchacha20poly1305_rekey(state) 132 | sodium.crypto_secretstream_xchacha20poly1305_push(state, c2, m2, null, sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE) 133 | 134 | sodium.crypto_secretstream_xchacha20poly1305_init_pull(state, header, key) 135 | tag.fill(0xdb) 136 | sodium.crypto_secretstream_xchacha20poly1305_pull(state, m1, tag, c1, null) 137 | t.alike(tag[0], sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE) 138 | 139 | t.exception.all(function () { 140 | sodium.crypto_secretstream_xchacha20poly1305_pull(state, m2, tag, c2, null) 141 | }) 142 | 143 | sodium.crypto_secretstream_xchacha20poly1305_rekey(state) 144 | tag.fill(0xdb) 145 | sodium.crypto_secretstream_xchacha20poly1305_pull(state, m2, tag, c2, null) 146 | t.alike(tag[0], sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE) 147 | }) 148 | -------------------------------------------------------------------------------- /test/crypto_sign.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | const fixtures = require('./fixtures/crypto_sign.json') 4 | 5 | test('crypto_sign_ed25519_sk_to_pk', function (t) { 6 | const pk = Buffer.alloc(sodium.crypto_sign_PUBLICKEYBYTES) 7 | const pke = Buffer.alloc(sodium.crypto_sign_PUBLICKEYBYTES) 8 | const sk = Buffer.alloc(sodium.crypto_sign_SECRETKEYBYTES) 9 | 10 | sodium.crypto_sign_keypair(pk, sk) 11 | sodium.crypto_sign_ed25519_sk_to_pk(pke, sk) 12 | 13 | t.ok(pk.equals(pke)) 14 | }) 15 | 16 | test('crypto_sign_seed_keypair', function (t) { 17 | const pk = Buffer.alloc(sodium.crypto_sign_PUBLICKEYBYTES) 18 | const sk = Buffer.alloc(sodium.crypto_sign_SECRETKEYBYTES) 19 | const seed = Buffer.alloc(sodium.crypto_sign_SEEDBYTES, 'lo') 20 | 21 | t.exception.all(function () { 22 | sodium.crypto_sign_seed_keypair() 23 | }, 'should validate input') 24 | 25 | t.exception.all(function () { 26 | sodium.crypto_sign_seed_keypair(Buffer.alloc(0), Buffer.alloc(0), Buffer.alloc(0)) 27 | }, 'should validate input length') 28 | 29 | sodium.crypto_sign_seed_keypair(pk, sk, seed) 30 | 31 | const eSk = '6c6f6c6f6c6f6c6f6c6f6c6f6c6f6c6f6c6f6c6f6c6f6c6f6c6f6c6f6c6f6c6f41eb5b4dba29b19e391d9a4d1a4a879b27958ff3734e10cfbf1f46d68f4d3038' 32 | const ePk = '41eb5b4dba29b19e391d9a4d1a4a879b27958ff3734e10cfbf1f46d68f4d3038' 33 | 34 | t.alike(pk.toString('hex'), ePk, 'seeded public key') 35 | t.alike(sk.toString('hex'), eSk, 'seeded secret key') 36 | }) 37 | 38 | test('crypto_sign_keypair', function (t) { 39 | const pk = Buffer.alloc(sodium.crypto_sign_PUBLICKEYBYTES) 40 | const sk = Buffer.alloc(sodium.crypto_sign_SECRETKEYBYTES) 41 | 42 | sodium.crypto_sign_keypair(pk, sk) 43 | 44 | t.not(pk, Buffer.alloc(pk.length), 'made public key') 45 | t.not(sk, Buffer.alloc(sk.length), 'made secret key') 46 | 47 | t.exception.all(function () { 48 | sodium.crypto_sign_keypair() 49 | }, 'should validate input') 50 | 51 | t.exception.all(function () { 52 | sodium.crypto_sign_keypair(Buffer.alloc(0), Buffer.alloc(0)) 53 | }, 'should validate input length') 54 | }) 55 | 56 | test('crypto_sign', function (t) { 57 | const pk = Buffer.alloc(sodium.crypto_sign_PUBLICKEYBYTES) 58 | const sk = Buffer.alloc(sodium.crypto_sign_SECRETKEYBYTES) 59 | 60 | sodium.crypto_sign_keypair(pk, sk) 61 | 62 | const message = Buffer.from('Hello, World!') 63 | const signedMessage = Buffer.alloc(message.length + sodium.crypto_sign_BYTES) 64 | 65 | sodium.crypto_sign(signedMessage, message, sk) 66 | 67 | t.alike(signedMessage.subarray(-message.length), message, 'contains message') 68 | 69 | const output = Buffer.alloc(message.length) 70 | 71 | t.absent(sodium.crypto_sign_open(output, Buffer.alloc(signedMessage.length), pk), 'was not signed') 72 | t.ok(sodium.crypto_sign_open(output, signedMessage, pk), 'was signed') 73 | 74 | t.alike(output, message, 'same message') 75 | }) 76 | 77 | test('crypto_sign_detached', function (t) { 78 | const pk = Buffer.alloc(sodium.crypto_sign_PUBLICKEYBYTES) 79 | const sk = Buffer.alloc(sodium.crypto_sign_SECRETKEYBYTES) 80 | 81 | sodium.crypto_sign_keypair(pk, sk) 82 | 83 | const message = Buffer.from('Hello, World!') 84 | const signature = Buffer.alloc(sodium.crypto_sign_BYTES) 85 | 86 | sodium.crypto_sign_detached(signature, message, sk) 87 | 88 | t.absent(sodium.crypto_sign_verify_detached(Buffer.concat([Buffer.alloc(1), signature]), message, pk), 'was not signed') 89 | t.ok(sodium.crypto_sign_verify_detached(signature, message, pk), 'was signed') 90 | }) 91 | 92 | test('crypto_sign_open fixtures', function (t) { 93 | for (let i = 0; i < fixtures.length; i++) { 94 | const publicKey = new Uint8Array(fixtures[i][1]) 95 | const message = new Uint8Array(fixtures[i][3]) 96 | const signed = new Uint8Array([].concat(fixtures[i][2], fixtures[i][3])) 97 | 98 | if (!sodium.crypto_sign_open(message, signed, publicKey)) { 99 | t.fail('Failed on fixture #' + i) 100 | t.end() 101 | return 102 | } 103 | } 104 | 105 | t.pass('Passed all fixtures') 106 | t.end() 107 | }) 108 | 109 | test('crypto_sign fixtures', function (t) { 110 | const fixtures = require('./fixtures/crypto_sign.json') 111 | 112 | for (let i = 0; i < fixtures.length; i++) { 113 | const secretKey = new Uint8Array([].concat(fixtures[i][0], fixtures[i][1])) 114 | const message = new Uint8Array(fixtures[i][3]) 115 | 116 | const expected = new Uint8Array([].concat(fixtures[i][2], fixtures[i][3])) 117 | const actual = new Uint8Array(sodium.crypto_sign_BYTES + message.length) 118 | 119 | sodium.crypto_sign(actual, message, secretKey) 120 | 121 | if (Buffer.compare(actual, expected) !== 0) { 122 | t.fail('Failed on fixture #' + i) 123 | t.end() 124 | return 125 | } 126 | } 127 | 128 | t.pass('Passed all fixtures') 129 | t.end() 130 | }) 131 | 132 | test('crypto_sign_verify_detached fixtures', function (t) { 133 | const fixtures = require('./fixtures/crypto_sign.json') 134 | 135 | for (let i = 0; i < fixtures.length; i++) { 136 | const publicKey = new Uint8Array(fixtures[i][1]) 137 | const message = new Uint8Array(fixtures[i][3]) 138 | const signature = new Uint8Array(fixtures[i][2]) 139 | 140 | if (!sodium.crypto_sign_verify_detached(signature, message, publicKey)) { 141 | t.fail('Failed on fixture #' + i) 142 | t.end() 143 | return 144 | } 145 | } 146 | 147 | t.pass('Passed all fixtures') 148 | t.end() 149 | }) 150 | 151 | test('crypto_sign_detached fixtures', function (t) { 152 | const fixtures = require('./fixtures/crypto_sign.json') 153 | 154 | for (let i = 0; i < fixtures.length; i++) { 155 | const secretKey = new Uint8Array([].concat(fixtures[i][0], fixtures[i][1])) 156 | const message = new Uint8Array(fixtures[i][3]) 157 | 158 | const expected = new Uint8Array(fixtures[i][2]) 159 | const actual = new Uint8Array(sodium.crypto_sign_BYTES) 160 | 161 | sodium.crypto_sign_detached(actual, message, secretKey) 162 | 163 | if (Buffer.compare(actual, expected) !== 0) { 164 | t.fail('Failed on fixture #' + i) 165 | t.end() 166 | return 167 | } 168 | } 169 | 170 | t.pass('Passed all fixtures') 171 | t.end() 172 | }) 173 | 174 | /* eslint-disable camelcase */ 175 | test('libsodium', function (t) { 176 | const sig = new Uint8Array(sodium.crypto_sign_BYTES) 177 | const sm = new Uint8Array(1024 + sodium.crypto_sign_BYTES) 178 | const skpk = new Uint8Array(sodium.crypto_sign_SECRETKEYBYTES) 179 | const pk = new Uint8Array(sodium.crypto_sign_PUBLICKEYBYTES) 180 | const sk = new Uint8Array(sodium.crypto_sign_SECRETKEYBYTES) 181 | 182 | let smlen 183 | let i 184 | let test 185 | 186 | sig.fill(0) 187 | 188 | let pass = true 189 | for (i = 0; i < fixtures.length; i++) { 190 | test = parseTest(fixtures[i]) 191 | 192 | skpk.set(test.sk) 193 | skpk.set(test.pk, sodium.crypto_sign_SEEDBYTES) 194 | 195 | smlen = sodium.crypto_sign_BYTES + test.m.byteLength 196 | 197 | sodium.crypto_sign(sm.subarray(0, test.m.byteLength + sodium.crypto_sign_BYTES), test.m, skpk) 198 | pass &= Buffer.compare(test.sig, sm.subarray(0, 64)) === 0 199 | pass &= sodium.crypto_sign_open(test.m, sm.subarray(0, smlen), test.pk) 200 | 201 | sodium.crypto_sign_detached(sig, test.m, skpk) 202 | 203 | pass &= sig.byteLength !== 0 && sig.byteLength <= sodium.crypto_sign_BYTES 204 | pass &= Buffer.compare(test.sig, sig) === 0 205 | pass &= sodium.crypto_sign_verify_detached(sig, test.m.subarray(0, i), test.pk) 206 | 207 | if (!pass) t.fail('failed on fixture #' + i) 208 | } 209 | t.pass('passed all fixtures') 210 | 211 | for (let j = 1; j < 8; j++) { 212 | sig[63] ^= (j << 5) 213 | 214 | t.absent(sodium.crypto_sign_verify_detached(sig, test.m.subarray(0, i), test.pk)) 215 | 216 | sig[63] ^= (j << 5) 217 | } 218 | 219 | pk.fill(0) 220 | t.absent(sodium.crypto_sign_verify_detached(sig, test.m.subarray(0, i), pk)) 221 | 222 | sig.subarray(0, 32).fill(0xff) 223 | sig[0] = 0xdb 224 | 225 | t.absent(sodium.crypto_sign_verify_detached(sig, test.m.subarray(0, i), pk)) 226 | sodium.crypto_sign_detached(sig, test.m.subarray(0, i), skpk) 227 | 228 | hex2bin(pk, '3eee494fb9eac773144e34b0c755affaf33ea782c0722e5ea8b150e61209ab36') 229 | t.absent(sodium.crypto_sign_verify_detached(sig, test.m.subarray(0, i), pk)) 230 | 231 | hex2bin(pk, '0200000000000000000000000000000000000000000000000000000000000000') 232 | t.absent(sodium.crypto_sign_verify_detached(sig, test.m.subarray(0, i), pk)) 233 | 234 | hex2bin(pk, '0500000000000000000000000000000000000000000000000000000000000000') 235 | t.absent(sodium.crypto_sign_verify_detached(sig, test.m.subarray(0, i), pk)) 236 | 237 | const keypair_seed = new Uint8Array([ 238 | 0x42, 0x11, 0x51, 0xa4, 0x59, 0xfa, 0xea, 0xde, 0x3d, 0x24, 0x71, 239 | 0x15, 0xf9, 0x4a, 0xed, 0xae, 0x42, 0x31, 0x81, 0x24, 0x09, 0x5a, 240 | 0xfa, 0xbe, 0x4d, 0x14, 0x51, 0xa5, 0x59, 0xfa, 0xed, 0xee 241 | ]) 242 | 243 | t.execution(() => sodium.crypto_sign_seed_keypair(pk, sk, keypair_seed)) 244 | t.execution(() => sodium.crypto_sign_keypair(pk, sk)) 245 | 246 | t.ok(sodium.crypto_sign_BYTES > 0) 247 | t.ok(sodium.crypto_sign_SEEDBYTES > 0) 248 | t.ok(sodium.crypto_sign_PUBLICKEYBYTES > 0) 249 | t.ok(sodium.crypto_sign_SECRETKEYBYTES > 0) 250 | t.is(sodium.crypto_sign_BYTES, 64) 251 | t.is(sodium.crypto_sign_SEEDBYTES, 32) 252 | t.is(sodium.crypto_sign_PUBLICKEYBYTES, 32) 253 | t.is(sodium.crypto_sign_SECRETKEYBYTES, 64) 254 | 255 | t.end() 256 | }) 257 | 258 | test('ed25519 convert', function (t) { 259 | const keypair_seed = new Uint8Array([ 260 | 0x42, 0x11, 0x51, 0xa4, 0x59, 0xfa, 0xea, 0xde, 0x3d, 0x24, 0x71, 261 | 0x15, 0xf9, 0x4a, 0xed, 0xae, 0x42, 0x31, 0x81, 0x24, 0x09, 0x5a, 262 | 0xfa, 0xbe, 0x4d, 0x14, 0x51, 0xa5, 0x59, 0xfa, 0xed, 0xee 263 | ]) 264 | 265 | const ed25519_pk = new Uint8Array(sodium.crypto_sign_PUBLICKEYBYTES) 266 | const ed25519_skpk = new Uint8Array(sodium.crypto_sign_SECRETKEYBYTES) 267 | const curve25519_pk = new Uint8Array(sodium.crypto_scalarmult_BYTES) 268 | const curve25519_pk2 = new Uint8Array(sodium.crypto_scalarmult_BYTES) 269 | const curve25519_sk = new Uint8Array(sodium.crypto_scalarmult_BYTES) 270 | 271 | t.ok(sodium.crypto_sign_SEEDBYTES <= sodium.crypto_hash_sha512_BYTES) 272 | 273 | sodium.crypto_sign_seed_keypair(ed25519_pk, ed25519_skpk, keypair_seed) 274 | sodium.crypto_sign_ed25519_pk_to_curve25519(curve25519_pk, ed25519_pk) 275 | sodium.crypto_sign_ed25519_sk_to_curve25519(curve25519_sk, ed25519_skpk) 276 | 277 | const expected_pk = new Uint8Array([ 278 | 0xf1, 0x81, 0x4f, 0x0e, 0x8f, 0xf1, 0x04, 0x3d, 0x8a, 0x44, 0xd2, 0x5b, 279 | 0xab, 0xff, 0x3c, 0xed, 0xca, 0xe6, 0xc2, 0x2c, 0x3e, 0xda, 0xa4, 0x8f, 280 | 0x85, 0x7a, 0xe7, 0x0d, 0xe2, 0xba, 0xae, 0x50 281 | ]) 282 | 283 | const expected_sk = new Uint8Array([ 284 | 0x80, 0x52, 0x03, 0x03, 0x76, 0xd4, 0x71, 0x12, 0xbe, 0x7f, 0x73, 0xed, 285 | 0x7a, 0x01, 0x92, 0x93, 0xdd, 0x12, 0xad, 0x91, 0x0b, 0x65, 0x44, 0x55, 286 | 0x79, 0x8b, 0x46, 0x67, 0xd7, 0x3d, 0xe1, 0x66 287 | ]) 288 | 289 | t.alike(curve25519_pk, expected_pk) 290 | t.alike(curve25519_sk, expected_sk) 291 | 292 | for (let i = 0; i < 500; i++) { 293 | sodium.crypto_sign_keypair(ed25519_pk, ed25519_skpk) 294 | sodium.crypto_sign_ed25519_pk_to_curve25519(curve25519_pk, ed25519_pk) 295 | 296 | sodium.crypto_sign_ed25519_sk_to_curve25519(curve25519_sk, ed25519_skpk) 297 | sodium.crypto_scalarmult_base(curve25519_pk2, curve25519_sk) 298 | if (Buffer.compare(curve25519_pk, curve25519_pk2) !== 0) t.fail() 299 | } 300 | t.pass('passed all cases') 301 | 302 | ed25519_pk.fill(0) 303 | t.exception(() => { 304 | sodium.crypto_sign_ed25519_pk_to_curve25519(curve25519_pk, ed25519_pk) 305 | }) 306 | 307 | t.exception(() => { 308 | ed25519_pk[0] = 2 309 | sodium.crypto_sign_ed25519_pk_to_curve25519(curve25519_pk, ed25519_pk) 310 | }) 311 | 312 | t.exception(() => { 313 | ed25519_pk[0] = 5 314 | sodium.crypto_sign_ed25519_pk_to_curve25519(curve25519_pk, ed25519_pk) 315 | }) 316 | 317 | t.end() 318 | }) 319 | 320 | function parseTest (t) { 321 | return { 322 | sk: new Uint8Array(t[0]), 323 | pk: new Uint8Array(t[1]), 324 | sig: new Uint8Array(t[2]), 325 | m: new Uint8Array(t[3]) 326 | } 327 | } 328 | 329 | function hex2bin (buf, hex) { 330 | for (let i = 0; i < hex.length / 2; i++) { 331 | buf[i] = Number('0x' + hex.slice(2 * i, 2 * i + 1)) 332 | } 333 | } 334 | -------------------------------------------------------------------------------- /test/crypto_stream.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | const { isBare } = require('which-runtime') 4 | 5 | test('crypto_stream', function (t) { 6 | const buf = Buffer.alloc(50) 7 | const nonce = random(sodium.crypto_stream_NONCEBYTES) 8 | const key = random(sodium.crypto_stream_KEYBYTES) 9 | 10 | sodium.crypto_stream(buf, nonce, key) 11 | 12 | t.not(buf, Buffer.alloc(50), 'contains noise now') 13 | const copy = Buffer.from(buf.toString('hex'), 'hex') 14 | 15 | sodium.crypto_stream(buf, nonce, key) 16 | t.alike(buf, copy, 'predictable from nonce, key') 17 | }) 18 | 19 | test('crypto_stream_xor', function (t) { 20 | const message = Buffer.from('Hello, World!') 21 | const nonce = random(sodium.crypto_stream_NONCEBYTES) 22 | const key = random(sodium.crypto_stream_KEYBYTES) 23 | 24 | sodium.crypto_stream_xor(message, message, nonce, key) 25 | 26 | t.not(message, Buffer.from('Hello, World!'), 'encrypted') 27 | 28 | sodium.crypto_stream_xor(message, message, nonce, key) 29 | 30 | t.alike(message, Buffer.from('Hello, World!'), 'decrypted') 31 | }) 32 | 33 | test('crypto_stream_xor state', function (t) { 34 | const message = Buffer.from('Hello, world!') 35 | const nonce = random(sodium.crypto_stream_NONCEBYTES) 36 | const key = random(sodium.crypto_stream_KEYBYTES) 37 | 38 | const out = Buffer.alloc(message.length) 39 | 40 | const state = Buffer.alloc(sodium.crypto_stream_xor_STATEBYTES) 41 | sodium.crypto_stream_xor_init(state, nonce, key) 42 | 43 | for (let i = 0; i < message.length; i++) { 44 | sodium.crypto_stream_xor_update(state, out.subarray(i, i + 1), message.subarray(i, i + 1)) 45 | } 46 | 47 | sodium.crypto_stream_xor_final(state) 48 | sodium.crypto_stream_xor(out, out, nonce, key) 49 | t.alike(out, message, 'decrypted') 50 | }) 51 | 52 | test('crypto_stream_xor state with empty buffers', function (t) { 53 | const message = Buffer.from('Hello, world!') 54 | const nonce = random(sodium.crypto_stream_NONCEBYTES) 55 | const key = random(sodium.crypto_stream_KEYBYTES) 56 | 57 | const out = Buffer.alloc(message.length) 58 | 59 | const state = Buffer.alloc(sodium.crypto_stream_xor_STATEBYTES) 60 | sodium.crypto_stream_xor_init(state, nonce, key) 61 | 62 | sodium.crypto_stream_xor_update(state, Buffer.alloc(0), Buffer.alloc(0)) 63 | 64 | for (let i = 0; i < message.length; i++) { 65 | sodium.crypto_stream_xor_update(state, out.subarray(i, i + 1), message.subarray(i, i + 1)) 66 | sodium.crypto_stream_xor_update(state, Buffer.alloc(0), Buffer.alloc(0)) 67 | } 68 | 69 | sodium.crypto_stream_xor_final(state) 70 | sodium.crypto_stream_xor(out, out, nonce, key) 71 | t.alike(out, message, 'decrypted') 72 | }) 73 | 74 | test('crypto_stream_xor state long stream', function (t) { 75 | const nonce = random(sodium.crypto_stream_NONCEBYTES) 76 | const key = random(sodium.crypto_stream_KEYBYTES) 77 | 78 | const encState = Buffer.alloc(sodium.crypto_stream_xor_STATEBYTES) 79 | const decState = Buffer.alloc(sodium.crypto_stream_xor_STATEBYTES) 80 | 81 | sodium.crypto_stream_xor_init(encState, nonce, key) 82 | sodium.crypto_stream_xor_init(decState, nonce, key) 83 | const plain = [] 84 | const encrypted = [] 85 | const decrypted = [] 86 | 87 | for (let i = 0; i < 1000; i++) { 88 | const next = random(61) 89 | plain.push(next) 90 | 91 | const enc = Buffer.alloc(61) 92 | sodium.crypto_stream_xor_update(encState, enc, next) 93 | encrypted.push(enc) 94 | 95 | const dec = Buffer.alloc(61) 96 | sodium.crypto_stream_xor_update(decState, dec, enc) 97 | decrypted.push(dec) 98 | } 99 | 100 | const enc2 = Buffer.alloc(1000 * 61) 101 | sodium.crypto_stream_xor(enc2, Buffer.concat(plain), nonce, key) 102 | 103 | t.alike(Buffer.concat(encrypted), enc2, 'same as encrypting all at once') 104 | t.alike(Buffer.concat(decrypted), Buffer.concat(plain), 'decrypts') 105 | }) 106 | 107 | test('crypto_stream_xor state long stream (random chunks)', function (t) { 108 | const nonce = random(sodium.crypto_stream_NONCEBYTES) 109 | const key = random(sodium.crypto_stream_KEYBYTES) 110 | 111 | const encState = Buffer.alloc(sodium.crypto_stream_xor_STATEBYTES) 112 | const decState = Buffer.alloc(sodium.crypto_stream_xor_STATEBYTES) 113 | 114 | sodium.crypto_stream_xor_init(encState, nonce, key) 115 | sodium.crypto_stream_xor_init(decState, nonce, key) 116 | const plain = [] 117 | const encrypted = [] 118 | const decrypted = [] 119 | 120 | for (let i = 0; i < 10000; i++) { 121 | const len = Math.floor(Math.random() * 256) 122 | const next = random(len) 123 | plain.push(next) 124 | 125 | const enc = Buffer.alloc(len) 126 | sodium.crypto_stream_xor_update(encState, enc, next) 127 | encrypted.push(enc) 128 | 129 | const dec = Buffer.alloc(len) 130 | sodium.crypto_stream_xor_update(decState, dec, enc) 131 | decrypted.push(dec) 132 | } 133 | 134 | const enc2 = Buffer.alloc(Buffer.concat(plain).length) 135 | sodium.crypto_stream_xor(enc2, Buffer.concat(plain), nonce, key) 136 | 137 | t.alike(Buffer.concat(encrypted), enc2, 'same as encrypting all at once') 138 | t.alike(Buffer.concat(decrypted), Buffer.concat(plain), 'decrypts') 139 | }) 140 | 141 | test('crypto_stream_xor state long stream (random chunks) with empty buffers', function (t) { 142 | const nonce = random(sodium.crypto_stream_NONCEBYTES) 143 | const key = random(sodium.crypto_stream_KEYBYTES) 144 | 145 | const encState = Buffer.alloc(sodium.crypto_stream_xor_STATEBYTES) 146 | const decState = Buffer.alloc(sodium.crypto_stream_xor_STATEBYTES) 147 | 148 | sodium.crypto_stream_xor_init(encState, nonce, key) 149 | sodium.crypto_stream_xor_init(decState, nonce, key) 150 | const plain = [] 151 | const encrypted = [] 152 | const decrypted = [] 153 | 154 | for (let i = 0; i < 10000; i++) { 155 | const len = Math.floor(Math.random() * 256) 156 | const next = random(len) 157 | plain.push(next) 158 | 159 | sodium.crypto_stream_xor_update(encState, Buffer.alloc(0), Buffer.alloc(0)) 160 | 161 | const enc = Buffer.alloc(len) 162 | sodium.crypto_stream_xor_update(encState, enc, next) 163 | encrypted.push(enc) 164 | 165 | const dec = Buffer.alloc(len) 166 | sodium.crypto_stream_xor_update(decState, dec, enc) 167 | decrypted.push(dec) 168 | sodium.crypto_stream_xor_update(decState, Buffer.alloc(0), Buffer.alloc(0)) 169 | } 170 | 171 | const enc2 = Buffer.alloc(Buffer.concat(plain).length) 172 | sodium.crypto_stream_xor(enc2, Buffer.concat(plain), nonce, key) 173 | 174 | t.alike(Buffer.concat(encrypted), enc2, 'same as encrypting all at once') 175 | t.alike(Buffer.concat(decrypted), Buffer.concat(plain), 'decrypts') 176 | }) 177 | 178 | test('crypto_stream_xor state after GC', { skip: isBare }, function (t) { 179 | const message = Buffer.from('Hello, world!') 180 | let nonce = random(sodium.crypto_stream_NONCEBYTES) 181 | let key = random(sodium.crypto_stream_KEYBYTES) 182 | 183 | const out = Buffer.alloc(message.length) 184 | 185 | const state = Buffer.alloc(sodium.crypto_stream_xor_STATEBYTES) 186 | sodium.crypto_stream_xor_init(state, nonce, key) 187 | 188 | const nonceCopy = Buffer.from(nonce.toString('hex'), 'hex') 189 | const keyCopy = Buffer.from(key.toString('hex'), 'hex') 190 | nonce = null 191 | key = null 192 | 193 | forceGC() 194 | 195 | for (let i = 0; i < message.length; i++) { 196 | sodium.crypto_stream_xor_update(state, out.subarray(i, i + 1), message.subarray(i, i + 1)) 197 | } 198 | 199 | sodium.crypto_stream_xor_final(state) 200 | sodium.crypto_stream_xor(out, out, nonceCopy, keyCopy) 201 | t.alike(out, message, 'decrypted') 202 | }) 203 | 204 | function random (n) { 205 | const buf = Buffer.alloc(n) 206 | sodium.randombytes_buf(buf) 207 | return buf 208 | } 209 | 210 | function forceGC () { 211 | require('v8').setFlagsFromString('--expose-gc') 212 | require('vm').runInNewContext('gc')() 213 | } 214 | -------------------------------------------------------------------------------- /test/extension_pbkdf2.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('../') 3 | const vectors = require('./fixtures/pbkdf2.json') 4 | 5 | test('basic', async t => { 6 | const password = Buffer.from('password') 7 | const salt = Buffer.from('salt') 8 | 9 | const output = Buffer.alloc(256) 10 | 11 | await sodium.extension_pbkdf2_sha512(output, password, salt, 1000, 256) 12 | 13 | t.unlike(output, Buffer.alloc(256)) 14 | 15 | try { 16 | sodium.extension_pbkdf2_sha512() 17 | t.fail() 18 | } catch (e) { 19 | t.pass() 20 | } 21 | 22 | try { 23 | // output is too small 24 | const small = output.subarray(2) 25 | sodium.extension_pbkdf2_sha512(small, password, salt, 1000, 256) 26 | t.fail() 27 | } catch (e) { 28 | t.pass() 29 | } 30 | 31 | const big = Buffer.alloc(300) 32 | sodium.extension_pbkdf2_sha512(big, password, salt, 1000, 256) 33 | 34 | t.alike(big.subarray(0, 256), output) 35 | 36 | t.is(sodium.extension_pbkdf2_sha512_SALTBYTES, 16) 37 | t.is(sodium.extension_pbkdf2_sha512_HASHBYTES, 64) 38 | t.is(sodium.extension_pbkdf2_sha512_ITERATIONS_MIN, 1) 39 | t.is(sodium.extension_pbkdf2_sha512_BYTES_MAX, 0x3fffffffc0) 40 | }) 41 | 42 | test('vectors', { timeout: 0 }, async t => { 43 | for (const v of vectors) { 44 | const password = Buffer.from(v.P) 45 | const salt = Buffer.from(v.S) 46 | const iterations = v.c 47 | const length = v.dkLen 48 | const output = Buffer.alloc(length) 49 | 50 | await sodium.extension_pbkdf2_sha512( 51 | output, 52 | password, 53 | salt, 54 | iterations, 55 | length 56 | ) 57 | 58 | t.alike(output.toString('hex'), v.pbkdf2_hmac_sha512) 59 | } 60 | }) 61 | -------------------------------------------------------------------------------- /test/extension_tweak_ed25519.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | const fixtures = require('./fixtures/crypto_tweak_ed25519_sign.js') 4 | 5 | test('crypto_tweak', function (t) { 6 | const pk = Buffer.alloc(sodium.crypto_sign_PUBLICKEYBYTES) 7 | const sk = Buffer.alloc(sodium.crypto_sign_SECRETKEYBYTES) 8 | 9 | sodium.crypto_sign_keypair(pk, sk) 10 | 11 | const tpk = Buffer.alloc(sodium.extension_tweak_ed25519_BYTES) 12 | const tsk = Buffer.alloc(sodium.extension_tweak_ed25519_SCALARBYTES) 13 | 14 | const tkpk = Buffer.alloc(sodium.extension_tweak_ed25519_BYTES) 15 | const tksk = Buffer.alloc(sodium.extension_tweak_ed25519_SCALARBYTES) 16 | 17 | const point = Buffer.alloc(sodium.extension_tweak_ed25519_BYTES) 18 | const tweak = Buffer.alloc(sodium.extension_tweak_ed25519_SCALARBYTES) 19 | 20 | const ns = Buffer.alloc(32) 21 | sodium.crypto_generichash(ns, Buffer.from('namespace')) 22 | 23 | t.exception.all(function () { 24 | sodium.extension_tweak_ed25519_base() 25 | }, 'should validate input') 26 | 27 | t.exception.all(function () { 28 | sodium.extension_tweak_ed25519_base(Buffer.alloc(0), Buffer.alloc(0), ns) 29 | }, 'should validate input length') 30 | 31 | sodium.extension_tweak_ed25519_base(tweak, point, ns) 32 | 33 | const _sk = sk.subarray(0, 32) 34 | sodium.extension_tweak_ed25519_sk_to_scalar(_sk, sk) 35 | 36 | sodium.extension_tweak_ed25519_pk(tpk, pk, ns) 37 | sodium.extension_tweak_ed25519_scalar(tsk, _sk, ns) 38 | 39 | sodium.extension_tweak_ed25519_keypair(tkpk, tksk, _sk, ns) 40 | 41 | sodium.extension_tweak_ed25519_pk_add(pk, pk, point) 42 | sodium.extension_tweak_ed25519_scalar_add(_sk, _sk, tweak) 43 | 44 | t.alike(pk, tpk, 'tweak public key') 45 | t.alike(_sk, tsk, 'tweak secret key') 46 | t.alike(pk, tkpk, 'tweak keypair public key') 47 | t.alike(_sk, tksk, 'tweak keypair secret key') 48 | }) 49 | 50 | test('extension_tweak_sign', function (t) { 51 | const pk = Buffer.alloc(sodium.crypto_sign_PUBLICKEYBYTES) 52 | const sk = Buffer.alloc(sodium.crypto_sign_SECRETKEYBYTES) 53 | 54 | sodium.crypto_sign_keypair(pk, sk) 55 | 56 | const tpk = Buffer.alloc(sodium.extension_tweak_ed25519_BYTES) 57 | const tsk = Buffer.alloc(sodium.extension_tweak_ed25519_SCALARBYTES) 58 | 59 | const point = Buffer.alloc(sodium.extension_tweak_ed25519_BYTES) 60 | const tweak = Buffer.alloc(sodium.extension_tweak_ed25519_SCALARBYTES) 61 | 62 | const ns = Buffer.alloc(32) 63 | sodium.crypto_generichash(ns, Buffer.from('namespace')) 64 | 65 | sodium.extension_tweak_ed25519_base(tweak, point, ns) 66 | 67 | sodium.extension_tweak_ed25519_pk(tpk, pk, ns) 68 | sodium.extension_tweak_ed25519_sk_to_scalar(tsk, sk) 69 | sodium.extension_tweak_ed25519_scalar(tsk, tsk, ns) 70 | 71 | sodium.extension_tweak_ed25519_pk_add(pk, pk, point) 72 | 73 | const _sk = sk.subarray(0, 32) 74 | sodium.extension_tweak_ed25519_sk_to_scalar(_sk, sk) 75 | sodium.extension_tweak_ed25519_scalar_add(_sk, _sk, tweak) 76 | 77 | const m = Buffer.from('test message') 78 | const sig = Buffer.alloc(sodium.crypto_sign_BYTES) 79 | 80 | sodium.extension_tweak_ed25519_sign_detached(sig, m, _sk) 81 | t.ok(sodium.crypto_sign_verify_detached(sig, m, pk)) 82 | t.ok(sodium.crypto_sign_verify_detached(sig, m, tpk)) 83 | 84 | sodium.extension_tweak_ed25519_sign_detached(sig, m, tsk) 85 | t.ok(sodium.crypto_sign_verify_detached(sig, m, pk)) 86 | t.ok(sodium.crypto_sign_verify_detached(sig, m, tpk)) 87 | }) 88 | 89 | test('crypto_tweak sign fixtures', t => { 90 | for (const f of fixtures) { 91 | const [sk, n, m, sig, tweak, tpk, tn] = f.map(Buffer.from) 92 | 93 | const signature = Buffer.alloc(64) 94 | const scalar = Buffer.alloc(32) 95 | const pk = Buffer.alloc(32) 96 | 97 | sodium.extension_tweak_ed25519_sk_to_scalar(scalar, sk) 98 | t.alike(scalar, n) 99 | 100 | sodium.extension_tweak_ed25519_sign_detached(signature, m, n) 101 | t.alike(signature, sig) 102 | 103 | sodium.randombytes_buf(signature) 104 | sodium.crypto_sign_ed25519_sk_to_pk(pk, sk) 105 | 106 | sodium.extension_tweak_ed25519_sign_detached(signature, m, n, pk) 107 | t.alike(signature, sig) 108 | 109 | sodium.extension_tweak_ed25519_keypair(pk, scalar, n, tweak) 110 | t.alike(pk, tpk) 111 | t.alike(scalar, tn) 112 | } 113 | }) 114 | -------------------------------------------------------------------------------- /test/fastcalls.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | 4 | const _e = 1e2 5 | /* call counts */ 6 | const N = { 7 | hash_calls: 1 * _e, 8 | verify_calls: 1 * _e, 9 | unseal_calls: 1 * _e, // 2xunseal per loop 10 | hash_batch_len: 64, 11 | hash_batch_calls: 1 * _e, 12 | stream_xor_calls: 1 * _e, 13 | stream_xchacha20_calls: 1 * _e // 2 calls per loop 14 | } 15 | 16 | test('fastcall: crypto_generichash', t => { 17 | const buf = Buffer.alloc(1024).fill(0xAA) 18 | const out = Buffer.alloc(sodium.crypto_generichash_BYTES) 19 | const bpush = benchmark(t) 20 | 21 | for (let i = 0; i < N.hash_calls; i++) { 22 | sodium.crypto_generichash(out, buf) 23 | bpush(1) 24 | } 25 | 26 | bpush(-1) 27 | }) 28 | 29 | test('fastcall: crypto_sign_verify_detached', function (t) { 30 | const fixtures = require('./fixtures/crypto_sign.json') 31 | 32 | const publicKey = new Uint8Array(fixtures[0][1]) 33 | const message = new Uint8Array(fixtures[0][3]) 34 | const signature = new Uint8Array(fixtures[0][2]) 35 | 36 | const bpush = benchmark(t) 37 | 38 | for (let i = 0; i < N.verify_calls; i++) { 39 | const valid = sodium.crypto_sign_verify_detached(signature, message, publicKey) 40 | if (!valid) throw new Error('Unexpected verification failure') 41 | bpush(1) 42 | } 43 | 44 | bpush(-1) 45 | }) 46 | 47 | test('fastcall: crypto_box_unseal', function (t) { 48 | const pk = Buffer.alloc(sodium.crypto_box_PUBLICKEYBYTES) 49 | const sk = Buffer.alloc(sodium.crypto_box_SECRETKEYBYTES) 50 | 51 | sodium.crypto_box_keypair(pk, sk) 52 | 53 | const pk2 = Buffer.alloc(sodium.crypto_box_PUBLICKEYBYTES) 54 | const sk2 = Buffer.alloc(sodium.crypto_box_SECRETKEYBYTES) 55 | 56 | sodium.crypto_box_keypair(pk2, sk2) 57 | 58 | const message = Buffer.from('Hello, sealed World!') 59 | const cipher = Buffer.alloc(message.length + sodium.crypto_box_SEALBYTES) 60 | 61 | sodium.crypto_box_seal(cipher, message, pk) 62 | t.not(cipher, message, 'message encrypted') 63 | t.not(cipher, Buffer.alloc(cipher.length), 'not blank') 64 | 65 | const plain = Buffer.alloc(cipher.length - sodium.crypto_box_SEALBYTES) 66 | 67 | const bpush = benchmark(t) 68 | 69 | for (let i = 0; i < N.unseal_calls; i++) { 70 | let success = sodium.crypto_box_seal_open(plain, cipher, pk2, sk2) 71 | if (success) throw new Error('Unexpected decryption occured') 72 | 73 | success = sodium.crypto_box_seal_open(plain, cipher, pk, sk) 74 | if (!success) throw new Error('Unexpected decryption failure') 75 | bpush(2) 76 | } 77 | 78 | bpush(-1) 79 | }) 80 | 81 | test('fastcall: crypto_generichash_batch', t => { 82 | const buf = Buffer.from('Hej, Verden') 83 | const batch = [] 84 | for (let i = 0; i < N.hash_batch_len; i++) batch.push(buf) 85 | 86 | const out = Buffer.alloc(sodium.crypto_generichash_BYTES) 87 | 88 | const bpush = benchmark(t) 89 | 90 | for (let i = 0; i < N.hash_batch_calls; i++) { 91 | sodium.crypto_generichash_batch(out, batch) 92 | bpush(batch.length) 93 | } 94 | 95 | bpush(-1) 96 | }) 97 | 98 | test('fastcall: crypto_stream_xor', t => { 99 | const message = Buffer.alloc(4096).fill(0xaa) 100 | const plain = Buffer.alloc(4096).fill(0xaa) 101 | const nonce = random(sodium.crypto_stream_NONCEBYTES) 102 | const key = random(sodium.crypto_stream_KEYBYTES) 103 | 104 | const bpush = benchmark(t) 105 | 106 | for (let i = 0; i < N.stream_xor_calls; i++) { 107 | sodium.crypto_stream_xor(message, message, nonce, key) 108 | if (message.equals(plain)) throw new Error('encryption failed') 109 | 110 | sodium.crypto_stream_xor(message, message, nonce, key) 111 | if (!message.equals(plain)) throw new Error('decryption failed') 112 | bpush(2) 113 | } 114 | 115 | bpush(-1) 116 | 117 | function random (n) { 118 | const buf = Buffer.alloc(n) 119 | sodium.randombytes_buf(buf) 120 | return buf 121 | } 122 | }) 123 | 124 | test('fastcall: crypto_secretstream_xchacha20poly1305_push & pull', t => { 125 | const { 126 | crypto_secretstream_xchacha20poly1305_TAG_MESSAGE: TAG_MESSAGE, 127 | crypto_secretstream_xchacha20poly1305_ABYTES: ABYTES, 128 | crypto_secretstream_xchacha20poly1305_STATEBYTES: STATEBYTES, 129 | crypto_secretstream_xchacha20poly1305_HEADERBYTES: HEADERBYTES, 130 | crypto_secretstream_xchacha20poly1305_KEYBYTES: KEYBYTES, 131 | crypto_secretstream_xchacha20poly1305_TAGBYTES: TAGBYTES 132 | } = sodium 133 | 134 | const header = Buffer.alloc(HEADERBYTES) 135 | const key = Buffer.alloc(KEYBYTES) 136 | const tag = Buffer.alloc(TAGBYTES, 0xdb) 137 | 138 | const adIn = Buffer.alloc(sodium.randombytes_uniform(100)) // additional data 139 | const adOut = Buffer.alloc(adIn.byteLength) 140 | 141 | sodium.crypto_secretstream_xchacha20poly1305_keygen(key) 142 | 143 | const stateEnc = Buffer.alloc(STATEBYTES) 144 | sodium.crypto_secretstream_xchacha20poly1305_init_push(stateEnc, header, key) 145 | 146 | const stateDec = Buffer.alloc(STATEBYTES) 147 | sodium.crypto_secretstream_xchacha20poly1305_init_pull(stateDec, header, key) 148 | 149 | const message = Buffer.alloc(1024, 0xaa) 150 | const cipher = Buffer.alloc(message.byteLength + ABYTES) 151 | const plain = Buffer.alloc(message.byteLength) 152 | 153 | const bpush = benchmark(t) 154 | 155 | for (let i = 0; i < N.stream_xchacha20_calls; i++) { 156 | let ret = sodium.crypto_secretstream_xchacha20poly1305_push(stateEnc, cipher, message, adIn, TAG_MESSAGE) 157 | if (ret !== message.byteLength + ABYTES) t.fail('invalid amount written') 158 | 159 | ret = sodium.crypto_secretstream_xchacha20poly1305_pull(stateDec, plain, tag, cipher, adOut) 160 | 161 | if (ret !== cipher.byteLength - ABYTES) t.fail('invalid amount read') 162 | if (tag[0] !== TAG_MESSAGE) t.fail('bad tag decoded') 163 | if (!message.equals(plain)) t.fail('decryption failed') 164 | if (!adOut.equals(adIn)) t.fail('additional data mismatch') 165 | 166 | bpush(2) 167 | } 168 | 169 | bpush(-1) 170 | }) 171 | 172 | function benchmark (t, interval = 2000) { 173 | let prev 174 | const start = prev = Date.now() 175 | let n = 0 176 | let total = n 177 | 178 | return function measure (qty = 1) { 179 | const now = Date.now() 180 | const delta = now - prev 181 | 182 | if (qty > 0) { 183 | n += qty 184 | total += qty 185 | } 186 | 187 | if ((interval && interval < delta) || qty < 0) { 188 | const ops = (n / delta) * 1000 189 | const runtime = now - start 190 | const avg = (total / runtime) * 1000 191 | t.comment('ops', ops.toExponential(2), 'avg', Math.round(avg), 'total', total, 'runtime', runtime) 192 | prev = now 193 | n = 0 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /test/fixtures/mprotect_noaccess.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | const sodium = require('../..') 3 | const buf = sodium.sodium_malloc(1) 4 | sodium.sodium_mprotect_noaccess(buf) 5 | buf[0] 6 | process.send('read') 7 | -------------------------------------------------------------------------------- /test/fixtures/mprotect_readonly.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | const sodium = require('../..') 3 | const buf = sodium.sodium_malloc(1) 4 | sodium.sodium_mprotect_readonly(buf) 5 | buf[0] 6 | process.send('read') 7 | buf[0] = 1 8 | process.send('write') 9 | -------------------------------------------------------------------------------- /test/fixtures/mprotect_readwrite.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | const sodium = require('../..') 3 | const buf = sodium.sodium_malloc(1) 4 | sodium.sodium_mprotect_noaccess(buf) 5 | sodium.sodium_mprotect_readwrite(buf) 6 | buf[0] 7 | process.send('read') 8 | buf[0] = 1 9 | process.send('write') 10 | sodium.sodium_mprotect_readonly(buf) 11 | process.send(buf[0] === 1 ? 'did_write' : 'did_not_write') 12 | -------------------------------------------------------------------------------- /test/fixtures/pbkdf2.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "P": "password", 4 | "S": "salt", 5 | "c": 1, 6 | "dkLen": 20, 7 | "pbkdf2_hmac_sha224": "3c198cbdb9464b7857966bd05b7bc92bc1cc4e6e", 8 | "pbkdf2_hmac_sha256": "120fb6cffcf8b32c43e7225256c4f837a86548c9", 9 | "pbkdf2_hmac_sha384": "c0e14f06e49e32d73f9f52ddf1d0c5c719160923", 10 | "pbkdf2_hmac_sha512": "867f70cf1ade02cff3752599a3a53dc4af34c7a6" 11 | }, 12 | { 13 | "P": "password", 14 | "S": "salt", 15 | "c": 2, 16 | "dkLen": 20, 17 | "pbkdf2_hmac_sha224": "93200ffa96c5776d38fa10abdf8f5bfc0054b971", 18 | "pbkdf2_hmac_sha256": "ae4d0c95af6b46d32d0adff928f06dd02a303f8e", 19 | "pbkdf2_hmac_sha384": "54f775c6d790f21930459162fc535dbf04a93918", 20 | "pbkdf2_hmac_sha512": "e1d9c16aa681708a45f5c7c4e215ceb66e011a2e" 21 | }, 22 | { 23 | "P": "password", 24 | "S": "salt", 25 | "c": 4096, 26 | "dkLen": 20, 27 | "pbkdf2_hmac_sha224": "218c453bf90635bd0a21a75d172703ff6108ef60", 28 | "pbkdf2_hmac_sha256": "c5e478d59288c841aa530db6845c4c8d962893a0", 29 | "pbkdf2_hmac_sha384": "559726be38db125bc85ed7895f6e3cf574c7a01c", 30 | "pbkdf2_hmac_sha512": "d197b1b33db0143e018b12f3d1d1479e6cdebdcc" 31 | }, 32 | { 33 | "P": "password", 34 | "S": "salt", 35 | "c": 16777216, 36 | "dkLen": 20, 37 | "pbkdf2_hmac_sha224": "b49925184cb4b559f365e94fcafcd4cdb9f7aef4", 38 | "pbkdf2_hmac_sha256": "cf81c66fe8cfc04d1f31ecb65dab4089f7f179e8", 39 | "pbkdf2_hmac_sha384": "a7fdb349ba2bfa6bf647bb0161bae1320df27e64", 40 | "pbkdf2_hmac_sha512": "6180a3ceabab45cc3964112c811e0131bca93a35" 41 | }, 42 | { 43 | "P": "passwordPASSWORDpassword", 44 | "S": "saltSALTsaltSALTsaltSALTsaltSALTsalt", 45 | "c": 4096, 46 | "dkLen": 25, 47 | "pbkdf2_hmac_sha224": "056c4ba438ded91fc14e0594e6f52b87e1f3690c0dc0fbc057", 48 | "pbkdf2_hmac_sha256": "348c89dbcbd32b2f32d814b8116e84cf2b17347ebc1800181c", 49 | "pbkdf2_hmac_sha384": "819143ad66df9a552559b9e131c52ae6c5c1b0eed18f4d283b", 50 | "pbkdf2_hmac_sha512": "8c0511f4c6e597c6ac6315d8f0362e225f3c501495ba23b868" 51 | }, 52 | { 53 | "P": "pass\u0000word", 54 | "S": "sa\u0000lt", 55 | "c": 4096, 56 | "dkLen": 16, 57 | "pbkdf2_hmac_sha224": "9b4011b641f40a2a500a31d4a392d15c", 58 | "pbkdf2_hmac_sha256": "89b69d0516f829893c696226650a8687", 59 | "pbkdf2_hmac_sha384": "a3f00ac8657e095f8e0823d232fc60b3", 60 | "pbkdf2_hmac_sha512": "9d9e9c4cd21fe4be24d5b8244c759665" 61 | }, 62 | { 63 | "P": "passwd", 64 | "S": "salt", 65 | "c": 1, 66 | "dkLen": 128, 67 | "pbkdf2_hmac_sha224": "e55bd77cfc18b012ac6362e22d7cdf77c4b03879a6af51fbf0045bc32a03e7f0d829d26b765bff0ca5873e07a8e85804ff4a17683ed706130d51657456bc0ebd07c35ca0675b3113ad9c33fe48a5eb9e9dc6c6a8cf5cf6de1318b414dbe667bfaeb863ef8399ff4a732520dab4ba82336513a25077ddfc11fc618c11efaf04ae", 68 | "pbkdf2_hmac_sha256": "55ac046e56e3089fec1691c22544b605f94185216dde0465e68b9d57c20dacbc49ca9cccf179b645991664b39d77ef317c71b845b1e30bd509112041d3a19783c294e850150390e1160c34d62e9665d659ae49d314510fc98274cc79681968104b8f89237e69b2d549111868658be62f59bd715cac44a1147ed5317c9bae6b2a", 69 | "pbkdf2_hmac_sha384": "cd3443723a41cf1460cca9efeede428a8898a82d2ad4d1fc5cca08ed3f4d3cb47a62a70b3cb9ce65dcbfb9fb9d425027a8be69b53e2a22674b0939e5e0a682f76d21f449ad184562a3bc4c519b4d048de6d8e0999fb88770f95e40185e19fc8b68767417ccc064f47a455d045b3bafda7e81b97ad0e4c5581af1aa27871cd5e4", 70 | "pbkdf2_hmac_sha512": "c74319d99499fc3e9013acff597c23c5baf0a0bec5634c46b8352b793e324723d55caa76b2b25c43402dcfdc06cdcf66f95b7d0429420b39520006749c51a04ef3eb99e576617395a178ba33214793e48045132928a9e9bf2661769fdc668f31798597aaf6da70dd996a81019726084d70f152baed8aafe2227c07636c6ddece" 71 | }, 72 | { 73 | "P": "Password", 74 | "S": "NaCl", 75 | "c": 80000, 76 | "dkLen": 128, 77 | "pbkdf2_hmac_sha224": "bebbdf809d53fc84531d0abe06679a8c8526fde47b47245634186908335857334a7578543f9241726d845ee8e575105e4a733b5dcaefa7560af3d028eccf95937535918dbaa84269fc0586711e7a5b9dc0d4c28fc7a89469db7ff5829b8fc1ef709d7ef95c6c7db24cece88f7c1408c8e7cee55c84db0eebb8d8e419bb50e17b", 78 | "pbkdf2_hmac_sha256": "4ddcd8f60b98be21830cee5ef22701f9641a4418d04c0414aeff08876b34ab56a1d425a1225833549adb841b51c9b3176a272bdebba1d078478f62b397f33c8d62aae85a11cdde829d89cb6ffd1ab0e63a981f8747d2f2f9fe5874165c83c168d2eed1d2d5ca4052dec2be5715623da019b8c0ec87dc36aa751c38f9893d15c3", 79 | "pbkdf2_hmac_sha384": "11c198987730fa113458053cd5cc9b51d7024a35f9134f1ee8740923c901aab23bbaea43686981b6e6a9f4130a1401daeeec74060246ebac958f3cfc3c65579b6e3d08b94ade5fc257a6902a0a1664b8dbd5a8ae2af70438931d3f3679abffc7a17770582f1ee413cc0d9914ce5f8143c8a7dc9c43fbc31e3d41b2030fb73c02", 80 | "pbkdf2_hmac_sha512": "e6337d6fbeb645c794d4a9b5b75b7b30dac9ac50376a91df1f4460f6060d5addb2c1fd1f84409abacc67de7eb4056e6bb06c2d82c3ef4ccd1bded0f675ed97c65c33d39f81248454327aa6d03fd049fc5cbb2b5e6dac08e8ace996cdc960b1bd4530b7e754773d75f67a733fdb99baf6470e42ffcb753c15c352d4800fb6f9d6" 81 | }, 82 | { 83 | "P": "Password", 84 | "S": "sa\u0000lt", 85 | "c": 4096, 86 | "dkLen": 256, 87 | "pbkdf2_hmac_sha224": "a329a360c825e12e454ad8633a842a06ba1456907770779d1fa4e0b61a5b1c6ce02e71de74ae433bbf14b907690d008d0cab5b01c976c1e627b027a9a809fd001082c809650344ecfcdebdf0d64b92cb1e869bf91b75517ea36918127b1eccc4cac145fb965071292a6dfa388d8ad893d2541f83a0dac1c55d2d90709963b066de985e92974e87b7d8c0e8026d96684bb0425203919b4792962b065e2b2b815ba888b8428ae51f57a74f637a658e27cf5fbc5593e85f775a1f81660850a723e2eb565f30dfc2cf2973ad57ec95b89c0979c7bab81c11d8987540a32badb2f7bbe4ff21a4f0d91dbd911b88ddd928603fd27b0ede994ee99edd2c04667b82067f", 88 | "pbkdf2_hmac_sha256": "436c82c6af9010bb0fdb274791934ac7dee21745dd11fb57bb90112ab187c495ad82df776ad7cefb606f34fedca59baa5922a57f3e91bc0e11960da7ec87ed0471b456a0808b60dff757b7d313d4068bf8d337a99caede24f3248f87d1bf16892b70b076a07dd163a8a09db788ae34300ff2f2d0a92c9e678186183622a636f4cbce15680dfea46f6d224e51c299d4946aa2471133a649288eef3e4227b609cf203dba65e9fa69e63d35b6ff435ff51664cbd6773d72ebc341d239f0084b004388d6afa504eee6719a7ae1bb9daf6b7628d851fab335f1d13948e8ee6f7ab033a32df447f8d0950809a70066605d6960847ed436fa52cdfbcf261b44d2a87061", 89 | "pbkdf2_hmac_sha384": "cf6f194aaf4e970afea1f41169045029e34759e124a670b5f73053da552a190ad2d7085533b8b22901f0e3caeeb431ba673468f981352dfcbe517699db791777cf52346a460b093c59ea300fb18daee270e2ea8473806da1663cebe7438b51fe56ba832c13d88ad5b2e46404457c34cc6ad8e5cd8707a1acfa737f3617628a5983d8d10fa16a92652cfa736d4610132710a517c216cc3252e6c2b8aae0275d04a49756fa5bf1bb067bc367d1b8c80c3df7dc22ee74b4be4150871624bfdde3f86f5fbd4e0828af7d5a4f01b5605e54471435d827eaecf199db315ae60d1a6350105c0e1a71b40518a4a66ebba4792a511f8f52aeac961ebea215f8fb89ba998b", 90 | "pbkdf2_hmac_sha512": "10176fb32cb98cd7bb31e2bb5c8f6e425c103333a2e496058e3fd2bd88f657485c89ef92daa0668316bc23ebd1ef88f6dd14157b2320b5d54b5f26377c5dc279b1dcdec044bd6f91b166917c80e1e99ef861b1d2c7bce1b961178125fb86867f6db489a2eae0022e7bc9cf421f044319fac765d70cb89b45c214590e2ffb2c2b565ab3b9d07571fde0027b1dc57f8fd25afa842c1056dd459af4074d7510a0c020b914a5e202445d4d3f151070589dd6a2554fc506018c4f001df6239643dc86771286ae4910769d8385531bba57544d63c3640b90c98f1445ebdd129475e02086b600f0beb5b05cc6ca9b3633b452b7dad634e9336f56ec4c3ac0b4fe54ced8" 91 | } 92 | ] 93 | -------------------------------------------------------------------------------- /test/helpers.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | 4 | test('sodium_memcmp', function (t) { 5 | const b1 = Buffer.from([0, 1, 2, 3]) 6 | const b2 = Buffer.from([3, 2, 1, 0]) 7 | 8 | t.exception.all(_ => sodium.sodium_memcmp(), 'no args') 9 | t.exception.all(_ => sodium.sodium_memcmp(b1), 'arg mismatch') 10 | t.exception.all(_ => sodium.sodium_memcmp(b1, b2.subarray(1)), 'length mismatch') 11 | t.ok(sodium.sodium_memcmp(Buffer.alloc(0), Buffer.alloc(0))) 12 | t.ok(sodium.sodium_memcmp(Buffer.alloc(5), Buffer.alloc(5))) 13 | t.ok(sodium.sodium_memcmp(b1, b1)) 14 | t.absent(sodium.sodium_memcmp(b2, b1)) 15 | t.absent(sodium.sodium_memcmp(b1, b2)) 16 | }) 17 | 18 | test('sodium_compare', function (t) { 19 | const one = Buffer.from([1]) 20 | const two = Buffer.from([2]) 21 | const three = Buffer.from([3]) 22 | 23 | t.is(sodium.sodium_compare(Buffer.alloc(0), Buffer.alloc(0)), 0) 24 | t.is(sodium.sodium_compare(one, one), 0) 25 | t.is(sodium.sodium_compare(two, two), 0) 26 | t.is(sodium.sodium_compare(three, three), 0) 27 | 28 | t.is(sodium.sodium_compare(one, two), -1) 29 | t.is(sodium.sodium_compare(one, three), -1) 30 | t.is(sodium.sodium_compare(two, one), 1) 31 | t.is(sodium.sodium_compare(three, one), 1) 32 | 33 | t.is(sodium.sodium_compare(two, three), -1) 34 | t.is(sodium.sodium_compare(three, two), 1) 35 | }) 36 | 37 | test('sodium_add', function (t) { 38 | const large = Buffer.alloc(32) 39 | large[23] = 0b00000011 40 | const largeLessOne = Buffer.alloc(32) 41 | largeLessOne[23] = 0b00000001 42 | 43 | const c = Buffer.from(large) 44 | 45 | sodium.sodium_add(c, largeLessOne) 46 | t.ok(large[23], 4) 47 | 48 | const overflow = Buffer.alloc(56, 0xff) 49 | const one = Buffer.alloc(56) 50 | one[0] = 1 51 | sodium.sodium_add(overflow, one) 52 | 53 | t.ok(sodium.sodium_is_zero(overflow)) 54 | }) 55 | 56 | test('sub', function (t) { 57 | const large = Buffer.alloc(32) 58 | large[23] = 0b00000011 59 | const largeLessOne = Buffer.alloc(32) 60 | largeLessOne[23] = 0b00000001 61 | 62 | const c = Buffer.from(large) 63 | 64 | sodium.sodium_sub(c, largeLessOne) 65 | t.ok(large[23], 2) 66 | 67 | const overflow = Buffer.alloc(56, 0x00) 68 | const one = Buffer.alloc(56) 69 | one[0] = 1 70 | sodium.sodium_sub(overflow, one) 71 | 72 | t.ok(sodium.sodium_memcmp(overflow, Buffer.alloc(56, 0xff))) 73 | }) 74 | 75 | test('sodium_increment', function (t) { 76 | const zero = Buffer.alloc(4) 77 | sodium.sodium_increment(zero) 78 | 79 | t.ok(zero[0], 1) 80 | 81 | const overflow = Buffer.alloc(56, 0xff) 82 | sodium.sodium_increment(overflow) 83 | 84 | t.ok(sodium.sodium_is_zero(overflow)) 85 | }) 86 | 87 | test('sodium_is_zero', function (t) { 88 | const buf = Buffer.from([0, 0, 0, 1]) 89 | 90 | t.exception.all(_ => sodium.sodium_is_zero(), 'no args') 91 | t.exception.all(_ => sodium.sodium_is_zero(null), 'missing buf') 92 | 93 | t.ok(sodium.sodium_is_zero(Buffer.alloc(0)), 'empty buffer') 94 | t.ok(sodium.sodium_is_zero(buf.subarray(0, 0)), 'zero bytes') 95 | t.ok(sodium.sodium_is_zero(buf.subarray(0, 1)), 'one byte') 96 | t.ok(sodium.sodium_is_zero(buf.subarray(0, 2)), 'two bytes') 97 | t.ok(sodium.sodium_is_zero(buf.subarray(0, 3)), '3 bytes') 98 | t.absent(sodium.sodium_is_zero(buf), 'first non-zero byte') 99 | t.ok(sodium.sodium_is_zero(buf.subarray(1, 2)), 'view') 100 | t.ok(sodium.sodium_is_zero(buf.subarray(1, 2)), 'view') 101 | t.absent(sodium.sodium_is_zero(buf.subarray(3)), 'view') 102 | }) 103 | -------------------------------------------------------------------------------- /test/memory.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | const { isBare, isNode } = require('which-runtime') 4 | const fork = isNode ? require('child_process').fork : () => { throw new Error('fork() not supported on runtime') } 5 | 6 | test('sodium_mprotect_noaccess', { skip: isBare }, function (t) { 7 | t.plan(1) 8 | const p = fork(require.resolve('./fixtures/mprotect_noaccess')) 9 | 10 | p.on('message', function () { 11 | t.fail() 12 | }) 13 | p.on('exit', function (code, signal) { 14 | t.ok(p.signalCode !== null || p.exitCode > 0) 15 | }) 16 | }) 17 | 18 | test('sodium_mprotect_readonly', { skip: isBare }, function (t) { 19 | t.plan(2) 20 | const p = fork(require.resolve('./fixtures/mprotect_readonly')) 21 | 22 | p.on('message', function (msg) { 23 | t.ok(msg === 'read') 24 | }) 25 | p.on('exit', function (code, signal) { 26 | t.ok(p.signalCode !== null || p.exitCode > 0) 27 | }) 28 | }) 29 | 30 | test('sodium_mprotect_readwrite', { skip: isBare }, function (t) { 31 | t.plan(4) 32 | const p = fork(require.resolve('./fixtures/mprotect_readwrite')) 33 | 34 | p.on('message', function (msg) { 35 | switch (msg) { 36 | case 'read': t.pass() 37 | break 38 | case 'write': t.pass() 39 | break 40 | case 'did_write': t.pass() 41 | break 42 | case 'did_not_write': t.fail() 43 | break 44 | default: t.fail() 45 | break 46 | } 47 | }) 48 | p.on('exit', function (code, signal) { 49 | t.ok(p.signalCode === null || p.exitCode === 0) 50 | }) 51 | }) 52 | 53 | test('sodium_memzero', function (t) { 54 | const buf = Buffer.alloc(10, 0xab) 55 | const exp = Buffer.alloc(10, 0xab) 56 | const zero = Buffer.alloc(10) 57 | 58 | t.alike(buf, exp, 'buffers start out with same content') 59 | t.unlike(buf, zero, 'buffer is not zero') 60 | 61 | sodium.sodium_memzero(buf) 62 | t.unlike(buf, exp, 'buffers are not longer the same') 63 | t.alike(buf, zero, 'buffer is now zeroed') 64 | }) 65 | 66 | test('sodium_mlock / sodium_munlock', function (t) { 67 | const buf = Buffer.alloc(10, 0x18) 68 | const exp = Buffer.alloc(10, 0x18) 69 | 70 | sodium.sodium_mlock(buf) 71 | t.absent(buf.secure) 72 | t.alike(buf, exp, 'mlock did not corrupt data') 73 | sodium.sodium_munlock(buf) 74 | t.absent(buf.secure) 75 | t.alike(buf, Buffer.alloc(10), 'munlock did zero data') 76 | }) 77 | 78 | test('sodium_malloc', function (t) { 79 | const empty = sodium.sodium_malloc(0) 80 | const small = sodium.sodium_malloc(1) 81 | const large = sodium.sodium_malloc(1e8) 82 | 83 | t.ok(empty.secure) 84 | t.ok(small.secure) 85 | t.ok(large.secure) 86 | 87 | t.ok(empty.length === 0, 'has correct size') 88 | t.ok(small.length === 1, 'has correct size') 89 | t.ok(large.length === 1e8, 'has correct size') 90 | 91 | const expected = Buffer.from([0xdb]) 92 | expected.secure = true 93 | t.alike(small, expected, 'has canary content') 94 | 95 | // test gc 96 | for (let i = 0; i < 1e3; i++) { 97 | if (sodium.sodium_malloc(256).length !== 256) { 98 | t.fail('allocated incorrect size') 99 | } 100 | } 101 | t.ok(empty.length === 0, 'retained correct size') 102 | t.ok(small.length === 1, 'retained correct size') 103 | t.ok(large.length === 1e8, 'retained correct size') 104 | }) 105 | 106 | test('sodium_free', function (t) { 107 | const buf = sodium.sodium_malloc(1) 108 | t.ok(buf.byteLength === 1) 109 | sodium.sodium_free(buf) 110 | t.ok(buf.byteLength === 0) 111 | }) 112 | 113 | test('sodium_free, double free', function (t) { 114 | const buf = sodium.sodium_malloc(1) 115 | t.ok(buf.byteLength === 1) 116 | sodium.sodium_free(buf) 117 | t.ok(buf.byteLength === 0) 118 | sodium.sodium_free(buf) 119 | t.ok(buf.byteLength === 0) 120 | }) 121 | 122 | test.skip('sodium_malloc bounds', function (t) { 123 | t.throws(function () { 124 | sodium.sodium_malloc(-1) 125 | }, 'too small') 126 | t.throws(function () { 127 | sodium.sodium_malloc(Number.MAX_SAFE_INTEGER) 128 | }, 'too large') 129 | }) 130 | -------------------------------------------------------------------------------- /test/padding.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | 4 | test('sodium_pad / sodium_unpad', function (t) { 5 | for (let i = 0; i < 2000; i++) { 6 | const binLen = sodium.randombytes_uniform(200) 7 | const blocksize = 1 + sodium.randombytes_uniform(100) 8 | const binPaddedMaxlen = binLen + (blocksize - (binLen % blocksize)) 9 | const bingPaddedLong = Buffer.alloc(binPaddedMaxlen + 1) 10 | const binPaddedLen = bingPaddedLong.subarray(0, binPaddedMaxlen) 11 | sodium.randombytes_buf(binPaddedLen) 12 | 13 | const smallThrow = didThrow(function () { 14 | sodium.sodium_pad(binPaddedLen.subarray(0, binPaddedMaxlen - 1), binLen, blocksize) 15 | }) 16 | if (smallThrow === false) t.fail('did not throw') 17 | 18 | const zeroThrow = didThrow(function () { 19 | sodium.sodium_pad(binPaddedLen, binLen, 0) 20 | }) 21 | if (zeroThrow === false) t.fail('did not throw') 22 | 23 | sodium.sodium_pad(bingPaddedLong, binLen, blocksize) 24 | const binUnpaddedLen = sodium.sodium_pad(binPaddedLen, binLen, blocksize) 25 | if (binUnpaddedLen !== binPaddedMaxlen) t.fail('binUnpaddedLen was not same') 26 | 27 | const largeThrow = didThrow(function () { 28 | sodium.sodium_unpad(binPaddedLen, binUnpaddedLen, binPaddedMaxlen + 1) 29 | }) 30 | if (largeThrow === false) t.fail('did not throw') 31 | 32 | const emptyThrow = didThrow(function () { 33 | sodium.sodium_unpad(binPaddedLen, binUnpaddedLen, 0) 34 | }) 35 | if (emptyThrow === false) t.fail('did not throw') 36 | 37 | const len2 = sodium.sodium_unpad(binPaddedLen, binUnpaddedLen, blocksize) 38 | if (len2 !== binLen) t.fail('len2 was not same') 39 | } 40 | 41 | t.pass() 42 | }) 43 | 44 | function didThrow (fn) { 45 | try { 46 | fn() 47 | return false 48 | } catch (ex) { 49 | return true 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /test/randombytes.js: -------------------------------------------------------------------------------- 1 | const test = require('brittle') 2 | const sodium = require('..') 3 | 4 | test('constants', function (t) { 5 | t.alike(typeof sodium.randombytes_SEEDBYTES, 'number', 'randombytes_SEEDBYTES is number') 6 | }) 7 | 8 | test('randombytes_random', function (t) { 9 | for (let i = 0; i < 1e6; i++) { 10 | const n = sodium.randombytes_random() 11 | if (n > 0xffffffff || n < 0) t.fail() 12 | } 13 | }) 14 | 15 | test('randombytes_uniform', function (t) { 16 | const p = 5381 17 | for (let i = 0; i < 1e6; i++) { 18 | const n = sodium.randombytes_uniform(5381) 19 | if (n >= p || n < 0) t.fail() 20 | } 21 | }) 22 | 23 | test('randombytes_buf', function (t) { 24 | let buf = null 25 | 26 | buf = Buffer.alloc(10) 27 | sodium.randombytes_buf(buf) 28 | t.not(buf, Buffer.alloc(10), 'not blank') 29 | 30 | buf = Buffer.alloc(1024) 31 | sodium.randombytes_buf(buf) 32 | t.not(buf, Buffer.alloc(1024), 'large not blank') 33 | }) 34 | 35 | test('randombytes_deterministic', function (t) { 36 | const seed1 = Buffer.allocUnsafe(sodium.randombytes_SEEDBYTES) 37 | const seed2 = Buffer.allocUnsafe(sodium.randombytes_SEEDBYTES) 38 | const buf1 = Buffer.alloc(10) 39 | const buf2 = Buffer.alloc(10) 40 | 41 | for (let i = 0; i < 1e6; i++) { 42 | sodium.randombytes_buf(seed1) 43 | sodium.randombytes_buf(seed2) 44 | 45 | sodium.randombytes_buf_deterministic(buf1, seed1) 46 | sodium.randombytes_buf_deterministic(buf2, seed1) 47 | if (!buf1.equals(buf2)) t.fail('should equal') 48 | 49 | sodium.randombytes_buf_deterministic(buf1, seed1) 50 | sodium.randombytes_buf_deterministic(buf2, seed2) 51 | if (buf1.equals(buf2)) t.fail('should not equal') 52 | 53 | sodium.randombytes_buf_deterministic(buf1, seed2) 54 | sodium.randombytes_buf_deterministic(buf2, seed1) 55 | if (buf1.equals(buf2)) t.fail('should not equal') 56 | 57 | sodium.randombytes_buf_deterministic(buf1, seed2) 58 | sodium.randombytes_buf_deterministic(buf2, seed2) 59 | if (!buf1.equals(buf2)) t.fail('should equal') 60 | } 61 | }) 62 | 63 | test.skip('Various test cases', function (t) { 64 | sodium.randombytes_buf(Buffer.alloc(0)) 65 | sodium.randombytes_buf(new Uint8Array(16)) 66 | 67 | t.throws(function () { 68 | sodium.randombytes_buf([]) 69 | }) 70 | 71 | t.end() 72 | }) 73 | 74 | test('Generates random bytes', function (t) { 75 | const bufConst = Buffer.alloc(64) 76 | sodium.randombytes_buf(bufConst) 77 | 78 | const buf1 = Buffer.alloc(64) 79 | for (let i = 0; i < 1e4; i++) { 80 | sodium.randombytes_buf(buf1) 81 | if (Buffer.compare(buf1, bufConst) === 0) { 82 | t.fail('Constant buffer should not be equal') 83 | t.end() 84 | return 85 | } 86 | } 87 | 88 | t.pass('Generated unique buffers') 89 | t.end() 90 | }) 91 | 92 | test('Exceed quota', function (t) { 93 | const buf = Buffer.alloc(1 << 17) 94 | sodium.randombytes_buf(buf) 95 | 96 | const scores = new Array(256) 97 | scores.fill(0) 98 | 99 | for (const b of buf) { 100 | scores[b]++ 101 | } 102 | 103 | scores 104 | .map(cnt => cnt / 256) 105 | .forEach(cnt => { 106 | if (cnt < 1 && cnt > 3) t.fail('Statistically unreasonable') 107 | }) 108 | 109 | t.end() 110 | }) 111 | --------------------------------------------------------------------------------