Skip to content

Instantly share code, notes, and snippets.

@jvehent
Last active April 18, 2018 13:40
Show Gist options
  • Save jvehent/934795c25f4af153d5a714fd0c6a0381 to your computer and use it in GitHub Desktop.
Save jvehent/934795c25f4af153d5a714fd0c6a0381 to your computer and use it in GitHub Desktop.
Firefox MAR signature verification in Go
Header: MAR ID="MAR1", Offset to Index=49468896
Signatures Header: FileSize=49471514, NumSignatures=1
* Signature 0 Entry Header: Algorithm="RSA-PKCS1-SHA384", Size=512
* Signature 0 Data (len=512): 3671D5CE31DEAB2AEBA1F67EA10A20BEACCA45A76B993D1C26D3426C5ADD73B6634F40C37C0297F9E0E77F31F4739C6902B5C63714B018649BB1A458B7EC7BF871DA7EFA6169A806F646F931B00228E84262CBD05C92F599FFFE78BC5D84B6A3A82EDFEC290D2DE5F52FCFB24E116BBF45528FE6654F0FBCA65B7E3E03F4632EB924169D705C7A9FAA24DF61E9C2DFBF908C999CF508D09DE67CF10255F8CC1965110F29BFA39AA50A4889C122A8CD17244ABE36F3E4237F42D7123A13D31E698CDCF376E2F196FFB724CF18E120BE9BA2C5995EA9D3EB82B408F9C9A63E051571AD7ADB9FAF8DEF340C9AEEC7E2455D78A0E41C41D8CC3896A6A414AC1C69F7CB7D98691CDF5C2A9E3B81B693C874D2EF175E4EF2D9B35CD8CA8508D7110D0D6054746D04E565CA20ADFB440C4DEC692D372300C85437919133560CAC19517BFB1D6484AB88C8AF41CA2631124AF75394514AE6DA8CBCE74138836568E96EF72E1D484342E861070AC74891E89A6C348856619DE256BD72B94557015F9115D6E21BCDDFF6F98B24AAD88E4C3BA529DD7A28501F3B28D47675000988DA172E4C0E73764C7A55F833AFBB3C7AD4E798D8487BF825C486FEB70DEB94F1083F0D5AA0E36C63CE1C0CFBD70B502D8D8F1B23AD61D0807D1B6E758C5A84BC7B20CEF655C1DAF7268B5EE77FABE29778C1398563F86AF3839C6BAC883B3805FE910731
Additional Sections: 1
* Additional Section Entry 0: BlockSize=104, BlockID="Product Information"
* Additional Section Entry 0 Data (len=96): firefox-mozilla-central61.0a1
Index Size: 2614
* Index Entry 0: FileName="updatev2.manifest" Offset To Content=648 Stored Size=724 B Compressed=true Perm=-rw-r--r--
* Index Entry 1: FileName="updatev3.manifest" Offset To Content=1372 Stored Size=756 B Compressed=true Perm=-rw-r--r--
* Index Entry 2: FileName="updater.ini" Offset To Content=2128 Stored Size=532 B Compressed=true Perm=-rw-r--r--
* Index Entry 3: FileName="updater" Offset To Content=2660 Stored Size=106 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 4: FileName="update-settings.ini" Offset To Content=108116 Stored Size=188 B Compressed=true Perm=-rw-r--r--
* Index Entry 5: FileName="removed-files" Offset To Content=108304 Stored Size=32 B Compressed=true Perm=-rw-r--r--
* Index Entry 6: FileName="precomplete" Offset To Content=108336 Stored Size=768 B Compressed=true Perm=-rw-r--r--
* Index Entry 7: FileName="plugin-container.sig" Offset To Content=109104 Stored Size=1.4 kB Compressed=true Perm=-rw-r--r--
* Index Entry 8: FileName="plugin-container" Offset To Content=110500 Stored Size=94 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 9: FileName="platform.ini" Offset To Content=204968 Stored Size=212 B Compressed=true Perm=-rw-r--r--
* Index Entry 10: FileName="pingsender" Offset To Content=205180 Stored Size=146 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 11: FileName="omni.ja" Offset To Content=351012 Stored Size=3.3 MB Compressed=true Perm=-rw-r--r--
* Index Entry 12: FileName="minidump-analyzer" Offset To Content=3684736 Stored Size=228 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 13: FileName="libxul.so.sig" Offset To Content=3913228 Stored Size=1.4 kB Compressed=true Perm=-rw-r--r--
* Index Entry 14: FileName="libxul.so" Offset To Content=3914624 Stored Size=33 MB Compressed=true Perm=-rwxr-xr-x
* Index Entry 15: FileName="libssl3.so" Offset To Content=36507412 Stored Size=129 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 16: FileName="libsoftokn3.so" Offset To Content=36636844 Stored Size=98 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 17: FileName="libsoftokn3.chk" Offset To Content=36734992 Stored Size=968 B Compressed=true Perm=-rw-r--r--
* Index Entry 18: FileName="libsmime3.so" Offset To Content=36735960 Stored Size=62 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 19: FileName="libplds4.so" Offset To Content=36798392 Stored Size=6.9 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 20: FileName="libplc4.so" Offset To Content=36805308 Stored Size=7.8 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 21: FileName="libnssutil3.so" Offset To Content=36813136 Stored Size=67 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 22: FileName="libnssdbm3.so" Offset To Content=36880364 Stored Size=66 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 23: FileName="libnssdbm3.chk" Offset To Content=36946104 Stored Size=968 B Compressed=true Perm=-rw-r--r--
* Index Entry 24: FileName="libnssckbi.so" Offset To Content=36947072 Stored Size=166 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 25: FileName="libnss3.so" Offset To Content=37112872 Stored Size=236 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 26: FileName="libnspr4.so" Offset To Content=37348508 Stored Size=112 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 27: FileName="libmozsqlite3.so" Offset To Content=37460312 Stored Size=516 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 28: FileName="libmozsandbox.so" Offset To Content=37976376 Stored Size=40 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 29: FileName="libmozgtk.so" Offset To Content=38016564 Stored Size=2.2 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 30: FileName="libmozavutil.so" Offset To Content=38018716 Stored Size=63 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 31: FileName="libmozavcodec.so" Offset To Content=38081756 Stored Size=487 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 32: FileName="liblgpllibs.so" Offset To Content=38568488 Stored Size=26 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 33: FileName="libfreeblpriv3.so" Offset To Content=38595008 Stored Size=190 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 34: FileName="libfreeblpriv3.chk" Offset To Content=38785276 Stored Size=968 B Compressed=true Perm=-rw-r--r--
* Index Entry 35: FileName="icons/updater.png" Offset To Content=38786244 Stored Size=2.2 kB Compressed=true Perm=-rw-r--r--
* Index Entry 36: FileName="gtk2/libmozgtk.so" Offset To Content=38788468 Stored Size=4.0 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 37: FileName="gmp-clearkey/0.1/manifest.json" Offset To Content=38792428 Stored Size=192 B Compressed=true Perm=-rw-r--r--
* Index Entry 38: FileName="gmp-clearkey/0.1/libclearkey.so.sig" Offset To Content=38792620 Stored Size=1.4 kB Compressed=true Perm=-rw-r--r--
* Index Entry 39: FileName="gmp-clearkey/0.1/libclearkey.so" Offset To Content=38794016 Stored Size=45 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 40: FileName="fonts/TwemojiMozilla.ttf" Offset To Content=38838776 Stored Size=431 kB Compressed=true Perm=-rw-r--r--
* Index Entry 41: FileName="firefox.sig" Offset To Content=39269900 Stored Size=1.4 kB Compressed=true Perm=-rw-r--r--
* Index Entry 42: FileName="firefox-bin.sig" Offset To Content=39271296 Stored Size=1.4 kB Compressed=true Perm=-rw-r--r--
* Index Entry 43: FileName="firefox-bin" Offset To Content=39272692 Stored Size=97 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 44: FileName="firefox" Offset To Content=39369664 Stored Size=97 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 45: FileName="dictionaries/en-US.dic" Offset To Content=39466636 Stored Size=167 kB Compressed=true Perm=-rw-r--r--
* Index Entry 46: FileName="dictionaries/en-US.aff" Offset To Content=39633576 Stored Size=964 B Compressed=true Perm=-rw-r--r--
* Index Entry 47: FileName="dependentlibs.list" Offset To Content=39634540 Stored Size=156 B Compressed=true Perm=-rw-r--r--
* Index Entry 48: FileName="defaults/pref/channel-prefs.js" Offset To Content=39634696 Stored Size=280 B Compressed=true Perm=-rw-r--r--
* Index Entry 49: FileName="crashreporter.ini" Offset To Content=39634976 Stored Size=1.6 kB Compressed=true Perm=-rw-r--r--
* Index Entry 50: FileName="crashreporter" Offset To Content=39636612 Stored Size=102 kB Compressed=true Perm=-rwxr-xr-x
* Index Entry 51: FileName="chrome.manifest" Offset To Content=39738692 Stored Size=32 B Compressed=true Perm=-rw-r--r--
* Index Entry 52: FileName="browser/omni.ja" Offset To Content=39738724 Stored Size=7.9 MB Compressed=true Perm=-rw-r--r--
* Index Entry 53: FileName="browser/features/webcompat@mozilla.org.xpi" Offset To Content=47666300 Stored Size=3.4 kB Compressed=true Perm=-rw-r--r--
* Index Entry 54: FileName="browser/features/webcompat-reporter@mozilla.org.xpi" Offset To Content=47669716 Stored Size=4.4 kB Compressed=true Perm=-rw-r--r--
* Index Entry 55: FileName="browser/features/screenshots@mozilla.org.xpi" Offset To Content=47674108 Stored Size=331 kB Compressed=true Perm=-rw-r--r--
* Index Entry 56: FileName="browser/features/presentation@mozilla.org.xpi" Offset To Content=48004952 Stored Size=4.6 kB Compressed=true Perm=-rw-r--r--
* Index Entry 57: FileName="browser/features/onboarding@mozilla.org.xpi" Offset To Content=48009560 Stored Size=124 kB Compressed=true Perm=-rw-r--r--
* Index Entry 58: FileName="browser/features/formautofill@mozilla.org.xpi" Offset To Content=48133144 Stored Size=90 kB Compressed=true Perm=-rw-r--r--
* Index Entry 59: FileName="browser/features/followonsearch@mozilla.com.xpi" Offset To Content=48223532 Stored Size=5.3 kB Compressed=true Perm=-rw-r--r--
* Index Entry 60: FileName="browser/features/firefox@getpocket.com.xpi" Offset To Content=48228828 Stored Size=546 kB Compressed=true Perm=-rw-r--r--
* Index Entry 61: FileName="browser/features/aushelper@mozilla.org.xpi" Offset To Content=48774968 Stored Size=2.9 kB Compressed=true Perm=-rw-r--r--
* Index Entry 62: FileName="browser/features/activity-stream@mozilla.org.xpi" Offset To Content=48777900 Stored Size=640 kB Compressed=true Perm=-rw-r--r--
* Index Entry 63: FileName="browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}.xpi" Offset To Content=49418228 Stored Size=968 B Compressed=true Perm=-rw-r--r--
* Index Entry 64: FileName="browser/crashreporter-override.ini" Offset To Content=49419196 Stored Size=532 B Compressed=true Perm=-rw-r--r--
* Index Entry 65: FileName="browser/chrome/icons/default/default64.png" Offset To Content=49419728 Stored Size=5.3 kB Compressed=true Perm=-rw-r--r--
* Index Entry 66: FileName="browser/chrome/icons/default/default48.png" Offset To Content=49425060 Stored Size=3.6 kB Compressed=true Perm=-rw-r--r--
* Index Entry 67: FileName="browser/chrome/icons/default/default32.png" Offset To Content=49428708 Stored Size=2.2 kB Compressed=true Perm=-rw-r--r--
* Index Entry 68: FileName="browser/chrome/icons/default/default16.png" Offset To Content=49430916 Stored Size=852 B Compressed=true Perm=-rw-r--r--
* Index Entry 69: FileName="browser/chrome/icons/default/default128.png" Offset To Content=49431768 Stored Size=13 kB Compressed=true Perm=-rw-r--r--
* Index Entry 70: FileName="browser/chrome.manifest" Offset To Content=49444760 Stored Size=32 B Compressed=true Perm=-rw-r--r--
* Index Entry 71: FileName="browser/blocklist.xml" Offset To Content=49444792 Stored Size=23 kB Compressed=true Perm=-rw-r--r--
* Index Entry 72: FileName="application.ini" Offset To Content=49467972 Stored Size=528 B Compressed=true Perm=-rw-r--r--
* Index Entry 73: FileName="Throbber-small.gif" Offset To Content=49468500 Stored Size=396 B Compressed=true Perm=-rw-r--r--
Signed Block: len=49471002 sha384=2C80245D23665D2D7184315DC97378796E1F6623659AA0AF4EEE7A3ACE316CBF0258F66990F8CA9F087908E21A072889
* dep2_sha384 (rsa 4096 bits): failed
* dep1_sha1 (rsa 2048 bits): failed
* release2_sha384 (rsa 4096 bits): failed
* release2_sha1 (rsa 2048 bits): failed
* nightly1_sha1 (rsa 2048 bits): failed
* nightly2_sha1 (rsa 2048 bits): failed
* dep1_sha384 (rsa 4096 bits): failed
* dep2_sha1 (rsa 2048 bits): failed
* release1_sha384 (rsa 4096 bits): failed
* release1_sha1 (rsa 2048 bits): failed
* nightly1_sha384 (rsa 4096 bits): pass
* nightly2_sha384 (rsa 4096 bits): failed
@jvehent
Copy link
Author

jvehent commented Apr 18, 2018

package main

import (
        "bytes"
        "crypto"
        "crypto/rsa"
        "crypto/sha512"
        "crypto/x509"
        "encoding/binary"
        "encoding/pem"
        "fmt"
        "log"
        "os"
        "text/tabwriter"

        "github.com/dustin/go-humanize"
)

const (
        HeaderLen                        = 8
        SignaturesHeaderLen              = 12
        SignatureEntryHeaderLen          = 8
        AdditionalSectionsHeaderLen      = 4
        AdditionalSectionsEntryHeaderLen = 8
        IndexHeaderLen                   = 4
        IndexEntryHeaderLen              = 12

        SigAlgRsaPkcs1Sha1   = 1
        SigAlgRsaPkcs1Sha384 = 2

        BlockIDProductInfo = 1
)

type MARHeader struct {
        MarID         [4]byte
        OffsetToIndex uint32
}
type SignaturesHeader struct {
        FileSize      uint64
        NumSignatures uint32
}

type SignatureEntryHeader struct {
        AlgorithmID uint32
        Size        uint32
}

type AdditionalSections struct {
        NumAdditionalSections uint32
}

type AdditionalSectionEntry struct {
        BlockSize uint32
        BlockID   uint32
}

type Content []byte

type IndexHeader struct {
        Size uint32
}

type IndexEntryHeader struct {
        OffsetToContent uint32
        Size            uint32
        Flags           uint32
}
type FileName []byte

func main() {
        var (
                // current position of the cursor in the file
                cursor, c int

                header MARHeader

                sigHeader      SignaturesHeader
                sigEntryHeader SignatureEntryHeader
                sigalg         string

                additionalHeader AdditionalSections
                addSectionEntry  AdditionalSectionEntry
                blockid          string

                idxHeader      IndexHeader
                idxEntryHeader IndexEntryHeader

                i uint32

                signedBlock []byte
        )
        file, err := os.Open("firefox-61.0a1.en-US.linux-x86_64.complete.mar")
        if err != nil {
                log.Fatal("Error while opening file", err)
        }
        defer file.Close()

        c, err = parse(file, &header, HeaderLen)
        if err != nil {
                log.Fatalf("parsing failed at position %d: %v", cursor, err)
        }
        cursor += c
        fmt.Printf("Header: MAR ID=%q, Offset to Index=%d\n", header.MarID, header.OffsetToIndex)

        c, err = parse(file, &sigHeader, SignaturesHeaderLen)
        if err != nil {
                log.Fatalf("parsing failed at position %d: %v", cursor, err)
        }
        cursor += c
        fmt.Printf("\nSignatures Header: FileSize=%d, NumSignatures=%d\n", sigHeader.FileSize, sigHeader.NumSignatures)

        for i = 0; i < sigHeader.NumSignatures; i++ {
                c, err = parse(file, &sigEntryHeader, SignatureEntryHeaderLen)
                if err != nil {
                        log.Fatalf("parsing failed at position %d: %v", cursor, err)
                }
                cursor += c
                switch sigEntryHeader.AlgorithmID {
                case SigAlgRsaPkcs1Sha1:
                        sigalg = "RSA-PKCS1-SHA1"
                case SigAlgRsaPkcs1Sha384:
                        sigalg = "RSA-PKCS1-SHA384"
                default:
                        sigalg = fmt.Sprintf("unknown algorithm id %d", sigEntryHeader.AlgorithmID)
                }

                fmt.Printf("* Signature %d Entry Header: Algorithm=%q, Size=%d\n", i, sigalg, sigEntryHeader.Size)

                sigData := make([]byte, sigEntryHeader.Size, sigEntryHeader.Size)
                c, err = parse(file, &sigData, int(sigEntryHeader.Size))
                if err != nil {
                        log.Fatalf("parsing failed at position %d: %v", cursor, err)
                }
                cursor += c
                fmt.Printf("* Signature %d Data (len=%d): %X\n", i, len(sigData), sigData)
        }

        c, err = parse(file, &additionalHeader, AdditionalSectionsHeaderLen)
        if err != nil {
                log.Fatalf("parsing failed at position %d: %v", cursor, err)
        }
        cursor += c
        fmt.Printf("\nAdditional Sections: %d\n", additionalHeader.NumAdditionalSections)

        for {
                c, err = parse(file, &addSectionEntry, AdditionalSectionsEntryHeaderLen)
                if err != nil {
                        log.Fatalf("parsing failed at position %d: %v", cursor, err)
                }
                cursor += c

                break
        }
        for i = 0; i < additionalHeader.NumAdditionalSections; i++ {
                switch addSectionEntry.BlockID {
                case BlockIDProductInfo:
                        blockid = "Product Information"
                default:
                        blockid = fmt.Sprintf("unknown block id %d", addSectionEntry.BlockID)
                }
                fmt.Printf("* Additional Section Entry %d: BlockSize=%d, BlockID=%q\n", i, addSectionEntry.BlockSize, blockid)

                dataSize := addSectionEntry.BlockSize - AdditionalSectionsEntryHeaderLen
                addSectionData := make([]byte, dataSize, dataSize)
                c, err = parse(file, &addSectionData, int(dataSize))
                if err != nil {
                        log.Fatalf("parsing failed at position %d: %v", cursor, err)
                }
                cursor += c
                fmt.Printf("* Additional Section Entry %d Data (len=%d): %s\n", i, dataSize, addSectionData)
        }

        // the index isn't right after the content, so we need to seek to the start
        // of the index, using the start of the file as a base
        file.Seek(int64(header.OffsetToIndex), 0)
        cursor = int(header.OffsetToIndex)
        c, err = parse(file, &idxHeader, IndexHeaderLen)
        if err != nil {
                log.Fatalf("parsing failed at position %d: %v", cursor, err)
        }
        cursor += c
        fmt.Printf("\nIndex Size: %d\n", idxHeader.Size)

        w := new(tabwriter.Writer)
        w.Init(os.Stdout, 5, 0, 1, ' ', 0)
        for i = 0; ; i++ {
                c, err = parse(file, &idxEntryHeader, IndexEntryHeaderLen)
                if err != nil {
                        log.Fatalf("parsing failed at position %d: %v", cursor, err)
                }
                cursor += c
                var filename []byte
                oneChar := make([]byte, 1, 1)
                for {
                        c, err = file.Read(oneChar)
                        if err != nil {
                                log.Fatalf("parsing failed at position %d: %v", cursor, err)
                        }
                        cursor += c
                        if bytes.Equal(oneChar, []byte("\x00")) {
                                // null terminator
                                goto endFileName
                        }
                        filename = append(filename, oneChar...)
                }
        endFileName:

                // seek and read content
                file.Seek(int64(idxEntryHeader.OffsetToContent), 0)
                content := make([]byte, idxEntryHeader.Size, idxEntryHeader.Size)
                _, err = file.Read(content)
                if err != nil {
                        log.Fatalf("parsing of %q failed at position %d: %v", filename, idxEntryHeader.OffsetToContent, err)
                }
                var isCompressed = false
                // files in MAR archives can be compressed with xz, so we test
                // the first 6 bytes to check for that
                //                                                       /---XZ's magic number--\
                if len(content) > 6 && bytes.Equal(content[0:6], []byte("\xFD\x37\x7A\x58\x5A\x00")) {
                        isCompressed = true
                        /*
                                        this currently failed with error xz: unsupported filter count

                                // content is compressed with xz, decompress it
                                br := bytes.NewReader(content)
                                r, err := xz.NewReader(br)
                                if err != nil {
                                        log.Fatalf("decompressing xz failed with error %s", err)
                                }
                                buf := new(bytes.Buffer)
                                _, err = buf.ReadFrom(r)
                                if err != nil {
                                        log.Fatalf("reading from xz failed with error %s", err)
                                }
                                content = make([]byte, len(buf.Bytes()), len(buf.Bytes()))
                                copy(content, buf.Bytes())
                        */
                }

                // restore cursor position
                file.Seek(int64(cursor), 0)

                fmt.Fprintf(w, "* Index Entry %d:\tFileName=%q\tOffset To Content=%d\tStored Size=%s\tCompressed=%t\tPerm=%s\n",
                        i, filename, idxEntryHeader.OffsetToContent, humanize.Bytes(uint64(idxEntryHeader.Size)), isCompressed, os.FileMode(idxEntryHeader.Flags))
                if cursor == int(sigHeader.FileSize) {
                        goto endIndex
                }
        }
endIndex:
        w.Flush()

        // Make the signed block to verify the signatures.
        // by reading everything except the signature entries.
        // The beginning of the MAR until right after the signature
        // entry headers are covered, then the signatures are skipped,
        // then the additional sections until the end of the file are covered.
        file.Seek(0, 0)
        raw := make([]byte, HeaderLen+SignaturesHeaderLen)
        signature := make([]byte, 512)
        cursor, err = file.Read(raw)
        if err != nil {
                log.Fatal(err)
        }
        signedBlock = append(signedBlock, raw...)
        for i = 0; i < sigHeader.NumSignatures; i++ {
                raw = make([]byte, SignatureEntryHeaderLen)
                c, err = file.Read(raw)
                if err != nil {
                        log.Fatal(err)
                }
                cursor += c
                signedBlock = append(signedBlock, raw...)
                buffer := bytes.NewBuffer(raw)
                err = binary.Read(buffer, binary.BigEndian, sigEntryHeader)
                // jump over the signature to the next block
                //file.Seek(int64(sigEntryHeader.Size), 1)
                c, err = file.Read(signature)
                if err != nil {
                        log.Fatal(err)
                }
                cursor += c
        }
        // read the rest of the file
        raw = make([]byte, int(sigHeader.FileSize)-cursor)
        _, err = file.Read(raw)
        if err != nil {
                log.Fatal(err)
        }
        signedBlock = append(signedBlock, raw...)
        hashed := sha512.Sum384(signedBlock)
        fmt.Printf("\nSigned Block: len=%d sha384=%X\n", len(signedBlock), hashed)

        foundValidSig := false
        w = new(tabwriter.Writer)
        w.Init(os.Stdout, 5, 0, 1, ' ', 0)
        for keyName, keyPem := range releasePubKeys {
                block, _ := pem.Decode([]byte(keyPem))
                if block == nil {
                        log.Fatal("failed to parse PEM block containing the public key")
                }

                pub, err := x509.ParsePKIXPublicKey(block.Bytes)
                if err != nil {
                        log.Fatal("failed to parse DER encoded public key: " + err.Error())
                }
                rsaPub := pub.(*rsa.PublicKey)
                err = rsa.VerifyPKCS1v15(rsaPub, crypto.SHA384, hashed[:], signature)
                if err == nil {
                        fmt.Fprintf(w, "* %s\t(rsa %d bits):\tpass\n", keyName, rsaPub.N.BitLen())
                        foundValidSig = true
                } else {
                        fmt.Fprintf(w, "* %s\t(rsa %d bits):\tfailed \n", keyName, rsaPub.N.BitLen())
                }
        }
        w.Flush()
        if !foundValidSig {
                log.Fatal("Signature: failed")
        }

}

