示例#1
0
 def _ValidateEmptyParams(self, params):
   if params:
     param_value, rest = decoder.decode(params)
     if rest:
       raise Asn1Error('Extra unparsed content.')
     if param_value != univ.Null():
       raise Asn1Error('Hasher has parameters. No idea what to do with them.')
示例#2
0
    def _ParseAuthAttrs(self, auth_attrs, required):
        results = dict.fromkeys(required)
        for attr in auth_attrs:
            if (attr['type'] in oids.OID_TO_CLASS
                    and oids.OID_TO_CLASS.get(attr['type']) in required):
                # There are more than those I require, but I don't know what they are,
                # and what to do with them. The spec does not talk about them.
                # One example:
                # 1.3.6.1.4.1.311.2.1.11 contains as value 1.3.6.1.4.1.311.2.1.21
                # SPC_STATEMENT_TYPE_OBJID    SPC_INDIVIDUAL_SP_KEY_PURPOSE_OBJID
                results[oids.OID_TO_CLASS.get(attr['type'])] = attr['values']

        if None in results.itervalues():
            raise Asn1Error('Missing mandatory field(s) in auth_attrs.')

        # making sure that the auth_attrs were processed in correct order
        # they need to be sorted in ascending order in the SET, when DER encoded
        # This also makes sure that the tag on Attributes is correct.
        a = [der_encoder.encode(i) for i in auth_attrs]
        a.sort()
        attrs_for_hash = pkcs7.Attributes()
        for i in range(len(auth_attrs)):
            d, _ = decoder.decode(a[i], asn1Spec=pkcs7.Attribute())
            attrs_for_hash.setComponentByPosition(i, d)
        encoded_attrs = der_encoder.encode(attrs_for_hash)

        return results, encoded_attrs
示例#3
0
  def _ParseAuthAttrs(self, auth_attrs, required):
    results = dict.fromkeys(required)
    for attr in auth_attrs:
      if (attr['type'] in oids.OID_TO_CLASS and
          oids.OID_TO_CLASS.get(attr['type']) in required):
        # There are more than those I require, but I don't know what they are,
        # and what to do with them. The spec does not talk about them.
        # One example:
        # 1.3.6.1.4.1.311.2.1.11 contains as value 1.3.6.1.4.1.311.2.1.21
        # SPC_STATEMENT_TYPE_OBJID    SPC_INDIVIDUAL_SP_KEY_PURPOSE_OBJID
        results[oids.OID_TO_CLASS.get(attr['type'])] = attr['values']
        
    if None in results.itervalues():
      raise Asn1Error('Missing mandatory field(s) in auth_attrs.')

    # making sure that the auth_attrs were processed in correct order
    # they need to be sorted in ascending order in the SET, when DER encoded
    # This also makes sure that the tag on Attributes is correct.
    a = [der_encoder.encode(i) for i in auth_attrs]
    a.sort()
    attrs_for_hash = pkcs7.Attributes()
    for i in range(len(auth_attrs)):
      d, _ = decoder.decode(a[i], asn1Spec=pkcs7.Attribute())
      attrs_for_hash.setComponentByPosition(i, d)
    encoded_attrs = der_encoder.encode(attrs_for_hash)

    return results, encoded_attrs
示例#4
0
  def TraverseRdn(rdn):
    """Traverses RDN structure and returns string encoding of the DN.

    Args:
      rdn: ASN.1 SET (or SEQUENCE) containing RDNs (relative distinguished
           names), as identified by type / value pairs. A typical input would
          be of type X.509 RelativeDistinguishedName.

    Returns:
      A dict representing the Distinguished Name.
    """
    val = dict()
    for n in rdn:
      # Note that this does not work for e.g. DC which is present
      # multiple times.
      # For a real DN parser, make sure to follow the spec in regards
      # to multiple occurence of a field in subsequent RDNs, maintaining
      # original ordering etc.
      # TODO(user): What about elements other than [0]??
      name = DistinguishedName.OidToName(n[0]['type'])
      value = decoder.decode(n[0]['value'])
      if name in val:
        val[name] = str(value[0]) + ', ' + val.get(name, '')
      else:
        val[name] = str(value[0])
    return val
示例#5
0
 def _ValidateEmptyParams(self, params):
     if params:
         param_value, rest = decoder.decode(params)
         if rest:
             raise Asn1Error('Extra unparsed content.')
         if param_value != univ.Null():
             raise Asn1Error(
                 'Hasher has parameters. No idea what to do with them.')
