-
Notifications
You must be signed in to change notification settings - Fork 9
/
minisign.js
84 lines (67 loc) · 2.47 KB
/
minisign.js
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
const sodium = require('sodium-native');
// Parse a minisign key represented as a base64 string.
// Throws exceptions on invalid keys.
function parseKey(key_str) {
const key_info = Buffer.from(key_str, 'base64');
const id = key_info.subarray(2, 10);
const key = key_info.subarray(10);
if (key.byteLength !== sodium.crypto_sign_PUBLICKEYBYTES) {
throw new Error('invalid public key given');
}
return {
id: id,
key: key
};
}
// Parse a buffer containing the contents of a minisign signature file.
// Throws exceptions on invalid signature files.
function parseSignature(sig_buf) {
const untrusted_header = Buffer.from('untrusted comment: ');
// Validate untrusted comment header, and skip
if (!sig_buf.subarray(0, untrusted_header.byteLength).equals(untrusted_header)) {
throw new Error('file format not recognised');
}
sig_buf = sig_buf.subarray(untrusted_header.byteLength);
// Skip untrusted comment
sig_buf = sig_buf.subarray(sig_buf.indexOf('\n') + 1);
// Read and skip signature info
const sig_info_end = sig_buf.indexOf('\n');
const sig_info = Buffer.from(sig_buf.subarray(0, sig_info_end).toString(), 'base64');
sig_buf = sig_buf.subarray(sig_info_end + 1);
// Extract components of signature info
const algorithm = sig_info.subarray(0, 2);
const key_id = sig_info.subarray(2, 10);
const signature = sig_info.subarray(10);
// We don't look at the trusted comment or global signature, so we're done.
return {
algorithm: algorithm,
key_id: key_id,
signature: signature,
};
}
// Given a parsed key, parsed signature file, and raw file content, verifies the
// signature. Does not throw. Returns 'true' if the signature is valid for this
// file, 'false' otherwise.
function verifySignature(pubkey, signature, file_content) {
let signed_content;
if (signature.algorithm.equals(Buffer.from('ED'))) {
signed_content = Buffer.alloc(sodium.crypto_generichash_BYTES_MAX);
sodium.crypto_generichash(signed_content, file_content);
} else {
signed_content = file_content;
}
if (!signature.key_id.equals(pubkey.id)) {
return false;
}
if (!sodium.crypto_sign_verify_detached(signature.signature, signed_content, pubkey.key)) {
return false;
}
// Since we don't use the trusted comment, we don't bother verifying the global signature.
// If we were to start using the trusted comment for any purpose, we must add this.
return true;
}
module.exports = {
parseKey,
parseSignature,
verifySignature,
};