chiark / gitweb /
catacomb/__init__.py: Refactor the XDH and EdDSA classes.
authorMark Wooding <mdw@distorted.org.uk>
Thu, 11 May 2017 09:42:15 +0000 (10:42 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sun, 14 May 2017 03:29:42 +0000 (04:29 +0100)
  * Introduce `_BasePub' and `_BasePriv' underneath the existing
    classes, and move obvious common functionality like size checking
    and printing into them.  Push key-generation down here too.

  * Use `KeySZ' objects for key length checking rather than just the
    bare constants.  In particular, this means that `Ed25519Priv' isn't
    fussy about key sizes, preserving this feature of the underlying
    implementation.

  * Split the implementation of pretty-printing into more pieces.  In
    particular, now `_BasePriv' only needs to implement the printing of
    the private key rather than the whole lot.  (Plain `__repr__' is
    still duplicated, but the code is smaller and this is more
    tolerable.)

  * Rename `_Boxy...' to `_XDH...', since this appears to be the generic
    term I'm using for such things now.

catacomb/__init__.py

index 7ea8d575a79e9bbcd524b0d6d8fe66ebca600fbd..7ac5baf91383fba7f4f55635b7a4b55f6826aa3e 100644 (file)
@@ -835,77 +835,78 @@ X448_BASE = MP(5).storel(56)
 
 Z128 = ByteString.zero(16)
 
-class _BoxyPub (object):
+class _BasePub (object):
   def __init__(me, pub, *args, **kw):
-    if len(pub) != me._PUBSZ: raise ValueError, 'bad public key'
-    super(_BoxyPub, me).__init__(*args, **kw)
+    if not me._PUBSZ.check(len(pub)): raise ValueError, 'bad public key'
+    super(_BasePub, me).__init__(*args, **kw)
     me.pub = pub
   def __repr__(me): return '%s(pub = %r)' % (_clsname(me), me.pub)
+  def _pp(me, pp): _pp_kv(pp, 'pub', me.pub)
   def _repr_pretty_(me, pp, cyclep):
     ind = _pp_bgroup_tyname(pp, me)
-    if cyclep:
-      pp.text('...')
-    else:
-      _pp_kv(pp, 'pub', me.pub)
+    if cyclep: pp.text('...')
+    else: me._pp(pp)
     pp.end_group(ind, ')')
 
-class _BoxyPriv (_BoxyPub):
+class _BasePriv (object):
   def __init__(me, priv, pub = None, *args, **kw):
-    if len(priv) != me._KEYSZ: raise ValueError, 'bad private key'
-    if pub is None: pub = me._op(priv, me._BASE)
-    super(_BoxyPriv, me).__init__(pub = pub, *args, **kw)
+    if not me._KEYSZ.check(len(priv)): raise ValueError, 'bad private key'
+    if pub is None: pub = me._pubkey(priv)
+    super(_BasePriv, me).__init__(pub = pub, *args, **kw)
     me.priv = priv
+  @classmethod
+  def generate(cls, rng = rand):
+    return cls(rng.block(cls._KEYSZ.default))
+  def __repr__(me):
+    return '%s(priv = %d, pub = %r)' % \
+        (_clsname(me), _repr_secret(me.priv), me.pub)
+  def _pp(me, pp):
+    _pp_kv(pp, 'priv', me.priv, secretp = True); pp.text(','); pp.breakable()
+    super(_BasePriv, me)._pp(pp)
+
+class _XDHPub (_BasePub):  pass
+
+class _XDHPriv (_BasePriv):
+  def _pubkey(me, priv): return me._op(priv, me._BASE)
   def agree(me, you): return me._op(me.priv, you.pub)
-  def boxkey(me, recip):
-    return me._hashkey(me.agree(recip))
-  def box(me, recip, n, m):
-    return secret_box(me.boxkey(recip), n, m)
-  def unbox(me, recip, n, c):
-    return secret_unbox(me.boxkey(recip), n, c)
-  def __repr__(me): return '%s(priv = %s, pub = %r)' % \
-      (_clsname(me), _repr_secret(me.priv), me.pub)
-  def _repr_pretty_(me, pp, cyclep):
-    ind = _pp_bgroup_tyname(pp, me)
-    if cyclep:
-      pp.text('...')
-    else:
-      _pp_kv(pp, 'priv', me.priv, True); pp.text(','); pp.breakable()
-      _pp_kv(pp, 'pub', me.pub)
-    pp.end_group(ind, ')')
+  def boxkey(me, recip): return me._hashkey(me.agree(recip))
+  def box(me, recip, n, m): return secret_box(me.boxkey(recip), n, m)
+  def unbox(me, recip, n, c): return secret_unbox(me.boxkey(recip), n, c)
 
-class X25519Pub (_BoxyPub):
-  _PUBSZ = X25519_PUBSZ
+class X25519Pub (_XDHPub):
+  _PUBSZ = KeySZSet(X25519_PUBSZ)
   _BASE = X25519_BASE
 
-class X25519Priv (_BoxyPriv, X25519Pub):
-  _KEYSZ = X25519_KEYSZ
+class X25519Priv (_XDHPriv, X25519Pub):
+  _KEYSZ = KeySZSet(X25519_KEYSZ)
   def _op(me, k, X): return x25519(k, X)
   def _hashkey(me, z): return hsalsa20_prf(z, Z128)
 
-class X448Pub (_BoxyPub):
-  _PUBSZ = X448_PUBSZ
+class X448Pub (_XDHPub):
+  _PUBSZ = KeySZSet(X448_PUBSZ)
   _BASE = X448_BASE
 
-class X448Priv (_BoxyPriv, X448Pub):
-  _KEYSZ = X448_KEYSZ
+class X448Priv (_XDHPriv, X448Pub):
+  _KEYSZ = KeySZSet(X448_KEYSZ)
   def _op(me, k, X): return x448(k, X)
   ##def _hashkey(me, z): return ???
 
-class Ed25519Pub (object):
-  def __init__(me, pub):
-    me.pub = pub
+class _EdDSAPub (_BasePub):
+  pass
+
+class _EdDSAPriv (_BasePriv, _EdDSAPub):
+  pass
+
+class Ed25519Pub (_EdDSAPub):
+  _PUBSZ = KeySZSet(ED25519_PUBSZ)
   def verify(me, msg, sig):
     return ed25519_verify(me.pub, msg, sig)
 
-class Ed25519Priv (Ed25519Pub):
-  def __init__(me, priv):
-    me.priv = priv
-    Ed25519Pub.__init__(me, ed25519_pubkey(priv))
+class Ed25519Priv (_EdDSAPriv, Ed25519Pub):
+  _KEYSZ = KeySZAny(ED25519_KEYSZ)
+  def _pubkey(me, priv): return ed25519_pubkey(priv)
   def sign(me, msg):
     return ed25519_sign(me.priv, msg, pub = me.pub)
-  @classmethod
-  def generate(cls, rng = rand):
-    return cls(rng.block(ED25519_KEYSZ))
 
 ###--------------------------------------------------------------------------
 ### Built-in named curves and prime groups.