示例#6
0
 def _ParseCountersig(self, unauth_attrs):
   attr = unauth_attrs[0]
   if oids.OID_TO_CLASS.get(attr['type']) is not pkcs7.CountersignInfo:
     raise Asn1Error('Unexpected countersign OID.')
   values = attr['values']
   if len(values) != 1:
     raise Asn1Error('Expected one CS value, got %d.' % len(values))
   counter_sig_info, rest = decoder.decode(values[0],
                                           asn1Spec=pkcs7.CountersignInfo())
   if rest: raise Asn1Error('Extra unparsed content.')
   return counter_sig_info
示例#7
0
 def _ParseCountersig(self, unauth_attrs):
     attr = unauth_attrs[0]
     if oids.OID_TO_CLASS.get(attr['type']) is not pkcs7.CountersignInfo:
         raise Asn1Error('Unexpected countersign OID.')
     values = attr['values']
     if len(values) != 1:
         raise Asn1Error('Expected one CS value, got %d.' % len(values))
     counter_sig_info, rest = decoder.decode(
         values[0], asn1Spec=pkcs7.CountersignInfo())
     if rest: raise Asn1Error('Extra unparsed content.')
     return counter_sig_info
示例#8
0
    def ValidateHashes(self, computed_content_hash):
        """Compares computed against expected hashes.

    This method makes sure the chain of hashes is correct. The chain
    consists of Authenticode hash of the actual binary payload, as checked
    against the hash in SpcInfo to the hash of SpcInfo as stored in the
    AuthAttrs, and the hash of EncryptedDigest as stored in the counter-
    signature AuthAttrs, if present.

    Args:
      computed_content_hash: Authenticode hash of binary, as provided by
                             fingerprinter.
    Raises:
      Asn1Error: if hash validation fails.
    """

        if computed_content_hash != self.spc_info['messageDigest']['digest']:
            raise Asn1Error('1: Validation of content hash failed.')

        spc_blob = self.signed_data['contentInfo']['content']
        # According to RFC2315, 9.3, identifier (tag) and length need to be
        # stripped for hashing. We do this by having the parser just strip
        # out the SEQUENCE part of the spcIndirectData.
        # Alternatively this could be done by re-encoding and concatenating
        # the individual elements in spc_value, I _think_.
        _, hashable_spc_blob = decoder.decode(spc_blob, recursiveFlag=0)
        spc_blob_hash = self.digest_algorithm(str(hashable_spc_blob)).digest()
        if spc_blob_hash != self.expected_spc_info_hash:
            raise Asn1Error('2: Validation of SpcInfo hash failed.')
        # Can't check authAttr hash against encrypted hash, done implicitly in
        # M2's pubkey.verify. This can be added by explicit decryption of
        # encryptedDigest, if really needed. (See sample code for RSA in
        # 'verbose_authenticode_sig.py')

        if self.has_countersignature:
            # Validates the hash value found in the authenticated attributes of the
            # counter signature against the hash of the outer signature.
            auth_attr_hash = self.digest_algorithm(
                self.encrypted_digest).digest()
            if auth_attr_hash != self.expected_auth_attrs_hash:
                raise Asn1Error(
                    '3: Validation of countersignature hash failed.')
示例#9
0
  def ValidateHashes(self, computed_content_hash):
    """Compares computed against expected hashes.

    This method makes sure the chain of hashes is correct. The chain
    consists of Authenticode hash of the actual binary payload, as checked
    against the hash in SpcInfo to the hash of SpcInfo as stored in the
    AuthAttrs, and the hash of EncryptedDigest as stored in the counter-
    signature AuthAttrs, if present.

    Args:
      computed_content_hash: Authenticode hash of binary, as provided by
                             fingerprinter.
    Raises:
      Asn1Error: if hash validation fails.
    """

    if computed_content_hash != self.spc_info['messageDigest']['digest']:
      raise Asn1Error('1: Validation of content hash failed.')

    spc_blob = self.signed_data['contentInfo']['content']
    # According to RFC2315, 9.3, identifier (tag) and length need to be
    # stripped for hashing. We do this by having the parser just strip
    # out the SEQUENCE part of the spcIndirectData.
    # Alternatively this could be done by re-encoding and concatenating
    # the individual elements in spc_value, I _think_.
    _, hashable_spc_blob = decoder.decode(spc_blob, recursiveFlag=0)
    spc_blob_hash = self.digest_algorithm(str(hashable_spc_blob)).digest()
    if spc_blob_hash != self.expected_spc_info_hash:
      raise Asn1Error('2: Validation of SpcInfo hash failed.')
    # Can't check authAttr hash against encrypted hash, done implicitly in
    # M2's pubkey.verify. This can be added by explicit decryption of
    # encryptedDigest, if really needed. (See sample code for RSA in
    # 'verbose_authenticode_sig.py')

    if self.has_countersignature:
      # Validates the hash value found in the authenticated attributes of the
      # counter signature against the hash of the outer signature.
      auth_attr_hash = self.digest_algorithm(self.encrypted_digest).digest()
      if auth_attr_hash != self.expected_auth_attrs_hash:
        raise Asn1Error('3: Validation of countersignature hash failed.')
