gmsm/kdf/kdf.go

58 lines
1.5 KiB
Go

// Package kdf implements ShangMi(SM) used Key Derivation Function, compliances with GB/T 32918.4-2016 5.4.3.
package kdf
import (
"encoding"
"encoding/binary"
"hash"
)
// KdfInterface is the interface implemented by some specific Hash implementations.
type KdfInterface interface {
Kdf(z []byte, keyLen int) []byte
}
// Kdf key derivation function, compliance with GB/T 32918.4-2016 5.4.3.
// ANSI-X9.63-KDF
func Kdf(newHash func() hash.Hash, z []byte, keyLen int) []byte {
baseMD := newHash()
// If the hash implements KdfInterface, use the optimized Kdf method.
if kdfImpl, ok := baseMD.(KdfInterface); ok {
return kdfImpl.Kdf(z, keyLen)
}
limit := uint64(keyLen+baseMD.Size()-1) / uint64(baseMD.Size())
if limit >= uint64(1<<32)-1 {
panic("kdf: key length too long")
}
var countBytes [4]byte
var ct uint32 = 1
var k []byte
if marshaler, ok := baseMD.(encoding.BinaryMarshaler); limit == 1 || len(z) < baseMD.BlockSize() || !ok {
for i := 0; i < int(limit); i++ {
binary.BigEndian.PutUint32(countBytes[:], ct)
baseMD.Write(z)
baseMD.Write(countBytes[:])
k = baseMD.Sum(k)
ct++
baseMD.Reset()
}
} else {
baseMD.Write(z)
zstate, _ := marshaler.MarshalBinary()
for i := 0; i < int(limit); i++ {
md := newHash()
err := md.(encoding.BinaryUnmarshaler).UnmarshalBinary(zstate)
if err != nil {
panic(err)
}
binary.BigEndian.PutUint32(countBytes[:], ct)
md.Write(countBytes[:])
k = md.Sum(k)
ct++
}
}
return k[:keyLen]
}