def _parse_attributes(cls, data, required=()): """Given a set of Attributes, parses them and returns them as a dict :param data: The authenticatedAttributes or unauthenticatedAttributes to process :param required: A list of required attributes """ if isinstance(data, rfc2315.Attributes): type_key, value_key = 'type', 'values' elif isinstance( data, (rfc5652.SignedAttributes, rfc5652.UnsignedAttributes)): type_key, value_key = 'attrType', 'attrValues' result = {} for attr in data: typ = asn1.oids.get(attr[type_key]) values = [] for value in attr[value_key]: if not isinstance(typ, tuple): value = guarded_ber_decode(value, asn1_spec=typ()) values.append(value) result[typ] = values if not all((x in result for x in required)): raise SignerInfoParseError( "Not all required attributes found. Required: %s; Found: %s" % ([_print_type(x) for x in required], [_print_type(x) for x in result])) return result
def guarded_ber_decode(data, asn1_spec=None): from pyasn1.codec.ber import decoder as ber_decoder from signify.exceptions import ParseError from signify import _print_type try: result, rest = ber_decoder.decode(data, asn1Spec=asn1_spec) except Exception as e: raise ParseError("Error while parsing %s BER: %s" % (_print_type(asn1_spec), e)) if rest: raise ParseError("Extra information after parsing %s BER" % _print_type(asn1_spec)) return result
def _parse(self): # digestAlgorithms if len(self.data['digestAlgorithms']) != 1: raise ParseError( "SignedData.digestAlgorithms must contain exactly 1 algorithm, not %d" % len(self.data['digestAlgorithms'])) self.digest_algorithm = _get_digest_algorithm( self.data['digestAlgorithms'][0], "SignedData.digestAlgorithm") # contentType if isinstance(self.data, rfc2315.SignedData): self.content_type = asn1.oids.get( self.data['contentInfo']['contentType']) content = self.data['contentInfo']['content'] elif isinstance(self.data, rfc5652.SignedData): self.content_type = asn1.oids.get( self.data['encapContentInfo']['eContentType']) content = self.data['encapContentInfo']['eContent'] else: raise ParseError("Unknown SignedData data type {}".format( _print_type(self.data))) if self.content_type is not self._expected_content_type: raise ParseError("SignedData.contentInfo does not contain %s" % _print_type(self._expected_content_type)) # Content self.content = guarded_ber_decode( content, asn1_spec=self._expected_content_type()) # Certificates self.certificates = CertificateStore([ Certificate(cert) for cert in self.data['certificates'] if Certificate.is_certificate(cert) ]) # SignerInfo if self._signerinfo_class is not None: self.signer_infos = [ self._signerinfo_class(si, parent=self) for si in self.data['signerInfos'] ]
def _get_digest_algorithm(algorithm, location, acceptable=ACCEPTED_DIGEST_ALGORITHMS): result = asn1.oids.get(algorithm['algorithm']) if result not in acceptable: raise ParseError( "%s must be one of %s, not %s" % (location, [x().name for x in acceptable], _print_type(result))) _verify_empty_algorithm_parameters(algorithm, location) return result
def _parse(self): if self.data['version'] != 1: raise SignerInfoParseError("SignerInfo.version must be 1, not %d" % self.data['version']) # We can handle several different rfc types here if isinstance(self.data, rfc2315.SignerInfo): self.issuer = CertificateName( self.data['issuerAndSerialNumber']['issuer'][0]) self.serial_number = self.data['issuerAndSerialNumber'][ 'serialNumber'] self.authenticated_attributes = self._parse_attributes( self.data['authenticatedAttributes'], required=self._required_authenticated_attributes) self._encoded_authenticated_attributes = self._encode_attributes( self.data['authenticatedAttributes']) self.unauthenticated_attributes = self._parse_attributes( self.data['unauthenticatedAttributes']) self.digest_encryption_algorithm = _get_encryption_algorithm( self.data['digestEncryptionAlgorithm'], location="SignerInfo.digestEncryptionAlgorithm") self.encrypted_digest = bytes(self.data['encryptedDigest']) elif isinstance(self.data, rfc5652.SignerInfo): # TODO: handle case where sid contains key identifier self.issuer = CertificateName( self.data['sid']['issuerAndSerialNumber']['issuer'][0]) self.serial_number = self.data['sid']['issuerAndSerialNumber'][ 'serialNumber'] self.authenticated_attributes = self._parse_attributes( self.data['signedAttrs'], required=self._required_authenticated_attributes) self._encoded_authenticated_attributes = self._encode_attributes( self.data['signedAttrs']) self.unauthenticated_attributes = self._parse_attributes( self.data['unsignedAttrs']) self.digest_encryption_algorithm = _get_encryption_algorithm( self.data['signatureAlgorithm'], location="SignerInfo.signatureAlgorithm") self.encrypted_digest = bytes(self.data['signature']) else: raise SignerInfoParseError("Unknown SignerInfo type %s" % type(self.data)) self.digest_algorithm = _get_digest_algorithm( self.data['digestAlgorithm'], location="SignerInfo.digestAlgorithm") # Parse the content of the authenticated attributes # - The messageDigest self.message_digest = None if rfc2315.Digest in self.authenticated_attributes: if len(self.authenticated_attributes[rfc2315.Digest]) != 1: raise SignerInfoParseError( "Only one Digest expected in SignerInfo.authenticatedAttributes" ) self.message_digest = bytes( self.authenticated_attributes[rfc2315.Digest][0]) # - The contentType self.content_type = None if rfc2315.ContentType in self.authenticated_attributes: if len(self.authenticated_attributes[rfc2315.ContentType]) != 1: raise SignerInfoParseError( "Only one ContentType expected in SignerInfo.authenticatedAttributes" ) self.content_type = asn1.oids.get( self.authenticated_attributes[rfc2315.ContentType][0]) if self._expected_content_type is not None and self.content_type is not self._expected_content_type: raise SignerInfoParseError( "Unexpected content type for SignerInfo, expected %s, got %s" % (_print_type(self._expected_content_type), _print_type(self.content_type))) # - The signingTime (used by countersigner) self.signing_time = None if rfc5652.SigningTime in self.authenticated_attributes: if len(self.authenticated_attributes[rfc5652.SigningTime]) != 1: raise SignerInfoParseError( "Only one SigningTime expected in SignerInfo.authenticatedAttributes" ) self.signing_time = time_to_python( self.authenticated_attributes[rfc5652.SigningTime][0]) # - The countersigner self.countersigner = None if pkcs7.Countersignature in self.unauthenticated_attributes: if len(self.unauthenticated_attributes[ pkcs7.Countersignature]) != 1: raise SignerInfoParseError( "Only one CountersignInfo expected in SignerInfo.unauthenticatedAttributes" ) self.countersigner = self._countersigner_class( self.unauthenticated_attributes[pkcs7.Countersignature][0])