示例#10
0
  def _ParseOpusInfo(self, opus_info_asn1):
    spc_opus_info, rest = decoder.decode(opus_info_asn1,
                                         asn1Spec=spc.SpcSpOpusInfo())
    if rest: raise Asn1Error('Extra unparsed content.')

    if spc_opus_info['programName']:
      # According to spec, this should always be a Unicode string. However,
      # the ASN.1 syntax allows both ASCII and Unicode. So, let's be careful.
      opus_prog_name = spc_opus_info['programName']
      uni_name = opus_prog_name['unicode']
      ascii_name = opus_prog_name['ascii']
      if ascii_name and uni_name:
        # WTF? This is supposed to be a CHOICE
        raise Asn1Error('Both elements of a choice are present.')
      elif uni_name:
        program_name = str(uni_name).decode('utf-16-be')
      elif ascii_name:
        program_name = str(ascii_name)
      else:
        raise Asn1Error('No element of opusInfo choice is present.')
    else:
      # According to spec, there should always be a program name,
      # and be it zero-length. But let's be gentle, since ASN.1 marks
      # this field als optional.
      program_name = None

    # Again, according to Authenticode spec, the moreInfo field should always
    # be there and point to an ASCII string with a URL.
    if spc_opus_info['moreInfo']:
      more_info = spc_opus_info['moreInfo']
      if more_info['url']:
        more_info_link = str(more_info['url'])
      else:
        raise Asn1Error('Expected a URL in moreInfo.')
    else:
      more_info_link = None

    return program_name, more_info_link
示例#11
0
    def _ParseOpusInfo(self, opus_info_asn1):
        spc_opus_info, rest = decoder.decode(opus_info_asn1,
                                             asn1Spec=spc.SpcSpOpusInfo())
        if rest: raise Asn1Error('Extra unparsed content.')

        if spc_opus_info['programName']:
            # According to spec, this should always be a Unicode string. However,
            # the ASN.1 syntax allows both ASCII and Unicode. So, let's be careful.
            opus_prog_name = spc_opus_info['programName']
            uni_name = opus_prog_name['unicode']
            ascii_name = opus_prog_name['ascii']
            if ascii_name and uni_name:
                # WTF? This is supposed to be a CHOICE
                raise Asn1Error('Both elements of a choice are present.')
            elif uni_name:
                program_name = str(uni_name).decode('utf-16-be')
            elif ascii_name:
                program_name = str(ascii_name)
            else:
                raise Asn1Error('No element of opusInfo choice is present.')
        else:
            # According to spec, there should always be a program name,
            # and be it zero-length. But let's be gentle, since ASN.1 marks
            # this field als optional.
            program_name = None

        # Again, according to Authenticode spec, the moreInfo field should always
        # be there and point to an ASCII string with a URL.
        if spc_opus_info['moreInfo']:
            more_info = spc_opus_info['moreInfo']
            if more_info['url']:
                more_info_link = str(more_info['url'])
            else:
                raise Asn1Error('Expected a URL in moreInfo.')
        else:
            more_info_link = None

        return program_name, more_info_link
示例#12
0
  def ValidateAsn1(self):
    """Validate overall information / consistency.

    Can be invoked to check through most of the assumptions on
    ASN.1 integrity, and constraints placed on PKCS#7 / X.509 by
    Authenticode.

    Returns:
      Nothing.

    Raises:
      Asn1Error: with a descriptive string, if anything is amiss.
    """

    # Validate overall information
    if (oids.OID_TO_CLASS.get(self.container['contentType']) is not
        pkcs7.SignedData):
      raise Asn1Error('Unexpected OID: %s' %
                      self.container['contentType'].prettyPrint())
    if self.signed_data['version'] != 1:
      raise Asn1Error('SignedData wrong version: %s' %
                      self.signed_data['version'].prettyPrint())

    # Validate content digest specs.
    if len(self.signed_data['digestAlgorithms']) != 1:
      raise Asn1Error('Expected exactly one digestAlgorithm, got %d.' %
                      len(self.signed_data['digestAlgorithms']))
    spec = self.signed_data['digestAlgorithms'][0]
    if (self.digest_algorithm is not hashlib.md5 and
        self.digest_algorithm is not hashlib.sha1):
      raise Asn1Error('digestAlgorithm must be md5 or sha1, was %s.' %
                      spec['algorithm'].prettyPrint())
    self._ValidateEmptyParams(spec['parameters'])

    # Validate SpcIndirectDataContent structure
    oid = self.signed_data['contentInfo']['contentType']
    if oids.OID_TO_CLASS.get(oid) is not spc.SpcIndirectDataContent:
      raise Asn1Error('Unexpected contentInfo OID: %s' % oid.prettyPrint())

    # Validate content hash meta data in spcIndirectDataContent
    oid = self.spc_info['messageDigest']['digestAlgorithm']['algorithm']
    if oids.OID_TO_CLASS.get(oid) is not self.digest_algorithm:
      raise Asn1Error('Outer and SPC message_digest algorithms don\'t match.')
    params = self.spc_info['messageDigest']['digestAlgorithm']['parameters']
    self._ValidateEmptyParams(params)

    if self.signed_data['crls']:
      raise Asn1Error('Don\'t know what to do with CRL information.')

    # Work through signer_info pieces that are easily validated
    if len(self.signed_data['signerInfos']) != 1:
      raise Asn1Error('Expected one signer_info, got %d.' %
                      len(self.signed_data['signerInfos']))
    if self.signer_info['version'] != 1:
      raise Asn1Error('SignerInfo wrong version: %s' %
                      self.signer_info['version'].prettyPrint())

    # Make sure signer_info hash algorithm is consistent
    oid = self.signer_info['digestAlgorithm']['algorithm']
    if oids.OID_TO_CLASS.get(oid) is not self.digest_algorithm:
      raise Asn1Error('Outer and signer_info digest algorithms don\'t match.')
    params = self.signer_info['digestAlgorithm']['parameters']
    self._ValidateEmptyParams(params)

    # Make sure the signing cert is actually in the list of certs
    if self.signing_cert_id not in self.certificates:
      raise Asn1Error('Signing cert not in list of known certificates.')

    # auth_attrs has three fields, where we do some integrity / sanity checks
    # content_type
    content_type_set = self.auth_attrs[pkcs7.ContentType]
    if len(content_type_set) != 1:
      raise Asn1Error('authAttr.content_type expected to hold one value.')
    content_type, rest = decoder.decode(content_type_set[0])
    if rest:
      raise Asn1Error('Extra unparsed content.')
    # Spec claims this should be messageDigestOID, but that's not true.
    if oids.OID_TO_CLASS.get(content_type) is not spc.SpcIndirectDataContent:
      raise Asn1Error('Unexpected authAttr.content_type OID: %s' %
                      content_type.prettyPrint())
    # Message_digest -- 'just' an octet string
    message_digest_set = self.auth_attrs[pkcs7.DigestInfo]
    if len(message_digest_set) != 1:
      raise Asn1Error('authAttr.messageDigest expected to hold one value.')
    _, rest = decoder.decode(message_digest_set[0])
    if rest:
      raise Asn1Error('Extra unparsed content.')
    # opusInfo -- has it's own section

    enc_alg = self.signer_info['digestEncryptionAlgorithm']['algorithm']
    if enc_alg not in oids.OID_TO_PUBKEY:
      raise Asn1Error('Could not parse digestEncryptionAlgorithm.')
    params = self.signer_info['digestEncryptionAlgorithm']['parameters']
    self._ValidateEmptyParams(params)

    if not self.has_countersignature: return

    unauth_attrs = self.signer_info['unauthenticatedAttributes']
    if len(unauth_attrs) != 1:
      raise Asn1Error('Expected one attribute, got %d.' % len(unauth_attrs))
    # Extra structure parsed in _ParseCountersig

    # signer_info of the counter signature
    if self.counter_sig_info['version'] != 1:
      raise Asn1Error('Countersignature wrong version: %s' %
                      self.counter_sig_info['version'].prettyPrint())

    # Make sure counter_sig_info hash algorithm is consistent
    oid = self.counter_sig_info['digestAlgorithm']['algorithm']
    if oids.OID_TO_CLASS.get(oid) is not self.digest_algorithm:
      raise Asn1Error('Outer and countersign digest algorithms don\'t match.')
    params = self.counter_sig_info['digestAlgorithm']['parameters']
    self._ValidateEmptyParams(params)

    # Make sure the counter-signing cert is actually in the list of certs
    if self.counter_sig_cert_id not in self.certificates:
      raise Asn1Error('Countersigning cert not in list of known certificates.')

    # counterSig auth_attrs also has three fields, where we do some
    # integrity / sanity checks
    # content_type
    content_type_set = self.counter_attrs[pkcs7.ContentType]
    if len(content_type_set) != 1:
      raise Asn1Error('counterAttr.content_type expected to hold one value.')
    content_type, rest = decoder.decode(content_type_set[0])
    if rest:
      raise Asn1Error('Extra unparsed content.')
    if oids.OID_TO_CLASS.get(content_type) != 'PKCS#7 Data':
      raise Asn1Error('Unexpected counterAttr.content_type OID: %s' %
                      content_type.prettyPrint())
    # message_digest -- 'just' an octet string
    message_digest_set = self.counter_attrs[pkcs7.DigestInfo]
    if len(message_digest_set) != 1:
      raise Asn1Error('counterAttr.message_digest expected to hold one value.')
    _, rest = decoder.decode(message_digest_set[0])
    if rest:
      raise Asn1Error('Extra unparsed content.')
    # TODO(user): Check SigningTime integrity
    # e.g. only one value in the set

    enc_alg = self.counter_sig_info['digestEncryptionAlgorithm']['algorithm']
    if enc_alg not in oids.OID_TO_PUBKEY:
      raise Asn1Error('Could not parse CS digestEncryptionAlgorithm.')
    params = self.counter_sig_info['digestEncryptionAlgorithm']['parameters']
    self._ValidateEmptyParams(params)
示例#13
0
 def _ParseTimestamp(self, time_asn1):
   # Parses countersignature timestamp according to RFC3280, section 4.1.2.5+
   timestamp_choice, rest = decoder.decode(time_asn1,
                                           asn1Spec=pkcs7.SigningTime())
   if rest: raise Asn1Error('Extra unparsed content.')
   return timestamp_choice.ToPythonEpochTime()
示例#14
0
 def _ParseTimestamp(self, time_asn1):
     # Parses countersignature timestamp according to RFC3280, section 4.1.2.5+
     timestamp_choice, rest = decoder.decode(time_asn1,
                                             asn1Spec=pkcs7.SigningTime())
     if rest: raise Asn1Error('Extra unparsed content.')
     return timestamp_choice.ToPythonEpochTime()
示例#15
0
    def ValidateAsn1(self):
        """Validate overall information / consistency.

    Can be invoked to check through most of the assumptions on
    ASN.1 integrity, and constraints placed on PKCS#7 / X.509 by
    Authenticode.

    Returns:
      Nothing.

    Raises:
      Asn1Error: with a descriptive string, if anything is amiss.
    """

        # Validate overall information
        if (oids.OID_TO_CLASS.get(self.container['contentType'])
                is not pkcs7.SignedData):
            raise Asn1Error('Unexpected OID: %s' %
                            self.container['contentType'].prettyPrint())
        if self.signed_data['version'] != 1:
            raise Asn1Error('SignedData wrong version: %s' %
                            self.signed_data['version'].prettyPrint())

        # Validate content digest specs.
        if len(self.signed_data['digestAlgorithms']) != 1:
            raise Asn1Error('Expected exactly one digestAlgorithm, got %d.' %
                            len(self.signed_data['digestAlgorithms']))
        spec = self.signed_data['digestAlgorithms'][0]
        if (self.digest_algorithm is not hashlib.md5
                and self.digest_algorithm is not hashlib.sha1):
            raise Asn1Error('digestAlgorithm must be md5 or sha1, was %s.' %
                            spec['algorithm'].prettyPrint())
        self._ValidateEmptyParams(spec['parameters'])

        # Validate SpcIndirectDataContent structure
        oid = self.signed_data['contentInfo']['contentType']
        if oids.OID_TO_CLASS.get(oid) is not spc.SpcIndirectDataContent:
            raise Asn1Error('Unexpected contentInfo OID: %s' %
                            oid.prettyPrint())

        # Validate content hash meta data in spcIndirectDataContent
        oid = self.spc_info['messageDigest']['digestAlgorithm']['algorithm']
        if oids.OID_TO_CLASS.get(oid) is not self.digest_algorithm:
            raise Asn1Error(
                'Outer and SPC message_digest algorithms don\'t match.')
        params = self.spc_info['messageDigest']['digestAlgorithm'][
            'parameters']
        self._ValidateEmptyParams(params)

        if self.signed_data['crls']:
            raise Asn1Error('Don\'t know what to do with CRL information.')

        # Work through signer_info pieces that are easily validated
        if len(self.signed_data['signerInfos']) != 1:
            raise Asn1Error('Expected one signer_info, got %d.' %
                            len(self.signed_data['signerInfos']))
        if self.signer_info['version'] != 1:
            raise Asn1Error('SignerInfo wrong version: %s' %
                            self.signer_info['version'].prettyPrint())

        # Make sure signer_info hash algorithm is consistent
        oid = self.signer_info['digestAlgorithm']['algorithm']
        if oids.OID_TO_CLASS.get(oid) is not self.digest_algorithm:
            raise Asn1Error(
                'Outer and signer_info digest algorithms don\'t match.')
        params = self.signer_info['digestAlgorithm']['parameters']
        self._ValidateEmptyParams(params)

        # Make sure the signing cert is actually in the list of certs
        if self.signing_cert_id not in self.certificates:
            raise Asn1Error('Signing cert not in list of known certificates.')

        # auth_attrs has three fields, where we do some integrity / sanity checks
        # content_type
        content_type_set = self.auth_attrs[pkcs7.ContentType]
        if len(content_type_set) != 1:
            raise Asn1Error(
                'authAttr.content_type expected to hold one value.')
        content_type, rest = decoder.decode(content_type_set[0])
        if rest:
            raise Asn1Error('Extra unparsed content.')
        # Spec claims this should be messageDigestOID, but that's not true.
        if oids.OID_TO_CLASS.get(
                content_type) is not spc.SpcIndirectDataContent:
            raise Asn1Error('Unexpected authAttr.content_type OID: %s' %
                            content_type.prettyPrint())
        # Message_digest -- 'just' an octet string
        message_digest_set = self.auth_attrs[pkcs7.DigestInfo]
        if len(message_digest_set) != 1:
            raise Asn1Error(
                'authAttr.messageDigest expected to hold one value.')
        _, rest = decoder.decode(message_digest_set[0])
        if rest:
            raise Asn1Error('Extra unparsed content.')
        # opusInfo -- has it's own section

        enc_alg = self.signer_info['digestEncryptionAlgorithm']['algorithm']
        if enc_alg not in oids.OID_TO_PUBKEY:
            raise Asn1Error('Could not parse digestEncryptionAlgorithm.')
        params = self.signer_info['digestEncryptionAlgorithm']['parameters']
        self._ValidateEmptyParams(params)

        if not self.has_countersignature: return

        unauth_attrs = self.signer_info['unauthenticatedAttributes']
        if len(unauth_attrs) != 1:
            raise Asn1Error('Expected one attribute, got %d.' %
                            len(unauth_attrs))
        # Extra structure parsed in _ParseCountersig

        # signer_info of the counter signature
        if self.counter_sig_info['version'] != 1:
            raise Asn1Error('Countersignature wrong version: %s' %
                            self.counter_sig_info['version'].prettyPrint())

        # Make sure counter_sig_info hash algorithm is consistent
        oid = self.counter_sig_info['digestAlgorithm']['algorithm']
        if oids.OID_TO_CLASS.get(oid) is not self.digest_algorithm:
            raise Asn1Error(
                'Outer and countersign digest algorithms don\'t match.')
        params = self.counter_sig_info['digestAlgorithm']['parameters']
        self._ValidateEmptyParams(params)

        # Make sure the counter-signing cert is actually in the list of certs
        if self.counter_sig_cert_id not in self.certificates:
            raise Asn1Error(
                'Countersigning cert not in list of known certificates.')

        # counterSig auth_attrs also has three fields, where we do some
        # integrity / sanity checks
        # content_type
        content_type_set = self.counter_attrs[pkcs7.ContentType]
        if len(content_type_set) != 1:
            raise Asn1Error(
                'counterAttr.content_type expected to hold one value.')
        content_type, rest = decoder.decode(content_type_set[0])
        if rest:
            raise Asn1Error('Extra unparsed content.')
        if oids.OID_TO_CLASS.get(content_type) != 'PKCS#7 Data':
            raise Asn1Error('Unexpected counterAttr.content_type OID: %s' %
                            content_type.prettyPrint())
        # message_digest -- 'just' an octet string
        message_digest_set = self.counter_attrs[pkcs7.DigestInfo]
        if len(message_digest_set) != 1:
            raise Asn1Error(
                'counterAttr.message_digest expected to hold one value.')
        _, rest = decoder.decode(message_digest_set[0])
        if rest:
            raise Asn1Error('Extra unparsed content.')
        # TODO(user): Check SigningTime integrity
        # e.g. only one value in the set

        enc_alg = self.counter_sig_info['digestEncryptionAlgorithm'][
            'algorithm']
        if enc_alg not in oids.OID_TO_PUBKEY:
            raise Asn1Error('Could not parse CS digestEncryptionAlgorithm.')
        params = self.counter_sig_info['digestEncryptionAlgorithm'][
            'parameters']
        self._ValidateEmptyParams(params)
