Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions wrappers/wasm/tests/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,35 @@
# devolutions-crypto
[![Build Status](https://dev.azure.com/devolutions-net/Open%20Source/_apis/build/status/devolutions-crypto?branchName=master)](https://dev.azure.com/devolutions-net/Open%20Source/_build/latest?definitionId=170&branchName=master) [![npm version](https://img.shields.io/npm/v/devolutions-crypto.svg?style=flat)](https://npmjs.org/package/devolutions-crypto "View this project on npm")

This folder contains the Javascript/Typescript unit tests for the library. You can also use them as usage example.
This folder contains the TypeScript unit tests for the library. You can also use them as usage examples.

## Test Framework

Tests use the **Node.js native test runner** with TypeScript support via `tsx`. This provides a modern, zero-dependency testing solution with built-in assertions.

## Build Native Library

# Build native library
```bash
cd ..
./wasm_build.sh
```

# Run tests
## Run Tests

```bash
# Run all tests
npm test
```

# Run tests in watch mode
npm run test:watch
```

## Test Files

- `asymmetric.ts` - Asymmetric encryption and key exchange tests
- `conformity.ts` - Cross-language compatibility tests
- `hashing.ts` - Password hashing tests
- `secret-sharing.ts` - Shamir's Secret Sharing tests
- `signature.ts` - Digital signature tests
- `symmetric.ts` - Symmetric encryption tests
- `utils.ts` - Key generation, derivation, and encoding tests
5,166 changes: 180 additions & 4,986 deletions wrappers/wasm/tests/package-lock.json

Large diffs are not rendered by default.

20 changes: 4 additions & 16 deletions wrappers/wasm/tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
"name": "devolutions-crypto-tests",
"version": "1.0.0",
"description": "Tests for the devolutions cryptographic library",
"main": "./src/bootstrap.js",
"type": "module",
"scripts": {
"test": "mocha -r tsx tests/**.ts",
"fmt": "eslint tests/**.ts --fix"
"test": "tsx --test tests/asymmetric.ts tests/conformity.ts tests/hashing.ts tests/secret-sharing.ts tests/signature.ts tests/symmetric.ts tests/utils.ts",
"test:watch": "tsx --test --watch tests/*.ts"
},
"repository": {
"type": "git",
Expand All @@ -18,20 +18,8 @@
},
"homepage": "https://github.com/Devolutions/devolutions-crypto#readme",
"devDependencies": {
"@types/chai": "^5.2.0",
"@types/jest": "^29.5.14",
"@types/mocha": "^10.0.10",
"@typescript-eslint/eslint-plugin": "^2.23.0",
"@typescript-eslint/parser": "^2.23.0",
"chai": "^5.2.0",
"@types/node": "^22.10.0",
"devolutions-crypto": "file:../dist/node/",
"eslint": "^6.8.0",
"eslint-config-standard": "^14.1.0",
"eslint-plugin-import": "^2.20.1",
"eslint-plugin-node": "^11.0.0",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-standard": "^4.0.1",
"mocha": "^11.1.0",
"tsx": "^4.19.3",
"typescript": "^5.8.2"
}
Expand Down
64 changes: 32 additions & 32 deletions wrappers/wasm/tests/tests/asymmetric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,66 +2,66 @@ import {
KeyPair, PublicKey, PrivateKey, Argon2Parameters,
generateKey, generateKeyPair, encryptAsymmetric, decryptAsymmetric, mixKeyExchange
} from 'devolutions-crypto'
import { expect } from 'chai'
import { describe, it } from 'mocha'
import { describe, test } from 'node:test'
import assert from 'node:assert/strict'

const encoder: TextEncoder = new TextEncoder()

describe('generateKeyPair', () => {
it('should generate a random keypair', () => {
test('should generate a random keypair', () => {
const keypair: KeyPair = generateKeyPair()
expect(keypair.private.bytes).to.not.have.lengthOf(0)
expect(keypair.public.bytes).to.not.have.lengthOf(0)
expect(keypair.private).to.not.eql(keypair.public)
assert.notStrictEqual(keypair.private.bytes.length, 0)
assert.notStrictEqual(keypair.public.bytes.length, 0)
assert.notDeepStrictEqual(keypair.private, keypair.public)
})
})

describe('asymmetricEncrypt/asymmetricDecrypt', () => {
it('should be able to encrypt and decrypt', () => {
test('should be able to encrypt and decrypt', () => {
const input: Uint8Array = encoder.encode('This is some test data')
const keypair: KeyPair = generateKeyPair()
const encrypted: Uint8Array = encryptAsymmetric(input, keypair.public)
const decrypted: Uint8Array = decryptAsymmetric(encrypted, keypair.private)
expect(encrypted).to.not.contains(input)
expect(decrypted).to.eql(input)
assert.notDeepStrictEqual(encrypted, input)
assert.deepStrictEqual(decrypted, input)
})

it('should be able to encrypt and decrypt with an AAD', () => {
test('should be able to encrypt and decrypt with an AAD', () => {
const input: Uint8Array = encoder.encode('This is some test data')
const aad: Uint8Array = encoder.encode('This is some public data')
const keypair: KeyPair = generateKeyPair()
const encrypted: Uint8Array = encryptAsymmetric(input, keypair.public, aad)
const decrypted: Uint8Array = decryptAsymmetric(encrypted, keypair.private, aad)
expect(encrypted).to.not.contains(input)
expect(decrypted).to.eql(input)
assert.notDeepStrictEqual(encrypted, input)
assert.deepStrictEqual(decrypted, input)
})

it('should fail if AAD is invalid', () => {
test('should fail if AAD is invalid', () => {
const input: Uint8Array = encoder.encode('This is some test data')
const aad: Uint8Array = encoder.encode('This is some public data')
const wrongAad: Uint8Array = encoder.encode('this is some public data')
const keypair: KeyPair = generateKeyPair()
const encrypted: Uint8Array = encryptAsymmetric(input, keypair.public, aad)

expect(() => decryptAsymmetric(encrypted, keypair.private)).to.throw()
expect(() => decryptAsymmetric(encrypted, keypair.private, wrongAad)).to.throw()
assert.throws(() => decryptAsymmetric(encrypted, keypair.private))
assert.throws(() => decryptAsymmetric(encrypted, keypair.private, wrongAad))
})
})

describe('mixKeyExchange', () => {
it('should give the same 32 byte shared key', () => {
test('should give the same 32 byte shared key', () => {
const bobKeyPair: KeyPair = generateKeyPair()
const aliceKeyPair: KeyPair = generateKeyPair()

const bobShared: Uint8Array = mixKeyExchange(bobKeyPair.private, aliceKeyPair.public)
const aliceShared: Uint8Array = mixKeyExchange(aliceKeyPair.private, bobKeyPair.public)

expect(bobShared).to.have.lengthOf(32)
expect(bobShared).to.not.eql(new Array(32).fill(0))
expect(bobShared).to.eql(aliceShared)
assert.strictEqual(bobShared.length, 32)
assert.notDeepStrictEqual(bobShared, new Uint8Array(32))
assert.deepStrictEqual(bobShared, aliceShared)
})

it('should not give the same 32 byte shared key', () => {
test('should not give the same 32 byte shared key', () => {
const bobKeyPair: KeyPair = generateKeyPair()
const aliceKeyPair: KeyPair = generateKeyPair()
const eveKeyPair: KeyPair = generateKeyPair()
Expand All @@ -72,36 +72,36 @@ describe('mixKeyExchange', () => {
const eveBobShared: Uint8Array = mixKeyExchange(eveKeyPair.private, bobKeyPair.public)
const eveAliceShared: Uint8Array = mixKeyExchange(eveKeyPair.private, aliceKeyPair.public)

expect(eveBobShared).to.not.eql(bobShared)
expect(eveBobShared).to.not.eql(aliceShared)
expect(eveAliceShared).to.not.eql(bobShared)
expect(eveAliceShared).to.not.eql(aliceShared)
assert.notDeepStrictEqual(eveBobShared, bobShared)
assert.notDeepStrictEqual(eveBobShared, aliceShared)
assert.notDeepStrictEqual(eveAliceShared, bobShared)
assert.notDeepStrictEqual(eveAliceShared, aliceShared)
})
})

describe('KeyPair serialization', () => {
it('should return the same keypair', () => {
test('should return the same keypair', () => {
const keypair = generateKeyPair()
const privateKeyBytes: Uint8Array = keypair.private.bytes
const publicKeyBytes: Uint8Array = keypair.public.bytes

const privateKey: PrivateKey = PrivateKey.fromBytes(privateKeyBytes)
const publicKey: PublicKey = PublicKey.fromBytes(publicKeyBytes)

expect(privateKey.bytes).to.eql(privateKeyBytes)
expect(publicKey.bytes).to.eql(publicKeyBytes)
assert.deepStrictEqual(privateKey.bytes, privateKeyBytes)
assert.deepStrictEqual(publicKey.bytes, publicKeyBytes)
})

it('should not allow to parse a public key as a private key and vis-versa', () => {
test('should not allow to parse a public key as a private key and vis-versa', () => {
const keypair = generateKeyPair()
const privateKeyBytes: Uint8Array = keypair.private.bytes
const publicKeyBytes: Uint8Array = keypair.public.bytes

const symmetricKey: Uint8Array = generateKey()

expect(() => PrivateKey.fromBytes(publicKeyBytes)).to.throw()
expect(() => PublicKey.fromBytes(privateKeyBytes)).to.throw()
expect(() => PrivateKey.fromBytes(symmetricKey)).to.throw()
expect(() => PublicKey.fromBytes(symmetricKey)).to.throw()
assert.throws(() => PrivateKey.fromBytes(publicKeyBytes))
assert.throws(() => PublicKey.fromBytes(privateKeyBytes))
assert.throws(() => PrivateKey.fromBytes(symmetricKey))
assert.throws(() => PublicKey.fromBytes(symmetricKey))
})
})
52 changes: 26 additions & 26 deletions wrappers/wasm/tests/tests/conformity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,98 +2,98 @@
import {
KeyPair, deriveKeyPbkdf2, base64encode, base64decode, decrypt, Argon2Parameters, PrivateKey, SigningPublicKey, decryptAsymmetric, verifyPassword, verifySignature, deriveKeyArgon2
} from 'devolutions-crypto'
import { expect } from 'chai'
import { describe, it } from 'mocha'
import { describe, test } from 'node:test'
import assert from 'node:assert/strict'

const encoder: TextEncoder = new TextEncoder()
const decoder: TextDecoder = new TextDecoder()

describe('Conformity Tests', () => {
it('Key Derivation PBKDF2', () => {
test('Key Derivation PBKDF2', () => {
const derivedKey: Uint8Array = deriveKeyPbkdf2(encoder.encode('testpassword'))
const derivedKeyWithIterations: Uint8Array = deriveKeyPbkdf2(encoder.encode('testPa$$'), null, 100)
const derivedKeyWithSalt: Uint8Array = deriveKeyPbkdf2(encoder.encode('testPa$$'), base64decode('tdTt5wgeqQYLvkiXKkFirqy2hMbzadBtL+jekVeNCRA='), 100)

expect(base64encode(derivedKey)).to.eql('ImfGCyv6PwMYaJShGxR4MfVrjuUrsI0CSarJgOApwf8=')
expect(base64encode(derivedKeyWithIterations)).to.eql('ev/GiJLvOgIkkWrnIrHSi2fdZE5qJBIrW+DLeMLIXK4=')
expect(base64encode(derivedKeyWithSalt)).to.eql('ZaYRZeQiIPJ+Jl511AgHZjv4/HbCFq4eUP9yNa3gowI=')
assert.strictEqual(base64encode(derivedKey), 'ImfGCyv6PwMYaJShGxR4MfVrjuUrsI0CSarJgOApwf8=')
assert.strictEqual(base64encode(derivedKeyWithIterations), 'ev/GiJLvOgIkkWrnIrHSi2fdZE5qJBIrW+DLeMLIXK4=')
assert.strictEqual(base64encode(derivedKeyWithSalt), 'ZaYRZeQiIPJ+Jl511AgHZjv4/HbCFq4eUP9yNa3gowI=')
})

it('Key Derivation Argon2', () => {
test('Key Derivation Argon2', () => {
const parameters: Argon2Parameters = Argon2Parameters.fromBytes(base64decode('AQAAACAAAAABAAAAIAAAAAEAAAACEwAAAAAQAAAAimFBkm3f8+f+YfLRnF5OoQ=='))
const result: Uint8Array = deriveKeyArgon2(encoder.encode('password'), parameters)

expect(base64encode(result)).to.eql('AcEN6Cb1Om6tomZScAM725qiXMzaxaHlj3iMiT/Ukq0=')
assert.strictEqual(base64encode(result), 'AcEN6Cb1Om6tomZScAM725qiXMzaxaHlj3iMiT/Ukq0=')
})

it('Symmetric Decrypt V1', () => {
test('Symmetric Decrypt V1', () => {
const key: Uint8Array = base64decode('ozJVEme4+5e/4NG3C+Rl26GQbGWAqGc0QPX8/1xvaFM=')
const ciphertext: Uint8Array = base64decode('DQwCAAAAAQCK1twEut+TeJfFbTWCRgHjyS6bOPOZUEQAeBtSFFRl2jHggM/34n68zIZWGbsZHkufVzU6mTN5N2Dx9bTplrycv5eNVevT4P9FdVHJ751D+A==')

const decrypted: Uint8Array = decrypt(ciphertext, key)

expect(decoder.decode(decrypted)).to.eql('test Ciph3rtext~')
assert.strictEqual(decoder.decode(decrypted), 'test Ciph3rtext~')
})

it('Symmetric Decrypt with AAD V1', () => {
test('Symmetric Decrypt with AAD V1', () => {
const key: Uint8Array = base64decode('ozJVEme4+5e/4NG3C+Rl26GQbGWAqGc0QPX8/1xvaFM=')
const ciphertext: Uint8Array = base64decode('DQwCAAEAAQCeKfbTqYjfVCEPEiAJjiypBstPmZz0AnpliZKoR+WXTKdj2f/4ops0++dDBVZ+XdyE1KfqxViWVc9djy/HSCcPR4nDehtNI69heGCIFudXfQ==')
const aad: Uint8Array = encoder.encode('this is some public data')

const decrypted: Uint8Array = decrypt(ciphertext, key, aad)

expect(decoder.decode(decrypted)).to.eql('test Ciph3rtext~')
assert.strictEqual(decoder.decode(decrypted), 'test Ciph3rtext~')
})

it('Symmetric Decrypt V2', () => {
test('Symmetric Decrypt V2', () => {
const key: Uint8Array = base64decode('ozJVEme4+5e/4NG3C+Rl26GQbGWAqGc0QPX8/1xvaFM=')
const ciphertext: Uint8Array = base64decode('DQwCAAAAAgAA0iPpI4IEzcrWAQiy6tqDqLbRYduGvlMC32mVH7tpIN2CXDUu5QHF91I7pMrmjt/61pm5CeR/IcU=')

const decrypted: Uint8Array = decrypt(ciphertext, key)

expect(decoder.decode(decrypted)).to.eql('test Ciph3rtext~2')
assert.strictEqual(decoder.decode(decrypted), 'test Ciph3rtext~2')
})

it('Symmetric Decrypt with AAD V2', () => {
test('Symmetric Decrypt with AAD V2', () => {
const key: Uint8Array = base64decode('ozJVEme4+5e/4NG3C+Rl26GQbGWAqGc0QPX8/1xvaFM=')
const ciphertext: Uint8Array = base64decode('DQwCAAEAAgA9bh989dao0Pvaz1NpJTI5m7M4br2qVjZtFwXXoXZOlkCjtqU/uif4pbNCcpEodzeP4YG1QvfKVQ==')
const aad: Uint8Array = encoder.encode('this is some public data')

const decrypted: Uint8Array = decrypt(ciphertext, key, aad)

expect(decoder.decode(decrypted)).to.eql('test Ciph3rtext~')
assert.strictEqual(decoder.decode(decrypted), 'test Ciph3rtext~')
})

it('Asymmetric Decrypt V2', () => {
test('Asymmetric Decrypt V2', () => {
const privateKey: PrivateKey = PrivateKey.fromBytes(base64decode('DQwBAAEAAQAAwQ3oJvU6bq2iZlJwAzvbmqJczNrFoeWPeIyJP9SSbQ=='))
const result: Uint8Array = decryptAsymmetric(base64decode('DQwCAAIAAgCIG9L2MTiumytn7H/p5I3aGVdhV3WUL4i8nIeMWIJ1YRbNQ6lEiQDAyfYhbs6gg1cD7+5Ft2Q5cm7ArsGfiFYWnscm1y7a8tAGfjFFTonzrg=='), privateKey)

expect(decoder.decode(result)).to.eql('testdata')
assert.strictEqual(decoder.decode(result), 'testdata')
})

it('Asymmetric Decrypt V2 with AAD', () => {
test('Asymmetric Decrypt V2 with AAD', () => {
const privateKey: PrivateKey = PrivateKey.fromBytes(base64decode('DQwBAAEAAQC9qf9UY1ovL/48ALGHL9SLVpVozbdjYsw0EPerUl3zYA=='))
const aad: Uint8Array = encoder.encode('this is some public data')

const result: Uint8Array = decryptAsymmetric(base64decode('DQwCAAIAAgB1u62xYeyppWf83QdWwbwGUt5QuiAFZr+hIiFEvMRbXiNCE3RMBNbmgQkLr/vME0BeQa+uUTXZARvJcyNXHyAE4tSdw6o/psU/kw/Z/FbsPw=='), privateKey, aad)

expect(decoder.decode(result)).to.eql('testdata')
assert.strictEqual(decoder.decode(result), 'testdata')
})

it('Password Hashing V1', () => {
test('Password Hashing V1', () => {
const hash1: Uint8Array = base64decode('DQwDAAAAAQAQJwAAXCzLFoyeZhFSDYBAPiIWhCk04aoP/lalOoCl7D+skIY/i+3WT7dn6L8WvnfEq6flCd7i+IcKb3GEK4rCpzhDlw==')
const hash2: Uint8Array = base64decode('DQwDAAAAAQAKAAAAmH1BBckBJYDD0xfiwkAk1xwKgw8a57YQT0Igm+Faa9LFamTeEJgqn/qHc2R/8XEyK2iLPkVy+IErdGLLtLKJ2g==')
expect(verifyPassword(encoder.encode('password1'), hash1)).to.eql(true)
expect(verifyPassword(encoder.encode('password1'), hash2)).to.eql(true)
assert.strictEqual(verifyPassword(encoder.encode('password1'), hash1), true)
assert.strictEqual(verifyPassword(encoder.encode('password1'), hash2), true)
})

it('Signature V1', () => {
test('Signature V1', () => {
const public_key_bytes: Uint8Array = base64decode('DQwFAAIAAQDeEvwlEigK5AXoTorhmlKP6+mbiUU2rYrVQ25JQ5xang==')
const signature: Uint8Array = base64decode('DQwGAAAAAQD82uRk4sFC8vEni6pDNw/vOdN1IEDg9cAVfprWJZ/JBls9Gi61cUt5u6uBJtseNGZFT7qKLvp4NUZrAOL8FH0K')

const public_key = SigningPublicKey.fromBytes(public_key_bytes);

expect(verifySignature(encoder.encode('this is a test'), public_key, signature)).to.eql(true)
expect(verifySignature(encoder.encode('this is wrong'), public_key, signature)).to.eql(false)
assert.strictEqual(verifySignature(encoder.encode('this is a test'), public_key, signature), true)
assert.strictEqual(verifySignature(encoder.encode('this is wrong'), public_key, signature), false)
})
})
16 changes: 8 additions & 8 deletions wrappers/wasm/tests/tests/hashing.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import { hashPassword, verifyPassword } from 'devolutions-crypto'
import { expect } from 'chai'
import { describe, it } from 'mocha'
import { describe, test } from 'node:test'
import assert from 'node:assert/strict'

const encoder: TextEncoder = new TextEncoder()

describe('hashing', () => {
it('should validate the password', () => {
test('should validate the password', () => {
const hash: Uint8Array = hashPassword(encoder.encode('password'), 10)

expect(verifyPassword(encoder.encode('password'), hash)).to.eql(true)
assert.strictEqual(verifyPassword(encoder.encode('password'), hash), true)
})

it('should not validate the password', () => {
test('should not validate the password', () => {
const hash: Uint8Array = hashPassword(encoder.encode('password'), 10)

expect(verifyPassword(encoder.encode('pa$$word'), hash)).to.eql(false)
expect(verifyPassword(encoder.encode('Password'), hash)).to.eql(false)
expect(verifyPassword(encoder.encode('password1'), hash)).to.eql(false)
assert.strictEqual(verifyPassword(encoder.encode('pa$$word'), hash), false)
assert.strictEqual(verifyPassword(encoder.encode('Password'), hash), false)
assert.strictEqual(verifyPassword(encoder.encode('password1'), hash), false)
})
})
Loading