This script parses ASN.1 structure of the Windows driver catalog files, and extracts information about what architecture and windows flavor the driver is suitable for. Signed-off-by: Roman Kagan <rkagan@xxxxxxxxxxxxx> --- changes since v2: - add extracting of catalog timestamp and signing time - minor refactoring for better readability changes since v1: - fix pep8 warnings util/parsecat.py | 290 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100644 util/parsecat.py diff --git a/util/parsecat.py b/util/parsecat.py new file mode 100644 index 0000000..bcfaaaf --- /dev/null +++ b/util/parsecat.py @@ -0,0 +1,290 @@ +#!/usr/bin/python +# +# Copyright 2016 Parallels IP Holdings GmbH +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. +""" +Parse relevant items in the ASN.1 structure of a Windows driver catalog file +""" + +import sys +from pprint import pprint +from itertools import chain +from datetime import datetime +from pyasn1_modules import rfc2315 +from pyasn1.type import tag, namedtype, namedval, univ, char, useful +from pyasn1.codec.der.decoder import decode +from pyasn1 import debug + + +# rfc2315 allowed only two certificate types here; later versions of CMS +# (rfc3852) allowed more, and recent catalogs use those. As we aren't doing +# security verifications stub it with ANY type and igore the content. +class CertificateSet(univ.SetOf): + componentType = univ.Any() + + +# redefine rfc2315.SignedData with rfc3852-compatible +class SignedData(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('version', rfc2315.Version()), + namedtype.NamedType('digestAlgorithms', + rfc2315.DigestAlgorithmIdentifiers()), + namedtype.NamedType('contentInfo', rfc2315.ContentInfo()), + namedtype.OptionalNamedType('certificates', CertificateSet().subtype( + implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatConstructed, 0))), + namedtype.OptionalNamedType( + 'crls', rfc2315.CertificateRevocationLists().subtype( + implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatConstructed, 1))), + namedtype.NamedType('signerInfos', rfc2315.SignerInfos()) + ) + + +class CatalogList(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('oid', univ.ObjectIdentifier()) + ) + + +class CatalogListMemberId(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('oid', univ.ObjectIdentifier()), + namedtype.NamedType('null', univ.Null()) + ) + + +class CatalogNameValue(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('name', char.BMPString()), + namedtype.NamedType('someInt', univ.Integer()), + namedtype.NamedType('value', univ.OctetString(encoding='utf-16-le')) + ) + + +class SpcKind(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('oid', univ.ObjectIdentifier()), + namedtype.NamedType('someTh', univ.Any()) + ) + + +class SpcIndirectData(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('spcKind', SpcKind()), + namedtype.NamedType('digest', rfc2315.DigestInfo()) + ) + + +class MemberAttributeContent(univ.SetOf): + componentType = univ.Any() + + +class MemberAttribute(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('oid', univ.ObjectIdentifier()), + namedtype.NamedType('content', MemberAttributeContent()) + ) + + +class MemberAttributes(univ.SetOf): + componentType = MemberAttribute() + + +class CatalogListMember(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('referenceTag', univ.OctetString()), + namedtype.NamedType('attributes', MemberAttributes()) + ) + + +class CatalogMembers(univ.SequenceOf): + componentType = CatalogListMember() + + +class CatalogAttribute(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('oid', univ.ObjectIdentifier()), + namedtype.NamedType('content', univ.OctetString()) + ) + + +class CatalogAttributes(univ.SequenceOf): + componentType = CatalogAttribute() + tagSet = univ.SequenceOf.tagSet.tagExplicitly(tag.Tag(tag.tagClassContext, + tag.tagFormatConstructed, 0)) + + +class TimeChoice(univ.Choice): + componentType = namedtype.NamedTypes( + namedtype.NamedType('utcTime', useful.UTCTime()), + namedtype.NamedType('genTime', useful.GeneralizedTime()) + ) + + +class TSTInfo(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('version', rfc2315.Version()), + namedtype.NamedType('policy', univ.ObjectIdentifier()), + namedtype.NamedType('messageImprint', univ.Any()), + namedtype.NamedType('serialNumber', univ.Integer()), + namedtype.NamedType('genTime', useful.GeneralizedTime()), + namedtype.OptionalNamedType('accuracy', univ.Any()), + namedtype.OptionalNamedType('ordering', univ.Boolean()), + namedtype.OptionalNamedType('nonce', univ.Integer()), + namedtype.OptionalNamedType('tsa', rfc2315.GeneralName().subtype( + explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))), + namedtype.OptionalNamedType('extensions', rfc2315.Extensions().subtype( + implicitTag=tag.Tag(tag.tagClassContext, + tag.tagFormatConstructed, 1))) + ) + + +class CertTrustList(univ.Sequence): + componentType = namedtype.NamedTypes( + namedtype.NamedType('catalogList', CatalogList()), + namedtype.NamedType('someStr0', univ.OctetString()), + namedtype.NamedType('utcTime', useful.UTCTime()), + namedtype.NamedType('catalogListMemberId', CatalogListMemberId()), + namedtype.NamedType('members', CatalogMembers()), + namedtype.OptionalNamedType('attributes', CatalogAttributes()) + ) + + +def parseNameValue(attr): + nv, _ = decode(attr, asn1Spec=CatalogNameValue()) + strtype = type(u'') # python2/3 compat + name, value = str(strtype(nv['name'])), str(strtype(nv['value'])) + assert value[-1] == '\x00' + return name, value[:-1] + + +spcKindMap = { + univ.ObjectIdentifier('1.3.6.1.4.1.311.2.1.15'): 'spcPEImageData', + univ.ObjectIdentifier('1.3.6.1.4.1.311.2.1.25'): 'spcLink', +} + + +digestAlgoMap = { + univ.ObjectIdentifier('1.3.14.3.2.26'): 'sha1', + univ.ObjectIdentifier('2.16.840.1.101.3.4.2.1'): 'sha256', +} + + +def parseSpcIndirectData(attr): + sid, _ = decode(attr, asn1Spec=SpcIndirectData()) + spcKind, digest = sid['spcKind'], sid['digest'] + algo = digestAlgoMap[digest['digestAlgorithm']['algorithm']] + return 'signature', { + 'kind': spcKindMap[spcKind['oid']], + 'digestAlgorithm': algo, + 'digest': digest['digest'].asOctets() + } + + +memberAttrMap = { + univ.ObjectIdentifier('1.3.6.1.4.1.311.12.2.1'): parseNameValue, + univ.ObjectIdentifier('1.3.6.1.4.1.311.12.2.2'): None, + univ.ObjectIdentifier('1.3.6.1.4.1.311.12.2.3'): None, + univ.ObjectIdentifier('1.3.6.1.4.1.311.2.1.4'): parseSpcIndirectData, +} + + +def parseCatMember(member): + for attr in member['attributes']: + meth = memberAttrMap[attr['oid']] + if meth: + yield meth(attr['content'][0]) + + +def parsePKCS7SignedData(data): + container, _ = decode(data, asn1Spec=rfc2315.ContentInfo()) + assert container['contentType'] == rfc2315.signedData + content, _ = decode(container['content'], SignedData()) + return content + + +def parseNameValueObj(nameValue): + assert nameValue['oid'] == univ.ObjectIdentifier('1.3.6.1.4.1.311.12.2.1') + return parseNameValue(nameValue['content']) + + +def parseUTCTime(utcTime): + return datetime.strptime(str(utcTime), '%y%m%d%H%M%SZ') + + +def parseGeneralizedTime(genTime): + return datetime.strptime(str(genTime), '%Y%m%d%H%M%S.%fZ') + + +def parseTimeChoice(timeChoice): + utcTime = timeChoice['utcTime'] + if utcTime: + return parseUTCTime(utcTime) + return parseGeneralizedTime(signingTime['genTime']) + + +def getSigningTimeAuthenticode(data): + signerInfo, _ = decode(data, asn1Spec=rfc2315.SignerInfo()) + attrs = signerInfo['authenticatedAttributes'] + if not attrs: + return + for attr in attrs: + if attr['type'] == univ.ObjectIdentifier('1.2.840.113549.1.9.5'): + signingTime, _ = decode(attr['values'][0], asn1Spec=TimeChoice()) + return parseTimeChoice(signingTime) + + +def getSigningTimeRFC3161(data): + content = parsePKCS7SignedData(data) + contentInfo = content['contentInfo'] + contentType = contentInfo['contentType'] + assert (contentInfo['contentType'] == + univ.ObjectIdentifier('1.2.840.113549.1.9.16.1.4')) + ostr, _ = decode(contentInfo['content'], asn1Spec=univ.OctetString()) + tSTInfo, _ = decode(ostr, asn1Spec=TSTInfo()) + return parseGeneralizedTime(tSTInfo['genTime']) + + +def getSigningTimes(signerInfo): + attrs = signerInfo['unauthenticatedAttributes'] + if not attrs: + return + for attr in attrs: + if attr['type'] == univ.ObjectIdentifier('1.2.840.113549.1.9.6'): + for val in attr['values']: + yield getSigningTimeAuthenticode(val) + + if attr['type'] == univ.ObjectIdentifier('1.3.6.1.4.1.311.3.3.1'): + for val in attr['values']: + yield getSigningTimeRFC3161(val) + + +def parseCat(fname): + cat = open(fname, "rb").read() + content = parsePKCS7SignedData(cat) + contentInfo = content['contentInfo'] + assert (contentInfo['contentType'] == + univ.ObjectIdentifier('1.3.6.1.4.1.311.10.1')) + + ctl, _ = decode(contentInfo['content'], asn1Spec=CertTrustList()) + assert (ctl['catalogList']['oid'] == + univ.ObjectIdentifier('1.3.6.1.4.1.311.12.1.1')) + assert (ctl['catalogListMemberId']['oid'] in ( + univ.ObjectIdentifier('1.3.6.1.4.1.311.12.1.2'), + univ.ObjectIdentifier('1.3.6.1.4.1.311.12.1.3') + )) + + members = [dict(parseCatMember(member)) for member in ctl['members']] + attributes = dict(parseNameValueObj(attr) for attr in ctl['attributes']) + + attributes['timestamp'] = parseUTCTime(ctl['utcTime']) + attributes['signingTimes'] = list(chain.from_iterable( + getSigningTimes(si) for si in content['signerInfos'])) + + return attributes, members + +if __name__ == "__main__": + pprint(parseCat(sys.argv[1])) -- 2.5.0 _______________________________________________ virt-tools-list mailing list virt-tools-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/virt-tools-list