mar

package module
v0.0.0-...-8180aff Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 14, 2021 License: MPL-2.0 Imports: 20 Imported by: 3

README

MARGO: Mozilla ARchive library written in Go

Test 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"!

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

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

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL