From 6a0da2b28c21ea4ed159de8ba56eb9d1d7a7821e Mon Sep 17 00:00:00 2001 From: bbrodriges Date: Thu, 23 Aug 2018 19:59:30 +0300 Subject: [PATCH 1/7] WIP: init commit --- hashlib/hashlib.go | 104 +++++++++++++++++++++++++++++++++++++++++++++ py/args.go | 5 +++ py/hash.go | 87 +++++++++++++++++++++++++++++++++++++ 3 files changed, 196 insertions(+) create mode 100644 hashlib/hashlib.go create mode 100644 py/hash.go diff --git a/hashlib/hashlib.go b/hashlib/hashlib.go new file mode 100644 index 00000000..629d0c89 --- /dev/null +++ b/hashlib/hashlib.go @@ -0,0 +1,104 @@ +// Copyright 2018 The go-python Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* Hashlib module -- standard C hashlib library functions */ +package hashlib + +import ( + "crypto/md5" + "hash" + + "github.com/go-python/gpython/py" +) + +/* Implements the HMAC algorithm as described by RFC 2104. */ + +const hashlib_new_doc = `new(name, data=b'') - returns a new hash object implementing the +given hash function; initializing the hash +using the given binary data.` + +func hashlib_new(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Object, error) { + var on py.Object + var od py.Object = new(py.Bytes) + + kwlist := []string{"name", "data"} + + err := py.ParseTupleAndKeywords(args, kwargs, "s|y:new", kwlist, &on, &od) + if err != nil { + return nil, err + } + + name, err := py.StrAsString(on) + if err != nil { + return nil, err + } + data, err := py.BytesFromObject(od) + if err != nil { + return nil, err + } + + var hasher hash.Hash + switch name { + case "md5": + hasher = md5.New() + default: + return nil, py.ExceptionNewf(py.ValueError, "unsupported hash type "+name) + } + + return py.NewHash(name, hasher, data), nil +} + +func hashlib_md5(self py.Object, arg py.Object) (py.Object, error) { + data, err := py.BytesFromObject(arg) + if err != nil { + return nil, err + } + return py.NewHash("md5", md5.New(), data), nil +} + +const hashlib_doc = `hashlib module - A common interface to many hash functions. +new(name, data=b'') - returns a new hash object implementing the + given hash function; initializing the hash + using the given binary data. +Named constructor functions are also available, these are faster +than using new(name): +md5(), sha1(), sha224(), sha256(), sha384(), and sha512() +More algorithms may be available on your platform but the above are guaranteed +to exist. See the algorithms_guaranteed and algorithms_available attributes +to find out what algorithm names can be passed to new(). +NOTE: If you want the adler32 or crc32 hash functions they are available in +the zlib module. +Choose your hash function wisely. Some have known collision weaknesses. +sha384 and sha512 will be slow on 32 bit platforms. +Hash objects have these methods: + - update(arg): Update the hash object with the bytes in arg. Repeated calls + are equivalent to a single call with the concatenation of all + the arguments. + - digest(): Return the digest of the bytes passed to the update() method + so far. + - hexdigest(): Like digest() except the digest is returned as a unicode + object of double length, containing only hexadecimal digits. + - copy(): Return a copy (clone) of the hash object. This can be used to + efficiently compute the digests of strings that share a common + initial substring. +For example, to obtain the digest of the string 'Nobody inspects the +spammish repetition': + >>> import hashlib + >>> m = hashlib.md5() + >>> m.update(b"Nobody inspects") + >>> m.update(b" the spammish repetition") + >>> m.digest() + b'\\xbbd\\x9c\\x83\\xdd\\x1e\\xa5\\xc9\\xd9\\xde\\xc9\\xa1\\x8d\\xf0\\xff\\xe9' +More condensed: + >>> hashlib.sha224(b"Nobody inspects the spammish repetition").hexdigest() + 'a4337bc45a8fc544c03f52dc550cd6e1e87021bc896588bd79e901e2' +` + +// Initialise the module +func init() { + methods := []*py.Method{ + py.MustNewMethod("new", hashlib_new, 0, hashlib_new_doc), + } + py.NewModule("hashlib", hashlib_doc, methods, nil) +} diff --git a/py/args.go b/py/args.go index d32c319e..8e828552 100644 --- a/py/args.go +++ b/py/args.go @@ -471,6 +471,11 @@ func ParseTupleAndKeywords(args Tuple, kwargs StringDict, format string, kwlist default: return ExceptionNewf(TypeError, "%s() argument %d must be float, not %s", name, i+1, arg.Type().Name) } + case "y": + if _, ok := arg.(Bytes); !ok { + return ExceptionNewf(TypeError, "%s() argument %d must be byte-like object, not %s", name, i+1, arg.Type().Name) + } + *result = arg default: return ExceptionNewf(TypeError, "Unknown/Unimplemented format character %q in ParseTupleAndKeywords called from %s", op, name) diff --git a/py/hash.go b/py/hash.go new file mode 100644 index 00000000..99c5da94 --- /dev/null +++ b/py/hash.go @@ -0,0 +1,87 @@ +// Copyright 2018 The go-python Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Float objects + +package py + +import ( + "bytes" + "fmt" + "hash" +) + +var HashType = ObjectType.NewType("hash", "a hash object implementing the given hash function", nil, nil) + +type Hash struct { + Name string + Hasher hash.Hash + Data []byte +} + +// Type of this Hash object +func (h Hash) Type() *Type { + return HashType +} + +// NewHash defines a new hash +func NewHash(name string, h hash.Hash, data []byte) *Hash { + return &Hash{name, h, data} +} + +func (h *Hash) M__str__() (Object, error) { + return h.M__repr__() +} + +func (h *Hash) M__repr__() (Object, error) { + return String(fmt.Sprintf("<%s HASH object @ %v>", h.Name, h)), nil +} + +func (h Hash) M__eq__(other Object) (Object, error) { + b, ok := other.(Hash) + if !ok { + return NotImplemented, nil + } + return NewBool(bytes.Equal(h.Data, b.Data)), nil +} + +func (h Hash) M__ne__(other Object) (Object, error) { + b, ok := other.(Hash) + if !ok { + return NotImplemented, nil + } + return NewBool(!bytes.Equal(h.Data, b.Data)), nil +} + +func (h Hash) M__gt__(other Object) (Object, error) { + b, ok := other.(Hash) + if !ok { + return NotImplemented, nil + } + return NewBool(bytes.Compare(h.Data, b.Data) > 0), nil +} + +func (h Hash) M__lt__(other Object) (Object, error) { + b, ok := other.(Hash) + if !ok { + return NotImplemented, nil + } + return NewBool(bytes.Compare(h.Data, b.Data) < 0), nil +} + +func (h Hash) M__ge__(other Object) (Object, error) { + b, ok := other.(Hash) + if !ok { + return NotImplemented, nil + } + return NewBool(bytes.Compare(h.Data, b.Data) >= 0), nil +} + +func (h Hash) M__le__(other Object) (Object, error) { + b, ok := other.(Hash) + if !ok { + return NotImplemented, nil + } + return NewBool(bytes.Compare(h.Data, b.Data) <= 0), nil +} From a62cf50235a6ae8e1378783cb759f20b903146bb Mon Sep 17 00:00:00 2001 From: bbrodriges Date: Fri, 24 Aug 2018 11:55:34 +0300 Subject: [PATCH 2/7] WIP: further development --- hashlib/hashlib.go | 1 + main.go | 1 + py/hash.go | 2 ++ py/tests/hash.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+) create mode 100644 py/tests/hash.py diff --git a/hashlib/hashlib.go b/hashlib/hashlib.go index 629d0c89..fe84dce8 100644 --- a/hashlib/hashlib.go +++ b/hashlib/hashlib.go @@ -99,6 +99,7 @@ More condensed: func init() { methods := []*py.Method{ py.MustNewMethod("new", hashlib_new, 0, hashlib_new_doc), + py.MustNewMethod("md5", hashlib_md5, 0, ``), } py.NewModule("hashlib", hashlib_doc, methods, nil) } diff --git a/main.go b/main.go index 66e493f7..3f90145a 100644 --- a/main.go +++ b/main.go @@ -20,6 +20,7 @@ import ( "strings" "github.com/go-python/gpython/compile" + _ "github.com/go-python/gpython/hashlib" "github.com/go-python/gpython/marshal" _ "github.com/go-python/gpython/math" "github.com/go-python/gpython/py" diff --git a/py/hash.go b/py/hash.go index 99c5da94..a1ea9901 100644 --- a/py/hash.go +++ b/py/hash.go @@ -85,3 +85,5 @@ func (h Hash) M__le__(other Object) (Object, error) { } return NewBool(bytes.Compare(h.Data, b.Data) <= 0), nil } + +var _ richComparison = Hash{} diff --git a/py/tests/hash.py b/py/tests/hash.py new file mode 100644 index 00000000..012d4b47 --- /dev/null +++ b/py/tests/hash.py @@ -0,0 +1,45 @@ +# Copyright 2018 The go-python Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +import hashlib + +doc="str" +s = str(hashlib.new("md5")) +assert " h2 + +doc="le" +h1 = hashlib.new("md5", data=b'one') +h2 = hashlib.new("md5", data=b'two') +assert h1 <= h2 + +doc="ge" +h1 = hashlib.new("md5", data=b'two') +h2 = hashlib.new("md5", data=b'one') +assert h1 >= h2 + +doc="finished" From 86306d905e197012185a9843baa1a57e14cdbdbf Mon Sep 17 00:00:00 2001 From: bbrodriges Date: Sun, 2 Sep 2018 20:51:42 +0300 Subject: [PATCH 3/7] WIP: sha1, sha224, sha256, sha384, sha512 algos; copy, digest, hexdigest methods; name, block_size, digest_size attributes --- hashlib/hashlib.go | 60 +++++++++++++++++++++++++++++++++++++++++++++- py/hash.go | 44 ++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 1 deletion(-) diff --git a/hashlib/hashlib.go b/hashlib/hashlib.go index fe84dce8..38113510 100644 --- a/hashlib/hashlib.go +++ b/hashlib/hashlib.go @@ -7,6 +7,9 @@ package hashlib import ( "crypto/md5" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" "hash" "github.com/go-python/gpython/py" @@ -42,6 +45,16 @@ func hashlib_new(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Object switch name { case "md5": hasher = md5.New() + case "sha1": + hasher = sha1.New() + case "sha224": + hasher = sha256.New224() + case "sha256": + hasher = sha256.New() + case "sha384": + hasher = sha512.New384() + case "sha512": + hasher = sha512.New() default: return nil, py.ExceptionNewf(py.ValueError, "unsupported hash type "+name) } @@ -57,6 +70,46 @@ func hashlib_md5(self py.Object, arg py.Object) (py.Object, error) { return py.NewHash("md5", md5.New(), data), nil } +func hashlib_sha1(self py.Object, arg py.Object) (py.Object, error) { + data, err := py.BytesFromObject(arg) + if err != nil { + return nil, err + } + return py.NewHash("sha1", sha1.New(), data), nil +} + +func hashlib_sha224(self py.Object, arg py.Object) (py.Object, error) { + data, err := py.BytesFromObject(arg) + if err != nil { + return nil, err + } + return py.NewHash("sha224", sha256.New224(), data), nil +} + +func hashlib_sha256(self py.Object, arg py.Object) (py.Object, error) { + data, err := py.BytesFromObject(arg) + if err != nil { + return nil, err + } + return py.NewHash("sha256", sha256.New(), data), nil +} + +func hashlib_sha384(self py.Object, arg py.Object) (py.Object, error) { + data, err := py.BytesFromObject(arg) + if err != nil { + return nil, err + } + return py.NewHash("sha384", sha512.New384(), data), nil +} + +func hashlib_sha512(self py.Object, arg py.Object) (py.Object, error) { + data, err := py.BytesFromObject(arg) + if err != nil { + return nil, err + } + return py.NewHash("sha512", sha512.New(), data), nil +} + const hashlib_doc = `hashlib module - A common interface to many hash functions. new(name, data=b'') - returns a new hash object implementing the given hash function; initializing the hash @@ -99,7 +152,12 @@ More condensed: func init() { methods := []*py.Method{ py.MustNewMethod("new", hashlib_new, 0, hashlib_new_doc), - py.MustNewMethod("md5", hashlib_md5, 0, ``), + py.MustNewMethod("md5", hashlib_md5, 0, ""), + py.MustNewMethod("sha1", hashlib_sha1, 0, ""), + py.MustNewMethod("sha224", hashlib_sha224, 0, ""), + py.MustNewMethod("sha256", hashlib_sha256, 0, ""), + py.MustNewMethod("sha384", hashlib_sha384, 0, ""), + py.MustNewMethod("sha512", hashlib_sha512, 0, ""), } py.NewModule("hashlib", hashlib_doc, methods, nil) } diff --git a/py/hash.go b/py/hash.go index a1ea9901..2be0bc64 100644 --- a/py/hash.go +++ b/py/hash.go @@ -86,4 +86,48 @@ func (h Hash) M__le__(other Object) (Object, error) { return NewBool(bytes.Compare(h.Data, b.Data) <= 0), nil } +func init() { + HashType.Dict["update"] = MustNewMethod("update", func(self Object, arg Object) (Object, error) { + data, err := BytesFromObject(arg) + if err != nil { + return self, err + } + self.(*Hash).Data = append(self.(*Hash).Data, data...) + return self, nil + }, 0, "update(arg) -> Update the hash object with the object arg, which must be interpretable as a buffer of bytes. Repeated calls are equivalent to a single call with the concatenation of all the arguments: m.update(a); m.update(b) is equivalent to m.update(a+b).") + + HashType.Dict["digest"] = MustNewMethod("digest", func(self Object) Bytes { + return Bytes(self.(*Hash).Hasher.Sum(nil)) + }, 0, "digest() -> Return the digest of the data passed to the update() method so far. This is a bytes object of size digest_size which may contain bytes in the whole range from 0 to 255.") + + HashType.Dict["hexdigest"] = MustNewMethod("hexdigest", func(self Object) String { + return String(fmt.Sprintf("%x", self.(*Hash).Hasher.Sum(nil))) + }, 0, "hexdigest() -> Like digest() except the digest is returned as a string object of double length, containing only hexadecimal digits. This may be used to exchange the value safely in email or other non-binary environments.") + + HashType.Dict["copy"] = MustNewMethod("copy", func(self Object) Object { + hash := self.(*Hash) + dst := make([]byte, len(hash.Data)) + copy(dst, hash.Data) + return NewHash(hash.Name, hash.Hasher, dst) + }, 0, "copy() -> Return a copy (“clone”) of the hash object. This can be used to efficiently compute the digests of data sharing a common initial substring.") + + HashType.Dict["name"] = &Property{ + Fget: func(self Object) (Object, error) { + return String(self.(*Hash).Name), nil + }, + } + + HashType.Dict["block_szie"] = &Property{ + Fget: func(self Object) (Object, error) { + return Int(self.(*Hash).Hasher.BlockSize()), nil + }, + } + + HashType.Dict["digest_size"] = &Property{ + Fget: func(self Object) (Object, error) { + return Int(self.(*Hash).Hasher.Size()), nil + }, + } +} + var _ richComparison = Hash{} From 9f564d5b827be0f2b7694af86ac3fdb866e34939 Mon Sep 17 00:00:00 2001 From: bbrodriges Date: Mon, 3 Sep 2018 12:48:20 +0300 Subject: [PATCH 4/7] some tests, unimplemented copy --- hashlib/hashlib.go | 95 +++++++++++++++++++++++++++++----------- hashlib/hashlib_test.go | 15 +++++++ hashlib/tests/hashlib.py | 25 +++++++++++ hashlib/tests/libtest.py | 45 +++++++++++++++++++ py/hash.go | 38 +++++++--------- py/tests/hash.py | 45 ------------------- 6 files changed, 170 insertions(+), 93 deletions(-) create mode 100644 hashlib/hashlib_test.go create mode 100644 hashlib/tests/hashlib.py create mode 100644 hashlib/tests/libtest.py delete mode 100644 py/tests/hash.py diff --git a/hashlib/hashlib.go b/hashlib/hashlib.go index 38113510..eab5018d 100644 --- a/hashlib/hashlib.go +++ b/hashlib/hashlib.go @@ -23,11 +23,11 @@ using the given binary data.` func hashlib_new(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Object, error) { var on py.Object - var od py.Object = new(py.Bytes) + var os py.Object - kwlist := []string{"name", "data"} + kwlist := []string{"name", "string"} - err := py.ParseTupleAndKeywords(args, kwargs, "s|y:new", kwlist, &on, &od) + err := py.ParseTupleAndKeywords(args, kwargs, "s|y:new", kwlist, &on, &os) if err != nil { return nil, err } @@ -36,9 +36,13 @@ func hashlib_new(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Object if err != nil { return nil, err } - data, err := py.BytesFromObject(od) - if err != nil { - return nil, err + + var data py.Bytes + if os != nil { + data, err = py.BytesFromObject(os) + if err != nil { + return nil, err + } } var hasher hash.Hash @@ -59,31 +63,64 @@ func hashlib_new(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Object return nil, py.ExceptionNewf(py.ValueError, "unsupported hash type "+name) } - return py.NewHash(name, hasher, data), nil + _, err = hasher.Write(data) + return py.NewHash(name, hasher), err } -func hashlib_md5(self py.Object, arg py.Object) (py.Object, error) { - data, err := py.BytesFromObject(arg) +func hashlib_md5(self py.Object, args py.Tuple) (py.Object, error) { + return hashlib_new(self, append([]py.Object{py.String("md5")}, args...), nil) +} + +func hashlib_sha1(self py.Object, args py.Tuple) (py.Object, error) { + var d py.Object + err := py.UnpackTuple(args, nil, "sha1", 0, 1, &d) if err != nil { return nil, err } - return py.NewHash("md5", md5.New(), data), nil -} -func hashlib_sha1(self py.Object, arg py.Object) (py.Object, error) { - data, err := py.BytesFromObject(arg) + var data py.Bytes + if d != nil { + switch d.Type() { + case py.BytesType: + data, err = py.BytesFromObject(d) + case py.StringType: + data = []byte(string(d.(py.String))) + } + } + if err != nil { return nil, err } - return py.NewHash("sha1", sha1.New(), data), nil + + hasher := sha1.New() + _, err = hasher.Write(data) + return py.NewHash("sha1", hasher), err } -func hashlib_sha224(self py.Object, arg py.Object) (py.Object, error) { - data, err := py.BytesFromObject(arg) +func hashlib_sha224(self py.Object, args py.Tuple) (py.Object, error) { + var d py.Object + err := py.UnpackTuple(args, nil, "sha224", 0, 1, &d) if err != nil { return nil, err } - return py.NewHash("sha224", sha256.New224(), data), nil + + var data py.Bytes + if d != nil { + switch d.Type() { + case py.BytesType: + data, err = py.BytesFromObject(d) + case py.StringType: + data = []byte(string(d.(py.String))) + } + } + + if err != nil { + return nil, err + } + + hasher := sha256.New224() + _, err = hasher.Write(data) + return py.NewHash("sha224", hasher), err } func hashlib_sha256(self py.Object, arg py.Object) (py.Object, error) { @@ -91,7 +128,9 @@ func hashlib_sha256(self py.Object, arg py.Object) (py.Object, error) { if err != nil { return nil, err } - return py.NewHash("sha256", sha256.New(), data), nil + hasher := sha256.New() + _, err = hasher.Write(data) + return py.NewHash("sha256", hasher), err } func hashlib_sha384(self py.Object, arg py.Object) (py.Object, error) { @@ -99,7 +138,9 @@ func hashlib_sha384(self py.Object, arg py.Object) (py.Object, error) { if err != nil { return nil, err } - return py.NewHash("sha384", sha512.New384(), data), nil + hasher := sha512.New384() + _, err = hasher.Write(data) + return py.NewHash("sha384", hasher), err } func hashlib_sha512(self py.Object, arg py.Object) (py.Object, error) { @@ -107,7 +148,9 @@ func hashlib_sha512(self py.Object, arg py.Object) (py.Object, error) { if err != nil { return nil, err } - return py.NewHash("sha512", sha512.New(), data), nil + hasher := sha512.New() + _, err = hasher.Write(data) + return py.NewHash("sha512", hasher), err } const hashlib_doc = `hashlib module - A common interface to many hash functions. @@ -152,12 +195,12 @@ More condensed: func init() { methods := []*py.Method{ py.MustNewMethod("new", hashlib_new, 0, hashlib_new_doc), - py.MustNewMethod("md5", hashlib_md5, 0, ""), - py.MustNewMethod("sha1", hashlib_sha1, 0, ""), - py.MustNewMethod("sha224", hashlib_sha224, 0, ""), - py.MustNewMethod("sha256", hashlib_sha256, 0, ""), - py.MustNewMethod("sha384", hashlib_sha384, 0, ""), - py.MustNewMethod("sha512", hashlib_sha512, 0, ""), + py.MustNewMethod("md5", hashlib_md5, 0, "Returns a md5 hash object; optionally initialized with a string"), + py.MustNewMethod("sha1", hashlib_sha1, 0, "Returns a sha1 hash object; optionally initialized with a string"), + py.MustNewMethod("sha224", hashlib_sha224, 0, "Returns a sha224 hash object; optionally initialized with a string"), + py.MustNewMethod("sha256", hashlib_sha256, 0, "Returns a sha256 hash object; optionally initialized with a string"), + py.MustNewMethod("sha384", hashlib_sha384, 0, "Returns a sha384 hash object; optionally initialized with a string"), + py.MustNewMethod("sha512", hashlib_sha512, 0, "Returns a sha512 hash object; optionally initialized with a string"), } py.NewModule("hashlib", hashlib_doc, methods, nil) } diff --git a/hashlib/hashlib_test.go b/hashlib/hashlib_test.go new file mode 100644 index 00000000..1050ca1f --- /dev/null +++ b/hashlib/hashlib_test.go @@ -0,0 +1,15 @@ +// Copyright 2018 The go-python Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package hashlib_test + +import ( + "testing" + + "github.com/go-python/gpython/pytest" +) + +func TestVm(t *testing.T) { + pytest.RunTests(t, "tests") +} diff --git a/hashlib/tests/hashlib.py b/hashlib/tests/hashlib.py new file mode 100644 index 00000000..ce3ef531 --- /dev/null +++ b/hashlib/tests/hashlib.py @@ -0,0 +1,25 @@ +# Copyright 2018 The go-python Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +# FIXME Convert tests from cpython/Lib/test/test_hashlib.py + +import hashlib +from libtest import * + +def ftest(name, got, want): + what = '%s got %r, want %r' % (name, got, want) + assert got == want, what + +doc="MD5" +ftest('new_md5_0', hashlib.new('md5', b'').hexdigest(), 'd41d8cd98f00b204e9800998ecf8427e') +ftest('new_md5_1', hashlib.new('md5', b'abc').hexdigest(), '900150983cd24fb0d6963f7d28e17f72') +ftest('mew_md5_2', + hashlib.new('md5', b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789').hexdigest(), + 'd174ab98d277d9f5a5611c2c9f419d9f') +assertRaises(TypeError, hashlib.new, 'md5', 'abc') + +ftest('md5_0', hashlib.md5().hexdigest(), 'd41d8cd98f00b204e9800998ecf8427e') +ftest('md5_1', hashlib.md5(b'abc').hexdigest(), '900150983cd24fb0d6963f7d28e17f72') + +doc="finished" diff --git a/hashlib/tests/libtest.py b/hashlib/tests/libtest.py new file mode 100644 index 00000000..41fafc58 --- /dev/null +++ b/hashlib/tests/libtest.py @@ -0,0 +1,45 @@ +# Copyright 2018 The go-python Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +""" +Simple test harness +""" + +def assertRaises(expecting, fn, *args, **kwargs): + """Check the exception was raised - don't check the text""" + try: + fn(*args, **kwargs) + except expecting as e: + pass + else: + assert False, "%s not raised" % (expecting,) + +def assertRaisesText(expecting, text, fn, *args, **kwargs): + """Check the exception with text in is raised""" + try: + fn(*args, **kwargs) + except expecting as e: + assert text in e.args[0], "'%s' not found in '%s'" % (text, e.args[0]) + else: + assert False, "%s not raised" % (expecting,) + +def assertTrue(x): + """assert x is True""" + assert x + +def assertFalse(x): + """assert x is False""" + assert not x + +def assertEqual(x, y): + """assert x == y""" + assert x == y + +def assertAlmostEqual(x, y, places=7): + """assert x == y to places""" + assert round(abs(y-x), places) == 0 + +def fail(x): + """Fails with error message""" + assert False, x diff --git a/py/hash.go b/py/hash.go index 2be0bc64..66ffa03d 100644 --- a/py/hash.go +++ b/py/hash.go @@ -17,7 +17,6 @@ var HashType = ObjectType.NewType("hash", "a hash object implementing the given type Hash struct { Name string Hasher hash.Hash - Data []byte } // Type of this Hash object @@ -26,8 +25,8 @@ func (h Hash) Type() *Type { } // NewHash defines a new hash -func NewHash(name string, h hash.Hash, data []byte) *Hash { - return &Hash{name, h, data} +func NewHash(name string, h hash.Hash) *Hash { + return &Hash{name, h} } func (h *Hash) M__str__() (Object, error) { @@ -43,7 +42,7 @@ func (h Hash) M__eq__(other Object) (Object, error) { if !ok { return NotImplemented, nil } - return NewBool(bytes.Equal(h.Data, b.Data)), nil + return NewBool(bytes.Equal(h.Hasher.Sum(nil), b.Hasher.Sum(nil))), nil } func (h Hash) M__ne__(other Object) (Object, error) { @@ -51,7 +50,7 @@ func (h Hash) M__ne__(other Object) (Object, error) { if !ok { return NotImplemented, nil } - return NewBool(!bytes.Equal(h.Data, b.Data)), nil + return NewBool(!bytes.Equal(h.Hasher.Sum(nil), b.Hasher.Sum(nil))), nil } func (h Hash) M__gt__(other Object) (Object, error) { @@ -59,7 +58,7 @@ func (h Hash) M__gt__(other Object) (Object, error) { if !ok { return NotImplemented, nil } - return NewBool(bytes.Compare(h.Data, b.Data) > 0), nil + return NewBool(bytes.Compare(h.Hasher.Sum(nil), b.Hasher.Sum(nil)) > 0), nil } func (h Hash) M__lt__(other Object) (Object, error) { @@ -67,7 +66,7 @@ func (h Hash) M__lt__(other Object) (Object, error) { if !ok { return NotImplemented, nil } - return NewBool(bytes.Compare(h.Data, b.Data) < 0), nil + return NewBool(bytes.Compare(h.Hasher.Sum(nil), b.Hasher.Sum(nil)) < 0), nil } func (h Hash) M__ge__(other Object) (Object, error) { @@ -75,7 +74,7 @@ func (h Hash) M__ge__(other Object) (Object, error) { if !ok { return NotImplemented, nil } - return NewBool(bytes.Compare(h.Data, b.Data) >= 0), nil + return NewBool(bytes.Compare(h.Hasher.Sum(nil), b.Hasher.Sum(nil)) >= 0), nil } func (h Hash) M__le__(other Object) (Object, error) { @@ -83,7 +82,7 @@ func (h Hash) M__le__(other Object) (Object, error) { if !ok { return NotImplemented, nil } - return NewBool(bytes.Compare(h.Data, b.Data) <= 0), nil + return NewBool(bytes.Compare(h.Hasher.Sum(nil), b.Hasher.Sum(nil)) <= 0), nil } func init() { @@ -92,24 +91,19 @@ func init() { if err != nil { return self, err } - self.(*Hash).Data = append(self.(*Hash).Data, data...) - return self, nil + _, err = self.(*Hash).Hasher.Write(data) + return self, err }, 0, "update(arg) -> Update the hash object with the object arg, which must be interpretable as a buffer of bytes. Repeated calls are equivalent to a single call with the concatenation of all the arguments: m.update(a); m.update(b) is equivalent to m.update(a+b).") - HashType.Dict["digest"] = MustNewMethod("digest", func(self Object) Bytes { - return Bytes(self.(*Hash).Hasher.Sum(nil)) + HashType.Dict["digest"] = MustNewMethod("digest", func(self Object) (Object, error) { + return Bytes(self.(*Hash).Hasher.Sum(nil)), nil }, 0, "digest() -> Return the digest of the data passed to the update() method so far. This is a bytes object of size digest_size which may contain bytes in the whole range from 0 to 255.") - HashType.Dict["hexdigest"] = MustNewMethod("hexdigest", func(self Object) String { - return String(fmt.Sprintf("%x", self.(*Hash).Hasher.Sum(nil))) + HashType.Dict["hexdigest"] = MustNewMethod("hexdigest", func(self Object) (Object, error) { + return String(fmt.Sprintf("%x", self.(*Hash).Hasher.Sum(nil))), nil }, 0, "hexdigest() -> Like digest() except the digest is returned as a string object of double length, containing only hexadecimal digits. This may be used to exchange the value safely in email or other non-binary environments.") - HashType.Dict["copy"] = MustNewMethod("copy", func(self Object) Object { - hash := self.(*Hash) - dst := make([]byte, len(hash.Data)) - copy(dst, hash.Data) - return NewHash(hash.Name, hash.Hasher, dst) - }, 0, "copy() -> Return a copy (“clone”) of the hash object. This can be used to efficiently compute the digests of data sharing a common initial substring.") + // FIXME find a way to implement copy() method HashType.Dict["name"] = &Property{ Fget: func(self Object) (Object, error) { @@ -117,7 +111,7 @@ func init() { }, } - HashType.Dict["block_szie"] = &Property{ + HashType.Dict["block_size"] = &Property{ Fget: func(self Object) (Object, error) { return Int(self.(*Hash).Hasher.BlockSize()), nil }, diff --git a/py/tests/hash.py b/py/tests/hash.py deleted file mode 100644 index 012d4b47..00000000 --- a/py/tests/hash.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright 2018 The go-python Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -import hashlib - -doc="str" -s = str(hashlib.new("md5")) -assert " h2 - -doc="le" -h1 = hashlib.new("md5", data=b'one') -h2 = hashlib.new("md5", data=b'two') -assert h1 <= h2 - -doc="ge" -h1 = hashlib.new("md5", data=b'two') -h2 = hashlib.new("md5", data=b'one') -assert h1 >= h2 - -doc="finished" From c7542230763a355c8863e83cc6a33e9d169ca581 Mon Sep 17 00:00:00 2001 From: bbrodriges Date: Mon, 3 Sep 2018 12:52:25 +0300 Subject: [PATCH 5/7] some tests, unimplemented copy --- hashlib/tests/hashlib.py | 3 +++ hashlib/tests/libtest.py | 4 ---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/hashlib/tests/hashlib.py b/hashlib/tests/hashlib.py index ce3ef531..1815f0b2 100644 --- a/hashlib/tests/hashlib.py +++ b/hashlib/tests/hashlib.py @@ -11,6 +11,9 @@ def ftest(name, got, want): what = '%s got %r, want %r' % (name, got, want) assert got == want, what +doc='Rich comparision' +assertEqual(hashlib.md5(b'abc'), hashlib.md5(b'abc')) + doc="MD5" ftest('new_md5_0', hashlib.new('md5', b'').hexdigest(), 'd41d8cd98f00b204e9800998ecf8427e') ftest('new_md5_1', hashlib.new('md5', b'abc').hexdigest(), '900150983cd24fb0d6963f7d28e17f72') diff --git a/hashlib/tests/libtest.py b/hashlib/tests/libtest.py index 41fafc58..d3894f1a 100644 --- a/hashlib/tests/libtest.py +++ b/hashlib/tests/libtest.py @@ -36,10 +36,6 @@ def assertEqual(x, y): """assert x == y""" assert x == y -def assertAlmostEqual(x, y, places=7): - """assert x == y to places""" - assert round(abs(y-x), places) == 0 - def fail(x): """Fails with error message""" assert False, x From 52d69a19d8d291c88965f696c18ac0f5fd9e962c Mon Sep 17 00:00:00 2001 From: bbrodriges Date: Mon, 3 Sep 2018 13:28:22 +0300 Subject: [PATCH 6/7] more tests --- hashlib/hashlib.go | 96 +++++++--------------------------------- hashlib/tests/hashlib.py | 38 +++++++++++++--- py/hash.go | 4 +- 3 files changed, 48 insertions(+), 90 deletions(-) diff --git a/hashlib/hashlib.go b/hashlib/hashlib.go index eab5018d..dce0ff49 100644 --- a/hashlib/hashlib.go +++ b/hashlib/hashlib.go @@ -17,15 +17,14 @@ import ( /* Implements the HMAC algorithm as described by RFC 2104. */ -const hashlib_new_doc = `new(name, data=b'') - returns a new hash object implementing the -given hash function; initializing the hash -using the given binary data.` +const hashlib_new_doc = `new(name, data=b'') - Return a new hashing object using the named algorithm; +optionally initialized with data (which must be bytes).` func hashlib_new(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Object, error) { var on py.Object - var os py.Object + var os py.Object = py.Bytes(nil) - kwlist := []string{"name", "string"} + kwlist := []string{"name", "data"} err := py.ParseTupleAndKeywords(args, kwargs, "s|y:new", kwlist, &on, &os) if err != nil { @@ -37,12 +36,9 @@ func hashlib_new(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Object return nil, err } - var data py.Bytes - if os != nil { - data, err = py.BytesFromObject(os) - if err != nil { - return nil, err - } + data, err := py.BytesFromObject(os) + if err != nil { + return nil, err } var hasher hash.Hash @@ -72,85 +68,23 @@ func hashlib_md5(self py.Object, args py.Tuple) (py.Object, error) { } func hashlib_sha1(self py.Object, args py.Tuple) (py.Object, error) { - var d py.Object - err := py.UnpackTuple(args, nil, "sha1", 0, 1, &d) - if err != nil { - return nil, err - } - - var data py.Bytes - if d != nil { - switch d.Type() { - case py.BytesType: - data, err = py.BytesFromObject(d) - case py.StringType: - data = []byte(string(d.(py.String))) - } - } - - if err != nil { - return nil, err - } - - hasher := sha1.New() - _, err = hasher.Write(data) - return py.NewHash("sha1", hasher), err + return hashlib_new(self, append([]py.Object{py.String("sha1")}, args...), nil) } func hashlib_sha224(self py.Object, args py.Tuple) (py.Object, error) { - var d py.Object - err := py.UnpackTuple(args, nil, "sha224", 0, 1, &d) - if err != nil { - return nil, err - } - - var data py.Bytes - if d != nil { - switch d.Type() { - case py.BytesType: - data, err = py.BytesFromObject(d) - case py.StringType: - data = []byte(string(d.(py.String))) - } - } - - if err != nil { - return nil, err - } - - hasher := sha256.New224() - _, err = hasher.Write(data) - return py.NewHash("sha224", hasher), err + return hashlib_new(self, append([]py.Object{py.String("sha224")}, args...), nil) } -func hashlib_sha256(self py.Object, arg py.Object) (py.Object, error) { - data, err := py.BytesFromObject(arg) - if err != nil { - return nil, err - } - hasher := sha256.New() - _, err = hasher.Write(data) - return py.NewHash("sha256", hasher), err +func hashlib_sha256(self py.Object, args py.Tuple) (py.Object, error) { + return hashlib_new(self, append([]py.Object{py.String("sha256")}, args...), nil) } -func hashlib_sha384(self py.Object, arg py.Object) (py.Object, error) { - data, err := py.BytesFromObject(arg) - if err != nil { - return nil, err - } - hasher := sha512.New384() - _, err = hasher.Write(data) - return py.NewHash("sha384", hasher), err +func hashlib_sha384(self py.Object, args py.Tuple) (py.Object, error) { + return hashlib_new(self, append([]py.Object{py.String("sha384")}, args...), nil) } -func hashlib_sha512(self py.Object, arg py.Object) (py.Object, error) { - data, err := py.BytesFromObject(arg) - if err != nil { - return nil, err - } - hasher := sha512.New() - _, err = hasher.Write(data) - return py.NewHash("sha512", hasher), err +func hashlib_sha512(self py.Object, args py.Tuple) (py.Object, error) { + return hashlib_new(self, append([]py.Object{py.String("sha512")}, args...), nil) } const hashlib_doc = `hashlib module - A common interface to many hash functions. diff --git a/hashlib/tests/hashlib.py b/hashlib/tests/hashlib.py index 1815f0b2..86b91015 100644 --- a/hashlib/tests/hashlib.py +++ b/hashlib/tests/hashlib.py @@ -11,18 +11,42 @@ def ftest(name, got, want): what = '%s got %r, want %r' % (name, got, want) assert got == want, what -doc='Rich comparision' -assertEqual(hashlib.md5(b'abc'), hashlib.md5(b'abc')) - -doc="MD5" +doc="MD5" ftest('new_md5_0', hashlib.new('md5', b'').hexdigest(), 'd41d8cd98f00b204e9800998ecf8427e') -ftest('new_md5_1', hashlib.new('md5', b'abc').hexdigest(), '900150983cd24fb0d6963f7d28e17f72') -ftest('mew_md5_2', +ftest('new_md5_1', hashlib.new('md5', b'').digest(), b'\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04\xe9\x80\t\x98\xec\xf8B~') +ftest('new_md5_2', hashlib.new('md5', b'abc').hexdigest(), '900150983cd24fb0d6963f7d28e17f72') +ftest('new_md5_3', hashlib.new('md5', b'abc').digest(), b'\x90\x01P\x98<\xd2O\xb0\xd6\x96?}(\xe1\x7fr') +ftest('new_md5_4', hashlib.new('md5', b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789').hexdigest(), 'd174ab98d277d9f5a5611c2c9f419d9f') assertRaises(TypeError, hashlib.new, 'md5', 'abc') ftest('md5_0', hashlib.md5().hexdigest(), 'd41d8cd98f00b204e9800998ecf8427e') -ftest('md5_1', hashlib.md5(b'abc').hexdigest(), '900150983cd24fb0d6963f7d28e17f72') +ftest('md5_1', hashlib.md5().digest(), b'\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04\xe9\x80\t\x98\xec\xf8B~') +ftest('md5_2', hashlib.md5(b'abc').hexdigest(), '900150983cd24fb0d6963f7d28e17f72') +ftest('md5_3', hashlib.md5(b'abc').digest(), b'\x90\x01P\x98<\xd2O\xb0\xd6\x96?}(\xe1\x7fr') +ftest('md5_4', + hashlib.md5(b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789').hexdigest(), + 'd174ab98d277d9f5a5611c2c9f419d9f') +assertRaises(TypeError, hashlib.md5, 'abc') + +doc="SHA1" +ftest('new_sha1_0', hashlib.new('sha1', b'').hexdigest(), 'da39a3ee5e6b4b0d3255bfef95601890afd80709') +ftest('new_sha1_1', hashlib.new('sha1', b'').digest(), b'\xda9\xa3\xee^kK\r2U\xbf\xef\x95`\x18\x90\xaf\xd8\x07\t') +ftest('new_sha1_2', hashlib.new('sha1', b'abc').hexdigest(), 'a9993e364706816aba3e25717850c26c9cd0d89d') +ftest('new_sha1_3', hashlib.new('sha1', b'abc').digest(), b'\xa9\x99>6G\x06\x81j\xba>%qxP\xc2l\x9c\xd0\xd8\x9d') +ftest('new_sha1_4', + hashlib.new('sha1', b'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq').hexdigest(), + '84983e441c3bd26ebaae4aa1f95129e5e54670f1') +assertRaises(TypeError, hashlib.new, 'sha1', 'abc') + +ftest('sha1_0', hashlib.sha1(b'').hexdigest(), 'da39a3ee5e6b4b0d3255bfef95601890afd80709') +ftest('sha1_1', hashlib.sha1(b'').digest(), b'\xda9\xa3\xee^kK\r2U\xbf\xef\x95`\x18\x90\xaf\xd8\x07\t') +ftest('sha1_2', hashlib.sha1(b'abc').hexdigest(), 'a9993e364706816aba3e25717850c26c9cd0d89d') +ftest('sha1_3', hashlib.sha1(b'abc').digest(), b'\xa9\x99>6G\x06\x81j\xba>%qxP\xc2l\x9c\xd0\xd8\x9d') +ftest('sha1_4', + hashlib.sha1(b'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq').hexdigest(), + '84983e441c3bd26ebaae4aa1f95129e5e54670f1') +assertRaises(TypeError, hashlib.sha1, 'abc') doc="finished" diff --git a/py/hash.go b/py/hash.go index 66ffa03d..0b39996e 100644 --- a/py/hash.go +++ b/py/hash.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Float objects +// Hash objects package py @@ -124,4 +124,4 @@ func init() { } } -var _ richComparison = Hash{} +var _ richComparison = new(Hash) From 1290850e70ed0a11535e6029dc5f53a3e2f3b9f1 Mon Sep 17 00:00:00 2001 From: bbrodriges Date: Mon, 3 Sep 2018 17:40:03 +0300 Subject: [PATCH 7/7] sha3 funcs --- go.mod | 5 ++++- go.sum | 2 ++ hashlib/hashlib.go | 23 +++++++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index aed4efd5..456fbefb 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,6 @@ module github.com/go-python/gpython -require github.com/peterh/liner v1.1.0 +require ( + github.com/peterh/liner v1.1.0 + golang.org/x/crypto v0.0.0-20180830192347-182538f80094 // indirect +) diff --git a/go.sum b/go.sum index 324d4e14..61622022 100644 --- a/go.sum +++ b/go.sum @@ -2,3 +2,5 @@ github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8Bz github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/peterh/liner v1.1.0 h1:f+aAedNJA6uk7+6rXsYBnhdo4Xux7ESLe+kcuVUF5os= github.com/peterh/liner v1.1.0/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= +golang.org/x/crypto v0.0.0-20180830192347-182538f80094 h1:rVTAlhYa4+lCfNxmAIEOGQRoD23UqP72M3+rSWVGDTg= +golang.org/x/crypto v0.0.0-20180830192347-182538f80094/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= diff --git a/hashlib/hashlib.go b/hashlib/hashlib.go index dce0ff49..6d226e51 100644 --- a/hashlib/hashlib.go +++ b/hashlib/hashlib.go @@ -12,6 +12,8 @@ import ( "crypto/sha512" "hash" + "golang.org/x/crypto/sha3" + "github.com/go-python/gpython/py" ) @@ -55,6 +57,12 @@ func hashlib_new(self py.Object, args py.Tuple, kwargs py.StringDict) (py.Object hasher = sha512.New384() case "sha512": hasher = sha512.New() + case "sha3_256": + hasher = sha3.New256() + case "sha3_384": + hasher = sha3.New384() + case "sha3_512": + hasher = sha3.New512() default: return nil, py.ExceptionNewf(py.ValueError, "unsupported hash type "+name) } @@ -87,6 +95,18 @@ func hashlib_sha512(self py.Object, args py.Tuple) (py.Object, error) { return hashlib_new(self, append([]py.Object{py.String("sha512")}, args...), nil) } +func hashlib_sha3_256(self py.Object, args py.Tuple) (py.Object, error) { + return hashlib_new(self, append([]py.Object{py.String("sha3_256")}, args...), nil) +} + +func hashlib_sha3_384(self py.Object, args py.Tuple) (py.Object, error) { + return hashlib_new(self, append([]py.Object{py.String("sha3_384")}, args...), nil) +} + +func hashlib_sha3_512(self py.Object, args py.Tuple) (py.Object, error) { + return hashlib_new(self, append([]py.Object{py.String("sha3_512")}, args...), nil) +} + const hashlib_doc = `hashlib module - A common interface to many hash functions. new(name, data=b'') - returns a new hash object implementing the given hash function; initializing the hash @@ -135,6 +155,9 @@ func init() { py.MustNewMethod("sha256", hashlib_sha256, 0, "Returns a sha256 hash object; optionally initialized with a string"), py.MustNewMethod("sha384", hashlib_sha384, 0, "Returns a sha384 hash object; optionally initialized with a string"), py.MustNewMethod("sha512", hashlib_sha512, 0, "Returns a sha512 hash object; optionally initialized with a string"), + py.MustNewMethod("sha3_256", hashlib_sha3_256, 0, "Return a new SHA3 hash object with a hashbit length of 32 bytes."), + py.MustNewMethod("sha3_384", hashlib_sha3_384, 0, "Return a new SHA3 hash object with a hashbit length of 48 bytes."), + py.MustNewMethod("sha3_512", hashlib_sha3_512, 0, "Return a new SHA3 hash object with a hashbit length of 64 bytes."), } py.NewModule("hashlib", hashlib_doc, methods, nil) }