Most of the dependent bugs have patches. One thing that is messy is that they want to keep the updater executable as small and standalone as possible, so they are doing things like using OS APIs to verify signatures (on Windows they use CryptoAPI and on Mac OS they use Apple's Security framework). I think they plan to use NSS on Linux, and we may want to use NSS everywhere for trust and auditability reasons.
While thinking about comment:10:ticket:13407 and that it probably is wise to "just" have a role signing key due to just one key for verifying our MARs I was wondering whether it would be feasible to take advantage of reproducibly built MAR files given that no human interaction is interfering here. This is definitely worth a new bug if it is worth one at all (and I am volunteering for coding this actually). Given your knowledge of the MAR signing code Mozilla provides do you think there are general obstacles to extend that to add support for a verification method relying on more than one key?
Given your knowledge of the MAR signing code Mozilla provides do you think there are general obstacles to extend that to add support for a verification method relying on more than one key?
I am not sure exactly what you are asking. Mozilla currently supports embedding zero or more signatures in a MAR file. The signatures are added using a program named signmar which is really just a more capable variant of the mar program. signmar requires an NSS certificate database that contains a private key plus a self-signed certificate.
Then, if you configure the Firefox build with --enable-verify-mar, one or two certificates are embedded in the updater program and signatures contained within any MAR file that is downloaded are checked against those certificates. All signatures must be verified using one or the other cert or the MAR file will be rejected; that is, if the MAR file contains two signatures both must be verifiable. And at least one signature must be present when --enable-verify-mar is turned on.
Oh, I wasn't aware that multiple signatures were already supported in this way. If that is the case, we may want to consider having two or three keys: one held by Georg, one by myself, and one on a dist server. Though this has downsides in that it would require Georg and I to always be available to sign builds.. I suppose we could instead share a builders key, and then have the second key live on a signing machine that other people can get access to in an emergency?
Might be too much to deal with for the first rollout, though.
Given your knowledge of the MAR signing code Mozilla provides do you think there are general obstacles to extend that to add support for a verification method relying on more than one key?
I am not sure exactly what you are asking. Mozilla currently supports embedding zero or more signatures in a MAR file. The signatures are added using a program named signmar which is really just a more capable variant of the mar program. signmar requires an NSS certificate database that contains a private key plus a self-signed certificate.
Then, if you configure the Firefox build with --enable-verify-mar, one or two certificates are embedded in the updater program and signatures contained within any MAR file that is downloaded are checked against those certificates. All signatures must be verified using one or the other cert or the MAR file will be rejected; that is, if the MAR file contains two signatures both must be verifiable. And at least one signature must be present when --enable-verify-mar is turned on.
Thanks. I was basically asking whether it is easily possible to avoid the bottleneck of just having one signing key. Originally, I was thinking in order to avoid that we somehow need to bolt the verification of the signing and hashing we do for the reproducible builds onto the MAR signing as a kind of additional assurance that everything is okay (like we have it now with a signature for each package and an "advanced verification" via the sah256sums and a couple of builder who sign that file with their own key). But, great that Mozilla supports having multiple signing keys as we may be able to leverage that work to get the same results or at least comparable ones (security-wise).
Oh, I wasn't aware that multiple signatures were already supported in this way. If that is the case, we may want to consider having two or three keys: one held by Georg, one by myself, and one on a dist server. Though this has downsides in that it would require Georg and I to always be available to sign builds.. I suppose we could instead share a builders key, and then have the second key live on a signing machine that other people can get access to in an emergency?
Might be too much to deal with for the first rollout, though.
Yes, I think that should be in a new ticket. I've created legacy/trac#13730 (moved) for it. I am especially interested in thinking about needing just a threshold of valid signatures which might release the burden on us to be always available for signing purposes.
I have a design question. The new updater binary has a dependency on various shared libraries that are bundled with the browser (libnss3.so, libnspr4.so, etc.) On Windows, these libraries are found by the OS when the updater is started because of the fix we made for legacy/trac#13594 (moved).
On Mac OS and Linux, the libraries won't be found. Possible solutions:
(1) Modify the browser to set LD_LIBRARY_PATH before launching the updater. This means that while it is running, the updater would use libraries that are possibly going to be updated. I think that is OK because both Linux and Mac OS allow rename and unlink on an open file.
I think whichever mechanism we use should be the same on all three platforms. I have a quick question about approach 2: would we be copying the old libraries into the update directory, or the new ones from the included mar?
I'm assuming old because the updater hasn't run at this point, and if this is true, it seems to me that option 1 is the winner (simply adding the libraries to the search path is better than trying to maintain a list and copy them, since its the same code either way).
I think whichever mechanism we use should be the same on all three platforms. I have a quick question about approach 2: would we be copying the old libraries into the update directory, or the new ones from the included mar?
I'm assuming old because the updater hasn't run at this point, and if this is true, it seems to me that option 1 is the winner (simply adding the libraries to the search path is better than trying to maintain a list and copy them, since its the same code either way).
Correct, we would be copying old libraries. If we can avoid copying them, it seems like a win.
There are two "Brian R. Bondy" commits (from mozilla-central, where the fix for 902761 has landed), one commit to backport some Mozilla patches that have r+ on the Mozilla side but have not yet been committed, and one commit that contains our changes to always use NSS and fix up various things.
The most recent commit (68a488187fde8a1f50e1e85e45b0f0beac15446c) will need to be discarded; it embeds a certificate that brade and I used for testing our own 4.5-alpha-1-ish builds.
Also, these changes cause the NSS certutil command to be built as well as signmar, which is a variant of the mar program that supports signing, verifying signatures, etc. (signmar uses NSS key and cert databases). We have some uncommitted builders/tor-browser-bundle changes that cause certutil and signmar to be included in the mar-tools-linux*.zip archives along with the dependent NSS and NSPR libraries. We will publish those patches soon along with other changes we plan to make to automate the signing process as much as possible (e.g., only prompt once for the NSS password).
Trac: Keywords: N/Adeleted, MikePerry201411R added Status: assigned to needs_review
dep1.der and dep2.der are no longer used; Mozilla used to use them for "depend" builds (maybe for their try server?).
The nightly_aurora_level3*.der files will be embedded in nightly builds. We need to decide what to do about those, if anything (at the moment, people who run our nightly builds do not expect to receive automated updates).
The release_*.der files will be embedded in our alpha, beta, and release builds. These are the most important ones.
The xpcshellCertificate.der is used by Mozilla for testing; it is embedded in all other builds, e.g., developer builds that lack an update channel.
I generated a test certificate by running these two commands:
I then replaced both release_primary.der and release_secondary.der with the contents of marsigner.der (currently, a signature is considered "good" if the key associated with either the primary or the secondary certificates was used to create the signature; other policies could be implemented).
The change to add the --createIncrementalMARs command line to update_responses looks good.
The other changes introduce a single makefile rule to generate the incremental mar files and sign them. I am wondering if we should separate the incremental mar files generation, and the signature, to allow a process like this:
build tor-browser
generate incremental mars
upload sha256sums.incrementals.txt of unsigned mar files
check that sha256sums.txt and sha256sums.incrementals.txt are matching
sign the mar files, update responses xml files and upload
The change to add the --createIncrementalMARs command line to update_responses looks good.
The other changes introduce a single makefile rule to generate the incremental mar files and sign them. I am wondering if we should separate the incremental mar files generation, and the signature, to allow a process like this:
build tor-browser
generate incremental mars
upload sha256sums.incrementals.txt of unsigned mar files
check that sha256sums.txt and sha256sums.incrementals.txt are matching
sign the mar files, update responses xml files and upload
It would be simple to keep 'incrementals' as a separate Make target. The reason I put everything in one script was to make it less likely that things would happen in the wrong order.
gk or mikeperry: What do you think? What will the release process look like once we need to sign the MAR files?
The change to add the --createIncrementalMARs command line to update_responses looks good.
The other changes introduce a single makefile rule to generate the incremental mar files and sign them. I am wondering if we should separate the incremental mar files generation, and the signature, to allow a process like this:
build tor-browser
generate incremental mars
upload sha256sums.incrementals.txt of unsigned mar files
check that sha256sums.txt and sha256sums.incrementals.txt are matching
sign the mar files, update responses xml files and upload
It would be simple to keep 'incrementals' as a separate Make target. The reason I put everything in one script was to make it less likely that things would happen in the wrong order.
gk or mikeperry: What do you think? What will the release process look like once we need to sign the MAR files?
I think we should use a process that allows independent builders to check whether they got the same results as we easily. And this means, I think, we should follow boklm's idea: building everything including the incremental MAR files and uploading everything and then in a separate step doing the signing and all the things needed for getting the updates delivered. I see at least two important reasons why we want to do it this way:
We want to have many builders to make it less likely our builds are compromised. Building with gitian is already tedious and we should not make it even more difficult to get matching builds which we we would if we included the signing before the SHA sum creation.
There may be people that trust our reproducible build system but not the complex signing process/code and fetching some update from some server. Following boklm's idea they could pretty well get the benefits of building Tor Browser themselves and applying the MAR update manually (which users are already doing).
That one looks good. Nit: s/so we have can/so we can/ in signmar.sh.
Regarding the incremental updates: I think we want to create the incremental .mar files during the usual build process just before we call the hash-bundles script. But that belongs to a different bug as we need some build process changes then, too (like having the config.yml already updated when starting the release builds etc.).
Gonna test that shortly but it seems to me that the signing can be done offline, right? Like, I could download the MAR files, get offline, enter my passphrase, get all MAR files signed, get online again and upload them?
We removed nss/cmd/certutil within the ifdef ENABLE_TESTS block and moved it outside so that nss/cmd/certutil is always included in NSS_DIRS (and therefore always compiled).
Gonna test that shortly but it seems to me that the signing can be done offline, right? Like, I could download the MAR files, get offline, enter my passphrase, get all MAR files signed, get online again and upload them?
I missed those comments -- thanks for pointing them out.
It is kind of amusing that NSS is viewed as less reliable than the OS-provided APIs. but the arguments in those comments make sense. But since we are stuck with NSS on Linux, I am still inclined to use it on Windows and Mac as well.
Oh, I forgot one nit:
{{{
// On Windows we rely on CyrptoAPI to do verifications so we don't need to
// initialize NSS at all there.
}}}
This (in updater.cpp) is a bit misleading as we rely on NSS on all platforms. Might be confusing for people reading our code.
We removed nss/cmd/certutil within the ifdef ENABLE_TESTS block and moved it outside so that nss/cmd/certutil is always included in NSS_DIRS (and therefore always compiled).
Oh, yes, sorry. Seems to have been kind of code-blindness on my side. :(
Newer certuils versions use SHA256 by default. This got implemented by https://bugzilla.mozilla.org/show_bug.cgi?id=1058933. So be sure to check the resulting cert with something like openssl x509 -in marsigner2.der -inform der -text | grep sha1WithRSAEncryption.
This seems important to fix before we ship a version of the browser that verifies MAR signatures. I do not fully understand all of the NSS and libmar code, but it looks to me like a signature algorithm ID of 1 is arbitrarily assigned to the only signature algorithm that is supported by the libmar code, SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE. What would be the best algorithm to use? I guess the signature algorithms that NSS supports can be seen by reading the sec_DecodeSigAlg() code here:
It seems fine to me if we want to hold off until 4.5-alpha-3 for this for stability and logistical reasons (key management, release delay), but that said I think a SHA1-based sig is still better than no sig.
Still, to pick from the ones listed in secvfy.c, probably either: SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE or SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA256_DIGEST.
It seems fine to me if we want to hold off until 4.5-alpha-3 for this for stability and logistical reasons (key management, release delay), but that said I think a SHA1-based sig is still better than no sig.
That's true but if we start signing with the current code then we get the additional problem of how to transition the users with a 4.5-alpha-2 to a later version that has additional signature algorithm support. Might be not a big deal but I think I'd prefer having the key creation/management issues properly sorted out (we don't even have them sorted out for the bundle signatures yet hinthint) and give the signed updates a bit more testing.
Still, to pick from the ones listed in secvfy.c, probably either: SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE or SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA256_DIGEST.
I have not looked at those algorithms yet but just wanted to add that we are probably going to use RSA 4096/SHA512 for the packages. Might make sense to use a comparable security level if it does not cost much.
On the build side of things, boklm contributed some changes to update_responses to select the action based on the program name. Kathy and I incorporated those changes into a new commit:
Please review this as well. The changes to gitian/descriptors/linux/gitian-firefox.yml and the contents of gitian/signmars.sh are the same as in the previous build-related patch that we posted.
I created a test certificate and exported it to a .der file by using these commands:
{{{
./certutil -d .nss -N
./certutil -d .nss -S -x -g 4096 -Z SHA512 -n marsigner -s "CN=Tor Browser MAR signing key" -t,,
./certutil -d .nss -L -r -n marsigner -o marsigner.der
}}}
This one looks good to me. Just one question: Why do we need the changes in cryptox.h? I was under the impression we have MAR_NSS defined anyway and thus there is no risk we would enter the #elif XP_MACOSX and #elif defined(XP_WIN) blocks.
I think I am going to test the MAR signing a bit. What scenarios did your testing already cover?
On the build side of things, boklm contributed some changes to update_responses to select the action based on the program name. Kathy and I incorporated those changes into a new commit:
Please review this as well. The changes to gitian/descriptors/linux/gitian-firefox.yml and the contents of gitian/signmars.sh are the same as in the previous build-related patch that we posted.
This one looks good to me. Just one question: Why do we need the changes in cryptox.h? I was under the impression we have MAR_NSS defined anyway and thus there is no risk we would enter the #elif XP_MACOSX and #elif defined(XP_WIN) blocks.
Just paranoia. Indeed, we should not be using any code in those blocks.
I think I am going to test the MAR signing a bit. What scenarios did your testing already cover?
We tested a variety of scenarios as we worked on signing. With the most recent code, we built a 4.5-alpha-2-ish build using the gitian-based process (embedding the certificate I described in comment:36) and ran the resulting builds on Mac OS 10.8.5, an old Fedora Linux32 system, and Win7.
To force an update, we modified the .htaccess file on a test update server of ours.
We tested that unsigned MARs were rejected.
We tested that signed MARs were accepted.
ERROR: Unsupported signature algorithm (SHA1 with RSA).ERROR: Unsupported signature algorithm (SHA1 with RSA).
How do I debug this? Any ideas? I did the following:
I created two certificates and added them atop of your tor-browser changes (commit 14447aca2f31c56ccadc289cef5f756e97d1f3a9) and tagged that (I just checked that I really included the 4k-bit certs with SHA-512)
I checked out your tor-browser-bundle branch (commit 186d339c394a7083faed064a218280fe52500f1b) and built a 4.5-alpha-2 with the tag mentioned in 1)
I bumped the version to 4.5-alpha-3 and excluded HTTPS-Everywhere from the bundling step and built that version, too.
I modified the config.yml to allow an incremental update from 4.5-alpha-2 to 4.5-alpha-3
I built that incremental update.
I downloaded the .mar files and the 4.5-alpha-2*tar.xz on my local computer and signed the .mar files (verifying the signature with signmar gives me no error and there is indeed a signature available; marsigner on my local computer is indeed the cert I added to tor-browser)
Ugh. Kathy and I messed up the file mode when we created a new branch (where we merged in boklm's changes and applies other small fixes). We will fix it.
I don't get the update working it seems. I get
{{{
ERROR: Unsupported signature algorithm (SHA1 with RSA).
ERROR: Unsupported signature algorithm (SHA1 with RSA).
}}}
How do I debug this? Any ideas? I did the following:
I created two certificates and added them atop of your tor-browser changes (commit 14447aca2f31c56ccadc289cef5f756e97d1f3a9) and tagged that (I just checked that I really included the 4k-bit certs with SHA-512)
...
update.log shows basically "failed: 19" and the above error messages are shown
Based on the info you provided, I think the MAR file has been signed using the older (now wrong) algorithm. Kathy and I added the "Unsupported signature algorithm (SHA1 with RSA)" log message to make it easier to detect this situation. But it sounds like you did everything correctly. Is there any chance you used an older signmar program (from mar-tools)? If you used the signmars-alpha make target the correct signmar should have been used though.
update.log shows basically "failed: 19" and the above error messages are shown
Based on the info you provided, I think the MAR file has been signed using the older (now wrong) algorithm. Kathy and I added the "Unsupported signature algorithm (SHA1 with RSA)" log message to make it easier to detect this situation. But it sounds like you did everything correctly. Is there any chance you used an older signmar program (from mar-tools)? If you used the signmars-alpha make target the correct signmar should have been used though.
Yes, you guessed correctly. I am not signing on my build server as I don't put the private keys there and had forgotten to update my local signmar copy. Interesting that it signed the .mar at all with the new key... Anyway, I found a new problem: signature verification works but for some reason my incremental update is broken now. In the update.log I get:
The full update is working fine, though. I was curious and tested a vanilla 4.5-alpha-2 and made exactly the same changes as I did when testing your patch and it turned out that incremental update is working. Thus, I suspect there is something in the new code that is causing this. Any ideas?
And one request: Could you make the path to the nssdb configurable by an environment variable (e.g. NSSDBPATH)? For security reasons I plan to keep my signing keys offline using them offline directly from the storage device and hard-coding the path to the database does not work so well under that scenario.
And one request: Could you make the path to the nssdb configurable by an environment variable (e.g. NSSDBPATH)? For security reasons I plan to keep my signing keys offline using them offline directly from the storage device and hard-coding the path to the database does not work so well under that scenario.
Oh, and please make the name of the cert configurable as well (I think via environment variable might be enough for now, too) in order to avoid bottlenecks from the beginning.
If I sign the .mar files with the key embedded in the second certificate I get
ERROR: Error verifying signature.ERROR: Not all signatures were verified.
But the update with the full .mar file works and the one with the incremental .mar file is broken as described above. I guess these errors occur as the verifier is first trying the first key which results in an error and then falling back to the second one. I am not sure whether users get to see these errors during a "real" update. They might get confused and thus it might be better to show errors only if the signature verification fails. On the other hand it might be helpful later on when we embed more than one signature to log verification failures even if the signature verification succeeds (for instance if we have two signatures but require only one to succeed). So, maybe we should leave that for now as-is?
The full update is working fine, though. I was curious and tested a vanilla 4.5-alpha-2 and made exactly the same changes as I did when testing your patch and it turned out that incremental update is working. Thus, I suspect there is something in the new code that is causing this. Any ideas?
Got that working although I don't know how. I re-did the whole procedure until it worked. :)
One final thing: I needed to copy libnss3.dylib and libmozglue.dylib to the directory where updater was on my OS X machine. Otherwise the update failed.
Yes, you guessed correctly. I am not signing on my build server as I don't put the private keys there and had forgotten to update my local signmar copy. Interesting that it signed the .mar at all with the new key... Anyway, I found a new problem: signature verification works but for some reason my incremental update is broken now. In the update.log I get:
{{{
SOURCE DIRECTORY /home/firefox64/signtest/tor-browser_en-US/Browser/updates
DESTINATION DIRECTORY /home/firefox64/signtest/tor-browser_en-US/Browser
failed: 23
calling QuitProgressUI
}}}
The full update is working fine, though. I was curious and tested a vanilla 4.5-alpha-2 and made exactly the same changes as I did when testing your patch and it turned out that incremental update is working. Thus, I suspect there is something in the new code that is causing this. Any ideas?
I am not sure exactly what happened, but the product information block for the incremental MAR file must have contained the wrong version number. Unfortunately, the mar and signmar programs have a default version number embedded in them at build time, which is used to set the version within the Product Information Block of created MAR files. So we need to be really careful which mar or signmar program is used when the MAR files are created or we will need to modify Mozilla's make_incremental_update.sh and make_full_update.sh scripts to let us pass in the product version when we create a MAR file.
You can use the -T option with mar and signmar to see the version number that is embedded within the product info block. Kathy and I were hoping that using the default version number would not be a problem, but it may be depending on our signing procedure. Also, the mar and signmar programs support a -i option that can be used to "refresh" the product info that is embedded within a MAR file (including setting a new version number). The refresh can only be done on an unsigned MAR file. But if we need to, we could do that before signing the files. But I would like to know where the process went wrong for you (if you can figure that out).
And one request: Could you make the path to the nssdb configurable by an environment variable (e.g. NSSDBPATH)? For security reasons I plan to keep my signing keys offline using them offline directly from the storage device and hard-coding the path to the database does not work so well under that scenario.
Oh, and please make the name of the cert configurable as well (I think via environment variable might be enough for now, too) in order to avoid bottlenecks from the beginning.
Sure. We will use NSS_DB_DIR and NSS_CERTNAME to match the variable names used inside signmar.sh.
If I sign the .mar files with the key embedded in the second certificate I get
{{{
ERROR: Error verifying signature.
ERROR: Not all signatures were verified.
}}}
But the update with the full .mar file works and the one with the incremental .mar file is broken as described above. I guess these errors occur as the verifier is first trying the first key which results in an error and then falling back to the second one.
libmar writes those error messages to stderr. I don't think users will see them except at the terminal or if they capture stderr. Certainly they will have to go look for them. I think we should keep as possibly useful diagnostic messages (although "ERROR:" is misleading in this case).
One final thing: I needed to copy libnss3.dylib and libmozglue.dylib to the directory where updater was on my OS X machine. Otherwise the update failed.
Okay, good. I feared we had a Windows like problem visible on my machine and had forgotten about the DYLD_LIBRARY_PATH workaround, sorry.
I am happy now. :) Feel free to merge your patches squashed to the repos or let me know if I should do it. My current plan is to generate the signing keys tomorrow and land them atop of your tor-browser patch(es).
Okay, pushed. One final thing: Given that Mozilla's certificates were only valid in a three month period several years ago it seems the related cert attributes are not checked during signature verification and our certificates are essentially never invalid, right?
Okay, pushed. One final thing: Given that Mozilla's certificates were only valid in a three month period several years ago it seems the related cert attributes are not checked during signature verification and our certificates are essentially never invalid, right?
Yes. I am sorry we forgot to mention this sooner. Looking at the code in libmar, the public key is extracted from the cert data (that is compiled into the updater) via a couple of NSS calls:
CERT_NewTempCertificate() and CERT_ExtractPublicKey(). I don't think those calls to do cert validity checks, and I don't think the signature verifications calls do either, e.g., NSS_VerifySignature().
On the one hand, this is good because it means that old browsers can verify the MAR signatures even after the signing key expires. On the other hand, there does not seem to be a way to revoke a certificate.
On the one hand, this is good because it means that old browsers can verify the MAR signatures even after the signing key expires. On the other hand, there does not seem to be a way to revoke a certificate.
Do we need to fix this?
Definitely not in this ticket if at all. Having the certificate only valid for a certain amount of time would not help much as the procedure in all cases of key exchange (be it due to compromise, be it due to key expiry, be it due to a lost private key, ...) would be the same: exchanging the key in question with a new one, baking it into Tor Browser and signing the MAR files from now on with the new key (too).
Trac: Status: needs_review to closed Resolution: N/Ato fixed