README

MARGO: Mozilla ARchive library written in Go

Build Status GoDoc Coverage Status

import "go.mozilla.org/mar"

Requires Go 1.10

Margo is a fairly secure MAR parser written to allow autograph to sign Firefox MAR files. Its primary focus is signature, but it can also be used to parse, create and verify signatures on existing MAR files.

Take a look at example_test.go for a taste of the API, or run the command line tools under examples/.

FAQ

Why is it called "margo"?

it's subtle: it's a "mar" library, written in "go". get it? "margo"!

Expand ▾ Collapse ▴

Documentation

Overview

    Package mar implements support for the Mozilla ARchive format used by the Application Update Service of Firefox.

    The MAR format is specified at https://wiki.mozilla.org/Software_Update:MAR

    This package is primarily used to sign MARs by first parsing them via the Unmarshal function, then signing them with either RSA or ECDSA keys.

    // read a MAR file from disk
    input, _ := ioutil.ReadFile("/path/to/firefox.mar")
    // parse it
    _ = mar.Unmarshal(input, &file)
    // prepare a signature using a given RSA key
    file.PrepareSignature(rsaKey, rsaKey.Public())
    // sign
    _ = file.FinalizeSignatures()
    // write out the signed mar file
    output, _ := file.Marshal()
    ioutil.WriteFile("/path/to/signed_firefox.mar", output, 0644)
    

    It can also be used to create new MARs and manipulate existing ones.

    // create a new MAR
    marFile := mar.New()
    // Add data to the content section
    marFile.AddContent([]byte("cariboumaurice"), "/foo/bar", 640)
    // Add product information to the additional section
    m.AddProductInfo("caribou maurice v1.2")
    // Add random data to the additional section
    m.AddAdditionalSection([]byte("foo bar baz"), uint32(1664))
    

    The MAR data structure exposes all internal fields, including offsets, sizes, etc. Those fields can be manipulated directly, but are ignored and recomputed when marshalling.

    The parser is fairly secure and will refuse to parse files that have duplicate content or try to reference the same data chunk multiple times. Doing so requires keeping track of previously parsed sections of a MAR, which induces a significant memory cost. Be mindful of allocated memory if you're going to parse a lot of very large MAR before the garbage collector has a chance to reclaim memory from previously parsed files.

    Various limits are enforced, take a look at errors.go for the details.

    Example
    Output:
    
    MAR file signed and parsed without error
    

    Index

    Examples

    Constants

    View Source
    const (
    	// MarIDLen is the length of the MAR ID header.
    	// A MAR file starts with 4 bytes containing the MAR ID, typically "MAR1"
    	MarIDLen = 4
    
    	// OffsetToIndexLen is the length of the offset to index value.
    	// The MAR file continues with the position of the index relative
    	// to the beginning of the file
    	OffsetToIndexLen = 4
    
    	// FileSizeLen is a uint64 that contains the total size of the MAR in bytes
    	FileSizeLen = 8
    
    	// SignaturesHeaderLen is the length of the signatures header that
    	// contains the number of signatures in the MAR
    	SignaturesHeaderLen = 4
    
    	// SignatureEntryHeaderLen is the length of the header of each signature entry
    	// Each signature entry contains an algorithm and a size, each on 4 bytes
    	SignatureEntryHeaderLen = 8
    
    	// AdditionalSectionsHeaderLen is the length of the additional sections header
    	// Optional additional sections can be added, their number is stored on 4 bytes
    	AdditionalSectionsHeaderLen = 4
    
    	// AdditionalSectionsEntryHeaderLen is the length of the header of each
    	// additional section, containing a block size and identifier on 4 bytes each
    	AdditionalSectionsEntryHeaderLen = 8
    
    	// IndexHeaderLen is the length of the index header
    	// The size of the index is stored in a header on 4 bytes
    	IndexHeaderLen = 4
    
    	// IndexEntryHeaderLen is the length of the header of each index entry.
    	// Each index entry contains a header with an offset to content (relative to
    	// the beginning of the file), a content size and permission flags,
    	// each on 4 bytes
    	IndexEntryHeaderLen = 12
    
    	// BlockIDProductInfo is the ID of a Product Information Block
    	// in additional sections
    	BlockIDProductInfo = 1
    )
    View Source
    const (
    	// SigAlgRsaPkcs1Sha1 is the ID of a signature of type RSA-PKCS1-SHA1
    	SigAlgRsaPkcs1Sha1 = 1
    
    	// SigAlgRsaPkcs1Sha384 is the ID of a signature of type RSA-PKCS1-SHA384
    	SigAlgRsaPkcs1Sha384 = 2
    
    	// SigAlgEcdsaP256Sha256 is the ID of a signature of type ECDSA on NIST curve P256 with SHA256
    	SigAlgEcdsaP256Sha256 = 3
    
    	// SigAlgEcdsaP384Sha384 is the ID of a signature of type ECDSA on NIST curve P384 with SHA384
    	SigAlgEcdsaP384Sha384 = 4
    )

      Signature types

      Variables

      View Source
      var FirefoxReleasePublicKeys = map[string]string{
      
      	"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-----`,
      
      	"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-----`,
      
      	"release1_sha1": `-----BEGIN PUBLIC KEY-----
      MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvH4r94FpQ0gvr1hhTfV9
      NUeWPJ5CN6TZRq7v/Dc4nkJ1J4IP1B3UEii34tcNKpy1nKupiZuTT6T1zQYT+z5x
      3UkDF9qQboQ8RNb/BEz/cN3on/LTEnZ7YSraRL11M6cEB8mvmJxddCEquwqccRbs
      Usp8WUB7uRv1w6Anley7N9F/LE1iLPwJasZypRnzWb3aYsJy0cMFOYy+OXVdpktn
      qYqlNIjnt84u4Nil6UXnBbIJNUVOCY8wOFClNvVpubjPkWK1gtdWy3x/hJU5RpAO
      K9cnHxq4M/I4SUWTWO3r7yweQiHG4Jyoc7sP1jkwjBkSG93sDEycfwOdOoZft3wN
      sQIDAQAB
      -----END PUBLIC KEY-----`,
      
      	"release2_sha1": `-----BEGIN PUBLIC KEY-----
      MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq65HLYYaIvB/snHd7Oto
      CFGCiV7mx6VMJb+25ZeFIQk7y5fsPDlgLG/V7a84hGVROp8C2gAHxOXXJlk0v/n6
      dtruT0GxdLw4mUKB1uiPHLXV46k9ar/6QVgPRMWoJeeh3SVB2JyCtC+uqFca/N4D
      VuZjnidGjqrDbQf1gr68cviZSBGzPGirIcYP4CKoNu3vB8BZWyI9NYn0+KfxVn0a
      ynUKDd0zshI5FOBRAmmgKRB4tifefe41XQ7G8J62cGUlimH7Rbi1MQ3WFpkVdlh5
      fciTekyH9fav66rj7erU/lcnoFJLKrf2Wpu04R0na7q5TACjJx8yYta6fbwCQU01
      uwIDAQAB
      -----END PUBLIC KEY-----`,
      
      	"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-----`,
      
      	"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-----`,
      
      	"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-----`,
      
      	"nightly2_sha1": `-----BEGIN PUBLIC KEY-----
      MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7jCVFImsVY7ILLBHsqnL
      sxkXqkFvT9pnlCKITKL1DuUe1C5dl2wxnUBLngufRNcfiInPSfhl07rEcmMJxsW3
      2o7GxR5rqtZfGjBXerIRY36H1igXgODs+MuDuOBVe+ZJOwgGYoQoKP7THrtk/xr6
      GKZUI8T4azeOxg60LNXQ1T0kAnrLJ5wZZqT6u8yvQxiCeiCyG6Upfnazb4mgrn0M
      uJkvMZOHEuJwWT8ywfaXx/CN/jVt2OF+hCd20RVe08T5V6SjTM/QBgUtlRpQv2+e
      4OVz3QsK5cN8ZYWHi/9MxcAkraDI55r67ZDgwmindyPII5VGHuMph6XXhXNFAG8l
      MwIDAQAB
      -----END PUBLIC KEY-----`,
      
      	"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-----`,
      
      	"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-----`,
      
      	"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-----`,
      
      	"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-----`,
      }

        FirefoxReleasePublicKeys contains a map of PEM encoded public keys used to verify signatures on MAR files. This map is automatically generated, do not edit it by hand!

        Functions

        func Hash

        func Hash(input []byte, sigalg uint32) (output []byte, h crypto.Hash, err error)

          Hash takes an input and a signature algorithm and returns its hashed value

          func Sign

          func Sign(key crypto.PrivateKey, rand io.Reader, digest []byte, sigalg uint32) (sigData []byte, err error)

            Sign signs digest with the private key, possibly using entropy from rand

            func Unmarshal

            func Unmarshal(input []byte, file *File) error

              Unmarshal takes an unparsed MAR file as input and parses it into a File struct. The MAR format is described at https://wiki.mozilla.org/Software_Update:MAR but don't believe everything it says, because the format has changed over the years to support more fields, and of course the MarID has not changed since. There's a bit of magic in this function to detect which version of a MAR we're dealing with, and store that in the Revision field of the file. 2005 is an old MAR, 2012 is a current one with signatures and additional sections.

              func VerifyHashSignature

              func VerifyHashSignature(signature []byte, digest []byte, hashAlg crypto.Hash, key crypto.PublicKey) error

                VerifyHashSignature takes a signature, the digest of a signed MAR block, a hash algorithm and a public key and returns nil if a valid signature is found, or an error if it isn't

                func VerifySignature

                func VerifySignature(input []byte, signature []byte, sigalg uint32, key crypto.PublicKey) error

                  VerifySignature takes a signed block, a signature, an algorithm id and a public key and returns nil if the signature verifies, or an error if it does not

                  Types

                  type AdditionalSection

                  type AdditionalSection struct {
                  	AdditionalSectionEntryHeader `json:"additional_section_entry" yaml:"additional_section_entry"`
                  	// Data contains the additional section data
                  	Data []byte `json:"data" yaml:"-"`
                  }

                    AdditionalSection is a single additional section on the MAR file

                    type AdditionalSectionEntryHeader

                    type AdditionalSectionEntryHeader struct {
                    	// BlockSize is the size of the additional section in bytes, including
                    	// the header and the following data. You need to substract the header length
                    	// to parse just the data..
                    	BlockSize uint32 `json:"block_size" yaml:"block_size"`
                    	// BlockID is the identifier of the block.
                    	// BlockIDProductInfo (1) for Product Information
                    	BlockID uint32 `json:"block_id" yaml:"block_id"`
                    }

                      AdditionalSectionEntryHeader is the header of each additional section that contains the block size and ID

                      type AdditionalSectionsHeader

                      type AdditionalSectionsHeader struct {
                      	// NumAdditionalSections is the count of additional sections
                      	NumAdditionalSections uint32 `json:"num_additional_sections" yaml:"num_additional_sections"`
                      }

                        AdditionalSectionsHeader contains the number of additional sections in the MAR file

                        type Entry

                        type Entry struct {
                        	// Data contains the raw data of the entry. It may still be compressed.
                        	Data []byte `json:"data" yaml:"-"`
                        	// IsCompressed is set to true if the Data is compressed with xz
                        	IsCompressed bool `json:"is_compressed" yaml:"-"`
                        }

                          Entry is a single file entry in the MAR file. If IsCompressed is true, the content is compressed with xz

                          type File

                          type File struct {
                          	MarID                    string                   `json:"mar_id" yaml:"mar_id"`
                          	OffsetToIndex            uint32                   `json:"offset_to_index" yaml:"offset_to_index"`
                          	Size                     uint64                   `json:"size" yaml:"size"`
                          	ProductInformation       string                   `json:"product_information,omitempty" yaml:"product_information,omitempty"`
                          	SignaturesHeader         SignaturesHeader         `json:"signature_header" yaml:"signature_header"`
                          	Signatures               []Signature              `json:"signatures" yaml:"signatures"`
                          	AdditionalSectionsHeader AdditionalSectionsHeader `json:"additional_sections_header" yaml:"additional_sections_header"`
                          	AdditionalSections       []AdditionalSection      `json:"additional_sections" yaml:"additional_sections"`
                          	IndexHeader              IndexHeader              `json:"index_header" yaml:"index_header"`
                          	Index                    []IndexEntry             `json:"index" yaml:"index"`
                          	Content                  map[string]Entry         `json:"-" yaml:"-"`
                          	Revision                 int                      `json:"revision" yaml:"revision"`
                          	// contains filtered or unexported fields
                          }

                            File is a parsed MAR file.

                            func New

                            func New() *File

                              New returns an initialized MAR data structure

                              func (*File) AddAdditionalSection

                              func (file *File) AddAdditionalSection(data []byte, blockID uint32)

                                AddAdditionalSection stores data in the additional section of a MAR

                                func (*File) AddContent

                                func (file *File) AddContent(data []byte, name string, flags uint32) error

                                  AddContent stores content in a MAR and creates a new entry in the index

                                  func (*File) AddProductInfo

                                  func (file *File) AddProductInfo(productInfo string)

                                    AddProductInfo adds a product information string (typically, the version of firefox) into the additional sections of a MAR

                                    func (*File) FinalizeSignatures

                                    func (file *File) FinalizeSignatures() error

                                      FinalizeSignatures calculates RSA signatures on a MAR file and stores them in the Signatures slice

                                      func (*File) Marshal

                                      func (file *File) Marshal() ([]byte, error)

                                        Marshal returns an []byte of the marshalled MAR file that follows the expected MAR binary format. It expects a properly constructed MAR object with the index and content already in place. It also should already be signed, as the output of this function can no longer be modified.

                                        func (*File) MarshalForSignature

                                        func (file *File) MarshalForSignature() ([]byte, error)

                                          MarshalForSignature returns an []byte of the data to be signed, or verified

                                          func (*File) PrepareSignature

                                          func (file *File) PrepareSignature(key crypto.PrivateKey, pubkey crypto.PublicKey) error

                                            PrepareSignature adds a new signature header to a MAR file but does not sign yet. You have to call FinalizeSignature to actually sign the MAR file.

                                            func (*File) VerifySignature

                                            func (file *File) VerifySignature(key crypto.PublicKey) error

                                              VerifySignature attempts to verify signatures in the MAR file using the provided public key until one of them passes. A valid signature is indicated by returning a nil error.

                                              func (*File) VerifyWithFirefoxKeys

                                              func (file *File) VerifyWithFirefoxKeys() (keys []string, isSigned bool, err error)

                                                VerifyWithFirefoxKeys checks each signature in the MAR file against the list of known Firefox signing keys, and returns isSigned = true if at least one signature validates against a known key. It also returns the names of the signing keys in an []string

                                                type IndexEntry

                                                type IndexEntry struct {
                                                	IndexEntryHeader `json:"index_entry" yaml:"index_entry"`
                                                	// Filename is the name of the file being indexed
                                                	FileName string `json:"file_name" yaml:"file_name"`
                                                }

                                                  IndexEntry is a single index entry in the MAR index

                                                  type IndexEntryHeader

                                                  type IndexEntryHeader struct {
                                                  	// OffsetToContent is the position in bytes of the entry data relative
                                                  	// to the start of the MAR file
                                                  	OffsetToContent uint32 `json:"offset_to_content" yaml:"offset_to_content"`
                                                  	// Size is the size of the data in bytes
                                                  	Size uint32 `json:"size" yaml:"size"`
                                                  	// Flags is the file permission bits in standard unix-style format
                                                  	Flags uint32 `json:"flags" yaml:"flags"`
                                                  }

                                                    IndexEntryHeader is the header of each index entry that contains the offset to content, size and flags

                                                    type IndexHeader

                                                    type IndexHeader struct {
                                                    	// Size is the size of the index entries, in bytes
                                                    	Size uint32 `json:"size" yaml:"size"`
                                                    }

                                                      IndexHeader is the size of the index section of the MAR file, in bytes

                                                      type Signature

                                                      type Signature struct {
                                                      	SignatureEntryHeader `json:"signature_entry" yaml:"signature_entry"`
                                                      	// Algorithm is a string that represents the signing algorithm name
                                                      	Algorithm string `json:"algorithm" yaml:"algorithm"`
                                                      	// Data is the signature bytes
                                                      	Data []byte `json:"data" yaml:"-"`
                                                      	// contains filtered or unexported fields
                                                      }

                                                        Signature is a single signature on the MAR file

                                                        type SignatureEntryHeader

                                                        type SignatureEntryHeader struct {
                                                        	// AlgorithmID is either SigAlgRsaPkcs1Sha1 (1) or SigAlgRsaPkcs1Sha384 (2)
                                                        	AlgorithmID uint32 `json:"algorithm_id" yaml:"algorithm_id"`
                                                        	// Size is the size of the signature data in bytes
                                                        	Size uint32 `json:"size" yaml:"size"`
                                                        }

                                                          SignatureEntryHeader is the header of each signature entry that contains the Algorithm ID and Size

                                                          type SignaturesHeader

                                                          type SignaturesHeader struct {
                                                          	// NumSignatures is the count of signatures
                                                          	NumSignatures uint32 `json:"num_signatures" yaml:"num_signatures"`
                                                          }

                                                            SignaturesHeader contains the number of signatures in the MAR file

                                                            Directories

                                                            Path Synopsis
                                                            cmd