diff --git a/packages/sncrypto-common/package.json b/packages/sncrypto-common/package.json index 5052b1464..e674ec1d1 100644 --- a/packages/sncrypto-common/package.json +++ b/packages/sncrypto-common/package.json @@ -26,7 +26,8 @@ "devDependencies": { "@typescript-eslint/eslint-plugin": "^5.30.0", "@typescript-eslint/parser": "^5.12.1", - "eslint-plugin-prettier": "^4.2.1" + "eslint-plugin-prettier": "^4.2.1", + "typescript": "*" }, "dependencies": { "reflect-metadata": "^0.1.13" diff --git a/packages/sncrypto-common/src/Types/PkcKeyPair.ts b/packages/sncrypto-common/src/Types/PkcKeyPair.ts new file mode 100644 index 000000000..59ecd59bc --- /dev/null +++ b/packages/sncrypto-common/src/Types/PkcKeyPair.ts @@ -0,0 +1,7 @@ +import { HexString } from './HexString' + +export type PkcKeyPair = { + keyType: 'curve25519' | 'ed25519' | 'x25519' + privateKey: HexString + publicKey: HexString +} diff --git a/packages/sncrypto-common/src/Types/index.ts b/packages/sncrypto-common/src/Types/index.ts index 7d4470971..2f65faabb 100644 --- a/packages/sncrypto-common/src/Types/index.ts +++ b/packages/sncrypto-common/src/Types/index.ts @@ -8,3 +8,4 @@ export * from './StreamDecryptorResult' export * from './StreamEncryptor' export * from './Unencrypted' export * from './Utf8String' +export * from './PkcKeyPair' diff --git a/packages/sncrypto-web/package.json b/packages/sncrypto-web/package.json index 2ee395b2f..20336cceb 100644 --- a/packages/sncrypto-web/package.json +++ b/packages/sncrypto-web/package.json @@ -39,11 +39,13 @@ "babel-loader": "^8.2.3", "chai": "^4.3.6", "connect": "^3.7.0", + "eslint": "*", "eslint-plugin-prettier": "*", "regenerator-runtime": "^0.13.9", "serve-static": "^1.14.2", "ts-loader": "^9.2.6", "typedarray-to-buffer": "^4.0.0", + "typescript": "*", "uuid": "^8.3.2", "webpack": "*", "webpack-cli": "*" diff --git a/packages/sncrypto-web/src/crypto.ts b/packages/sncrypto-web/src/crypto.ts index cb1b793df..2114409c2 100644 --- a/packages/sncrypto-web/src/crypto.ts +++ b/packages/sncrypto-web/src/crypto.ts @@ -9,6 +9,7 @@ import { PureCryptoInterface, Utf8String, timingSafeEqual, + PkcKeyPair, } from '@standardnotes/sncrypto-common' import * as Utils from './utils' import * as sodium from './libsodium' @@ -83,7 +84,7 @@ export class SNWebCrypto implements PureCryptoInterface { iterations: number, length: number, ): Promise { - const keyData = await Utils.stringToArrayBuffer(password) + const keyData = Utils.stringToArrayBuffer(password) const key = await this.webCryptoImportKey(keyData, WebCryptoAlgs.Pbkdf2, [WebCryptoActions.DeriveBits]) if (!key) { console.error('Key is null, unable to continue') @@ -99,21 +100,21 @@ export class SNWebCrypto implements PureCryptoInterface { } public async aes256CbcEncrypt(plaintext: Utf8String, iv: HexString, key: HexString): Promise { - const keyData = await Utils.hexStringToArrayBuffer(key) - const ivData = await Utils.hexStringToArrayBuffer(iv) + const keyData = Utils.hexStringToArrayBuffer(key) + const ivData = Utils.hexStringToArrayBuffer(iv) const alg = { name: WebCryptoAlgs.AesCbc, iv: ivData } const importedKeyData = await this.webCryptoImportKey(keyData, alg.name, [WebCryptoActions.Encrypt]) - const textData = await Utils.stringToArrayBuffer(plaintext) + const textData = Utils.stringToArrayBuffer(plaintext) const result = await crypto.subtle.encrypt(alg, importedKeyData, textData) return Utils.arrayBufferToBase64(result) } public async aes256CbcDecrypt(ciphertext: Base64String, iv: HexString, key: HexString): Promise { - const keyData = await Utils.hexStringToArrayBuffer(key) - const ivData = await Utils.hexStringToArrayBuffer(iv) + const keyData = Utils.hexStringToArrayBuffer(key) + const ivData = Utils.hexStringToArrayBuffer(iv) const alg = { name: WebCryptoAlgs.AesCbc, iv: ivData } const importedKeyData = await this.webCryptoImportKey(keyData, alg.name, [WebCryptoActions.Decrypt]) - const textData = await Utils.base64ToArrayBuffer(ciphertext) + const textData = Utils.base64ToArrayBuffer(ciphertext) try { const result = await crypto.subtle.decrypt(alg, importedKeyData, textData) @@ -150,11 +151,11 @@ export class SNWebCrypto implements PureCryptoInterface { } public async hmac1(message: Utf8String, key: HexString): Promise { - const keyHexData = await Utils.hexStringToArrayBuffer(key) + const keyHexData = Utils.hexStringToArrayBuffer(key) const keyData = await this.webCryptoImportKey(keyHexData, WebCryptoAlgs.Hmac, [WebCryptoActions.Sign], { name: WebCryptoAlgs.Sha1, }) - const messageData = await Utils.stringToArrayBuffer(message) + const messageData = Utils.stringToArrayBuffer(message) const funcParams = { name: WebCryptoAlgs.Hmac } try { @@ -169,15 +170,14 @@ export class SNWebCrypto implements PureCryptoInterface { } public async unsafeSha1(text: string): Promise { - const textData = await Utils.stringToArrayBuffer(text) + const textData = Utils.stringToArrayBuffer(text) const digest = await crypto.subtle.digest(WebCryptoAlgs.Sha1, textData) return Utils.arrayBufferToHexString(digest) } /** * Converts a raw string key to a WebCrypto CryptoKey object. - * @param rawKey - * A plain utf8 string or an array buffer + * @param keyData * @param alg * The name of the algorithm this key will be used for (i.e 'AES-CBC' or 'HMAC') * @param actions @@ -220,7 +220,7 @@ export class SNWebCrypto implements PureCryptoInterface { ): Promise { const params = { name: WebCryptoAlgs.Pbkdf2, - salt: await Utils.stringToArrayBuffer(salt), + salt: Utils.stringToArrayBuffer(salt), iterations: iterations, hash: { name: WebCryptoAlgs.Sha512 }, } @@ -298,13 +298,13 @@ export class SNWebCrypto implements PureCryptoInterface { public xchacha20StreamEncryptorPush( encryptor: StreamEncryptor, plainBuffer: Uint8Array, - assocData: Utf8String, + assocData?: Utf8String, tag: SodiumConstant = SodiumConstant.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PUSH, ): Uint8Array { const encryptedBuffer = sodium.crypto_secretstream_xchacha20poly1305_push( encryptor.state as sodium.StateAddress, plainBuffer, - assocData.length > 0 ? Utils.stringToArrayBuffer(assocData) : null, + assocData && assocData.length > 0 ? Utils.stringToArrayBuffer(assocData) : null, tag, ) return encryptedBuffer @@ -325,7 +325,7 @@ export class SNWebCrypto implements PureCryptoInterface { public xchacha20StreamDecryptorPush( decryptor: StreamDecryptor, encryptedBuffer: Uint8Array, - assocData: Utf8String, + assocData?: Utf8String, ): StreamDecryptorResult | false { if (encryptedBuffer.length < SodiumConstant.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES) { throw new Error('Invalid ciphertext size') @@ -334,7 +334,7 @@ export class SNWebCrypto implements PureCryptoInterface { const result = sodium.crypto_secretstream_xchacha20poly1305_pull( decryptor.state as sodium.StateAddress, encryptedBuffer, - assocData.length > 0 ? Utils.stringToArrayBuffer(assocData) : null, + assocData && assocData.length > 0 ? Utils.stringToArrayBuffer(assocData) : null, ) if ((result as unknown) === false) { @@ -344,6 +344,51 @@ export class SNWebCrypto implements PureCryptoInterface { return result } + /** + * https://doc.libsodium.org/public-key_cryptography/authenticated_encryption + */ + public sodiumCryptoBoxEasyEncrypt( + message: Utf8String, + nonce: HexString, + senderSecretKey: HexString, + recipientPublicKey: HexString, + ): Base64String { + const result = sodium.crypto_box_easy( + message, + Utils.hexStringToArrayBuffer(nonce), + Utils.hexStringToArrayBuffer(recipientPublicKey), + Utils.hexStringToArrayBuffer(senderSecretKey), + ) + + return Utils.arrayBufferToBase64(result) + } + + public sodiumCryptoBoxEasyDecrypt( + ciphertext: Base64String, + nonce: HexString, + senderPublicKey: HexString, + recipientSecretKey: HexString, + ): Base64String { + const result = sodium.crypto_box_open_easy( + Utils.base64ToArrayBuffer(ciphertext), + Utils.hexStringToArrayBuffer(nonce), + Utils.hexStringToArrayBuffer(senderPublicKey), + Utils.hexStringToArrayBuffer(recipientSecretKey), + 'text', + ) + + return result + } + + public sodiumCryptoBoxGenerateKeypair(): PkcKeyPair { + const result = sodium.crypto_box_keypair() + + const publicKey = Utils.arrayBufferToHexString(result.publicKey) + const privateKey = Utils.arrayBufferToHexString(result.privateKey) + + return { publicKey, privateKey, keyType: result.keyType } + } + /** * Generates a random secret for TOTP authentication * diff --git a/packages/sncrypto-web/src/index.ts b/packages/sncrypto-web/src/index.ts index b73dafe4a..ca2161138 100644 --- a/packages/sncrypto-web/src/index.ts +++ b/packages/sncrypto-web/src/index.ts @@ -1,16 +1,3 @@ -export { SNWebCrypto } from './crypto' -export { - arrayBufferToBase64, - arrayBufferToHexString, - arrayBufferToString, - base64Decode, - base64Encode, - base64ToArrayBuffer, - base64ToHex, - hexStringToArrayBuffer, - hexToBase64, - isWebCryptoAvailable, - stringToArrayBuffer, - base32Decode, - base32Encode, -} from './utils' +export * from './crypto' +export * from './utils' +export { SodiumConstant } from '@standardnotes/sncrypto-common' diff --git a/packages/sncrypto-web/src/libsodium.ts b/packages/sncrypto-web/src/libsodium.ts index ed1db2c7f..d39d4ff90 100644 --- a/packages/sncrypto-web/src/libsodium.ts +++ b/packages/sncrypto-web/src/libsodium.ts @@ -3,12 +3,15 @@ export { base64_variants, crypto_aead_xchacha20poly1305_ietf_decrypt, crypto_aead_xchacha20poly1305_ietf_encrypt, - crypto_secretstream_xchacha20poly1305_push, - crypto_secretstream_xchacha20poly1305_pull, - crypto_secretstream_xchacha20poly1305_init_push, - crypto_secretstream_xchacha20poly1305_init_pull, + crypto_box_easy, + crypto_box_keypair, + crypto_box_open_easy, crypto_pwhash_ALG_DEFAULT, crypto_pwhash, + crypto_secretstream_xchacha20poly1305_init_pull, + crypto_secretstream_xchacha20poly1305_init_push, + crypto_secretstream_xchacha20poly1305_pull, + crypto_secretstream_xchacha20poly1305_push, from_base64, from_hex, from_string, diff --git a/packages/sncrypto-web/test/crypto.test.js b/packages/sncrypto-web/test/crypto.test.js index a2c4d9f6a..25dde0ca8 100644 --- a/packages/sncrypto-web/test/crypto.test.js +++ b/packages/sncrypto-web/test/crypto.test.js @@ -50,31 +50,18 @@ describe('crypto operations', async function () { it('compares strings with timing safe comparison', async function () { const crypto = new SNWebCrypto() - expect(crypto.timingSafeEqual('hello world 🌍', 'hello world 🌍')).to.equal( - true, - ) - expect(crypto.timingSafeEqual('helo world 🌍', 'hello world 🌍')).to.equal( - false, - ) + expect(crypto.timingSafeEqual('hello world 🌍', 'hello world 🌍')).to.equal(true) + expect(crypto.timingSafeEqual('helo world 🌍', 'hello world 🌍')).to.equal(false) expect(crypto.timingSafeEqual('', 'a')).to.equal(false) expect(crypto.timingSafeEqual('', '')).to.equal(true) expect( - crypto.timingSafeEqual( - '2e1ee7920bb188a88f94bb912153befd83cc55cd', - '2e1ee7920bb188a88f94bb912153befd83cc55cd', - ), + crypto.timingSafeEqual('2e1ee7920bb188a88f94bb912153befd83cc55cd', '2e1ee7920bb188a88f94bb912153befd83cc55cd'), ).to.equal(true) expect( - crypto.timingSafeEqual( - '1e1ee7920bb188a88f94bb912153befd83cc55cd', - '2e1ee7920bb188a88f94bb912153befd83cc55cd', - ), + crypto.timingSafeEqual('1e1ee7920bb188a88f94bb912153befd83cc55cd', '2e1ee7920bb188a88f94bb912153befd83cc55cd'), ).to.equal(false) expect( - crypto.timingSafeEqual( - '2e1ee7920bb188a88f94bb912153befd83cc55cc', - '2e1ee7920bb188a88f94bb912153befd83cc55cd', - ), + crypto.timingSafeEqual('2e1ee7920bb188a88f94bb912153befd83cc55cc', '2e1ee7920bb188a88f94bb912153befd83cc55cd'), ).to.equal(false) }) @@ -85,22 +72,16 @@ describe('crypto operations', async function () { it('pbkdf2 1', async function () { const password = 'very_secureπŸ”’' - const salt = - 'c3feb78823adce65c4ab024dab9c5cdcda5a04cdbd98f65eac0311dfa432d67b' - const expected = - 'bbb3d3af19dd1cbb901c958003faa55f193aad6a57fff30e51a62591bdc054d8' + const salt = 'c3feb78823adce65c4ab024dab9c5cdcda5a04cdbd98f65eac0311dfa432d67b' + const expected = 'bbb3d3af19dd1cbb901c958003faa55f193aad6a57fff30e51a62591bdc054d8' const result = await webCrypto.pbkdf2(password, salt, 100000, 256) expect(result).to.equal(expected) }) it('pbkdf2 2', async function () { const password = 'correct horse battery staple βœ…' - const salt = Buffer.from( - '808182838485868788898a8b8c8d8e8f', - 'hex', - ).toString('utf8') - const expected = - '795d83b18e55d860d3799f85a20f66ee17eb9dcf041df1d7a13fac30af7103d9' + const salt = Buffer.from('808182838485868788898a8b8c8d8e8f', 'hex').toString('utf8') + const expected = '795d83b18e55d860d3799f85a20f66ee17eb9dcf041df1d7a13fac30af7103d9' const result = await webCrypto.pbkdf2(password, salt, 100000, 256) expect(result).to.equal(expected) }) @@ -116,19 +97,16 @@ describe('crypto operations', async function () { it('hmac 256', async function () { const text = 'hello world 🌍' - const key = - 'e802dc953f3f1f7b5db62409b74ac848559d4711c4e0047ecc5e312ad8ab8397' + const key = 'e802dc953f3f1f7b5db62409b74ac848559d4711c4e0047ecc5e312ad8ab8397' const hash = await webCrypto.hmac256(text, key) - const expected = - 'b63f94ee33a067ffac3ee97c7987dd3171dcdc747a322bb3f3ab890201c8e6f9' + const expected = 'b63f94ee33a067ffac3ee97c7987dd3171dcdc747a322bb3f3ab890201c8e6f9' expect(hash).to.equal(expected) }) it('sha256', async function () { const text = 'hello world 🌍' const hash = await webCrypto.sha256(text) - const expected = - '1e71fe32476da1ff115b44dfd74aed5c90d68a1d80a2033065e30cff4335211a' + const expected = '1e71fe32476da1ff115b44dfd74aed5c90d68a1d80a2033065e30cff4335211a' expect(hash).to.equal(expected) }) @@ -154,13 +132,7 @@ describe('crypto operations', async function () { const bytes = 67108864 const length = 16 const iterations = 2 - const result = await webCrypto.argon2( - password, - salt, - iterations, - bytes, - length, - ) + const result = await webCrypto.argon2(password, salt, iterations, bytes, length) const expectedResult = '18dfbc268f251701652c8e38b5273f73' expect(result).to.equal(expectedResult) }) @@ -172,15 +144,8 @@ describe('crypto operations', async function () { const bytes = 67108864 const length = 32 const iterations = 5 - const result = await webCrypto.argon2( - password, - truncatedSalt, - iterations, - bytes, - length, - ) - const expected = - 'bb6ec440708c271ce34decd7f997e2444d309b1105992779ccdb47f78a5fda6f' + const result = await webCrypto.argon2(password, truncatedSalt, iterations, bytes, length) + const expected = 'bb6ec440708c271ce34decd7f997e2444d309b1105992779ccdb47f78a5fda6f' expect(result).to.equal(expected) }) @@ -189,69 +154,38 @@ describe('crypto operations', async function () { const nonce = await webCrypto.generateRandomKey(192) const plaintext = 'hello world 🌍' const aad = JSON.stringify({ uuid: '123🎀' }) - const ciphertext = await webCrypto.xchacha20Encrypt( - plaintext, - nonce, - key, - aad, - ) - const decrypted = await webCrypto.xchacha20Decrypt( - ciphertext, - nonce, - key, - aad, - ) + const ciphertext = await webCrypto.xchacha20Encrypt(plaintext, nonce, key, aad) + const decrypted = await webCrypto.xchacha20Decrypt(ciphertext, nonce, key, aad) expect(decrypted).to.equal(plaintext) }) it('xchacha20 streaming encrypt/decrypt', async function () { const key = await webCrypto.generateRandomKey(256) - const bigFile = await fetch( - 'http://localhost:9003/test/resources/big_file.md', - ) + const bigFile = await fetch('http://localhost:9003/test/resources/big_file.md') const bigText = await bigFile.text() const plaintext = bigText const plainBuffer = stringToArrayBuffer(plaintext) - const encryptor = webCrypto.xchacha20StreamEncryptInitEncryptor(key) - const header = base64StringToArrayBuffer(encryptor.header) + const encryptor = webCrypto.xchacha20StreamInitEncryptor(key) - let encryptedBuffer = Buffer.concat([header]) + const headerBase64 = encryptor.header + const headerBuffer = base64ToArrayBuffer(encryptor.header) + + let encryptedBuffer = Buffer.concat([headerBuffer]) const pushChunkSize = plainBuffer.length / 200 - const pullChunkSize = - pushChunkSize + - SodiumConstant.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES + const pullChunkSize = pushChunkSize + SodiumConstant.CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES for (let i = 0; i < plainBuffer.length; i += pushChunkSize) { - const readUntil = - i + pushChunkSize > plainBuffer.length - ? plainBuffer.length - : i + pushChunkSize - const chunk = webCrypto.xchacha20StreamEncryptorPush( - encryptor, - plainBuffer.slice(i, readUntil), - ) + const readUntil = i + pushChunkSize > plainBuffer.length ? plainBuffer.length : i + pushChunkSize + const chunk = webCrypto.xchacha20StreamEncryptorPush(encryptor, plainBuffer.slice(i, readUntil)) encryptedBuffer = Buffer.concat([encryptedBuffer, chunk]) } - const decryptor = webCrypto.xchacha20StreamEncryptInitDecryptor( - header, - key, - ) + const decryptor = webCrypto.xchacha20StreamInitDecryptor(headerBase64, key) let decryptedBuffer = Buffer.alloc(0) - for ( - let i = header.length; - i < encryptedBuffer.length; - i += pullChunkSize - ) { - const readUntil = - i + pullChunkSize > encryptedBuffer.length - ? encryptedBuffer.length - : i + pullChunkSize - const chunk = webCrypto.xchacha20StreamDecryptorPush( - decryptor, - encryptedBuffer.slice(i, readUntil), - ) + for (let i = headerBuffer.length; i < encryptedBuffer.length; i += pullChunkSize) { + const readUntil = i + pullChunkSize > encryptedBuffer.length ? encryptedBuffer.length : i + pullChunkSize + const chunk = webCrypto.xchacha20StreamDecryptorPush(decryptor, encryptedBuffer.slice(i, readUntil)) decryptedBuffer = Buffer.concat([decryptedBuffer, chunk.message]) } @@ -263,18 +197,8 @@ describe('crypto operations', async function () { const key = await webCrypto.generateRandomKey(256) const nonce = await webCrypto.generateRandomKey(192) const plaintext = 'hello world 🌍' - const ciphertext = await webCrypto.xchacha20Encrypt( - plaintext, - nonce, - key, - JSON.stringify({ uuid: 'foo🎲' }), - ) - const result = await webCrypto.xchacha20Decrypt( - ciphertext, - nonce, - key, - JSON.stringify({ uuid: 'bar🎲' }), - ) + const ciphertext = await webCrypto.xchacha20Encrypt(plaintext, nonce, key, JSON.stringify({ uuid: 'foo🎲' })) + const result = await webCrypto.xchacha20Decrypt(ciphertext, nonce, key, JSON.stringify({ uuid: 'bar🎲' })) expect(result).to.not.be.ok }) @@ -284,23 +208,12 @@ describe('crypto operations', async function () { "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.🌞" const assocData = await hexStringToArrayBuffer('50515253c0c1c2c3c4c5c6c7') const nonce = '404142434445464748494a4b4c4d4e4f5051525354555657' - const key = - '808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f' - const ciphertext = await webCrypto.xchacha20Encrypt( - plaintext, - nonce, - key, - assocData, - ) + const key = '808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f' + const ciphertext = await webCrypto.xchacha20Encrypt(plaintext, nonce, key, assocData) const expected = 'vW0XnT6D1DuVdleUk8DpOVcqFwAlK/rMvtKQLCE5bLtzHH8bC0qmRAvzqC9O2n45rmTGcIxUwhbLlrcuEhO0Ui+Mm6QNtdlFsRtpuYLBu54/P6wrw2lIj3ayODVl0//5IflmTJdjfal2iBL2FcaLE7UuOqw6kdl7HV6PKzn0pIOeHH3rkwQ=' expect(ciphertext).to.equal(expected) - const decrypted = await webCrypto.xchacha20Decrypt( - ciphertext, - nonce, - key, - assocData, - ) + const decrypted = await webCrypto.xchacha20Decrypt(ciphertext, nonce, key, assocData) expect(decrypted).to.equal(plaintext) }) @@ -315,16 +228,10 @@ describe('crypto operations', async function () { ).toString('utf8') const assocData = Buffer.from('50515253c0c1c2c3c4c5c6c7', 'hex') const nonce = '404142434445464748494a4b4c4d4e4f5051525354555657' - const key = - '808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f' + const key = '808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f' /** Encrypt */ - const ciphertextBase64 = await webCrypto.xchacha20Encrypt( - plaintext, - nonce, - key, - assocData, - ) + const ciphertextBase64 = await webCrypto.xchacha20Encrypt(plaintext, nonce, key, assocData) const ciphertextHex = await base64ToHex(ciphertextBase64) const expected = 'bd6d179d3e83d43b9576579493c0e939572a1700252bfaccbed2902c21396cbb' + @@ -335,12 +242,7 @@ describe('crypto operations', async function () { expect(ciphertextHex).to.equal(expected) /** Decrypt */ - const decrypted = await webCrypto.xchacha20Decrypt( - ciphertextBase64, - nonce, - key, - assocData, - ) + const decrypted = await webCrypto.xchacha20Decrypt(ciphertextBase64, nonce, key, assocData) expect(decrypted).to.equal(plaintext) }) @@ -351,17 +253,44 @@ describe('crypto operations', async function () { const bytes = 67108864 const length = 16 const iterations = 2 - const result = await webCrypto.argon2( - password, - salt, - iterations, - bytes, - length, - ) + const result = await webCrypto.argon2(password, salt, iterations, bytes, length) const expectedResult = '720f95400220748a811bca9b8cff5d6e' expect(result).to.equal(expectedResult) }) + it('pkc crypto_box_easy keypair generation', async function () { + const keypair = await webCrypto.sodiumCryptoBoxGenerateKeypair() + expect(keypair.keyType).to.equal('x25519') + expect(keypair.publicKey.length).to.equal(64) + expect(keypair.privateKey.length).to.equal(64) + }) + + it('pkc crypto_box_easy encrypt/decrypt', async function () { + const senderKeypair = await webCrypto.sodiumCryptoBoxGenerateKeypair() + const recipientKeypair = await webCrypto.sodiumCryptoBoxGenerateKeypair() + + const nonce = await webCrypto.generateRandomKey(192) + const plaintext = 'hello world 🌍' + + const ciphertext = await webCrypto.sodiumCryptoBoxEasyEncrypt( + plaintext, + nonce, + senderKeypair.privateKey, + recipientKeypair.publicKey, + ) + + expect(ciphertext.length).to.equal(44) + + const decrypted = await webCrypto.sodiumCryptoBoxEasyDecrypt( + ciphertext, + nonce, + senderKeypair.publicKey, + recipientKeypair.privateKey, + ) + + expect(decrypted).to.equal(plaintext) + }) + it('generates random OTP secret 160 bits long', async function () { const secret = await webCrypto.generateOtpSecret() expect(secret).to.have.length(32) diff --git a/packages/sncrypto-web/test/test.html b/packages/sncrypto-web/test/test.html index e2e882358..b8d7b71a0 100644 --- a/packages/sncrypto-web/test/test.html +++ b/packages/sncrypto-web/test/test.html @@ -6,7 +6,7 @@ - +