def getSchedule(self, name): """ Get a schedule with the given name. :param str name: The name of the schedule. :return: A new Schedule object. :rtype: Schedule :raises GroupManagerDb.Error: If the schedule does not exist or other database error. """ schedule = None try: cursor = self._database.cursor() cursor.execute("SELECT schedule FROM schedules WHERE schedule_name=?", (name,)) result = cursor.fetchone() if result != None: schedule = Schedule() schedule.wireDecode(bytearray(result[0])) cursor.close() except Exception as ex: raise GroupManagerDb.Error("Sqlite3GroupManagerDb.getSchedule: SQLite error: " + str(ex)) if schedule == None: raise GroupManagerDb.Error("Sqlite3GroupManagerDb.getSchedule: Cannot get the result from the database") return schedule
def getSchedule(self, name): """ Get a schedule with the given name. :param str name: The name of the schedule. :return: A new Schedule object. :rtype: Schedule :raises GroupManagerDb.Error: If the schedule does not exist or other database error. """ schedule = None try: cursor = self._database.cursor() cursor.execute( "SELECT schedule FROM schedules WHERE schedule_name=?", (name, )) result = cursor.fetchone() if result != None: schedule = Schedule() schedule.wireDecode(bytearray(result[0])) cursor.close() except Exception as ex: raise GroupManagerDb.Error( "Sqlite3GroupManagerDb.getSchedule: SQLite error: " + str(ex)) if schedule == None: raise GroupManagerDb.Error( "Sqlite3GroupManagerDb.getSchedule: Cannot get the result from the database") return schedule
def _handleCoveringKey(self, interest, data, timeSlot, onEncryptedKeys, onError): """ This is called from an expressInterest OnData to check that the encryption key contained in data fits the timeSlot. This sends a refined interest if required. :param Interest interest: The interest given to expressInterest. :param Data data: The fetched Data packet. :param float timeSlot: The time slot as milliseconds since Jan 1, 1970 UTC. :param onEncryptedKeys: When there are no more interests to process, this calls onEncryptedKeys(keys) where keys is a list of encrypted content key Data packets. If onEncryptedKeys is None, this does not use it. :type onEncryptedKeys: function object :param onError: This calls onError(errorCode, message) for an error. :type onError: function object """ timeCount = round(timeSlot) keyRequest = self._keyRequests[timeCount] interestName = interest.getName() keyName = data.getName() begin = Schedule.fromIsoString( str(keyName.get(Producer.START_TIME_STAMP_INDEX).getValue())) end = Schedule.fromIsoString( str(keyName.get(Producer.END_TIME_STAMP_INDEX).getValue())) if timeSlot >= end: # If the received E-KEY covers some earlier period, try to retrieve # an E-KEY covering a later one. timeRange = Exclude(interest.getExclude()) Producer.excludeBefore( timeRange, keyName.get(Producer.START_TIME_STAMP_INDEX)) keyRequest.repeatAttempts[interestName] = 0 self._sendKeyInterest( Interest(interestName).setExclude(timeRange).setChildSelector( 1), timeSlot, onEncryptedKeys, onError) else: # If the received E-KEY covers the content key, encrypt the content. encryptionKey = data.getContent() # If everything is correct, save the E-KEY as the current key. if self._encryptContentKey(encryptionKey, keyName, timeSlot, onEncryptedKeys, onError): keyInfo = self._eKeyInfo[interestName] keyInfo.beginTimeSlot = begin keyInfo.endTimeSlot = end keyInfo.keyBits = encryptionKey
def getGroupKey(self, timeSlot): """ Create a group key for the interval into which timeSlot falls. This creates a group key if it doesn't exist, and encrypts the key using the public key of each eligible member. :param float timeSlot: The time slot to cover as milliseconds since Jan 1, 1970 UTC. :return: A List of Data packets where the first is the E-KEY data packet with the group's public key and the rest are the D-KEY data packets with the group's private key encrypted with the public key of each eligible member. :raises GroupManagerDb.Error: For a database error. :raises SecurityException: For an error using the security KeyChain. """ unsortedMemberKeys = {} result = [] # Get the time interval. finalInterval = self._calculateInterval(timeSlot, unsortedMemberKeys) if finalInterval.isValid() == False: return result startTimeStamp = Schedule.toIsoString(finalInterval.getStartTime()) endTimeStamp = Schedule.toIsoString(finalInterval.getEndTime()) # Generate the private and public keys. (privateKeyBlob, publicKeyBlob) = self._generateKeyPair() # Add the first element to the result. # The E-KEY (public key) data packet name convention is: # /<data_type>/E-KEY/[start-ts]/[end-ts] data = self._createEKeyData(startTimeStamp, endTimeStamp, publicKeyBlob) result.append(data) # Encrypt the private key with the public key from each member's certificate. # Sort the key names. for keyName in sorted(unsortedMemberKeys.keys()): certificateKey = unsortedMemberKeys[keyName] # Generate the name of the packet. # The D-KEY (private key) data packet name convention is: # /<data_type>/D-KEY/[start-ts]/[end-ts]/[member-name] data = self._createDKeyData(startTimeStamp, endTimeStamp, keyName, privateKeyBlob, certificateKey) result.append(data) return result
def _handleCoveringKey( self, interest, data, timeSlot, onEncryptedKeys, onError): """ This is called from an expressInterest OnData to check that the encryption key contained in data fits the timeSlot. This sends a refined interest if required. :param Interest interest: The interest given to expressInterest. :param Data data: The fetched Data packet. :param float timeSlot: The time slot as milliseconds since Jan 1, 1970 UTC. :param onEncryptedKeys: When there are no more interests to process, this calls onEncryptedKeys(keys) where keys is a list of encrypted content key Data packets. If onEncryptedKeys is None, this does not use it. :type onEncryptedKeys: function object :param onError: This calls onError(errorCode, message) for an error. :type onError: function object """ timeCount = round(timeSlot) keyRequest = self._keyRequests[timeCount] interestName = interest.getName() keyName = data.getName() begin = Schedule.fromIsoString( str(keyName.get(Producer.START_TIME_STAMP_INDEX).getValue())) end = Schedule.fromIsoString( str(keyName.get(Producer.END_TIME_STAMP_INDEX).getValue())) if timeSlot >= end: # If the received E-KEY covers some earlier period, try to retrieve # an E-KEY covering a later one. timeRange = Exclude(interest.getExclude()) Producer.excludeBefore(timeRange, keyName.get(Producer.START_TIME_STAMP_INDEX)) keyRequest.repeatAttempts[interestName] = 0 self._sendKeyInterest( Interest(interestName).setExclude(timeRange).setChildSelector(1), timeSlot, onEncryptedKeys, onError) else: # If the received E-KEY covers the content key, encrypt the content. encryptionKey = data.getContent() # If everything is correct, save the E-KEY as the current key. if self._encryptContentKey( encryptionKey, keyName, timeSlot, onEncryptedKeys, onError): keyInfo = self._eKeyInfo[interestName] keyInfo.beginTimeSlot = begin keyInfo.endTimeSlot = end keyInfo.keyBits = encryptionKey
def getGroupKey(self, timeSlot): """ Create a group key for the interval into which timeSlot falls. This creates a group key if it doesn't exist, and encrypts the key using the public key of each eligible member. :param float timeSlot: The time slot to cover as milliseconds since Jan 1, 1970 UTC. :return: A List of Data packets where the first is the E-KEY data packet with the group's public key and the rest are the D-KEY data packets with the group's private key encrypted with the public key of each eligible member. :raises GroupManagerDb.Error: For a database error. :raises SecurityException: For an error using the security KeyChain. """ unsortedMemberKeys = {} result = [] # Get the time interval. finalInterval = self._calculateInterval(timeSlot, unsortedMemberKeys) if finalInterval.isValid() == False: return result startTimeStamp = Schedule.toIsoString(finalInterval.getStartTime()) endTimeStamp = Schedule.toIsoString(finalInterval.getEndTime()) # Generate the private and public keys. (privateKeyBlob, publicKeyBlob) = self._generateKeyPair() # Add the first element to the result. # The E-KEY (public key) data packet name convention is: # /<data_type>/E-KEY/[start-ts]/[end-ts] data = self._createEKeyData(startTimeStamp, endTimeStamp, publicKeyBlob) result.append(data) # Encrypt the private key with the public key from each member's certificate. # Sort the key names. for keyName in sorted(unsortedMemberKeys.keys()): certificateKey = unsortedMemberKeys[keyName] # Generate the name of the packet. # The D-KEY (private key) data packet name convention is: # /<data_type>/D-KEY/[start-ts]/[end-ts]/[member-name] data = self._createDKeyData( startTimeStamp, endTimeStamp, keyName, privateKeyBlob, certificateKey) result.append(data) return result
def produce(self, data, timeSlot, content, onError=defaultOnError): """ Encrypt the given content with the content key that covers timeSlot, and update the data packet with the encrypted content and an appropriate data name. :param Data data: An empty Data object which is updated. :param float timeSlot: The time slot as milliseconds since Jan 1, 1970 UTC. :param Blob content: The content to encrypt. :param onError: (optional) This calls onError(errorCode, message) for an error, where errorCode is from EncryptError.ErrorCode and message is a str. If omitted, use a default callback which does nothing. NOTE: The library will log any exceptions raised by this callback, but for better error handling the callback should catch and properly handle any exceptions. :type onError: function object """ # Get a content key. contentKeyName = self.createContentKey(timeSlot, None, onError) contentKey = self._database.getContentKey(timeSlot) # Produce data. dataName = Name(self._namespace) dataName.append(Schedule.toIsoString(timeSlot)) data.setName(dataName) params = EncryptParams(EncryptAlgorithmType.AesCbc, 16) Encryptor.encryptData(data, content, contentKeyName, contentKey, params) self._keyChain.sign(data)
def __str__(self): """ Get a string representation of this certificate. :return: The string representation. :rtype: str """ result = "" result += "Certificate name:\n" result += " " + self.getName().toUri() + "\n" result += "Validity:\n" result += " NotBefore: " + Schedule.toIsoString( self.getValidityPeriod().getNotBefore()) + "\n" result += " NotAfter: " + Schedule.toIsoString( self.getValidityPeriod().getNotAfter()) + "\n" # TODO: Print the extension. result += "Public key bits:\n" try: result += Common.base64Encode(self.getPublicKey().toBytes(), True) except: # No public key. pass result += "Signature Information:\n" result += " Signature Type: " if isinstance(self.getSignature(), Sha256WithEcdsaSignature): result += "SignatureSha256WithEcdsa\n" elif isinstance(self.getSignature(), Sha256WithRsaSignature): result += "SignatureSha256WithRsa\n" else: result += "<unknown>\n" if KeyLocator.canGetFromSignature(self.getSignature()): result += " Key Locator: " keyLocator = KeyLocator.getFromSignature(self.getSignature()) if keyLocator.getType() == KeyLocatorType.KEYNAME: if keyLocator.getKeyName().equals(self.getKeyName()): result += "Self-Signed " result += "Name=" + keyLocator.getKeyName().toUri() + "\n" else: result += "<no KeyLocator key name>\n" return result
def __str__(self): """ Get a string representation of this certificate. :return: The string representation. :rtype: str """ result = "" result += "Certificate name:\n" result += " " + self.getName().toUri() + "\n" result += "Validity:\n" result += " NotBefore: " + Schedule.toIsoString( self.getValidityPeriod().getNotBefore()) + "\n" result += " NotAfter: " + Schedule.toIsoString( self.getValidityPeriod().getNotAfter()) + "\n" # TODO: Print the extension. result += "Public key bits:\n" try: result += Common.base64Encode(self.getPublicKey().toBytes(), True) except: # No public key. pass result += "Signature Information:\n" result += " Signature Type: " if type(self.getSignature()) is Sha256WithEcdsaSignature: result += "SignatureSha256WithEcdsa\n" elif type(self.getSignature()) is Sha256WithRsaSignature: result += "SignatureSha256WithRsa\n" else: result += "<unknown>\n" if KeyLocator.canGetFromSignature(self.getSignature()): result += " Key Locator: " keyLocator = KeyLocator.getFromSignature(self.getSignature()) if keyLocator.getType() == KeyLocatorType.KEYNAME: if keyLocator.getKeyName().equals(self.getKeyName()): result += "Self-Signed " result += "Name=" + keyLocator.getKeyName().toUri() + "\n" else: result += "<no KeyLocator key name>\n" return result
def _encryptContentKey(self, encryptionKey, eKeyName, timeSlot, onEncryptedKeys, onError): """ Get the content key from the database_ and encrypt it for the timeSlot using encryptionKey. :param Blob encryptionKey: The encryption key value. :param Name eKeyName: The key name for the EncryptedContent. :param float timeSlot: The time slot as milliseconds since Jan 1, 1970 UTC. :param onEncryptedKeys: When there are no more interests to process, this calls onEncryptedKeys(keys) where keys is a list of encrypted content key Data packets. If onEncryptedKeys is None, this does not use it. :type onEncryptedKeys: function object :param onError: This calls onError(errorCode, message) for an error. :type onError: function object :return: True if encryption succeeds, otherwise False. :rtype: bool """ timeCount = round(timeSlot) keyRequest = self._keyRequests[timeCount] keyName = Name(self._namespace) keyName.append(Encryptor.NAME_COMPONENT_C_KEY) keyName.append( Schedule.toIsoString(Producer._getRoundedTimeSlot(timeSlot))) contentKey = self._database.getContentKey(timeSlot) cKeyData = Data() cKeyData.setName(keyName) params = EncryptParams(EncryptAlgorithmType.RsaOaep) try: Encryptor.encryptData( cKeyData, contentKey, eKeyName, encryptionKey, params) except Exception as ex: try: onError(EncryptError.ErrorCode.EncryptionFailure, "encryptData error: " + repr(ex)) except: logging.exception("Error in onError") return False self._keyChain.sign(cKeyData) keyRequest.encryptedKeys.append(cKeyData) self._updateKeyRequest(keyRequest, timeCount, onEncryptedKeys) return True
def _encryptContentKey(self, encryptionKey, eKeyName, timeSlot, onEncryptedKeys, onError): """ Get the content key from the database_ and encrypt it for the timeSlot using encryptionKey. :param Blob encryptionKey: The encryption key value. :param Name eKeyName: The key name for the EncryptedContent. :param float timeSlot: The time slot as milliseconds since Jan 1, 1970 UTC. :param onEncryptedKeys: When there are no more interests to process, this calls onEncryptedKeys(keys) where keys is a list of encrypted content key Data packets. If onEncryptedKeys is None, this does not use it. :type onEncryptedKeys: function object :param onError: This calls onError(errorCode, message) for an error. :type onError: function object :return: True if encryption succeeds, otherwise False. :rtype: bool """ timeCount = round(timeSlot) keyRequest = self._keyRequests[timeCount] keyName = Name(self._namespace) keyName.append(Encryptor.NAME_COMPONENT_C_KEY) keyName.append( Schedule.toIsoString(Producer._getRoundedTimeSlot(timeSlot))) contentKey = self._database.getContentKey(timeSlot) cKeyData = Data() cKeyData.setName(keyName) params = EncryptParams(EncryptAlgorithmType.RsaOaep) try: Encryptor.encryptData(cKeyData, contentKey, eKeyName, encryptionKey, params) except Exception as ex: try: onError(EncryptError.ErrorCode.EncryptionFailure, "encryptData error: " + repr(ex)) except: logging.exception("Error in onError") return False self._keyChain.sign(cKeyData) keyRequest.encryptedKeys.append(cKeyData) self._updateKeyRequest(keyRequest, timeCount, onEncryptedKeys) return True
def insert(self, certificate): """ Insert the certificate into the cache. The inserted certificate will be removed no later than its NotAfter time, or maxLifetimeMilliseconds given to the constructor. :param CertificateV2 certificate: The certificate object, which is copied. """ notAfterTime = certificate.getValidityPeriod().getNotAfter() # _nowOffsetMilliseconds is only used for testing. now = Common.getNowMilliseconds() + self._nowOffsetMilliseconds if notAfterTime < now: logging.getLogger(__name__).info( "Not adding " + certificate.getName().toUri() + ": already expired at " + Schedule.toIsoString(notAfterTime)) return removalTime = min(notAfterTime, now + self._maxLifetimeMilliseconds) if removalTime < self._nextRefreshTime: # We need to run refresh() sooner. self._nextRefreshTime = removalTime logging.getLogger(__name__).info("Adding " + certificate.getName().toUri() + ", will remove in " + str((removalTime - now) / (3600 * 1000.0)) + " hours") certificateCopy = CertificateV2(certificate) certificateName = certificateCopy.getName() if certificateName in self._certificatesByName: # A duplicate name. Simply replace. self._certificatesByName[ certificateName]._certificate = certificateCopy self._certificatesByName[ certificateName]._removalTime = removalTime else: # Insert into _certificatesByNameKeys sorted. # Keep it sync with _certificatesByName. self._certificatesByName[ certificateName] = CertificateCacheV2._Entry( certificateCopy, removalTime) bisect.insort(self._certificatesByNameKeys, certificateName)
def insert(self, certificate): """ Insert the certificate into the cache. The inserted certificate will be removed no later than its NotAfter time, or maxLifetimeMilliseconds given to the constructor. :param CertificateV2 certificate: The certificate object, which is copied. """ notAfterTime = certificate.getValidityPeriod().getNotAfter() # _nowOffsetMilliseconds is only used for testing. now = Common.getNowMilliseconds() + self._nowOffsetMilliseconds if notAfterTime < now: logging.getLogger(__name__).info("Not adding " + certificate.getName().toUri() + ": already expired at " + Schedule.toIsoString(notAfterTime)) return removalTime = min(notAfterTime, now + self._maxLifetimeMilliseconds) if removalTime < self._nextRefreshTime: # We need to run refresh() sooner. self._nextRefreshTime = removalTime logging.getLogger(__name__).info("Adding " + certificate.getName().toUri() + ", will remove in " + str((removalTime - now) / (3600 * 1000.0)) + " hours") certificateCopy = CertificateV2(certificate) certificateName = certificateCopy.getName() if certificateName in self._certificatesByName: # A duplicate name. Simply replace. self._certificatesByName[certificateName]._certificate = certificateCopy self._certificatesByName[certificateName]._removalTime = removalTime else: # Insert into _certificatesByNameKeys sorted. # Keep it sync with _certificatesByName. self._certificatesByName[certificateName] = CertificateCacheV2._Entry( certificateCopy, removalTime) bisect.insort(self._certificatesByNameKeys, certificateName)
def createContentKey(self, timeSlot, onEncryptedKeys, onError=defaultOnError): """ Create the content key corresponding to the timeSlot. This first checks if the content key exists. For an existing content key, this returns the content key name directly. If the key does not exist, this creates one and encrypts it using the corresponding E-KEYs. The encrypted content keys are passed to the onEncryptedKeys callback. :param float timeSlot: The time slot as milliseconds since Jan 1, 1970 UTC. :param onEncryptedKeys: If this creates a content key, then this calls onEncryptedKeys(keys) where keys is a list of encrypted content key Data packets. If onEncryptedKeys is None, this does not use it. NOTE: The library will log any exceptions raised by this callback, but for better error handling the callback should catch and properly handle any exceptions. :type onEncryptedKeys: function object :param onError: (optional) This calls errorCode, message) for an error, where errorCode is from EncryptError.ErrorCode and message is a str. If omitted, use a default callback which does nothing. NOTE: The library will log any exceptions raised by this callback, but for better error handling the callback should catch and properly handle any exceptions. :type onError: function object :return: The content key name. :rtype: Name """ hourSlot = Producer._getRoundedTimeSlot(timeSlot) # Create the content key name. contentKeyName = Name(self._namespace) contentKeyName.append(Encryptor.NAME_COMPONENT_C_KEY) contentKeyName.append(Schedule.toIsoString(hourSlot)) # Check if we have created the content key before. if self._database.hasContentKey(timeSlot): # We have created the content key. Return its name directly. return contentKeyName # We haven't created the content key. Create one and add it into the # database. aesParams = AesKeyParams(128) contentKeyBits = AesAlgorithm.generateKey(aesParams).getKeyBits() self._database.addContentKey(timeSlot, contentKeyBits) # Now we need to retrieve the E-KEYs for content key encryption. timeCount = round(timeSlot) self._keyRequests[timeCount] = Producer._KeyRequest(len( self._eKeyInfo)) keyRequest = self._keyRequests[timeCount] # Send interests for all nodes in the tree. for keyName in self._eKeyInfo: # For each current E-KEY. keyInfo = self._eKeyInfo[keyName] if (timeSlot < keyInfo.beginTimeSlot or timeSlot >= keyInfo.endTimeSlot): # The current E-KEY cannot cover the content key, so retrieve one. keyRequest.repeatAttempts[keyName] = 0 self._sendKeyInterest(Interest(keyName), timeSlot, onEncryptedKeys, onError) else: # The current E-KEY can cover the content key. # Encrypt the content key directly. eKeyName = Name(keyName) eKeyName.append(Schedule.toIsoString(keyInfo.beginTimeSlot)) eKeyName.append(Schedule.toIsoString(keyInfo.endTimeSlot)) self._encryptContentKey(keyInfo.keyBits, eKeyName, timeSlot, onEncryptedKeys, onError) return contentKeyName
def getGroupKey(self, timeSlot, needRegenerate=True): """ Create a group key for the interval into which timeSlot falls. This creates a group key if it doesn't exist, and encrypts the key using the public key of each eligible member. :param float timeSlot: The time slot to cover as milliseconds since Jan 1, 1970 UTC. :param bool needRegenerate: (optional) needRegenerate should be True if this is the first time this method is called, or a member was removed. needRegenerate can be False if this is not the first time this method is called, or a member was added. If omitted, use True. :return: A List of Data packets where the first is the E-KEY data packet with the group's public key and the rest are the D-KEY data packets with the group's private key encrypted with the public key of each eligible member. :raises GroupManagerDb.Error: For a database error. :raises SecurityException: For an error using the security KeyChain. """ unsortedMemberKeys = {} result = [] # Get the time interval. finalInterval = self._calculateInterval(timeSlot, unsortedMemberKeys) if finalInterval.isValid() == False: return result startTimeStamp = Schedule.toIsoString(finalInterval.getStartTime()) endTimeStamp = Schedule.toIsoString(finalInterval.getEndTime()) # Generate the private and public keys. eKeyName = Name(self._namespace) eKeyName.append(Encryptor.NAME_COMPONENT_E_KEY).append( startTimeStamp).append(endTimeStamp) if not needRegenerate and self._database.hasEKey(eKeyName): (publicKeyBlob, privateKeyBlob) = self._getEKey(eKeyName) else: (privateKeyBlob, publicKeyBlob) = self._generateKeyPair() if self._database.hasEKey(eKeyName): self._deleteEKey(eKeyName) self._addEKey(eKeyName, publicKeyBlob, privateKeyBlob) # Add the first element to the result. # The E-KEY (public key) data packet name convention is: # /<data_type>/E-KEY/[start-ts]/[end-ts] data = self._createEKeyData(startTimeStamp, endTimeStamp, publicKeyBlob) result.append(data) # Encrypt the private key with the public key from each member's certificate. # Sort the key names. for keyName in sorted(unsortedMemberKeys.keys()): certificateKey = unsortedMemberKeys[keyName] # Generate the name of the packet. # The D-KEY (private key) data packet name convention is: # /<data_type>/D-KEY/[start-ts]/[end-ts]/[member-name] data = self._createDKeyData(startTimeStamp, endTimeStamp, keyName, privateKeyBlob, certificateKey) result.append(data) return result
def getGroupKey(self, timeSlot, needRegenerate = True): """ Create a group key for the interval into which timeSlot falls. This creates a group key if it doesn't exist, and encrypts the key using the public key of each eligible member. :param float timeSlot: The time slot to cover as milliseconds since Jan 1, 1970 UTC. :param bool needRegenerate: (optional) needRegenerate should be True if this is the first time this method is called, or a member was removed. needRegenerate can be False if this is not the first time this method is called, or a member was added. If omitted, use True. :return: A List of Data packets where the first is the E-KEY data packet with the group's public key and the rest are the D-KEY data packets with the group's private key encrypted with the public key of each eligible member. :raises GroupManagerDb.Error: For a database error. :raises SecurityException: For an error using the security KeyChain. """ unsortedMemberKeys = {} result = [] # Get the time interval. finalInterval = self._calculateInterval(timeSlot, unsortedMemberKeys) if finalInterval.isValid() == False: return result startTimeStamp = Schedule.toIsoString(finalInterval.getStartTime()) endTimeStamp = Schedule.toIsoString(finalInterval.getEndTime()) # Generate the private and public keys. eKeyName = Name(self._namespace) eKeyName.append(Encryptor.NAME_COMPONENT_E_KEY).append( startTimeStamp).append(endTimeStamp) if not needRegenerate and self._database.hasEKey(eKeyName): (publicKeyBlob, privateKeyBlob) = self._getEKey(eKeyName) else: (privateKeyBlob, publicKeyBlob) = self._generateKeyPair() if self._database.hasEKey(eKeyName): self._deleteEKey(eKeyName) self._addEKey(eKeyName, publicKeyBlob, privateKeyBlob) # Add the first element to the result. # The E-KEY (public key) data packet name convention is: # /<data_type>/E-KEY/[start-ts]/[end-ts] data = self._createEKeyData(startTimeStamp, endTimeStamp, publicKeyBlob) result.append(data) # Encrypt the private key with the public key from each member's certificate. # Sort the key names. for keyName in sorted(unsortedMemberKeys.keys()): certificateKey = unsortedMemberKeys[keyName] # Generate the name of the packet. # The D-KEY (private key) data packet name convention is: # /<data_type>/D-KEY/[start-ts]/[end-ts]/[member-name] data = self._createDKeyData( startTimeStamp, endTimeStamp, keyName, privateKeyBlob, certificateKey) result.append(data) return result
def createContentKey(self, timeSlot, onEncryptedKeys, onError=defaultOnError): """ Create the content key corresponding to the timeSlot. This first checks if the content key exists. For an existing content key, this returns the content key name directly. If the key does not exist, this creates one and encrypts it using the corresponding E-KEYs. The encrypted content keys are passed to the onEncryptedKeys callback. :param float timeSlot: The time slot as milliseconds since Jan 1, 1970 UTC. :param onEncryptedKeys: If this creates a content key, then this calls onEncryptedKeys(keys) where keys is a list of encrypted content key Data packets. If onEncryptedKeys is None, this does not use it. NOTE: The library will log any exceptions raised by this callback, but for better error handling the callback should catch and properly handle any exceptions. :type onEncryptedKeys: function object :param onError: (optional) This calls errorCode, message) for an error, where errorCode is from EncryptError.ErrorCode and message is a str. If omitted, use a default callback which does nothing. NOTE: The library will log any exceptions raised by this callback, but for better error handling the callback should catch and properly handle any exceptions. :type onError: function object :return: The content key name. :rtype: Name """ hourSlot = Producer._getRoundedTimeSlot(timeSlot) # Create the content key name. contentKeyName = Name(self._namespace) contentKeyName.append(Encryptor.NAME_COMPONENT_C_KEY) contentKeyName.append(Schedule.toIsoString(hourSlot)) # Check if we have created the content key before. if self._database.hasContentKey(timeSlot): # We have created the content key. Return its name directly. return contentKeyName # We haven't created the content key. Create one and add it into the # database. aesParams = AesKeyParams(128) contentKeyBits = AesAlgorithm.generateKey(aesParams).getKeyBits() self._database.addContentKey(timeSlot, contentKeyBits) # Now we need to retrieve the E-KEYs for content key encryption. timeCount = round(timeSlot) self._keyRequests[timeCount] = Producer._KeyRequest(len(self._eKeyInfo)) keyRequest = self._keyRequests[timeCount] # Check if the current E-KEYs can cover the content key. timeRange = Exclude() Producer.excludeAfter(timeRange, Name.Component(Schedule.toIsoString(timeSlot))) # Send interests for all nodes in the tree. for keyName in self._eKeyInfo: # For each current E-KEY. keyInfo = self._eKeyInfo[keyName] if timeSlot < keyInfo.beginTimeSlot or timeSlot >= keyInfo.endTimeSlot: # The current E-KEY cannot cover the content key, so retrieve one. keyRequest.repeatAttempts[keyName] = 0 self._sendKeyInterest( Interest(keyName).setExclude(timeRange).setChildSelector(1), timeSlot, onEncryptedKeys, onError ) else: # The current E-KEY can cover the content key. # Encrypt the content key directly. eKeyName = Name(keyName) eKeyName.append(Schedule.toIsoString(keyInfo.beginTimeSlot)) eKeyName.append(Schedule.toIsoString(keyInfo.endTimeSlot)) self._encryptContentKey(keyInfo.keyBits, eKeyName, timeSlot, onEncryptedKeys, onError) return contentKeyName