def _processRecoveryInterest(self, interest, syncDigest, face): logging.getLogger(__name__).info("processRecoveryInterest") if self._logFind(syncDigest) != -1: tempContent = SyncStateMsg() for i in range(self._digestTree.size()): content = getattr(tempContent, "ss").add() content.name = self._digestTree.get(i).getDataPrefix() content.type = SyncState_UPDATE content.seqno.seq = self._digestTree.get(i).getSequenceNo() content.seqno.session = self._digestTree.get(i).getSessionNo() if len(getattr(tempContent, "ss")) != 0: # TODO: Check if this works in Python 3. #pylint: disable=E1103 array = tempContent.SerializeToString() #pylint: enable=E1103 data = Data(interest.getName()) data.setContent(Blob(array)) if interest.getName().get(-1).toEscapedString() == "00": # Limit the lifetime of replies to interest for "00" since # they can be different. data.getMetaInfo().setFreshnessPeriod(1000) self._keyChain.sign(data, self._certificateName) try: face.putData(data) except Exception as ex: logging.getLogger(__name__).error( "Error in face.putData: %s", str(ex)) return logging.getLogger(__name__).info("send recovery data back") logging.getLogger(__name__).info("%s", interest.getName().toUri())
def _createDKeyData(self, startTimeStamp, endTimeStamp, keyName, privateKeyBlob, certificateKey): """ Create a D-KEY Data packet with an EncryptedContent for the given private key, encrypted with the certificate key. :param str startTimeStamp: The start time stamp string to put in the name. :param str endTimeStamp: The end time stamp string to put in the name. :param Name keyName The key name to put in the data packet name and the EncryptedContent key locator. :param Blob privateKeyBlob: A Blob of the encoded private key. :param Blob certificateKey: The certificate key encoding, used to encrypt the private key. :return: The Data packet. :rtype: Data """ name = Name(self._namespace) name.append(Encryptor.NAME_COMPONENT_D_KEY) name.append(startTimeStamp).append(endTimeStamp) data = Data(name) data.getMetaInfo().setFreshnessPeriod( self._freshnessHours * GroupManager.MILLISECONDS_IN_HOUR) encryptParams = EncryptParams(EncryptAlgorithmType.RsaOaep) Encryptor.encryptData(data, privateKeyBlob, keyName, certificateKey, encryptParams) self._keyChain.sign(data) return data
def _processRecoveryInterest(self, interest, syncDigest, face): logging.getLogger(__name__).info("processRecoveryInterest") if self._logFind(syncDigest) != -1: tempContent = SyncStateMsg() for i in range(self._digestTree.size()): content = getattr(tempContent, "ss").add() content.name = self._digestTree.get(i).getDataPrefix() content.type = SyncState_UPDATE content.seqno.seq = self._digestTree.get(i).getSequenceNo() content.seqno.session = self._digestTree.get(i).getSessionNo() if len(getattr(tempContent, "ss")) != 0: # TODO: Check if this works in Python 3. #pylint: disable=E1103 array = tempContent.SerializeToString() #pylint: enable=E1103 data = Data(interest.getName()) data.setContent(Blob(array)) if interest.getName().get(-1).toEscapedString() == "00": # Limit the lifetime of replies to interest for "00" since # they can be different. data.getMetaInfo().setFreshnessPeriod(1000) self._keyChain.sign(data, self._certificateName) try: face.putData(data) except Exception as ex: logging.getLogger(__name__).error( "Error in face.putData: %s", str(ex)) return logging.getLogger(__name__).info("send recovery data back") logging.getLogger(__name__).info("%s", interest.getName().toUri())
def _createDKeyData(self, startTimeStamp, endTimeStamp, keyName, privateKeyBlob, certificateKey): """ Create a D-KEY Data packet with an EncryptedContent for the given private key, encrypted with the certificate key. :param str startTimeStamp: The start time stamp string to put in the name. :param str endTimeStamp: The end time stamp string to put in the name. :param Name keyName The key name to put in the data packet name and the EncryptedContent key locator. :param Blob privateKeyBlob: A Blob of the encoded private key. :param Blob certificateKey: The certificate key encoding, used to encrypt the private key. :return: The Data packet. :rtype: Data """ name = Name(self._namespace) name.append(Encryptor.NAME_COMPONENT_D_KEY) name.append(startTimeStamp).append(endTimeStamp) data = Data(name) data.getMetaInfo().setFreshnessPeriod( self._freshnessHours * GroupManager.MILLISECONDS_IN_HOUR) encryptParams = EncryptParams(EncryptAlgorithmType.RsaOaep) Encryptor.encryptData( data, privateKeyBlob, keyName, certificateKey, encryptParams) self._keyChain.sign(data) return data
def __init__(self, identity, dataset, keyChain, face): self._identity = identity self._keyChain = keyChain self._face = face # storage_ is for the KEK and KDKs. self._storage = InMemoryStorageRetaining() # The NAC identity is: <identity>/NAC/<dataset> # Generate the NAC key. nacIdentity = self._keyChain.createIdentityV2( Name(identity.getName()) .append(EncryptorV2.NAME_COMPONENT_NAC).append(dataset), RsaKeyParams()) self._nacKey = nacIdentity.getDefaultKey() if self._nacKey.getKeyType() != KeyType.RSA: logging.getLogger(__name__).info( "Cannot re-use existing KEK/KDK pair, as it is not an RSA key, regenerating") self._nacKey = self._keyChain.createKey(nacIdentity, RsaKeyParams()) nacKeyId = self._nacKey.getName().get(-1) kekPrefix = Name(self._nacKey.getIdentityName()).append( EncryptorV2.NAME_COMPONENT_KEK) kekData = Data(self._nacKey.getDefaultCertificate()) kekData.setName(Name(kekPrefix).append(nacKeyId)) kekData.getMetaInfo().setFreshnessPeriod( AccessManagerV2.DEFAULT_KEK_FRESHNESS_PERIOD_MS) self._keyChain.sign(kekData, SigningInfo(self._identity)) # kek looks like a cert, but doesn't have ValidityPeriod self._storage.insert(kekData) def serveFromStorage(prefix, interest, face, interestFilterId, filter): data = self._storage.find(interest) if data != None: logging.getLogger(__name__).info("Serving " + data.getName().toUri() + " from in-memory-storage") try: face.putData(data) except: logging.exception("AccessManagerV2: Error in Face.putData") else: logging.getLogger(__name__).info("Didn't find data for " + interest.getName().toUri()) # TODO: Send NACK? def onRegisterFailed(prefix): logging.getLogger(__name__).error( "AccessManagerV2: Failed to register prefix " + prefix.toUri()) self._kekRegisteredPrefixId = self._face.registerPrefix( kekPrefix, serveFromStorage, onRegisterFailed) kdkPrefix = Name(self._nacKey.getIdentityName()).append( EncryptorV2.NAME_COMPONENT_KDK).append(nacKeyId) self._kdkRegisteredPrefixId_ = self._face.registerPrefix( kdkPrefix, serveFromStorage, onRegisterFailed)
def addMember(self, memberCertificate): """ Authorize a member identified by memberCertificate to decrypt data under the policy. :param CertificateV2 memberCertificate: The certificate that identifies the member to authorize. :return: The published KDK Data packet. :rtype: Data """ kdkName = Name(self._nacKey.getIdentityName()) kdkName.append(EncryptorV2.NAME_COMPONENT_KDK).append( # key-id self._nacKey.getName().get(-1)).append( EncryptorV2.NAME_COMPONENT_ENCRYPTED_BY).append( memberCertificate.getKeyName()) secretLength = 32 secret = bytearray(secretLength) for i in range(secretLength): secret[i] = _systemRandom.randint(0, 0xff) # To be compatible with OpenSSL which uses a null-terminated string, # replace each 0 with 1. And to be compatible with the Java security # library which interprets the secret as a char array converted to UTF8, # limit each byte to the ASCII range 1 to 127. for i in range(secretLength): if secret[i] == 0: secret[i] = 1 secret[i] &= 0x7f kdkSafeBag = self._keyChain.exportSafeBag( self._nacKey.getDefaultCertificate(), Blob(secret, False).toBytes()) memberKey = PublicKey(memberCertificate.getPublicKey()) encryptedContent = EncryptedContent() encryptedContent.setPayload(kdkSafeBag.wireEncode()) encryptedContent.setPayloadKey( memberKey.encrypt( Blob(secret, False).toBytes(), EncryptAlgorithmType.RsaOaep)) kdkData = Data(kdkName) kdkData.setContent(encryptedContent.wireEncodeV2()) # FreshnessPeriod can serve as a soft access control for revoking access. kdkData.getMetaInfo().setFreshnessPeriod( AccessManagerV2.DEFAULT_KDK_FRESHNESS_PERIOD_MS) self._keyChain.sign(kdkData, SigningInfo(self._identity)) self._storage.insert(kdkData) return kdkData
def _createEKeyData(self, startTimeStamp, endTimeStamp, publicKeyBlob): """ Create an E-KEY Data packet for the given public key. :param str startTimeStamp: The start time stamp string to put in the name. :param str endTimeStamp: The end time stamp string to put in the name. :param Blob publicKeyBlob: A Blob of the public key DER. :return: The Data packet. :rtype: Data """ name = Name(self._namespace) name.append(Encryptor.NAME_COMPONENT_E_KEY).append(startTimeStamp).append(endTimeStamp) data = Data(name) data.getMetaInfo().setFreshnessPeriod(self._freshnessHours * GroupManager.MILLISECONDS_IN_HOUR) data.setContent(publicKeyBlob) self._keyChain.sign(data) return data
def _createEKeyData(self, startTimeStamp, endTimeStamp, publicKeyBlob): """ Create an E-KEY Data packet for the given public key. :param str startTimeStamp: The start time stamp string to put in the name. :param str endTimeStamp: The end time stamp string to put in the name. :param Blob publicKeyBlob: A Blob of the public key DER. :return: The Data packet. :rtype: Data """ name = Name(self._namespace) name.append(Encryptor.NAME_COMPONENT_E_KEY).append( startTimeStamp).append(endTimeStamp) data = Data(name) data.getMetaInfo().setFreshnessPeriod( self._freshnessHours * GroupManager.MILLISECONDS_IN_HOUR) data.setContent(publicKeyBlob) self._keyChain.sign(data) return data
def _makeAndPublishCkData(self, onError): """ Make a CK Data packet for _ckName encrypted by the KEK in _kekData and insert it in the _storage. :param onError: On failure, this calls onError(errorCode, message) where errorCode is from EncryptError.ErrorCode, and message is an error string. :type onError: function object :return: True on success, else False. :rtype: bool """ try: kek = PublicKey(self._kekData.getContent()) content = EncryptedContent() content.setPayload( kek.encrypt(Blob(self._ckBits, False), EncryptAlgorithmType.RsaOaep)) ckData = Data( Name(self._ckName).append( EncryptorV2.NAME_COMPONENT_ENCRYPTED_BY).append( self._kekData.getName())) ckData.setContent(content.wireEncodeV2()) # FreshnessPeriod can serve as a soft access control for revoking access. ckData.getMetaInfo().setFreshnessPeriod( EncryptorV2.DEFAULT_CK_FRESHNESS_PERIOD_MS) self._keyChain.sign(ckData, self._ckDataSigningInfo) self._storage.insert(ckData) logging.getLogger(__name__).info("Publishing CK data: " + ckData.getName().toUri()) return True except Exception as ex: onError( EncryptError.ErrorCode.EncryptionFailure, "Failed to encrypt generated CK with KEK " + self._kekData.getName().toUri()) return False
def _makeAndPublishCkData(self, onError): """ Make a CK Data packet for _ckName encrypted by the KEK in _kekData and insert it in the _storage. :param onError: On failure, this calls onError(errorCode, message) where errorCode is from EncryptError.ErrorCode, and message is an error string. :type onError: function object :return: True on success, else False. :rtype: bool """ try: kek = PublicKey(self._kekData.getContent()) content = EncryptedContent() content.setPayload(kek.encrypt (Blob(self._ckBits, False), EncryptAlgorithmType.RsaOaep)) ckData = Data( Name(self._ckName).append(EncryptorV2.NAME_COMPONENT_ENCRYPTED_BY) .append(self._kekData.getName())) ckData.setContent(content.wireEncodeV2()) # FreshnessPeriod can serve as a soft access control for revoking access. ckData.getMetaInfo().setFreshnessPeriod( EncryptorV2.DEFAULT_CK_FRESHNESS_PERIOD_MS) self._keyChain.sign(ckData, self._ckDataSigningInfo) self._storage.insert(ckData) logging.getLogger(__name__).info("Publishing CK data: " + ckData.getName().toUri()) return True except Exception as ex: onError(EncryptError.ErrorCode.EncryptionFailure, "Failed to encrypt generated CK with KEK " + self._kekData.getName().toUri()) return False
def publish(self, interestName, dataName, content, freshnessPeriod, signingInfo = SigningInfo()): """ Put all the segments in the memory store. :param Name interestName: If the Interest name ends in a segment, immediately send the Data packet for the segment to the Face. :param Name dataName: The Data name, which has components after the Interest name. :param Blob content: The content of the data to be segmented. :param float freshnessPeriod The freshness period of the segments, in milliseconds. :param SigningInfo signingInfo (optional) The SigningInfo for signing segment Data packets. If omitted, use the default SigningInfo(). """ interestSegment = 0 if interestName[-1].isSegment(): interestSegment = interestName[-1].toSegment() rawBuffer = content.buf() iSegmentBegin = 0 iEnd = len(content) maxPacketSize = int(Common.MAX_NDN_PACKET_SIZE / 2) totalSegments = int(len(content) / maxPacketSize) finalBlockId = Name.Component.fromSegment(totalSegments) segmentPrefix = Name(dataName) segmentPrefix.appendVersion(int(Common.getNowMilliseconds())) segmentNo = 0 while(True): iSegmentEnd = iSegmentBegin + maxPacketSize if iSegmentEnd > iEnd: iSegmentEnd = iEnd segmentName = Name(segmentPrefix) segmentName.appendSegment(segmentNo) data = Data(segmentName) data.setContent(Blob(rawBuffer[iSegmentBegin : iSegmentEnd])) data.getMetaInfo().setFreshnessPeriod(freshnessPeriod) data.getMetaInfo().setFinalBlockId(finalBlockId) iSegmentBegin = iSegmentEnd self._keyChain.sign(data, signingInfo) # Only send the segment to the Face if it has a pending interest. # Otherwise, the segment is unsolicited. if interestSegment == segmentNo: self._face.putData(data) # Until InMemoryStorageFifo implements an eviction policy, use InMemoryStorageRetaining. # storage_.insert(*data, freshnessPeriod) self._storage.insert(data) # Make and return a callback since segmentName is different each time. def makeCallback(localSegmentName): def callback(): self._storage.remove(localSegmentName) return callback self._face.callLater(freshnessPeriod, makeCallback(segmentName)) segmentNo += 1 if not (iSegmentBegin < iEnd): break
def publish(self, interestName, dataName, content, freshnessPeriod, signingInfo=SigningInfo()): """ Put all the segments in the memory store. :param Name interestName: If the Interest name ends in a segment, immediately send the Data packet for the segment to the Face. :param Name dataName: The Data name, which has components after the Interest name. :param Blob content: The content of the data to be segmented. :param float freshnessPeriod The freshness period of the segments, in milliseconds. :param SigningInfo signingInfo (optional) The SigningInfo for signing segment Data packets. If omitted, use the default SigningInfo(). """ interestSegment = 0 if interestName[-1].isSegment(): interestSegment = interestName[-1].toSegment() rawBuffer = content.buf() iSegmentBegin = 0 iEnd = len(content) maxPacketSize = int(Common.MAX_NDN_PACKET_SIZE / 2) totalSegments = int(len(content) / maxPacketSize) finalBlockId = Name.Component.fromSegment(totalSegments) segmentPrefix = Name(dataName) segmentPrefix.appendVersion(int(Common.getNowMilliseconds())) segmentNo = 0 while (True): iSegmentEnd = iSegmentBegin + maxPacketSize if iSegmentEnd > iEnd: iSegmentEnd = iEnd segmentName = Name(segmentPrefix) segmentName.appendSegment(segmentNo) data = Data(segmentName) data.setContent(Blob(rawBuffer[iSegmentBegin:iSegmentEnd])) data.getMetaInfo().setFreshnessPeriod(freshnessPeriod) data.getMetaInfo().setFinalBlockId(finalBlockId) iSegmentBegin = iSegmentEnd self._keyChain.sign(data, signingInfo) # Only send the segment to the Face if it has a pending interest. # Otherwise, the segment is unsolicited. if interestSegment == segmentNo: self._face.putData(data) # Until InMemoryStorageFifo implements an eviction policy, use InMemoryStorageRetaining. # storage_.insert(*data, freshnessPeriod) self._storage.insert(data) # Make and return a callback since segmentName is different each time. def makeCallback(localSegmentName): def callback(): self._storage.remove(localSegmentName) return callback self._face.callLater(freshnessPeriod, makeCallback(segmentName)) segmentNo += 1 if not (iSegmentBegin < iEnd): break