11/**
2- * Ported from: https://github.com/mlugg/setup-zig/blob/main/main.js (MIT)
2+ * Ported from: https://github.com/mlugg/setup-zig/blob/main/minisign.js
3+ *
4+ * Copyright Matthew Lugg
5+ *
6+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7+ * of this software and associated documentation files (the "Software"), to deal
8+ * in the Software without restriction, including without limitation the rights
9+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+ * copies of the Software, and to permit persons to whom the Software is
11+ * furnished to do so, subject to the following conditions:
12+ *
13+ * The above copyright notice and this permission notice shall be included in
14+ * all copies or substantial portions of the Software.
15+ *
16+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+ * THE SOFTWARE.
323 */
424
525import sodium from "libsodium-wrappers" ;
@@ -9,6 +29,8 @@ export interface Key {
929 key : Buffer ;
1030}
1131
32+ export const ready = sodium . ready ;
33+
1234// Parse a minisign key represented as a base64 string.
1335// Throws exceptions on invalid keys.
1436export function parseKey ( keyString : string ) : Key {
@@ -31,16 +53,19 @@ export interface Signature {
3153 algorithm : Buffer ;
3254 keyID : Buffer ;
3355 signature : Buffer ;
56+ trustedComment : Buffer ;
57+ globalSignature : Buffer ;
3458}
3559
3660// Parse a buffer containing the contents of a minisign signature file.
3761// Throws exceptions on invalid signature files.
3862export function parseSignature ( sigBuf : Buffer ) : Signature {
3963 const untrustedHeader = Buffer . from ( "untrusted comment: " ) ;
64+ const trustedHeader = Buffer . from ( "trusted comment: " ) ;
4065
4166 // Validate untrusted comment header, and skip
4267 if ( ! sigBuf . subarray ( 0 , untrustedHeader . byteLength ) . equals ( untrustedHeader ) ) {
43- throw new Error ( "file format not recognised " ) ;
68+ throw new Error ( "invalid minisign signature: bad untrusted comment header " ) ;
4469 }
4570 sigBuf = sigBuf . subarray ( untrustedHeader . byteLength ) ;
4671
@@ -57,36 +82,59 @@ export function parseSignature(sigBuf: Buffer): Signature {
5782 const keyID = sigInfo . subarray ( 2 , 10 ) ;
5883 const signature = sigInfo . subarray ( 10 ) ;
5984
60- // We don't look at the trusted comment or global signature, so we're done.
85+ // Validate trusted comment header, and skip
86+ if ( ! sigBuf . subarray ( 0 , trustedHeader . byteLength ) . equals ( trustedHeader ) ) {
87+ throw new Error ( "invalid minisign signature: bad trusted comment header" ) ;
88+ }
89+ sigBuf = sigBuf . subarray ( trustedHeader . byteLength ) ;
90+
91+ // Read and skip trusted comment
92+ const trustedCommentEnd = sigBuf . indexOf ( "\n" ) ;
93+ const trustedComment = sigBuf . subarray ( 0 , trustedCommentEnd ) ;
94+ sigBuf = sigBuf . subarray ( trustedCommentEnd + 1 ) ;
95+
96+ // Read and skip global signature; handle missing trailing newline, just in case
97+ let globalSigEnd = sigBuf . indexOf ( "\n" ) ;
98+ if ( globalSigEnd === - 1 ) globalSigEnd = sigBuf . length ;
99+ const globalSig = Buffer . from ( sigBuf . subarray ( 0 , globalSigEnd ) . toString ( ) , "base64" ) ;
100+ sigBuf = sigBuf . subarray ( sigInfoEnd + 1 ) ; // this might be length+1, but that's allowed
101+
102+ // Validate that all data has been consumed
103+ if ( sigBuf . length !== 0 ) {
104+ throw new Error ( "invalid minisign signature: trailing bytes" ) ;
105+ }
61106
62107 return {
63108 algorithm : algorithm ,
64109 keyID : keyID ,
65110 signature : signature ,
111+ trustedComment : trustedComment ,
112+ globalSignature : globalSig ,
66113 } ;
67114}
68115
69- // Given a parsed key, parsed signature file, and raw file content, verifies the
70- // signature. Does not throw. Returns 'true' if the signature is valid for this
71- // file, 'false' otherwise.
116+ // Given a parsed key, parsed signature file, and raw file content, verifies the signature,
117+ // including the global signature (hence validating the trusted comment). Does not throw.
118+ // Returns 'true' if the signature is valid for this file, 'false' otherwise.
72119export function verifySignature ( pubkey : Key , signature : Signature , fileContent : Buffer ) {
120+ if ( ! signature . keyID . equals ( pubkey . id ) ) {
121+ return false ;
122+ }
123+
73124 let signedContent ;
74125 if ( signature . algorithm . equals ( Buffer . from ( "ED" ) ) ) {
75126 signedContent = sodium . crypto_generichash ( sodium . crypto_generichash_BYTES_MAX , fileContent ) ;
76127 } else {
77128 signedContent = fileContent ;
78129 }
79-
80- if ( ! signature . keyID . equals ( pubkey . id ) ) {
130+ if ( ! sodium . crypto_sign_verify_detached ( signature . signature , signedContent , pubkey . key ) ) {
81131 return false ;
82132 }
83133
84- if ( ! sodium . crypto_sign_verify_detached ( signature . signature , signedContent , pubkey . key ) ) {
134+ const globalSignedContent = Buffer . concat ( [ signature . signature , signature . trustedComment ] ) ;
135+ if ( ! sodium . crypto_sign_verify_detached ( signature . globalSignature , globalSignedContent , pubkey . key ) ) {
85136 return false ;
86137 }
87138
88- // Since we don't use the trusted comment, we don't bother verifying the global signature.
89- // If we were to start using the trusted comment for any purpose, we must add this.
90-
91139 return true ;
92140}
0 commit comments