class ActiveAuthentication(Logger): """ This class implement the Active Authentication protocol. The main method is I{executeAA} that return True is the verification is ok or False. """ def __init__(self, iso7816, openssl=None): """ @param iso7816: a valid iso7816 object @type iso7816: doc9303 """ Logger.__init__(self, "AA") self._iso7816 = iso7816 if not openssl: self._openssl = OpenSSL() else: self._openssl = openssl self.RND_IFD = None self.F = None self.T = None self.decryptedSignature = None self.D = None self.D_ = None self.M1 = None self.M_ = None self._dg15 = None def executeAA(self, dg15): """ Perform the Active Authentication protocol. Work only with RSA, modulus length of 1024 and with SHA1. @param dg15: A initialized dataGroup15 object @type dg15: dataGroup15 @return: True if the authentication succeed, else False. @rtype: Boolean @raise ActiveAuthenticationException: If the Active Authentication is not supported (The DG15 is not found or the hash algo is not supported). @raise ActiveAuthenticationException: If the parameter is not set or invalid. @raise ActiveAuthenticationException: If OpenSSL is not installed. @raise ActiveAuthenticationException: If the public key cannot be recovered from the DG15. @raise ActiveAuthenticationException: If the DG15 is invalid and the signature cannot be verified. """ self._dg15 = dg15 self.RND_IFD = self._genRandom(8) self.signature = self._getSignature(self.RND_IFD) self.F = self._decryptSignature(dg15.body, self.signature) (hash, hashSize, offset) = self._getHashAlgo(self.F) self.D = self._extractDigest(self.F, hashSize, offset) self.M1 = self._extractM1(self.F, hashSize, offset) self.M_ = self.M1 + self.RND_IFD self.log("Concatenate M1 with known M2") self.log("\tM*: " + binToHexRep(self.M_)) self.D_ = self._hash(hash, self.M_) self.log("Compare D and D*") self.log("\t" + str(self.D == self.D_)) return self.D == self.D_ def _genRandom(self, size): rnd_ifd = os.urandom(size) self.log("Generate an 8 byte random") self.log("\tRND.IFD: " + binToHexRep(rnd_ifd)) return rnd_ifd def _getSignature(self, rnd_ifd): return self._iso7816.internalAuthentication(rnd_ifd) def getPubKey(self, dg15): """ Retrieve the public key in PEM format from the dataGroup15 @return: A PEM reprensation of the public key @rtype: A string @raise ActiveAuthenticationException: I{The parameter type is not valid, must be a dataGroup15 object}: The parameter dg15 is not set or invalid. @raise ActiveAuthenticationException: I{The public key could not be recovered from the DG15}: Is open SSL installed? """ if type(dg15) != type(datagroup.DataGroup15(None)): raise ActiveAuthenticationException( "The parameter type is not valid, must be a dataGroup15 object" ) return self._openssl.retrieveRsaPubKey(dg15.body) def _decryptSignature(self, pubK, signature): data = self._openssl.retrieveSignedData(pubK, signature) self.log("Decrypt the signature with the public key") self.log("\tF: " + binToHexRep(data)) return data def _hash(self, hash, data): digest = hash(data).digest() self.log("Calculate digest of M*") self.log("\tD*: " + binToHexRep(digest)) return digest def _getHashAlgo(self, sig): hash = None offset = None hashSize = None if sig[-1] == hexRepToBin("BC"): self.T = sig[-1] hash = sha1 offset = -1 elif sig[-1] == hexRepToBin("CC"): self.T = sig[-2] #hash = The algorithm corresponding to the algo designed by T offset = -2 else: raise ActiveAuthenticationException("Unknow hash algorithm") self.log("Determine hash algorithm by trailer T*") self.log("\tT: " + binToHexRep(self.T)) #Find out the hash size hashSize = len(hash("test").digest()) return (hash, hashSize, offset) def _extractDigest(self, sig, hashSize, offset): digest = sig[offset - hashSize:offset] self.log("Extract digest:") self.log("\tD: " + binToHexRep(digest)) return digest def _extractM1(self, sig, hashSize, offset): M1 = sig[1:offset - hashSize] self.log("Extract M1:") self.log("\tM1: " + binToHexRep(M1)) return M1 def __str__(self): spec = self._asn1Parse() return spec.prettyPrint() def algorithm(self, dg15): """ Return the algorithm name used to store the signature @return: A string from the OID dictionnary. @raise ActiveAuthenticationException: I{Unsupported algorithm}: The algorithm does not exist in the OID enumeration. @raise ActiveAuthenticationException: I{The parameter type is not valid, must be a dataGroup15 object}: The parameter dg15 is not set or invalid. """ if type(dg15) != type(datagroup.DataGroup15(None)): raise ActiveAuthenticationException( "The parameter type is not valid, must be a dataGroup15 object" ) algo = "" try: spec = self._asn1Parse() algo = spec.getComponentByName('algorithm').getComponentByName( 'algorithm').prettyPrint() return OID[algo] except KeyError: raise ActiveAuthenticationException("Unsupported algorithm: " + algo) except Exception, msg: raise ActiveAuthenticationException( "Active Authentication not supported: ", msg)
class ActiveAuthentication(Logger): """ This class implement the Active Authentication protocol. The main method is I{executeAA} that return True is the verification is ok or False. """ def __init__(self, iso7816, openssl=None): """ @param iso7816: a valid iso7816 object @type iso7816: doc9303 """ Logger.__init__(self, "AA") self._iso7816 = iso7816 if not openssl: self._openssl = OpenSSL() else: self._openssl = openssl self._openssl.register(self.log) self.RND_IFD = None self.F = None self.T = None self.decryptedSignature = None self.D = None self.D_ = None self.M1 = None self.M_ = None self._dg15 = None def executeAA(self, dg15, EC=False, hash=sha256): """ Perform the Active Authentication protocol. Work only with RSA, modulus length of 1024 and with SHA1. @param dg15: A initialized dataGroup15 object @type dg15: dataGroup15 @param EC: True if ECDSA is used y the passport @param hash: hash function (default: sha256) @return: True if the authentication succeed, else False. @rtype: Boolean @raise ActiveAuthenticationException: If the Active Authentication is not supported (The DG15 is not found or the hash algo is not supported). @raise ActiveAuthenticationException: If the parameter is not set or invalid. @raise ActiveAuthenticationException: If OpenSSL is not installed. @raise ActiveAuthenticationException: If the public key cannot be recovered from the DG15. @raise ActiveAuthenticationException: If the DG15 is invalid and the signature cannot be verified. """ self._dg15 = dg15 self.RND_IFD = self._genRandom(8) self.signature = self._getSignature(self.RND_IFD) if EC: return self._verifyECSignature(dg15.body, self.signature, self.RND_IFD, hash) self.F = self._decryptSignature(dg15.body, self.signature) (hash, hashSize, offset) = self._getHashAlgo(self.F) self.D = self._extractDigest(self.F, hashSize, offset) self.M1 = self._extractM1(self.F, hashSize, offset) self.M_ = self.M1 + self.RND_IFD self.log("Concatenate M1 with known M2") self.log("\tM*: " + binToHexRep(self.M_)) self.D_ = self._hash(hash, self.M_) self.log("Compare D and D*") self.log("\t" + str(self.D == self.D_)) return self.D == self.D_ def _genRandom(self, size): rnd_ifd = os.urandom(size) self.log("Generate an 8 byte random") self.log("\tRND.IFD: " + binToHexRep(rnd_ifd)) return rnd_ifd def _getSignature(self, rnd_ifd): return self._iso7816.internalAuthentication(rnd_ifd) def getPubKey(self, dg15): """ Retrieve the public key in PEM format from the dataGroup15 @return: A PEM reprensation of the public key @rtype: A string @raise ActiveAuthenticationException: I{The parameter type is not valid, must be a dataGroup15 object}: The parameter dg15 is not set or invalid. @raise ActiveAuthenticationException: I{The public key could not be recovered from the DG15}: Is open SSL installed? """ if type(dg15) != type(datagroup.DataGroup15(None)): raise ActiveAuthenticationException( "The parameter type is not valid, must be a dataGroup15 object" ) try: return self._openssl.retrieveRsaPubKey(dg15.body) except OpenSSLException: return self._openssl.retrieveECPubKey(dg15.body) def _verifyECSignature(self, pubK, signature, challenge, hash): # Generate hash of the challenge challenge = sha256(challenge).digest() # Signature is returned in plain format by the card (big endian) # It must be converted to ASN.1 to be usable by OpenSSL # See https://crypto.stackexchange.com/questions/57731/ecdsa-signature-rs-to-asn1-der-encoding-question sig_rec = ECSignatureRecord() l = int(len(signature) / 2) sig_rec['r'] = int.from_bytes(signature[:l], 'big') sig_rec['s'] = int.from_bytes(signature[-l:], 'big') sig_der = encoder.encode(sig_rec) status = self._openssl.verifyECSignature(pubK, sig_der, challenge) self.log("Verify the EC signature with the public key") self.log("\tStatus: " + str(status)) return status def _decryptSignature(self, pubK, signature): data = self._openssl.retrieveSignedData(pubK, signature) self.log("Decrypt the signature with the public key") self.log("\tF: " + binToHexRep(data)) return data def _hash(self, hash, data): digest = hash(data).digest() self.log("Calculate digest of M*") self.log("\tD*: " + binToHexRep(digest)) return digest def _getHashAlgo(self, sig): hash = None offset = None hashSize = None if sig[-1] == 0xBC: self.T = sig[-1] hash = sha1 offset = -1 elif sig[-1] == 0xCC: self.T = sig[-2] #hash = The algorithm corresponding to the algo designed by T offset = -2 else: raise ActiveAuthenticationException("Unknow hash algorithm") self.log("Determine hash algorithm by trailer T*") self.log("\tT: " + binToHexRep(self.T)) #Find out the hash size hashSize = len(hash(b"test").digest()) return (hash, hashSize, offset) def _extractDigest(self, sig, hashSize, offset): digest = sig[offset - hashSize:offset] self.log("Extract digest:") self.log("\tD: " + binToHexRep(digest)) return digest def _extractM1(self, sig, hashSize, offset): M1 = sig[1:offset - hashSize] self.log("Extract M1:") self.log("\tM1: " + binToHexRep(M1)) return M1 def __str__(self): spec = self._asn1Parse() return spec.prettyPrint() def algorithm(self, dg15): """ Return the algorithm name used to store the signature @return: A string from the OID dictionnary. @raise ActiveAuthenticationException: I{Unsupported algorithm}: The algorithm does not exist in the OID enumeration. @raise ActiveAuthenticationException: I{The parameter type is not valid, must be a dataGroup15 object}: The parameter dg15 is not set or invalid. """ if type(dg15) != type(datagroup.DataGroup15(None)): raise ActiveAuthenticationException( "The parameter type is not valid, must be a dataGroup15 object" ) algo = "" try: spec = self._asn1Parse() algo = spec.getComponentByName('algorithm').getComponentByName( 'algorithm').prettyPrint() return OID[algo] except KeyError: raise ActiveAuthenticationException("Unsupported algorithm: " + algo) except Exception as msg: raise ActiveAuthenticationException( "Active Authentication not supported: ", msg) def _asn1Parse(self): if self._dg15 != None: certType = SubjectPublicKeyInfo() return decoder.decode(self._dg15.body, asn1Spec=certType)[0] return ""
class ActiveAuthentication(Logger): """ This class implements the Active Authentication protocol. The main method is I{executeAA} that returns True if the verification is ok or False. """ def __init__(self, iso7816, openssl=None): """ @param iso7816: a valid iso7816 object @type iso7816: doc9303 """ Logger.__init__(self, "AA") self._iso7816 = iso7816 if not openssl: self._openssl = OpenSSL() else: self._openssl = openssl self.RND_IFD = None self.F = None self.T = None self.decryptedSignature = None self.D = None self.D_ = None self.M1 = None self.M_ = None self._dg15 = None def executeAA(self, dg15): """ Perform the Active Authentication protocol. Work only with RSA, modulus length of 1024 and with SHA1. @param dg15: An initialized dataGroup15 object @type dg15: dataGroup15 @return: True if the authentication succeeded, else False. @rtype: Boolean @raise ActiveAuthenticationException: If the Active Authentication is not supported (The DG15 is not found or the hash algo is not supported). @raise ActiveAuthenticationException: If the parameter is not set or is invalid. @raise ActiveAuthenticationException: If OpenSSL is not installed. @raise ActiveAuthenticationException: If the public key cannot be recovered from the DG15. @raise ActiveAuthenticationException: If the DG15 is invalid and the signature cannot be verified. """ self._dg15 = dg15 self.RND_IFD = self._genRandom(8) self.signature = self._getSignature(self.RND_IFD) self.F = self._decryptSignature(dg15.body, self.signature) (hash, hashSize, offset) = self._getHashAlgo(self.F) self.D = self._extractDigest(self.F, hashSize, offset) self.M1 = self._extractM1(self.F, hashSize, offset) self.M_ = self.M1 + self.RND_IFD self.log("Concatenate M1 with known M2") self.log("\tM*: " + binToHexRep(self.M_)) self.D_ = self._hash(hash, self.M_) self.log("Compare D and D*") self.log("\t" + str(self.D == self.D_)) return self.D == self.D_ def _genRandom(self, size): rnd_ifd = os.urandom(size) self.log("Generate an 8 byte random") self.log("\tRND.IFD: " + binToHexRep(rnd_ifd)) return rnd_ifd def _getSignature(self, rnd_ifd): return self._iso7816.internalAuthentication(rnd_ifd) def getPubKey(self, dg15): """ Retrieve the public key in PEM format from the dataGroup15 @return: A PEM representation of the public key @rtype: A string @raise ActiveAuthenticationException: I{The parameter type is not valid, must be a dataGroup15 object}: The parameter dg15 is not set or is invalid. @raise ActiveAuthenticationException: I{The public key could not be recovered from the DG15}: Is open SSL installed? """ if type(dg15) != type(datagroup.DataGroup15(None)): raise ActiveAuthenticationException("The parameter type is not valid, must be a dataGroup15 object") return self._openssl.retrieveRsaPubKey(dg15.body) def _decryptSignature(self, pubK, signature): data = self._openssl.retrieveSignedData(pubK, signature) self.log("Decrypt the signature with the public key") self.log("\tF: " + binToHexRep(data)) return data def _hash(self, hash, data): digest = hash(data).digest() self.log("Calculate digest of M*") self.log("\tD*: " + binToHexRep(digest)) return digest def _getHashAlgo(self, sig): hash = None offset = None hashSize = None if sig[-1] == hexRepToBin("BC"): self.T = sig[-1] hash = sha1 offset = -1 elif sig[-1] == hexRepToBin("CC"): self.T = sig[-2] #hash = The algorithm corresponding to the algo designed by T offset = -2 else: raise ActiveAuthenticationException("Unknow hash algorithm") self.log("Determine hash algorithm by trailer T*") self.log("\tT: " + binToHexRep(self.T)) #Find out the hash size hashSize = len(hash("test").digest()) return (hash, hashSize, offset) def _extractDigest(self, sig, hashSize, offset): digest = sig[offset - hashSize:offset] self.log("Extract digest:") self.log("\tD: " + binToHexRep(digest)) return digest def _extractM1(self, sig, hashSize, offset): M1 = sig[1:offset - hashSize] self.log("Extract M1:") self.log("\tM1: " + binToHexRep(M1)) return M1 def __str__(self): spec = self._asn1Parse() return spec.prettyPrint() def algorithm(self, dg15): """ Return the algorithm name used to store the signature @return: A string from the OID dictionary. @raise ActiveAuthenticationException: I{Unsupported algorithm}: The algorithm does not exist in the OID enumeration. @raise ActiveAuthenticationException: I{The parameter type is not valid, must be a dataGroup15 object}: The parameter dg15 is not set or is invalid. """ if type(dg15) != type(datagroup.DataGroup15(None)): raise ActiveAuthenticationException("The parameter type is not valid, must be a dataGroup15 object") algo = "" try: spec = self._asn1Parse() algo = spec.getComponentByName('algorithm').getComponentByName('algorithm').prettyPrint() return OID[algo] except KeyError: raise ActiveAuthenticationException("Unsupported algorithm: " + algo) except Exception, msg: raise ActiveAuthenticationException("Active Authentication not supported: ", msg)