@@ -194,7 +194,18 @@ def _trytodict(o):
except Exception: # pragma: no cover
raise TypeError('unable to find __to_dict__ on %s: %s' % (type(o), `o`))
_asn1coder = pasn1.ASN1DictCoder(coerce=_trytodict)
class CononicalCoder(pasn1.ASN1DictCoder):
def enc_dict(self, obj, **kwargs):
class FakeIter:
def iteritems(self):
itms = obj.items()
itms.sort()
return iter(itms)
return pasn1.ASN1DictCoder.enc_dict(self, FakeIter(), **kwargs)
_asn1coder = CononicalCoder(coerce=_trytodict)
class Persona(object):
'''The object that represents a persona, or identity. It will
@@ -216,6 +227,23 @@ class Persona(object):
self._created_by_ref = self._identity.uuid
def __repr__(self): # pragma: no cover
r = '<Persona: has key: %s, has pubkey: %s, identity: %s>' % \
(self._key is not None, self._pubkey is not None,
`self._identity`)
return r
@classmethod
def from_pubkey(cls, pubkeystr):
pubstr = base58.b58decode_check(pubkeystr)
uuid, pubkey = _asn1coder.loads(pubstr)
ident = Identity(uuid=uuid, pubkey=pubkey)
return cls(ident)
def get_identity(self):
'''Return the Identity object for this Persona.'''
@@ -225,7 +253,10 @@ class Persona(object):
'''Get a printable version of the public key. This is used
for importing into different programs, or for shared.'''
return base58.b58encode_check(self._identity.pubkey)
idobj = self._identity
pubstr = _asn1coder.dumps([ idobj.uuid, idobj.pubkey ])
return base58.b58encode_check(pubstr)
def new_version(self, *args):
'''Update the Persona's Identity object.'''
@@ -300,6 +331,8 @@ class Persona(object):
def verify(self, obj):
sigbytes = self._makesigbytes(obj)
pubkey = self._pubkey.public_bytes(Encoding.Raw,
PublicFormat.Raw)
self._pubkey.verify(obj['sig'], sigbytes)
return True
@@ -551,6 +584,28 @@ def main():
if __name__ == '__main__': # pragma: no cover
main()
class _TestCononicalCoder(unittest.TestCase):
def test_con(self):
obja = { 'foo': 23984732, 'a': 5, 'b': 6, 'something': '2398472398723498273dfasdfjlaksdfj' }
objaitems = obja.items()
objaitems.sort()
objb = dict(objaitems)
self.assertEqual(obja, objb)
# This is to make sure that item order changed
self.assertNotEqual(obja.items(), objb.items())
astr = pasn1.dumps(obja)
bstr = pasn1.dumps(objb)
self.assertNotEqual(astr, bstr)
astr = _asn1coder.dumps(obja)
bstr = _asn1coder.dumps(objb)
self.assertEqual(astr, bstr)
class _TestCases(unittest.TestCase):
def setUp(self):
d = os.path.realpath(tempfile.mkdtemp())
@@ -724,11 +779,22 @@ class _TestCases(unittest.TestCase):
self.assertIsInstance(idobj['pubkey'], str)
# that get_pubkey returns the correct thing
self.assertEqual(persona.get_pubkey(), base58.b58encode_check(idobj['pubkey']))
pubstr = _asn1coder.dumps([ idobj['uuid'], idobj['pubkey'] ])
self.assertEqual(persona.get_pubkey(),
base58.b58encode_check(pubstr))
# and that there is a signature
self.assertIsInstance(idobj['sig'], str)
# and that it can verify itself
persona.verify(idobj)
# and that a new persona can be created from the pubkey
pkpersona = Persona.from_pubkey(persona.get_pubkey())
# and that it can verify the old identity
self.assertTrue(pkpersona.verify(idobj))
# that a second time, it raises an exception
self.assertRaises(RuntimeError, persona.generate_key)