@@ -22,10 +22,11 @@ function parseKey(key_str) {
22
22
// Throws exceptions on invalid signature files.
23
23
function parseSignature ( sig_buf ) {
24
24
const untrusted_header = Buffer . from ( 'untrusted comment: ' ) ;
25
+ const trusted_header = Buffer . from ( 'trusted comment: ' ) ;
25
26
26
27
// Validate untrusted comment header, and skip
27
28
if ( ! sig_buf . subarray ( 0 , untrusted_header . byteLength ) . equals ( untrusted_header ) ) {
28
- throw new Error ( 'file format not recognised ' ) ;
29
+ throw new Error ( 'invalid minisign signature: bad untrusted comment header ' ) ;
29
30
}
30
31
sig_buf = sig_buf . subarray ( untrusted_header . byteLength ) ;
31
32
@@ -42,38 +43,61 @@ function parseSignature(sig_buf) {
42
43
const key_id = sig_info . subarray ( 2 , 10 ) ;
43
44
const signature = sig_info . subarray ( 10 ) ;
44
45
45
- // We don't look at the trusted comment or global signature, so we're done.
46
+ // Validate trusted comment header, and skip
47
+ if ( ! sig_buf . subarray ( 0 , trusted_header . byteLength ) . equals ( trusted_header ) ) {
48
+ throw new Error ( 'invalid minisign signature: bad trusted comment header' ) ;
49
+ }
50
+ sig_buf = sig_buf . subarray ( trusted_header . byteLength ) ;
51
+
52
+ // Read and skip trusted comment
53
+ const trusted_comment_end = sig_bug . indexOf ( '\n' ) ;
54
+ const trusted_comment = sig_buf . subarray ( 0 , trusted_comment_end ) ;
55
+ sig_buf = sig_buf . subarray ( trusted_comment_end + 1 ) ;
56
+
57
+ // Read and skip global signature; handle missing trailing newline, just in case
58
+ let global_sig_end = sig_buf . indexOf ( '\n' ) ;
59
+ if ( global_sig_end == - 1 ) global_sig_end = sig_buf . length ;
60
+ const global_sig = Buffer . from ( sig_buf . subarray ( 0 , global_sig_end ) . toString ( ) , 'base64' ) ;
61
+ sig_buf = sig_buf . subarray ( sig_info_end + 1 ) ; // this might be length+1, but that's allowed
62
+
63
+ // Validate that all data has been consumed
64
+ if ( sig_buf . length !== 0 ) {
65
+ throw new Error ( 'invalid minisign signature: trailing bytes' ) ;
66
+ }
46
67
47
68
return {
48
69
algorithm : algorithm ,
49
70
key_id : key_id ,
50
71
signature : signature ,
72
+ trusted_comment : trusted_comment ,
73
+ global_signature : global_signature ,
51
74
} ;
52
75
}
53
76
54
- // Given a parsed key, parsed signature file, and raw file content, verifies the
55
- // signature. Does not throw. Returns 'true' if the signature is valid for this
56
- // file, 'false' otherwise.
77
+ // Given a parsed key, parsed signature file, and raw file content, verifies the signature,
78
+ // including the global signature (hence validating the trusted comment). Does not throw.
79
+ // Returns 'true' if the signature is valid for this file, 'false' otherwise.
57
80
function verifySignature ( pubkey , signature , file_content ) {
81
+ if ( ! signature . key_id . equals ( pubkey . id ) ) {
82
+ return false ;
83
+ }
84
+
58
85
let signed_content ;
59
86
if ( signature . algorithm . equals ( Buffer . from ( 'ED' ) ) ) {
60
87
signed_content = Buffer . alloc ( sodium . crypto_generichash_BYTES_MAX ) ;
61
88
sodium . crypto_generichash ( signed_content , file_content ) ;
62
89
} else {
63
90
signed_content = file_content ;
64
91
}
65
-
66
- if ( ! signature . key_id . equals ( pubkey . id ) ) {
92
+ if ( ! sodium . crypto_sign_verify_detached ( signature . signature , signed_content , pubkey . key ) ) {
67
93
return false ;
68
94
}
69
95
70
- if ( ! sodium . crypto_sign_verify_detached ( signature . signature , signed_content , pubkey . key ) ) {
96
+ const global_signed_content = Buffer . concat ( [ signature . signature , signature . trusted_comment ] ) ;
97
+ if ( ! sodium . crypto_sign_verify_detached ( signature . global_signature , global_signed_content , pubkey . key ) ) {
71
98
return false ;
72
99
}
73
100
74
- // Since we don't use the trusted comment, we don't bother verifying the global signature.
75
- // If we were to start using the trusted comment for any purpose, we must add this.
76
-
77
101
return true ;
78
102
}
79
103
0 commit comments