示例#16
0
  def __init__(self, content):
    self.container, rest = decoder.decode(content,
                                          asn1Spec=pkcs7.ContentInfo())
    if rest:
      self.trailing_data = rest

    self.signed_data, rest = decoder.decode(self.container['content'],
                                            asn1Spec=pkcs7.SignedData())
    if rest: raise Asn1Error('Extra unparsed content.')

    digest_algorithm_oid = self.signed_data['digestAlgorithms'][0]['algorithm']
    self.digest_algorithm = oids.OID_TO_CLASS.get(digest_algorithm_oid)

    spc_blob = self.signed_data['contentInfo']['content']
    self.spc_info, rest = decoder.decode(spc_blob,
                                         asn1Spec=spc.SpcIndirectDataContent())
    if rest: raise Asn1Error('Extra unparsed content.')
    # Currently not parsing the SpcIndirectDataContent 'data' field.
    # It used to contain information about the software publisher, but now
    # is set to default content, or under Vista+, may hold page hashes.

    self.certificates = self._ParseCerts(self.signed_data['certificates'])

    self.signer_info = self.signed_data['signerInfos'][0]

    self.signing_cert_id = self._ParseIssuerInfo(
        self.signer_info['issuerAndSerialNumber'])

    # Parse out mandatory fields in authenticated attributes.
    self.auth_attrs, self.computed_auth_attrs_for_hash = (
        self._ParseAuthAttrs(self.signer_info['authenticatedAttributes'],
                             required=[pkcs7.ContentType,
                                       pkcs7.DigestInfo,
                                       spc.SpcSpOpusInfo]))
    hashval, rest = decoder.decode(self.auth_attrs[pkcs7.DigestInfo][0])
    if rest: raise Asn1Error('Extra unparsed content.')
    if hashval.__class__ is not univ.OctetString:
      raise Asn1Error('Hash value expected to be OctetString.')
    self.expected_spc_info_hash = str(hashval)

    opus_info_asn1 = self.auth_attrs[spc.SpcSpOpusInfo][0]
    self.program_name, self.program_url = self._ParseOpusInfo(opus_info_asn1)

    self.encrypted_digest = str(self.signer_info['encryptedDigest'])

    unauth_attrs = self.signer_info['unauthenticatedAttributes']
    if unauth_attrs is None:
      self.has_countersignature = False
      return

    self.has_countersignature = True
    self.counter_sig_info = self._ParseCountersig(unauth_attrs)
    self.counter_sig_cert_id = self._ParseIssuerInfo(
        self.counter_sig_info['issuerAndSerialNumber'])

    # Parse out mandatory fields in countersig authenticated attributes.
    self.counter_attrs, self.computed_counter_attrs_for_hash = (
        self._ParseAuthAttrs(self.counter_sig_info['authenticatedAttributes'],
                             required=[pkcs7.ContentType,
                                       pkcs7.SigningTime,
                                       pkcs7.DigestInfo]))

    hashval, rest = decoder.decode(self.counter_attrs[pkcs7.DigestInfo][0])
    if rest: raise Asn1Error('Extra unparsed content.')
    if hashval.__class__ is not univ.OctetString:
      raise Asn1Error('Hash value expected to be OctetString.')
    self.expected_auth_attrs_hash = str(hashval)

    self.counter_timestamp = self._ParseTimestamp(
        self.counter_attrs[pkcs7.SigningTime][0])

    self.encrypted_counter_digest = str(
        self.counter_sig_info['encryptedDigest'])
