X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/catacomb-python/blobdiff_plain/5a8b43b3b20b540f20877f14ecb9cb0dae8ddaf3..1b3b79da95ab3a541fb613c82d6e2e8b5136e4ae:/catacomb/__init__.py diff --git a/catacomb/__init__.py b/catacomb/__init__.py index d4eac30..1a2b5f4 100644 --- a/catacomb/__init__.py +++ b/catacomb/__init__.py @@ -23,10 +23,14 @@ ### along with Catacomb/Python; if not, write to the Free Software Foundation, ### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +from __future__ import with_statement + import _base import types as _types from binascii import hexlify as _hexify, unhexlify as _unhexify +from contextlib import contextmanager as _ctxmgr from sys import argv as _argv +from struct import pack as _pack ###-------------------------------------------------------------------------- ### Basic stuff. @@ -160,6 +164,96 @@ class _tmp: _augment(GHash, _tmp) _augment(Poly1305Hash, _tmp) +class _HashBase (object): + ## The standard hash methods. Assume that `hash' is defined and returns + ## the receiver. + def hashu8(me, n): return me.hash(_pack('B', n)) + def hashu16l(me, n): return me.hash(_pack('H', n)) + hashu16 = hashu16b + def hashu32l(me, n): return me.hash(_pack('L', n)) + hashu32 = hashu32b + def hashu64l(me, n): return me.hash(_pack('Q', n)) + hashu64 = hashu64b + def hashbuf8(me, s): return me.hashu8(len(s)).hash(s) + def hashbuf16l(me, s): return me.hashu16l(len(s)).hash(s) + def hashbuf16b(me, s): return me.hashu16b(len(s)).hash(s) + hashbuf16 = hashbuf16b + def hashbuf32l(me, s): return me.hashu32l(len(s)).hash(s) + def hashbuf32b(me, s): return me.hashu32b(len(s)).hash(s) + hashbuf32 = hashbuf32b + def hashbuf64l(me, s): return me.hashu64l(len(s)).hash(s) + def hashbuf64b(me, s): return me.hashu64b(len(s)).hash(s) + hashbuf64 = hashbuf64b + def hashstrz(me, s): return me.hash(s).hashu8(0) + +class _ShakeBase (_HashBase): + + ## Python gets really confused if I try to augment `__new__' on native + ## classes, so wrap and delegate. Sorry. + def __init__(me, perso = '', *args, **kw): + super(_ShakeBase, me).__init__(*args, **kw) + me._h = me._SHAKE(perso = perso, func = me._FUNC) + + ## Delegate methods... + def copy(me): new = me.__class__(); new._copy(me) + def _copy(me, other): me._h = other._h + def hash(me, m): me._h.hash(m); return me + def xof(me): me._h.xof(); return me + def get(me, n): return me._h.get(n) + def mask(me, m): return me._h.mask(m) + def done(me, n): return me._h.done(n) + def check(me, h): return ctstreq(h, me.done(len(h))) + @property + def state(me): return me._h.state + @property + def buffered(me): return me._h.buffered + @property + def rate(me): return me._h.rate + +class _tmp: + def check(me, h): + return ctstreq(h, me.done(len(h))) + def leftenc(me, n): + nn = MP(n).storeb() + return me.hashu8(len(nn)).hash(nn) + def rightenc(me, n): + nn = MP(n).storeb() + return me.hash(nn).hashu8(len(nn)) + def stringenc(me, str): + return me.leftenc(8*len(str)).hash(str) + def bytepad_before(me): + return me.leftenc(me.rate) + def bytepad_after(me): + if me.buffered: me.hash(me._Z[:me.rate - me.buffered]) + return me + @_ctxmgr + def bytepad(me): + me.bytepad_before() + yield me + me.bytepad_after() +_augment(Shake, _tmp) +_augment(_ShakeBase, _tmp) +Shake._Z = _ShakeBase._Z = ByteString(200*'\0') + +class KMAC (_ShakeBase): + _FUNC = 'KMAC' + def __init__(me, k, *arg, **kw): + super(KMAC, me).__init__(*arg, **kw) + with me.bytepad(): me.stringenc(k) + def done(me, n = -1): + if n < 0: n = me._TAGSZ + me.rightenc(8*n) + return super(KMAC, me).done(n) + def xof(me): + me.rightenc(0) + return super(KMAC, me).xof() + +class KMAC128 (KMAC): _SHAKE = Shake128; _TAGSZ = 16 +class KMAC256 (KMAC): _SHAKE = Shake256; _TAGSZ = 32 + ###-------------------------------------------------------------------------- ### NaCl `secretbox'. @@ -706,21 +800,23 @@ _augment(RSAPriv, _tmp) ### DSA and related schemes. class _tmp: - def __repr__(me): return '%s(G = %r, p = %r)' % (_clsname(me), me.G, me.p) + def __repr__(me): return '%s(G = %r, p = %r, hash = %r)' % \ + (_clsname(me), me.G, me.p, me.hash) def _repr_pretty_(me, pp, cyclep): ind = _pp_bgroup_tyname(pp, me) if cyclep: pp.text('...') else: _pp_kv(pp, 'G', me.G); pp.text(','); pp.breakable() - _pp_kv(pp, 'p', me.p) + _pp_kv(pp, 'p', me.p); pp.text(','); pp.breakable() + _pp_kv(pp, 'hash', me.hash) pp.end_group(ind, ')') _augment(DSAPub, _tmp) _augment(KCDSAPub, _tmp) class _tmp: - def __repr__(me): return '%s(G = %r, u = %s, p = %r)' % \ - (_clsname(me), me.G, _repr_secret(me.u), me.p) + def __repr__(me): return '%s(G = %r, u = %s, p = %r, hash = %r)' % \ + (_clsname(me), me.G, _repr_secret(me.u), me.p, me.hash) def _repr_pretty_(me, pp, cyclep): ind = _pp_bgroup_tyname(pp, me) if cyclep: @@ -728,7 +824,8 @@ class _tmp: else: _pp_kv(pp, 'G', me.G); pp.text(','); pp.breakable() _pp_kv(pp, 'u', me.u, True); pp.text(','); pp.breakable() - _pp_kv(pp, 'p', me.p) + _pp_kv(pp, 'p', me.p); pp.text(','); pp.breakable() + _pp_kv(pp, 'hash', me.hash) pp.end_group(ind, ')') _augment(DSAPriv, _tmp) _augment(KCDSAPriv, _tmp) @@ -741,77 +838,92 @@ 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 - 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)) - 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)) + def _hashkey(me, z): return Shake256().hash(z).done(salsa20.keysz.default) + +class _EdDSAPub (_BasePub): + def beginhash(me): return me._HASH() + def endhash(me, h): return h.done() + +class _EdDSAPriv (_BasePriv, _EdDSAPub): + pass + +class Ed25519Pub (_EdDSAPub): + _PUBSZ = KeySZSet(ED25519_PUBSZ) + _HASH = sha512 + def verify(me, msg, sig, **kw): + return ed25519_verify(me.pub, msg, sig, **kw) + +class Ed25519Priv (_EdDSAPriv, Ed25519Pub): + _KEYSZ = KeySZAny(ED25519_KEYSZ) + def _pubkey(me, priv): return ed25519_pubkey(priv) + def sign(me, msg, **kw): + return ed25519_sign(me.priv, msg, pub = me.pub, **kw) + +class Ed448Pub (_EdDSAPub): + _PUBSZ = KeySZSet(ED448_PUBSZ) + _HASH = shake256 + def verify(me, msg, sig, **kw): + return ed448_verify(me.pub, msg, sig, **kw) + +class Ed448Priv (_EdDSAPriv, Ed448Pub): + _KEYSZ = KeySZAny(ED448_KEYSZ) + def _pubkey(me, priv): return ed448_pubkey(priv) + def sign(me, msg, **kw): + return ed448_sign(me.priv, msg, pub = me.pub, **kw) ###-------------------------------------------------------------------------- ### Built-in named curves and prime groups.