Skip to content

Commit

Permalink
Merge pull request #41 from microsoft/md4md5
Browse files Browse the repository at this point in the history
Add support for MD4 and MD5
  • Loading branch information
qmuntal authored Aug 22, 2023
2 parents 7a8face + cfa4587 commit 306d53c
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 54 deletions.
124 changes: 74 additions & 50 deletions cng/sha.go → cng/hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,120 +18,144 @@ import (
// SupportsHash returns true if a hash.Hash implementation is supported for h.
func SupportsHash(h crypto.Hash) bool {
switch h {
case crypto.SHA1, crypto.SHA256, crypto.SHA384, crypto.SHA512:
case crypto.MD4, crypto.MD5, crypto.SHA1, crypto.SHA256, crypto.SHA384, crypto.SHA512:
return true
case crypto.SHA3_256:
_, err := loadSha(bcrypt.SHA3_256_ALGORITHM, bcrypt.ALG_NONE_FLAG)
_, err := loadHash(bcrypt.SHA3_256_ALGORITHM, bcrypt.ALG_NONE_FLAG)
return err == nil
case crypto.SHA3_384:
_, err := loadSha(bcrypt.SHA3_384_ALGORITHM, bcrypt.ALG_NONE_FLAG)
_, err := loadHash(bcrypt.SHA3_384_ALGORITHM, bcrypt.ALG_NONE_FLAG)
return err == nil
case crypto.SHA3_512:
_, err := loadSha(bcrypt.SHA3_512_ALGORITHM, bcrypt.ALG_NONE_FLAG)
_, err := loadHash(bcrypt.SHA3_512_ALGORITHM, bcrypt.ALG_NONE_FLAG)
return err == nil
}
return false
}

func shaOneShot(id string, p, sum []byte) error {
h, err := loadSha(id, 0)
func hashOneShot(id string, p, sum []byte) error {
h, err := loadHash(id, 0)
if err != nil {
return err
}
return bcrypt.Hash(h.handle, nil, p, sum)
}

func MD4(p []byte) (sum [16]byte) {
if err := hashOneShot(bcrypt.MD4_ALGORITHM, p, sum[:]); err != nil {
panic("bcrypt: MD4 failed")
}
return
}

func MD5(p []byte) (sum [16]byte) {
if err := hashOneShot(bcrypt.MD5_ALGORITHM, p, sum[:]); err != nil {
panic("bcrypt: MD5 failed")
}
return
}

func SHA1(p []byte) (sum [20]byte) {
if err := shaOneShot(bcrypt.SHA1_ALGORITHM, p, sum[:]); err != nil {
if err := hashOneShot(bcrypt.SHA1_ALGORITHM, p, sum[:]); err != nil {
panic("bcrypt: SHA1 failed")
}
return
}

func SHA256(p []byte) (sum [32]byte) {
if err := shaOneShot(bcrypt.SHA256_ALGORITHM, p, sum[:]); err != nil {
if err := hashOneShot(bcrypt.SHA256_ALGORITHM, p, sum[:]); err != nil {
panic("bcrypt: SHA256 failed")
}
return
}

func SHA384(p []byte) (sum [48]byte) {
if err := shaOneShot(bcrypt.SHA384_ALGORITHM, p, sum[:]); err != nil {
if err := hashOneShot(bcrypt.SHA384_ALGORITHM, p, sum[:]); err != nil {
panic("bcrypt: SHA384 failed")
}
return
}

func SHA512(p []byte) (sum [64]byte) {
if err := shaOneShot(bcrypt.SHA512_ALGORITHM, p, sum[:]); err != nil {
if err := hashOneShot(bcrypt.SHA512_ALGORITHM, p, sum[:]); err != nil {
panic("bcrypt: SHA512 failed")
}
return
}

func SHA3_256(p []byte) (sum [32]byte) {
if err := shaOneShot(bcrypt.SHA3_256_ALGORITHM, p, sum[:]); err != nil {
if err := hashOneShot(bcrypt.SHA3_256_ALGORITHM, p, sum[:]); err != nil {
panic("bcrypt: SHA3_256 failed")
}
return
}

func SHA3_384(p []byte) (sum [48]byte) {
if err := shaOneShot(bcrypt.SHA3_384_ALGORITHM, p, sum[:]); err != nil {
if err := hashOneShot(bcrypt.SHA3_384_ALGORITHM, p, sum[:]); err != nil {
panic("bcrypt: SHA3_384 failed")
}
return
}

func SHA3_512(p []byte) (sum [64]byte) {
if err := shaOneShot(bcrypt.SHA3_512_ALGORITHM, p, sum[:]); err != nil {
if err := hashOneShot(bcrypt.SHA3_512_ALGORITHM, p, sum[:]); err != nil {
panic("bcrypt: SHA3_512 failed")
}
return
}

// NewMD4 returns a new MD4 hash.
func NewMD4() hash.Hash {
return newHashX(bcrypt.MD4_ALGORITHM, bcrypt.ALG_NONE_FLAG, nil)
}

// NewMD5 returns a new MD5 hash.
func NewMD5() hash.Hash {
return newHashX(bcrypt.MD5_ALGORITHM, bcrypt.ALG_NONE_FLAG, nil)
}

// NewSHA1 returns a new SHA1 hash.
func NewSHA1() hash.Hash {
return newSHAX(bcrypt.SHA1_ALGORITHM, bcrypt.ALG_NONE_FLAG, nil)
return newHashX(bcrypt.SHA1_ALGORITHM, bcrypt.ALG_NONE_FLAG, nil)
}

// NewSHA256 returns a new SHA256 hash.
func NewSHA256() hash.Hash {
return newSHAX(bcrypt.SHA256_ALGORITHM, bcrypt.ALG_NONE_FLAG, nil)
return newHashX(bcrypt.SHA256_ALGORITHM, bcrypt.ALG_NONE_FLAG, nil)
}

// NewSHA384 returns a new SHA384 hash.
func NewSHA384() hash.Hash {
return newSHAX(bcrypt.SHA384_ALGORITHM, bcrypt.ALG_NONE_FLAG, nil)
return newHashX(bcrypt.SHA384_ALGORITHM, bcrypt.ALG_NONE_FLAG, nil)
}

// NewSHA512 returns a new SHA512 hash.
func NewSHA512() hash.Hash {
return newSHAX(bcrypt.SHA512_ALGORITHM, bcrypt.ALG_NONE_FLAG, nil)
return newHashX(bcrypt.SHA512_ALGORITHM, bcrypt.ALG_NONE_FLAG, nil)
}

// NewSHA3_256 returns a new SHA256 hash.
func NewSHA3_256() hash.Hash {
return newSHAX(bcrypt.SHA3_256_ALGORITHM, bcrypt.ALG_NONE_FLAG, nil)
return newHashX(bcrypt.SHA3_256_ALGORITHM, bcrypt.ALG_NONE_FLAG, nil)
}

// NewSHA3_384 returns a new SHA384 hash.
func NewSHA3_384() hash.Hash {
return newSHAX(bcrypt.SHA3_384_ALGORITHM, bcrypt.ALG_NONE_FLAG, nil)
return newHashX(bcrypt.SHA3_384_ALGORITHM, bcrypt.ALG_NONE_FLAG, nil)
}

// NewSHA3_512 returns a new SHA512 hash.
func NewSHA3_512() hash.Hash {
return newSHAX(bcrypt.SHA3_512_ALGORITHM, bcrypt.ALG_NONE_FLAG, nil)
return newHashX(bcrypt.SHA3_512_ALGORITHM, bcrypt.ALG_NONE_FLAG, nil)
}

type shaAlgorithm struct {
type hashAlgorithm struct {
handle bcrypt.ALG_HANDLE
size uint32
blockSize uint32
}

func loadSha(id string, flags bcrypt.AlgorithmProviderFlags) (shaAlgorithm, error) {
func loadHash(id string, flags bcrypt.AlgorithmProviderFlags) (hashAlgorithm, error) {
v, err := loadOrStoreAlg(id, flags, "", func(h bcrypt.ALG_HANDLE) (interface{}, error) {
size, err := getUint32(bcrypt.HANDLE(h), bcrypt.HASH_LENGTH)
if err != nil {
Expand All @@ -141,15 +165,15 @@ func loadSha(id string, flags bcrypt.AlgorithmProviderFlags) (shaAlgorithm, erro
if err != nil {
return nil, err
}
return shaAlgorithm{h, size, blockSize}, nil
return hashAlgorithm{h, size, blockSize}, nil
})
if err != nil {
return shaAlgorithm{}, err
return hashAlgorithm{}, err
}
return v.(shaAlgorithm), nil
return v.(hashAlgorithm), nil
}

type shaXHash struct {
type hashX struct {
h bcrypt.ALG_HANDLE
ctx bcrypt.HASH_HANDLE
size int
Expand All @@ -158,33 +182,33 @@ type shaXHash struct {
key []byte
}

func newSHAX(id string, flag bcrypt.AlgorithmProviderFlags, key []byte) *shaXHash {
h, err := loadSha(id, flag)
func newHashX(id string, flag bcrypt.AlgorithmProviderFlags, key []byte) *hashX {
h, err := loadHash(id, flag)
if err != nil {
panic(err)
}
sha := new(shaXHash)
sha.h = h.handle
sha.size = int(h.size)
sha.blockSize = int(h.blockSize)
sha.buf = make([]byte, sha.size)
hx := new(hashX)
hx.h = h.handle
hx.size = int(h.size)
hx.blockSize = int(h.blockSize)
hx.buf = make([]byte, hx.size)
if len(key) > 0 {
sha.key = make([]byte, len(key))
copy(sha.key, key)
hx.key = make([]byte, len(key))
copy(hx.key, key)
}
sha.Reset()
runtime.SetFinalizer(sha, (*shaXHash).finalize)
return sha
hx.Reset()
runtime.SetFinalizer(hx, (*hashX).finalize)
return hx
}

func (h *shaXHash) finalize() {
func (h *hashX) finalize() {
if h.ctx != 0 {
bcrypt.DestroyHash(h.ctx)
}
}

func (h *shaXHash) Clone() (hash.Hash, error) {
h2 := &shaXHash{
func (h *hashX) Clone() (hash.Hash, error) {
h2 := &hashX{
h: h.h,
size: h.size,
blockSize: h.blockSize,
Expand All @@ -196,12 +220,12 @@ func (h *shaXHash) Clone() (hash.Hash, error) {
if err != nil {
return nil, err
}
runtime.SetFinalizer(h2, (*shaXHash).finalize)
runtime.SetFinalizer(h2, (*hashX).finalize)
runtime.KeepAlive(h)
return h2, nil
}

func (h *shaXHash) Reset() {
func (h *hashX) Reset() {
if h.ctx != 0 {
bcrypt.DestroyHash(h.ctx)
h.ctx = 0
Expand All @@ -213,7 +237,7 @@ func (h *shaXHash) Reset() {
runtime.KeepAlive(h)
}

func (h *shaXHash) Write(p []byte) (n int, err error) {
func (h *hashX) Write(p []byte) (n int, err error) {
for n < len(p) && err == nil {
nn := len32(p[n:])
err = bcrypt.HashData(h.ctx, p[n:n+nn], 0)
Expand All @@ -227,7 +251,7 @@ func (h *shaXHash) Write(p []byte) (n int, err error) {
return len(p), nil
}

func (h *shaXHash) WriteString(s string) (int, error) {
func (h *hashX) WriteString(s string) (int, error) {
// TODO: use unsafe.StringData once we drop support
// for go1.19 and earlier.
hdr := (*struct {
Expand All @@ -237,7 +261,7 @@ func (h *shaXHash) WriteString(s string) (int, error) {
return h.Write(unsafe.Slice(hdr.Data, len(s)))
}

func (h *shaXHash) WriteByte(c byte) error {
func (h *hashX) WriteByte(c byte) error {
if err := bcrypt.HashDataRaw(h.ctx, &c, 1, 0); err != nil {
// hash.Hash interface mandates Write should never return an error.
panic(err)
Expand All @@ -246,20 +270,20 @@ func (h *shaXHash) WriteByte(c byte) error {
return nil
}

func (h *shaXHash) Size() int {
func (h *hashX) Size() int {
return h.size
}

func (h *shaXHash) BlockSize() int {
func (h *hashX) BlockSize() int {
return h.blockSize
}

func (h *shaXHash) Sum(in []byte) []byte {
func (h *hashX) Sum(in []byte) []byte {
h.sum(h.buf)
return append(in, h.buf...)
}

func (h *shaXHash) sum(out []byte) {
func (h *hashX) sum(out []byte) {
var ctx2 bcrypt.HASH_HANDLE
err := bcrypt.DuplicateHash(h.ctx, &ctx2, nil, 0)
if err != nil {
Expand Down
18 changes: 16 additions & 2 deletions cng/sha_test.go → cng/hash_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ import (

func cryptoToHash(h crypto.Hash) func() hash.Hash {
switch h {
case crypto.MD4:
return cng.NewMD4
case crypto.MD5:
return cng.NewMD5
case crypto.SHA1:
return cng.NewSHA1
case crypto.SHA256:
Expand All @@ -36,9 +40,11 @@ func cryptoToHash(h crypto.Hash) func() hash.Hash {
return nil
}

func TestSha(t *testing.T) {
func TestHash(t *testing.T) {
msg := []byte("testing")
var tests = []crypto.Hash{
crypto.MD4,
crypto.MD5,
crypto.SHA1,
crypto.SHA224,
crypto.SHA256,
Expand Down Expand Up @@ -106,12 +112,20 @@ func TestSha(t *testing.T) {
}
}

func TestSHA_OneShot(t *testing.T) {
func TestHash_OneShot(t *testing.T) {
msg := []byte("testing")
var tests = []struct {
h crypto.Hash
oneShot func([]byte) []byte
}{
{crypto.MD4, func(p []byte) []byte {
b := cng.MD4(p)
return b[:]
}},
{crypto.MD5, func(p []byte) []byte {
b := cng.MD5(p)
return b[:]
}},
{crypto.SHA1, func(p []byte) []byte {
b := cng.SHA1(p)
return b[:]
Expand Down
4 changes: 2 additions & 2 deletions cng/hmac.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
// hashToID converts a hash.Hash implementation from this package
// to a CNG hash ID
func hashToID(h hash.Hash) string {
if _, ok := h.(*shaXHash); !ok {
if _, ok := h.(*hashX); !ok {
return ""
}
var id string
Expand Down Expand Up @@ -51,5 +51,5 @@ func NewHMAC(h func() hash.Hash, key []byte) hash.Hash {
ch.Write(key)
key = ch.Sum(nil)
}
return newSHAX(id, bcrypt.ALG_HANDLE_HMAC_FLAG, key)
return newHashX(id, bcrypt.ALG_HANDLE_HMAC_FLAG, key)
}
1 change: 1 addition & 0 deletions internal/bcrypt/bcrypt_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const (
SHA3_512_ALGORITHM = "SHA3-512"
AES_ALGORITHM = "AES"
RSA_ALGORITHM = "RSA"
MD4_ALGORITHM = "MD4"
MD5_ALGORITHM = "MD5"
ECDSA_ALGORITHM = "ECDSA"
ECDH_ALGORITHM = "ECDH"
Expand Down

0 comments on commit 306d53c

Please sign in to comment.