示例#17
0
    def __init__(self, content):
        self.container, rest = decoder.decode(content,
                                              asn1Spec=pkcs7.ContentInfo())
        if rest:
            self.trailing_data = rest

        self.signed_data, rest = decoder.decode(self.container['content'],
                                                asn1Spec=pkcs7.SignedData())
        if rest: raise Asn1Error('Extra unparsed content.')

        digest_algorithm_oid = self.signed_data['digestAlgorithms'][0][
            'algorithm']
        self.digest_algorithm = oids.OID_TO_CLASS.get(digest_algorithm_oid)

        spc_blob = self.signed_data['contentInfo']['content']
        self.spc_info, rest = decoder.decode(
            spc_blob, asn1Spec=spc.SpcIndirectDataContent())
        if rest: raise Asn1Error('Extra unparsed content.')
        # Currently not parsing the SpcIndirectDataContent 'data' field.
        # It used to contain information about the software publisher, but now
        # is set to default content, or under Vista+, may hold page hashes.

        self.certificates = self._ParseCerts(self.signed_data['certificates'])

        self.signer_info = self.signed_data['signerInfos'][0]

        self.signing_cert_id = self._ParseIssuerInfo(
            self.signer_info['issuerAndSerialNumber'])

        # Parse out mandatory fields in authenticated attributes.
        self.auth_attrs, self.computed_auth_attrs_for_hash = (
            self._ParseAuthAttrs(self.signer_info['authenticatedAttributes'],
                                 required=[
                                     pkcs7.ContentType, pkcs7.DigestInfo,
                                     spc.SpcSpOpusInfo
                                 ]))
        hashval, rest = decoder.decode(self.auth_attrs[pkcs7.DigestInfo][0])
        if rest: raise Asn1Error('Extra unparsed content.')
        if hashval.__class__ is not univ.OctetString:
            raise Asn1Error('Hash value expected to be OctetString.')
        self.expected_spc_info_hash = str(hashval)

        opus_info_asn1 = self.auth_attrs[spc.SpcSpOpusInfo][0]
        self.program_name, self.program_url = self._ParseOpusInfo(
            opus_info_asn1)

        self.encrypted_digest = str(self.signer_info['encryptedDigest'])

        unauth_attrs = self.signer_info['unauthenticatedAttributes']
        if unauth_attrs is None:
            self.has_countersignature = False
            return

        self.has_countersignature = True
        self.counter_sig_info = self._ParseCountersig(unauth_attrs)
        self.counter_sig_cert_id = self._ParseIssuerInfo(
            self.counter_sig_info['issuerAndSerialNumber'])

        # Parse out mandatory fields in countersig authenticated attributes.
        self.counter_attrs, self.computed_counter_attrs_for_hash = (
            self._ParseAuthAttrs(
                self.counter_sig_info['authenticatedAttributes'],
                required=[
                    pkcs7.ContentType, pkcs7.SigningTime, pkcs7.DigestInfo
                ]))

        hashval, rest = decoder.decode(self.counter_attrs[pkcs7.DigestInfo][0])
        if rest: raise Asn1Error('Extra unparsed content.')
        if hashval.__class__ is not univ.OctetString:
            raise Asn1Error('Hash value expected to be OctetString.')
        self.expected_auth_attrs_hash = str(hashval)

        self.counter_timestamp = self._ParseTimestamp(
            self.counter_attrs[pkcs7.SigningTime][0])

        self.encrypted_counter_digest = str(
            self.counter_sig_info['encryptedDigest'])