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')