Building a better catalog file

Inside a windows driver package you’ll probably see a few .dll‘s, a .inf file and a .cat file. If you’ve ever been curious in Windows you’ll have double clicked it and it would show some technical information about the driver and some information about who signed the file.

We want to use this file to avoid having to get vendors to manually sign the firmware file with a GPG detached signature, which also implies trusting the Microsoft WHQL certificate. These are my notes on my adventure so far.

There are not many resources on this stuff, and I’d like to thank dwmw2 and dhowels for all their help so far answering all my stupid questions. osslsigncode is also useful to see how other signing is implemented.

So, the basics. A .cat file is a SMIME PKCS DER file. We can dump the file using:

openssl asn1parse -in ecfirmware.cat  -inform DER

and if we were signing just one file we should be able to verify the .cat file with something like this:

wget http://www.microsoft.com/pki/certs/MicRooCerAut_2010-06-23.crt
openssl x509 -in MicRooCerAut_2010-06-23.crt -inform DER -out ms/msroot.pem -outform PEM
cat ms/*.pem > ms/certs.pem
openssl smime -verify -CAfile ms/certs.pem -in ecfirmware.cat -inform DER -attime $(date +%s --date=2015-01-01) -content ECFirmware.38.7.50.0.cap
Verification failed

(Ignore the need to have the root certificate for now, that seems to be a bug in OpenSSL and they probably have bigger fires to fight at this point)

…but it’s not. We have a pkcs7-signed blob and we need to work out how to get the signature to actually *match* and then we have to work out how to interpret the pkcs7-signed data blob, and use the sha256sums therein to validate the actual data. OpenSSL doesn’t know how to interpret the MS content type OID (1.3.6.1.4.1.311.10.1) so it wimps out and doesn’t put any data into the digest at all.

We can get the blob using a simple:

dd if=ecfirmware.cat of=ecfirmware.cat.payload bs=1 skip=66 count=1340

…which now verifies:

openssl smime -verify -CAfile ms/certs.pem -in ecfirmware.cat -inform DER -attime $(date +%s --date=2015-01-01) -content ecfirmware.cat.payload
Verification successful

The blob appears to be a few tables of UTF-16 filename and SHA1/SHA256 checksummed data, encoded in ASN.1 notation. I’ve spent quite a few evenings decompiling the DER file into an ASN file without a whole lot of success (there are 14(!) layers of structures to contend with) and I’ve still not got an ASN file that can correctly parse my DER file for my simple unsigned v1 (ohh yes, v1 = SHA1, v2 = SHA256) test files. There is also a lot of junk data in the blob, and some questionable design choices which mean it’s a huge pain to read. Even if I manage to write the code to read the .cat data blob I’ve then got to populate the data (including the junk data…) so that Windows will accept my file to avoid needing a Microsoft box to generate all firmware images. Also add to the mix that the ASN.1 data is different on different Windows versions (with legacy versions overridden), which explains why you see things like 1.3.6.1.4.1.311.12.2.2 rather than translated titles in the catalog viewer in Windows XP when trying to view .cat files created on Windows 7.

I’ve come to the conclusion that writing a library to reliably read and write all versions of .cat files is probably about 3 months work, and that’s 3 months I simply don’t have. Given there isn’t actually a specification (apart from a super small guide on how to use the MS crypto API) it would also be an uphill battle with every Windows release.

We could of course do something Linux specific that does the same thing, although that obviously would not work on Windows and means we have to ask the vendors to do an extra step in release engineering. Using GPG would be easiest, but a lot of the hardware vendors seem wed to the PKCS certificate mechanism, and I suppose it does mean you can layer certificates for root trust, vendors and contractors. GPG signing the firmware file only doesn’t actually give us a file-list with the digest values of the other metadata in the .cab file.

A naive solution would be to do something like this:

sha25sum firmware.inf firmware.metainfo.xml firmware.bin > firmware.digest
openssl dgst -sha256 -sign cert-private.pem -out firmware.sign firmware.digest
openssl dgst -sha256 -verify cert-pubkey.pem -signature firmware.sign firmware.files

But to actually extract the firmware.digest file we need the private key. We can check prepared data using the public key, but that means shipping firmware.digest and firmware.sign when we only really want one file (.cab files checksum the files internally, so we can be sure against data corruption).

Before I go crazy and invent yet another file format specification does anybody know of a signed digest format with an open specification? Better ideas certainly welcome, thanks.

Richard.

Published by

hughsie

Richard has over 10 years of experience developing open source software. He is the maintainer of GNOME Software, PackageKit, GNOME Packagekit, GNOME Power Manager, GNOME Color Manager, colord, and UPower and also contributes to many other projects and opensource standards. Richard has three main areas of interest on the free desktop, color management, package management, and power management. Richard graduated a few years ago from the University of Surrey with a Masters in Electronics Engineering. He now works for Red Hat in the desktop group, and also manages a company selling open source calibration equipment. Richard's outside interests include taking photos and eating good food.

7 thoughts on “Building a better catalog file”

  1. Hmm.. You haven’t really explained what you want very well…

    I think you are asking for something equivalent to a .cat file (which is a signed list of SHA256 hashes). It isn’t clear why that’s important to you…

    Why not zip the files, then create an openssl cms detached signature file of that? You have an easily extractable file containing everything, with a small signature file sitting next to it.

    At install/run time you’ll need to call CryptVerifyDetachedMessageSignature which IIRC is pretty compatible with openssl cms.

    I don’t think that the .cat file really buys you anything in terms of usability from the Windows GUI (the GUI doesn’t actually verify the file hashes – it just shows what they should be). So even if you managed to produce a makecat equivalent, you’d still be messing did around with CryptCATOpen and friends, rather than CMS.

    1. In a nutshell, I need a way to verify that vendor X has signed all the files in the .cab file, with a file that’s in the .cab file (so you can extract and repackage the files as required). If all the files are signed with a key we trust, we can install the firmware update automatically.

      1. What about Java signed Jar files. Jar files are simple zip files and when they are signed some text files are added to the META-INF directory with the signature of the files inside the Jar file. You can use the text files format inside the cab file.

  2. Hmm… Now you are introducing yet more Windows formats – CAB file now!

    If you have a requirement to use all these Windows formats, you’re in a world of pain on Linux. Even on Windows, extracting CAB files programmatically is a serious PITA.

    If you just want something quick and equivalent, then:
    (1) Copy your files to a directory.
    (2) Add your DER format X509 signing certificate to it.
    (3) Zip everything to myzip.zip
    (4) openssl cms sign -detached … -out myzip.zip.signature

    Ship two files: “myzip.zip” and “myzip.zip.signature”

    Reverse:
    (1) Extract X509 certificate from myzip.zip
    (2) openssl cms verify … myzip.zip

    Sorry – I can’t remember the exact openssl commandline. Also, note that CMS detached signatures can include the signing certificate in them so you don’t actually need it in the zip file. There isn’t much value to what you’re doing if you can’t validate the certificate cryptographically…

  3. I may be missing lots of things, but M$ has some HOWTO about signing and checking a CAB file:
    https://support.microsoft.com/en-us/kb/247257

    Then we have opensrc implementation of CheckTrust tool in Mono:
    https://github.com/mono/mono/blob/master/mcs/tools/security/chktrust.cs#L36
    https://github.com/mono/mono/blob/master/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeDeformatter.cs

    Then Wine project has some code regarding CAT files:
    http://source.winehq.org/git/wine.git/blob/HEAD:/dlls/wintrust/tests/crypt.c
    And re compression/decompression of CAB files too:
    http://source.winehq.org/git/wine.git/tree/HEAD:/dlls/cabinet

    And then just have I found libwdi which claims having reimplemented MakeCat/Inf2Cat, MakeCert, CertMgr, SignTool under LGPL license:
    https://github.com/pbatard/libwdi/wiki/Reuse#WDKs_MakeCatInf2Cat_MakeCert_CertMgr_SignTool_with_source

    Wish you good luck (you seem unstoppable:)

Comments are closed.