func parse(file *os.File, data interface{}, number int) (readBytes int, err error) {
        raw := make([]byte, number)
        readBytes, err = file.Read(raw)
        if err != nil {
                return
        }
        buffer := bytes.NewBuffer(raw)
        err = binary.Read(buffer, binary.BigEndian, data)
        return
}

//
// Automatically generated - do not edit!
//
var releasePubKeys = map[string]string{
        // From https://hg.mozilla.org/mozilla-central/raw-file/92f6879a8f9fc7e727d7c281c9fa9f538cb96cb5/toolkit/mozapps/update/updater/release_primary.der
        "release1_sha384": `-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxCHbY+fP3dvaP9XVbmK6
i4rbqo72INEWgDSYbr/DIYfCSzHC9H8pU8dyjt+Nd8OtoUZtBD1N9fP7SlrvPZSI
ZSW4k0e9Ky5aV3Uy+ivamSvYszkhqdeP2y7MBu73XHKYONR9PnKa+ovmREwSEI+h
1e0ebm8zvF7Ndwx0mOeZkDu9SDkDGg4aj2xrJyBBOuGVjuctMZ6l1davANI5xiJ0
GBEU3tR1gJs1T4vLBis5mEFn9y4kgyw/HrxmRYGnZL4fLb2fTI+pNW0Twu3KWwwi
LgLkkVrNWiHSk7YWqxjcg5IA3pQETQ17paTHoB5Mnkvuh6MkDXvRG5VgAHZAigr6
fJMsasOUaBeos/cD1LDQEIObpetlxc0Fiu/lvUts0755otkhI+yv35+wUa6GJrsE
CsT7c/LaFtQXg06aGXbMLDn0bE/e+nw9KWT/rE1iYXMFkzrqoTeYJ+v7/fD/ywU8
m8l4CZmXxzd/RogMrM3xl+j4ucAAltDQyL4yLySaIT05w5U8z2zJDEXFvpFDSRfF
K3kjLwGub7wNwaQDuh/msIUdavu4g+GNikCXAJ8AssLuYatyHoltd2tf+EIIDW3U
zzLpymnLo3cAz3IPfXyqVB+mcLcpqbHjl3hWms6l1wGtz6S4WqdrWs/KfzS5EyDK
r63xn1Rg/XFmR57EsFEXAZ8CAwEAAQ==
-----END PUBLIC KEY-----`,

        // From https://hg.mozilla.org/mozilla-central/raw-file/92f6879a8f9fc7e727d7c281c9fa9f538cb96cb5/toolkit/mozapps/update/updater/release_secondary.der
        "release2_sha384": `-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvki6CZE2td7jAtx/+51m
V7w+/xA16HegUXVaesBC/00jG6aAMRo7fczXolCzhMatBeTWrweXsiJ9UhwMhanj
V9uZ1Nj6ITBDtG7WB9ottf+GOpu8/V4PwwFWl4zQ5rjSvnZLGpLPY2KIN0wxArba
Aqz8XsP3WePY7RL+7mG1CX/HEXSDzWMN+OIjZTmd5Z7pkRpUIoRSlGu4bR7J9D31
xCEBnZqP4p8nCqOJZHUk0O5B93z9WprMggQ/BLW4AidAIgBLeSXmGRh4p+kVlYmb
KkMDn+/h/iuP4rhnG1+kk7thnQIGwaqa/MDqijpPtlkQTKPcbrw4MthiWgo2Ag0U
uNS2HqH1TCQMq/lslTgiEaJ1xYTE8xA9lYPS6nFzQpvmDOaaXMg7O6rdnDoCOKMi
pkb27RRlnZe8VV5OTF/e5yw6chEF7dSGfSv4HIMf6wKIWAznacmNCVDbwESrfOdG
VWWjT9Qvv92v/hnoVHdhYJ9sZKI5xVzM0bNZy25cQACFFFMMSfsutM5D8apqmOpm
OZF/aoKQeSAmE+HKAXt785x+buHjlYjqE1SmqG2GUOmvaFV8NeWvUOoeA8jtGEC+
qJ/32l7KXHVoVYje7hncEzxzR1VVURArga5PWIVnSEQoturNKNBPQ3pso6S/YmWO
V64NQxJ6oJ7swf3MkDa1enkCAwEAAQ==
-----END PUBLIC KEY-----`,

        // From https://hg.mozilla.org/mozilla-central/raw-file/58402b43c9e1e22d8a9976ee9a7e4ffeee1bbbf2/toolkit/mozapps/update/updater/release_primary.der
        "release1_sha1": `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvH4r94FpQ0gvr1hhTfV9
NUeWPJ5CN6TZRq7v/Dc4nkJ1J4IP1B3UEii34tcNKpy1nKupiZuTT6T1zQYT+z5x
3UkDF9qQboQ8RNb/BEz/cN3on/LTEnZ7YSraRL11M6cEB8mvmJxddCEquwqccRbs
Usp8WUB7uRv1w6Anley7N9F/LE1iLPwJasZypRnzWb3aYsJy0cMFOYy+OXVdpktn
qYqlNIjnt84u4Nil6UXnBbIJNUVOCY8wOFClNvVpubjPkWK1gtdWy3x/hJU5RpAO
K9cnHxq4M/I4SUWTWO3r7yweQiHG4Jyoc7sP1jkwjBkSG93sDEycfwOdOoZft3wN
sQIDAQAB
-----END PUBLIC KEY-----`,

        // From https://hg.mozilla.org/mozilla-central/raw-file/58402b43c9e1e22d8a9976ee9a7e4ffeee1bbbf2/toolkit/mozapps/update/updater/release_secondary.der
        "release2_sha1": `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq65HLYYaIvB/snHd7Oto
CFGCiV7mx6VMJb+25ZeFIQk7y5fsPDlgLG/V7a84hGVROp8C2gAHxOXXJlk0v/n6
dtruT0GxdLw4mUKB1uiPHLXV46k9ar/6QVgPRMWoJeeh3SVB2JyCtC+uqFca/N4D
VuZjnidGjqrDbQf1gr68cviZSBGzPGirIcYP4CKoNu3vB8BZWyI9NYn0+KfxVn0a
ynUKDd0zshI5FOBRAmmgKRB4tifefe41XQ7G8J62cGUlimH7Rbi1MQ3WFpkVdlh5
fciTekyH9fav66rj7erU/lcnoFJLKrf2Wpu04R0na7q5TACjJx8yYta6fbwCQU01
uwIDAQAB
-----END PUBLIC KEY-----`,

        // From https://hg.mozilla.org/mozilla-central/raw-file/92f6879a8f9fc7e727d7c281c9fa9f538cb96cb5/toolkit/mozapps/update/updater/nightly_aurora_level3_primary.der
        "nightly1_sha384": `-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAth151NGY8PBzn0bii9Yc
AjYHZDwP9Lj1c3owG0zLqW2kPcdp86QTAcoYunHGYFFakNG3tooZhzwkMjZ1OrXc
ERjD6AuVSGIBdsKtKP4vLtMjDUteFN4K2+rveozcnYFZuTWEajGu8uoYsv4QgdEA
nTBC39j0J33xlfUR+XKuxzhxNrFX+fRFWuLDJrPziMcVA/mzf0gXlhtEsfV0HYyg
yWpHdIWww+llysD1QOQAHk94Ss8c/4BFXFxlwlLeNlB1ZqLm1LsNy0jUy9EHeO3C
H6eqmiFEbpdjlrkJdgR1NcTzeY/Qf/nhWH6BAZrSapQycF7OSLU+rFWMQUElSPLc
NVl7oNAAfSYLTvRjPGi+mJK3wGFQw1EpwQl+elE1oj4+sHvIVpDrLb6btpxfr1cZ
pR4Di/hkOIymxEDWvtUhOxUXnYbDKQSDcAHKM/xR3sdIAiVtVuL4hyBwlAqkQc2j
H+SmnCbazgnq5+dN4y5DjoOgbZQ/koE3s3bUzzMeIxaul9v4gMtGROw3PQ3OZcP0
lgjPRhY+NeTnWMo2nGb4/eS6Cn2qFLfbEQjsj6pJJBNKfvK/gm1jXb3PgXXdf8+d
2xTPOX8QNpSK7C0w4vYlvSpYZlsx2cznEOV6LDqP0QHUnmd/k1xWRRGiQ7gtT+BV
Fn0h7JyTGmEdFu6l4OhS8hMCAwEAAQ==
-----END PUBLIC KEY-----`,

        // From https://hg.mozilla.org/mozilla-central/raw-file/92f6879a8f9fc7e727d7c281c9fa9f538cb96cb5/toolkit/mozapps/update/updater/nightly_aurora_level3_secondary.der
        "nightly2_sha384": `-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzxgPvz/iBTM1s8pPOYpF
Vfd/B1IGoNOwhh0zezL2QZHDqYZSLG3DMLQIQr3iEGwJq2wwRnOlZm5MqPfVKpif
68iSMwcNW83xgPJLKm2D/8z4RlhM3UUcq0ZOZFARC+mi4OYNmQg8BRRoCORvDpSR
DkZSujbR+nqnYg2bmWidt3KmHEpAne8/2jqNXw34tTERmCaIDU1XD6/M8vhalRXF
9Q4iFWoynoJ88gWdVOu2cfpAsnM/xmD5Zav6RKtGJlJtnpbQUPd5euXdfveT6tsj
kXjsk50L/WbBmr30it7mLwjzxhVlJ+zNWRJMUTipdNL+y+C4QY3e6MDNkIjKXjT7
MkTCHdDeYkFveRJ23eZ3FIcxATHqrUKnVQt3i3801V6zihaL8WmEf+H92K7/pvFV
HopZewG6jBU+AvCg4g/XJEbxYsKnuauL/56vkdsvhYkDKgJunjXA9jiCmNFeeeod
EOE0Ii6f2f3+3Q1quMMz1GnI5tt9qZsFwDfI989v4viWmLfXCCcVmZFnNszUDEHb
7uzbR1dQZtcHFBghsmiEdOS2Lc8jK3EW1liFPb+qq45Xh2vyJ5iLYIJnZqX2wQjq
zQo5Nr4g/hA1Se+bzZNs3JalT0UT1gQ4M71NAIrtjI/+tfnKLb4VJ6yC7GG6PwFI
hd/nHIowE+9e2+ry/tfDpFcCAwEAAQ==
-----END PUBLIC KEY-----`,

        // From https://hg.mozilla.org/mozilla-central/raw-file/58402b43c9e1e22d8a9976ee9a7e4ffeee1bbbf2/toolkit/mozapps/update/updater/nightly_aurora_level3_primary.der
        "nightly1_sha1": `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4j/IS3gWbyVVnWn4ZRnC
Fuzb6VAaHa0I+4E504ekhVAhbKlSfBstkLbXajdjUVAJpn02zWnOaTl5KAdpDpIp
SkdA4mK20ej3/Ij7gIt8IwaX+ArXL8mP84pxDn5BgaNADm3206Z6YQzc/TDYu529
qkDFmLqNUVRJAhPO+qqhKHIcVGh8HUHXN6XV1qOFip+UU0M474jAGgurVmAv8Rh7
VvM0v5KmB6V6WHwM5gwjg2yRY/o+xYIsNeSes9rpp+MOs/RnUA6LI4WZGY4YahvX
VclIXBDgbWPYtojexIJkmYj8JIIRsh3eCsrRRe14fq7cBurp3CxBYMlDHf0RUoaq
hQIDAQAB
-----END PUBLIC KEY-----`,

        // From https://hg.mozilla.org/mozilla-central/raw-file/58402b43c9e1e22d8a9976ee9a7e4ffeee1bbbf2/toolkit/mozapps/update/updater/nightly_aurora_level3_secondary.der
        "nightly2_sha1": `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7jCVFImsVY7ILLBHsqnL
sxkXqkFvT9pnlCKITKL1DuUe1C5dl2wxnUBLngufRNcfiInPSfhl07rEcmMJxsW3
2o7GxR5rqtZfGjBXerIRY36H1igXgODs+MuDuOBVe+ZJOwgGYoQoKP7THrtk/xr6
GKZUI8T4azeOxg60LNXQ1T0kAnrLJ5wZZqT6u8yvQxiCeiCyG6Upfnazb4mgrn0M
uJkvMZOHEuJwWT8ywfaXx/CN/jVt2OF+hCd20RVe08T5V6SjTM/QBgUtlRpQv2+e
4OVz3QsK5cN8ZYWHi/9MxcAkraDI55r67ZDgwmindyPII5VGHuMph6XXhXNFAG8l
MwIDAQAB
-----END PUBLIC KEY-----`,

        // From https://hg.mozilla.org/mozilla-central/raw-file/92f6879a8f9fc7e727d7c281c9fa9f538cb96cb5/toolkit/mozapps/update/updater/dep1.der
        "dep1_sha384": `-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA8Y6AS+xwKoXZl0X5qOKr
0I00xC4UN+IMjA1LIQoZ2GBkiqQF3q8v2nWTFE0+47+3NtP0l8tvsQY+LSYR4Fek
v2Vx4m/CAMKmWzW6Vtlj80y6rQ04V19l41bZXvCIBW5fm9sAvPgc7CngkcLySNqk
8vf57cUEpOmbsjSOCmK0j8hh03I1eWogpbAVEchSm1xN2sUJaVTvz5j8BfE6Vm0i
nN7V0zF+AOxzvntZIpfUqMZbHRiMkGn4l9rjia1Rz0qUc9RNCJkNocyKtQ2N2wnN
FjHpmK9x2V71cS1JQGhgLegrswPCAWY1lTmiLk9LweqGoVL0rqR4LCkb0VCaeSRe
6bUEYcU1ZQedE80zGKB3AfoC5br1shYY0xjmyRSCQ8m8WE60HzXhL8wczKrn5yoJ
iF6BxFwcYsvrWBPgIYVZLcqjODfR/M62o8yIfTC7yBcIdycJ0sWhB47dHAFxv1kc
wv8Ik9ftvDyupE8kwcl58fNOXz93j7IxMry/ey27NyYpESPOUNcjT8TP26FdGebg
4iJx0/LaYmaNUdchfBBlaYqGdH6ZGK0OeVxzHstGuG0gebm/igYcpaFxiQzvWijX
MIAU56s4g+yj7pSzT5/s9r8Gv+YhsNHKm4hnwLZaITV0lLMT5h/OZGseQTPMBnAR
hK3CIfcqG0I23hdwI29ZuUMCAwEAAQ==
-----END PUBLIC KEY-----`,

        // From https://hg.mozilla.org/mozilla-central/raw-file/92f6879a8f9fc7e727d7c281c9fa9f538cb96cb5/toolkit/mozapps/update/updater/dep2.der
        "dep2_sha384": `-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAzR/PTXo4ZUIV3p2mBwOy
1qEemi4ZW84TqO0W5ws5ENuYvKGusYETvSS/WnUEzI3J7aQOzAtCIuxEsaGZTXdX
Y5/oxcepKGzfSr7T8Wegklr0WIgi0Lili0n0DYRh4Aw7OUJy73N6gBS0QM0GYB0s
cJX/Ofr6nOXSxT5KWJO5joI8a9Fr4kpQK8gj0jiXhtGbZSkaGKoVzdzz7dua/jSj
HXM6EHjAO5PzJh9LDHqM5KiCUAKRVS3mz4jty/Qt1U4+qYmb8mu/ADWtyz/VV3VG
dbffLsSTVz3NSJD5lW8QxwXhFSCP4lHxKwFYl5CjIEhKRwoWV8JG0HjgNivPBYLX
A7m9lEwFden0mXayyHjgn3gBjYBUF7hfBjRi45DrPyayz6/1ZcdQlAuVoGWmPQZ9
gf0xUFnt7JadMdG74K87sPxJSGOtcOCfst9KozGP8451VzkSoOY712GcCfxzsAwP
NveKEfAVG8ayUiRFlFvNSQ13YlRltRwf0Gto2tJcgTWGKQLapi6Z6R55WquQyiaV
UbwNIJmNldl555LFw+dSeCugbFMnE92NWeRdU1iYkGUt8H1llW7R3vt8y4h77eXF
bpjl2nk6199VyCiHf9olnC5rBqLvf+xqduC0UJ+jWgxeFvbBcRJHEF0rA2XNNZPJ
RPlEUn3O+exsA1gHlcddQY0CAwEAAQ==
-----END PUBLIC KEY-----`,

        // From https://hg.mozilla.org/mozilla-central/raw-file/58402b43c9e1e22d8a9976ee9a7e4ffeee1bbbf2/toolkit/mozapps/update/updater/dep1.der
        "dep1_sha1": `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzjHSobdeiQ3JHP/cCIOp
WaX9y12rL5mIo9OR9bpqEZdD0yXJJJeZA887Mv8slqsM+qObMUpKvfEE6zyYPIZJ
ANib31neI5BBYHhfhf2f5EnkilSYlmU3Gx+uRsmsdt58PpYe124tOAGgca/8bUy3
eb6kUUTwvMI0oWQuPkGUaoHVQyj/bBMTrIkyF3UbfFtiX/SfOPvIoabNUe+pQHUe
pqC2+RxzDGj+shTq/hYhtXlptFzsEEb2+0foLy0MY8C30dP2QqbM2iavvr/P8OcS
Gm3H0TQcRzIEBzvPcIjiZi1nQj/r/3TlYRNCjuYT/HsNLXrB/U5Tc990jjAUJxdH
0wIDAQAB
-----END PUBLIC KEY-----`,

        // From https://hg.mozilla.org/mozilla-central/raw-file/58402b43c9e1e22d8a9976ee9a7e4ffeee1bbbf2/toolkit/mozapps/update/updater/dep2.der
        "dep2_sha1": `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1WIFPTzP2Q4c5/8o1w/L
oth5BE6pc7RlqxLC5vDIIoMHyLIYw7FJsaqnYEebBKjm2ZXqV7/94ILJEc+wgwqs
1hKx7qSonAZ1IEiDpaGwvbxIP/gTXKcHX0VOnXImy7vN2r++N0aJhn46gOfZ9cys
bUjMN2R6aSvPNpl1QDFd/3DVefP/7RG9Y0Wg7Tz4U6Ip4wR4MY839dMV1ObX8zQx
ikFkUzNDBbwTp3CLCcvR40GZdkQ2XfjFNZmlhmH6iJYmRwDT4SRnAiicdnDcK+o/
alRnlvBZWbO9ZoiXbyuxXjZRRRx6vO8UTEOQTsKmXBAGZCW6z0+AAlgvPnILgOG+
jQIDAQAB
-----END PUBLIC KEY-----`,
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment