PyNDN: Initial import of PyNDN code
Refs #1010 (http://redmine.named-data.net/issues/1010)
diff --git a/PyNDN/ContentObject.py b/PyNDN/ContentObject.py
new file mode 100644
index 0000000..5af7b0c
--- /dev/null
+++ b/PyNDN/ContentObject.py
@@ -0,0 +1,263 @@
+## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# Alexander Afanasyev
+#
+# GNU 3.0 license, See the LICENSE file for more information
+#
+# Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+#
+
+#
+# Based on PyCCN code, copyrighted and licensed as follows
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# BSD license, See the COPYING file for more information
+# Written by: Derek Kulinski <takeda@takeda.tk>
+# Jeff Burke <jburke@ucla.edu>
+#
+
+from . import _ndn
+import utils
+
+class ContentType(utils.Enum):
+ _prefix = "ndn"
+
+CONTENT_DATA = ContentType.new_flag('CONTENT_DATA', 0x0C04C0)
+CONTENT_ENCR = ContentType.new_flag('CONTENT_ENCR', 0x10D091)
+CONTENT_GONE = ContentType.new_flag('CONTENT_GONE', 0x18E344)
+CONTENT_KEY = ContentType.new_flag('CONTENT_KEY', 0x28463F)
+CONTENT_LINK = ContentType.new_flag('CONTENT_LINK', 0x2C834A)
+CONTENT_NACK = ContentType.new_flag('CONTENT_NACK', 0x34008A)
+
+class ContentObject(object):
+ def __init__(self, name = None, content = None, signed_info = None):
+ self.name = name
+ self.content = content
+
+ self.signedInfo = signed_info or SignedInfo()
+ self.digestAlgorithm = None # Default
+
+ # generated
+ self.signature = None
+ self.verified = False
+
+ # py-ndn
+ self.ccn = None # Reference to CCN object
+ self.ccn_data_dirty = True
+ self.ccn_data = None # backing charbuf
+
+ # this is the finalization step
+ # must pass a key here, there is no "default key" because
+ # an NDN Face is not required to create the content object
+ # thus there is no access to the ccn library keystore.
+ #
+ def sign(self, key):
+ self.ccn_data = _ndn.encode_ContentObject(self, self.name.ccn_data, \
+ self.content, self.signedInfo.ccn_data, key)
+ self.ccn_data_dirty = False
+
+ def digest(self):
+ return _ndn.digest_contentobject(self.ccn_data)
+
+ def verify_content(self, handle):
+ return _ndn.verify_content(handle.ccn_data, self.ccn_data)
+
+ def verify_signature(self, key):
+ return _ndn.verify_signature(self.ccn_data, key.ccn_data_public)
+
+ def matchesInterest(self, interest):
+ return _ndn.content_matches_interest(self.ccn_data, interest.ccn_data)
+
+ def __setattr__(self, name, value):
+ if name == 'name' or name == 'content' or name == 'signedInfo' or name == 'digestAlgorithm':
+ self.ccn_data_dirty = True
+
+ if name == 'content':
+ object.__setattr__(self, name, _ndn.content_to_bytes(value))
+ else:
+ object.__setattr__(self, name, value)
+
+ def __getattribute__(self, name):
+ if name == "ccn_data":
+ if object.__getattribute__(self, 'ccn_data_dirty'):
+ raise _ndn.CCNContentObjectError("Call sign() to finalize \
+ before accessing ccn_data for a ContentObject")
+ return object.__getattribute__(self, name)
+
+ # Where do we support versioning and segmentation?
+
+ def __str__(self):
+ ret = []
+ ret.append("Name: %s" % self.name)
+ ret.append("Content: %r" % self.content)
+ ret.append("DigestAlg: %r" % self.digestAlgorithm)
+ ret.append("SignedInfo: %s" % self.signedInfo)
+ ret.append("Signature: %s" % self.signature)
+ return "\n".join(ret)
+
+ def __repr__(self):
+ args = []
+
+ if self.name is not None:
+ args += ["name=%r" % self.name]
+
+ if self.content is not None:
+ args += ["content=%r" % self.content]
+
+ if self.signedInfo is not None:
+ args += ["signed_info=%r" % self.signedInfo]
+
+ if self.signature is not None:
+ args += ["<signed>"]
+
+ return "ndn.ContentObject(%s)" % ", ".join(args)
+
+ def get_ccnb(self):
+ return _ndn.dump_charbuf(self.ccn_data)
+
+ @staticmethod
+ def from_ccnb (ccnb):
+ return _ndn.ContentObject_obj_from_ccn_buffer (ccnb)
+
+class Signature(object):
+ def __init__(self):
+ self.digestAlgorithm = None
+ self.witness = None
+ self.signatureBits = None
+
+ # py-ndn
+ self.ccn_data_dirty = False
+ self.ccn_data = None
+
+ def __setattr__(self, name, value):
+ if name == 'witness' or name == 'signatureBits' or name == 'digestAlgorithm':
+ self.ccn_data_dirty = True
+ object.__setattr__(self, name, value)
+
+ def __getattribute__(self, name):
+ if name == "ccn_data":
+ if object.__getattribute__(self, 'ccn_data_dirty'):
+ self.ccn_data = _ndn.Signature_obj_to_ccn(self)
+ self.ccn_data_dirty = False
+ return object.__getattribute__(self, name)
+
+ def __str__(self):
+ res = []
+ res.append("digestAlgorithm = %s" % self.digestAlgorithm)
+ res.append("witness = %s" % self.witness)
+ res.append("signatureBits = %r" % self.signatureBits)
+ return "\n".join(res)
+
+class SignedInfo(object):
+ def __init__(self, key_digest = None, key_locator = None, type = CONTENT_DATA,
+ freshness = None, final_block = None, py_timestamp = None,
+ timestamp = None):
+
+ self.publisherPublicKeyDigest = key_digest
+
+ if py_timestamp is not None:
+ if timestamp:
+ raise ValueError("You can define only timestamp or py_timestamp")
+ self.timeStamp = utils.py2ccn_time(py_timestamp)
+ else:
+ self.timeStamp = timestamp
+
+ self.type = type
+ self.freshnessSeconds = freshness
+ self.finalBlockID = final_block
+ self.keyLocator = key_locator
+
+ # py-ndn
+ self.ccn_data_dirty = True
+ self.ccn_data = None # backing charbuf
+
+ def __setattr__(self, name, value):
+ if name != "ccn_data" and name != "ccn_data_dirty":
+ self.ccn_data_dirty = True
+
+ if name == "type" and type(value) is not ContentType:
+ value = ContentType(value)
+
+ object.__setattr__(self, name, value)
+
+ def __getattribute__(self, name):
+ if name == "ccn_data":
+ if object.__getattribute__(self, 'ccn_data_dirty'):
+ key_locator = self.keyLocator.ccn_data if self.keyLocator else None
+ self.ccn_data = _ndn.SignedInfo_to_ccn(\
+ self.publisherPublicKeyDigest, self.type, self.timeStamp, \
+ self.freshnessSeconds or (-1), self.finalBlockID, key_locator)
+ self.ccn_data_dirty = False
+
+ if name == "py_timestamp":
+ ts = self.timeStamp
+ if ts is None:
+ return None
+ return None if ts is None else utils.ccn2py_time(ts)
+
+ return object.__getattribute__(self, name)
+
+ def __repr__(self):
+ args = []
+
+ if self.publisherPublicKeyDigest is not None:
+ args += ["key_digest=%r" % self.publisherPublicKeyDigest]
+ if self.keyLocator is not None:
+ args += ["key_locator=%r" % self.keyLocator]
+ if self.type is not None:
+ args += ["type=%r" % self.type]
+ if self.freshnessSeconds is not None:
+ args += ["freshness=%r" % self.freshnessSeconds]
+ if self.finalBlockID is not None:
+ args += ["final_block=%r" % self.finalBlockID]
+ if self.timeStamp is not None:
+ args += ["py_timestamp=%r" % self.py_timestamp]
+
+ return "ndn.SignedInfo(%s)" % ", ".join(args)
+
+#
+#
+# These are not used in signing in Python (all the info needed is in SignedInfo)
+# But it is here in case the parsing of the c library version of signing params
+# is needed.
+
+class SigningParams(object):
+ CCN_SP_TEMPL_TIMESTAMP = 0x0001
+ CCN_SP_TEMPL_FINAL_BLOCK_ID = 0x0002
+ CCN_SP_TEMPL_FRESHNESS = 0x0004
+ CCN_SP_TEMPL_KEY_LOCATOR = 0x0008
+ CCN_SP_FINAL_BLOCK = 0x0010
+ CCN_SP_OMIT_KEY_LOCATOR = 0x0020
+
+ def __init__(self):
+ self.flags; # Use the CCN_SP flags above
+ self.type; # Content type, really should be somewhere else, it's not that related to signing
+ self.freshness;
+
+ # These three are only relevant, for now, if they are coming *from* a c object
+ # otherwise, API version is filled in from CCN_SIGNING_PARAMS_INIT and
+ # both template and key will come from the ContentObject's SignedInfo object
+ self.apiVersion;
+ self.template; # SignedInfo referred to by this content object,
+ self.key; # Key to use - this should filled by a lookup against content object's signedinfo,
+
+ # py-ndn
+ self.ccn_data_dirty = False
+ self.ccn_data = None # backing ccn_signing_params
+
+ def __setattr__(self, name, value):
+ if name != "ccn_data" and name != "ccn_data_dirty":
+ self.ccn_data_dirty = True
+ object.__setattr__(self, name, value)
+
+ def __getattribute__(self, name):
+ if name == "ccn_data":
+ if object.__getattribute__(self, 'ccn_data_dirty'):
+ self.ccn_data = _ndn._ndn_SigningParams_to_ccn(self)
+ self.ccn_data_dirty = False
+ return object.__getattribute__(self, name)
+
+ def __get_ccn(self):
+ pass
+ # Call ccn_signed_info_create
diff --git a/PyNDN/Face.py b/PyNDN/Face.py
new file mode 100644
index 0000000..54803db
--- /dev/null
+++ b/PyNDN/Face.py
@@ -0,0 +1,159 @@
+## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# Alexander Afanasyev
+#
+# GNU 3.0 license, See the LICENSE file for more information
+#
+# Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+#
+
+#
+# Based on PyCCN code, copyrighted and licensed as follows
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# BSD license, See the COPYING file for more information
+# Written by: Derek Kulinski <takeda@takeda.tk>
+# Jeff Burke <jburke@ucla.edu>
+#
+
+import ns.core
+import ns.ndnSIM
+
+# from ns3.ndnSIM import ApiFace, Interest, Name
+
+class Face (object):
+ def __init__(self, node):
+ self._face = ns.ndnSIM.ndn.ApiFace (node)
+
+ def connect (self):
+ pass
+
+ def disconnect (self):
+ pass
+
+ def defer_verification (self, deferVerification = True):
+ pass
+
+ def expressInterestSimple (self, name, onData, onTimeout, template = None):
+
+ pass
+ # class TrivialExpressClosure (Closure.Closure):
+ # __slots__ = ["_baseName", "_onData", "_onTimeout"];
+
+ # def __init__ (self, baseName, onData, onTimeout):
+ # self._baseName = baseName
+ # self._onData = onData
+ # self._onTimeout = onTimeout
+
+ # def upcall(self, kind, upcallInfo):
+ # if (kind == Closure.UPCALL_CONTENT or
+ # kind == Closure.UPCALL_CONTENT_UNVERIFIED or
+ # kind == Closure.UPCALL_CONTENT_UNVERIFIED or
+ # kind == Closure.UPCALL_CONTENT_KEYMISSING or
+ # kind == Closure.UPCALL_CONTENT_RAW):
+ # return self._onData (self._baseName, upcallInfo.Interest, upcallInfo.ContentObject, kind)
+ # elif (kind == Closure.UPCALL_INTEREST_TIMED_OUT):
+ # return self._onTimeout (self._baseName, upcallInfo.Interest)
+ # return Closure.RESULT_OK
+
+ # trivial_closure = TrivialExpressClosure (name, onData, onTimeout)
+ # self.expressInterest (name, trivial_closure, template)
+
+ def setInterestFilterSimple (self, name, onInterest, flags = None):
+ pass
+ # class TrivialFilterClosure (Closure.Closure):
+ # # __slots__ = ["_baseName", "_onInterest"];
+
+ # def __init__ (self, baseName, onInterest):
+ # self._baseName = baseName
+ # self._onInterest = onInterest
+
+ # def upcall(self, kind, upcallInfo):
+ # if (kind == Closure.UPCALL_INTEREST):
+ # return self._onInterest (self._baseName, upcallInfo.Interest)
+ # return Closure.RESULT_OK
+
+ # trivial_closure = TrivialFilterClosure (name, onInterest)
+ # self.setInterestFilter (name, trivial_closure, flags)
+
+ def clearInterestFilter(self, name):
+ pass
+
+ # Blocking!
+ def get(self, name, template = None, timeoutms = 3000):
+ # self._acquire_lock("get")
+ # try:
+ # return _ndn.get(self, name, template, timeoutms)
+ # finally:
+ # self._release_lock("get")
+
+ # def put(self, contentObject):
+ # self._acquire_lock("put")
+ # try:
+ # return _ndn.put(self, contentObject)
+ # finally:
+ # self._release_lock("put")
+
+ @staticmethod
+ def getDefaultKey():
+ pass
+ # return _ndn.get_default_key()
+
+# class EventLoop(object):
+# def __init__(self, *handles):
+# self.running = False
+# self.fds = {}
+# for handle in handles:
+# self.fds[handle.fileno()] = handle
+# self.eventLock = threading.Lock ()
+# self.events = []
+
+# def execute (self, event):
+# self.eventLock.acquire ()
+# self.events.append (event)
+# self.eventLock.release ()
+
+# def run_scheduled(self):
+# wait = {}
+# for fd, handle in zip(self.fds.keys(), self.fds.values()):
+# wait[fd] = handle.process_scheduled()
+# return wait[sorted(wait, key=wait.get)[0]] / 1000.0
+
+# def run_once(self):
+# fd_read = self.fds.values()
+# fd_write = []
+# for handle in self.fds.values():
+# if handle.output_is_pending():
+# fd_write.append(handle)
+
+# timeout = min(self.run_scheduled(), 1.000)
+
+# res = select.select(fd_read, fd_write, [], timeout)
+
+# handles = set(res[0]).union(res[1])
+# for handle in handles:
+# handle.run(0)
+
+# def run(self):
+# self.running = True
+# while self.running:
+# try:
+# self.eventLock.acquire ()
+# for event in self.events:
+# event ()
+# self.events = []
+# self.eventLock.release ()
+
+# self.run_once()
+# except select.error, e:
+# if e[0] == 4:
+# continue
+# else:
+# raise
+# self.running = False
+
+# def stop(self):
+# self.running = False
+# for fd, handle in zip(self.fds.keys(), self.fds.values()):
+# handle.disconnect ()
diff --git a/PyNDN/Interest.py b/PyNDN/Interest.py
new file mode 100644
index 0000000..7ebee01
--- /dev/null
+++ b/PyNDN/Interest.py
@@ -0,0 +1,182 @@
+## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# Alexander Afanasyev
+#
+# GNU 3.0 license, See the LICENSE file for more information
+#
+# Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+#
+
+#
+# Based on PyCCN code, copyrighted and licensed as follows
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# BSD license, See the COPYING file for more information
+# Written by: Derek Kulinski <takeda@takeda.tk>
+# Jeff Burke <jburke@ucla.edu>
+#
+
+import ndn.Name
+
+class AOKType(utils.Flag):
+ _prefix = "ndn"
+
+AOK_NONE = AOKType.new_flag('AOK_NONE', 0x0)
+AOK_CS = AOKType.new_flag('AOK_CS', 0x1) # Answer from content store
+AOK_NEW = AOKType.new_flag('AOK_NEW', 0x2) # OK to produce new content
+AOK_STALE = AOKType.new_flag('AOK_STALE', 0x4) # OK to answer with stale data
+AOK_EXPIRE = AOKType.new_flag('AOK_EXPIRE', 0x10) # Mark as stale (requires scope 0)
+
+AOK_DEFAULT = AOK_CS | AOK_NEW
+
+CHILD_SELECTOR_LEFT = 0
+CHILD_SELECTOR_RIGHT = 1
+
+class Interest(object):
+ def __init__(self, name = None, minSuffixComponents = None,
+ maxSuffixComponents = None, publisherPublicKeyDigest = None,
+ exclude = None, childSelector = None, answerOriginKind = None,
+ scope = None, interestLifetime = None, nonce = None):
+
+ self.name = name
+
+ #
+ # Not supported at the moment
+ #
+ # self.minSuffixComponents = minSuffixComponents # default 0
+ # self.maxSuffixComponents = maxSuffixComponents # default infinity
+ # self.publisherPublicKeyDigest = publisherPublicKeyDigest # SHA256 hash
+ # self.exclude = exclude
+ # self.childSelector = childSelector
+ # self.answerOriginKind = answerOriginKind
+
+ self.scope = scope
+ self.interestLifetime = interestLifetime
+ self.nonce = nonce
+
+ # wire
+ self.wire_dirty = True
+ self.wire = None # backing charbuf
+
+ def __setattr__(self, name, value):
+ if name != "wire_dirty":
+ self.wire_dirty = True
+ object.__setattr__(self, name, value)
+
+ def __getattribute__(self, name):
+ if name == "wire":
+ # force refresh if components changed
+ if object.__getattribute__(self, 'name') and self.name.wire_dirty:
+ self.wire_dirty = True
+ elif object.__getattribute__(self, 'exclude') and self.exclude.wire_dirty:
+ self.wire_dirty = True
+
+ if object.__getattribute__(self, 'wire_dirty'):
+ self.wire = _ndn.Interest_obj_to_ccn(self)
+ self.wire_dirty = False
+ return object.__getattribute__(self, name)
+
+ def __str__(self):
+ res = []
+ res.append("name: %s" % self.name)
+ res.append("minSuffixComponents: %s" % self.minSuffixComponents)
+ res.append("maxSuffixComponents: %s" % self.maxSuffixComponents)
+ res.append("publisherPublicKeyDigest: %r" % self.publisherPublicKeyDigest)
+ res.append("exclude:\n%s" % self.exclude)
+ res.append("childSelector: %s" % self.childSelector)
+ res.append("answerOriginKind: %s" % self.answerOriginKind)
+ res.append("scope: %s" % self.scope)
+ res.append("interestLifetime: %s" % self.interestLifetime)
+ res.append("nonce: %r" % self.nonce)
+ return "\n".join(res)
+
+ def __repr__(self):
+ args = []
+
+ if self.name is not None:
+ args += ["name=%r" % self.name]
+ if self.minSuffixComponents is not None:
+ args += ["minSuffixComponents=%r" % self.minSuffixComponents]
+ if self.maxSuffixComponents is not None:
+ args += ["maxSuffixComponents=%r" % self.maxSuffixComponents]
+ if self.publisherPublicKeyDigest is not None:
+ args += ["publisherPublicKeyDigest=%r" % self.publisherPublicKeyDigest]
+ if self.exclude is not None:
+ args += ["exclude=%r" % self.exclude]
+ if self.childSelector is not None:
+ args += ["childSelector=%r" % self.childSelector]
+ if self.answerOriginKind is not None:
+ args += ["answerOriginKind=%r" % self.answerOriginKind]
+ if self.scope is not None:
+ args += ["scope=%r" % self.scope]
+ if self.interestLifetime is not None:
+ args += ["interestLifetime=%r" % self.interestLifetime]
+ if self.nonce is not None:
+ args += ["nonce=%r" % self.nonce]
+
+ return "ndn.Interest(%s)" % ", ".join(args)
+
+ def get_aok_value(self):
+ global AOK_DEFAULT
+
+ return AOK_DEFAULT if not self.answerOriginKind else self.answerOriginKind
+
+ def matches_name(self, name):
+ i_name = self.name.components
+ o_name = name.components
+
+ # requested name is longer than ours
+ if len(i_name) > len(o_name):
+ return False
+
+ # at least one of given components don't match
+ if not all(i == j for i, j in zip(i_name, o_name)):
+ return False
+
+ return True
+
+# # Bloom filters will be deprecated, so we do not support them.
+# class ExclusionFilter(object):
+# def __init__(self):
+# self.components = []
+
+# # py-ndn
+# self.wire_dirty = False
+# self.wire = None # backing charbuf
+
+# def reset(self):
+# self.components = []
+
+# def add_names(self, names):
+# self.wire_dirty = True
+# self.components.extend(sorted(names))
+
+# def add_name(self, name):
+# if not type(name) is Name.Name:
+# raise TypeError("Name type required")
+
+# self.wire_dirty = True
+# self.components.append(name)
+
+# def add_any(self):
+# self.components.append(Name.Name(name_type = Name.NAME_ANY))
+
+# def __setattr__(self, name, value):
+# if name != "wire_dirty":
+# self.wire_dirty = True
+# object.__setattr__(self, name, value)
+
+# def __getattribute__(self, name):
+# if name == "wire":
+# if object.__getattribute__(self, 'wire_dirty'):
+# self.wire = _ndn.ExclusionFilter_names_to_ccn(
+# self.components)
+# self.wire_dirty = False
+# return object.__getattribute__(self, name)
+
+# def __str__(self):
+# comps = []
+# for n in self.components:
+# comps.append(str(n))
+# return str(comps)
diff --git a/PyNDN/Key.py b/PyNDN/Key.py
new file mode 100644
index 0000000..e57a846
--- /dev/null
+++ b/PyNDN/Key.py
@@ -0,0 +1,144 @@
+## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# Alexander Afanasyev
+#
+# GNU 3.0 license, See the LICENSE file for more information
+#
+# Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+#
+
+#
+# Based on PyCCN code, copyrighted and licensed as follows
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# BSD license, See the COPYING file for more information
+# Written by: Derek Kulinski <takeda@takeda.tk>
+# Jeff Burke <jburke@ucla.edu>
+#
+
+# Fronts ccn_pkey.
+from . import _ndn
+from . import Name
+
+class Key(object):
+ def __init__(self):
+ self.type = None
+ self.publicKeyID = None # SHA256 hash
+ # ndn
+ self.ccn_data_dirty = False
+ self.ccn_data_public = None # backing pkey
+ self.ccn_data_private = None # backing pkey
+
+ def __get_ccn(self):
+ pass
+
+ def generateRSA(self, numbits):
+ _ndn.generate_RSA_key(self, numbits)
+
+ def privateToDER(self):
+ if not self.ccn_data_private:
+ raise _ndn.CCNKeyError("Key is not private")
+ return _ndn.DER_write_key(self.ccn_data_private)
+
+ def publicToDER(self):
+ return _ndn.DER_write_key(self.ccn_data_public)
+
+ def privateToPEM(self, filename = None, password = None):
+ if not self.ccn_data_private:
+ raise _ndn.CCNKeyError("Key is not private")
+
+ if filename:
+ f = open(filename, 'w')
+ _ndn.PEM_write_key(self.ccn_data_private, file=f, password = password)
+ f.close()
+ else:
+ return _ndn.PEM_write_key(self.ccn_data_private, password = password)
+
+ def publicToPEM(self, filename = None):
+ if filename:
+ f = open(filename, 'w')
+ _ndn.PEM_write_key(self.ccn_data_public, file=f)
+ f.close()
+ else:
+ return _ndn.PEM_write_key(self.ccn_data_public)
+
+ def fromDER(self, private = None, public = None):
+ if private:
+ (self.ccn_data_private, self.ccn_data_public, self.publicKeyID) = \
+ _ndn.DER_read_key(private=private)
+ return
+ if public:
+ (self.ccn_data_private, self.ccn_data_public, self.publicKeyID) = \
+ _ndn.DER_read_key(public=public)
+ return
+
+ def fromPEM(self, filename = None, private = None, public = None, password = None):
+ if filename:
+ f = open(filename, 'r')
+ (self.ccn_data_private, self.ccn_data_public, self.publicKeyID) = \
+ _ndn.PEM_read_key(file=f, password = password)
+ f.close()
+ elif private:
+ (self.ccn_data_private, self.ccn_data_public, self.publicKeyID) = \
+ _ndn.PEM_read_key(private=private, password = password)
+ elif public:
+ (self.ccn_data_private, self.ccn_data_public, self.publicKeyID) = \
+ _ndn.PEM_read_key(public=public)
+
+ @staticmethod
+ def createFromDER (private = None, public = None):
+ key = Key ()
+ key.fromDER (private, public)
+ return key
+
+ @staticmethod
+ def createFromPEM (filename = None, private = None, public = None, password = None):
+ key = Key ()
+ key.fromPEM (filename, private, public, password)
+ return key
+
+ @staticmethod
+ def getDefaultKey():
+ return _ndn.get_default_key()
+
+# plus library helper functions to generate and serialize keys?
+
+class KeyLocator(object):
+ def __init__(self, arg=None):
+ #whichever one is not none will be used
+ #if multiple set, checking order is: keyName, key, certificate
+ self.key = arg if type(arg) is Key else None
+ self.keyName = arg if type(arg) is Name.Name else None
+ self.certificate = None
+
+ # ndn
+ self.ccn_data_dirty = True
+ self.ccn_data = None # backing charbuf
+
+ def __setattr__(self, name, value):
+ if name != "ccn_data" and name != "ccn_data_dirty":
+ self.ccn_data_dirty = True
+ object.__setattr__(self, name, value)
+
+ def __getattribute__(self, name):
+ if name=="ccn_data":
+ if object.__getattribute__(self, 'ccn_data_dirty'):
+ if object.__getattribute__(self, 'keyName'):
+ self.ccn_data = _ndn.KeyLocator_to_ccn(
+ name=self.keyName.ccn_data)
+ elif object.__getattribute__(self, 'key'):
+ self.ccn_data = _ndn.KeyLocator_to_ccn(
+ key=self.key.ccn_data_public)
+ elif object.__getattribute__(self, 'certificate'):
+ #same but with cert= arg
+ raise NotImplementedError("certificate support is not implemented")
+ else:
+ raise TypeError("No name, key nor certificate defined")
+
+ self.ccn_data_dirty = False
+ return object.__getattribute__(self, name)
+
+ @staticmethod
+ def getDefaultKeyLocator():
+ return KeyLocator (_ndn.get_default_key_name ())
diff --git a/PyNDN/Name.py b/PyNDN/Name.py
new file mode 100644
index 0000000..ccb7c6a
--- /dev/null
+++ b/PyNDN/Name.py
@@ -0,0 +1,195 @@
+## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# Alexander Afanasyev
+#
+# GNU 3.0 license, See the LICENSE file for more information
+#
+# Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+#
+
+#
+# Based on PyCCN code, copyrighted and licensed as follows
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# BSD license, See the COPYING file for more information
+# Written by: Derek Kulinski <takeda@takeda.tk>
+# Jeff Burke <jburke@ucla.edu>
+#
+
+import ndn
+from . import _ndn
+
+from copy import copy
+import time, struct, random
+
+NAME_NORMAL = 0
+NAME_ANY = 1
+
+class Name(object):
+ def __init__(self, components=[], name_type=NAME_NORMAL, ccn_data=None, ccnb_buffer=None):
+ self._setattr('type', name_type)
+
+ # py-ndn
+ #self._setattr('ccn_data_dirty', True)
+ self._setattr('ccn_data', ccn_data)
+
+ # Name from simple buffer containing name in ccnb encoding
+ if ccnb_buffer:
+ self._setattr('components', _ndn.name_comps_from_ccn_buffer (bytes (ccnb_buffer)))
+
+ # Name from CCN
+ elif ccn_data:
+ self._setattr('components', _ndn.name_comps_from_ccn(ccn_data))
+ self._setattr('ccn_data_dirty', False)
+
+ # Copy Name from another Name object
+ elif isinstance(components, self.__class__):
+ self._setattr('components', copy(components.components))
+ if not components.ccn_data_dirty:
+ self._setattr('ccn_data', components.ccn_data)
+ self._setattr('ccn_data_dirty', False)
+
+ # Name as string (URI)
+ elif type(components) is str:
+ ccn_data = _ndn.name_from_uri(components)
+ self._setattr('components', _ndn.name_comps_from_ccn(ccn_data))
+ self._setattr('ccn_data', ccn_data)
+ self._setattr('ccn_data_dirty', False)
+
+ # Otherwise assume name is a list
+ else:
+ self._setattr('components', copy(components))
+
+ def _setattr(self, name, value):
+ if name == 'components' or name == 'ccn_data':
+ self._setattr('ccn_data_dirty', True)
+ super(Name, self).__setattr__(name, value)
+
+ def _append(self, component):
+ components = copy(self.components)
+ components.append(component)
+
+ return Name(components)
+
+ def append(self, value):
+ components = copy(self.components)
+ if isinstance (value, Name):
+ components.extend (value.components)
+ else:
+ components.append (bytes (value))
+ return Name(components)
+
+ def appendKeyID(self, digest):
+ if isinstance(digest, ndn.Key):
+ digest = digest.publicKeyID
+
+ component = b'\xc1.M.K\x00'
+ component += digest
+
+ return self._append(component)
+
+ def appendVersion(self, version = None):
+ if not version:
+ inttime = int(time.time() * 4096 + 0.5)
+ bintime = struct.pack("!Q", inttime)
+ version = bintime.lstrip(b'\x00')
+ component = b'\xfd' + version
+
+ return self._append(component)
+
+ def appendSegment(self, segment):
+ return self._append(self.num2seg(segment))
+
+ def appendNonce(self):
+ val = random.getrandbits(64)
+ component = b'\xc1.N\x00' + struct.pack("@Q", val)
+
+ return self._append(component)
+
+ def get_ccnb(self):
+ return _ndn.dump_charbuf(self.ccn_data)
+
+ def __repr__(self):
+ global NAME_NORMAL, NAME_ANY
+
+ if self.type == NAME_NORMAL:
+ return "ndn.Name('ccnx:" + _ndn.name_to_uri(self.ccn_data) + "')"
+ elif self.type == NAME_ANY:
+ return "ndn.Name(name_type=ndn.NAME_ANY)"
+ else:
+ raise ValueError("Name is of wrong type %d" % self.type)
+
+ def __str__(self):
+ global NAME_NORMAL, NAME_ANY
+
+ if self.type == NAME_NORMAL:
+ return _ndn.name_to_uri(self.ccn_data)
+ elif self.type == NAME_ANY:
+ return "<any>"
+ else:
+ raise ValueError("Name is of wrong type %d" % self.type)
+
+ def __len__(self):
+ return len(self.components)
+
+ def __add__(self, other):
+ return self.append(other)
+
+ def __setattr__(self, name, value):
+ raise TypeError("can't modify immutable instance")
+
+ __delattr__ = __setattr__
+
+ def __getattribute__(self, name):
+ if name == "ccn_data":
+ if object.__getattribute__(self, 'ccn_data_dirty'):
+ self._setattr('ccn_data', _ndn.name_comps_to_ccn(self.components))
+ self._setattr('ccn_data_dirty', False)
+ return object.__getattribute__(self, name)
+
+ def __getitem__(self, key):
+ if type(key) is int:
+ return self.components[key]
+ elif type(key) is slice:
+ return Name(self.components[key])
+ else:
+ raise ValueError("Unknown __getitem__ type: %s" % type(key))
+
+ def __setitem__(self, key, value):
+ self.components[key] = value
+
+ def __delitem__(self, key):
+ del self.components[key]
+
+ def __len__(self):
+ return len(self.components)
+
+ def __lt__(self, other):
+ return _ndn.compare_names(self.ccn_data, other.ccn_data) < 0
+
+ def __gt__(self, other):
+ return _ndn.compare_names(self.ccn_data, other.ccn_data) > 0
+
+ def __eq__(self, other):
+ return _ndn.compare_names(self.ccn_data, other.ccn_data) == 0
+
+ def __le__(self, other):
+ return _ndn.compare_names(self.ccn_data, other.ccn_data) <= 0
+
+ def __ge__(self, other):
+ return _ndn.compare_names(self.ccn_data, other.ccn_data) >= 0
+
+ def __ne__(self, other):
+ return _ndn.compare_names(self.ccn_data, other.ccn_data) != 0
+
+ @staticmethod
+ def num2seg(num):
+ return b'\x00' + struct.pack('!Q', num).lstrip(b'\x00')
+
+ @staticmethod
+ def seg2num(segment):
+ return long(struct.unpack("!Q", (8 - len(segment)) * "\x00" + segment)[0])
+
+ def isPrefixOf (self, other):
+ return self[:] == other[:len(self)]
diff --git a/PyNDN/NameCrypto.py b/PyNDN/NameCrypto.py
new file mode 100644
index 0000000..427db09
--- /dev/null
+++ b/PyNDN/NameCrypto.py
@@ -0,0 +1,41 @@
+## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# Alexander Afanasyev
+#
+# GNU 3.0 license, See the LICENSE file for more information
+#
+# Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+#
+
+#
+# Based on PyCCN code, copyrighted and licensed as follows
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# BSD license, See the COPYING file for more information
+# Written by: Derek Kulinski <takeda@takeda.tk>
+# Jeff Burke <jburke@ucla.edu>
+#
+
+from . import _ndn, Name
+
+def new_state():
+ return _ndn.nc_new_state()
+
+def generate_application_key(fixture_key, app_name):
+ app_id = _ndn.nc_app_id(app_name)
+ app_key = _ndn.nc_app_key(fixture_key, app_id)
+ return app_key
+
+def authenticate_command(state, name, app_name, app_key):
+ signed_name = _ndn.nc_authenticate_command(state, name.ccn_data, app_name, app_key)
+ return Name(ccn_data = signed_name)
+
+def authenticate_command_sig(state, name, app_name, key):
+ signed_name = _ndn.nc_authenticate_command_sig(state, name.ccn_data, app_name, key.ccn_data_private)
+ return Name(ccn_data = signed_name)
+
+def verify_command(state, name, max_time, **args):
+ if args.has_key('pub_key'): # TODO: use magic bytes to detect signature type, instead of asking caller to explicitly specify key type
+ args['pub_key'] = args['pub_key'].ccn_data_public
+ return _ndn.nc_verify_command(state, name.ccn_data, max_time, **args)
diff --git a/PyNDN/__init__.py b/PyNDN/__init__.py
new file mode 100644
index 0000000..8281cc1
--- /dev/null
+++ b/PyNDN/__init__.py
@@ -0,0 +1,38 @@
+## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# Alexander Afanasyev
+#
+# GNU 3.0 license, See the LICENSE file for more information
+#
+# Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+#
+
+#
+# Based on PyCCN code, copyrighted and licensed as follows
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# BSD license, See the COPYING file for more information
+# Written by: Derek Kulinski <takeda@takeda.tk>
+# Jeff Burke <jburke@ucla.edu>
+#
+
+__all__ = ['Face', 'Closure', 'ContentObject', 'Interest', 'Key', 'Name']
+
+VERSION = 0.3
+
+# import sys as _sys
+
+# try:
+# from ndn.Face import *
+# from ndn.Closure import *
+# from ndn.ContentObject import *
+# from ndn.Interest import *
+# from ndn.Key import *
+# from ndn.Name import *
+# from ndn import NameCrypto
+# from ndn.LocalPrefixDiscovery import *
+
+# except ImportError:
+# del _sys.modules[__name__]
+# raise
diff --git a/PyNDN/impl/__init__.py b/PyNDN/impl/__init__.py
new file mode 100644
index 0000000..e048223
--- /dev/null
+++ b/PyNDN/impl/__init__.py
@@ -0,0 +1,18 @@
+## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# Alexander Afanasyev
+#
+# GNU 3.0 license, See the LICENSE file for more information
+#
+# Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+#
+
+#
+# Based on PyCCN code, copyrighted and licensed as follows
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# BSD license, See the COPYING file for more information
+# Written by: Derek Kulinski <takeda@takeda.tk>
+# Jeff Burke <jburke@ucla.edu>
+#
diff --git a/PyNDN/impl/ccnb.py b/PyNDN/impl/ccnb.py
new file mode 100644
index 0000000..b95b3fa
--- /dev/null
+++ b/PyNDN/impl/ccnb.py
@@ -0,0 +1,129 @@
+## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# Alexander Afanasyev
+#
+# GNU 3.0 license, See the LICENSE file for more information
+#
+# Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+#
+
+#
+# Based on PyCCN code, copyrighted and licensed as follows
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# BSD license, See the COPYING file for more information
+# Written by: Derek Kulinski <takeda@takeda.tk>
+# Jeff Burke <jburke@ucla.edu>
+#
+
+__TT_BITS__ = 3
+__TT_MASK__ = (1 << __TT_BITS__) - 1
+__TT_HBIT__ = (1 << 7)
+__TT_LBIT__ = __TT_HBIT__ - 1
+
+DTAG_NAME = 14
+DTAG_COLLECTION = 17
+DTAG_LINK = 31
+
+def blob(value):
+ return _encode(len(value), 5) + value
+
+def dtag(tag, value):
+ return _encode(tag, 2) + value + '\x00'
+
+def _encode(value, tt):
+ global __TT_BITS__, __TT_HBIT__, __TT_LBIT__
+
+ header = (value << __TT_BITS__) | tt
+
+ blocks = []
+ blocks.append((header & __TT_LBIT__) | __TT_HBIT__)
+ header >>= 7
+
+ while header != 0:
+ blocks.append(header & __TT_LBIT__)
+ header >>= 7
+
+ blocks.reverse()
+
+ return bytearray(blocks)
+
+class CCNBDecoder(object):
+ def __init__(self, ccnb_data):
+ self.ccnb_data = ccnb_data
+ self.reset()
+
+ def reset(self):
+ self.position = 0
+ self.decoded = 0
+ self.stack = []
+
+ def _process_next_byte(self):
+ global __TT_HBIT__, __TT_LBIT__
+
+ assert self.position < len(self.ccnb_data)
+
+ char = ord(self.ccnb_data[self.position])
+ self.position += 1
+
+ if self.decoded == 0 and char == 0:
+ return None, True
+
+ decoded = (self.decoded << 7) | (char & __TT_LBIT__)
+ complete = (char & __TT_HBIT__) == __TT_HBIT__
+
+ self.decoded = decoded if not complete else 0
+
+ return decoded, complete
+
+ def print_element(self, tt, value, data = None):
+ if tt == 2:
+ print "DTAG",
+
+ if value == 14:
+ print "Name"
+ elif value == 15:
+ print "Component"
+ elif value == 17:
+ print "Collection"
+ elif value == 31:
+ print "Link"
+ else:
+ print value
+
+ elif tt == 5:
+ print "BLOB",
+ print value,
+ print repr(data)
+ else:
+ print tt,
+ print value,
+ print repr(data)
+
+ def get_tags(self):
+ global __TT_MASK__, __TT_BITS__
+
+ while self.position < len(self.ccnb_data):
+ while True:
+ decoded, complete = self._process_next_byte()
+ if complete:
+ break
+
+ if decoded is None:
+ tt, value = self.stack.pop()
+ print "Close",
+ else:
+ tt = decoded & __TT_MASK__
+ value = decoded >> __TT_BITS__
+
+ data = None
+ if decoded is not None:
+ if tt == 2:
+ self.stack.append((tt, value))
+ elif tt == 5:
+ data = self.ccnb_data[self.position:self.position + value]
+ self.position += value
+
+ self.print_element(tt, value, data)
+
diff --git a/PyNDN/impl/enumeration.py b/PyNDN/impl/enumeration.py
new file mode 100644
index 0000000..851e43c
--- /dev/null
+++ b/PyNDN/impl/enumeration.py
@@ -0,0 +1,29 @@
+## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# Alexander Afanasyev
+#
+# GNU 3.0 license, See the LICENSE file for more information
+#
+# Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+#
+
+#
+# Based on PyCCN code, copyrighted and licensed as follows
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# BSD license, See the COPYING file for more information
+# Written by: Derek Kulinski <takeda@takeda.tk>
+# Jeff Burke <jburke@ucla.edu>
+#
+
+import ndn
+from ndn.impl import ccnb
+
+def ccnb_enumerate(names):
+ out = bytearray()
+
+ for name in names:
+ out += ccnb.dtag(ccnb.DTAG_LINK, name.get_ccnb())
+
+ return ccnb.dtag(ccnb.DTAG_COLLECTION, out)
diff --git a/PyNDN/impl/segmenting.py b/PyNDN/impl/segmenting.py
new file mode 100644
index 0000000..fb2d253
--- /dev/null
+++ b/PyNDN/impl/segmenting.py
@@ -0,0 +1,55 @@
+## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# Alexander Afanasyev
+#
+# GNU 3.0 license, See the LICENSE file for more information
+#
+# Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+#
+
+#
+# Based on PyCCN code, copyrighted and licensed as follows
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# BSD license, See the COPYING file for more information
+# Written by: Derek Kulinski <takeda@takeda.tk>
+# Jeff Burke <jburke@ucla.edu>
+#
+
+import math
+import ndn
+
+class Wrapper(object):
+ def __init__(self, name, key):
+ self.name = name
+ self.key = key
+
+ kl = ndn.KeyLocator(key)
+ self.signed_info = ndn.SignedInfo(key_locator = kl, key_digest = key.publicKeyID)
+
+ def __call__(self, chunk, segment, segments):
+ name = self.name + ndn.Name.num2seg(segment)
+ self.signed_info.finalBlockID = ndn.Name.num2seg(segments - 1)
+
+ co = ndn.ContentObject(name = name, content = chunk, signed_info = self.signed_info)
+ co.sign(self.key)
+
+ return co
+
+def segmenter(data, wrapper = None, chunk_size = 4096):
+ segment = 0
+ segments = math.ceil(len(data) / float(chunk_size))
+
+ while segment < segments:
+ start = segment * chunk_size
+ end = min(start + chunk_size, len(data))
+ chunk = data[start : end]
+
+ if wrapper is not None:
+ chunk = wrapper(chunk, segment, segments)
+
+ yield chunk
+
+ segment += 1
+
diff --git a/PyNDN/utils.py b/PyNDN/utils.py
new file mode 100644
index 0000000..7cfe514
--- /dev/null
+++ b/PyNDN/utils.py
@@ -0,0 +1,98 @@
+## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# Alexander Afanasyev
+#
+# GNU 3.0 license, See the LICENSE file for more information
+#
+# Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+#
+
+#
+# Based on PyCCN code, copyrighted and licensed as follows
+#
+# Copyright (c) 2011-2013, Regents of the University of California
+# BSD license, See the COPYING file for more information
+# Written by: Derek Kulinski <takeda@takeda.tk>
+# Jeff Burke <jburke@ucla.edu>
+#
+
+import struct
+
+class Flag(int):
+ __initialized = False
+ _flags = None
+ _prefix = None
+ __flags_values__ = None
+
+ @classmethod
+ def initialize(cls):
+ cls._flags = {}
+ cls.__flags_values__ = {}
+ cls.__initialized = True
+
+ @classmethod
+ def new_flag(cls, name, value):
+ if not cls.__initialized:
+ cls.initialize()
+
+ cls._flags[value] = name
+
+ obj = cls(value)
+ cls.__flags_values__[value] = obj
+
+ return obj
+
+ def __new__(cls, value):
+ if cls.__flags_values__.has_key(value):
+ return cls.__flags_values__[value]
+
+ return super(Flag, cls).__new__(cls, value)
+
+ def generate_repr(self):
+ val = long(self)
+ flags = [name for i, name in self._flags.items() if i & val]
+ return " | ".join(flags)
+
+ def __repr__(self):
+ if self._prefix:
+ return self._prefix + "." + self.generate_repr()
+
+ t = type(self)
+ type_name = "%s.%s" % (t.__module__, t.__name__)
+ return "<flags %s of type %s>" % (self.generate_repr(), type_name)
+
+ def __and__(self, other):
+ cls = type(self)
+ return cls(long(self) & long(other))
+
+ def __xor__(self, other):
+ cls = type(self)
+ return cls(long(self) ^ long(other))
+
+ def __or__(self, other):
+ cls = type(self)
+ return cls(long(self) | long(other))
+
+class Enum(Flag):
+ def __new__(cls, value):
+ if cls.__flags_values__.has_key(value):
+ return cls.__flags_values__[value]
+
+ if cls._flags.has_key(value):
+ return super(Enum, cls).__new__(cls, value)
+
+ raise ValueError("invalid flag value: %d" % value)
+
+ def generate_repr(self):
+ return self._flags[long(self)]
+
+def ccn2py_time(value):
+ bintime = b'\x00' * (8 - len(value)) + value
+ inttime = struct.unpack("!Q", bintime)[0]
+ return inttime / 4096.0
+
+def py2ccn_time(value):
+ inttime = int(value * 4096 + 0.5)
+ bintime = struct.pack("!Q", inttime)
+ return bintime.lstrip(b'\x00')