-
Notifications
You must be signed in to change notification settings - Fork 10
/
web.ts
96 lines (82 loc) · 2.56 KB
/
web.ts
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
import { Algorithm, type AlgorithmLike, type SignOptions } from "./types";
import { getAlgorithm } from "./utils";
const enc = new TextEncoder();
function hexToUInt8Array(string: string) {
// convert string to pairs of 2 characters
const pairs = string.match(/[\dA-F]{2}/gi) as RegExpMatchArray;
// convert the octets to integers
const integers = pairs.map(function (s) {
return parseInt(s, 16);
});
return new Uint8Array(integers);
}
function UInt8ArrayToHex(signature: ArrayBuffer) {
return Array.prototype.map
.call(new Uint8Array(signature), (x) => x.toString(16).padStart(2, "0"))
.join("");
}
function getHMACHashName(algorithm: AlgorithmLike) {
return (
{
[Algorithm.SHA1]: "SHA-1",
[Algorithm.SHA256]: "SHA-256",
} as { [key in Algorithm]: string }
)[algorithm];
}
async function importKey(secret: string, algorithm: AlgorithmLike) {
// ref: https://developer.mozilla.org/en-US/docs/Web/API/HmacImportParams
return crypto.subtle.importKey(
"raw", // raw format of the key - should be Uint8Array
enc.encode(secret),
{
// algorithm details
name: "HMAC",
hash: { name: getHMACHashName(algorithm) },
},
false, // export = false
["sign", "verify"], // what this key can do
);
}
export async function sign(options: SignOptions | string, payload: string) {
const { secret, algorithm } =
typeof options === "object"
? {
secret: options.secret,
algorithm: options.algorithm || Algorithm.SHA256,
}
: { secret: options, algorithm: Algorithm.SHA256 };
if (!secret || !payload) {
throw new TypeError(
"[@octokit/webhooks-methods] secret & payload required for sign()",
);
}
if (!Object.values(Algorithm).includes(algorithm as Algorithm)) {
throw new TypeError(
`[@octokit/webhooks] Algorithm ${algorithm} is not supported. Must be 'sha1' or 'sha256'`,
);
}
const signature = await crypto.subtle.sign(
"HMAC",
await importKey(secret, algorithm),
enc.encode(payload),
);
return `${algorithm}=${UInt8ArrayToHex(signature)}`;
}
export async function verify(
secret: string,
eventPayload: string,
signature: string,
) {
if (!secret || !eventPayload || !signature) {
throw new TypeError(
"[@octokit/webhooks-methods] secret, eventPayload & signature required",
);
}
const algorithm = getAlgorithm(signature);
return await crypto.subtle.verify(
"HMAC",
await importKey(secret, algorithm),
hexToUInt8Array(signature.replace(`${algorithm}=`, "")),
enc.encode(eventPayload),
);
}