124 lines
4.9 KiB
TypeScript
124 lines
4.9 KiB
TypeScript
// Copyright (C) 2016 Dmitry Chestnykh
|
|
// MIT License. See LICENSE file for details.
|
|
|
|
import { describe, expect, it } from 'vitest';
|
|
import { XChaCha20Poly1305 } from "./xchacha20poly1305.js";
|
|
import { encode, decode } from "@stablelib/hex";
|
|
|
|
const testVectors = [
|
|
/**
|
|
* Test vector from draft-irtf-cfrg-xchacha
|
|
* see https://tools.ietf.org/html/draft-irtf-cfrg-xchacha-01#appendix-A.3.1
|
|
* tag value is appended to the result (aka ciphertext)
|
|
*/
|
|
{
|
|
key: "808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F",
|
|
nonce: "404142434445464748494a4b4c4d4e4f5051525354555657",
|
|
aad: "50515253C0C1C2C3C4C5C6C7",
|
|
plaintext:
|
|
"4C616469657320616E642047656E746C656D656E206F662074686520636C617373206F66" +
|
|
"202739393A204966204920636F756C64206F6666657220796F75206F6E6C79206F6E6520" +
|
|
"74697020666F7220746865206675747572652C2073756E73637265656E20776F756C6420" +
|
|
"62652069742E",
|
|
result:
|
|
"BD6D179D3E83D43B9576579493C0E939" +
|
|
"572A1700252BFACCBED2902C21396CBB" +
|
|
"731C7F1B0B4AA6440BF3A82F4EDA7E39" +
|
|
"AE64C6708C54C216CB96B72E1213B452" +
|
|
"2F8C9BA40DB5D945B11B69B982C1BB9E" +
|
|
"3F3FAC2BC369488F76B2383565D3FFF9" +
|
|
"21F9664C97637DA9768812F615C68B13" +
|
|
"B52EC0875924C1C7987947DEAFD8780A" +
|
|
"CF49",
|
|
}
|
|
];
|
|
|
|
// TODO(dchest): add more various tests.
|
|
|
|
describe("XChaCha20Poly1305", () => {
|
|
it("should correctly seal", () => {
|
|
testVectors.forEach(v => {
|
|
const aead = new XChaCha20Poly1305(decode(v.key));
|
|
const sealed = aead.seal(decode(v.nonce), decode(v.plaintext), v.aad ? decode(v.aad) : undefined);
|
|
expect(encode(sealed)).toBe(v.result);
|
|
});
|
|
});
|
|
|
|
it("should correctly open", () => {
|
|
testVectors.forEach(v => {
|
|
const aead = new XChaCha20Poly1305(decode(v.key));
|
|
const plaintext = aead.open(decode(v.nonce), decode(v.result), v.aad ? decode(v.aad) : undefined);
|
|
expect(plaintext).not.toBeNull();
|
|
if (plaintext) {
|
|
expect(encode(plaintext)).toBe(v.plaintext);
|
|
}
|
|
});
|
|
});
|
|
|
|
it("should not open when ciphertext is corrupted", () => {
|
|
const v = testVectors[0];
|
|
const sealed = decode(v.result);
|
|
sealed[0] ^= sealed[0];
|
|
const aead = new XChaCha20Poly1305(decode(v.key));
|
|
const plaintext = aead.open(decode(v.nonce), sealed, v.aad ? decode(v.aad) : undefined);
|
|
expect(plaintext).toBeNull();
|
|
});
|
|
|
|
it("should not open when tag is corrupted", () => {
|
|
const v = testVectors[0];
|
|
const sealed = decode(v.result);
|
|
sealed[sealed.length - 1] ^= sealed[sealed.length - 1];
|
|
const aead = new XChaCha20Poly1305(decode(v.key));
|
|
const plaintext = aead.open(decode(v.nonce), sealed, v.aad ? decode(v.aad) : undefined);
|
|
expect(plaintext).toBeNull();
|
|
});
|
|
|
|
it("should seal to dst it is provided", () => {
|
|
const v = testVectors[0];
|
|
const aead = new XChaCha20Poly1305(decode(v.key));
|
|
const plaintext = decode(v.plaintext);
|
|
const ad = v.aad ? decode(v.aad) : undefined;
|
|
const dst = new Uint8Array(plaintext.length + aead.tagLength);
|
|
const sealed = aead.seal(decode(v.nonce), decode(v.plaintext), ad, dst);
|
|
expect(encode(dst)).toBe(encode(sealed));
|
|
expect(encode(sealed)).toBe(v.result);
|
|
});
|
|
|
|
it("should throw if seal got dst of wrong length", () => {
|
|
const v = testVectors[0];
|
|
const aead = new XChaCha20Poly1305(decode(v.key));
|
|
const plaintext = decode(v.plaintext);
|
|
const ad = v.aad ? decode(v.aad) : undefined;
|
|
const dst = new Uint8Array(plaintext.length + aead.tagLength - 1); // wrong length
|
|
expect(() =>
|
|
aead.seal(decode(v.nonce), decode(v.plaintext), ad, dst)
|
|
).toThrowError(/length/);
|
|
});
|
|
|
|
it("should open to dst it is provided", () => {
|
|
const v = testVectors[0];
|
|
const aead = new XChaCha20Poly1305(decode(v.key));
|
|
const sealed = decode(v.result);
|
|
const ad = v.aad ? decode(v.aad) : undefined;
|
|
const dst = new Uint8Array(sealed.length - aead.tagLength);
|
|
const plaintext = aead.open(decode(v.nonce), decode(v.result), ad, dst);
|
|
expect(plaintext).not.toBeNull();
|
|
if (plaintext) {
|
|
expect(encode(dst)).toBe(encode(plaintext));
|
|
expect(encode(plaintext)).toBe(v.plaintext);
|
|
}
|
|
});
|
|
|
|
it("should throw if open got dst of wrong length", () => {
|
|
const v = testVectors[0];
|
|
const aead = new XChaCha20Poly1305(decode(v.key));
|
|
const sealed = decode(v.result);
|
|
const ad = v.aad ? decode(v.aad) : undefined;
|
|
const dst = new Uint8Array(sealed.length - aead.tagLength + 1); // wrong length
|
|
expect(() =>
|
|
aead.open(decode(v.nonce), decode(v.result), ad, dst)
|
|
).toThrowError(/length/);
|
|
});
|
|
});
|
|
|