class CredentialStorage: def __init__(self): self.identityStorage = MemoryIdentityStorage() self.privateKeyStorage = MemoryPrivateKeyStorage() self.keyChain = KeyChain(IdentityManager(self.identityStorage, self.privateKeyStorage), SelfVerifyPolicyManager(self.identityStorage)) keyName = Name("/testname/DSK-123") self.defaultCertName = keyName[:-1].append( "KEY").append(keyName[-1]).append("ID-CERT").append("0") ecdsaKeyName = Name("/testEcdsa/DSK-123") self.ecdsaCertName = ecdsaKeyName[:-1].append( "KEY").append(ecdsaKeyName[-1]).append("ID-CERT").append("0") self.identityStorage.addKey( keyName, KeyType.RSA, Blob(DEFAULT_RSA_PUBLIC_KEY_DER)) self.privateKeyStorage.setKeyPairForKeyName( keyName, KeyType.RSA, DEFAULT_RSA_PUBLIC_KEY_DER, DEFAULT_RSA_PRIVATE_KEY_DER) self.identityStorage.addKey( ecdsaKeyName, KeyType.ECDSA, Blob(DEFAULT_EC_PUBLIC_KEY_DER)) self.privateKeyStorage.setKeyPairForKeyName( ecdsaKeyName, KeyType.ECDSA, DEFAULT_EC_PUBLIC_KEY_DER, DEFAULT_EC_PRIVATE_KEY_DER) def signData(self, data, certificateName = None): if certificateName is None: certificateName = self.defaultCertName self.keyChain.sign(data, certificateName) def signDataWithSha256(self, data): self.keyChain.signWithSha256(data) def verifyData(self, data, verifiedCallback, failedCallback): self.keyChain.verifyData(data, verifiedCallback, failedCallback)
def benchmarkDecodeDataSeconds(nIterations, useCrypto, encoding): """ Loop to decode a data packet nIterations times. :param int nIterations: The number of iterations. :param bool useCrypto: If true, verify the signature. If false, don't verify. :param Blob encoding: The wire encoding to decode. """ # Initialize the private key storage in case useCrypto is true. identityStorage = MemoryIdentityStorage() privateKeyStorage = MemoryPrivateKeyStorage() keyChain = KeyChain(IdentityManager(identityStorage, privateKeyStorage), SelfVerifyPolicyManager(identityStorage)) keyName = Name("/testname/DSK-123") certificateName = keyName.getSubName(0, keyName.size() - 1).append( "KEY").append(keyName[-1]).append("ID-CERT").append("0") identityStorage.addKey(keyName, KeyType.RSA, Blob(DEFAULT_RSA_PUBLIC_KEY_DER)) start = getNowSeconds() for i in range(nIterations): data = Data() data.wireDecode(encoding) if useCrypto: keyChain.verifyData(data, onVerified, onVerifyFailed) finish = getNowSeconds() return finish - start
def benchmarkDecodeDataSeconds(nIterations, useCrypto, encoding): """ Loop to decode a data packet nIterations times. :param int nIterations: The number of iterations. :param bool useCrypto: If true, verify the signature. If false, don't verify. :param Blob encoding: The wire encoding to decode. """ # Initialize the private key storage in case useCrypto is true. identityStorage = MemoryIdentityStorage() privateKeyStorage = MemoryPrivateKeyStorage() keyChain = KeyChain(IdentityManager(identityStorage, privateKeyStorage), SelfVerifyPolicyManager(identityStorage)) keyName = Name("/testname/DSK-123") certificateName = keyName.getSubName( 0, keyName.size() - 1).append("KEY").append( keyName[-1]).append("ID-CERT").append("0") identityStorage.addKey(keyName, KeyType.RSA, Blob(DEFAULT_RSA_PUBLIC_KEY_DER)) start = getNowSeconds() for i in range(nIterations): data = Data() data.wireDecode(encoding) if useCrypto: keyChain.verifyData(data, onVerified, onVerifyFailed) finish = getNowSeconds() return finish - start
class CredentialStorage: def __init__(self): self.identityStorage = MemoryIdentityStorage() self.privateKeyStorage = MemoryPrivateKeyStorage() self.keyChain = KeyChain( IdentityManager(self.identityStorage, self.privateKeyStorage), SelfVerifyPolicyManager(self.identityStorage)) keyName = Name("/testname/DSK-123") self.defaultCertName = keyName.getSubName( 0, keyName.size() - 1).append("KEY").append( keyName[-1]).append("ID-CERT").append("0") self.identityStorage.addKey(keyName, KeyType.RSA, Blob(DEFAULT_RSA_PUBLIC_KEY_DER)) self.privateKeyStorage.setKeyPairForKeyName( keyName, KeyType.RSA, DEFAULT_RSA_PUBLIC_KEY_DER, DEFAULT_RSA_PRIVATE_KEY_DER) def signData(self, data, certificateName=None): if certificateName is None: certificateName = self.defaultCertName self.keyChain.sign(data, certificateName) def verifyData(self, data, verifiedCallback, failedCallback): self.keyChain.verifyData(data, verifiedCallback, failedCallback)
def benchmarkDecodeDataSeconds(nIterations, useCrypto, keyType, encoding): """ Loop to decode a data packet nIterations times. :param int nIterations: The number of iterations. :param bool useCrypto: If true, verify the signature. If false, don't verify. :param KeyType keyType: KeyType.RSA or EC, used if useCrypto is True. :param Blob encoding: The wire encoding to decode. :return: The number of seconds for all iterations. :rtype: float """ # Initialize the private key storage in case useCrypto is true. identityStorage = MemoryIdentityStorage() privateKeyStorage = MemoryPrivateKeyStorage() keyChain = KeyChain(IdentityManager(identityStorage, privateKeyStorage), SelfVerifyPolicyManager(identityStorage)) keyName = Name("/testname/DSK-123") identityStorage.addKey( keyName, keyType, Blob( DEFAULT_EC_PUBLIC_KEY_DER if keyType == KeyType.ECDSA else DEFAULT_RSA_PUBLIC_KEY_DER)) start = getNowSeconds() for i in range(nIterations): data = Data() data.wireDecode(encoding) if useCrypto: keyChain.verifyData(data, onVerified, onValidationFailed) finish = getNowSeconds() return finish - start
def main(): data = Data() data.wireDecode(TlvData) dump("Decoded Data:") dumpData(data) # Set the content again to clear the cached encoding so we encode again. data.setContent(data.getContent()) encoding = data.wireEncode() reDecodedData = Data() reDecodedData.wireDecode(encoding) dump("") dump("Re-decoded Data:") dumpData(reDecodedData) identityStorage = MemoryIdentityStorage() privateKeyStorage = MemoryPrivateKeyStorage() keyChain = KeyChain(IdentityManager(identityStorage, privateKeyStorage), SelfVerifyPolicyManager(identityStorage)) # Initialize the storage. keyName = Name("/testname/DSK-123") certificateName = keyName.getSubName( 0, keyName.size() - 1).append("KEY").append( keyName[-1]).append("ID-CERT").append("0") identityStorage.addKey(keyName, KeyType.RSA, Blob(DEFAULT_RSA_PUBLIC_KEY_DER)) privateKeyStorage.setKeyPairForKeyName(keyName, KeyType.RSA, DEFAULT_RSA_PUBLIC_KEY_DER, DEFAULT_RSA_PRIVATE_KEY_DER) keyChain.verifyData(reDecodedData, makeOnVerified("Re-decoded Data"), makeOnVerifyFailed("Re-decoded Data")) freshData = Data(Name("/ndn/abc")) freshData.setContent("SUCCESS!") freshData.getMetaInfo().setFreshnessPeriod(5000) freshData.getMetaInfo().setFinalBlockId(Name("/%00%09")[0]) keyChain.sign(freshData, certificateName) dump("") dump("Freshly-signed Data:") dumpData(freshData) keyChain.verifyData(freshData, makeOnVerified("Freshly-signed Data"), makeOnVerifyFailed("Freshly-signed Data"))
def main(): data = Data() data.wireDecode(TlvData) dump("Decoded Data:") dumpData(data) # Set the content again to clear the cached encoding so we encode again. data.setContent(data.getContent()) encoding = data.wireEncode() reDecodedData = Data() reDecodedData.wireDecode(encoding) dump("") dump("Re-decoded Data:") dumpData(reDecodedData) identityStorage = MemoryIdentityStorage() privateKeyStorage = MemoryPrivateKeyStorage() keyChain = KeyChain(IdentityManager(identityStorage, privateKeyStorage), SelfVerifyPolicyManager(identityStorage)) # Initialize the storage. keyName = Name("/testname/DSK-123") certificateName = keyName.getSubName(0, keyName.size() - 1).append( "KEY").append(keyName[-1]).append("ID-CERT").append("0") identityStorage.addKey(keyName, KeyType.RSA, Blob(DEFAULT_RSA_PUBLIC_KEY_DER)) privateKeyStorage.setKeyPairForKeyName( keyName, KeyType.RSA, DEFAULT_RSA_PUBLIC_KEY_DER, DEFAULT_RSA_PRIVATE_KEY_DER) keyChain.verifyData(reDecodedData, makeOnVerified("Re-decoded Data"), makeOnVerifyFailed("Re-decoded Data")) freshData = Data(Name("/ndn/abc")) freshData.setContent("SUCCESS!") freshData.getMetaInfo().setFreshnessPeriod(5000) freshData.getMetaInfo().setFinalBlockId(Name("/%00%09")[0]) keyChain.sign(freshData, certificateName) dump("") dump("Freshly-signed Data:") dumpData(freshData) keyChain.verifyData(freshData, makeOnVerified("Freshly-signed Data"), makeOnVerifyFailed("Freshly-signed Data"))
def main(): data = Data() data.wireDecode(TlvData) dump("Decoded Data:") dumpData(data) # Set the content again to clear the cached encoding so we encode again. data.setContent(data.getContent()) encoding = data.wireEncode() reDecodedData = Data() reDecodedData.wireDecode(encoding) dump("") dump("Re-decoded Data:") dumpData(reDecodedData) # Set up the KeyChain. pibImpl = PibMemory() keyChain = KeyChain( pibImpl, TpmBackEndMemory(), SelfVerifyPolicyManager(pibImpl)) # This puts the public key in the pibImpl used by the SelfVerifyPolicyManager. keyChain.importSafeBag(SafeBag (Name("/testname/KEY/123"), Blob(DEFAULT_RSA_PRIVATE_KEY_DER, False), Blob(DEFAULT_RSA_PUBLIC_KEY_DER, False))) keyChain.verifyData(reDecodedData, makeOnVerified("Re-decoded Data"), makeOnValidationFailed("Re-decoded Data")) freshData = Data(Name("/ndn/abc")) freshData.setContent("SUCCESS!") freshData.getMetaInfo().setFreshnessPeriod(5000) freshData.getMetaInfo().setFinalBlockId(Name("/%00%09")[0]) keyChain.sign(freshData) dump("") dump("Freshly-signed Data:") dumpData(freshData) keyChain.verifyData(freshData, makeOnVerified("Freshly-signed Data"), makeOnValidationFailed("Freshly-signed Data"))
def benchmarkDecodeDataSeconds(nIterations, useCrypto, keyType, encoding): """ Loop to decode a data packet nIterations times. :param int nIterations: The number of iterations. :param bool useCrypto: If true, verify the signature. If false, don't verify. :param KeyType keyType: KeyType.RSA or EC, used if useCrypto is True. :param Blob encoding: The wire encoding to decode. :return: The number of seconds for all iterations. :rtype: float """ # Initialize the private key storage in case useCrypto is true. pibImpl = PibMemory() keyChain = KeyChain(pibImpl, TpmBackEndMemory(), SelfVerifyPolicyManager(pibImpl)) # This puts the public key in the pibImpl used by the SelfVerifyPolicyManager. keyChain.importSafeBag( SafeBag( Name("/testname/KEY/123"), Blob( DEFAULT_EC_PRIVATE_KEY_DER if keyType == KeyType.ECDSA else DEFAULT_RSA_PRIVATE_KEY_DER, False), Blob( DEFAULT_EC_PUBLIC_KEY_DER if keyType == KeyType.ECDSA else DEFAULT_RSA_PUBLIC_KEY_DER, False))) start = getNowSeconds() for i in range(nIterations): data = Data() data.wireDecode(encoding) if useCrypto: keyChain.verifyData(data, onVerified, onValidationFailed) finish = getNowSeconds() return finish - start
class BmsNode(object): def __init__(self): self.conf = None self._keyChain = None self._certificateName = None self._dataQueue = dict() self._memoryContentCache = None self._identityName = None self._aggregation = Aggregation() def setConfiguration(self, fileName, trustSchemaFile): self.conf = BoostInfoParser() self.conf.read(fileName) self._identityName = Name(self.conf.getNodePrefix()) self._trustSchemaFile = trustSchemaFile def onDataNotFound(self, prefix, interest, face, interestFilterId, filter): #print('Data not found for ' + interest.getName().toUri()) return def startPublishing(self): # One-time security setup self.prepareLogging() privateKeyStorage = FilePrivateKeyStorage() identityStorage = BasicIdentityStorage() policyManager = ConfigPolicyManager(self._trustSchemaFile) self._keyChain = KeyChain( IdentityManager(identityStorage, privateKeyStorage), policyManager) self._certificateName = self._keyChain.createIdentityAndCertificate( self._identityName) print("My Identity name: " + self._identityName.toUri()) print("My certificate name: " + self._certificateName.toUri()) certificateData = self._keyChain.getIdentityManager( )._identityStorage.getCertificate(self._certificateName) print("My certificate string: " + b64encode(certificateData.wireEncode().toBuffer())) # self._keyChain.getIdentityCertificate(self._certificateName).) self._loop = asyncio.get_event_loop() self._face = ThreadsafeFace(self._loop) self._keyChain.setFace(self._face) self._face.setCommandSigningInfo(self._keyChain, self._certificateName) self._memoryContentCache = MemoryContentCache(self._face) # We should only ask for cert to be signed upon the first run of a certain aggregator if DO_CERT_SETUP: if (KeyLocator.getFromSignature( certificateData.getSignature()).getKeyName().equals( self._certificateName.getPrefix(-1))): # Need to configure for mini-ndn; aggregation node runs outside of mini-ndn first so that signed cert get installed and mini-ndn won't ask for this again print("certificate " + self._certificateName.toUri() + " asking for signature") response = urllib2.urlopen( "http://192.168.56.1:5000/bms-cert-hack?cert=" + b64encode(certificateData.wireEncode().toBuffer()) + "&cert_prefix=" + self._identityName.toUri() + '&subject_name=' + self._identityName.toUri()).read() signedCertData = Data() signedCertData.wireDecode(Blob(b64decode(response))) self._memoryContentCache.add(signedCertData) cmdline = ['ndnsec-install-cert', '-'] p = subprocess.Popen(cmdline, stdin=subprocess.PIPE, stdout=subprocess.PIPE) # desanitize + sign in GET request cert, err = p.communicate(response) if p.returncode != 0: raise RuntimeError("ndnsec-install-cert error") else: self._memoryContentCache.add(certificateData) else: self._memoryContentCache.add(certificateData) dataNode = self.conf.getDataNode() childrenNode = self.conf.getChildrenNode() self._memoryContentCache.registerPrefix(Name(self._identityName), self.onRegisterFailed, self.onDataNotFound) # For each type of data, we refresh each type of aggregation according to the interval in the configuration for i in range(len(dataNode.subtrees)): dataType = dataNode.subtrees.keys()[i] aggregationParams = self.conf.getProducingParamsForAggregationType( dataNode.subtrees.items()[i][1]) if childrenNode == None: self._dataQueue[dataType] = DataQueue(None, None, None) self.generateData(dataType, 2, 0) for aggregationType in aggregationParams: childrenList = OrderedDict() if childrenNode != None: for j in range(len(childrenNode.subtrees)): if dataType in childrenNode.subtrees.items( )[j][1].subtrees['data'].subtrees: if aggregationType in childrenNode.subtrees.items( )[j][1].subtrees['data'].subtrees[ dataType].subtrees: childrenList[childrenNode.subtrees.items()[j][ 0]] = self.conf.getProducingParamsForAggregationType( childrenNode.subtrees.items()[j] [1].subtrees['data'].subtrees[dataType] )[aggregationType] self.startPublishingAggregation( aggregationParams[aggregationType], childrenList, dataType, aggregationType) return def startPublishingAggregation(self, params, childrenList, dataType, aggregationType): if __debug__: print('Start publishing for ' + dataType + '-' + aggregationType) # aggregation calculating and publishing mechanism publishingPrefix = Name( self._identityName).append(DATA_COMPONENT).append(dataType).append( AGGREGATION_COMPONENT).append(aggregationType) self._dataQueue[dataType + aggregationType] = DataQueue( params, childrenList, publishingPrefix) if len(childrenList.keys()) == 0: # TODO: make start_time optional for leaf nodes self._loop.call_later(int(params['producer_interval']), self.calculateAggregation, dataType, aggregationType, childrenList, int(params['start_time']), int(params['producer_interval']), publishingPrefix, True) else: # express interest for children who produce the same data and aggregation type for childName in childrenList.keys(): name = Name(self._identityName).append(childName).append( DATA_COMPONENT).append(dataType).append( AGGREGATION_COMPONENT).append(aggregationType) interest = Interest(name) # if start_time is specified, we ask for data starting at start_time; # if not, we ask for the right most child and go from there if ('start_time' in childrenList[childName]): endTime = int(childrenList[childName]['start_time']) + int( childrenList[childName]['producer_interval']) interest.getName().append( str(childrenList[childName]['start_time'])).append( str(endTime)) else: # TODO: For now we are playing with historical data, for each run we don't want to miss any data, thus we start with leftMost interest.setChildSelector(0) interest.setMustBeFresh(True) interest.setInterestLifetimeMilliseconds( DEFAULT_INTEREST_LIFETIME) if __debug__: print(' Issue interest: ' + interest.getName().toUri()) self._face.expressInterest(interest, self.onData, self.onTimeout) return # TODO: once one calculation's decided a child has not answered, we should do another calculation def calculateAggregation(self, dataType, aggregationType, childrenList, startTime, interval, publishingPrefix, repeat=False): doCalc = True dataList = [] # TODO: an intermediate node cannot produce raw data for now if len(childrenList.keys()) != 0: for childName in childrenList.keys(): dataDictKey = self.getDataDictKey(startTime, (startTime + interval), childName) if dataDictKey in self._dataQueue[dataType + aggregationType]._dataDict: data = self._dataQueue[ dataType + aggregationType]._dataDict[dataDictKey] dataList.append(float(data.getContent().toRawStr())) else: #print('Child ' + childName + ' has not replied yet') doCalc = False break else: for inst in self._dataQueue[dataType]._dataDict.keys(): if int(inst) >= startTime and int(inst) < startTime + interval: dataList.append(self._dataQueue[dataType]._dataDict[inst]) if doCalc: content = self._aggregation.getAggregation(aggregationType, dataList) if content: publishData = Data( Name(publishingPrefix).append(str(startTime)).append( str(startTime + interval))) publishData.setContent(str(content)) publishData.getMetaInfo().setFreshnessPeriod( DEFAULT_DATA_LIFETIME) self._keyChain.sign(publishData, self._certificateName) self._memoryContentCache.add(publishData) for childName in childrenList.keys(): dataDictKey = self.getDataDictKey(startTime, (startTime + interval), childName) if dataDictKey in self._dataQueue[ dataType + aggregationType]._dataDict: del self._dataQueue[ dataType + aggregationType]._dataDict[dataDictKey] if __debug__: print("Produced: " + publishData.getName().toUri() + "; " + publishData.getContent().toRawStr()) # repetition of this function only happens for raw data producer, otherwise calculateAggregation is called by each onData if repeat: self._loop.call_later(interval, self.calculateAggregation, dataType, aggregationType, childrenList, startTime + interval, interval, publishingPrefix, repeat) return def generateData(self, dataType, interval, startTime): self._dataQueue[dataType]._dataDict[str(startTime)] = random.randint( 0, 9) self._loop.call_later(interval, self.generateData, dataType, interval, startTime + interval) return def onRegisterFailed(self, prefix): raise RuntimeError("Register failed for prefix", prefix.toUri()) def onVerified(self, data): print('Data verified: ' + data.getName().toUri()) return def onVerifyFailed(self, data): print('Data verification failed: ' + data.getName().toUri()) return def onData(self, interest, data): self._keyChain.verifyData(data, self.onVerified, self.onVerifyFailed) dataName = data.getName() dataQueue = None if __debug__: print("Got data: " + dataName.toUri() + "; " + data.getContent().toRawStr()) for i in range(0, len(dataName)): if dataName.get(i).toEscapedString() == AGGREGATION_COMPONENT: dataType = dataName.get(i - 1).toEscapedString() aggregationType = dataName.get(i + 1).toEscapedString() startTime = int(dataName.get(i + 2).toEscapedString()) endTime = int(dataName.get(i + 3).toEscapedString()) childName = dataName.get(i - 3).toEscapedString() dataAndAggregationType = dataType + aggregationType dataDictKey = self.getDataDictKey(startTime, endTime, childName) dataQueue = self._dataQueue[dataAndAggregationType] dataQueue._dataDict[dataDictKey] = data break # TODO: check what if interval/starttime is misconfigured if dataQueue: self.calculateAggregation(dataType, aggregationType, dataQueue._childrenList, startTime, endTime - startTime, dataQueue._publishingPrefix) # Always ask for the next piece of data when we receive this one; assumes interval does not change; this also assumes there are no more components after endTime #newInterestName = dataName.getPrefix(i + 2).append(str(endTime)).append(str(endTime + (endTime - startTime))) # We don't expect aggregated data name to be continuous within our given time window, so we ask with exclusion instead newInterestName = dataName.getPrefix(i + 2) newInterest = Interest(interest) newInterest.setName(newInterestName) newInterest.setChildSelector(0) exclude = Exclude() exclude.appendAny() exclude.appendComponent(dataName.get(i + 2)) newInterest.setExclude(exclude) self._face.expressInterest(newInterest, self.onData, self.onTimeout) if __debug__: print(" issue interest: " + interest.getName().toUri()) return def onTimeout(self, interest): if __debug__: print(" interest timeout: " + interest.getName().toUri() + "; reexpress") pass self._face.expressInterest(interest, self.onData, self.onTimeout) return def stop(self): self._loop.stop() if __debug__: print("Stopped") return # This creation of dataDictKey means parent and child should not have the same name @staticmethod def getDataDictKey(startTime, endTime, childName): return str(startTime) + '/' + str(endTime) + '/' + childName ## # Logging ## def prepareLogging(self): self.log = logging.getLogger(str(self.__class__)) self.log.setLevel(logging.DEBUG) logFormat = "%(asctime)-15s %(name)-20s %(funcName)-20s (%(levelname)-8s):\n\t%(message)s" self._console = logging.StreamHandler() self._console.setFormatter(logging.Formatter(logFormat)) self._console.setLevel(logging.INFO) # without this, a lot of ThreadsafeFace errors get swallowed up logging.getLogger("trollius").addHandler(self._console) self.log.addHandler(self._console) def setLogLevel(self, level): """ Set the log level that will be output to standard error :param level: A log level constant defined in the logging module (e.g. logging.INFO) """ self._console.setLevel(level) def getLogger(self): """ :return: The logger associated with this node :rtype: logging.Logger """ return self.log
class TestSqlIdentityStorage(ut.TestCase): def setUp(self): # Reuse the policy_config subdirectory for the temporary SQLite file. self.databaseFilePath = "policy_config/test-public-info.db" try: os.remove(self.databaseFilePath) except OSError: # no such file pass self.identityStorage = BasicIdentityStorage(self.databaseFilePath) self.identityManager = IdentityManager(self.identityStorage, FilePrivateKeyStorage()) self.policyManager = SelfVerifyPolicyManager(self.identityStorage) self.keyChain = KeyChain(self.identityManager, self.policyManager) def tearDown(self): try: os.remove(self.databaseFilePath) except OSError: pass def test_identity_create_delete(self): identityName = Name('/TestIdentityStorage/Identity').appendVersion( int(time.time())) certificateName = self.keyChain.createIdentityAndCertificate( identityName) keyName = IdentityCertificate.certificateNameToPublicKeyName( certificateName) self.assertTrue(self.identityStorage.doesIdentityExist(identityName), "Identity was not added to IdentityStorage") self.assertIsNotNone(keyName, "New identity has no key") self.assertTrue(self.identityStorage.doesKeyExist(keyName), "Key was not added to IdentityStorage") self.assertIsNotNone(certificateName, "Certificate was not added to IdentityStorage") self.keyChain.deleteIdentity(identityName) self.assertFalse( self.identityStorage.doesIdentityExist(identityName), "Identity still in IdentityStorage after identity was deleted") self.assertFalse( self.identityStorage.doesKeyExist(keyName), "Key still in IdentityStorage after identity was deleted") self.assertFalse( self.identityStorage.doesCertificateExist(certificateName), "Certificate still in IdentityStorage after identity was deleted") with self.assertRaises(SecurityException): self.identityManager.getDefaultCertificateNameForIdentity( identityName) def test_key_create_delete(self): identityName = Name('/TestIdentityStorage/Identity').appendVersion( int(time.time())) keyName1 = self.keyChain.generateRSAKeyPair(identityName, True) self.keyChain.getIdentityManager().setDefaultKeyForIdentity(keyName1) keyName2 = self.keyChain.generateRSAKeyPair(identityName, False) self.assertEqual( self.identityManager.getDefaultKeyNameForIdentity(identityName), keyName1, "Default key name was changed without explicit request") self.assertNotEqual( self.identityManager.getDefaultKeyNameForIdentity(identityName), keyName2, "Newly created key replaced default key without explicit request") self.identityStorage.deletePublicKeyInfo(keyName2) self.assertFalse(self.identityStorage.doesKeyExist(keyName2)) self.keyChain.deleteIdentity(identityName) def test_key_autocreate_identity(self): keyName1 = Name('/TestSqlIdentityStorage/KeyType/RSA/ksk-12345') identityName = keyName1[:-1] decodedKey = base64.b64decode(RSA_DER) self.identityStorage.addKey(keyName1, KeyType.RSA, Blob(decodedKey)) self.identityStorage.setDefaultKeyNameForIdentity(keyName1) self.assertTrue(self.identityStorage.doesKeyExist(keyName1), "Key was not added") self.assertTrue(self.identityStorage.doesIdentityExist(identityName), "Identity for key was not automatically created") self.assertEqual( self.identityManager.getDefaultKeyNameForIdentity(identityName), keyName1, "Default key was not set on identity creation") with self.assertRaises(SecurityException): self.identityStorage.getDefaultCertificateNameForKey(keyName1) with self.assertRaises(SecurityException): # we have no private key for signing self.identityManager.selfSign(keyName1) with self.assertRaises(SecurityException): self.identityStorage.getDefaultCertificateNameForKey(keyName1) with self.assertRaises(SecurityException): self.identityManager.getDefaultCertificateNameForIdentity( identityName) keyName2 = self.identityManager.generateRSAKeyPairAsDefault( identityName) cert = self.identityManager.selfSign(keyName2) self.identityManager.addCertificateAsIdentityDefault(cert) certName1 = self.identityManager.getDefaultCertificateNameForIdentity( identityName) certName2 = self.identityStorage.getDefaultCertificateNameForKey( keyName2) self.assertEqual( certName1, certName2, "Key-certificate mapping and identity-certificate mapping are not consistent" ) self.keyChain.deleteIdentity(identityName) self.assertFalse(self.identityStorage.doesKeyExist(keyName1)) def test_certificate_add_delete(self): identityName = Name('/TestIdentityStorage/Identity').appendVersion( int(time.time())) self.identityManager.createIdentityAndCertificate( identityName, KeyChain.getDefaultKeyParams()) keyName1 = self.identityManager.getDefaultKeyNameForIdentity( identityName) cert2 = self.identityManager.selfSign(keyName1) self.identityStorage.addCertificate(cert2) certName2 = cert2.getName() certName1 = self.identityManager.getDefaultCertificateNameForIdentity( identityName) self.assertNotEqual( certName1, certName2, "New certificate was set as default without explicit request") self.identityStorage.deleteCertificateInfo(certName1) self.assertTrue(self.identityStorage.doesCertificateExist(certName2)) self.assertFalse(self.identityStorage.doesCertificateExist(certName1)) self.keyChain.deleteIdentity(identityName) self.assertFalse(self.identityStorage.doesCertificateExist(certName2)) def test_stress(self): # ndn-cxx/tests/unit-tests/security/test-sec-public-info-sqlite3.cpp identityName = Name("/TestSecPublicInfoSqlite3/Delete").appendVersion( int(time.time())) # ndn-cxx returns the cert name, but the IndentityManager docstring # specifies a key certName1 = self.keyChain.createIdentityAndCertificate(identityName) keyName1 = IdentityCertificate.certificateNameToPublicKeyName( certName1) keyName2 = self.keyChain.generateRSAKeyPairAsDefault(identityName) cert2 = self.identityManager.selfSign(keyName2) certName2 = cert2.getName() self.identityManager.addCertificateAsDefault(cert2) keyName3 = self.keyChain.generateRSAKeyPairAsDefault(identityName) cert3 = self.identityManager.selfSign(keyName3) certName3 = cert3.getName() self.identityManager.addCertificateAsDefault(cert3) cert4 = self.identityManager.selfSign(keyName3) self.identityManager.addCertificateAsDefault(cert4) certName4 = cert4.getName() cert5 = self.identityManager.selfSign(keyName3) self.identityManager.addCertificateAsDefault(cert5) certName5 = cert5.getName() self.assertTrue(self.identityStorage.doesIdentityExist(identityName)) self.assertTrue(self.identityStorage.doesKeyExist(keyName1)) self.assertTrue(self.identityStorage.doesKeyExist(keyName2)) self.assertTrue(self.identityStorage.doesKeyExist(keyName3)) self.assertTrue(self.identityStorage.doesCertificateExist(certName1)) self.assertTrue(self.identityStorage.doesCertificateExist(certName2)) self.assertTrue(self.identityStorage.doesCertificateExist(certName3)) self.assertTrue(self.identityStorage.doesCertificateExist(certName4)) self.assertTrue(self.identityStorage.doesCertificateExist(certName5)) self.identityStorage.deleteCertificateInfo(certName5) self.assertFalse(self.identityStorage.doesCertificateExist(certName5)) self.assertTrue(self.identityStorage.doesCertificateExist(certName4)) self.assertTrue(self.identityStorage.doesCertificateExist(certName3)) self.assertTrue(self.identityStorage.doesKeyExist(keyName2)) self.identityStorage.deletePublicKeyInfo(keyName3) self.assertFalse(self.identityStorage.doesCertificateExist(certName4)) self.assertFalse(self.identityStorage.doesCertificateExist(certName3)) self.assertFalse(self.identityStorage.doesKeyExist(keyName3)) self.assertTrue(self.identityStorage.doesKeyExist(keyName2)) self.assertTrue(self.identityStorage.doesKeyExist(keyName1)) self.assertTrue(self.identityStorage.doesIdentityExist(identityName)) self.keyChain.deleteIdentity(identityName) self.assertFalse(self.identityStorage.doesCertificateExist(certName2)) self.assertFalse(self.identityStorage.doesKeyExist(keyName2)) self.assertFalse(self.identityStorage.doesCertificateExist(certName1)) self.assertFalse(self.identityStorage.doesKeyExist(keyName1)) self.assertFalse(self.identityStorage.doesIdentityExist(identityName)) def test_ecdsa_identity(self): identityName = Name("/TestSqlIdentityStorage/KeyType/ECDSA") keyName = self.identityManager.generateEcdsaKeyPairAsDefault( identityName) cert = self.identityManager.selfSign(keyName) self.identityManager.addCertificateAsIdentityDefault(cert) # Check the self-signature. failedCallback = Mock() verifiedCallback = Mock() self.keyChain.verifyData(cert, verifiedCallback, failedCallback) self.assertEqual(verifiedCallback.call_count, 1, "Verification callback was not used.") self.keyChain.deleteIdentity(identityName) self.assertFalse(self.identityStorage.doesKeyExist(keyName))
class Bootstrap(object): """ Create a Bootstrap object. Bootstrap object provides interface for setting up KeyChain, default certificate name; (as a producer) requesting publishing authorization from controller; and (as a consumer) keeping track of changes :param face: the face for communicating with a local / remote forwarder :type face: ThreadsafeFace TODO: support Face as well as ThreadsafeFace """ def __init__(self, face): self._defaultIdentity = None self._defaultCertificateName = None self._controllerName = None self._controllerCertificate = None self._applicationName = "" self._identityManager = IdentityManager(BasicIdentityStorage(), FilePrivateKeyStorage()) self._policyManager = ConfigPolicyManager() self._policyManager.config.read("validator \n \ { \n \ rule \n \ { \n \ id \"initial rule\" \n \ for data \n \ checker \n \ { \n \ type hierarchical \n \ } \n \ } \n \ }", "initial-schema") # keyChain is what we return to the application after successful setup # TODO: should we separate keyChain from internal KeyChain used to verify trust schemas? self._keyChain = KeyChain(self._identityManager, self._policyManager) self._face = face # setFace for keyChain or else it won't be able to express interests for certs self._keyChain.setFace(self._face) self._certificateContentCache = MemoryContentCache(face) self._trustSchemas = dict() ############################################### # Initial keyChain and defaultCertificate setup ############################################### def setupDefaultIdentityAndRoot(self, defaultIdentityOrFileName, signerName = None, onSetupComplete = None, onSetupFailed = None): """ Sets up the keyChain, default key name and certificate name according to given configuration. If successful, this KeyChain and default certificate name will be returned to the application, which can be passed to instances like Consumer, Discovery, etc :param defaultIdentityOrFileName: if str, the name of the configuration file; if Name, the default identity name of this IoT node. The node will use the default keys and certificate of that identity name. :type defaultIdentityOrFileName: Name or str :param signerName: (optional) the expected signing identity of the certificate :type signerName: Name :param onSetupComplete: (optional) onSetupComplete(Name, KeyChain) will be called if set up's successful :type onSetupComplete: function object :param onSetupFailed: (optional) onSetupFailed(msg) will be called if setup fails :type onSetupFailed: function object """ def helper(identityName, signerName): try: self._defaultIdentity = identityName self._defaultCertificateName = self._identityManager.getDefaultCertificateNameForIdentity(self._defaultIdentity) self._defaultKeyName = self._identityManager.getDefaultKeyNameForIdentity(identityName) except SecurityException: msg = "Identity " + identityName.toUri() + " in configuration does not exist. Please configure the device with this identity first." if onSetupFailed: onSetupFailed(msg) return if not self._defaultCertificateName: msg = "Unable to get default certificate name for identity " + identityName.toUri() + ". Please configure the device with this identity first." if onSetupFailed: onSetupFailed(msg) return if not self._defaultKeyName: msg = "Unable to get default key name for identity " + identityName.toUri() + ". Please configure the device with this identity first." if onSetupFailed: onSetupFailed(msg) return # Note we'll not be able to issue face commands before this point self._face.setCommandSigningInfo(self._keyChain, self._defaultCertificateName) # Serve our own certificate self._certificateContentCache.registerPrefix(Name(self._defaultCertificateName).getPrefix(-1), self.onRegisterFailed) self._certificateContentCache.add(self._keyChain.getCertificate(self._defaultCertificateName)) actualSignerName = self._keyChain.getCertificate(self._defaultCertificateName).getSignature().getKeyLocator().getKeyName() if not signerName: print "Deriving from " + actualSignerName.toUri() + " for controller name" else: if signerName and actualSignerName.toUri() != signerName.toUri(): msg = "Configuration signer names mismatch: expected " + signerName.toUri() + "; got " + actualSignerName.toUri() print msg if onSetupFailed: onSetupFailed(msg) self._controllerName = self.getIdentityNameFromCertName(actualSignerName) print "Controller name: " + self._controllerName.toUri() try: self._controllerCertificate = self._keyChain.getCertificate(self._identityManager.getDefaultCertificateNameForIdentity(self._controllerName)) # TODO: this does not seem a good approach, implementation-wise and security implication self._policyManager._certificateCache.insertCertificate(self._controllerCertificate) if onSetupComplete: onSetupComplete(Name(self._defaultCertificateName), self._keyChain) except SecurityException as e: print "don't have controller certificate " + actualSignerName.toUri() + " yet" controllerCertInterest = Interest(Name(actualSignerName)) controllerCertInterest.setInterestLifetimeMilliseconds(4000) controllerCertRetries = 3 self._face.expressInterest(controllerCertInterest, lambda interest, data: self.onControllerCertData(interest, data, onSetupComplete, onSetupFailed), lambda interest: self.onControllerCertTimeout(interest, onSetupComplete, onSetupFailed, controllerCertRetries)) return if isinstance(defaultIdentityOrFileName, basestring): confObj = self.processConfiguration(defaultIdentityOrFileName) if "identity" in confObj: if confObj["identity"] == "default": # TODO: handling the case where no default identity is present defaultIdentity = self._keyChain.getDefaultIdentity() else: defaultIdentity = Name(confObj["identity"]) else: defaultIdentity = self._keyChain.getDefaultIdentity() # TODO: handling signature with direct bits instead of keylocator keyname if "signer" in confObj: if confObj["signer"] == "default": signerName = None else: signerName = Name(confObj["signer"]) else: signerName = None print "Deriving from " + signerName.toUri() + " for controller name" helper(defaultIdentity, signerName) else: if isinstance(defaultIdentityOrFileName, Name): helper(defaultIdentityOrFileName, signerName) else: raise RuntimeError("Please call setupDefaultIdentityAndRoot with identity name and root key name") return def onControllerCertData(self, interest, data, onSetupComplete, onSetupFailed): # TODO: verification rule for received self-signed cert. # So, if a controller comes masquerading in at this point with the right name, it is problematic. Similar with ndn-pi's implementation self._controllerCertificate = IdentityCertificate(data) # insert root certificate so that we could verify initial trust schemas # TODO: this does not seem a good approach, implementation-wise and security implication self._keyChain.getPolicyManager()._certificateCache.insertCertificate(self._controllerCertificate) try: self._identityManager.addCertificate(self._controllerCertificate) except SecurityException as e: print str(e) for schema in self._trustSchemas: # TODO: remove the concept of pending-schema if "pending-schema" in self._trustSchemas[schema]: self._keyChain.verifyData(self._trustSchemas[schema]["pending-schema"], self.onSchemaVerified, self.onSchemaVerificationFailed) if onSetupComplete: onSetupComplete(Name(self._defaultCertificateName), self._keyChain) return def onControllerCertTimeout(self, interest, onSetupComplete, onSetupFailed, controllerCertRetries): print "Controller certificate interest times out" newInterest = Interest(interest) newInterest.refreshNonce() if controllerCertRetries == 0: if onSetupFailed: onSetupFailed("Controller certificate interest times out") else: print "Set up failed: controller certificate interest times out" else: self._face.expressInterest(newInterest, lambda interest, data: self.onControllerCertData(interest, data, onSetupComplete, onSetupFailed), lambda interest: self.onControllerCertTimeout(interest, onSetupComplete, onSetupFailed, controllerCertRetries - 1)) return ######################################################### # Handling application consumption (trust schema updates) ######################################################### # TODO: if trust schema gets over packet size limit, segmentation def startTrustSchemaUpdate(self, appPrefix, onUpdateSuccess = None, onUpdateFailed = None): """ Starts trust schema update for under an application prefix: initial interest asks for the rightMostChild, and later interests are sent with previous version excluded. Each verified trust schema will trigger onUpdateSuccess and update the ConfigPolicyManager for the keyChain in this instance, and unverified ones will trigger onUpdateFailed. The keyChain and trust anchor should be set up using setupDefaultIdentityAndRoot before calling this method. :param appPrefix: the prefix to ask trust schema for. (interest name: /<prefix>/_schema) :type appPrefix: Name :param onUpdateSuccess: (optional) onUpdateSuccess(trustSchemaStr, isInitial) is called when update succeeds :type onUpdateSuccess: function object :param onUpdateFailed: (optional) onUpdateFailed(msg) is called when update fails :type onUpdateFailed: function object """ namespace = appPrefix.toUri() if namespace in self._trustSchemas: if self._trustSchemas[namespace]["following"] == True: print "Already following trust schema under this namespace!" return self._trustSchemas[namespace]["following"] = True else: self._trustSchemas[namespace] = {"following": True, "version": 0, "is-initial": True} initialInterest = Interest(Name(namespace).append("_schema")) initialInterest.setChildSelector(1) self._face.expressInterest(initialInterest, lambda interest, data: self.onTrustSchemaData(interest, data, onUpdateSuccess, onUpdateFailed), lambda interest: self.onTrustSchemaTimeout(interest, onUpdateSuccess, onUpdateFailed)) return def stopTrustSchemaUpdate(self): print "stopTrustSchemaUpdate not implemented" return def onSchemaVerified(self, data, onUpdateSuccess, onUpdateFailed): print "trust schema verified: " + data.getName().toUri() version = data.getName().get(-1) namespace = data.getName().getPrefix(-2).toUri() if not (namespace in self._trustSchemas): print "unexpected: received trust schema for application namespace that's not being followed; malformed data name?" return if version.toVersion() <= self._trustSchemas[namespace]["version"]: msg = "Got out-of-date trust schema" print msg if onUpdateFailed: onUpdateFailed(msg) return self._trustSchemas[namespace]["version"] = version.toVersion() if "pending-schema" in self._trustSchemas[namespace] and self._trustSchemas[namespace]["pending-schema"].getName().toUri() == data.getName().toUri(): # we verified a pending trust schema, don't need to keep that any more del self._trustSchemas[namespace]["pending-schema"] self._trustSchemas[namespace]["trust-schema"] = data.getContent().toRawStr() print self._trustSchemas[namespace]["trust-schema"] # TODO: what about trust schema for discovery, is discovery its own application? newInterest = Interest(Name(data.getName()).getPrefix(-1)) newInterest.setChildSelector(1) exclude = Exclude() exclude.appendAny() exclude.appendComponent(version) newInterest.setExclude(exclude) self._face.expressInterest(newInterest, lambda interest, data: self.onTrustSchemaData(interest, data, onUpdateSuccess, onUpdateFailed), lambda interest: self.onTrustSchemaTimeout(interest, onUpdateSuccess, onUpdateFailed)) # Note: this changes the verification rules for root cert, future trust schemas as well; ideally from the outside this doesn't have an impact, but do we want to avoid this? # Per reset function in ConfigPolicyManager; For now we don't call reset as we still want root cert in our certCache, instead of asking for it again (when we want to verify) each time we update the trust schema self._policyManager.config = BoostInfoParser() self._policyManager.config.read(self._trustSchemas[namespace]["trust-schema"], "updated-schema") if onUpdateSuccess: onUpdateSuccess(data.getContent().toRawStr(), self._trustSchemas[namespace]["is-initial"]) self._trustSchemas[namespace]["is-initial"] = False return def onSchemaVerificationFailed(self, data, reason, onUpdateSuccess, onUpdateFailed): print "trust schema verification failed: " + reason namespace = data.getName().getPrefix(-2).toUri() if not (namespace in self._trustSchemas): print "unexpected: received trust schema for application namespace that's not being followed; malformed data name?" return newInterest = Interest(Name(data.getName()).getPrefix(-1)) newInterest.setChildSelector(1) exclude = Exclude() exclude.appendAny() exclude.appendComponent(Name.Component.fromVersion(self._trustSchemas[namespace]["version"])) newInterest.setExclude(exclude) # Don't immediately ask for potentially the same content again if verification fails self._face.callLater(4000, lambda : self._face.expressInterest(newInterest, lambda interest, data: self.onTrustSchemaData(interest, data, onUpdateSuccess, onUpdateFailed), lambda interest: self.onTrustSchemaTimeout(interest, onUpdateSuccess, onUpdateFailed))) return def onTrustSchemaData(self, interest, data, onUpdateSuccess, onUpdateFailed): print("Trust schema received: " + data.getName().toUri()) namespace = data.getName().getPrefix(-2).toUri() # Process newly received trust schema if not self._controllerCertificate: # we don't yet have the root certificate fetched, so we store this cert for now print "Controller certificate not yet present, verify once it's in place" self._trustSchemas[namespace]["pending-schema"] = data else: # we veriy the received trust schema, should we use an internal KeyChain instead? self._keyChain.verifyData(data, lambda data: self.onSchemaVerified(data, onUpdateSuccess, onUpdateFailed), lambda data, reason: self.onSchemaVerificationFailed(data, reason, onUpdateSuccess, onUpdateFailed)) return def onTrustSchemaTimeout(self, interest, onUpdateSuccess, onUpdateFailed): print("Trust schema interest times out: " + interest.getName().toUri()) newInterest = Interest(interest) newInterest.refreshNonce() self._face.expressInterest(newInterest, lambda interest, data: self.onTrustSchemaData(interest, data, onUpdateSuccess, onUpdateFailed), lambda interest: self.onTrustSchemaTimeout(interest, onUpdateSuccess, onUpdateFailed)) return ############################################### # Handling application producing authorizations ############################################### # Wrapper for sendAppRequest, fills in already configured defaultCertificateName def requestProducerAuthorization(self, dataPrefix, appName, onRequestSuccess = None, onRequestFailed = None): """ Requests producing authorization for a data prefix: commandInterest is sent out to the controller, using /<controller identity>/requests/<encoded-application-parameters>/<signed-interest-suffix> where encoded-application-parameters is a ProtobufTlv encoding of {appPrefix, certificateName, appName} The keyChain, trust anchor and controller name should be set up using setupDefaultIdentityAndRoot before calling this method. :param dataPrefix: the prefix to request publishing for :type dataPrefix: Name :param appName: the application name to request publishing for :type appName: str :param onRequestSuccess: (optional) onRequestSuccess() is called when a valid response if received for the request :type onRequestSuccess: function object :param onRequestFailed: (optional) onRequestFailed(msg) is called when request fails :type onRequestFailed: function object """ # TODO: update logic on this part, should the presence of default certificate name be mandatory? # And allow application developer to send app request to a configured root/controller? if not self._defaultCertificateName: raise RuntimeError("Default certificate is missing! Try setupDefaultIdentityAndRoot first?") return self.sendAppRequest(self._defaultCertificateName, dataPrefix, appName, onRequestSuccess, onRequestFailed) def sendAppRequest(self, certificateName, dataPrefix, applicationName, onRequestSuccess, onRequestFailed): message = AppRequestMessage() for component in range(certificateName.size()): message.command.idName.components.append(certificateName.get(component).toEscapedString()) for component in range(dataPrefix.size()): message.command.dataPrefix.components.append(dataPrefix.get(component).toEscapedString()) message.command.appName = applicationName paramComponent = ProtobufTlv.encode(message) requestInterest = Interest(Name(self._controllerName).append("requests").append(paramComponent)) requestInterest.setInterestLifetimeMilliseconds(4000) self._face.makeCommandInterest(requestInterest) appRequestTimeoutCnt = 3 self._face.expressInterest(requestInterest, lambda interest, data : self.onAppRequestData(interest, data, onRequestSuccess, onRequestFailed), lambda interest : self.onAppRequestTimeout(interest, onRequestSuccess, onRequestFailed, appRequestTimeoutCnt)) print "Application publish request sent: " + requestInterest.getName().toUri() return def onAppRequestData(self, interest, data, onRequestSuccess, onRequestFailed): print "Got application publishing request data" def onVerified(data): responseObj = json.loads(data.getContent().toRawStr()) if responseObj["status"] == "200": if onRequestSuccess: onRequestSuccess() else: print "onSetupComplete" else: print "Verified content: " + data.getContent().toRawStr() if onRequestFailed: onRequestFailed(data.getContent().toRawStr()) def onVerifyFailed(data, reason): msg = "Application request response verification failed: " + reason print msg if onRequestFailed: onRequestFailed(msg) self._keyChain.verifyData(data, onVerified, onVerifyFailed) return def onAppRequestTimeout(self, interest, onSetupComplete, onSetupFailed, appRequestTimeoutCnt): print "Application publishing request times out" newInterest = Interest(interest) newInterest.refreshNonce() if appRequestTimeoutCnt == 0: if onSetupFailed: onSetupFailed("Application publishing request times out") else: print "Setup failed: application publishing request times out" else: self._face.expressInterest(newInterest, lambda interest, data : self.onAppRequestData(interest, data, onSetupComplete, onSetupFailed), lambda interest : self.onAppRequestTimeout(interest, onSetupComplete, onSetupFailed, appRequestTimeoutCnt - 1)) return ############################################### # Helper functions ############################################### def onRegisterFailed(self, prefix): print("register failed for prefix " + prefix.getName().toUri()) return def processConfiguration(self, confFile): config = BoostInfoParser() config.read(confFile) # TODO: handle missing configuration, refactor dict representation confObj = dict() try: confObj["identity"] = config["application/identity"][0].value confObj["signer"] = config["application/signer"][0].value except KeyError as e: msg = "Missing key in configuration: " + str(e) print msg return None return confObj def getIdentityNameFromCertName(self, certName): i = certName.size() - 1 idString = "KEY" while i >= 0: if certName.get(i).toEscapedString() == idString: break i -= 1 if i < 0: print "Error: unexpected certName " + certName.toUri() return None return Name(certName.getPrefix(i)) ################################# # Getters and setters ################################# def getKeyChain(self): return self._keyChain
class TestSqlIdentityStorage(ut.TestCase): def setUp(self): # Reuse the policy_config subdirectory for the temporary SQLite file. self.databaseFilePath = "policy_config/test-public-info.db" try: os.remove(self.databaseFilePath) except OSError: # no such file pass self.identityStorage = BasicIdentityStorage(self.databaseFilePath) self.identityManager = IdentityManager(self.identityStorage, FilePrivateKeyStorage()) self.policyManager = SelfVerifyPolicyManager(self.identityStorage) self.keyChain = KeyChain(self.identityManager, self.policyManager) def tearDown(self): try: os.remove(self.databaseFilePath) except OSError: pass def test_identity_create_delete(self): identityName = Name('/TestIdentityStorage/Identity').appendVersion( int(time.time())) certificateName = self.keyChain.createIdentityAndCertificate(identityName) keyName = IdentityCertificate.certificateNameToPublicKeyName(certificateName) self.assertTrue(self.identityStorage.doesIdentityExist(identityName), "Identity was not added to IdentityStorage") self.assertIsNotNone(keyName, "New identity has no key") self.assertTrue(self.identityStorage.doesKeyExist(keyName), "Key was not added to IdentityStorage") self.assertIsNotNone(certificateName, "Certificate was not added to IdentityStorage") self.keyChain.deleteIdentity(identityName) self.assertFalse(self.identityStorage.doesIdentityExist(identityName), "Identity still in IdentityStorage after identity was deleted") self.assertFalse(self.identityStorage.doesKeyExist(keyName), "Key still in IdentityStorage after identity was deleted") self.assertFalse(self.identityStorage.doesCertificateExist(certificateName), "Certificate still in IdentityStorage after identity was deleted") with self.assertRaises(SecurityException): self.identityManager.getDefaultCertificateNameForIdentity(identityName) def test_key_create_delete(self): identityName = Name('/TestIdentityStorage/Identity').appendVersion( int(time.time())) keyName1 = self.keyChain.generateRSAKeyPair(identityName, True) self.keyChain.getIdentityManager().setDefaultKeyForIdentity(keyName1) keyName2 = self.keyChain.generateRSAKeyPair(identityName, False) self.assertEqual(self.identityManager.getDefaultKeyNameForIdentity(identityName), keyName1, "Default key name was changed without explicit request") self.assertNotEqual(self.identityManager.getDefaultKeyNameForIdentity(identityName), keyName2, "Newly created key replaced default key without explicit request") self.identityStorage.deletePublicKeyInfo(keyName2) self.assertFalse(self.identityStorage.doesKeyExist(keyName2)) self.keyChain.deleteIdentity(identityName) def test_key_autocreate_identity(self): keyName1 = Name('/TestSqlIdentityStorage/KeyType/RSA/ksk-12345') identityName = keyName1[:-1] decodedKey = base64.b64decode(RSA_DER) self.identityStorage.addKey(keyName1, KeyType.RSA, Blob(decodedKey)) self.identityStorage.setDefaultKeyNameForIdentity(keyName1) self.assertTrue(self.identityStorage.doesKeyExist(keyName1), "Key was not added") self.assertTrue(self.identityStorage.doesIdentityExist(identityName), "Identity for key was not automatically created") self.assertEqual(self.identityManager.getDefaultKeyNameForIdentity(identityName), keyName1, "Default key was not set on identity creation") with self.assertRaises(SecurityException): self.identityStorage.getDefaultCertificateNameForKey(keyName1) with self.assertRaises(SecurityException): # we have no private key for signing self.identityManager.selfSign(keyName1) with self.assertRaises(SecurityException): self.identityStorage.getDefaultCertificateNameForKey(keyName1) with self.assertRaises(SecurityException): self.identityManager.getDefaultCertificateNameForIdentity(identityName) keyName2 = self.identityManager.generateRSAKeyPairAsDefault(identityName) cert = self.identityManager.selfSign(keyName2) self.identityManager.addCertificateAsIdentityDefault(cert) certName1 = self.identityManager.getDefaultCertificateNameForIdentity(identityName) certName2 = self.identityStorage.getDefaultCertificateNameForKey(keyName2) self.assertEqual(certName1, certName2, "Key-certificate mapping and identity-certificate mapping are not consistent") self.keyChain.deleteIdentity(identityName) self.assertFalse(self.identityStorage.doesKeyExist(keyName1)) def test_certificate_add_delete(self): identityName = Name('/TestIdentityStorage/Identity').appendVersion( int(time.time())) self.identityManager.createIdentityAndCertificate( identityName, KeyChain.getDefaultKeyParams()) keyName1 = self.identityManager.getDefaultKeyNameForIdentity(identityName) cert2 = self.identityManager.selfSign(keyName1) self.identityStorage.addCertificate(cert2) certName2 = cert2.getName() certName1 = self.identityManager.getDefaultCertificateNameForIdentity(identityName) self.assertNotEqual(certName1, certName2, "New certificate was set as default without explicit request") self.identityStorage.deleteCertificateInfo(certName1) self.assertTrue(self.identityStorage.doesCertificateExist(certName2)) self.assertFalse(self.identityStorage.doesCertificateExist(certName1)) self.keyChain.deleteIdentity(identityName) self.assertFalse(self.identityStorage.doesCertificateExist(certName2)) def test_stress(self): # ndn-cxx/tests/unit-tests/security/test-sec-public-info-sqlite3.cpp identityName = Name("/TestSecPublicInfoSqlite3/Delete").appendVersion( int(time.time())) # ndn-cxx returns the cert name, but the IndentityManager docstring # specifies a key certName1 = self.keyChain.createIdentityAndCertificate(identityName) keyName1 = IdentityCertificate.certificateNameToPublicKeyName(certName1) keyName2 = self.keyChain.generateRSAKeyPairAsDefault(identityName) cert2 = self.identityManager.selfSign(keyName2) certName2 = cert2.getName() self.identityManager.addCertificateAsDefault(cert2) keyName3 = self.keyChain.generateRSAKeyPairAsDefault(identityName) cert3 = self.identityManager.selfSign(keyName3) certName3 = cert3.getName() self.identityManager.addCertificateAsDefault(cert3) cert4 = self.identityManager.selfSign(keyName3) self.identityManager.addCertificateAsDefault(cert4) certName4 = cert4.getName() cert5 = self.identityManager.selfSign(keyName3) self.identityManager.addCertificateAsDefault(cert5) certName5 = cert5.getName() self.assertTrue(self.identityStorage.doesIdentityExist(identityName)) self.assertTrue(self.identityStorage.doesKeyExist(keyName1)) self.assertTrue(self.identityStorage.doesKeyExist(keyName2)) self.assertTrue(self.identityStorage.doesKeyExist(keyName3)) self.assertTrue(self.identityStorage.doesCertificateExist(certName1)) self.assertTrue(self.identityStorage.doesCertificateExist(certName2)) self.assertTrue(self.identityStorage.doesCertificateExist(certName3)) self.assertTrue(self.identityStorage.doesCertificateExist(certName4)) self.assertTrue(self.identityStorage.doesCertificateExist(certName5)) self.identityStorage.deleteCertificateInfo(certName5) self.assertFalse(self.identityStorage.doesCertificateExist(certName5)) self.assertTrue(self.identityStorage.doesCertificateExist(certName4)) self.assertTrue(self.identityStorage.doesCertificateExist(certName3)) self.assertTrue(self.identityStorage.doesKeyExist(keyName2)) self.identityStorage.deletePublicKeyInfo(keyName3) self.assertFalse(self.identityStorage.doesCertificateExist(certName4)) self.assertFalse(self.identityStorage.doesCertificateExist(certName3)) self.assertFalse(self.identityStorage.doesKeyExist(keyName3)) self.assertTrue(self.identityStorage.doesKeyExist(keyName2)) self.assertTrue(self.identityStorage.doesKeyExist(keyName1)) self.assertTrue(self.identityStorage.doesIdentityExist(identityName)) self.keyChain.deleteIdentity(identityName) self.assertFalse(self.identityStorage.doesCertificateExist(certName2)) self.assertFalse(self.identityStorage.doesKeyExist(keyName2)) self.assertFalse(self.identityStorage.doesCertificateExist(certName1)) self.assertFalse(self.identityStorage.doesKeyExist(keyName1)) self.assertFalse(self.identityStorage.doesIdentityExist(identityName)) def test_ecdsa_identity(self): identityName = Name("/TestSqlIdentityStorage/KeyType/ECDSA") keyName = self.identityManager.generateEcdsaKeyPairAsDefault(identityName) cert = self.identityManager.selfSign(keyName) self.identityManager.addCertificateAsIdentityDefault(cert) # Check the self-signature. failedCallback = Mock() verifiedCallback = Mock() self.keyChain.verifyData(cert, verifiedCallback, failedCallback) self.assertEqual(verifiedCallback.call_count, 1, "Verification callback was not used.") self.keyChain.deleteIdentity(identityName) self.assertFalse(self.identityStorage.doesKeyExist(keyName))
class BmsNode(object): def __init__(self): self.conf = None self._keyChain = None self._certificateName = None self._dataQueue = dict() self._memoryContentCache = None self._identityName = None self._aggregation = Aggregation() def setConfiguration(self, fileName, trustSchemaFile): self.conf = BoostInfoParser() self.conf.read(fileName) self._identityName = Name(self.conf.getNodePrefix()) self._trustSchemaFile = trustSchemaFile def onDataNotFound(self, prefix, interest, face, interestFilterId, filter): #print('Data not found for ' + interest.getName().toUri()) return def startPublishing(self): # One-time security setup self.prepareLogging() privateKeyStorage = FilePrivateKeyStorage() identityStorage = BasicIdentityStorage() policyManager = ConfigPolicyManager(self._trustSchemaFile) self._keyChain = KeyChain(IdentityManager(identityStorage, privateKeyStorage), policyManager) self._certificateName = self._keyChain.createIdentityAndCertificate(self._identityName) print("My Identity name: " + self._identityName.toUri()) print("My certificate name: " + self._certificateName.toUri()) certificateData = self._keyChain.getIdentityManager()._identityStorage.getCertificate(self._certificateName, True) print("My certificate string: " + b64encode(certificateData.wireEncode().toBuffer())) # self._keyChain.getIdentityCertificate(self._certificateName).) self._loop = asyncio.get_event_loop() self._face = ThreadsafeFace(self._loop) self._keyChain.setFace(self._face) self._face.setCommandSigningInfo(self._keyChain, self._certificateName) self._memoryContentCache = MemoryContentCache(self._face) # We should only ask for cert to be signed upon the first run of a certain aggregator if DO_CERT_SETUP: if (KeyLocator.getFromSignature(certificateData.getSignature()).getKeyName().equals(self._certificateName.getPrefix(-1))): # Need to configure for mini-ndn; aggregation node runs outside of mini-ndn first so that signed cert get installed and mini-ndn won't ask for this again print("certificate " + self._certificateName.toUri() + " asking for signature") response = urllib2.urlopen("http://192.168.56.1:5000/bms-cert-hack?cert=" + b64encode(certificateData.wireEncode().toBuffer()) + "&cert_prefix=" + self._identityName.toUri() + '&subject_name=' + self._identityName.toUri()).read() signedCertData = Data() signedCertData.wireDecode(Blob(b64decode(response))) self._memoryContentCache.add(signedCertData) cmdline = ['ndnsec-install-cert', '-'] p = subprocess.Popen(cmdline, stdin=subprocess.PIPE, stdout=subprocess.PIPE) # desanitize + sign in GET request cert, err = p.communicate(response) if p.returncode != 0: raise RuntimeError("ndnsec-install-cert error") else: self._memoryContentCache.add(certificateData) else: self._memoryContentCache.add(certificateData) dataNode = self.conf.getDataNode() childrenNode = self.conf.getChildrenNode() self._memoryContentCache.registerPrefix(Name(self._identityName), self.onRegisterFailed, self.onDataNotFound) # For each type of data, we refresh each type of aggregation according to the interval in the configuration for i in range(len(dataNode.subtrees)): dataType = dataNode.subtrees.keys()[i] aggregationParams = self.conf.getProducingParamsForAggregationType(dataNode.subtrees.items()[i][1]) if childrenNode == None: self._dataQueue[dataType] = DataQueue(None, None, None) self.generateData(dataType, 2, 0) for aggregationType in aggregationParams: childrenList = OrderedDict() if childrenNode != None: for j in range(len(childrenNode.subtrees)): if dataType in childrenNode.subtrees.items()[j][1].subtrees['data'].subtrees: if aggregationType in childrenNode.subtrees.items()[j][1].subtrees['data'].subtrees[dataType].subtrees: childrenList[childrenNode.subtrees.items()[j][0]] = self.conf.getProducingParamsForAggregationType(childrenNode.subtrees.items()[j][1].subtrees['data'].subtrees[dataType])[aggregationType] self.startPublishingAggregation(aggregationParams[aggregationType], childrenList, dataType, aggregationType) return def startPublishingAggregation(self, params, childrenList, dataType, aggregationType): if __debug__: print('Start publishing for ' + dataType + '-' + aggregationType) # aggregation calculating and publishing mechanism publishingPrefix = Name(self._identityName).append(DATA_COMPONENT).append(dataType).append(AGGREGATION_COMPONENT).append(aggregationType) self._dataQueue[dataType + aggregationType] = DataQueue(params, childrenList, publishingPrefix) if len(childrenList.keys()) == 0: # TODO: make start_time optional for leaf nodes self._loop.call_later(int(params['producer_interval']), self.calculateAggregation, dataType, aggregationType, childrenList, int(params['start_time']), int(params['producer_interval']), publishingPrefix, True) else: # express interest for children who produce the same data and aggregation type for childName in childrenList.keys(): name = Name(self._identityName).append(childName).append(DATA_COMPONENT).append(dataType).append(AGGREGATION_COMPONENT).append(aggregationType) interest = Interest(name) # if start_time is specified, we ask for data starting at start_time; # if not, we ask for the right most child and go from there if ('start_time' in childrenList[childName]): endTime = int(childrenList[childName]['start_time']) + int(childrenList[childName]['producer_interval']) interest.getName().append(str(childrenList[childName]['start_time'])).append(str(endTime)) else: # TODO: For now we are playing with historical data, for each run we don't want to miss any data, thus we start with leftMost interest.setChildSelector(0) interest.setMustBeFresh(True) interest.setInterestLifetimeMilliseconds(DEFAULT_INTEREST_LIFETIME) if __debug__: print(' Issue interest: ' + interest.getName().toUri()) self._face.expressInterest(interest, self.onData, self.onTimeout) return # TODO: once one calculation's decided a child has not answered, we should do another calculation def calculateAggregation(self, dataType, aggregationType, childrenList, startTime, interval, publishingPrefix, repeat = False): doCalc = True dataList = [] # TODO: an intermediate node cannot produce raw data for now if len(childrenList.keys()) != 0: for childName in childrenList.keys(): dataDictKey = self.getDataDictKey(startTime, (startTime + interval), childName) if dataDictKey in self._dataQueue[dataType + aggregationType]._dataDict: data = self._dataQueue[dataType + aggregationType]._dataDict[dataDictKey] dataList.append(float(data.getContent().toRawStr())) else: #print('Child ' + childName + ' has not replied yet') doCalc = False break else: for inst in self._dataQueue[dataType]._dataDict.keys(): if int(inst) >= startTime and int(inst) < startTime + interval: dataList.append(self._dataQueue[dataType]._dataDict[inst]) if doCalc: content = self._aggregation.getAggregation(aggregationType, dataList) if content: publishData = Data(Name(publishingPrefix).append(str(startTime)).append(str(startTime + interval))) publishData.setContent(str(content)) publishData.getMetaInfo().setFreshnessPeriod(DEFAULT_DATA_LIFETIME) self._keyChain.sign(publishData, self._certificateName) self._memoryContentCache.add(publishData) for childName in childrenList.keys(): dataDictKey = self.getDataDictKey(startTime, (startTime + interval), childName) if dataDictKey in self._dataQueue[dataType + aggregationType]._dataDict: del self._dataQueue[dataType + aggregationType]._dataDict[dataDictKey] if __debug__: print("Produced: " + publishData.getName().toUri() + "; " + publishData.getContent().toRawStr()) # repetition of this function only happens for raw data producer, otherwise calculateAggregation is called by each onData if repeat: self._loop.call_later(interval, self.calculateAggregation, dataType, aggregationType, childrenList, startTime + interval, interval, publishingPrefix, repeat) return def generateData(self, dataType, interval, startTime): self._dataQueue[dataType]._dataDict[str(startTime)] = random.randint(0,9) self._loop.call_later(interval, self.generateData, dataType, interval, startTime + interval) return def onRegisterFailed(self, prefix): raise RuntimeError("Register failed for prefix", prefix.toUri()) def onVerified(self, data): print('Data verified: ' + data.getName().toUri()) return def onVerifyFailed(self, data): print('Data verification failed: ' + data.getName().toUri()) return def onData(self, interest, data): self._keyChain.verifyData(data, self.onVerified, self.onVerifyFailed) dataName = data.getName() dataQueue = None if __debug__: print("Got data: " + dataName.toUri() + "; " + data.getContent().toRawStr()) for i in range(0, len(dataName)): if dataName.get(i).toEscapedString() == AGGREGATION_COMPONENT: dataType = dataName.get(i - 1).toEscapedString() aggregationType = dataName.get(i + 1).toEscapedString() startTime = int(dataName.get(i + 2).toEscapedString()) endTime = int(dataName.get(i + 3).toEscapedString()) childName = dataName.get(i - 3).toEscapedString() dataAndAggregationType = dataType + aggregationType dataDictKey = self.getDataDictKey(startTime, endTime, childName) dataQueue = self._dataQueue[dataAndAggregationType] dataQueue._dataDict[dataDictKey] = data break # TODO: check what if interval/starttime is misconfigured if dataQueue: self.calculateAggregation(dataType, aggregationType, dataQueue._childrenList, startTime, endTime - startTime, dataQueue._publishingPrefix) # Always ask for the next piece of data when we receive this one; assumes interval does not change; this also assumes there are no more components after endTime #newInterestName = dataName.getPrefix(i + 2).append(str(endTime)).append(str(endTime + (endTime - startTime))) # We don't expect aggregated data name to be continuous within our given time window, so we ask with exclusion instead newInterestName = dataName.getPrefix(i + 2) newInterest = Interest(interest) newInterest.setName(newInterestName) newInterest.setChildSelector(0) exclude = Exclude() exclude.appendAny() exclude.appendComponent(dataName.get(i + 2)) newInterest.setExclude(exclude) self._face.expressInterest(newInterest, self.onData, self.onTimeout) if __debug__: print(" issue interest: " + interest.getName().toUri()) return def onTimeout(self, interest): if __debug__: print(" interest timeout: " + interest.getName().toUri() + "; reexpress") pass self._face.expressInterest(interest, self.onData, self.onTimeout) return def stop(self): self._loop.stop() if __debug__: print("Stopped") return # This creation of dataDictKey means parent and child should not have the same name @staticmethod def getDataDictKey(startTime, endTime, childName): return str(startTime) + '/' + str(endTime) + '/' + childName ## # Logging ## def prepareLogging(self): self.log = logging.getLogger(str(self.__class__)) self.log.setLevel(logging.DEBUG) logFormat = "%(asctime)-15s %(name)-20s %(funcName)-20s (%(levelname)-8s):\n\t%(message)s" self._console = logging.StreamHandler() self._console.setFormatter(logging.Formatter(logFormat)) self._console.setLevel(logging.INFO) # without this, a lot of ThreadsafeFace errors get swallowed up logging.getLogger("trollius").addHandler(self._console) self.log.addHandler(self._console) def setLogLevel(self, level): """ Set the log level that will be output to standard error :param level: A log level constant defined in the logging module (e.g. logging.INFO) """ self._console.setLevel(level) def getLogger(self): """ :return: The logger associated with this node :rtype: logging.Logger """ return self.log