summaryrefslogtreecommitdiff
path: root/tests/unit_node/crypto/crypto_cipher_gcm_test.ts
blob: b379a43696bf0bc63a3179cf804b93817f898128 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.

import crypto from "node:crypto";
import { Buffer } from "node:buffer";
import testVectors128 from "./gcmEncryptExtIV128.json" with { type: "json" };
import testVectors256 from "./gcmEncryptExtIV256.json" with { type: "json" };
import { assertEquals } from "@std/assert";

const aesGcm = (bits: string, key: Uint8Array) => {
  const ALGO = bits == "128" ? `aes-128-gcm` : `aes-256-gcm`;

  // encrypt returns base64-encoded ciphertext
  const encrypt = (
    iv: Uint8Array,
    str: string,
    aad: Uint8Array,
  ): [string, Buffer] => {
    const cipher = crypto.createCipheriv(ALGO, key, iv);
    cipher.setAAD(aad);
    let enc = cipher.update(str, "base64", "base64");
    enc += cipher.final("base64");
    return [enc, cipher.getAuthTag()];
  };

  const decrypt = (
    enc: string,
    iv: Uint8Array,
    aad: Uint8Array,
    authTag: Uint8Array,
  ) => {
    const decipher = crypto.createDecipheriv(ALGO, key, iv);
    decipher.setAuthTag(authTag);
    decipher.setAAD(aad);
    let str = decipher.update(enc, "base64", "base64");
    str += decipher.final("base64");

    return str;
  };

  return {
    encrypt,
    decrypt,
  };
};

type TestVector = {
  key: Uint8Array;
  nonce: Uint8Array;
  aad: Uint8Array;
  plaintext: string;
  ciphertext: string;
  tag: Uint8Array;
};

for (
  // NIST CAVS vectors
  const [bits, vectors] of Object.entries({
    // <https://csrc.nist.gov/Projects/cryptographic-algorithm-validation-program/CAVP-TESTING-BLOCK-CIPHER-MODES>
    //
    // From: `gcmEncryptExtIV128.rsp`
    128: testVectors128,
    // <https://csrc.nist.gov/Projects/cryptographic-algorithm-validation-program/CAVP-TESTING-BLOCK-CIPHER-MODES>
    //
    // From: `gcmEncryptExtIV256.rsp`
    256: testVectors256,
  })
) {
  for (let i = 0; i < vectors.length; i++) {
    const rawTest = vectors[i];
    const test: TestVector = {
      key: new Uint8Array(rawTest.key),
      nonce: new Uint8Array(rawTest.nonce),
      aad: new Uint8Array(rawTest.aad),
      plaintext: Buffer.from(rawTest.plaintext).toString("base64"),
      ciphertext: Buffer.from(rawTest.ciphertext).toString("base64"),
      tag: new Uint8Array(rawTest.tag),
    };

    Deno.test({
      name: `aes-${bits}-gcm encrypt ${i + 1}/${vectors.length}`,
      fn() {
        const cipher = aesGcm(bits, test.key);
        const [enc, tag] = cipher.encrypt(test.nonce, test.plaintext, test.aad);
        assertEquals(enc, test.ciphertext);
        assertEquals(new Uint8Array(tag), test.tag);
      },
    });

    Deno.test({
      name: `aes-${bits}-gcm decrypt ${i + 1}/${vectors.length}`,
      fn() {
        const cipher = aesGcm(bits, test.key);
        const plaintext = cipher.decrypt(
          test.ciphertext,
          test.nonce,
          test.aad,
          test.tag,
        );
        assertEquals(plaintext, test.plaintext);
      },
    });
  }
}

Deno.test({
  name: "aes-128-gcm encrypt multiple",
  fn() {
    const key = Buffer.alloc(16);
    const nonce = Buffer.alloc(12);

    const gcm = crypto.createCipheriv("aes-128-gcm", key, nonce);

    assertEquals(gcm.update("hello", "utf8", "hex"), "6bedb6a20f");
    assertEquals(gcm.update("world", "utf8", "hex"), "c1cce09f4c");
    gcm.final();
    assertEquals(
      gcm.getAuthTag().toString("hex"),
      "bf6d20a38e0c828bea3de63b7ff1dfbd",
    );
  },
});