class ECCurve(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_STRING("a", ""), ASN1F_STRING("b", ""), ASN1F_optional( ASN1F_BIT_STRING("seed", None)))
def __init__(self, **kargs): seq = [ ASN1F_PACKET("signatureAlgorithm", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_BIT_STRING("subjectPublicKey", None) ] ASN1F_SEQUENCE.__init__(self, *seq, **kargs)
def __init__(self, **kargs): seq = [ ASN1F_PACKET("tbsCertList", X509_TBSCertList(), X509_TBSCertList), ASN1F_PACKET("signatureAlgorithm", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_BIT_STRING("signatureValue", "defaultsignature" * 2) ] ASN1F_SEQUENCE.__init__(self, *seq, **kargs)
def __init__(self, **kargs): seq = [ ASN1F_PACKET("tbsResponseData", OCSP_ResponseData(), OCSP_ResponseData), ASN1F_PACKET("signatureAlgorithm", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_BIT_STRING("signature", "defaultsignature" * 2), ASN1F_optional( ASN1F_SEQUENCE_OF("certs", None, X509_Cert, explicit_tag=0xa0)) ] ASN1F_SEQUENCE.__init__(self, *seq, **kargs)
class SAPPSE_Root_Key(ASN1_Packet): """SAP PSEv2 Root Key definition""" ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_enum_INTEGER("version", 0x0, ["v0"], explicit_tag=0xa0), ASN1F_INTEGER("serial_number", 0), ASN1F_PACKET("public_key", X509_SubjectPublicKeyInfo(), X509_SubjectPublicKeyInfo), ASN1F_PACKET("validity", X509_Validity(), X509_Validity, explicit_tag=0xa1), ASN1F_PACKET("sign_alg_id", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier, explicit_tag=0xa2), ASN1F_BIT_STRING("sign_bit_string", ""), )
class X509_TBSCertificate(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_optional( ASN1F_enum_INTEGER("version", 0x2, ["v1", "v2", "v3"], explicit_tag=0xa0)), ASN1F_INTEGER("serialNumber", 1), ASN1F_PACKET("signature", X509_AlgorithmIdentifier(), X509_AlgorithmIdentifier), ASN1F_SEQUENCE_OF("issuer", _default_issuer, X509_RDN), ASN1F_PACKET("validity", X509_Validity(), X509_Validity), ASN1F_SEQUENCE_OF("subject", _default_subject, X509_RDN), ASN1F_PACKET("subjectPublicKeyInfo", X509_SubjectPublicKeyInfo(), X509_SubjectPublicKeyInfo), ASN1F_optional( ASN1F_BIT_STRING("issuerUniqueID", None, implicit_tag=0x81)), ASN1F_optional( ASN1F_BIT_STRING("subjectUniqueID", None, implicit_tag=0x82)), ASN1F_optional( ASN1F_SEQUENCE_OF("extensions", [X509_Extension()], X509_Extension, explicit_tag=0xa3))) def get_issuer(self): attrs = self.issuer attrsDict = {} for attr in attrs: # we assume there is only one name in each rdn ASN1_SET attrsDict[attr.rdn[0].type.oidname] = plain_str( attr.rdn[0].value.val) # noqa: E501 return attrsDict def get_issuer_str(self): """ Returns a one-line string containing every type/value in a rather specific order. sorted() built-in ensures unicity. """ name_str = "" attrsDict = self.get_issuer() for attrType, attrSymbol in _attrName_mapping: if attrType in attrsDict: name_str += "/" + attrSymbol + "=" name_str += attrsDict[attrType] for attrType in sorted(attrsDict): if attrType not in _attrName_specials: name_str += "/" + attrType + "=" name_str += attrsDict[attrType] return name_str def get_subject(self): attrs = self.subject attrsDict = {} for attr in attrs: # we assume there is only one name in each rdn ASN1_SET attrsDict[attr.rdn[0].type.oidname] = plain_str( attr.rdn[0].value.val) # noqa: E501 return attrsDict def get_subject_str(self): name_str = "" attrsDict = self.get_subject() for attrType, attrSymbol in _attrName_mapping: if attrType in attrsDict: name_str += "/" + attrSymbol + "=" name_str += attrsDict[attrType] for attrType in sorted(attrsDict): if attrType not in _attrName_specials: name_str += "/" + attrType + "=" name_str += attrsDict[attrType] return name_str
class X509_ExtNetscapeCertType(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_BIT_STRING("netscapeCertType", "")
class ECDSAPublicKey(ASN1_Packet): ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_BIT_STRING("ecPoint", "")
class SAPCredv2_Cred_LPS(ASN1_Packet): """SAP Credv2 Credential with LPS definition""" ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_INTEGER("version", 2), ASN1F_SEQUENCE( ASN1F_SET( ASN1F_SEQUENCE(ASN1F_OID("oid", "2.5.4.3"), ASN1F_PRINTABLE_STRING("value", None)))), ASN1F_UTF8_STRING("pse_path", None), ASN1F_BIT_STRING("cipher", None), ) @property def common_name(self): return self.value.val @property def pse_file_path(self): return self.pse_path.val @property def lps_type(self): return ord(self.cipher.val_readable[1]) @property def lps_type_str(self): if self.lps_type in SAP_LPS_Cipher.lps_types: lps = SAP_LPS_Cipher.lps_types[self.lps_type] else: lps = "OFF" return lps @property def cipher_format_version(self): return ord(self.cipher.val_readable[0]) @property def cipher_algorithm(self): if self.version == 2: return CIPHER_ALGORITHM_AES256 else: return CIPHER_ALGORITHM_3DES def decrypt(self, username=None): """Decrypt a credential file using LPS. :param username: Username to use when decrypting. Not used but kept to match signature :type username: string :return: decrypted object :rtype: SAPCredv2_Cred_Plain """ cipher = SAP_LPS_Cipher(self.cipher.val_readable) log_cred.debug( "Obtained LPS cipher object (version={}, lps={})".format( cipher.version, cipher.lps_type)) plain = cipher.decrypt() # Get the pin from the raw data plain_size = ord(plain[0]) pin = plain[plain_size + 1:] # Create a plain credential container plain_cred = SAPCredv2_Cred_Plain() plain_cred.pin = ASN1_IA5_STRING(pin) return plain_cred
class SAPCredv2_Cred(ASN1_Packet): """SAP Credv2 Credential without LPS definition""" ASN1_codec = ASN1_Codecs.BER ASN1_root = ASN1F_SEQUENCE( ASN1F_IA5_STRING("cert_name", None), ASN1F_IA5_STRING("unknown1", None), ASN1F_IA5_STRING("pse_path", None), ASN1F_IA5_STRING("unknown2", None), ASN1F_BIT_STRING("cipher", None), ) @property def common_name(self): return self.cert_name.val @property def pse_file_path(self): return self.pse_path.val @property def lps_type(self): return None @property def lps_type_str(self): return "OFF" @property def cipher_format_version(self): cipher = self.cipher.val_readable if len(cipher) >= 36 and ord(cipher[0]) in [0, 1]: return ord(cipher[0]) return 0 @property def cipher_algorithm(self): if self.cipher_format_version == 1: return ord(self.cipher.val_readable[1]) return 0 def decrypt(self, username): """Decrypt a credential given a particular username. Tries to identify the credential format and choose the decryption method to use. :param username: Username to use when decrypting :type username: string :return: decrypted object :rtype: SAPCredv2_Cred_Plain """ if self.cipher_format_version == 1: return self.decrypt_with_header(username) else: return self.decrypt_simple(username) def decrypt_simple(self, username): """Decrypt a credential using the simple approach. It only handles 3DES. Tries to parse the decrypted object into a plain credential object type. If it fails, probably due to an invalid username use to decrypt it, raises an exception. :param username: Username to use when decrypting :type username: string :return: decrypted object :rtype: SAPCredv2_Cred_Plain """ blob = self.cipher.val_readable # Construct the key using the key format and the username key = (cred_key_fmt % username)[:24] # Set empty IV iv = "\x00" * 8 # Decrypt the cipher text with the derived key and IV decryptor = Cipher(algorithms.TripleDES(key), modes.CBC(iv), backend=default_backend()).decryptor() plain = decryptor.update(blob) + decryptor.finalize() return SAPCredv2_Cred_Plain(plain) def decrypt_with_header(self, username): """Decrypt a credential file using the header. It handles 3DES and AES256 algorithms. Tries to parse the decrypted object into a plain credential object type. If it fails, probably due to an invalid username use to decrypt it, raises an exception. :param username: Username to use when decrypting :type username: string :return: decrypted object :rtype: SAPCredv2_Cred_Plain :raise SAPCredv2_Decryption_Error: if there's an error decrypting the object """ blob = self.cipher.val_readable header = SAPCredv2_Cred_Cipher(blob) # Validate supported version if header.version != 1: raise SAPCredv2_Decryption_Error("Version not supported") # Validate and select proper algorithm if header.algorithm == CIPHER_ALGORITHM_3DES: algorithm = algorithms.TripleDES elif header.algorithm == CIPHER_ALGORITHM_AES256: algorithm = algorithms.AES else: raise SAPCredv2_Decryption_Error("Algorithm not supported") def xor(string, start): """XOR a given string using a fixed key and a starting number.""" key = 0x15a4e35 x = start y = "" for c in string: x *= key x += 1 y += chr(ord(c) ^ (x & 0xff)) return y def derive_key(key, header, salt, username): """Derive a key using SAP's algorithm. The key is derived using SHA256 and xor from an initial key, a header, salt and username. """ digest = Hash(SHA256(), backend=default_backend()) digest.update(key) digest.update(header) digest.update(salt) digest.update(xor(username, ord(salt[0]))) digest.update("" * 0x20) hashed = digest.finalize() derived_key = xor(hashed, ord(salt[1])) return derived_key # Derive the key using SAP's algorithm key = derive_key(cred_key_fmt, blob[0:4], header.salt, username) # Decrypt the cipher text with the derived key and IV decryptor = Cipher(algorithm(key), modes.CBC(header.iv), backend=default_backend()).decryptor() plain = decryptor.update(header.cipher_text) + decryptor.finalize() # Perform a final xor over the decrypted content with a fixed key plain = xor(plain, 0x64FB914E) return SAPCredv2_Cred_Plain(plain)