def test_append(self): # could possibly split this into different tests uri = "/localhost/user/folders/files/%00%0F" name = Name(uri) name2 = Name("/localhost").append(Name("/user/folders/")) self.assertEqual(name2.size(), 3, 'Name constructed by appending names has ' + str(name2.size()) + ' components instead of 3') self.assertTrue(name2.get(2).getValue().equals(Blob(bytearray("folders"))), 'Name constructed with append has wrong suffix') name2 = name2.append("files") self.assertEqual(name2.size(), 4, 'Name constructed by appending string has ' + str(name2.size()) + ' components instead of 4') name2 = name2.appendSegment(15) self.assertTrue(name2.get(4).getValue().equals(Blob(bytearray([0x00, 0x0F]))), 'Name constructed by appending segment has wrong segment value') self.assertTrue(name2.equals(name), 'Name constructed with append is not equal to URI constructed name') self.assertEqual(name2.toUri(), name.toUri(), 'Name constructed with append has wrong URI')
def main(): # The default Face will connect using a Unix socket, or to "localhost". face = Face() identityStorage = MemoryIdentityStorage() privateKeyStorage = MemoryPrivateKeyStorage() keyChain = KeyChain( IdentityManager(identityStorage, privateKeyStorage), None) keyChain.setFace(face) # 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) echo = Echo(keyChain, certificateName) prefix = Name("/testecho") dump("Register prefix", prefix.toUri()) face.registerPrefix(prefix, echo.onInterest, echo.onRegisterFailed) while echo._responseCount < 1: face.processEvents() # We need to sleep for a few milliseconds so we don't use 100% of the CPU. time.sleep(0.01) face.shutdown()
def wrap_content(self, name, content, key=None, key_locator=None): """ @param name - name of the data @param content - data to be wrapped @param key - key used to sign the data @return the content object created wraps the given name and content into a content object """ co = Data(Name(name)) co.setContent(content) co.getMetaInfo().setFreshnessPeriod(5000) co.getMetaInfo().setFinalBlockID(Name("/%00%09")[0]) identityStorage = MemoryIdentityStorage() privateKeyStorage = MemoryPrivateKeyStorage() identityManager = IdentityManager(identityStorage, privateKeyStorage) keyChain = KeyChain(identityManager, None) # Initialize the storage. keyName = Name("/ndn/bms/DSK-default") certificateName = keyName.getSubName(0, keyName.size() - 1).append( "KEY").append(keyName[-1]).append("ID-CERT").append("0") identityStorage.addKey(keyName, KeyType.RSA, Blob(DEFAULT_PUBLIC_KEY_DER)) privateKeyStorage.setKeyPairForKeyName(keyName, DEFAULT_PUBLIC_KEY_DER, DEFAULT_PRIVATE_KEY_DER) keyChain.sign(co, certificateName) _data = co.wireEncode() return _data.toRawStr()
def createKeyChain(): """ Create an in-memory KeyChain with default keys. :return: A tuple with the new KeyChain and certificate name. :rtype: (KeyChain,Name) """ identityStorage = MemoryIdentityStorage() privateKeyStorage = MemoryPrivateKeyStorage() keyChain = KeyChain(IdentityManager(identityStorage, privateKeyStorage), NoVerifyPolicyManager()) # Initialize the storage. keyName = Name("/testname/DSK-123") certificateName = keyName.getSubName( 0, keyName.size() - 1).append("KEY").append( keyName.get(-1)).append("ID-CERT").append("0") identityStorage.addKey(keyName, KeyType.RSA, Blob(DEFAULT_RSA_PUBLIC_KEY_DER, False)) privateKeyStorage.setKeyPairForKeyName(keyName, KeyType.RSA, DEFAULT_RSA_PUBLIC_KEY_DER, DEFAULT_RSA_PRIVATE_KEY_DER) return keyChain, certificateName
def _updateCapabilities(self): """ Send the controller a list of our commands. """ fullCommandName = Name(self._policyManager.getTrustRootIdentity() ).append('updateCapabilities') capabilitiesMessage = UpdateCapabilitiesCommandMessage() for command in self._commands: commandName = Name(self.prefix).append(Name(command.suffix)) capability = capabilitiesMessage.capabilities.add() for i in range(commandName.size()): capability.commandPrefix.components.append( str(commandName.get(i).getValue())) for kw in command.keywords: capability.keywords.append(kw) capability.needsSignature = command.isSigned encodedCapabilities = ProtobufTlv.encode(capabilitiesMessage) fullCommandName.append(encodedCapabilities) interest = Interest(fullCommandName) interest.setInterestLifetimeMilliseconds(5000) self.face.makeCommandInterest(interest) signature = self._policyManager._extractSignature(interest) self.log.info("Sending capabilities to controller") self.face.expressInterest(interest, self._onCapabilitiesAck, self._onCapabilitiesTimeout) # update twice a minute self.loop.call_later(30, self._updateCapabilities)
def main(): face = Face("localhost") identityStorage = MemoryIdentityStorage() privateKeyStorage = MemoryPrivateKeyStorage() keyChain = KeyChain( IdentityManager(identityStorage, privateKeyStorage), None) keyChain.setFace(face) # Initialize the storage. keyName = Name("/testname/DSK-reposerver") certificateName = keyName.getSubName(0, keyName.size() - 1).append( "KEY").append(keyName[-1]).append("ID-CERT").append("0") identityStorage.addKey(keyName, KeyType.RSA, Blob(DEFAULT_PUBLIC_KEY_DER)) privateKeyStorage.setKeyPairForKeyName( keyName, DEFAULT_PUBLIC_KEY_DER, DEFAULT_PRIVATE_KEY_DER) echo = RepoServer(keyChain, certificateName) prefix = Name("/ndn/ucla.edu/bms") dump("Register prefix", prefix.toUri()) face.registerPrefix(prefix, echo.onInterest, echo.onRegisterFailed) while True: face.processEvents() # We need to sleep for a few milliseconds so we don't use 100% of the CPU. time.sleep(0.01) face.shutdown()
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 main(): # The default Face will connect using a Unix socket, or to "localhost". face = Face() identityStorage = MemoryIdentityStorage() privateKeyStorage = MemoryPrivateKeyStorage() keyChain = KeyChain(IdentityManager(identityStorage, privateKeyStorage), None) keyChain.setFace(face) # 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) echo = Echo(keyChain, certificateName) prefix = Name("/testecho") dump("Register prefix", prefix.toUri()) face.registerPrefix(prefix, echo.onInterest, echo.onRegisterFailed) while echo._responseCount < 1: face.processEvents() # We need to sleep for a few milliseconds so we don't use 100% of the CPU. time.sleep(0.01) face.shutdown()
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 _updateCapabilities(self): """ Send the controller a list of our commands. """ fullCommandName = Name( self._policyManager.getTrustRootIdentity()).append( 'updateCapabilities') capabilitiesMessage = UpdateCapabilitiesCommandMessage() for command in self._commands: commandName = Name(self.prefix).append(Name(command.suffix)) capability = capabilitiesMessage.capabilities.add() for i in range(commandName.size()): capability.commandPrefix.components.append( str(commandName.get(i).getValue())) for kw in command.keywords: capability.keywords.append(kw) capability.needsSignature = command.isSigned encodedCapabilities = ProtobufTlv.encode(capabilitiesMessage) fullCommandName.append(encodedCapabilities) interest = Interest(fullCommandName) interest.setInterestLifetimeMilliseconds(5000) self.face.makeCommandInterest(interest) signature = self._policyManager._extractSignature(interest) self.log.info("Sending capabilities to controller") self.face.expressInterest(interest, self._onCapabilitiesAck, self._onCapabilitiesTimeout) # update twice a minute self.loop.call_later(30, self._updateCapabilities)
def main(): interest = Interest() interest.wireDecode(TlvInterest) dump("Interest:") dumpInterest(interest) # Set the name again to clear the cached encoding so we encode again. interest.setName(interest.getName()) encoding = interest.wireEncode() dump("") dump("Re-encoded interest", encoding.toHex()) reDecodedInterest = Interest() reDecodedInterest.wireDecode(encoding) dump("Re-decoded Interest:") dumpInterest(reDecodedInterest) freshInterest = Interest(Name("/ndn/abc")) freshInterest.setMustBeFresh(False) dump(freshInterest.toUri()) freshInterest.setMinSuffixComponents(4) freshInterest.setMaxSuffixComponents(6) freshInterest.getKeyLocator().setType(KeyLocatorType.KEY_LOCATOR_DIGEST) freshInterest.getKeyLocator().setKeyData(bytearray( [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F])) freshInterest.getExclude().appendComponent(Name("abc")[0]).appendAny() freshInterest.setInterestLifetimeMilliseconds(30000) freshInterest.setChildSelector(1) freshInterest.setMustBeFresh(True); freshInterest.setScope(2) 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) # Make a Face just so that we can sign the interest. face = Face("localhost") face.setCommandSigningInfo(keyChain, certificateName) face.makeCommandInterest(freshInterest) reDecodedFreshInterest = Interest() reDecodedFreshInterest.wireDecode(freshInterest.wireEncode()) dump("") dump("Re-decoded fresh Interest:") dumpInterest(reDecodedFreshInterest) keyChain.verifyInterest( reDecodedFreshInterest, makeOnVerified("Freshly-signed Interest"), makeOnVerifyFailed("Freshly-signed Interest"))
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 setUp(self): self.decryptionKeys = {} # key: Name, value: Blob self.encryptionKeys = {} # key: Name, value: Data # Reuse the policy_config subdirectory for the temporary SQLite files. self.databaseFilePath = "policy_config/test.db" try: os.remove(self.databaseFilePath) except OSError: # no such file pass self.groupName = Name("/Prefix/READ") self.contentName = Name("/Prefix/SAMPLE/Content") self.cKeyName = Name("/Prefix/SAMPLE/Content/C-KEY/1") self.eKeyName = Name("/Prefix/READ/E-KEY/1/2") self.dKeyName = Name("/Prefix/READ/D-KEY/1/2") self.uKeyName = Name("/U/Key") self.uName = Name("/U") # Generate the E-KEY and D-KEY. params = RsaKeyParams() self.fixtureDKeyBlob = RsaAlgorithm.generateKey(params).getKeyBits() self.fixtureEKeyBlob = RsaAlgorithm.deriveEncryptKey( self.fixtureDKeyBlob).getKeyBits() # Generate the user key. self.fixtureUDKeyBlob = RsaAlgorithm.generateKey(params).getKeyBits() self.fixtureUEKeyBlob = RsaAlgorithm.deriveEncryptKey( self.fixtureUDKeyBlob).getKeyBits() # Load the C-KEY. self.fixtureCKeyBlob = Blob(AES_KEY, False) # Set up the keyChain. identityStorage = MemoryIdentityStorage() privateKeyStorage = MemoryPrivateKeyStorage() self.keyChain = KeyChain( IdentityManager(identityStorage, privateKeyStorage), NoVerifyPolicyManager()) # Initialize the storage. keyName = Name("/testname/DSK-123") self.certificateName = keyName.getSubName( 0, keyName.size() - 1).append("KEY").append( keyName.get(-1)).append("ID-CERT").append("0") identityStorage.addKey(keyName, KeyType.RSA, Blob(DEFAULT_RSA_PUBLIC_KEY_DER, False)) privateKeyStorage.setKeyPairForKeyName(keyName, KeyType.RSA, DEFAULT_RSA_PUBLIC_KEY_DER, DEFAULT_RSA_PRIVATE_KEY_DER)
def createCheckInterest(self, fullName, checkNum): insertionName = Name(self.repoPrefix).append('insert check') commandParams = RepoCommandParameterMessage() interestName = Name(fullName) commandParams.repo_command_parameter.process_id = checkNum for i in range(interestName.size()): commandParams.repo_command_parameter.name.component.append(str(interestName.get(i).getValue())) commandName = insertionName.append(ProtobufTlv.encode(commandParams)) interest = Interest(commandName) return interest
def createCheckInterest(fullName, checkNum): insertionName = Name("/repotest/repo/insert check") commandParams = RepoCommandParameterMessage() interestName = Name(fullName) commandParams.repo_command_parameter.process_id = checkNum for i in range(interestName.size()): commandParams.repo_command_parameter.name.component.append(interestName.get(i).toEscapedString()) commandName = insertionName.append(ProtobufTlv.encode(commandParams)) interest = Interest(commandName) return interest
def setUp(self): self.decryptionKeys = {} # key: Name, value: Blob self.encryptionKeys = {} # key: Name, value: Data # Reuse the policy_config subdirectory for the temporary SQLite files. self.databaseFilePath = "policy_config/test.db" try: os.remove(self.databaseFilePath) except OSError: # no such file pass self.groupName = Name("/Prefix/READ") self.contentName = Name("/Prefix/SAMPLE/Content") self.cKeyName = Name("/Prefix/SAMPLE/Content/C-KEY/1") self.eKeyName = Name("/Prefix/READ/E-KEY/1/2") self.dKeyName = Name("/Prefix/READ/D-KEY/1/2") self.uKeyName = Name("/U/Key") self.uName = Name("/U") # Generate the E-KEY and D-KEY. params = RsaKeyParams() self.fixtureDKeyBlob = RsaAlgorithm.generateKey(params).getKeyBits() self.fixtureEKeyBlob = RsaAlgorithm.deriveEncryptKey( self.fixtureDKeyBlob).getKeyBits() # Generate the user key. self.fixtureUDKeyBlob = RsaAlgorithm.generateKey(params).getKeyBits() self.fixtureUEKeyBlob = RsaAlgorithm.deriveEncryptKey( self.fixtureUDKeyBlob).getKeyBits() # Load the C-KEY. self.fixtureCKeyBlob = Blob(AES_KEY, False) # Set up the keyChain. identityStorage = MemoryIdentityStorage() privateKeyStorage = MemoryPrivateKeyStorage() self.keyChain = KeyChain( IdentityManager(identityStorage, privateKeyStorage), NoVerifyPolicyManager()) # Initialize the storage. keyName = Name("/testname/DSK-123") self.certificateName = keyName.getSubName(0, keyName.size() - 1).append( "KEY").append(keyName.get(-1)).append("ID-CERT").append("0") identityStorage.addKey( keyName, KeyType.RSA, Blob(DEFAULT_RSA_PUBLIC_KEY_DER, False)) privateKeyStorage.setKeyPairForKeyName( keyName, KeyType.RSA, DEFAULT_RSA_PUBLIC_KEY_DER, DEFAULT_RSA_PRIVATE_KEY_DER)
def onListResponse(self, interest, data): tempCont = data.getContent() tempStr = tempCont.__str__() #sleep for 1 sec therefore we can run more important process in the threadSafeFace time.sleep(1) tempList = tempStr.split(",") #add songs to song list for i in tempList: self.totalList.append(i) print "List Update:",self.totalList dataName = Name(data.getName()) deviceComponent = data.getName().get(dataName.size()-1) device = deviceComponent.toEscapedString() self.issueListCommand(device)
def onListResponse(self, interest, data): tempCont = data.getContent() tempStr = tempCont.__str__() #sleep for 1 sec therefore we can run more important process in the threadSafeFace time.sleep(1) tempList = tempStr.split(",") #add songs to song list for i in tempList: self.totalList.append(i) print "List Update:", self.totalList dataName = Name(data.getName()) deviceComponent = data.getName().get(dataName.size() - 1) device = deviceComponent.toEscapedString() self.issueListCommand(device)
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 __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 createInsertInterest(fullName): # we have to do the versioning when we poke the repo interestName = Name(fullName) logger.debug('Creating insert interest for: '+interestName.toUri()) insertionName = Name("/repotest/repo/insert") commandParams = RepoCommandParameterMessage() for i in range(interestName.size()): commandParams.repo_command_parameter.name.component.append(interestName.get(i).toEscapedString()) commandParams.repo_command_parameter.start_block_id = 0 commandParams.repo_command_parameter.end_block_id = 0 commandName = insertionName.append(ProtobufTlv.encode(commandParams)) interest = Interest(commandName) return interest
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 test_append_parameters_digest(self): name = Name("/local/ndn/prefix") interest = Interest(name) self.assertTrue(not interest.hasApplicationParameters()) # No parameters yet, so it should do nothing. interest.appendParametersDigestToName() self.assertEqual("/local/ndn/prefix", interest.getName().toUri()) applicationParameters = Blob(bytearray([ 0x23, 0x01, 0xC0 ])) interest.setApplicationParameters(applicationParameters) self.assertTrue(interest.hasApplicationParameters()) interest.appendParametersDigestToName() self.assertEqual(name.size() + 1, interest.getName().size()) self.assertTrue(interest.getName().getPrefix(-1).equals(name)) SHA256_LENGTH = 32 self.assertEqual(SHA256_LENGTH, interest.getName().get(-1).getValue().size()) self.assertEqual(interest.getName().toUri(), "/local/ndn/prefix/" + "params-sha256=a16cc669b4c9ef6801e1569488513f9523ffb28a39e53aa6e11add8d00a413fc")
def createInsertInterest(self, fullName): ''' For poking the repo ''' # we have to do the versioning before we poke the repo interestName = Name(fullName) logger.debug('Creating insert interest for: '+interestName.toUri()) insertionName = Name(self.repoPrefix).append('insert') commandParams = RepoCommandParameterMessage() for i in range(interestName.size()): commandParams.repo_command_parameter.name.component.append(interestName.get(i).getValue().toRawStr()) commandParams.repo_command_parameter.start_block_id = 0 commandParams.repo_command_parameter.end_block_id = 0 commandName = insertionName.append(ProtobufTlv.encode(commandParams)) interest = Interest(commandName) interest.setInterestLifetimeMilliseconds(2000) return interest
def createKeyChain(): """ Create an in-memory KeyChain with default keys. :return: A tuple with the new KeyChain and certificate name. :rtype: (KeyChain,Name) """ identityStorage = MemoryIdentityStorage() privateKeyStorage = MemoryPrivateKeyStorage() keyChain = KeyChain( IdentityManager(identityStorage, privateKeyStorage), NoVerifyPolicyManager()) # Initialize the storage. keyName = Name("/testname/DSK-123") certificateName = keyName.getSubName(0, keyName.size() - 1).append( "KEY").append(keyName.get(-1)).append("ID-CERT").append("0") identityStorage.addKey( keyName, KeyType.RSA, Blob(DEFAULT_RSA_PUBLIC_KEY_DER, False)) privateKeyStorage.setKeyPairForKeyName( keyName, KeyType.RSA, DEFAULT_RSA_PUBLIC_KEY_DER, DEFAULT_RSA_PRIVATE_KEY_DER) return keyChain, certificateName
class BACnetAggregator(BIPSimpleApplication, Logging): def __init__(self, config): if _debug: BACnetAggregator._debug("__init__ %r", config) # get local address from the config file laddr = config.get('BACpypes', 'address') # make a local device object local_device = \ LocalDeviceObject( objectName=config.get('BACpypes','objectName') , objectIdentifier=config.getint('BACpypes','objectIdentifier') , maxApduLengthAccepted=config.getint('BACpypes','maxApduLengthAccepted') , segmentationSupported=config.get('BACpypes','segmentationSupported') , vendorIdentifier=config.getint('BACpypes','vendorIdentifier') ) # build a bit string that knows about the bit names pss = ServicesSupported() pss['whoIs'] = 1 pss['iAm'] = 1 pss['readProperty'] = 1 pss['writeProperty'] = 1 # set the property value to be just the bits local_device.protocolServicesSupported = pss.value # make a simple application BIPSimpleApplication.__init__(self, local_device, laddr) # create logger self.logger = BACnetDataLogger(self, config) self.loadKey() # keep track of requests to line up responses self._request = None # connect to local repo self.publisher = RepoSocketPublisher(12345) self.interval = 5 # in seconds def loadKey(self): self.identityStorage = MemoryIdentityStorage() self.privateKeyStorage = MemoryPrivateKeyStorage() self.keychain = KeyChain(IdentityManager(self.identityStorage, self.privateKeyStorage)) f = open(key_file, "r") self.key = RSA.importKey(f.read()) self.key_name = Name(bld_root).append(getKeyID(self.key)) key_pub_der = bytearray(self.key.publickey().exportKey(format="DER")) key_pri_der = bytearray(self.key.exportKey(format="DER")) self.identityStorage.addKey(self.key_name, KeyType.RSA, Blob(key_pub_der)) self.privateKeyStorage.setKeyPairForKeyName(self.key_name, key_pub_der, key_pri_der) self.cert_name = self.key_name.getSubName(0, self.key_name.size() - 1).append( "KEY").append(self.key_name[-1]).append("ID-CERT").append("0") print 'KeyName = ' + self.key_name.toUri() print 'CertName = ' + self.cert_name.toUri() def publishData(self, name_str, payload, timestamp): data = Data(Name(name_str).append(bytearray(timestamp))) iv = Random.new().read(AES.block_size) encryptor = AES.new(key, AES.MODE_CBC, iv) data.setContent(bytearray(time_s + iv + encryptor.encrypt(pad(json.dumps(payload))))) data.getMetaInfo().setFreshnessPeriod(10000) self.keychain.sign(data, self.cert_name) self.publisher.put(data) #print payload #print 'Publish ' + data.getName().toUri() def request(self, apdu): if _debug: BACnetAggregator._debug("request %r", apdu) # save a copy of the request self._request = apdu # forward it along BIPSimpleApplication.request(self, apdu) def confirmation(self, apdu): #print thread.get_ident() global kds_count, key, time_s, point_count if _debug: BACnetAggregator._debug("confirmation %r", apdu) if isinstance(apdu, Error): sys.stdout.write("error: %s\n" % (apdu.errorCode,)) sys.stdout.flush() elif isinstance(apdu, AbortPDU): apdu.debug_contents() elif (isinstance(self._request, ReadPropertyRequest)) and (isinstance(apdu, ReadPropertyACK)): # find the datatype datatype = get_datatype(apdu.objectIdentifier[0], apdu.propertyIdentifier) BACnetAggregator._debug(" - datatype: %r", datatype) if not datatype: raise TypeError, "unknown datatype" # special case for array parts, others are managed by cast_out if issubclass(datatype, Array) and (apdu.propertyArrayIndex is not None): if apdu.propertyArrayIndex == 0: value = apdu.propertyValue.cast_out(Unsigned) else: value = apdu.propertyValue.cast_out(datatype.subtype) else: value = apdu.propertyValue.cast_out(datatype) BACnetAggregator._debug(" - value: %r", value) #sys.stdout.write(str(value) + '\n') #sys.stdout.flush() # KDS if kds_count % 1200 == 0: time_t = int(time.time() * 1000) time_s = struct.pack("!Q", time_t) key = Random.new().read(32) kds_thread = kds.SimpleKDSPublisher(Name(bld_root), self.keychain, self.cert_name, key, time_s) kds_thread.start() kds_count = 0 kds_count = kds_count + 1 # now = int(time.time() * 1000) # in milliseconds payload = {'ts': now, 'val': value} timestamp = struct.pack("!Q", now) self.publishData(datapoints[point_count]['prefix'], payload, timestamp) point_count = (point_count + 1) % len(datapoints) # # # We could move the 'sleep&read' looping into logger thread so # that we could parallel read and write processes. For now we # only work on a single thread. The logger thread simply kicks # off the initial request and then exits. # if point_count == 0: time.sleep(self.interval) self.logger.do_read() def indication(self, apdu): if _debug: BACnetAggregator._debug("indication %r", apdu) if (isinstance(self._request, WhoIsRequest)) and (isinstance(apdu, IAmRequest)): device_type, device_instance = apdu.iAmDeviceIdentifier if device_type != 'device': raise DecodingError, "invalid object type" if (self._request.deviceInstanceRangeLowLimit is not None) and \ (device_instance < self._request.deviceInstanceRangeLowLimit): pass elif (self._request.deviceInstanceRangeHighLimit is not None) and \ (device_instance > self._request.deviceInstanceRangeHighLimit): pass else: # print out the contents sys.stdout.write('pduSource = ' + repr(apdu.pduSource) + '\n') sys.stdout.write('iAmDeviceIdentifier = ' + str(apdu.iAmDeviceIdentifier) + '\n') sys.stdout.write('maxAPDULengthAccepted = ' + str(apdu.maxAPDULengthAccepted) + '\n') sys.stdout.write('segmentationSupported = ' + str(apdu.segmentationSupported) + '\n') sys.stdout.write('vendorID = ' + str(apdu.vendorID) + '\n') sys.stdout.flush() # forward it along BIPSimpleApplication.indication(self, apdu)
class IotController(BaseNode): """ The controller class has a few built-in commands: - listCommands: return the names and capabilities of all attached devices - certificateRequest: takes public key information and returns name of new certificate - updateCapabilities: should be sent periodically from IotNodes to update their command lists - addDevice: called by the console to begin pairing, the payload is encrypted for the controller as it contains a PIN It is unlikely that you will need to subclass this. """ def __init__(self, nodeName, networkName): super(IotController, self).__init__() self.deviceSuffix = Name(nodeName) self.networkPrefix = Name(networkName) self.prefix = Name(self.networkPrefix).append(self.deviceSuffix) self._policyManager.setEnvironmentPrefix(self.networkPrefix) self._policyManager.setTrustRootIdentity(self.prefix) self._policyManager.setDeviceIdentity(self.prefix) self._policyManager.updateTrustRules() # the controller keeps a directory of capabilities->names self._directory = defaultdict(list) # keep track of who's still using HMACs # key is device serial, value is the HmacHelper self._hmacDevices = {} # our capabilities self._baseDirectory = {} # add the built-ins self._insertIntoCapabilities('listCommands', 'directory', False) # TODO: use xDialog in XWindows self.ui = Dialog(backtitle='NDN IoT User Console', height=18, width=78) self._directory.update(self._baseDirectory) self.setLogLevel(logging.INFO) def _insertIntoCapabilities(self, commandName, keyword, isSigned): newUri = Name(self.prefix).append(Name(commandName)).toUri() self._baseDirectory[keyword] = [{'signed':isSigned, 'name':newUri}] def beforeLoopStart(self): if not self._policyManager.hasRootSignedCertificate(): # make one.... self.log.warn('Generating controller key pair (this could take a while)...') newKey = self._identityManager.generateRSAKeyPairAsDefault( self.prefix, isKsk=True, progressFunc=self._showRSAProgress) newCert = self._identityManager.selfSign(newKey) self._identityManager.addCertificateAsDefault(newCert) self.face.setCommandSigningInfo(self._keyChain, self.getDefaultCertificateName()) self.face.registerPrefix(self.prefix, self._onCommandReceived, self.onRegisterFailed) self.loop.call_soon(self.onStartup) ###### # Initial device configuration ####### def _beginPairing(self, encryptedMessage): # base64 decode, decrypt, protobuf decode responseCode = 202 try: encryptedBytes = base64.urlsafe_b64decode(str(encryptedMessage.getValue())) decryptedBytes = self._identityManager.decryptAsIdentity(encryptedBytes, self.prefix) message = DevicePairingInfoMessage() ProtobufTlv.decode(message, decryptedBytes) except: responseCode = 500 else: info = message.info self.loop.call_soon(self._addDeviceToNetwork, info.deviceSerial, info.deviceSuffix, info.devicePin) return responseCode def _addDeviceToNetwork(self, deviceSerial, newDeviceSuffix, pin): h = HmacHelper(pin) self._hmacDevices[deviceSerial] = h d = DeviceConfigurationMessage() newDeviceSuffix = Name(newDeviceSuffix) for source, dest in [(self.networkPrefix, d.configuration.networkPrefix), (self.deviceSuffix, d.configuration.controllerName), (newDeviceSuffix, d.configuration.deviceSuffix)]: for i in range(len(source)): component = source.get(i) dest.components.append(component.getValue().toRawStr()) interestName = Name('/localhop/configure').append(Name(deviceSerial)) encodedParams = ProtobufTlv.encode(d) interestName.append(encodedParams) interest = Interest(interestName) interest.setInterestLifetimeMilliseconds(5000) h.signInterest(interest) self.face.expressInterest(interest, self._deviceAdditionResponse, self._deviceAdditionTimedOut) def _deviceAdditionTimedOut(self, interest): deviceSerial = str(interest.getName().get(2).getValue()) self.log.warn("Timed out trying to configure device " + deviceSerial) # don't try again self._hmacDevices.pop(deviceSerial) def _deviceAdditionResponse(self, interest, data): status = data.getContent().toRawStr() deviceSerial = str(interest.getName().get(2).getValue()) hmacChecker = self._hmacDevices[deviceSerial] if (hmacChecker.verifyData(data)): self.log.info("Received {} from {}".format(status, deviceSerial)) else: self.log.warn("Received invalid HMAC from {}".format(deviceSerial)) ###### # Certificate signing ###### def _handleCertificateRequest(self, interest, transport): """ Extracts a public key name and key bits from a command interest name component. Generates a certificate if the request is verifiable. This expects an HMAC signed interest. """ message = CertificateRequestMessage() commandParamsTlv = interest.getName().get(self.prefix.size()+1) ProtobufTlv.decode(message, commandParamsTlv.getValue()) signature = HmacHelper.extractInterestSignature(interest) deviceSerial = str(signature.getKeyLocator().getKeyName().get(-1).getValue()) response = Data(interest.getName()) certData = None hmac = None try: hmac = self._hmacDevices[deviceSerial] if hmac.verifyInterest(interest): certData = self._createCertificateFromRequest(message) # remove this hmac; another request will require a new pin self._hmacDevices.pop(deviceSerial) except KeyError: self.log.warn('Received certificate request for device with no registered key') except SecurityException: self.log.warn('Could not create device certificate') else: self.log.info('Creating certificate for device {}'.format(deviceSerial)) if certData is not None: response.setContent(certData.wireEncode()) response.getMetaInfo().setFreshnessPeriod(10000) # should be good even longer else: response.setContent("Denied") if hmac is not None: hmac.signData(response) self.sendData(response, transport, False) def _createCertificateFromRequest(self, message): """ Generate an IdentityCertificate from the public key information given. """ # TODO: Verify the certificate was actually signed with the private key # matching the public key we are issuing a cert for!! keyComponents = message.command.keyName.components keyName = Name("/".join(keyComponents)) self.log.debug("Key name: " + keyName.toUri()) if not self._policyManager.getEnvironmentPrefix().match(keyName): # we do not issue certs for keys outside of our network return None keyDer = Blob(message.command.keyBits) keyType = message.command.keyType try: self._identityStorage.addKey(keyName, keyType, keyDer) except SecurityException: # assume this is due to already existing? pass certificate = self._identityManager.generateCertificateForKey(keyName) self._keyChain.sign(certificate, self.getDefaultCertificateName()) # store it for later use + verification self._identityStorage.addCertificate(certificate) return certificate ###### # Device Capabilities ###### def _updateDeviceCapabilities(self, interest): """ Take the received capabilities update interest and update our directory listings. """ # we assume the sender is the one who signed the interest... signature = self._policyManager._extractSignature(interest) certificateName = signature.getKeyLocator().getKeyName() senderIdentity = IdentityCertificate.certificateNameToPublicKeyName(certificateName).getPrefix(-1) self.log.info('Updating capabilities for {}'.format(senderIdentity.toUri())) # get the params from the interest name messageComponent = interest.getName().get(self.prefix.size()+1) message = UpdateCapabilitiesCommandMessage() ProtobufTlv.decode(message, messageComponent.getValue()) # we remove all the old capabilities for the sender tempDirectory = defaultdict(list) for keyword in self._directory: tempDirectory[keyword] = [cap for cap in self._directory[keyword] if not senderIdentity.match(Name(cap['name']))] # then we add the ones from the message for capability in message.capabilities: capabilityPrefix = Name() for component in capability.commandPrefix.components: capabilityPrefix.append(component) commandUri = capabilityPrefix.toUri() if not senderIdentity.match(capabilityPrefix): self.log.error("Node {} tried to register another prefix: {} - ignoring update".format( senderIdentity.toUri(),commandUri)) else: for keyword in capability.keywords: allUris = [info['name'] for info in tempDirectory[keyword]] if capabilityPrefix not in allUris: listing = {'signed':capability.needsSignature, 'name':commandUri} tempDirectory[keyword].append(listing) self._directory= tempDirectory def _prepareCapabilitiesList(self, interestName): """ Responds to a directory listing request with JSON """ dataName = Name(interestName).append(Name.Component.fromNumber(int(time.time()))) response = Data(dataName) response.setContent(json.dumps(self._directory)) return response ##### # Interest handling #### def _onCommandReceived(self, prefix, interest, transport, prefixId): """ """ interestName = interest.getName() #if it is a certificate name, serve the certificate foundCert = self._identityStorage.getCertificate(interestName) if foundCert is not None: self.log.debug("Serving certificate request") transport.send(foundCert.wireEncode().buf()) return afterPrefix = interestName.get(prefix.size()).toEscapedString() if afterPrefix == "listCommands": #compose device list self.log.debug("Received device list request") response = self._prepareCapabilitiesList(interestName) self.sendData(response, transport) elif afterPrefix == "certificateRequest": #build and sign certificate self.log.debug("Received certificate request") self._handleCertificateRequest(interest, transport) elif afterPrefix == "updateCapabilities": # needs to be signed! self.log.debug("Received capabilities update") def onVerifiedCapabilities(interest): response = Data(interest.getName()) response.setContent(str(time.time())) self.sendData(response, transport) self._updateDeviceCapabilities(interest) self._keyChain.verifyInterest(interest, onVerifiedCapabilities, self.verificationFailed) elif afterPrefix == "addDevice": self.log.debug("Received pairing request") def onVerifiedPairingRequest(interest): response = Data(interest.getName()) encryptedMessage = interest.getName()[len(prefix)+1] responseCode = self._beginPairing(encryptedMessage) response.setContent(str(responseCode)) self.sendData(response, transport) self._keyChain.verifyInterest(interest, onVerifiedPairingRequest, self.verificationFailed) else: response = Data(interest.getName()) response.setContent("500") response.getMetaInfo().setFreshnessPeriod(1000) transport.send(response.wireEncode().buf()) def onStartup(self): # begin taking add requests self.log.info('Controller is ready') def _showRSAProgress(self, displayStr=''): displayStr = displayStr.strip() msg = '' if displayStr == 'p,q': msg = 'Generating giant prime numbers...' elif displayStr == 'd': msg = 'Generating private exponent...' elif displayStr == 'u': msg = 'Checking CRT coefficient...' self.log.debug(msg)
def main(): # Uncomment these lines to print ChronoSync debug messages. # logging.getLogger('').addHandler(logging.StreamHandler(sys.stdout)) # logging.getLogger('').setLevel(logging.INFO) screenName = promptAndInput("Enter your chat username: "******"ndn/edu/ucla/remap" hubPrefix = promptAndInput("Enter your hub prefix [" + defaultHubPrefix + "]: ") if hubPrefix == "": hubPrefix = defaultHubPrefix defaultChatRoom = "ndnchat" chatRoom = promptAndInput("Enter the chatroom name [" + defaultChatRoom + "]: ") if chatRoom == "": chatRoom = defaultChatRoom host = "localhost" print("Connecting to " + host + ", Chatroom: " + chatRoom + ", Username: "******"") # Set up the key chain. face = Face(host) identityStorage = MemoryIdentityStorage() privateKeyStorage = MemoryPrivateKeyStorage() keyChain = KeyChain(IdentityManager(identityStorage, privateKeyStorage), NoVerifyPolicyManager()) keyChain.setFace(face) 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) face.setCommandSigningInfo(keyChain, certificateName) chat = Chat(screenName, chatRoom, Name(hubPrefix), face, keyChain, certificateName) # The main loop to process Chat while checking stdin to send a message. print("Enter your chat message. To quit, enter \"leave\" or \"exit\".") while True: # Set timeout to 0 for an immediate check. isReady, _, _ = select.select([sys.stdin], [], [], 0) if len(isReady) != 0: input = promptAndInput("") if input == "leave" or input == "exit": # We will send the leave message below. break chat.sendMessage(input) face.processEvents() # We need to sleep for a few milliseconds so we don't use 100% of the CPU. time.sleep(0.01) # The user entered the command to leave. chat.leave() # Wait a little bit to allow other applications to fetch the leave message. startTime = Chat.getNowMilliseconds() while True: if Chat.getNowMilliseconds() - startTime >= 1000.0: break face.processEvents() time.sleep(0.01)
class IotController(BaseNode): """ The controller class has a few built-in commands: - listDevices: return the names and capabilities of all attached devices - certificateRequest: takes public key information and returns name of new certificate - updateCapabilities: should be sent periodically from IotNodes to update their command lists - addDevice: add a device based on HMAC It is unlikely that you will need to subclass this. """ def __init__(self, nodeName, networkName): super(IotController, self).__init__() self.deviceSuffix = Name(nodeName) self.networkPrefix = Name(networkName) self.prefix = Name(self.networkPrefix).append(self.deviceSuffix) self._policyManager.setEnvironmentPrefix(self.networkPrefix) self._policyManager.setTrustRootIdentity(self.prefix) self._policyManager.setDeviceIdentity(self.prefix) self._policyManager.updateTrustRules() # the controller keeps a directory of capabilities->names self._directory = defaultdict(list) # keep track of who's still using HMACs # key is device serial, value is the HmacHelper self._hmacDevices = {} # our capabilities self._baseDirectory = {} # add the built-ins self._insertIntoCapabilities('listDevices', 'directory', False) self._insertIntoCapabilities('updateCapabilities', 'capabilities', True) self._directory.update(self._baseDirectory) def _insertIntoCapabilities(self, commandName, keyword, isSigned): newUri = Name(self.prefix).append(Name(commandName)).toUri() self._baseDirectory[keyword] = [{'signed':isSigned, 'name':newUri}] def beforeLoopStart(self): if not self._policyManager.hasRootSignedCertificate(): # make one.... self.log.warn('Generating controller certificate...') newKey = self._identityManager.generateRSAKeyPairAsDefault( self.prefix, isKsk=True) newCert = self._identityManager.selfSign(newKey) self._identityManager.addCertificateAsDefault(newCert) self.face.setCommandSigningInfo(self._keyChain, self.getDefaultCertificateName()) self.face.registerPrefix(self.prefix, self._onCommandReceived, self.onRegisterFailed) self.loop.call_soon(self.onStartup) ###### # Initial configuration ####### # TODO: deviceSuffix will be replaced by deviceSerial def _addDeviceToNetwork(self, deviceSerial, newDeviceSuffix, pin): h = HmacHelper(pin) self._hmacDevices[deviceSerial] = h d = DeviceConfigurationMessage() for source, dest in [(self.networkPrefix, d.configuration.networkPrefix), (self.deviceSuffix, d.configuration.controllerName), (newDeviceSuffix, d.configuration.deviceSuffix)]: for i in range(source.size()): component = source.get(i) dest.components.append(component.getValue().toRawStr()) interestName = Name('/localhop/configure').append(Name(deviceSerial)) encodedParams = ProtobufTlv.encode(d) interestName.append(encodedParams) interest = Interest(interestName) h.signInterest(interest) self.face.expressInterest(interest, self._deviceAdditionResponse, self._deviceAdditionTimedOut) def _deviceAdditionTimedOut(self, interest): deviceSerial = str(interest.getName().get(2).getValue()) self.log.warn("Timed out trying to configure device " + deviceSerial) # don't try again self._hmacDevices.pop(deviceSerial) def _deviceAdditionResponse(self, interest, data): status = data.getContent().toRawStr() deviceSerial = str(interest.getName().get(2).getValue()) hmacChecker = self._hmacDevices[deviceSerial] if (hmacChecker.verifyData(data)): self.log.info("Received {} from {}".format(status, deviceSerial)) else: self.log.warn("Received invalid HMAC from {}".format(deviceSerial)) ###### # Certificate signing ###### def _handleCertificateRequest(self, interest, transport): """ Extracts a public key name and key bits from a command interest name component. Generates a certificate if the request is verifiable. This expects an HMAC signed interest. """ message = CertificateRequestMessage() commandParamsTlv = interest.getName().get(self.prefix.size()+1) ProtobufTlv.decode(message, commandParamsTlv.getValue()) signature = HmacHelper.extractInterestSignature(interest) deviceSerial = str(signature.getKeyLocator().getKeyName().get(-1).getValue()) response = Data(interest.getName()) certData = None hmac = None try: hmac = self._hmacDevices[deviceSerial] if hmac.verifyInterest(interest): certData = self._createCertificateFromRequest(message) # remove this hmac; another request will require a new pin self._hmacDevices.pop(deviceSerial) except KeyError: self.log.warn('Received certificate request for device with no registered key') except SecurityException: self.log.warn('Could not create device certificate') else: self.log.info('Creating certificate for device {}'.format(deviceSerial)) if certData is not None: response.setContent(certData.wireEncode()) response.getMetaInfo().setFreshnessPeriod(10000) # should be good even longer else: response.setContent("Denied") if hmac is not None: hmac.signData(response) self.sendData(response, transport, False) def _createCertificateFromRequest(self, message): """ Generate an IdentityCertificate from the public key information given. """ # TODO: Verify the certificate was actually signed with the private key # matching the public key we are issuing a cert for!! keyComponents = message.command.keyName.components keyName = Name("/".join(keyComponents)) self.log.debug("Key name: " + keyName.toUri()) if not self._policyManager.getEnvironmentPrefix().match(keyName): # we do not issue certs for keys outside of our network return None keyDer = Blob(message.command.keyBits) keyType = message.command.keyType try: self._identityStorage.addKey(keyName, keyType, keyDer) except SecurityException: # assume this is due to already existing? pass certificate = self._identityManager.generateCertificateForKey(keyName) self._keyChain.sign(certificate, self.getDefaultCertificateName()) # store it for later use + verification self._identityStorage.addCertificate(certificate) self._policyManager._certificateCache.insertCertificate(certificate) return certificate ###### # Device Capabilities ###### def _updateDeviceCapabilities(self, interest): """ Take the received capabilities update interest and update our directory listings. """ # we assume the sender is the one who signed the interest... signature = self._policyManager._extractSignature(interest) certificateName = signature.getKeyLocator().getKeyName() senderIdentity = IdentityCertificate.certificateNameToPublicKeyName(certificateName).getPrefix(-1) self.log.info('Updating capabilities for {}'.format(senderIdentity.toUri())) # get the params from the interest name messageComponent = interest.getName().get(self.prefix.size()+1) message = UpdateCapabilitiesCommandMessage() ProtobufTlv.decode(message, messageComponent.getValue()) # we remove all the old capabilities for the sender tempDirectory = defaultdict(list) for keyword in self._directory: tempDirectory[keyword] = [cap for cap in self._directory[keyword] if not senderIdentity.match(Name(cap['name']))] # then we add the ones from the message for capability in message.capabilities: capabilityPrefix = Name() for component in capability.commandPrefix.components: capabilityPrefix.append(component) commandUri = capabilityPrefix.toUri() if not senderIdentity.match(capabilityPrefix): self.log.error("Node {} tried to register another prefix: {} - ignoring update".format( senderIdentity.toUri(),commandUri)) else: for keyword in capability.keywords: allUris = [info['name'] for info in tempDirectory[keyword]] if capabilityPrefix not in allUris: listing = {'signed':capability.needsSignature, 'name':commandUri} tempDirectory[keyword].append(listing) self._directory= tempDirectory def _prepareCapabilitiesList(self, interestName): """ Responds to a directory listing request with JSON """ dataName = Name(interestName).append(Name.Component.fromNumber(int(time.time()))) response = Data(dataName) response.setContent(json.dumps(self._directory)) return response ##### # Interest handling #### def _onCommandReceived(self, prefix, interest, transport, prefixId): """ """ interestName = interest.getName() #if it is a certificate name, serve the certificate foundCert = self._identityStorage.getCertificate(interestName) if foundCert is not None: self.log.debug("Serving certificate request") transport.send(foundCert.wireEncode().buf()) return afterPrefix = interestName.get(prefix.size()).toEscapedString() if afterPrefix == "listDevices": #compose device list self.log.debug("Received device list request") response = self._prepareCapabilitiesList(interestName) self.sendData(response, transport) elif afterPrefix == "certificateRequest": #build and sign certificate self.log.debug("Received certificate request") self._handleCertificateRequest(interest, transport) elif afterPrefix == "updateCapabilities": # needs to be signed! self.log.debug("Received capabilities update") def onVerifiedCapabilities(interest): response = Data(interest.getName()) response.setContent(str(time.time())) self.sendData(response, transport) self._updateDeviceCapabilities(interest) self._keyChain.verifyInterest(interest, onVerifiedCapabilities, self.verificationFailed) else: response = Data(interest.getName()) response.setContent("500") response.getMetaInfo().setFreshnessPeriod(1000) transport.send(response.wireEncode().buf()) def onStartup(self): # begin taking add requests self.loop.call_soon(self.displayMenu) self.loop.add_reader(stdin, self.handleUserInput) def displayMenu(self): menuStr = "\n" menuStr += "P)air a new device with serial and PIN\n" menuStr += "D)irectory listing\n" menuStr += "E)xpress an interest\n" menuStr += "Q)uit\n" print(menuStr) print ("> ", end="") stdout.flush() def listDevices(self): menuStr = '' for capability, commands in self._directory.items(): menuStr += '{}:\n'.format(capability) for info in commands: signingStr = 'signed' if info['signed'] else 'unsigned' menuStr += '\t{} ({})\n'.format(info['name'], signingStr) print(menuStr) self.loop.call_soon(self.displayMenu) def onInterestTimeout(self, interest): print('Interest timed out: {}'.interest.getName().toUri()) def onDataReceived(self, interest, data): print('Received data named: {}'.format(data.getName().toUri())) print('Contents:\n{}'.format(data.getContent().toRawStr())) def expressInterest(self): try: interestName = input('Interest name: ') if len(interestName): toSign = input('Signed? (y/N): ').upper().startswith('Y') interest = Interest(Name(interestName)) interest.setInterestLifetimeMilliseconds(5000) interest.setChildSelector(1) if (toSign): self.face.makeCommandInterest(interest) self.face.expressInterest(interest, self.onDataReceived, self.onInterestTimeout) else: print("Aborted") except KeyboardInterrupt: print("Aborted") finally: self.loop.call_soon(self.displayMenu) def beginPairing(self): try: deviceSerial = input('Device serial: ') devicePin = input('PIN: ') deviceSuffix = input('Node name: ') except KeyboardInterrupt: print('Pairing attempt aborted') else: if len(deviceSerial) and len(devicePin) and len(deviceSuffix): self._addDeviceToNetwork(deviceSerial, Name(deviceSuffix), devicePin.decode('hex')) else: print('Pairing attempt aborted') finally: self.loop.call_soon(self.displayMenu) def handleUserInput(self): inputStr = stdin.readline().upper() if inputStr.startswith('D'): self.listDevices() elif inputStr.startswith('P'): self.beginPairing() elif inputStr.startswith('E'): self.expressInterest() elif inputStr.startswith('Q'): self.stop() else: self.loop.call_soon(self.displayMenu)
rp.setName(dataPrefix) rp.setStartBlockId(0) interest = Interest( Name("/example/repo/1").append("insert").append(rp.wireEncode())) 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) # Make a Face just so that we can sign the interest. face = Face("localhost") face.setCommandSigningInfo(keyChain, certificateName) face.makeCommandInterest(interest) callbacks = Callbacks() print interest.getName().toUri() face.expressInterest(interest, callbacks.onData, callbacks.onTimeout)
class Chat(object): def __init__(self, screenName, chatRoom, hubPrefix, face, keyChain, certificateName): self._screenName = screenName self._chatRoom = chatRoom self._face = face self._keyChain = keyChain self._certificateName = certificateName self._messageCache = [] # of CachedMessage self._roster = [] # of str self._maxMessageCacheLength = 100 self._isRecoverySyncState = True self._syncLifetime = 5000.0 # milliseconds # This should only be called once, so get the random string here. self._chatPrefix = Name(hubPrefix).append(self._chatRoom).append( self._getRandomString()) session = int(round(self.getNowMilliseconds() / 1000.0)) self._userName = self._screenName + str(session) self._sync = ChronoSync2013( self._sendInterest, self._initial, self._chatPrefix, Name("/ndn/broadcast/ChronoChat-0.3").append(self._chatRoom), session, face, keyChain, certificateName, self._syncLifetime, onRegisterFailed) face.registerPrefix(self._chatPrefix, self._onInterest, onRegisterFailed) def sendMessage(self, chatMessage): """ Send a chat message. """ if len(self._messageCache) == 0: self._messageCacheAppend(chatbuf_pb2.ChatMessage.JOIN, "xxx") # Ignore an empty message. # Forming Sync Data Packet. if chatMessage != "": self._sync.publishNextSequenceNo() self._messageCacheAppend(chatbuf_pb2.ChatMessage.CHAT, chatMessage) print(self._screenName + ": " + chatMessage) def leave(self): """ Send the leave message and leave. """ self._sync.publishNextSequenceNo() self._messageCacheAppend(chatbuf_pb2.ChatMessage.LEAVE, "xxx") @staticmethod def getNowMilliseconds(): """ Get the current time in milliseconds. :return: The current time in milliseconds since 1/1/1970, including fractions of a millisecond. :rtype: float """ return time.time() * 1000.0 def _initial(self): """ Push the JOIN message in to the messageCache_, update roster and start the heartbeat. """ # Set the heartbeat timeout using the Interest timeout mechanism. The # heartbeat() function will call itself again after a timeout. # TODO: Are we sure using a "/local/timeout" interest is the best future call # approach? timeout = Interest(Name("/local/timeout")) timeout.setInterestLifetimeMilliseconds(60000) self._face.expressInterest(timeout, self._dummyOnData, self._heartbeat) try: self._roster.index(self._userName) except ValueError: self._roster.append(self._userName) print("Member: " + self._screenName) print(self._screenName + ": Join") self._messageCacheAppend(chatbuf_pb2.ChatMessage.JOIN, "xxx") def _sendInterest(self, syncStates, isRecovery): """ Send a Chat Interest to fetch chat messages after the user gets the Sync data packet back but will not send interest. """ # This is used by _onData to decide whether to display the chat messages. self._isRecoverySyncState = isRecovery sendList = [] # of str sessionNoList = [] # of int sequenceNoList = [] # of int for j in range(len(syncStates)): syncState = syncStates[j] nameComponents = Name(syncState.getDataPrefix()) tempName = nameComponents.get(-1).toEscapedString() sessionNo = syncState.getSessionNo() if not tempName == self._screenName: index = -1 for k in range(len(sendList)): if sendList[k] == syncState.getDataPrefix(): index = k break if index != -1: sessionNoList[index] = sessionNo sequenceNoList[index] = syncState.getSequenceNo() else: sendList.append(syncState.getDataPrefix()) sessionNoList.append(sessionNo) sequenceNoList.append(syncState.getSequenceNo()) for i in range(len(sendList)): uri = (sendList[i] + "/" + str(sessionNoList[i]) + "/" + str(sequenceNoList[i])) interest = Interest(Name(uri)) interest.setInterestLifetimeMilliseconds(self._syncLifetime) self._face.expressInterest(interest, self._onData, self._chatTimeout) def _onInterest(self, prefix, interest, face, interestFilterId, filter): """ Send back a Chat Data Packet which contains the user's message. """ content = chatbuf_pb2.ChatMessage() sequenceNo = int( interest.getName().get(self._chatPrefix.size() + 1).toEscapedString()) gotContent = False for i in range(len(self._messageCache) - 1, -1, -1): message = self._messageCache[i] if message.sequenceNo == sequenceNo: if message.messageType != chatbuf_pb2.ChatMessage.CHAT: # Use setattr because "from" is a reserved keyword. setattr(content, "from", self._screenName) content.to = self._chatRoom content.type = message.messageType content.timestamp = int(round(message.time / 1000.0)) else: setattr(content, "from", self._screenName) content.to = self._chatRoom content.type = message.messageType content.data = message.message content.timestamp = int(round(message.time / 1000.0)) gotContent = True break if gotContent: # TODO: Check if this works in Python 3. array = content.SerializeToString() data = Data(interest.getName()) data.setContent(Blob(array)) self._keyChain.sign(data, self._certificateName) try: face.putData(data) except Exception as ex: logging.getLogger(__name__).error( "Error in transport.send: %s", str(ex)) return def _onData(self, interest, data): """ Process the incoming Chat data. """ # TODO: Check if this works in Python 3. content = chatbuf_pb2.ChatMessage() content.ParseFromString(data.getContent().toRawStr()) if self.getNowMilliseconds() - content.timestamp * 1000.0 < 120000.0: # Use getattr because "from" is a reserved keyword. name = getattr(content, "from") prefix = data.getName().getPrefix(-2).toUri() sessionNo = int(data.getName().get(-2).toEscapedString()) sequenceNo = int(data.getName().get(-1).toEscapedString()) nameAndSession = name + str(sessionNo) l = 0 # Update roster. while l < len(self._roster): entry = self._roster[l] tempName = entry[0:len(entry) - 10] tempSessionNo = int(entry[len(entry) - 10:]) if (name != tempName and content.type != chatbuf_pb2.ChatMessage.LEAVE): l += 1 else: if name == tempName and sessionNo > tempSessionNo: self._roster[l] = nameAndSession break if l == len(self._roster): self._roster.append(nameAndSession) print(name + ": Join") # Set the alive timeout using the Interest timeout mechanism. # TODO: Are we sure using a "/local/timeout" interest is the best # future call approach? timeout = Interest(Name("/local/timeout")) timeout.setInterestLifetimeMilliseconds(120000) self._face.expressInterest( timeout, self._dummyOnData, self._makeAlive(sequenceNo, name, sessionNo, prefix)) # isRecoverySyncState_ was set by sendInterest. # TODO: If isRecoverySyncState_ changed, this assumes that we won't get # data from an interest sent before it changed. # Use getattr because "from" is a reserved keyword. if (content.type == chatbuf_pb2.ChatMessage.CHAT and not self._isRecoverySyncState and getattr(content, "from") != self._screenName): print(getattr(content, "from") + ": " + content.data) elif content.type == chatbuf_pb2.ChatMessage.LEAVE: # leave message try: n = self._roster.index(nameAndSession) if name != self._screenName: self._roster.pop(n) print(name + ": Leave") except ValueError: pass @staticmethod def _chatTimeout(interest): print("Timeout waiting for chat data") def _heartbeat(self, interest): """ This repeatedly calls itself after a timeout to send a heartbeat message (chat message type HELLO). This method has an "interest" argument because we use it as the onTimeout for Face.expressInterest. """ if len(self._messageCache) == 0: self._messageCacheAppend(chatbuf_pb2.ChatMessage.JOIN, "xxx") self._sync.publishNextSequenceNo() self._messageCacheAppend(chatbuf_pb2.ChatMessage.HELLO, "xxx") # Call again. # TODO: Are we sure using a "/local/timeout" interest is the best future call # approach? timeout = Interest(Name("/local/timeout")) timeout.setInterestLifetimeMilliseconds(60000) self._face.expressInterest(timeout, self._dummyOnData, self._heartbeat) def _makeAlive(self, tempSequenceNo, name, sessionNo, prefix): """ Return a function for onTimeout which calls _alive. """ def f(interest): self._alive(interest, tempSequenceNo, name, sessionNo, prefix) return f def _alive(self, interest, tempSequenceNo, name, sessionNo, prefix): """ This is called after a timeout to check if the user with prefix has a newer sequence number than the given tempSequenceNo. If not, assume the user is idle and remove from the roster and print a leave message. This method has an "interest" argument because we use it as the onTimeout for Face.expressInterest. """ sequenceNo = self._sync.getProducerSequenceNo(prefix, sessionNo) nameAndSession = name + sessionNo try: n = self._roster.index(nameAndSession) except ValueError: n = -1 if sequenceNo != -1 and n >= 0: if tempSequenceNo == sequenceNo: self._roster.pop(n) print(name + ": Leave") def _messageCacheAppend(self, messageType, message): """ Append a new CachedMessage to messageCache_, using given messageType and message, the sequence number from _sync.getSequenceNo() and the current time. Also remove elements from the front of the cache as needed to keep the size to _maxMessageCacheLength. """ self._messageCache.append(self._CachedMessage( self._sync.getSequenceNo(), messageType, message, self.getNowMilliseconds())) while len(self._messageCache) > self._maxMessageCacheLength: self._messageCache.pop(0) @staticmethod def _getRandomString(): """ Generate a random name for ChronoSync. """ seed = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789" result = "" for i in range(10): # Using % means the distribution isn't uniform, but that's OK. position = random.randrange(256) % len(seed) result += seed[position] return result @staticmethod def _dummyOnData(interest, data): """ This is a do-nothing onData for using expressInterest for timeouts. This should never be called. """ pass class _CachedMessage(object): def __init__(self, sequenceNo, messageType, message, time): self.sequenceNo = sequenceNo self.messageType = messageType self.message = message self.time = time
def test_uri_constructor(self): name = Name(self.expectedURI) self.assertEqual(name.size(),3, 'Constructed name has ' + str(name.size()) + ' components instead of 3') self.assertEqual(name.toUri(), self.expectedURI, 'URI is incorrect')
class SensorDataLogger: def __init__(self, data_interval): # connect to modbus self.master = modbus_tcp.TcpMaster("172.17.66.246", 502) # self.master.set_timeout(120) # in seconds # connect to local repo self.publisher = RepoSocketPublisher(12345) self.prefix = "/ndn/ucla.edu/bms/strathmore/data/demand" self.interval = data_interval # in seconds self.loadKey() def loadKey(self): self.identityStorage = MemoryIdentityStorage() self.privateKeyStorage = MemoryPrivateKeyStorage() self.keychain = KeyChain(IdentityManager(self.identityStorage, self.privateKeyStorage)) f = open(key_file, "r") self.key = RSA.importKey(f.read()) self.key_name = Name(bld_root).append(getKeyID(self.key)) key_pub_der = bytearray(self.key.publickey().exportKey(format="DER")) key_pri_der = bytearray(self.key.exportKey(format="DER")) self.identityStorage.addKey(self.key_name, KeyType.RSA, Blob(key_pub_der)) self.privateKeyStorage.setKeyPairForKeyName(self.key_name, key_pub_der, key_pri_der) self.cert_name = self.key_name.getSubName(0, self.key_name.size() - 1).append( "KEY").append(self.key_name[-1]).append("ID-CERT").append("0") print 'KeyName = ' + self.key_name.toUri() print 'CertName = ' + self.cert_name.toUri() def publishData(self, key, key_ts, payload, timestamp): data = Data(Name(self.prefix).append(bytearray(timestamp))) iv = Random.new().read(AES.block_size) encryptor = AES.new(key, AES.MODE_CBC, iv) data.setContent(bytearray(key_ts + iv + encryptor.encrypt(pad(json.dumps(payload))))) data.getMetaInfo().setFreshnessPeriod(5000) self.keychain.sign(data, self.cert_name) self.publisher.put(data) #print payload #print data.getName().toUri() def run(self): key_ts = struct.pack('!Q', int(time.time() * 1000)) key = Random.new().read(32) kds_count = -1 while (True): # KDS kds_count = kds_count + 1 if kds_count % 120 == 0: key_ts = struct.pack("!Q", int(time.time() * 1000)) key = Random.new().read(32) kds_thread = kds.SimpleKDSPublisher(Name(bld_root), self.keychain, self.cert_name, key, key_ts) kds_thread.start() kds_count = 0 # Data now = int(time.time() * 1000) # in milliseconds a = self.master.execute(100, cst.READ_HOLDING_REGISTERS, 166, 1) b = self.master.execute(100, cst.READ_HOLDING_REGISTERS, 167, 1) vln = (b[0] << 16) + a[0] c = self.master.execute(1, cst.READ_HOLDING_REGISTERS, 150, 1) la = c[0] payload = {'ts': now, 'vlna': vln, 'la': la} timestamp = struct.pack("!Q", now) # timestamp is in milliseconds self.publishData(key, key_ts, payload, timestamp) time.sleep(self.interval)
class SyncBasedDiscovery(object): """ Sync (digest exchange) based name discovery. Discovery maintains a list of discovered, and a list of hosted objects. Calls observer.onStateChanged(name, msgType, msg) when an entity is discovered or removed. Uses serializer.serialize(entityObject) to serialize a hosted entity's entityInfo into string. :param face: :type face: Face :param keyChain: :type keyChain: KeyChain :param certificateName: :type certificateName: Name :param syncPrefix: :type syncPrefix: Name :param observer: :type observer: ExternalObserver :param serializer: :type serializer: EntitySerializer """ def __init__(self, face, keyChain, certificateName, syncPrefix, observer, serializer, syncDataFreshnessPeriod = 4000, initialDigest = "00", syncInterestLifetime = 4000, syncInterestMinInterval = 500, timeoutCntThreshold = 3, maxResponseWaitPeriod = 2000, minResponseWaitPeriod = 400, entityDataFreshnessPeriod = 10000): self._face = face self._keyChain = keyChain self._syncPrefix = Name(syncPrefix) self._objects = dict() self._hostedObjects = dict() self._memoryContentCache = MemoryContentCache(self._face) self._certificateName = Name(certificateName) self._currentDigest = initialDigest self._syncDataFreshnessPeriod = syncDataFreshnessPeriod self._initialDigest = initialDigest self._syncInterestLifetime = syncInterestLifetime self._syncInterestMinInterval = syncInterestMinInterval self._timeoutCntThreshold = timeoutCntThreshold self._entityDataFreshnessPeriod = entityDataFreshnessPeriod # TODO: policy manager etc setup #self._maxResponseWaitPeriod = maxResponseWaitPeriod #self._minResponseWaitPeriod = minResponseWaitPeriod self._observer = observer self._serializer = serializer self._numOutstandingInterest = 0 return """ Public facing interface """ def start(self): """ Starts the discovery """ interest = Interest(Name(self._syncPrefix).append(self._initialDigest)) interest.setMustBeFresh(True) interest.setInterestLifetimeMilliseconds(self._syncInterestLifetime) self._face.expressInterest(interest, self.onSyncData, self.onSyncTimeout) self._numOutstandingInterest += 1 if __debug__: print("Express interest: " + interest.getName().toUri()) return def stop(self): """ Stops the discovery """ # TODO: interest expression and data response flag self._memoryContentCache.unregisterAll() return def getHostedObjects(self): return self._hostedObjects def getObjects(self): return self._objects def publishObject(self, name, entityInfo): """ Adds another object and registers prefix for that object's name :param name: the object's name string :type name: str :param entityInfo: the application given entity info to describe this object name with :type entityInfo: EntityInfo """ # If this is the first object we host, we register for sync namespace: meaning a participant not hosting anything # is only "listening" for sync, and will not help in the sync process if len(self._hostedObjects.keys()) == 0: self._memoryContentCache.registerPrefix(self._syncPrefix, self.onRegisterFailed, self.onSyncInterest) if self.addObject(name, False): # Do not add itself to contentCache if its currentDigest is "00". if self._currentDigest != self._initialDigest: self.contentCacheAddSyncData(Name(self._syncPrefix).append(self._currentDigest)) self.updateDigest() interest = Interest(Name(self._syncPrefix).append(self._currentDigest)) interest.setInterestLifetimeMilliseconds(self._syncInterestLifetime) interest.setMustBeFresh(True) self._face.expressInterest(interest, self.onSyncData, self.onSyncTimeout) self._numOutstandingInterest += 1 self._hostedObjects[name] = entityInfo self.contentCacheAddEntityData(name, entityInfo) self.notifyObserver(name, "ADD", "") # TODO: should the user configure this prefix as well? self._memoryContentCache.registerPrefix(Name(name), self.onRegisterFailed, self.onEntityDataNotFound) else: if __debug__: print("Item with this name already added") return def removeHostedObject(self, name): """ Removes a locally hosted object :param name: the object's name string :type name: str :return: whether removal's successful or not :rtype: bool """ if name in self._hostedObjects: del self._hostedObjects[name] if len(self._hostedObjects) == 0: self._memoryContentCache.unregisterAll() if self.removeObject(name): return True else: if __debug__: print("Hosted item not in objects list") return False else: return False """ Internal functions """ def contentCacheAddEntityData(self, name, entityInfo): content = self._serializer.serialize(entityInfo) data = Data(Name(name)) data.setContent(content) data.getMetaInfo().setFreshnessPeriod(self._entityDataFreshnessPeriod) self._keyChain.sign(data, self._certificateName) self._memoryContentCache.add(data) print "added entity to cache: " + data.getName().toUri() + "; " + data.getContent().toRawStr() def contentCacheAddSyncData(self, dataName): sortedKeys = sorted(self._objects.keys()) content = "" for key in sortedKeys: content += key + "\n" content.strip() data = Data(Name(dataName)) data.setContent(content) data.getMetaInfo().setFreshnessPeriod(self._syncDataFreshnessPeriod) self._keyChain.sign(data, self._certificateName) # adding this data to memoryContentCache should satisfy the pending interest self._memoryContentCache.add(data) def onSyncInterest(self, prefix, interest, face, interestFilterId, filter): if interest.getName().size() != self._syncPrefix.size() + 1: # Not an interest for us return digest = interest.getName().get(-1).toEscapedString() self.updateDigest() if digest != self._currentDigest: # Wait a random period before replying; rationale being that "we are always doing ChronoSync recovery...this is the recovery timer but randomized" # Consider this statement: we are always doing ChronoSync recovery # TODO: this has the problem of potentially answering with wrong data, there will be more interest exchanges needed for the lifetime duration of one wrong answer # Consider appending "answerer" as the last component of data name? # TODO2: don't see why we should wait here self.replySyncInterest(interest, digest) #dummyInterest = Interest(Name("/local/timeout1")) #dummyInterest.setInterestLifetimeMilliseconds(random.randint(self._minResponseWaitPeriod, self._maxResponseWaitPeriod)) #self._face.expressInterest(dummyInterest, self.onDummyData, lambda a : self.replySyncInterest(a, digest)) return def replySyncInterest(self, interest, receivedDigest): self.updateDigest() if receivedDigest != self._currentDigest: # TODO: one participant may be answering with wrong info: scenario: 1 has {a}, 2 has {b} # 2 gets 1's {a} and asks again before 1 gets 2's {b}, 2 asks 1 with the digest of {a, b}, 1 will # create a data with the content {a} for the digest of {a, b}, and this data will be able to answer # later steady state interests from 2 until it expires (and by which time 1 should be updated with # {a, b} as well) self.contentCacheAddSyncData(Name(self._syncPrefix).append(receivedDigest)) return def onSyncData(self, interest, data): # TODO: do verification first if __debug__: print("Got sync data; name: " + data.getName().toUri() + "; content: " + data.getContent().toRawStr()) content = data.getContent().toRawStr().split('\n') self._numOutstandingInterest -= 1 for itemName in content: if itemName not in self._objects: if itemName != "": self.onReceivedSyncData(itemName) # Hack for re-expressing sync interest after a short interval dummyInterest = Interest(Name("/local/timeout")) dummyInterest.setInterestLifetimeMilliseconds(self._syncInterestLifetime) self._face.expressInterest(dummyInterest, self.onDummyData, self.onSyncTimeout) self._numOutstandingInterest += 1 return def onSyncTimeout(self, interest): newInterest = Interest(Name(self._syncPrefix).append(self._currentDigest)) newInterest.setInterestLifetimeMilliseconds(self._syncInterestLifetime) newInterest.setMustBeFresh(True) self._numOutstandingInterest -= 1 print "re-expressing: " + newInterest.getName().toUri() if self._numOutstandingInterest <= 0: self._face.expressInterest(newInterest, self.onSyncData, self.onSyncTimeout) self._numOutstandingInterest += 1 return """ Handling received sync data: express entity interest """ def onReceivedSyncData(self, itemName): interest = Interest(Name(itemName)) interest.setInterestLifetimeMilliseconds(4000) interest.setMustBeFresh(False) self._face.expressInterest(interest, self.onEntityData, self.onEntityTimeout) return def onEntityTimeout(self, interest): print "Item interest times out: " + interest.getName().toUri() return def onEntityData(self, interest, data): self.addObject(interest.getName().toUri(), True) self.notifyObserver(interest.getName().toUri(), "ADD", ""); dummyInterest = Interest(Name("/local/timeout")) dummyInterest.setInterestLifetimeMilliseconds(4000) self._face.expressInterest(dummyInterest, self.onDummyData, lambda a : self.expressHeartbeatInterest(a, interest)) return def expressHeartbeatInterest(self, dummyInterest, entityInterest): newInterest = Interest(entityInterest) newInterest.refreshNonce() self._face.expressInterest(newInterest, self.onHeartbeatData, self.onHeartbeatTimeout) def onHeartbeatData(self, interest, data): self.resetTimeoutCnt(interest.getName().toUri()) dummyInterest = Interest(Name("/local/timeout")) dummyInterest.setInterestLifetimeMilliseconds(4000) self._face.expressInterest(dummyInterest, self.onDummyData, lambda a : self.expressHeartbeatInterest(a, interest)) def onHeartbeatTimeout(self, interest): if self.incrementTimeoutCnt(interest.getName().toUri()): print "Remove: " + interest.getName().toUri() + " because of consecutive timeout cnt exceeded" else: newInterest = Interest(interest.getName()) newInterest.setInterestLifetimeMilliseconds(4000) self._face.expressInterest(newInterest, self.onHeartbeatData, self.onHeartbeatTimeout) def onDummyData(self, interest, data): if __debug__: print("Unexpected reply to dummy interest: " + data.getContent().toRawStr()) return def expressSyncInterest(self, interest): self.onSyncTimeout(interest) return def addObject(self, name, update): if name in self._objects: return False else: self._objects[name] = {"timeout_count": 0} if update: self.updateDigest() return True def removeObject(self, name): if name in self._objects: del self._objects[name] self.notifyObserver(name, "REMOVE", "") self.contentCacheAddSyncData(Name(self._syncPrefix).append(self._currentDigest)) self.updateDigest() return True else: return False def updateDigest(self): # TODO: for now, may change the format of the list encoding for easier cross language compatibility if len(self._objects) > 0: m = hashlib.sha256() for item in sorted(self._objects.keys()): m.update(item) self._currentDigest = str(m.hexdigest()) else: self._currentDigest = self._initialDigest return def incrementTimeoutCnt(self, name): if name in self._objects: self._objects[name]["timeout_count"] += 1 if self._objects[name]["timeout_count"] >= self._timeoutCntThreshold: return self.removeObject(name) else: return False else: return False def resetTimeoutCnt(self, name): if name in self._objects: self._objects[name]["timeout_count"] = 0 return True else: return False def notifyObserver(self, name, msgType, msg): self._observer.onStateChanged(name, msgType, msg) return def onRegisterFailed(self, prefix): if __debug__: print("Prefix registration failed: " + prefix.toUri()) return def onEntityDataNotFound(self, prefix, interest, face, interestFilterId, filter): name = interest.getName().toUri() if name in self._hostedObjects: content = self._serializer.serialize(self._hostedObjects[name]) data = Data(Name(name)) data.setContent(content) data.getMetaInfo().setFreshnessPeriod(self._entityDataFreshnessPeriod) self._keyChain.sign(data, self._certificateName) self._face.putData(data) return
class RepoPublisher: def __init__(self, repoPrefix, dataPrefix, dataSuffix, keychain=None): self.currentInsertion = -1 self.currentStatus = -1 self.face = None self.loop = None self.repoPrefix = Name(repoPrefix) self.dataName = Name(dataPrefix).append(dataSuffix) self.dataPrefix = Name(dataPrefix) if keychain is not None: self.keychain = keychain else: self.keychain = KeyChain() self.certificateName = self.keychain.getDefaultCertificateName() self.failureCount = 0 self.successCount = 0 self.processIdToVersion = {} def onRegisterFailed(self): logger.error("Could not register data publishing face!") self.stop() def versionFromCommandMessage(self, component): command = RepoCommandParameterMessage() try: ProtobufTlv.decode(command, component.getValue()) except Exception as e: logger.warn(e) # last component of name to insert is version versionStr = command.repo_command_parameter.name.component[-1] return versionStr def stop(self): self.loop.close() self.face.shutdown() def onPublishInterest(self, prefix, interest, transport, pxID): ''' For publishing face ''' # just make up some data and return it interestName = interest.getName() logger.info("Interest for " + interestName.toUri()) ## CURRENTLY ASSUMES THERE'S A VERSION+SEGMENT SUFFIX! dataName = Name(interestName) ts = (time.time()) segmentId = 0 #try: # segmentId = interestName.get(-1).toSegment() #except: #logger.debug("Could not find segment id!") #dataName.appendSegment(segmentId) versionStr = str(interestName.get(-2).getValue()) logger.debug('Publishing ' + versionStr + ' @ ' + str(ts)) d = Data(dataName) content = "(" + str(ts) + ") Data named " + dataName.toUri() d.setContent(content) d.getMetaInfo().setFinalBlockID(segmentId) d.getMetaInfo().setFreshnessPeriod(1000) self.keychain.sign(d, self.certificateName) encodedData = d.wireEncode() stats.insertDataForVersion(versionStr, {'publish_time': time.time()}) transport.send(encodedData.toBuffer()) #yield from asyncio.sleep() def generateVersionedName(self): fullName = Name(self.dataName) # currently we need to provide the version ourselves when we # poke the repo ts = int(time.time()*1000) fullName.appendVersion(int(ts)) return fullName def onTimeout(self, prefix): logger.warn('Timeout waiting for '+prefix.toUri()) def start(self): self.loop = asyncio.new_event_loop() self.face = ThreadsafeFace(self.loop, "") asyncio.set_event_loop(self.loop) self.face.setCommandSigningInfo(self.keychain, self.certificateName) self.face.registerPrefix(self.dataPrefix,self.onPublishInterest, self.onRegisterFailed) try: self.loop.call_soon(self.kickRepo) self.loop.run_forever() finally: self.stop() def kickRepo(self): # command the repo to insert a new bit of data fullName = self.generateVersionedName() versionStr = str(fullName.get(-1).getValue()) command = self.createInsertInterest(fullName) logger.debug('inserting: ' + versionStr) self.face.makeCommandInterest(command) def timeoutLoop(interest): logger.warn('Timed out on ' + interest.toUri()) self.face.expressInterest(command, self.onCommandData, self.onTimeout) self.face.expressInterest(command, self.onCommandData, timeoutLoop) stats.insertDataForVersion(versionStr, {'insert_request':time.time()}) def checkInsertion(self, versionStr, processID): fullName = Name(self.dataName).append(Name.fromEscapedString(versionStr)) checkCommand = self.createCheckInterest(fullName, processID) self.face.makeCommandInterest(checkCommand) def timeoutLoop(interest): logger.warn('Timed out waiting on: '+interest.toUri()) self.face.expressInterest(checkCommand, self.onCommandData, self.onTimeout) self.face.expressInterest(checkCommand, self.onCommandData, timeoutLoop) def createInsertInterest(self, fullName): ''' For poking the repo ''' # we have to do the versioning before we poke the repo interestName = Name(fullName) logger.debug('Creating insert interest for: '+interestName.toUri()) insertionName = Name(self.repoPrefix).append('insert') commandParams = RepoCommandParameterMessage() for i in range(interestName.size()): commandParams.repo_command_parameter.name.component.append(interestName.get(i).getValue().toRawStr()) commandParams.repo_command_parameter.start_block_id = 0 commandParams.repo_command_parameter.end_block_id = 0 commandName = insertionName.append(ProtobufTlv.encode(commandParams)) interest = Interest(commandName) interest.setInterestLifetimeMilliseconds(2000) return interest def createCheckInterest(self, fullName, checkNum): insertionName = Name(self.repoPrefix).append('insert check') commandParams = RepoCommandParameterMessage() interestName = Name(fullName) commandParams.repo_command_parameter.process_id = checkNum for i in range(interestName.size()): commandParams.repo_command_parameter.name.component.append(str(interestName.get(i).getValue())) commandName = insertionName.append(ProtobufTlv.encode(commandParams)) interest = Interest(commandName) return interest def onCommandData(self, interest, data): # assume it's a command response now = time.time() response = RepoCommandResponseMessage() ProtobufTlv.decode(response, data.getContent()) self.currentStatus = response.repo_command_response.status_code self.currentInsertion = response.repo_command_response.process_id logger.debug("Response status code: " + str(self.currentStatus) + ", process id: " + str(self.currentInsertion) + ", insert #" + str(response.repo_command_response.insert_num)) command_idx = self.repoPrefix.size() # we also need to keep track of the mapping from version to processID for stats commandName = interest.getName().get(command_idx).getValue().toRawStr() if commandName == 'insert check': try: versionStr = self.processIdToVersion[self.currentInsertion] if self.currentStatus == 200: stats.insertDataForVersion(versionStr, {'insert_complete': now}) self.loop.call_soon(self.kickRepo) elif self.currentStatus >= 400: self.failureCount += 1 self.loop.call_soon(self.kickRepo) else: self.loop.call_soon(self.checkInsertion, versionStr, self.currentInserion) except: logger.warn('Missing version for process ID {}'.format(self.currentInsertion)) elif commandName == 'insert': if self.currentStatus == 100: versionStr = self.versionFromCommandMessage(interest.getName().get(command_idx+1)) self.processIdToVersion[self.currentInsertion] = versionStr stats.insertDataForVersion(versionStr, {'insert_begin': now}) self.loop.call_soon(self.checkInsertion, versionStr, self.currentInsertion) else: self.failureCount += 1 self.loop.call_soon(self.kickRepo)
def main(): interest = Interest() interest.wireDecode(TlvInterest) dump("Interest:") dumpInterest(interest) # Set the name again to clear the cached encoding so we encode again. interest.setName(interest.getName()) encoding = interest.wireEncode() dump("") dump("Re-encoded interest", encoding.toHex()) reDecodedInterest = Interest() reDecodedInterest.wireDecode(encoding) dump("Re-decoded Interest:") dumpInterest(reDecodedInterest) freshInterest = (Interest( Name("/ndn/abc")).setMustBeFresh(False).setMinSuffixComponents( 4).setMaxSuffixComponents(6).setInterestLifetimeMilliseconds( 30000).setChildSelector(1).setMustBeFresh(True)) freshInterest.getKeyLocator().setType(KeyLocatorType.KEY_LOCATOR_DIGEST) freshInterest.getKeyLocator().setKeyData( bytearray([ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F ])) freshInterest.getExclude().appendComponent(Name("abc")[0]).appendAny() dump(freshInterest.toUri()) 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) # Make a Face just so that we can sign the interest. face = Face("localhost") face.setCommandSigningInfo(keyChain, certificateName) face.makeCommandInterest(freshInterest) reDecodedFreshInterest = Interest() reDecodedFreshInterest.wireDecode(freshInterest.wireEncode()) dump("") dump("Re-decoded fresh Interest:") dumpInterest(reDecodedFreshInterest) keyChain.verifyInterest(reDecodedFreshInterest, makeOnVerified("Freshly-signed Interest"), makeOnVerifyFailed("Freshly-signed Interest"))
class RegisterSongList(object): def __init__(self,sFace,sLoop,skeychain,scertificateName,prefix="/ndn/edu/ucla/remap/music/list"): logging.basicConfig() self.device = "PC1" self.deviceComponent = Name.Component(self.device) self.excludeDevice = None self.listPrefix = Name(prefix) self.address = "" self._isStopped = True self.face = sFace self.loop = sLoop self.keychain = skeychain self.certificateName = scertificateName def start(self): self.face.registerPrefix(self.listPrefix,self.onInterest,self.onRegisterFailed) def stop(self): self.loop.close() self.face.shutdown() self.face = None sys.exit(1) def signData(self,data): data.setSignature(Sha256WithRsaSignature()) def onInterest(self, prefix, interest, transport, registeredPrefixId): initInterest = Name(interest.getName()) print "interest name:",initInterest.toUri() d = Data(interest.getName().append(self.deviceComponent)) try: if(initInterest == self.listPrefix): #receive the .../list interest currentString = ','.join(currentList) d.setContent(currentString) encodedData = d.wireEncode() transport.send(encodedData.toBuffer()) print d.getName().toUri() print d.getContent() else: self.excludeDevice = initInterest.get(self.listPrefix.size()) excDevice = self.excludeDevice.toEscapedString() if(excDevice != str("exc")+self.device): # receive the .../list/excOther interest currentString = ','.join(currentList) d.setContent(currentString) encodedData = d.wireEncode() transport.send(encodedData.toBuffer()) print d.getName().toUri() print d.getContent() else: # receive the .../list/excMe interest print"controller has exclude me, I have to remove register!" self.face.removeRegisteredPrefix(registeredPrefixId) time.sleep(30) print"register again" self.face.registerPrefix(self.listPrefix,self.onInterest,self.onRegisterFailed) except KeyboardInterrupt: print "key interrupt" sys.exit(1) except Exception as e: print e d.setContent("Bad command\n") finally: self.keychain.sign(d,self.certificateName) def onRegisterFailed(self, prefix): self.log.error("Could not register " + prefix.toUri()) self.stop()
def main(): # Uncomment these lines to print ChronoSync debug messages. # logging.getLogger('').addHandler(logging.StreamHandler(sys.stdout)) # logging.getLogger('').setLevel(logging.INFO) defaultUserPrefix = "com/newspaper/USER/bob" userPrefix = promptAndInput("Enter user prefix: [" + defaultUserPrefix + "]") if userPrefix == "": userPrefix = defaultUserPrefix defaultNamespacePrefix = "/ndn/hackathon/cnl-demo/slides" #"com/newspaper" namespacePrefix = promptAndInput("Enter namespace prefix [" + defaultNamespacePrefix + "]: ") if namespacePrefix == "": namespacePrefix = defaultNamespacePrefix host = "localhost" #"memoria.ndn.ucla.edu" print("Connecting to " + host) print("") # Set up the key chain. face = Face(host) identityStorage = MemoryIdentityStorage() privateKeyStorage = MemoryPrivateKeyStorage() keyChain = KeyChain(IdentityManager(identityStorage, privateKeyStorage), NoVerifyPolicyManager()) keyChain.setFace(face) 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) face.setCommandSigningInfo(keyChain, certificateName) newspaper = Namespace(namespacePrefix) def onContentSet(namespace, contentNamespace, callbackId): global currentSlideName if contentNamespace == namespace: print("content size "+str(contentNamespace.content.size())) currentSlideName = contentNamespace.getName() displayImage(contentNamespace.content.toRawStr(), contentNamespace.getName().toUri()) # dump("Got segmented content ", contentNamespace.content.toRawStr()) def onNewName(namespace, addedNamespace, callbackId): print("namespace ("+addedNamespace.getName().toUri()+") added to "+namespace.getName().toUri()) if addedNamespace.getName().get(-1).isSegment() and addedNamespace.getName().get(-1).toSegment() == 0: addedNamespace.getParent().addOnContentSet(onContentSet) SegmentedContent(addedNamespace.getParent()).start() newspaper.addOnNameAdded(onNewName) newspaper.setFace(face) namesync = NameSyncHandler(newspaper, userPrefix, keyChain, certificateName) # The main loop to process Chat while checking stdin to send a message. print("Enter your namespace update. To quit, enter \"exit\".") def process(): # while True: # Set timeout to 0 for an immediate check. isReady, _, _ = select.select([sys.stdin], [], [], 0) if len(isReady) != 0: input = promptAndInput("") if input == "exit": stopGui() # We will send the leave message below. # break # before producer has namespace.publish call, we manually call onNameAdded as a hack to publish namesync.onNameAdded(None, Namespace(Name(input)), 0, True) face.processEvents() if root: root.after(100, process) def leftKey(event): global currentSlideName allVersions = newspaper.getChildComponents() currentVersion = currentSlideName[-1] selected = allVersions[0] for c in allVersions: print(str(c.toVersion())) if c.toVersion() == currentVersion.toVersion(): break selected = c currentSlideName = Name(newspaper.getName()).append(selected) displayImage(newspaper.getChild(selected).content.toRawStr(), currentSlideName.toUri()) def rightKey(event): global currentSlideName allVersions = newspaper.getChildComponents() currentVersion = currentSlideName[-1] selected = None for c in allVersions[::-1]: if c.toVersion() == currentVersion.toVersion(): break selected = c if selected: currentSlideName = Name(newspaper.getName()).append(selected) displayImage(newspaper.getChild(selected).content.toRawStr(), currentSlideName.toUri()) else: print("no slides to show") runGui(process, leftKey, rightKey)
def benchmarkEncodeDataSeconds(nIterations, useComplex, useCrypto): """ Loop to encode a data packet nIterations times. :param int nIterations: The number of iterations. :param bool useComplex: If true, use a large name, large content and all fields. If false, use a small name, small content and only required fields. :param bool useCrypto: If true, sign the data packet. If false, use a blank signature. :return: A tuple (duration, encoding) where duration is the number of seconds for all iterations and encoding is the wire encoding. :rtype: (float, Blob) """ if useComplex: # Use a large name and content. name = Name( "/ndn/ucla.edu/apps/lwndn-test/numbers.txt/%FD%05%05%E8%0C%CE%1D/%00" ) contentString = "" count = 1 contentString += "%d" % count count += 1 while len(contentString) < 1115: contentString += " %d" % count count += 1 content = Name.fromEscapedString(contentString) else: # Use a small name and content. name = Name("/test") content = Name.fromEscapedString("abc") finalBlockId = Name("/%00")[0] # 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)) privateKeyStorage.setKeyPairForKeyName(keyName, KeyType.RSA, DEFAULT_RSA_PUBLIC_KEY_DER, DEFAULT_RSA_PRIVATE_KEY_DER) # Set up signatureBits in case useCrypto is false. signatureBits = Blob(bytearray(256)) emptyBlob = Blob([]) start = getNowSeconds() for i in range(nIterations): data = Data(name) data.setContent(content) if useComplex: data.getMetaInfo().setFreshnessPeriod(1000) data.getMetaInfo().setFinalBlockId(finalBlockId) if useCrypto: # This sets the signature fields. keyChain.sign(data, certificateName) else: # Imitate IdentityManager.signByCertificate to set up the signature # fields, but don't sign. sha256Signature = data.getSignature() keyLocator = sha256Signature.getKeyLocator() keyLocator.setType(KeyLocatorType.KEYNAME) keyLocator.setKeyName(certificateName) sha256Signature.setSignature(signatureBits) encoding = data.wireEncode() finish = getNowSeconds() return (finish - start, encoding)
def test_content_key_request(self): prefix = Name("/prefix") suffix = Name("/a/b/c") expectedInterest = Name(prefix) expectedInterest.append(Encryptor.NAME_COMPONENT_READ) expectedInterest.append(suffix) expectedInterest.append(Encryptor.NAME_COMPONENT_E_KEY) cKeyName = Name(prefix) cKeyName.append(Encryptor.NAME_COMPONENT_SAMPLE) cKeyName.append(suffix) cKeyName.append(Encryptor.NAME_COMPONENT_C_KEY) timeMarker = Name("20150101T100000/20150101T120000") testTime1 = Schedule.fromIsoString("20150101T100001") testTime2 = Schedule.fromIsoString("20150101T110001") testTimeRounded1 = Name.Component("20150101T100000") testTimeRounded2 = Name.Component("20150101T110000") testTimeComponent2 = Name.Component("20150101T110001") # Create content keys required for this test case: for i in range(suffix.size()): self.createEncryptionKey(expectedInterest, timeMarker) expectedInterest = expectedInterest.getPrefix(-2).append( Encryptor.NAME_COMPONENT_E_KEY) expressInterestCallCount = [0] # Prepare a TestFace to instantly answer calls to expressInterest. class TestFace(object): def __init__(self, handleExpressInterest): self.handleExpressInterest = handleExpressInterest def expressInterest(self, interest, onData, onTimeout, onNetworkNack): return self.handleExpressInterest(interest, onData, onTimeout, onNetworkNack) def handleExpressInterest(interest, onData, onTimeout, onNetworkNack): expressInterestCallCount[0] += 1 interestName = Name(interest.getName()) interestName.append(timeMarker) self.assertTrue(interestName in self.encryptionKeys) onData(interest, self.encryptionKeys[interestName]) return 0 face = TestFace(handleExpressInterest) # Verify that the content key is correctly encrypted for each domain, and # the produce method encrypts the provided data with the same content key. testDb = Sqlite3ProducerDb(self.databaseFilePath) producer = Producer(prefix, suffix, face, self.keyChain, testDb) contentKey = [None] # Blob def checkEncryptionKeys(result, testTime, roundedTime, expectedExpressInterestCallCount): self.assertEqual(expectedExpressInterestCallCount, expressInterestCallCount[0]) self.assertEqual(True, testDb.hasContentKey(testTime)) contentKey[0] = testDb.getContentKey(testTime) params = EncryptParams(EncryptAlgorithmType.RsaOaep) for i in range(len(result)): key = result[i] keyName = key.getName() self.assertEqual(cKeyName, keyName.getSubName(0, 6)) self.assertEqual(keyName.get(6), roundedTime) self.assertEqual(keyName.get(7), Encryptor.NAME_COMPONENT_FOR) self.assertEqual(True, keyName.getSubName(8) in self.decryptionKeys) decryptionKey = self.decryptionKeys[keyName.getSubName(8)] self.assertEqual(True, decryptionKey.size() != 0) encryptedKeyEncoding = key.getContent() content = EncryptedContent() content.wireDecode(encryptedKeyEncoding) encryptedKey = content.getPayload() retrievedKey = RsaAlgorithm.decrypt(decryptionKey, encryptedKey, params) self.assertTrue(contentKey[0].equals(retrievedKey)) self.assertEqual(3, len(result)) # An initial test to confirm that keys are created for this time slot. contentKeyName1 = producer.createContentKey( testTime1, lambda keys: checkEncryptionKeys( keys, testTime1, testTimeRounded1, 3)) # Verify that we do not repeat the search for e-keys. The total # expressInterestCallCount should be the same. contentKeyName2 = producer.createContentKey( testTime2, lambda keys: checkEncryptionKeys( keys, testTime2, testTimeRounded2, 3)) # Confirm content key names are correct self.assertEqual(cKeyName, contentKeyName1.getPrefix(-1)) self.assertEqual(testTimeRounded1, contentKeyName1.get(6)) self.assertEqual(cKeyName, contentKeyName2.getPrefix(-1)) self.assertEqual(testTimeRounded2, contentKeyName2.get(6)) # Confirm that produce encrypts with the correct key and has the right name. testData = Data() producer.produce(testData, testTime2, Blob(DATA_CONTENT, False)) producedName = testData.getName() self.assertEqual(cKeyName.getPrefix(-1), producedName.getSubName(0, 5)) self.assertEqual(testTimeComponent2, producedName.get(5)) self.assertEqual(Encryptor.NAME_COMPONENT_FOR, producedName.get(6)) self.assertEqual(cKeyName, producedName.getSubName(7, 6)) self.assertEqual(testTimeRounded2, producedName.get(13)) dataBlob = testData.getContent() dataContent = EncryptedContent() dataContent.wireDecode(dataBlob) encryptedData = dataContent.getPayload() initialVector = dataContent.getInitialVector() params = EncryptParams(EncryptAlgorithmType.AesCbc, 16) params.setInitialVector(initialVector) decryptTest = AesAlgorithm.decrypt(contentKey[0], encryptedData, params) self.assertTrue(decryptTest.equals(Blob(DATA_CONTENT, False)))
def on_result_interest(self, _prefix, interest, face, _interest_filter_id, _filter_obj): # type: (Name, Interest, Face, int, InterestFilter) -> bool prefix = Name(SERVER_PREFIX).append(RESULT_PREFIX) if not prefix.isPrefixOf(interest.name): # Wrong prefix return False data_name = interest.name[prefix.size():] logging.info("On result interest: %s", data_name.toUri()) # key, stat = self._result_set_prefix_match(data_name) status = self.load_status(data_name) if status is None: # No such request self.nodata_reply(interest.name, RET_NO_REQUEST) return True if data_name[-1].isSegment(): # Segment no suffix seg_no = data_name[-1].toSegment() result = self.storage.get(data_name.getPrefix(-1)) elif data_name[-1] == Name("_meta")[0]: # MetaInfo suffix seg_no = -1 result = self.storage.get(data_name.getPrefix(-1)) else: # No suffix seg_no = None result = self.storage.get(data_name) if result is not None: # There are data segment_cnt = (len(result) + self.segment_size - 1) // self.segment_size # Note: I don't understand why namespace keep all data in memory metainfo = MetaInfo() # metainfo.setFinalBlockId(segment_cnt - 1) # WHY this doesn't work? metainfo.setFinalBlockId(Name().appendSegment(segment_cnt - 1)[0]) if segment_cnt > 1 and seg_no is None: # Fetch segmented data with no suffix will get only first segment seg_no = 0 data_name.appendSequenceNumber(seg_no) data = Data(Name(prefix).append(data_name)) data.setMetaInfo(metainfo) if seg_no == -1: # _meta data.content = self.storage.get(data_name) else: # data if segment_cnt > 1: # Segmented if seg_no < segment_cnt: start_offset = seg_no * self.segment_size end_offset = start_offset + self.segment_size data.content = Blob(bytearray(result[start_offset:end_offset])) else: data.content = None else: # No segmentation data.content = Blob(bytearray(result)) self.keychain.sign(data) face.putData(data) return True else: # Data are not ready if status.status == STATUS_NO_INPUT: self.nodata_reply(interest.name, RET_NO_INPUT) elif status.status == STATUS_FAILED: self.nodata_reply(interest.name, RET_EXECUTION_FAILED) else: self.nodata_reply(interest.name, RET_RETRY_AFTER, status.estimated_time - Common.getNowMilliseconds()) return True
def startFileSync(): global EXIT screenName = promptAndInput("Enter your name: ") defaultHubPrefix = "ndn/no/ntnu" hubPrefix = promptAndInput("Enter your hub prefix [" + defaultHubPrefix + "]: ") if hubPrefix == "": hubPrefix = defaultHubPrefix defaultpkList = "pklist" pkListName = promptAndInput("Sync with public key list [" + defaultpkList + "]: ") if pkListName == "": pkListName = defaultpkList host = "localhost" # host = "129.241.208.115" logging.info("Connecting to " + host + ", public Key List: " + pkListName + ", Name: " + screenName) # Set up the key chain. face = Face(host) identityStorage = MemoryIdentityStorage() privateKeyStorage = MemoryPrivateKeyStorage() # privateKeyStorage = OSXPrivateKeyStorage() identityManager = IdentityManager(identityStorage, privateKeyStorage) # identityManager.createIdentity(Name("/name/")) keyChain = KeyChain(identityManager, NoVerifyPolicyManager()) keyChain.setFace(face) 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) face.setCommandSigningInfo(keyChain, certificateName) # keyName = Name("/ndn/no/ntnu/stud/haakonmo/ksk-1426537450856") # certificateName = Name("/ndn/no/ntnu/KEY/stud/haakonmo/ksk-1426537450856/ID-CERT/%FD%00%00%01L%26%D9E%92") # publicKey = privateKeyStorage.getPublicKey(keyName) # identityStorage.addKey(keyName, publicKey.getKeyType(), publicKey.getKeyDer()) # face.setCommandSigningInfo(keyChain, certificateName) # print(identityStorage.getCertificate(certificateName)) # print(identityStorage.getKey(keyName)) path = './files/' fileSyncer = FileSync(screenName, pkListName, Name(hubPrefix), face, keyChain, certificateName, path) fileSyncer.initial() fileWatcher = FileWatch(fileSyncer, path) # TODO: # 1. Generate new public key or use existing? # 2. Watch new public key # 3. sendUpdatedPublicKey if key is changed # 4. Download and store other keys # 5. Verify data packet while not EXIT: isReady, _, _ = select.select([sys.stdin], [], [], 0) if len(isReady) != 0: input = promptAndInput("") if input == "leave" or input == "exit": EXIT = True break #fileSyncer.onFileUpdate(input) fileSyncer.face.processEvents() # We need to sleep for a few milliseconds so we don't use 100% of the CPU. time.sleep(0.01) fileSyncer.unsubscribe() startTime = FileSync.getNowMilliseconds() while True: if FileSync.getNowMilliseconds() - startTime >= 1000.0: break face.processEvents() time.sleep(0.01) # Shutdown all services fileSyncer.face.shutdown() fileWatcher.stopFileWatch()
def benchmarkEncodeDataSeconds(nIterations, useComplex, useCrypto): """ Loop to encode a data packet nIterations times. :param int nIterations: The number of iterations. :param bool useComplex: If true, use a large name, large content and all fields. If false, use a small name, small content and only required fields. :param bool useCrypto: If true, sign the data packet. If false, use a blank signature. :return: A tuple (duration, encoding) where duration is the number of seconds for all iterations and encoding is the wire encoding. :rtype: (float, Blob) """ if useComplex: # Use a large name and content. name = Name( "/ndn/ucla.edu/apps/lwndn-test/numbers.txt/%FD%05%05%E8%0C%CE%1D/%00") contentString = "" count = 1 contentString += "%d" % count count += 1 while len(contentString) < 1115: contentString += " %d" % count count += 1 content = Name.fromEscapedString(contentString) else: # Use a small name and content. name = Name("/test") content = Name.fromEscapedString("abc") finalBlockId = Name("/%00")[0] # 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)) privateKeyStorage.setKeyPairForKeyName( keyName, KeyType.RSA, DEFAULT_RSA_PUBLIC_KEY_DER, DEFAULT_RSA_PRIVATE_KEY_DER) # Set up signatureBits in case useCrypto is false. signatureBits = Blob(bytearray(256)) emptyBlob = Blob([]) start = getNowSeconds() for i in range(nIterations): data = Data(name) data.setContent(content) if useComplex: data.getMetaInfo().setFreshnessPeriod(1000) data.getMetaInfo().setFinalBlockId(finalBlockId) if useCrypto: # This sets the signature fields. keyChain.sign(data, certificateName) else: # Imitate IdentityManager.signByCertificate to set up the signature # fields, but don't sign. sha256Signature = data.getSignature() keyLocator = sha256Signature.getKeyLocator() keyLocator.setType(KeyLocatorType.KEYNAME) keyLocator.setKeyName(certificateName) sha256Signature.setSignature(signatureBits) encoding = data.wireEncode() finish = getNowSeconds() return (finish - start, encoding)
def main(): # Uncomment these lines to print ChronoSync debug messages. # logging.getLogger('').addHandler(logging.StreamHandler(sys.stdout)) # logging.getLogger('').setLevel(logging.INFO) screenName = promptAndInput("Enter your chat username: "******"ndn/edu/ucla/remap" hubPrefix = promptAndInput("Enter your hub prefix [" + defaultHubPrefix + "]: ") if hubPrefix == "": hubPrefix = defaultHubPrefix defaultChatRoom = "ndnchat" chatRoom = promptAndInput("Enter the chatroom name [" + defaultChatRoom + "]: ") if chatRoom == "": chatRoom = defaultChatRoom host = "localhost" print("Connecting to " + host + ", Chatroom: " + chatRoom + ", Username: "******"") # Set up the key chain. face = Face(host) identityStorage = MemoryIdentityStorage() privateKeyStorage = MemoryPrivateKeyStorage() keyChain = KeyChain(IdentityManager(identityStorage, privateKeyStorage), NoVerifyPolicyManager()) keyChain.setFace(face) 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) face.setCommandSigningInfo(keyChain, certificateName) chat = Chat( screenName, chatRoom, Name(hubPrefix), face, keyChain, certificateName) # The main loop to process Chat while checking stdin to send a message. print("Enter your chat message. To quit, enter \"leave\" or \"exit\".") while True: # Set timeout to 0 for an immediate check. isReady, _, _ = select.select([sys.stdin], [], [], 0) if len(isReady) != 0: input = promptAndInput("") if input == "leave" or input == "exit": # We will send the leave message below. break chat.sendMessage(input) face.processEvents() # We need to sleep for a few milliseconds so we don't use 100% of the CPU. time.sleep(0.01) # The user entered the command to leave. chat.leave() # Wait a little bit to allow other applications to fetch the leave message. startTime = Chat.getNowMilliseconds() while True: if Chat.getNowMilliseconds() - startTime >= 1000.0: break face.processEvents() time.sleep(0.01)
class IotController(BaseNode): """ The controller class has a few built-in commands: - listDevices: return the names and capabilities of all attached devices - certificateRequest: takes public key information and returns name of new certificate - updateCapabilities: should be sent periodically from IotNodes to update their command lists - addDevice: add a device based on HMAC It is unlikely that you will need to subclass this. """ def __init__(self, nodeName, networkName, applicationDirectory=""): super(IotController, self).__init__() self.deviceSuffix = Name(nodeName) self.networkPrefix = Name(networkName) self.prefix = Name(self.networkPrefix).append(self.deviceSuffix) self._policyManager.setEnvironmentPrefix(self.networkPrefix) self._policyManager.setTrustRootIdentity(self.prefix) self._policyManager.setDeviceIdentity(self.prefix) self._policyManager.updateTrustRules() # the controller keeps a directory of capabilities->names self._directory = defaultdict(list) # keep track of who's still using HMACs # key is device serial, value is the HmacHelper self._hmacDevices = {} # our capabilities self._baseDirectory = {} # add the built-ins self._insertIntoCapabilities('listDevices', 'directory', False) self._insertIntoCapabilities('updateCapabilities', 'capabilities', True) self._directory.update(self._baseDirectory) # Set up application directory if applicationDirectory == "": applicationDirectory = os.path.expanduser( '~/.ndn/iot/applications') self._applicationDirectory = applicationDirectory self._applications = dict() def _insertIntoCapabilities(self, commandName, keyword, isSigned): newUri = Name(self.prefix).append(Name(commandName)).toUri() self._baseDirectory[keyword] = [{'signed': isSigned, 'name': newUri}] def beforeLoopStart(self): if not self._policyManager.hasRootSignedCertificate(): # make one.... self.log.warn('Generating controller certificate...') newKey = self._identityManager.generateRSAKeyPairAsDefault( self.prefix, isKsk=True) newCert = self._identityManager.selfSign(newKey) self._identityManager.addCertificateAsDefault(newCert) # Trusting root's own certificate upon each run # TODO: debug where application starts first and controller starts second, application's interest cannot be verified self._rootCertificate = self._keyChain.getCertificate( self.getDefaultCertificateName()) self._policyManager._certificateCache.insertCertificate( self._rootCertificate) self._memoryContentCache = MemoryContentCache(self.face) self.face.setCommandSigningInfo(self._keyChain, self.getDefaultCertificateName()) self._memoryContentCache.registerPrefix( self.prefix, onRegisterFailed=self.onRegisterFailed, onRegisterSuccess=None, onDataNotFound=self._onCommandReceived) # Serve root certificate in our memoryContentCache self._memoryContentCache.add(self._rootCertificate) self.loadApplications() self.loop.call_soon(self.onStartup) ###### # Initial configuration ####### # TODO: deviceSuffix will be replaced by deviceSerial def _addDeviceToNetwork(self, deviceSerial, newDeviceSuffix, pin): h = HmacHelper(pin) self._hmacDevices[deviceSerial] = h d = DeviceConfigurationMessage() for source, dest in [ (self.networkPrefix, d.configuration.networkPrefix), (self.deviceSuffix, d.configuration.controllerName), (newDeviceSuffix, d.configuration.deviceSuffix) ]: for i in range(source.size()): component = source.get(i) dest.components.append(component.getValue().toRawStr()) interestName = Name('/home/configure').append(Name(deviceSerial)) encodedParams = ProtobufTlv.encode(d) interestName.append(encodedParams) interest = Interest(interestName) h.signInterest(interest) self.face.expressInterest(interest, self._deviceAdditionResponse, self._deviceAdditionTimedOut) def _deviceAdditionTimedOut(self, interest): deviceSerial = str(interest.getName().get(2).getValue()) self.log.warn("Timed out trying to configure device " + deviceSerial) # don't try again self._hmacDevices.pop(deviceSerial) def _deviceAdditionResponse(self, interest, data): status = data.getContent().toRawStr() deviceSerial = str(interest.getName().get(2).getValue()) hmacChecker = self._hmacDevices[deviceSerial] if (hmacChecker.verifyData(data)): self.log.info("Received {} from {}".format(status, deviceSerial)) else: self.log.warn("Received invalid HMAC from {}".format(deviceSerial)) ###### # Certificate signing ###### def _handleCertificateRequest(self, interest): """ Extracts a public key name and key bits from a command interest name component. Generates a certificate if the request is verifiable. This expects an HMAC signed interest. """ message = CertificateRequestMessage() commandParamsTlv = interest.getName().get(self.prefix.size() + 1) ProtobufTlv.decode(message, commandParamsTlv.getValue()) signature = HmacHelper.extractInterestSignature(interest) deviceSerial = str( signature.getKeyLocator().getKeyName().get(-1).getValue()) response = Data(interest.getName()) certData = None hmac = None try: hmac = self._hmacDevices[deviceSerial] if hmac.verifyInterest(interest): certData = self._createCertificateFromRequest(message) # remove this hmac; another request will require a new pin self._hmacDevices.pop(deviceSerial) except KeyError: self.log.warn( 'Received certificate request for device with no registered key' ) except SecurityException as e: self.log.warn('Could not create device certificate: ' + str(e)) else: self.log.info( 'Creating certificate for device {}'.format(deviceSerial)) if certData is not None: response.setContent(certData.wireEncode()) response.getMetaInfo().setFreshnessPeriod( 10000) # should be good even longer else: response.setContent("Denied") if hmac is not None: hmac.signData(response) self.sendData(response, False) def _createCertificateFromRequest(self, message): """ Generate an IdentityCertificate from the public key information given. """ # TODO: Verify the certificate was actually signed with the private key # matching the public key we are issuing a cert for!! keyComponents = message.command.keyName.components keyName = Name("/".join(keyComponents)) self.log.debug("Key name: " + keyName.toUri()) if not self._policyManager.getEnvironmentPrefix().match(keyName): # we do not issue certs for keys outside of our network return None keyDer = Blob(message.command.keyBits) keyType = message.command.keyType try: self._identityStorage.addKey(keyName, keyType, keyDer) except SecurityException as e: print(e) # assume this is due to already existing? pass certificate = self._identityManager._generateCertificateForKey(keyName) self._keyChain.sign(certificate, self.getDefaultCertificateName()) # store it for later use + verification self._identityStorage.addCertificate(certificate) self._policyManager._certificateCache.insertCertificate(certificate) return certificate ###### # Device Capabilities ###### def _updateDeviceCapabilities(self, interest): """ Take the received capabilities update interest and update our directory listings. """ # we assume the sender is the one who signed the interest... signature = self._policyManager._extractSignature(interest) certificateName = signature.getKeyLocator().getKeyName() senderIdentity = IdentityCertificate.certificateNameToPublicKeyName( certificateName).getPrefix(-1) self.log.info('Updating capabilities for {}'.format( senderIdentity.toUri())) # get the params from the interest name messageComponent = interest.getName().get(self.prefix.size() + 1) message = UpdateCapabilitiesCommandMessage() ProtobufTlv.decode(message, messageComponent.getValue()) # we remove all the old capabilities for the sender tempDirectory = defaultdict(list) for keyword in self._directory: tempDirectory[keyword] = [ cap for cap in self._directory[keyword] if not senderIdentity.match(Name(cap['name'])) ] # then we add the ones from the message for capability in message.capabilities: capabilityPrefix = Name() for component in capability.commandPrefix.components: capabilityPrefix.append(component) commandUri = capabilityPrefix.toUri() if not senderIdentity.match(capabilityPrefix): self.log.error( "Node {} tried to register another prefix: {} - ignoring update" .format(senderIdentity.toUri(), commandUri)) else: for keyword in capability.keywords: allUris = [info['name'] for info in tempDirectory[keyword]] if capabilityPrefix not in allUris: listing = { 'signed': capability.needsSignature, 'name': commandUri } tempDirectory[keyword].append(listing) self._directory = tempDirectory def _prepareCapabilitiesList(self, interestName): """ Responds to a directory listing request with JSON """ dataName = Name(interestName).append( Name.Component.fromNumber(int(time.time()))) response = Data(dataName) response.setContent(json.dumps(self._directory)) return response ##### # Interest handling #### def _onCommandReceived(self, prefix, interest, face, interestFilterId, filter): """ """ interestName = interest.getName() #if it is a certificate name, serve the certificate # TODO: since we've memoryContentCache serving root cert now, this should no longer be required try: if interestName.isPrefixOf(self.getDefaultCertificateName()): foundCert = self._identityManager.getCertificate( self.getDefaultCertificateName()) self.log.debug("Serving certificate request") self.face.putData(foundCert) return except SecurityException as e: # We don't have this certificate, this is probably not a certificate request # TODO: this does not differentiate from certificate request but certificate not exist; should update print(str(e)) pass afterPrefix = interestName.get(prefix.size()).toEscapedString() if afterPrefix == "listDevices": #compose device list self.log.debug("Received device list request") response = self._prepareCapabilitiesList(interestName) self.sendData(response) elif afterPrefix == "certificateRequest": #build and sign certificate self.log.debug("Received certificate request") self._handleCertificateRequest(interest) elif afterPrefix == "updateCapabilities": # needs to be signed! self.log.debug("Received capabilities update") def onVerifiedCapabilities(interest): print("capabilities good") response = Data(interest.getName()) response.setContent(str(time.time())) self.sendData(response) self._updateDeviceCapabilities(interest) self._keyChain.verifyInterest(interest, onVerifiedCapabilities, self.verificationFailed) elif afterPrefix == "requests": # application request to publish under some names received; need to be signed def onVerifiedAppRequest(interest): # TODO: for now, we automatically grant access to any valid signed interest print("verified! send response!") message = AppRequestMessage() ProtobufTlv.decode( message, interest.getName().get(prefix.size() + 1).getValue()) certName = Name("/".join(message.command.idName.components)) dataPrefix = Name("/".join( message.command.dataPrefix.components)) appName = message.command.appName isUpdated = self.updateTrustSchema(appName, certName, dataPrefix, True) response = Data(interest.getName()) if isUpdated: response.setContent( "{\"status\": 200, \"message\": \"granted, trust schema updated OK\" }" ) self.log.info( "Verified and granted application publish request") else: response.setContent( "{\"status\": 400, \"message\": \"not granted, requested publishing namespace already exists\" }" ) self.log.info( "Verified and but requested namespace already exists") self.sendData(response) return def onVerificationFailedAppRequest(interest): print("application request verify failed!") response = Data(interest.getName()) response.setContent( "{\"status\": 401, \"message\": \"command interest verification failed\" }" ) self.sendData(response) self.log.info("Received application request: " + interestName.toUri()) #print("Verifying with trust schema: ") #print(self._policyManager.config) self._keyChain.verifyInterest(interest, onVerifiedAppRequest, onVerificationFailedAppRequest) else: print("Got interest unable to answer yet: " + interest.getName().toUri()) if interest.getExclude(): print("interest has exclude: " + interest.getExclude().toUri()) # response = Data(interest.getName()) # response.setContent("500") # response.getMetaInfo().setFreshnessPeriod(1000) # self.sendData(response) def onStartup(self): # begin taking add requests self.loop.call_soon(self.displayMenu) self.loop.add_reader(stdin, self.handleUserInput) def displayMenu(self): menuStr = "\n" menuStr += "P)air a new device with serial and PIN\n" menuStr += "D)irectory listing\n" menuStr += "E)xpress an interest\n" menuStr += "L)oad hosted applications (" + ( self._applicationDirectory) + ")\n" menuStr += "Q)uit\n" print(menuStr) print("> ", end="") stdout.flush() def listDevices(self): menuStr = '' for capability, commands in self._directory.items(): menuStr += '{}:\n'.format(capability) for info in commands: signingStr = 'signed' if info['signed'] else 'unsigned' menuStr += '\t{} ({})\n'.format(info['name'], signingStr) print(menuStr) self.loop.call_soon(self.displayMenu) def loadApplicationsMenuSelect(self): try: confirm = input( 'This will override existing trust schemas, continue? (Y/N): ' ).upper().startswith('Y') if confirm: self.loadApplications(override=True) else: print("Aborted") except KeyboardInterrupt: print("Aborted") finally: self.loop.call_soon(self.displayMenu) def onInterestTimeout(self, interest): print('Interest timed out: {}'.interest.getName().toUri()) def onDataReceived(self, interest, data): print('Received data named: {}'.format(data.getName().toUri())) print('Contents:\n{}'.format(data.getContent().toRawStr())) def expressInterest(self): try: interestName = input('Interest name: ') if len(interestName): toSign = input('Signed? (y/N): ').upper().startswith('Y') interest = Interest(Name(interestName)) print(interest) interest.setInterestLifetimeMilliseconds(10000) interest.setChildSelector(1) if (toSign): self.face.makeCommandInterest(interest) self.face.expressInterest(interest, self.onDataReceived, self.onInterestTimeout) else: print("Aborted") except KeyboardInterrupt: print("Aborted") finally: self.loop.call_soon(self.displayMenu) def beginPairing(self): try: deviceSerial = input('Device serial: ') devicePin = input('PIN: ') deviceSuffix = input('Node name: ') except KeyboardInterrupt: print('Pairing attempt aborted') else: if len(deviceSerial) and len(devicePin) and len(deviceSuffix): self._addDeviceToNetwork(deviceSerial, Name(deviceSuffix), devicePin.decode('hex')) else: print('Pairing attempt aborted') finally: self.loop.call_soon(self.displayMenu) def handleUserInput(self): inputStr = stdin.readline().upper() if inputStr.startswith('D'): self.listDevices() elif inputStr.startswith('P'): self.beginPairing() elif inputStr.startswith('E'): self.expressInterest() elif inputStr.startswith('Q'): self.stop() elif inputStr.startswith('L'): self.loadApplicationsMenuSelect() else: self.loop.call_soon(self.displayMenu) ######################## # application trust schema distribution ######################## def updateTrustSchema(self, appName, certName, dataPrefix, publishNew=False): if appName in self._applications: if dataPrefix.toUri() in self._applications[appName]["dataPrefix"]: print("some key is configured for namespace " + dataPrefix.toUri() + " for application " + appName + ". Ignoring this request.") return False else: # TODO: Handle malformed conf where validator tree does not exist validatorNode = self._applications[appName]["tree"][ "validator"][0] else: # This application does not previously exist, we create its trust schema # (and for now, add in static rules for sync data) self._applications[appName] = { "tree": BoostInfoParser(), "dataPrefix": [], "version": 0 } validatorNode = self._applications[appName]["tree"].getRoot( ).createSubtree("validator") trustAnchorNode = validatorNode.createSubtree("trust-anchor") #trustAnchorNode.createSubtree("type", "file") #trustAnchorNode.createSubtree("file-name", os.path.expanduser("~/.ndn/iot/root.cert")) trustAnchorNode.createSubtree("type", "base64") trustAnchorNode.createSubtree( "base64-string", Blob(b64encode(self._rootCertificate.wireEncode().toBytes()), False).toRawStr()) #create cert verification rule # TODO: the idea for this would be, if the cert has /home-prefix/<one-component>/KEY/ksk-*/ID-CERT, then it should be signed by fixed controller(s) # if the cert has /home-prefix/<multiple-components>/KEY/ksk-*/ID-CERT, then it should be checked hierarchically (this is for subdomain support) certRuleNode = validatorNode.createSubtree("rule") certRuleNode.createSubtree("id", "Certs") certRuleNode.createSubtree("for", "data") filterNode = certRuleNode.createSubtree("filter") filterNode.createSubtree("type", "regex") filterNode.createSubtree("regex", "^[^<KEY>]*<KEY><>*<ID-CERT>") checkerNode = certRuleNode.createSubtree("checker") # TODO: wait how did my first hierarchical verifier work? #checkerNode.createSubtree("type", "hierarchical") checkerNode.createSubtree("type", "customized") checkerNode.createSubtree("sig-type", "rsa-sha256") keyLocatorNode = checkerNode.createSubtree("key-locator") keyLocatorNode.createSubtree("type", "name") # We don't put cert version in there keyLocatorNode.createSubtree( "name", Name(self.getDefaultCertificateName()).getPrefix(-1).toUri()) keyLocatorNode.createSubtree("relation", "equal") # Discovery rule: anything that multicasts under my home prefix should be signed, and the signer should have been authorized by root # TODO: This rule as of right now is over-general discoveryRuleNode = validatorNode.createSubtree("rule") discoveryRuleNode.createSubtree("id", "sync-data") discoveryRuleNode.createSubtree("for", "data") filterNode = discoveryRuleNode.createSubtree("filter") filterNode.createSubtree("type", "regex") filterNode.createSubtree("regex", "^[^<MULTICAST>]*<MULTICAST><>*") checkerNode = discoveryRuleNode.createSubtree("checker") # TODO: wait how did my first hierarchical verifier work? #checkerNode.createSubtree("type", "hierarchical") checkerNode.createSubtree("type", "customized") checkerNode.createSubtree("sig-type", "rsa-sha256") keyLocatorNode = checkerNode.createSubtree("key-locator") keyLocatorNode.createSubtree("type", "name") keyLocatorNode.createSubtree("regex", "^[^<KEY>]*<KEY><>*<ID-CERT>") ruleNode = validatorNode.createSubtree("rule") ruleNode.createSubtree("id", dataPrefix.toUri()) ruleNode.createSubtree("for", "data") filterNode = ruleNode.createSubtree("filter") filterNode.createSubtree("type", "name") filterNode.createSubtree("name", dataPrefix.toUri()) filterNode.createSubtree("relation", "is-prefix-of") checkerNode = ruleNode.createSubtree("checker") checkerNode.createSubtree("type", "customized") checkerNode.createSubtree("sig-type", "rsa-sha256") keyLocatorNode = checkerNode.createSubtree("key-locator") keyLocatorNode.createSubtree("type", "name") # We don't put cert version in there keyLocatorNode.createSubtree("name", certName.getPrefix(-1).toUri()) keyLocatorNode.createSubtree("relation", "equal") if not os.path.exists(self._applicationDirectory): os.makedirs(self._applicationDirectory) self._applications[appName]["tree"].write( os.path.join(self._applicationDirectory, appName + ".conf")) self._applications[appName]["dataPrefix"].append(dataPrefix.toUri()) self._applications[appName]["version"] = int(time.time()) if publishNew: # TODO: ideally, this is the trust schema of the application, and does not necessarily carry controller prefix. # We make it carry controller prefix here so that prefix registration / route setup is easier (implementation workaround) data = Data( Name(self.prefix).append(appName).append( "_schema").appendVersion( self._applications[appName]["version"])) data.setContent(str(self._applications[appName]["tree"].getRoot())) self.signData(data) self._memoryContentCache.add(data) return True # TODO: putting existing confs into memoryContentCache def loadApplications(self, directory=None, override=False): if not directory: directory = self._applicationDirectory if override: self._applications.clear() if os.path.exists(directory): for f in os.listdir(directory): fullFileName = os.path.join(directory, f) if os.path.isfile(fullFileName) and f.endswith('.conf'): appName = f.rstrip('.conf') if appName in self._applications and not override: print( "loadApplications: " + appName + " already exists, do nothing for configuration file: " + fullFileName) else: self._applications[appName] = { "tree": BoostInfoParser(), "dataPrefix": [], "version": int(time.time()) } self._applications[appName]["tree"].read(fullFileName) data = Data( Name(self.prefix).append(appName).append( "_schema").appendVersion( self._applications[appName]["version"])) data.setContent( str(self._applications[appName]["tree"].getRoot())) self.signData(data) self._memoryContentCache.add(data) try: validatorTree = self._applications[appName][ "tree"]["validator"][0] for rule in validatorTree["rule"]: self._applications[appName][ "dataPrefix"].append(rule["id"][0].value) # TODO: don't swallow any general exceptions, we want to catch only KeyError (make sure) here except Exception as e: print( "loadApplications parse configuration file " + fullFileName + " : " + str(e)) return
def test_content_key_request(self): prefix = Name("/prefix") suffix = Name("/a/b/c") expectedInterest = Name(prefix) expectedInterest.append(Encryptor.NAME_COMPONENT_READ) expectedInterest.append(suffix) expectedInterest.append(Encryptor.NAME_COMPONENT_E_KEY) cKeyName = Name(prefix) cKeyName.append(Encryptor.NAME_COMPONENT_SAMPLE) cKeyName.append(suffix) cKeyName.append(Encryptor.NAME_COMPONENT_C_KEY) timeMarker = Name("20150101T100000/20150101T120000") testTime1 = Schedule.fromIsoString("20150101T100001") testTime2 = Schedule.fromIsoString("20150101T110001") testTimeRounded1 = Name.Component("20150101T100000") testTimeRounded2 = Name.Component("20150101T110000") # Create content keys required for this test case: for i in range(suffix.size()): self.createEncryptionKey(expectedInterest, timeMarker) expectedInterest = expectedInterest.getPrefix(-2).append( Encryptor.NAME_COMPONENT_E_KEY) expressInterestCallCount = [0] # Prepare a TestFace to instantly answer calls to expressInterest. class TestFace(object): def __init__(self, handleExpressInterest): self.handleExpressInterest = handleExpressInterest def expressInterest(self, interest, onData, onTimeout): return self.handleExpressInterest(interest, onData, onTimeout) def handleExpressInterest(interest, onData, onTimeout): expressInterestCallCount[0] += 1 interestName = Name(interest.getName()) interestName.append(timeMarker) self.assertTrue(interestName in self.encryptionKeys) onData(interest, self.encryptionKeys[interestName]) return 0 face = TestFace(handleExpressInterest) # Verify that the content key is correctly encrypted for each domain, and # the produce method encrypts the provided data with the same content key. testDb = Sqlite3ProducerDb(self.databaseFilePath) producer = Producer(prefix, suffix, face, self.keyChain, testDb) contentKey = [None] # Blob def checkEncryptionKeys( result, testTime, roundedTime, expectedExpressInterestCallCount): self.assertEqual(expectedExpressInterestCallCount, expressInterestCallCount[0]) self.assertEqual(True, testDb.hasContentKey(testTime)) contentKey[0] = testDb.getContentKey(testTime) params = EncryptParams(EncryptAlgorithmType.RsaOaep) for i in range(len(result)): key = result[i] keyName = key.getName() self.assertEqual(cKeyName, keyName.getSubName(0, 6)) self.assertEqual(keyName.get(6), roundedTime) self.assertEqual(keyName.get(7), Encryptor.NAME_COMPONENT_FOR) self.assertEqual( True, keyName.getSubName(8) in self.decryptionKeys) decryptionKey = self.decryptionKeys[keyName.getSubName(8)] self.assertEqual(True, decryptionKey.size() != 0) encryptedKeyEncoding = key.getContent() content = EncryptedContent() content.wireDecode(encryptedKeyEncoding) encryptedKey = content.getPayload() retrievedKey = RsaAlgorithm.decrypt( decryptionKey, encryptedKey, params) self.assertTrue(contentKey[0].equals(retrievedKey)) self.assertEqual(3, len(result)) # An initial test to confirm that keys are created for this time slot. contentKeyName1 = producer.createContentKey( testTime1, lambda keys: checkEncryptionKeys(keys, testTime1, testTimeRounded1, 3)) # Verify that we do not repeat the search for e-keys. The total # expressInterestCallCount should be the same. contentKeyName2 = producer.createContentKey( testTime2, lambda keys: checkEncryptionKeys(keys, testTime2, testTimeRounded2, 3)) # Confirm content key names are correct self.assertEqual(cKeyName, contentKeyName1.getPrefix(-1)) self.assertEqual(testTimeRounded1, contentKeyName1.get(6)) self.assertEqual(cKeyName, contentKeyName2.getPrefix(-1)) self.assertEqual(testTimeRounded2, contentKeyName2.get(6)) # Confirm that produce encrypts with the correct key and has the right name. testData = Data() producer.produce(testData, testTime2, Blob(DATA_CONTENT, False)) producedName = testData.getName() self.assertEqual(cKeyName.getPrefix(-1), producedName.getSubName(0, 5)) self.assertEqual(testTimeRounded2, producedName.get(5)) self.assertEqual(Encryptor.NAME_COMPONENT_FOR, producedName.get(6)) self.assertEqual(cKeyName, producedName.getSubName(7, 6)) self.assertEqual(testTimeRounded2, producedName.get(13)) dataBlob = testData.getContent() dataContent = EncryptedContent() dataContent.wireDecode(dataBlob) encryptedData = dataContent.getPayload() initialVector = dataContent.getInitialVector() params = EncryptParams(EncryptAlgorithmType.AesCbc, 16) params.setInitialVector(initialVector) decryptTest = AesAlgorithm.decrypt(contentKey[0], encryptedData, params) self.assertTrue(decryptTest.equals(Blob(DATA_CONTENT, False)))
class FileSync(object): def __init__(self, screenName, fileFolderName, hubPrefix, face, keyChain, certificateName, path): """ FileSync: To be written (TBW) """ self.screenName = screenName self.fileFolderName = fileFolderName self.path = path # ChronoSync2013: The Face for calling registerPrefix and expressInterest. # The Face object must remain valid for the life of this ChronoSync2013 object. self.face = face self.keyChain = keyChain self.certificateName = certificateName self.syncDataCache = [] # of CachedSyncData self.roster = [] # of str (list of all nodes that are subscribing) self.maxDataCacheLength = 100 self.isRecoverySyncState = True self.syncLifetime = 15000.0 # milliseconds # This should only be called once, so get the random string here. self.fileFolderPrefix = Name(hubPrefix).append( self.fileFolderName).append(self.getRandomString()) # ChronoSync2013: The session number used with the applicationDataPrefix in sync state messages. session = int(round(self.getNowMilliseconds() / 1000.0)) self.userName = self.screenName + str(session) broadcastPrefix = Name("/ndn/broadcast/FileSync-0.1").append( self.fileFolderName) self.sync = ChronoSync2013( self.sendInterest, #onReceivedSyncState (function object) self.initial, #onInitialized (function object) self.fileFolderPrefix, #applicationDataPrefix (Name) broadcastPrefix, #applicationBroadcastPrefix (Name) session, #sessionNo (int) self.face, #face (Face) self.keyChain, #KeyChain (KeyChain) self.certificateName, #certificateName (Name) self.syncLifetime, #syncLifetime (float) self.onRegisterFailed ) #onRegisterFailed (function object) face.registerPrefix(self.fileFolderPrefix, self.onInterest, self.onRegisterFailed) def onFileUpdate(self, data): """ FileSync: When a key pair is edited, i.e. renewed, the application will publish a new sequence number in ChronoSync2013 """ # When the application wants to publish data, it calls ChronoSync2013 method publishNextSequenceNo() #Subscribe to "file folder" if not subscribing already if len(self.syncDataCache) == 0: self.syncDataCacheAppend(fileSyncBuf_pb2.FileSync.SUBSCRIBE, "xxx") #TODO: check wether the new public key is new. self.sync.publishNextSequenceNo() self.syncDataCacheAppend(fileSyncBuf_pb2.FileSync.UPDATE, data) def unsubscribe(self): """ FileSync: Send the unsubscribe message and unsubscribe the public key. """ self.sync.publishNextSequenceNo() self.syncDataCacheAppend(fileSyncBuf_pb2.FileSync.UNSUBSCRIBE, "xxx") # onInitialized def initial(self): """ FileSync: To be written (TBW) ChronoSync2013 docs: onInitialized: This calls onInitialized() when the first sync data is received (or the interest times out because there are no other publishers yet). """ timeout = Interest(Name("/local/timeout")) timeout.setInterestLifetimeMilliseconds(60000) self.face.expressInterest(timeout, self.dummyOnData, self.onTimeout) try: self.roster.index(self.userName) except ValueError: self.roster.append(self.userName) print("Member: " + self.screenName) print(self.screenName + ": Subscribe") self.syncDataCacheAppend(fileSyncBuf_pb2.FileSync.SUBSCRIBE, "xxx") # onReceivedSyncState def sendInterest(self, syncStates, isRecovery): """ FileSync: To be written (TBW) ChronoSync2013 docs: onReceivedSyncState: When ChronoSync receives a sync state message, this calls onReceivedSyncState(syncStates, isRecovery) where syncStates is the list of SyncState messages and isRecovery is true if this is the initial list of SyncState messages or from a recovery interest. (For example, if isRecovery is true, a chat application would not want to re-display all the associated chat messages.) The callback should send interests to fetch the application data for the sequence numbers in the sync state. """ self.isRecoverySyncState = isRecovery util.dump("onReceivedSyncState in recovery: ", self.isRecoverySyncState) sendList = [] # of str sessionNoList = [] # of int sequenceNoList = [] # of int # Loops through the syncStates # ChronoSync2013: A SyncState holds the values of a sync state message which is passed to the # onReceivedSyncState callback which was given to the ChronoSync2013 constructor. for j in range(len(syncStates)): syncState = syncStates[j] # ChronoSync2013: Get the application data prefix for this sync state message. nameComponents = Name(syncState.getDataPrefix()) #TODO not used.. tempName = nameComponents.get(-1).toEscapedString() # tempName is the random string # ChronoSync2013: Get the sequence number for this sync state message. sequenceNo = syncState.getSequenceNo() # ChronoSync2013: Get the session number associated with the application data prefix for this sync state message. sessionNo = syncState.getSessionNo() #Loop through sendList for not adding duplcates index = -1 for k in range(len(sendList)): if sendList[k] == syncState.getDataPrefix(): index = k break if index != -1: sessionNoList[index] = sessionNo sequenceNoList[index] = sequenceNo else: #append to sendList for sending out interest sendList.append(syncState.getDataPrefix()) sessionNoList.append(sessionNo) sequenceNoList.append(sequenceNo) # Loop through all syncStates and send an interest for all. for i in range(len(sendList)): uri = (sendList[i] + "/" + str(sessionNoList[i]) + "/" + str(sequenceNoList[i])) interestName = Name(uri) util.dump("Sync - sending interest: ", interestName.toUri()) interest = Interest(interestName) interest.setInterestLifetimeMilliseconds(self.syncLifetime) self.face.expressInterest(interest, self.onData, self.onTimeout) def onInterest(self, prefix, interest, transport, registeredPrefixId): """ FileSync: To be written (TBW) """ util.dump("Got interest packet with name", interest.getName().toUri()) util.dumpInterest(interest) content = fileSyncBuf_pb2.FileSync() sequenceNo = int(interest.getName().get(self.fileFolderPrefix.size() + 1).toEscapedString()) gotContent = False #loop through all cached data and find out if you have some new content to respond with for i in range(len(self.syncDataCache) - 1, -1, -1): data = self.syncDataCache[i] if data.sequenceNo == sequenceNo: if data.dataType != fileSyncBuf_pb2.FileSync.UPDATE: # Use setattr because "from" is a reserved keyword. setattr(content, "from", self.screenName) content.to = self.fileFolderName content.dataType = data.dataType content.timestamp = int(round(data.time / 1000.0)) else: setattr(content, "from", self.screenName) content.to = self.fileFolderName content.dataType = data.dataType content.data = data.data content.timestamp = int(round(data.time / 1000.0)) gotContent = True break if gotContent: logging.info("new content!") #Serialize the pklistbuf array = content.SerializeToString() #Initialize the data with Name data = Data(interest.getName()) #Set content for the data --> the serialized content to bytes data.setContent(Blob(array)) #Add sign the data self.keyChain.sign(data, self.certificateName) try: transport.send(data.wireEncode().toBuffer()) except Exception as ex: logging.getLogger(__name__).error( "Error in transport.send: %s", str(ex)) return def onData(self, interest, data): """ FileSync: To be written (TBW) """ # TODO: Verify packet self.keyChain.verifyData(data, self.onVerified, self.onVerifyFailed) util.dump("Got data packet with name", data.getName().toUri()) util.dumpData(data) content = fileSyncBuf_pb2.FileSync() content.ParseFromString(data.getContent().toRawStr()) print("Type: " + str(content.dataType) + ", data: " + content.data) if self.getNowMilliseconds() - content.timestamp * 1000.0 < 120000.0: # Use getattr because "from" is a reserved keyword. name = getattr(content, "from") prefix = data.getName().getPrefix(-2).toUri() sessionNo = int(data.getName().get(-2).toEscapedString()) sequenceNo = int(data.getName().get(-1).toEscapedString()) nameAndSession = name + str(sessionNo) l = 0 # Update roster. while l < len(self.roster): entry = self.roster[l] tempName = entry[0:len(entry) - 10] tempSessionNo = int(entry[len(entry) - 10:]) if (name != tempName and content.dataType != fileSyncBuf_pb2.FileSync.UNSUBSCRIBE): l += 1 else: if name == tempName and sessionNo > tempSessionNo: self.roster[l] = nameAndSession break if l == len(self.roster): self.roster.append(nameAndSession) print(name + ": Subscribe") # Use getattr because "from" is a reserved keyword. if (content.dataType == fileSyncBuf_pb2.FileSync.UPDATE and not self.isRecoverySyncState and getattr(content, "from") != self.screenName): self.onRecievedFileUpdate(content) elif content.dataType == fileSyncBuf_pb2.FileSync.UNSUBSCRIBE: # leave message try: n = self.roster.index(nameAndSession) if name != self.screenName: self.roster.pop(n) print(name + ": Unsubscribe") except ValueError: pass def onVerified(self, data): #TODO print("Data packet verified") def onVerifyFailed(self, data): #TODO print("Data packet failed verification") def onRecievedFileUpdate(self, content): print(getattr(content, "from") + ": " + content.data) fileName = self.path + getattr(content, "from") if (os.path.isfile(fileName)): # update file logging.info("Updating file" + fileName) fileTemp = open(fileName, 'r+') fileTemp.write(content.data) fileTemp.close() else: # create file logging.info("Creating file" + fileName) fileTemp = open(fileName, "w") fileTemp.write(content.data) fileTemp.close() def onRegisterFailed(prefix): print("Register failed for prefix " + prefix.toUri()) def heartbeat(self, interest): """ This repeatedly calls itself after a timeout to send a heartbeat message (pksync message type HELLO). This method has an "interest" argument because we use it as the onTimeout for Face.expressInterest. """ if len(self.syncDataCache) == 0: self.syncDataCacheAppend(fileSyncBuf_pb2.FileSync.SUBSCRIBE, "xxx") self.sync.publishNextSequenceNo() self.syncDataCacheAppend(fileSyncBuf_pb2.FileSync.HELLO, "xxx") # Call again. # TODO: Are we sure using a "/local/timeout" interest is the best future call # approach? timeout = Interest(Name("/local/timeout")) timeout.setInterestLifetimeMilliseconds(60000) self.face.expressInterest(timeout, self.dummyOnData, self.heartbeat) def onTimeout(self, interest): """ FileSync: To be written (TBW) """ util.dump("Time out for interest", interest.getName().toUri()) def syncDataCacheAppend(self, dataType, data): """ FileSync: To be written (TBW) ChronoChat: Append a new CachedMessage to messageCache_, using given messageType and message, the sequence number from _sync.getSequenceNo() and the current time. Also remove elements from the front of the cache as needed to keep the size to _maxMessageCacheLength. """ cachedData = self.CachedData(self.sync.getSequenceNo(), dataType, data, self.getNowMilliseconds()) self.syncDataCache.append(cachedData) while len(self.syncDataCache) > self.maxDataCacheLength: self.syncDataCache.pop(0) @staticmethod def getNowMilliseconds(): """ Get the current time in milliseconds. :return: The current time in milliseconds since 1/1/1970, including fractions of a millisecond. :rtype: float """ return time.time() * 1000.0 @staticmethod def getRandomString(): """ Generate a random name for ChronoSync. """ #TODO: better seed seed = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789" result = "" for i in range(10): # Using % means the distribution isn't uniform, but that's OK. position = random.randrange(256) % len(seed) result += seed[position] return result @staticmethod def dummyOnData(interest, data): """ This is a do-nothing onData for using expressInterest for timeouts. This should never be called. """ pass class CachedData(object): def __init__(self, sequenceNo, dataType, data, time): self.sequenceNo = sequenceNo self.dataType = dataType self.data = data self.time = time
class IotController(BaseNode): """ The controller class has a few built-in commands: - listDevices: return the names and capabilities of all attached devices - certificateRequest: takes public key information and returns name of new certificate - updateCapabilities: should be sent periodically from IotNodes to update their command lists - addDevice: add a device based on HMAC It is unlikely that you will need to subclass this. """ def __init__(self, nodeName, networkName, applicationDirectory = ""): super(IotController, self).__init__() self.deviceSuffix = Name(nodeName) self.networkPrefix = Name(networkName) self.prefix = Name(self.networkPrefix).append(self.deviceSuffix) self._policyManager.setEnvironmentPrefix(self.networkPrefix) self._policyManager.setTrustRootIdentity(self.prefix) self._policyManager.setDeviceIdentity(self.prefix) self._policyManager.updateTrustRules() # the controller keeps a directory of capabilities->names self._directory = defaultdict(list) # keep track of who's still using HMACs # key is device serial, value is the HmacHelper self._hmacDevices = {} # our capabilities self._baseDirectory = {} # add the built-ins self._insertIntoCapabilities('listDevices', 'directory', False) self._insertIntoCapabilities('updateCapabilities', 'capabilities', True) self._directory.update(self._baseDirectory) # Set up application directory if applicationDirectory == "": applicationDirectory = os.path.expanduser('~/.ndn/iot/applications') self._applicationDirectory = applicationDirectory self._applications = dict() def _insertIntoCapabilities(self, commandName, keyword, isSigned): newUri = Name(self.prefix).append(Name(commandName)).toUri() self._baseDirectory[keyword] = [{'signed':isSigned, 'name':newUri}] def beforeLoopStart(self): if not self._policyManager.hasRootSignedCertificate(): # make one.... self.log.warn('Generating controller certificate...') newKey = self._identityManager.generateRSAKeyPairAsDefault( self.prefix, isKsk=True) newCert = self._identityManager.selfSign(newKey) self._identityManager.addCertificateAsDefault(newCert) # Trusting root's own certificate upon each run # TODO: debug where application starts first and controller starts second, application's interest cannot be verified self._rootCertificate = self._keyChain.getCertificate(self.getDefaultCertificateName()) self._policyManager._certificateCache.insertCertificate(self._rootCertificate) self._memoryContentCache = MemoryContentCache(self.face) self.face.setCommandSigningInfo(self._keyChain, self.getDefaultCertificateName()) self._memoryContentCache.registerPrefix(self.prefix, onRegisterFailed = self.onRegisterFailed, onRegisterSuccess = None, onDataNotFound = self._onCommandReceived) # Serve root certificate in our memoryContentCache self._memoryContentCache.add(self._rootCertificate) self.loadApplications() self.loop.call_soon(self.onStartup) ###### # Initial configuration ####### # TODO: deviceSuffix will be replaced by deviceSerial def _addDeviceToNetwork(self, deviceSerial, newDeviceSuffix, pin): h = HmacHelper(pin) self._hmacDevices[deviceSerial] = h d = DeviceConfigurationMessage() for source, dest in [(self.networkPrefix, d.configuration.networkPrefix), (self.deviceSuffix, d.configuration.controllerName), (newDeviceSuffix, d.configuration.deviceSuffix)]: for i in range(source.size()): component = source.get(i) dest.components.append(component.getValue().toRawStr()) interestName = Name('/home/configure').append(Name(deviceSerial)) encodedParams = ProtobufTlv.encode(d) interestName.append(encodedParams) interest = Interest(interestName) h.signInterest(interest) self.face.expressInterest(interest, self._deviceAdditionResponse, self._deviceAdditionTimedOut) def _deviceAdditionTimedOut(self, interest): deviceSerial = str(interest.getName().get(2).getValue()) self.log.warn("Timed out trying to configure device " + deviceSerial) # don't try again self._hmacDevices.pop(deviceSerial) def _deviceAdditionResponse(self, interest, data): status = data.getContent().toRawStr() deviceSerial = str(interest.getName().get(2).getValue()) hmacChecker = self._hmacDevices[deviceSerial] if (hmacChecker.verifyData(data)): self.log.info("Received {} from {}".format(status, deviceSerial)) else: self.log.warn("Received invalid HMAC from {}".format(deviceSerial)) ###### # Certificate signing ###### def _handleCertificateRequest(self, interest): """ Extracts a public key name and key bits from a command interest name component. Generates a certificate if the request is verifiable. This expects an HMAC signed interest. """ message = CertificateRequestMessage() commandParamsTlv = interest.getName().get(self.prefix.size()+1) ProtobufTlv.decode(message, commandParamsTlv.getValue()) signature = HmacHelper.extractInterestSignature(interest) deviceSerial = str(signature.getKeyLocator().getKeyName().get(-1).getValue()) response = Data(interest.getName()) certData = None hmac = None try: hmac = self._hmacDevices[deviceSerial] if hmac.verifyInterest(interest): certData = self._createCertificateFromRequest(message) # remove this hmac; another request will require a new pin self._hmacDevices.pop(deviceSerial) except KeyError: self.log.warn('Received certificate request for device with no registered key') except SecurityException as e: self.log.warn('Could not create device certificate: ' + str(e)) else: self.log.info('Creating certificate for device {}'.format(deviceSerial)) if certData is not None: response.setContent(certData.wireEncode()) response.getMetaInfo().setFreshnessPeriod(10000) # should be good even longer else: response.setContent("Denied") if hmac is not None: hmac.signData(response) self.sendData(response, False) def _createCertificateFromRequest(self, message): """ Generate an IdentityCertificate from the public key information given. """ # TODO: Verify the certificate was actually signed with the private key # matching the public key we are issuing a cert for!! keyComponents = message.command.keyName.components keyName = Name("/".join(keyComponents)) self.log.debug("Key name: " + keyName.toUri()) if not self._policyManager.getEnvironmentPrefix().match(keyName): # we do not issue certs for keys outside of our network return None keyDer = Blob(message.command.keyBits) keyType = message.command.keyType try: self._identityStorage.addKey(keyName, keyType, keyDer) except SecurityException as e: print(e) # assume this is due to already existing? pass certificate = self._identityManager._generateCertificateForKey(keyName) self._keyChain.sign(certificate, self.getDefaultCertificateName()) # store it for later use + verification self._identityStorage.addCertificate(certificate) self._policyManager._certificateCache.insertCertificate(certificate) return certificate ###### # Device Capabilities ###### def _updateDeviceCapabilities(self, interest): """ Take the received capabilities update interest and update our directory listings. """ # we assume the sender is the one who signed the interest... signature = self._policyManager._extractSignature(interest) certificateName = signature.getKeyLocator().getKeyName() senderIdentity = IdentityCertificate.certificateNameToPublicKeyName(certificateName).getPrefix(-1) self.log.info('Updating capabilities for {}'.format(senderIdentity.toUri())) # get the params from the interest name messageComponent = interest.getName().get(self.prefix.size()+1) message = UpdateCapabilitiesCommandMessage() ProtobufTlv.decode(message, messageComponent.getValue()) # we remove all the old capabilities for the sender tempDirectory = defaultdict(list) for keyword in self._directory: tempDirectory[keyword] = [cap for cap in self._directory[keyword] if not senderIdentity.match(Name(cap['name']))] # then we add the ones from the message for capability in message.capabilities: capabilityPrefix = Name() for component in capability.commandPrefix.components: capabilityPrefix.append(component) commandUri = capabilityPrefix.toUri() if not senderIdentity.match(capabilityPrefix): self.log.error("Node {} tried to register another prefix: {} - ignoring update".format( senderIdentity.toUri(),commandUri)) else: for keyword in capability.keywords: allUris = [info['name'] for info in tempDirectory[keyword]] if capabilityPrefix not in allUris: listing = {'signed':capability.needsSignature, 'name':commandUri} tempDirectory[keyword].append(listing) self._directory = tempDirectory def _prepareCapabilitiesList(self, interestName): """ Responds to a directory listing request with JSON """ dataName = Name(interestName).append(Name.Component.fromNumber(int(time.time()))) response = Data(dataName) response.setContent(json.dumps(self._directory)) return response ##### # Interest handling #### def _onCommandReceived(self, prefix, interest, face, interestFilterId, filter): """ """ interestName = interest.getName() #if it is a certificate name, serve the certificate # TODO: since we've memoryContentCache serving root cert now, this should no longer be required try: if interestName.isPrefixOf(self.getDefaultCertificateName()): foundCert = self._identityManager.getCertificate(self.getDefaultCertificateName()) self.log.debug("Serving certificate request") self.face.putData(foundCert) return except SecurityException as e: # We don't have this certificate, this is probably not a certificate request # TODO: this does not differentiate from certificate request but certificate not exist; should update print(str(e)) pass afterPrefix = interestName.get(prefix.size()).toEscapedString() if afterPrefix == "listDevices": #compose device list self.log.debug("Received device list request") response = self._prepareCapabilitiesList(interestName) self.sendData(response) elif afterPrefix == "certificateRequest": #build and sign certificate self.log.debug("Received certificate request") self._handleCertificateRequest(interest) elif afterPrefix == "updateCapabilities": # needs to be signed! self.log.debug("Received capabilities update") def onVerifiedCapabilities(interest): print("capabilities good") response = Data(interest.getName()) response.setContent(str(time.time())) self.sendData(response) self._updateDeviceCapabilities(interest) self._keyChain.verifyInterest(interest, onVerifiedCapabilities, self.verificationFailed) elif afterPrefix == "requests": # application request to publish under some names received; need to be signed def onVerifiedAppRequest(interest): # TODO: for now, we automatically grant access to any valid signed interest print("verified! send response!") message = AppRequestMessage() ProtobufTlv.decode(message, interest.getName().get(prefix.size() + 1).getValue()) certName = Name("/".join(message.command.idName.components)) dataPrefix = Name("/".join(message.command.dataPrefix.components)) appName = message.command.appName isUpdated = self.updateTrustSchema(appName, certName, dataPrefix, True) response = Data(interest.getName()) if isUpdated: response.setContent("{\"status\": 200, \"message\": \"granted, trust schema updated OK\" }") self.log.info("Verified and granted application publish request") else: response.setContent("{\"status\": 400, \"message\": \"not granted, requested publishing namespace already exists\" }") self.log.info("Verified and but requested namespace already exists") self.sendData(response) return def onVerificationFailedAppRequest(interest): print("application request verify failed!") response = Data(interest.getName()) response.setContent("{\"status\": 401, \"message\": \"command interest verification failed\" }") self.sendData(response) self.log.info("Received application request: " + interestName.toUri()) #print("Verifying with trust schema: ") #print(self._policyManager.config) self._keyChain.verifyInterest(interest, onVerifiedAppRequest, onVerificationFailedAppRequest) else: print("Got interest unable to answer yet: " + interest.getName().toUri()) if interest.getExclude(): print("interest has exclude: " + interest.getExclude().toUri()) # response = Data(interest.getName()) # response.setContent("500") # response.getMetaInfo().setFreshnessPeriod(1000) # self.sendData(response) def onStartup(self): # begin taking add requests self.loop.call_soon(self.displayMenu) self.loop.add_reader(stdin, self.handleUserInput) def displayMenu(self): menuStr = "\n" menuStr += "P)air a new device with serial and PIN\n" menuStr += "D)irectory listing\n" menuStr += "E)xpress an interest\n" menuStr += "L)oad hosted applications (" + (self._applicationDirectory) + ")\n" menuStr += "Q)uit\n" print(menuStr) print ("> ", end="") stdout.flush() def listDevices(self): menuStr = '' for capability, commands in self._directory.items(): menuStr += '{}:\n'.format(capability) for info in commands: signingStr = 'signed' if info['signed'] else 'unsigned' menuStr += '\t{} ({})\n'.format(info['name'], signingStr) print(menuStr) self.loop.call_soon(self.displayMenu) def loadApplicationsMenuSelect(self): try: confirm = input('This will override existing trust schemas, continue? (Y/N): ').upper().startswith('Y') if confirm: self.loadApplications(override = True) else: print("Aborted") except KeyboardInterrupt: print("Aborted") finally: self.loop.call_soon(self.displayMenu) def onInterestTimeout(self, interest): print('Interest timed out: {}'.interest.getName().toUri()) def onDataReceived(self, interest, data): print('Received data named: {}'.format(data.getName().toUri())) print('Contents:\n{}'.format(data.getContent().toRawStr())) def expressInterest(self): try: interestName = input('Interest name: ') if len(interestName): toSign = input('Signed? (y/N): ').upper().startswith('Y') interest = Interest(Name(interestName)) interest.setInterestLifetimeMilliseconds(5000) interest.setChildSelector(1) if (toSign): self.face.makeCommandInterest(interest) self.face.expressInterest(interest, self.onDataReceived, self.onInterestTimeout) else: print("Aborted") except KeyboardInterrupt: print("Aborted") finally: self.loop.call_soon(self.displayMenu) def beginPairing(self): try: deviceSerial = input('Device serial: ') devicePin = input('PIN: ') deviceSuffix = input('Node name: ') except KeyboardInterrupt: print('Pairing attempt aborted') else: if len(deviceSerial) and len(devicePin) and len(deviceSuffix): self._addDeviceToNetwork(deviceSerial, Name(deviceSuffix), devicePin.decode('hex')) else: print('Pairing attempt aborted') finally: self.loop.call_soon(self.displayMenu) def handleUserInput(self): inputStr = stdin.readline().upper() if inputStr.startswith('D'): self.listDevices() elif inputStr.startswith('P'): self.beginPairing() elif inputStr.startswith('E'): self.expressInterest() elif inputStr.startswith('Q'): self.stop() elif inputStr.startswith('L'): self.loadApplicationsMenuSelect() else: self.loop.call_soon(self.displayMenu) ######################## # application trust schema distribution ######################## def updateTrustSchema(self, appName, certName, dataPrefix, publishNew = False): if appName in self._applications: if dataPrefix.toUri() in self._applications[appName]["dataPrefix"]: print("some key is configured for namespace " + dataPrefix.toUri() + " for application " + appName + ". Ignoring this request.") return False else: # TODO: Handle malformed conf where validator tree does not exist validatorNode = self._applications[appName]["tree"]["validator"][0] else: # This application does not previously exist, we create its trust schema # (and for now, add in static rules for sync data) self._applications[appName] = {"tree": BoostInfoParser(), "dataPrefix": [], "version": 0} validatorNode = self._applications[appName]["tree"].getRoot().createSubtree("validator") trustAnchorNode = validatorNode.createSubtree("trust-anchor") #trustAnchorNode.createSubtree("type", "file") #trustAnchorNode.createSubtree("file-name", os.path.expanduser("~/.ndn/iot/root.cert")) trustAnchorNode.createSubtree("type", "base64") trustAnchorNode.createSubtree("base64-string", Blob(b64encode(self._rootCertificate.wireEncode().toBytes()), False).toRawStr()) #create cert verification rule # TODO: the idea for this would be, if the cert has /home-prefix/<one-component>/KEY/ksk-*/ID-CERT, then it should be signed by fixed controller(s) # if the cert has /home-prefix/<multiple-components>/KEY/ksk-*/ID-CERT, then it should be checked hierarchically (this is for subdomain support) certRuleNode = validatorNode.createSubtree("rule") certRuleNode.createSubtree("id", "Certs") certRuleNode.createSubtree("for", "data") filterNode = certRuleNode.createSubtree("filter") filterNode.createSubtree("type", "regex") filterNode.createSubtree("regex", "^[^<KEY>]*<KEY><>*<ID-CERT>") checkerNode = certRuleNode.createSubtree("checker") # TODO: wait how did my first hierarchical verifier work? #checkerNode.createSubtree("type", "hierarchical") checkerNode.createSubtree("type", "customized") checkerNode.createSubtree("sig-type", "rsa-sha256") keyLocatorNode = checkerNode.createSubtree("key-locator") keyLocatorNode.createSubtree("type", "name") # We don't put cert version in there keyLocatorNode.createSubtree("name", Name(self.getDefaultCertificateName()).getPrefix(-1).toUri()) keyLocatorNode.createSubtree("relation", "equal") # Discovery rule: anything that multicasts under my home prefix should be signed, and the signer should have been authorized by root # TODO: This rule as of right now is over-general discoveryRuleNode = validatorNode.createSubtree("rule") discoveryRuleNode.createSubtree("id", "sync-data") discoveryRuleNode.createSubtree("for", "data") filterNode = discoveryRuleNode.createSubtree("filter") filterNode.createSubtree("type", "regex") filterNode.createSubtree("regex", "^[^<MULTICAST>]*<MULTICAST><>*") checkerNode = discoveryRuleNode.createSubtree("checker") # TODO: wait how did my first hierarchical verifier work? #checkerNode.createSubtree("type", "hierarchical") checkerNode.createSubtree("type", "customized") checkerNode.createSubtree("sig-type", "rsa-sha256") keyLocatorNode = checkerNode.createSubtree("key-locator") keyLocatorNode.createSubtree("type", "name") keyLocatorNode.createSubtree("regex", "^[^<KEY>]*<KEY><>*<ID-CERT>") ruleNode = validatorNode.createSubtree("rule") ruleNode.createSubtree("id", dataPrefix.toUri()) ruleNode.createSubtree("for", "data") filterNode = ruleNode.createSubtree("filter") filterNode.createSubtree("type", "name") filterNode.createSubtree("name", dataPrefix.toUri()) filterNode.createSubtree("relation", "is-prefix-of") checkerNode = ruleNode.createSubtree("checker") checkerNode.createSubtree("type", "customized") checkerNode.createSubtree("sig-type", "rsa-sha256") keyLocatorNode = checkerNode.createSubtree("key-locator") keyLocatorNode.createSubtree("type", "name") # We don't put cert version in there keyLocatorNode.createSubtree("name", certName.getPrefix(-1).toUri()) keyLocatorNode.createSubtree("relation", "equal") if not os.path.exists(self._applicationDirectory): os.makedirs(self._applicationDirectory) self._applications[appName]["tree"].write(os.path.join(self._applicationDirectory, appName + ".conf")) self._applications[appName]["dataPrefix"].append(dataPrefix.toUri()) self._applications[appName]["version"] = int(time.time()) if publishNew: # TODO: ideally, this is the trust schema of the application, and does not necessarily carry controller prefix. # We make it carry controller prefix here so that prefix registration / route setup is easier (implementation workaround) data = Data(Name(self.prefix).append(appName).append("_schema").appendVersion(self._applications[appName]["version"])) data.setContent(str(self._applications[appName]["tree"].getRoot())) self.signData(data) self._memoryContentCache.add(data) return True # TODO: putting existing confs into memoryContentCache def loadApplications(self, directory = None, override = False): if not directory: directory = self._applicationDirectory if override: self._applications.clear() if os.path.exists(directory): for f in os.listdir(directory): fullFileName = os.path.join(directory, f) if os.path.isfile(fullFileName) and f.endswith('.conf'): appName = f.rstrip('.conf') if appName in self._applications and not override: print("loadApplications: " + appName + " already exists, do nothing for configuration file: " + fullFileName) else: self._applications[appName] = {"tree": BoostInfoParser(), "dataPrefix": [], "version": int(time.time())} self._applications[appName]["tree"].read(fullFileName) data = Data(Name(self.prefix).append(appName).append("_schema").appendVersion(self._applications[appName]["version"])) data.setContent(str(self._applications[appName]["tree"].getRoot())) self.signData(data) self._memoryContentCache.add(data) try: validatorTree = self._applications[appName]["tree"]["validator"][0] for rule in validatorTree["rule"]: self._applications[appName]["dataPrefix"].append(rule["id"][0].value) # TODO: don't swallow any general exceptions, we want to catch only KeyError (make sure) here except Exception as e: print("loadApplications parse configuration file " + fullFileName + " : " + str(e)) return
class Chat(object): def __init__(self, screenName, chatRoom, hubPrefix, face, keyChain, certificateName): self._screenName = screenName self._chatRoom = chatRoom self._face = face self._keyChain = keyChain self._certificateName = certificateName self._messageCache = [] # of CachedMessage self._roster = [] # of str self._maxMessageCacheLength = 100 self._isRecoverySyncState = True self._syncLifetime = 5000.0 # milliseconds # This should only be called once, so get the random string here. self._chatPrefix = Name(hubPrefix).append(self._chatRoom).append( self._getRandomString()) session = int(round(self.getNowMilliseconds() / 1000.0)) self._userName = self._screenName + str(session) self._sync = ChronoSync2013( self._sendInterest, self._initial, self._chatPrefix, Name("/ndn/broadcast/ChronoChat-0.3").append(self._chatRoom), session, face, keyChain, certificateName, self._syncLifetime, onRegisterFailed) face.registerPrefix(self._chatPrefix, self._onInterest, onRegisterFailed) def sendMessage(self, chatMessage): """ Send a chat message. """ if len(self._messageCache) == 0: self._messageCacheAppend(chatbuf_pb2.ChatMessage.JOIN, "xxx") # Ignore an empty message. # Forming Sync Data Packet. if chatMessage != "": self._sync.publishNextSequenceNo() self._messageCacheAppend(chatbuf_pb2.ChatMessage.CHAT, chatMessage) print(self._screenName + ": " + chatMessage) def leave(self): """ Send the leave message and leave. """ self._sync.publishNextSequenceNo() self._messageCacheAppend(chatbuf_pb2.ChatMessage.LEAVE, "xxx") @staticmethod def getNowMilliseconds(): """ Get the current time in milliseconds. :return: The current time in milliseconds since 1/1/1970, including fractions of a millisecond. :rtype: float """ return time.time() * 1000.0 def _initial(self): """ Push the JOIN message in to the messageCache_, update roster and start the heartbeat. """ # Set the heartbeat timeout using the Interest timeout mechanism. The # heartbeat() function will call itself again after a timeout. # TODO: Are we sure using a "/local/timeout" interest is the best future call # approach? timeout = Interest(Name("/local/timeout")) timeout.setInterestLifetimeMilliseconds(60000) self._face.expressInterest(timeout, self._dummyOnData, self._heartbeat) try: self._roster.index(self._userName) except ValueError: self._roster.append(self._userName) print("Member: " + self._screenName) print(self._screenName + ": Join") self._messageCacheAppend(chatbuf_pb2.ChatMessage.JOIN, "xxx") def _sendInterest(self, syncStates, isRecovery): """ Send a Chat Interest to fetch chat messages after the user gets the Sync data packet back but will not send interest. """ # This is used by _onData to decide whether to display the chat messages. self._isRecoverySyncState = isRecovery sendList = [] # of str sessionNoList = [] # of int sequenceNoList = [] # of int for j in range(len(syncStates)): syncState = syncStates[j] nameComponents = Name(syncState.getDataPrefix()) tempName = nameComponents.get(-1).toEscapedString() sessionNo = syncState.getSessionNo() if not tempName == self._screenName: index = -1 for k in range(len(sendList)): if sendList[k] == syncState.getDataPrefix(): index = k break if index != -1: sessionNoList[index] = sessionNo sequenceNoList[index] = syncState.getSequenceNo() else: sendList.append(syncState.getDataPrefix()) sessionNoList.append(sessionNo) sequenceNoList.append(syncState.getSequenceNo()) for i in range(len(sendList)): uri = (sendList[i] + "/" + str(sessionNoList[i]) + "/" + str(sequenceNoList[i])) interest = Interest(Name(uri)) interest.setInterestLifetimeMilliseconds(self._syncLifetime) self._face.expressInterest(interest, self._onData, self._chatTimeout) def _onInterest(self, prefix, interest, face, interestFilterId, filter): """ Send back a Chat Data Packet which contains the user's message. """ content = chatbuf_pb2.ChatMessage() sequenceNo = int(interest.getName().get(self._chatPrefix.size() + 1).toEscapedString()) gotContent = False for i in range(len(self._messageCache) - 1, -1, -1): message = self._messageCache[i] if message.sequenceNo == sequenceNo: if message.messageType != chatbuf_pb2.ChatMessage.CHAT: # Use setattr because "from" is a reserved keyword. setattr(content, "from", self._screenName) content.to = self._chatRoom content.type = message.messageType content.timestamp = int(round(message.time / 1000.0)) else: setattr(content, "from", self._screenName) content.to = self._chatRoom content.type = message.messageType content.data = message.message content.timestamp = int(round(message.time / 1000.0)) gotContent = True break if gotContent: # TODO: Check if this works in Python 3. array = content.SerializeToString() data = Data(interest.getName()) data.setContent(Blob(array)) self._keyChain.sign(data, self._certificateName) try: face.putData(data) except Exception as ex: logging.getLogger(__name__).error( "Error in transport.send: %s", str(ex)) return def _onData(self, interest, data): """ Process the incoming Chat data. """ # TODO: Check if this works in Python 3. content = chatbuf_pb2.ChatMessage() content.ParseFromString(data.getContent().toBytes()) if self.getNowMilliseconds() - content.timestamp * 1000.0 < 120000.0: # Use getattr because "from" is a reserved keyword. name = getattr(content, "from") prefix = data.getName().getPrefix(-2).toUri() sessionNo = int(data.getName().get(-2).toEscapedString()) sequenceNo = int(data.getName().get(-1).toEscapedString()) nameAndSession = name + str(sessionNo) l = 0 # Update roster. while l < len(self._roster): entry = self._roster[l] tempName = entry[0:len(entry) - 10] tempSessionNo = int(entry[len(entry) - 10:]) if (name != tempName and content.type != chatbuf_pb2.ChatMessage.LEAVE): l += 1 else: if name == tempName and sessionNo > tempSessionNo: self._roster[l] = nameAndSession break if l == len(self._roster): self._roster.append(nameAndSession) print(name + ": Join") # Set the alive timeout using the Interest timeout mechanism. # TODO: Are we sure using a "/local/timeout" interest is the best # future call approach? timeout = Interest(Name("/local/timeout")) timeout.setInterestLifetimeMilliseconds(120000) self._face.expressInterest( timeout, self._dummyOnData, self._makeAlive(sequenceNo, name, sessionNo, prefix)) # isRecoverySyncState_ was set by sendInterest. # TODO: If isRecoverySyncState_ changed, this assumes that we won't get # data from an interest sent before it changed. # Use getattr because "from" is a reserved keyword. if (content.type == chatbuf_pb2.ChatMessage.CHAT and not self._isRecoverySyncState and getattr(content, "from") != self._screenName): print(getattr(content, "from") + ": " + content.data) elif content.type == chatbuf_pb2.ChatMessage.LEAVE: # leave message try: n = self._roster.index(nameAndSession) if name != self._screenName: self._roster.pop(n) print(name + ": Leave") except ValueError: pass @staticmethod def _chatTimeout(interest): print("Timeout waiting for chat data") def _heartbeat(self, interest): """ This repeatedly calls itself after a timeout to send a heartbeat message (chat message type HELLO). This method has an "interest" argument because we use it as the onTimeout for Face.expressInterest. """ if len(self._messageCache) == 0: self._messageCacheAppend(chatbuf_pb2.ChatMessage.JOIN, "xxx") self._sync.publishNextSequenceNo() self._messageCacheAppend(chatbuf_pb2.ChatMessage.HELLO, "xxx") # Call again. # TODO: Are we sure using a "/local/timeout" interest is the best future call # approach? timeout = Interest(Name("/local/timeout")) timeout.setInterestLifetimeMilliseconds(60000) self._face.expressInterest(timeout, self._dummyOnData, self._heartbeat) def _makeAlive(self, tempSequenceNo, name, sessionNo, prefix): """ Return a function for onTimeout which calls _alive. """ def f(interest): self._alive(interest, tempSequenceNo, name, sessionNo, prefix) return f def _alive(self, interest, tempSequenceNo, name, sessionNo, prefix): """ This is called after a timeout to check if the user with prefix has a newer sequence number than the given tempSequenceNo. If not, assume the user is idle and remove from the roster and print a leave message. This method has an "interest" argument because we use it as the onTimeout for Face.expressInterest. """ sequenceNo = self._sync.getProducerSequenceNo(prefix, sessionNo) nameAndSession = name + sessionNo try: n = self._roster.index(nameAndSession) except ValueError: n = -1 if sequenceNo != -1 and n >= 0: if tempSequenceNo == sequenceNo: self._roster.pop(n) print(name + ": Leave") def _messageCacheAppend(self, messageType, message): """ Append a new CachedMessage to messageCache_, using given messageType and message, the sequence number from _sync.getSequenceNo() and the current time. Also remove elements from the front of the cache as needed to keep the size to _maxMessageCacheLength. """ self._messageCache.append( self._CachedMessage(self._sync.getSequenceNo(), messageType, message, self.getNowMilliseconds())) while len(self._messageCache) > self._maxMessageCacheLength: self._messageCache.pop(0) @staticmethod def _getRandomString(): """ Generate a random name for ChronoSync. """ seed = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM0123456789" result = "" for i in range(10): # Using % means the distribution isn't uniform, but that's OK. position = random.randrange(256) % len(seed) result += seed[position] return result @staticmethod def _dummyOnData(interest, data): """ This is a do-nothing onData for using expressInterest for timeouts. This should never be called. """ pass class _CachedMessage(object): def __init__(self, sequenceNo, messageType, message, time): self.sequenceNo = sequenceNo self.messageType = messageType self.message = message self.time = time
rp = RepoCommandParameter() dataPrefix = Name("/example/data/1/test/test1") rp.setName(dataPrefix) rp.setStartBlockId(0) interest = Interest(Name("/example/repo/1").append("insert").append(rp.wireEncode())) 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) # Make a Face just so that we can sign the interest. face = Face("localhost") face.setCommandSigningInfo(keyChain, certificateName) face.makeCommandInterest(interest) callbacks = Callbacks() print interest.getName().toUri() face.expressInterest(interest, callbacks.onData, callbacks.onTimeout) face.registerPrefix(dataPrefix, callbacks.onInterest, callbacks.onRegisterFailed)