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 ¶
package main import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "crypto/rsa" "fmt" "log" "go.mozilla.org/mar" ) func main() { marFile := mar.New() marFile.AddContent([]byte("cariboumaurice"), "/foo/bar", 640) // make a new rsa key and add it for signature rsaPrivKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { log.Fatalf("rsa key generation failed with: %v", err) } marFile.PrepareSignature(rsaPrivKey, rsaPrivKey.Public()) // make a new ecdsa key and add it for signature ecdsaPrivKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { log.Fatalf("ecdsa key generation failed with: %v", err) } marFile.PrepareSignature(ecdsaPrivKey, ecdsaPrivKey.Public()) // once both keys are added to the file, finalize the signature err = marFile.FinalizeSignatures() if err != nil { log.Fatalf("mar signature failed with error: %v", err) } // write out the MAR file outputMar, err := marFile.Marshal() if err != nil { log.Fatalf("mar marshalling failed with error: %v", err) } // reparse the MAR to make sure it goes through fine var reparsedMar mar.File err = mar.Unmarshal(outputMar, &reparsedMar) if err != nil { log.Fatalf("mar unmarshalling failed with error: %v", err) } // verify the signatures err = reparsedMar.VerifySignature(rsaPrivKey.Public()) if err != nil { log.Fatalf("failed to verify rsa signature: %v", err) } err = reparsedMar.VerifySignature(ecdsaPrivKey.Public()) if err != nil { log.Fatalf("failed to verify ecdsa signature: %v", err) } fmt.Printf("MAR file signed and parsed without error") }
Output: MAR file signed and parsed without error
Index ¶
- Constants
- Variables
- func Hash(input []byte, sigalg uint32) (output []byte, h crypto.Hash, err error)
- func Sign(key crypto.PrivateKey, rand io.Reader, digest []byte, sigalg uint32) (sigData []byte, err error)
- func Unmarshal(input []byte, file *File) error
- func VerifyHashSignature(signature []byte, digest []byte, hashAlg crypto.Hash, key crypto.PublicKey) error
- func VerifySignature(input []byte, signature []byte, sigalg uint32, key crypto.PublicKey) error
- type AdditionalSection
- type AdditionalSectionEntryHeader
- type AdditionalSectionsHeader
- type Entry
- type File
- func (file *File) AddAdditionalSection(data []byte, blockID uint32)
- func (file *File) AddContent(data []byte, name string, flags uint32) error
- func (file *File) AddProductInfo(productInfo string)
- func (file *File) FinalizeSignatures() error
- func (file *File) Marshal() ([]byte, error)
- func (file *File) MarshalForSignature() ([]byte, error)
- func (file *File) PrepareSignature(key crypto.PrivateKey, pubkey crypto.PublicKey) error
- func (file *File) VerifySignature(key crypto.PublicKey) error
- func (file *File) VerifyWithFirefoxKeys() (keys []string, isSigned bool, err error)
- type IndexEntry
- type IndexEntryHeader
- type IndexHeader
- type Signature
- type SignatureEntryHeader
- type SignaturesHeader
Examples ¶
Constants ¶
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 )
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 ¶
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 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 ¶
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
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 (*File) AddAdditionalSection ¶
AddAdditionalSection stores data in the additional section of a MAR
func (*File) AddContent ¶
AddContent stores content in a MAR and creates a new entry in the index
func (*File) AddProductInfo ¶
AddProductInfo adds a product information string (typically, the version of firefox) into the additional sections of a MAR
func (*File) FinalizeSignatures ¶
FinalizeSignatures calculates RSA signatures on a MAR file and stores them in the Signatures slice
func (*File) Marshal ¶
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 ¶
MarshalForSignature returns an []byte of the data to be signed, or verified
func (*File) PrepareSignature ¶
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 ¶
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 ¶
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