summaryrefslogtreecommitdiff
path: root/tests/fixtures/setup.ts
blob: fdcc3bb7ad1d949a0f8d8d99a003039d974a4028 (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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import {
  createMessage,
  decryptKey,
  generateKey,
  type PrivateKey,
  sign,
} from "npm:openpgp@^6.1.1";
import { passphrase } from "./test_data.ts";
import type { MaybeIterable } from "../../src/utils/iterator.ts";
import { afterEach, beforeEach } from "@std/testing/bdd";
import { type Stub, stub } from "@std/testing/mock";

export async function generateKeyPair(
  name: string,
  options?: Partial<Parameters<typeof generateKey>[0]>,
): ReturnType<typeof generateKey> {
  const key = await generateKey({
    type: "ecc",
    userIDs: [{
      name,
      email: `${
        name.toLowerCase().replaceAll(/\s/g, "")
      }@localhost.localdomain`,
    }],
    passphrase,
    format: "object",
    ...options,
  });
  const privateKey = await decryptKey({
    privateKey: key.privateKey,
    passphrase,
  });
  return { ...key, privateKey };
}

export function generateKeyPairWithSubkey(
  name: string,
): ReturnType<typeof generateKey> {
  return generateKeyPair(name, {
    curve: "nistP256",
    subkeys: [{ type: "ecc", curve: "nistP256", sign: true }],
  });
}

export async function createDetachedSignature(
  data: Uint8Array,
  signingKeys: MaybeIterable<PrivateKey>,
): Promise<Uint8Array<ArrayBuffer>> {
  const message = await createMessage({ binary: data });
  const signature = await sign({
    message,
    signingKeys: Symbol.iterator in signingKeys
      // deno-lint-ignore no-undef
      ? Iterator.from(signingKeys).toArray()
      : signingKeys,
    detached: true,
    format: "object",
  });
  return signature.write() as Uint8Array<ArrayBuffer>;
}

export function corruptData(data: Uint8Array): Uint8Array<ArrayBuffer> {
  const corrupted = new Uint8Array(data);
  if (corrupted.length > 0) {
    corrupted[0] += 1;
    corrupted[0] %= 1 << 8;
  }
  return corrupted;
}

export function corruptSignatureFormat(
  signature: Uint8Array,
): Uint8Array<ArrayBuffer> {
  const corrupted = new Uint8Array(signature);

  if (corrupted.length > 0) {
    // Strategy 1: Change the packet tag byte
    // The first byte contains the tag and format information.
    // Changing the lower 6 bits (new format tag) or higher bits (packet format)
    // can easily break parsing. Let's try flipping a bit.
    // corrupted[0] = corrupted[0] ^ 0x01; // Flip the last bit

    // Strategy 2 (Alternative - more drastic): Truncate the signature
    // return corrupted.slice(0, corrupted.length / 2); // Cut off half the signature

    // Strategy 3 (Alternative - modify length field):
    // This is more complex as length encoding varies, but for typical new format
    // packets, length information is in the bytes immediately following the tag.
    // Modifying these can cause the parser to misinterpret the packet length.
    // Example (simplified, might need adjustment based on actual encoding):
    if (corrupted.length > 2) {
      corrupted[1] = (corrupted[1] + 10) % 256;
    }
  }
  return corrupted;
}

const inMemoryFiles = new Map<
  string,
  { text?: string; bytes?: Uint8Array<ArrayBuffer> }
>();

export function startMockFs(): void {
  inMemoryFiles.clear();

  beforeEach(() => {
    Deno.readTextFile = stub(
      Deno,
      "readTextFile",
      async (path: string | URL) => {
        const url = new URL(path).href;
        const content = inMemoryFiles.get(url)?.text;
        if (content === undefined) {
          throw new Deno.errors.NotFound(`File not found: ${url}`);
        }
        return await Promise.resolve(content);
      },
    );

    Deno.readFile = stub(Deno, "readFile", async (path: string | URL) => {
      const url = new URL(path).href;
      const content = inMemoryFiles.get(url)?.bytes;
      if (content === undefined) {
        throw new Deno.errors.NotFound(`File not found: ${url}`);
      }
      return await Promise.resolve(content);
    });

    inMemoryFiles.clear();
  });

  afterEach(() => {
    (Deno.readTextFile as Stub).restore();
    (Deno.readFile as Stub).restore();
  });
}

export function createInMemoryFile(
  url: URL,
  content: string | Uint8Array<ArrayBuffer>,
): URL {
  inMemoryFiles.set(
    url.href,
    typeof content === "string" ? { text: content } : { bytes: content },
  );
  return url;
}