From 543df27fc737fcbfb31b5e88bf6868a97aa5268f Mon Sep 17 00:00:00 2001 From: Ramesh V Rayaprolu Date: Thu, 13 Sep 2018 15:38:26 -0700 Subject: [PATCH] add m4 and m5 wrappers (#104) --- cert.go | 1 + md4.go | 89 ++++++++++++++++++++++++++++++++++++++ md4_test.go | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++++ md5.go | 89 ++++++++++++++++++++++++++++++++++++++ md5_test.go | 109 +++++++++++++++++++++++++++++++++++++++++++++++ shim.c | 4 ++ shim.h | 1 + 7 files changed, 413 insertions(+) create mode 100644 md4.go create mode 100644 md4_test.go create mode 100644 md5.go create mode 100644 md5_test.go diff --git a/cert.go b/cert.go index 3b424a6..e841e22 100644 --- a/cert.go +++ b/cert.go @@ -31,6 +31,7 @@ type EVP_MD int const ( EVP_NULL EVP_MD = iota EVP_MD5 EVP_MD = iota + EVP_MD4 EVP_MD = iota EVP_SHA EVP_MD = iota EVP_SHA1 EVP_MD = iota EVP_DSS EVP_MD = iota diff --git a/md4.go b/md4.go new file mode 100644 index 0000000..e5cc7d8 --- /dev/null +++ b/md4.go @@ -0,0 +1,89 @@ +// Copyright (C) 2017. See AUTHORS. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package openssl + +// #include "shim.h" +import "C" + +import ( + "errors" + "runtime" + "unsafe" +) + +type MD4Hash struct { + ctx *C.EVP_MD_CTX + engine *Engine +} + +func NewMD4Hash() (*MD4Hash, error) { return NewMD4HashWithEngine(nil) } + +func NewMD4HashWithEngine(e *Engine) (*MD4Hash, error) { + hash := &MD4Hash{engine: e} + hash.ctx = C.X_EVP_MD_CTX_new() + if hash.ctx == nil { + return nil, errors.New("openssl: md4: unable to allocate ctx") + } + runtime.SetFinalizer(hash, func(hash *MD4Hash) { hash.Close() }) + if err := hash.Reset(); err != nil { + return nil, err + } + return hash, nil +} + +func (s *MD4Hash) Close() { + if s.ctx != nil { + C.X_EVP_MD_CTX_free(s.ctx) + s.ctx = nil + } +} + +func (s *MD4Hash) Reset() error { + if 1 != C.X_EVP_DigestInit_ex(s.ctx, C.X_EVP_md4(), engineRef(s.engine)) { + return errors.New("openssl: md4: cannot init digest ctx") + } + return nil +} + +func (s *MD4Hash) Write(p []byte) (n int, err error) { + if len(p) == 0 { + return 0, nil + } + if 1 != C.X_EVP_DigestUpdate(s.ctx, unsafe.Pointer(&p[0]), + C.size_t(len(p))) { + return 0, errors.New("openssl: md4: cannot update digest") + } + return len(p), nil +} + +func (s *MD4Hash) Sum() (result [16]byte, err error) { + if 1 != C.X_EVP_DigestFinal_ex(s.ctx, + (*C.uchar)(unsafe.Pointer(&result[0])), nil) { + return result, errors.New("openssl: md4: cannot finalize ctx") + } + return result, s.Reset() +} + +func MD4(data []byte) (result [16]byte, err error) { + hash, err := NewMD4Hash() + if err != nil { + return result, err + } + defer hash.Close() + if _, err := hash.Write(data); err != nil { + return result, err + } + return hash.Sum() +} diff --git a/md4_test.go b/md4_test.go new file mode 100644 index 0000000..a4ca3f7 --- /dev/null +++ b/md4_test.go @@ -0,0 +1,120 @@ +// Copyright (C) 2017. See AUTHORS. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package openssl + +import ( + "crypto/rand" + "golang.org/x/crypto/md4" + "io" + "testing" +) + +func TestMD4(t *testing.T) { + for i := 0; i < 100; i++ { + buf := make([]byte, 10*1024-i) + if _, err := io.ReadFull(rand.Reader, buf); err != nil { + t.Fatal(err) + } + + expected := [16]byte{} + md4Ctx := md4.New() + md4Ctx.Write(buf) + copy(expected[:], md4Ctx.Sum(nil)) + got, err := MD4(buf) + if err != nil { + t.Fatal(err) + } + + if expected != got { + t.Fatalf("exp:%x got:%x", expected, got) + } + } +} + +func TestMD4Writer(t *testing.T) { + ohash, err := NewMD4Hash() + if err != nil { + t.Fatal(err) + } + hash := md4.New() + + for i := 0; i < 100; i++ { + if err := ohash.Reset(); err != nil { + t.Fatal(err) + } + hash.Reset() + buf := make([]byte, 10*1024-i) + if _, err := io.ReadFull(rand.Reader, buf); err != nil { + t.Fatal(err) + } + + if _, err := ohash.Write(buf); err != nil { + t.Fatal(err) + } + if _, err := hash.Write(buf); err != nil { + t.Fatal(err) + } + + var got, exp [16]byte + + hash.Sum(exp[:0]) + got, err := ohash.Sum() + if err != nil { + t.Fatal(err) + } + + if got != exp { + t.Fatalf("exp:%x got:%x", exp, got) + } + } +} + +type md4func func([]byte) + +func benchmarkMD4(b *testing.B, length int64, fn md4func) { + buf := make([]byte, length) + if _, err := io.ReadFull(rand.Reader, buf); err != nil { + b.Fatal(err) + } + b.SetBytes(length) + b.ResetTimer() + for i := 0; i < b.N; i++ { + fn(buf) + } +} + +func BenchmarkMD4Large_openssl(b *testing.B) { + benchmarkMD4(b, 1024*1024, func(buf []byte) { MD4(buf) }) +} + +func BenchmarkMD4Large_stdlib(b *testing.B) { + benchmarkMD4(b, 1024*1024, func(buf []byte) { + md4Ctx := md4.New() + md4Ctx.Write(buf) + md4Ctx.Sum(nil) + }) +} + +func BenchmarkMD4Small_openssl(b *testing.B) { + benchmarkMD4(b, 1, func(buf []byte) { MD4(buf) }) +} + +func BenchmarkMD4Small_stdlib(b *testing.B) { + benchmarkMD4(b, 1, func(buf []byte) { + md4Ctx := md4.New() + md4Ctx.Write(buf) + md4Ctx.Sum(nil) + }) +} diff --git a/md5.go b/md5.go new file mode 100644 index 0000000..82f2eb2 --- /dev/null +++ b/md5.go @@ -0,0 +1,89 @@ +// Copyright (C) 2017. See AUTHORS. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package openssl + +// #include "shim.h" +import "C" + +import ( + "errors" + "runtime" + "unsafe" +) + +type MD5Hash struct { + ctx *C.EVP_MD_CTX + engine *Engine +} + +func NewMD5Hash() (*MD5Hash, error) { return NewMD5HashWithEngine(nil) } + +func NewMD5HashWithEngine(e *Engine) (*MD5Hash, error) { + hash := &MD5Hash{engine: e} + hash.ctx = C.X_EVP_MD_CTX_new() + if hash.ctx == nil { + return nil, errors.New("openssl: md5: unable to allocate ctx") + } + runtime.SetFinalizer(hash, func(hash *MD5Hash) { hash.Close() }) + if err := hash.Reset(); err != nil { + return nil, err + } + return hash, nil +} + +func (s *MD5Hash) Close() { + if s.ctx != nil { + C.X_EVP_MD_CTX_free(s.ctx) + s.ctx = nil + } +} + +func (s *MD5Hash) Reset() error { + if 1 != C.X_EVP_DigestInit_ex(s.ctx, C.X_EVP_md5(), engineRef(s.engine)) { + return errors.New("openssl: md5: cannot init digest ctx") + } + return nil +} + +func (s *MD5Hash) Write(p []byte) (n int, err error) { + if len(p) == 0 { + return 0, nil + } + if 1 != C.X_EVP_DigestUpdate(s.ctx, unsafe.Pointer(&p[0]), + C.size_t(len(p))) { + return 0, errors.New("openssl: md5: cannot update digest") + } + return len(p), nil +} + +func (s *MD5Hash) Sum() (result [16]byte, err error) { + if 1 != C.X_EVP_DigestFinal_ex(s.ctx, + (*C.uchar)(unsafe.Pointer(&result[0])), nil) { + return result, errors.New("openssl: md5: cannot finalize ctx") + } + return result, s.Reset() +} + +func MD5(data []byte) (result [16]byte, err error) { + hash, err := NewMD5Hash() + if err != nil { + return result, err + } + defer hash.Close() + if _, err := hash.Write(data); err != nil { + return result, err + } + return hash.Sum() +} diff --git a/md5_test.go b/md5_test.go new file mode 100644 index 0000000..46fb93a --- /dev/null +++ b/md5_test.go @@ -0,0 +1,109 @@ +// Copyright (C) 2017. See AUTHORS. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package openssl + +import ( + "crypto/md5" + "crypto/rand" + "io" + "testing" +) + +func TestMD5(t *testing.T) { + for i := 0; i < 100; i++ { + buf := make([]byte, 10*1024-i) + if _, err := io.ReadFull(rand.Reader, buf); err != nil { + t.Fatal(err) + } + + expected := md5.Sum(buf) + got, err := MD5(buf) + if err != nil { + t.Fatal(err) + } + + if expected != got { + t.Fatalf("exp:%x got:%x", expected, got) + } + } +} + +func TestMD5Writer(t *testing.T) { + ohash, err := NewMD5Hash() + if err != nil { + t.Fatal(err) + } + hash := md5.New() + + for i := 0; i < 100; i++ { + if err := ohash.Reset(); err != nil { + t.Fatal(err) + } + hash.Reset() + buf := make([]byte, 10*1024-i) + if _, err := io.ReadFull(rand.Reader, buf); err != nil { + t.Fatal(err) + } + + if _, err := ohash.Write(buf); err != nil { + t.Fatal(err) + } + if _, err := hash.Write(buf); err != nil { + t.Fatal(err) + } + + var got, exp [16]byte + + hash.Sum(exp[:0]) + got, err := ohash.Sum() + if err != nil { + t.Fatal(err) + } + + if got != exp { + t.Fatalf("exp:%x got:%x", exp, got) + } + } +} + +type md5func func([]byte) + +func benchmarkMD5(b *testing.B, length int64, fn md5func) { + buf := make([]byte, length) + if _, err := io.ReadFull(rand.Reader, buf); err != nil { + b.Fatal(err) + } + b.SetBytes(length) + b.ResetTimer() + for i := 0; i < b.N; i++ { + fn(buf) + } +} + +func BenchmarkMD5Large_openssl(b *testing.B) { + benchmarkMD5(b, 1024*1024, func(buf []byte) { MD5(buf) }) +} + +func BenchmarkMD5Large_stdlib(b *testing.B) { + benchmarkMD5(b, 1024*1024, func(buf []byte) { md5.Sum(buf) }) +} + +func BenchmarkMD5Small_openssl(b *testing.B) { + benchmarkMD5(b, 1, func(buf []byte) { MD5(buf) }) +} + +func BenchmarkMD5Small_stdlib(b *testing.B) { + benchmarkMD5(b, 1, func(buf []byte) { md5.Sum(buf) }) +} diff --git a/shim.c b/shim.c index d8515ef..6e68084 100644 --- a/shim.c +++ b/shim.c @@ -602,6 +602,10 @@ const EVP_MD *X_EVP_md5() { return EVP_md5(); } +const EVP_MD *X_EVP_md4() { + return EVP_md4(); +} + const EVP_MD *X_EVP_ripemd160() { return EVP_ripemd160(); } diff --git a/shim.h b/shim.h index 016e695..b792822 100644 --- a/shim.h +++ b/shim.h @@ -109,6 +109,7 @@ extern EVP_MD_CTX *X_EVP_MD_CTX_new(); extern void X_EVP_MD_CTX_free(EVP_MD_CTX *ctx); extern const EVP_MD *X_EVP_md_null(); extern const EVP_MD *X_EVP_md5(); +extern const EVP_MD *X_EVP_md4(); extern const EVP_MD *X_EVP_sha(); extern const EVP_MD *X_EVP_sha1(); extern const EVP_MD *X_EVP_dss();