@@ -530,17 +530,44 @@ describe("release asset verification", () => {
530530 function makeMinisignKeypair ( keyIdHex : string ) : {
531531 pubkeyText : string ;
532532 keyId : Buffer ;
533- signPayload : ( payload : Buffer ) => string ;
533+ signPayload : ( payload : Buffer , rawBytes ?: boolean ) => string ;
534534 } {
535535 const { publicKey, privateKey } = generateKeyPairSync ( "ed25519" ) ;
536536 const raw = Buffer . from ( publicKey . export ( { format : "der" , type : "spki" } ) ) . subarray ( - 32 ) ;
537537 const keyId = Buffer . from ( keyIdHex , "hex" ) ;
538538 const pubkeyText = `untrusted comment: test public key\n${ Buffer . concat ( [ Buffer . from ( "Ed" ) , keyId , raw ] ) . toString ( "base64" ) } \n` ;
539- const signPayload = ( payload : Buffer ) : string =>
540- `untrusted comment: test signature\n${ Buffer . concat ( [ Buffer . from ( "Ed" ) , keyId , ed25519Sign ( null , payload , privateKey ) ] ) . toString ( "base64" ) } \n` ;
539+ const signPayload = ( payload : Buffer , rawBytes = false ) : string => {
540+ const signed = rawBytes ? payload : createHash ( "blake2b512" ) . update ( payload ) . digest ( ) ;
541+ const signature = ed25519Sign ( null , signed , privateKey ) ;
542+ const trusted = "timestamp:1\tfile:original-before-collection.msi" ;
543+ const packet = Buffer . concat ( [ Buffer . from ( "ED" ) , keyId , signature ] ) . toString ( "base64" ) ;
544+ const global = ed25519Sign ( null , Buffer . concat ( [ signature , Buffer . from ( trusted ) ] ) , privateKey ) . toString ( "base64" ) ;
545+ return Buffer . from ( `untrusted comment: test signature\n${ packet } \ntrusted comment: ${ trusted } \n${ global } \n` ) . toString ( "base64" ) ;
546+ } ;
541547 return { pubkeyText, keyId, signPayload } ;
542548 }
543549
550+ test ( "accepts the upstream minisign 0.7.3 prehashed vector in Tauri encoding" , ( ) => {
551+ const fixture = JSON . parse ( readFileSync ( repoPath ( "tests/fixtures/minisign/prehashed-vector.json" ) , "utf8" ) ) as {
552+ payload : string ; publicKeyBase64 : string ; signatureBox : string ;
553+ } ;
554+ const dir = temporaryDirectory ( ) ;
555+ try {
556+ const asset = join ( dir , "renamed-release.bin" ) ;
557+ const key = parseMinisignPublicKey ( `untrusted comment: upstream key\n${ fixture . publicKeyBase64 } \n` ) ;
558+ writeFileSync ( asset , fixture . payload ) ;
559+ writeFileSync ( `${ asset } .sig` , Buffer . from ( fixture . signatureBox ) . toString ( "base64" ) ) ;
560+ expect ( ( ) => verifyUpdaterSignature ( asset , key ) ) . not . toThrow ( ) ;
561+ writeFileSync ( asset , "tampered" ) ;
562+ expect ( ( ) => verifyUpdaterSignature ( asset , key ) ) . toThrow ( / S i g n a t u r e v e r i f i c a t i o n f a i l e d / ) ;
563+ writeFileSync ( asset , fixture . payload ) ;
564+ writeFileSync ( `${ asset } .sig` , Buffer . from ( fixture . signatureBox . replace ( "file:test" , "file:changed" ) ) . toString ( "base64" ) ) ;
565+ expect ( ( ) => verifyUpdaterSignature ( asset , key ) ) . toThrow ( / C o m m e n t s i g n a t u r e v e r i f i c a t i o n f a i l e d / ) ;
566+ } finally {
567+ rmSync ( dir , { recursive : true , force : true } ) ;
568+ }
569+ } ) ;
570+
544571 test ( "derives the expected set from the real release matrices and producer tables" , ( ) => {
545572 const workflow = readFileSync ( repoPath ( ".github" , "workflows" , "release.yml" ) , "utf8" ) ;
546573 const { standaloneTargets : workflowStandalone , desktopTargets } = releaseMatrixTargets ( workflow ) ;
@@ -624,9 +651,43 @@ describe("release asset verification", () => {
624651 writeFileSync ( `${ asset } .sig` , other . signPayload ( payload ) ) ;
625652 expect ( ( ) => verifyUpdaterSignature ( asset , key ) ) . toThrow ( / n o t t h e p i n n e d u p d a t e r k e y / ) ;
626653
627- const hashed = `untrusted comment: test\n${ Buffer . concat ( [ Buffer . from ( "ED" ) , other . keyId , Buffer . alloc ( 64 ) ] ) . toString ( "base64" ) } \n` ;
628- writeFileSync ( `${ asset } .sig` , hashed ) ;
654+ const valid = signPayload ( payload ) ;
655+ writeFileSync ( `${ asset } .sig` , signPayload ( payload , true ) ) ;
656+ expect ( ( ) => verifyUpdaterSignature ( asset , key ) ) . toThrow ( / S i g n a t u r e v e r i f i c a t i o n f a i l e d / ) ;
657+ const lines = Buffer . from ( valid , "base64" ) . toString ( "utf8" ) . trimEnd ( ) . split ( "\n" ) ;
658+ const encode = ( box : string [ ] ) => Buffer . from ( `${ box . join ( "\n" ) } \n` ) . toString ( "base64" ) ;
659+ const packet = Buffer . from ( lines [ 1 ] ! , "base64" ) ;
660+ packet [ 10 ] = packet [ 10 ] ! ^ 1 ;
661+ writeFileSync ( `${ asset } .sig` , encode ( [ lines [ 0 ] ! , packet . toString ( "base64" ) , lines [ 2 ] ! , lines [ 3 ] ! ] ) ) ;
662+ expect ( ( ) => verifyUpdaterSignature ( asset , key ) ) . toThrow ( / S i g n a t u r e v e r i f i c a t i o n f a i l e d / ) ;
663+ packet [ 10 ] = packet [ 10 ] ! ^ 1 ;
664+ packet . write ( "Ed" , 0 ) ;
665+ writeFileSync ( `${ asset } .sig` , encode ( [ lines [ 0 ] ! , packet . toString ( "base64" ) , lines [ 2 ] ! , lines [ 3 ] ! ] ) ) ;
629666 expect ( ( ) => verifyUpdaterSignature ( asset , key ) ) . toThrow ( / U n s u p p o r t e d s i g n a t u r e a l g o r i t h m / ) ;
667+
668+ writeFileSync ( `${ asset } .sig` , encode ( [ lines [ 0 ] ! , lines [ 1 ] ! , "trusted comment: changed" , lines [ 3 ] ! ] ) ) ;
669+ expect ( ( ) => verifyUpdaterSignature ( asset , key ) ) . toThrow ( / C o m m e n t s i g n a t u r e v e r i f i c a t i o n f a i l e d / ) ;
670+ writeFileSync ( `${ asset } .sig` , encode ( [ lines [ 0 ] ! , lines [ 1 ] ! , lines [ 2 ] ! , Buffer . alloc ( 64 ) . toString ( "base64" ) ] ) ) ;
671+ expect ( ( ) => verifyUpdaterSignature ( asset , key ) ) . toThrow ( / C o m m e n t s i g n a t u r e v e r i f i c a t i o n f a i l e d / ) ;
672+ const sameIdOtherKey = parseMinisignPublicKey ( makeMinisignKeypair ( "0123456789abcdef" ) . pubkeyText ) ;
673+ writeFileSync ( `${ asset } .sig` , valid ) ;
674+ expect ( ( ) => verifyUpdaterSignature ( asset , sameIdOtherKey ) ) . toThrow ( / S i g n a t u r e v e r i f i c a t i o n f a i l e d / ) ;
675+
676+ for ( const malformed of [ valid + "!" , encode ( lines . slice ( 0 , 3 ) ) , encode ( [ ...lines , "extra" ] ) ,
677+ encode ( [ lines [ 0 ] ! , Buffer . alloc ( 73 ) . toString ( "base64" ) , lines [ 2 ] ! , lines [ 3 ] ! ] ) ,
678+ encode ( [ lines [ 0 ] ! , lines [ 1 ] ! , lines [ 2 ] ! , Buffer . alloc ( 65 ) . toString ( "base64" ) ] ) ,
679+ Buffer . from ( [ 0xff ] ) . toString ( "base64" ) ,
680+ Buffer . from ( `\uFEFF${ lines . join ( "\n" ) } ` ) . toString ( "base64" ) ,
681+ lines . slice ( 0 , 2 ) . join ( "\n" ) ] ) {
682+ writeFileSync ( `${ asset } .sig` , malformed ) ;
683+ expect ( ( ) => verifyUpdaterSignature ( asset , key ) ) . toThrow ( ) ;
684+ }
685+ writeFileSync ( `${ asset } .sig` , encode ( [ "untrusted comment: changed" , ...lines . slice ( 1 ) ] ) ) ;
686+ expect ( ( ) => verifyUpdaterSignature ( asset , key ) ) . not . toThrow ( ) ;
687+ writeFileSync ( `${ asset } .sig` , Buffer . from ( `${ lines . join ( "\r\n" ) } \r\n` ) . toString ( "base64" ) ) ;
688+ expect ( ( ) => verifyUpdaterSignature ( asset , key ) ) . not . toThrow ( ) ;
689+ writeFileSync ( `${ asset } .sig` , ` \n${ valid } \n` ) ;
690+ expect ( ( ) => verifyUpdaterSignature ( asset , key ) ) . not . toThrow ( ) ;
630691 } finally {
631692 rmSync ( dir , { recursive : true , force : true } ) ;
632693 }
0 commit comments