This repository was archived by the owner on Jul 5, 2023. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathHMAC.js
More file actions
112 lines (101 loc) · 2.71 KB
/
HMAC.js
File metadata and controls
112 lines (101 loc) · 2.71 KB
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
import { SHA_512, SHA_384, SHA_256, SHA_1, bufferToHex, hexToBuffer } from './hash.js';
export const ALGOS = [SHA_512, SHA_384, SHA_256, SHA_1];
export const DEFAULT_ALGORITHM = SHA_512;
const symbols = {
key: Symbol('key'),
encoder: Symbol('encoder'),
algorithm: Symbol('algorithm'),
};
export class HMAC {
constructor(password, { algorithm = DEFAULT_ALGORITHM } = {}) {
const encoder = new TextEncoder();
Object.defineProperties(this, {
[symbols.algorithm]: {
configurable: false,
enumerable: false,
writable: false,
value: algorithm,
},
[symbols.encoder]: {
configurable: false,
enumerable: false,
writable: false,
value: encoder,
},
[symbols.key]: {
configurable: false,
enumerable: false,
writable: false,
value: HMAC.generateKey(password, { algorithm, encoder }),
},
});
}
get algorithm() {
return this[symbols.algorithm];
}
get encoding() {
return this[symbols.encoder].encoding;
}
async sign(data) {
if (! (data instanceof ArrayBuffer)) {
throw new TypeError('`sign()` accepts an ArrayBuffer');
} else {
const key = await this[symbols.key];
const buffer = await crypto.subtle.sign({ name: key.algorithm.name }, key, data);
return bufferToHex(buffer);
}
}
async signText(str) {
if (typeof str !== 'string') {
throw new TypeError('Expected a string');
} else {
return this.sign(this[symbols.encoder].encode(str).buffer);
}
}
async signFile(file) {
if (! (file instanceof File)) {
throw new TypeError('Expected a file');
} else {
return this.sign(await file.arrayBuffer());
}
}
async verify(data, signature) {
if (typeof signature !== 'string') {
throw new DOMException('Missing signture to verify');
} else if (! (data instanceof ArrayBuffer)) {
throw new TypeError('`verify()` requires an ArrayBuffer and hex signature');
} else {
const key = await this[symbols.key];
return await crypto.subtle.verify({ name: key.algorithm.name }, key, hexToBuffer(signature), data);
}
}
async verifyText(str, signature) {
if (typeof str !== 'string') {
throw new TypeError('Expected a string');
} else {
return this.verify(this[symbols.encoder].encode(str).buffer, signature);
}
}
async verifyFile(file, signature) {
if (! (file instanceof File)) {
throw new TypeError('Expected a file');
} else {
return this.verify(await file.arrayBuffer(), signature);
}
}
static async generateKey(password, {
format = 'raw',
algorithm: hash = DEFAULT_ALGORITHM,
extractable = false,
encoder = new TextEncoder(),
usages = ['sign', 'verify'],
} = {}) {
return await crypto.subtle.importKey(
format,
encoder.encode(password),
{ name: 'HMAC', hash },
extractable,
usages
);
}
}