class TackBreakSig(TlsStructure): LENGTH = 128 def __init__(self, data=None): TlsStructure.__init__(self, data) if data is not None and len(data) != TackBreakSig.LENGTH: raise SyntaxError( "Break signature is the wrong size. Is %s and should be %s." % (len(data), TackBreakSig.LENGTH)) if data is not None: self.public_key = ECPublicKey(self.getBytes(64)) self.signature = self.getBytes(64) if not self._verifySignature(): raise SyntaxError("Signature verification failure") if self.index != len(data): raise SyntaxError("Excess bytes in TACK_Break_Sig") @classmethod def createFromPem(cls, data): return cls(PEMDecoder(data).getDecoded("TACK BREAK SIG")) @classmethod def createFromPemList(cls, data): """Parse a string containing a sequence of PEM Break Sigs. Raise a SyntaxError if input is malformed. """ breakSigs = [] bList = PEMDecoder(data).getDecodedList("TACK BREAK SIG") for b in bList: breakSigs.append(TackBreakSig(b)) return breakSigs @classmethod def createFromParameters(cls, public_key, private_key): assert (len(public_key) == 64) tackBreakSig = cls() tackBreakSig.public_key = public_key tackBreakSig.signature = private_key.getSignature( bytearray("tack_break_sig", "ascii")) return tackBreakSig def serialize(self): """Return a bytearray containing the TACK_Break_Sig.""" w = TlsStructureWriter(TackBreakSig.LENGTH) w.add(self.public_key.getRawKey(), 64) w.add(self.signature, 64) assert (w.index == len(w.bytes)) return w.getBytes() def serializeAsPem(self): return PEMEncoder(self.serialize()).getEncoded("TACK BREAK SIG") def getTackId(self): return str(self.public_key) def _verifySignature(self): return self.public_key.verify(bytearray("tack_break_sig"), self.signature) def __str__(self): """Return a readable string describing this TACK_Break_Sig. Used by the "TACK view" command to display TACK objects.""" return "Breaks TACK ID = %s\n" % self.getTackId()
class TackBreakSig(TlsStructure): LENGTH = 128 def __init__(self, data=None): TlsStructure.__init__(self, data) if data is not None and len(data) != TackBreakSig.LENGTH: raise SyntaxError("Break signature is the wrong size. Is %s and should be %s." % (len(data), TackBreakSig.LENGTH)) if data is not None: self.public_key = ECPublicKey(self.getBytes(64)) self.signature = self.getBytes(64) if not self._verifySignature(): raise SyntaxError("Signature verification failure") if self.index != len(data): raise SyntaxError("Excess bytes in TACK_Break_Sig") @classmethod def createFromPem(cls, data): return cls(PEMDecoder(data).getDecoded("TACK BREAK SIG")) @classmethod def createFromPemList(cls, data): """Parse a string containing a sequence of PEM Break Sigs. Raise a SyntaxError if input is malformed. """ breakSigs = [] bList = PEMDecoder(data).getDecodedList("TACK BREAK SIG") for b in bList: breakSigs.append(TackBreakSig(b)) return breakSigs @classmethod def createFromParameters(cls, public_key, private_key): assert(len(public_key) == 64) tackBreakSig = cls() tackBreakSig.public_key = public_key tackBreakSig.signature = private_key.getSignature(bytearray("tack_break_sig", "ascii")) return tackBreakSig def serialize(self): """Return a bytearray containing the TACK_Break_Sig.""" w = TlsStructureWriter(TackBreakSig.LENGTH) w.add(self.public_key.getRawKey(), 64) w.add(self.signature, 64) assert(w.index == len(w.bytes)) return w.getBytes() def serializeAsPem(self): return PEMEncoder(self.serialize()).getEncoded("TACK BREAK SIG") def getTackId(self): return str(self.public_key) def _verifySignature(self): return self.public_key.verify(bytearray("tack_break_sig"), self.signature) def __str__(self): """Return a readable string describing this TACK_Break_Sig. Used by the "TACK view" command to display TACK objects.""" return "Breaks TACK ID = %s\n" % self.getTackId()
class TackKeyFile(TlsStructure): LENGTH = 149 # length of keyfile in bytes def __init__(self, data=None, password=None): TlsStructure.__init__(self, data) if data is not None: self.version = self.getInt(1) if self.version != 1: raise SyntaxError("Bad version in Secret File") self.password = password self.iter_count = self.getInt(4) self.salt = self.getBytes(16) self.ciphertext = self.getBytes(32) self.public_key = ECPublicKey(self.getBytes(64)) self.mac = bytearray(self.getBytes(32)) if self.password is not None: rawPrivateKey = self._decryptKey(password, self.salt, self.ciphertext, self.iter_count, self.public_key, self.mac) self.private_key = ECPrivateKey(rawPrivateKey, self.public_key.getRawKey()) @classmethod def createRandom(cls, password): tackKeyFile = cls() tackKeyFile.password = password tackKeyFile.version = 1 tackKeyFile.iter_count = 8192 tackKeyFile.salt = bytearray(os.urandom(16)) tackKeyFile.public_key, tackKeyFile.private_key = ECGenerator( ).generateECKeyPair() tackKeyFile.ciphertext, tackKeyFile.mac = tackKeyFile._encryptKey( password, tackKeyFile.salt, tackKeyFile.iter_count, tackKeyFile.public_key, tackKeyFile.private_key) return tackKeyFile @classmethod def createFromPem(cls, pem, password): return cls(PEMDecoder(pem).getDecoded("TACK PRIVATE KEY"), password) def getPublicKey(self): return self.public_key def getPrivateKey(self): return self.private_key def serialize(self): w = TlsStructureWriter(TackKeyFile.LENGTH) w.add(self.version, 1) w.add(self.iter_count, 4) w.add(self.salt, 16) w.add(self.ciphertext, 32) w.add(self.public_key.getRawKey(), 64) w.add(self.mac, 32) assert (w.index == len(w.bytes)) # did we fill entire bytearray? return w.getBytes() def serializeAsPem(self): return PEMEncoder(self.serialize()).getEncoded("TACK PRIVATE KEY") def _encryptKey(self, password, salt, iter_count, public_key, private_key): encKey, authKey = self._deriveKeys(password, salt, iter_count) ciphertext = AES(encKey, bytearray(16)).encrypt(private_key.getRawKey()) macData = ciphertext + public_key.getRawKey() mac = Digest.HMAC_SHA256(authKey, macData) return ciphertext, mac def _decryptKey(self, password, salt, ciphertext, iter_count, public_key, mac): encKey, authKey = self._deriveKeys(password, salt, iter_count) macData = ciphertext + public_key.getRawKey() calcMac = Digest.HMAC_SHA256(authKey, macData) if not Util.constTimeCompare(calcMac, mac): raise InvalidPasswordException("Bad password") return AES(encKey, bytearray(16)).decrypt(ciphertext) # Uses PBKDF2, then HMAC-SHA256 as PRF to derive independent 32-byte keys def _deriveKeys(self, password, salt, iter_count): assert (iter_count > 0) masterKey = PBKDF2.hmac_sha256(password, salt, iter_count) encKey = Digest.HMAC_SHA256(masterKey, bytearray([1])) authKey = Digest.HMAC_SHA256(masterKey, bytearray([2])) return encKey, authKey def __str__(self): return """TACK ID = %s\n""" % str(self.public_key)
class Tack(TlsStructure): LENGTH = 166 def __init__(self, data=None): TlsStructure.__init__(self, data) if data is not None: self.public_key = ECPublicKey(self.getBytes(64)) self.min_generation = self.getInt(1) self.generation = self.getInt(1) self.expiration = self.getInt(4) self.target_hash = self.getBytes(32) self.signature = self.getBytes(64) if not self._verifySignature(): raise SyntaxError("Signature verification failure") if self.index != len(data): raise SyntaxError("Excess bytes in TACK") @classmethod def createFromPem(cls, pem): data = PEMDecoder(pem).getDecoded("TACK") if len(data) != Tack.LENGTH: raise SyntaxError("TACK is the wrong size. %s, should be %s" % (len(data), Tack.LENGTH)) return cls(data) @classmethod def createFromParameters(cls, public_key, private_key, min_generation, generation, expiration, target_hash): assert(len(public_key.getRawKey()) == 64) assert(0 <= min_generation <= 255) assert(0 <= generation <= 255 and generation >= min_generation) assert(0 <= expiration <= 2**32-1) assert(len(target_hash) == 32) tack = cls() tack.public_key = public_key tack.min_generation = min_generation tack.generation = generation tack.expiration = expiration tack.target_hash = target_hash tack.signature = private_key.getSignature(tack._getDataToSign()) return tack def getTackId(self): return str(self.public_key) def serialize(self): writer = TlsStructureWriter(64) writer.add(self.signature, 64) return self._serializePrelude() + writer.getBytes() def serializeAsPem(self): return PEMEncoder(self.serialize()).getEncoded("TACK") def _serializePrelude(self): writer = TlsStructureWriter(Tack.LENGTH - 64) writer.add(self.public_key.getRawKey(), 64) writer.add(self.min_generation, 1) writer.add(self.generation, 1) writer.add(self.expiration, 4) writer.add(self.target_hash, 32) return writer.getBytes() def _getDataToSign(self): return bytearray("tack_sig", "ascii") + self._serializePrelude() def _verifySignature(self): bytesToVerify = self._getDataToSign() return self.public_key.verify(bytesToVerify, self.signature) def __str__(self): """Return a readable string describing this TACK. Used by the "TACK view" command to display TACK objects.""" s =\ """TACK ID = %s min_generation = %d generation = %d expiration = %s target_hash = %s\n""" %\ (self.getTackId(), self.min_generation, self.generation, Time.posixTimeToStr(self.expiration*60), Util.writeBytes(self.target_hash)) return s
class TackKeyFile(TlsStructure): LENGTH = 149 # length of keyfile in bytes def __init__(self, data=None, password=None): TlsStructure.__init__(self, data) if data is not None: self.version = self.getInt(1) if self.version != 1: raise SyntaxError("Bad version in Secret File") self.password = password self.iter_count = self.getInt(4) self.salt = self.getBytes(16) self.ciphertext = self.getBytes(32) self.public_key = ECPublicKey(self.getBytes(64)) self.mac = bytearray(self.getBytes(32)) if self.password is not None: rawPrivateKey = self._decryptKey(password, self.salt, self.ciphertext, self.iter_count, self.public_key, self.mac) self.private_key = ECPrivateKey(rawPrivateKey, self.public_key.getRawKey()) @classmethod def createRandom(cls, password): tackKeyFile = cls() tackKeyFile.password = password tackKeyFile.version = 1 tackKeyFile.iter_count = 8192 tackKeyFile.salt = bytearray(os.urandom(16)) tackKeyFile.public_key, tackKeyFile.private_key = ECGenerator().generateECKeyPair() tackKeyFile.ciphertext, tackKeyFile.mac = tackKeyFile._encryptKey(password, tackKeyFile.salt, tackKeyFile.iter_count, tackKeyFile.public_key, tackKeyFile.private_key) return tackKeyFile @classmethod def createFromPem(cls, pem, password): return cls(PEMDecoder(pem).getDecoded("TACK PRIVATE KEY"), password) def getPublicKey(self): return self.public_key def getPrivateKey(self): return self.private_key def serialize(self): w = TlsStructureWriter(TackKeyFile.LENGTH) w.add(self.version, 1) w.add(self.iter_count, 4) w.add(self.salt, 16) w.add(self.ciphertext, 32) w.add(self.public_key.getRawKey(), 64) w.add(self.mac, 32) assert(w.index == len(w.bytes)) # did we fill entire bytearray? return w.getBytes() def serializeAsPem(self): return PEMEncoder(self.serialize()).getEncoded("TACK PRIVATE KEY") def _encryptKey(self, password, salt, iter_count, public_key, private_key): encKey, authKey = self._deriveKeys(password, salt, iter_count) ciphertext = AES(encKey, bytearray(16)).encrypt(private_key.getRawKey()) macData = ciphertext + public_key.getRawKey() mac = Digest.HMAC_SHA256(authKey, macData) return ciphertext, mac def _decryptKey(self, password, salt, ciphertext, iter_count, public_key, mac): encKey, authKey = self._deriveKeys(password, salt, iter_count) macData = ciphertext + public_key.getRawKey() calcMac = Digest.HMAC_SHA256(authKey, macData) if not Util.constTimeCompare(calcMac, mac): raise InvalidPasswordException("Bad password") return AES(encKey, bytearray(16)).decrypt(ciphertext) # Uses PBKDF2, then HMAC-SHA256 as PRF to derive independent 32-byte keys def _deriveKeys(self, password, salt, iter_count): assert(iter_count>0) masterKey = PBKDF2.hmac_sha256(password, salt, iter_count) encKey = Digest.HMAC_SHA256(masterKey, bytearray([1])) authKey = Digest.HMAC_SHA256(masterKey, bytearray([2])) return encKey, authKey def __str__(self): return """TACK ID = %s\n""" % str(self.public_key)