github.com/emmansun/gmsm@v0.29.1/kdf/kdf.go (about)

     1  // Package kdf implements ShangMi(SM) used Key Derivation Function, compliances with GB/T 32918.4-2016 5.4.3.
     2  package kdf
     3  
     4  import (
     5  	"encoding"
     6  	"encoding/binary"
     7  	"hash"
     8  )
     9  
    10  // KdfInterface is the interface implemented by some specific Hash implementations.
    11  type KdfInterface interface {
    12  	Kdf(z []byte, keyLen int) []byte
    13  }
    14  
    15  // Kdf key derivation function, compliance with GB/T 32918.4-2016 5.4.3.
    16  // ANSI-X9.63-KDF
    17  func Kdf(newHash func() hash.Hash, z []byte, keyLen int) []byte {
    18  	baseMD := newHash()
    19  	// If the hash implements KdfInterface, use the optimized Kdf method.
    20  	if kdfImpl, ok := baseMD.(KdfInterface); ok {
    21  		return kdfImpl.Kdf(z, keyLen)
    22  	}
    23  	limit := uint64(keyLen+baseMD.Size()-1) / uint64(baseMD.Size())
    24  	if limit >= uint64(1<<32)-1 {
    25  		panic("kdf: key length too long")
    26  	}
    27  	var countBytes [4]byte
    28  	var ct uint32 = 1
    29  	var k []byte
    30  
    31  	if marshaler, ok := baseMD.(encoding.BinaryMarshaler); limit == 1 || len(z) < baseMD.BlockSize() || !ok {
    32  		for i := 0; i < int(limit); i++ {
    33  			binary.BigEndian.PutUint32(countBytes[:], ct)
    34  			baseMD.Write(z)
    35  			baseMD.Write(countBytes[:])
    36  			k = baseMD.Sum(k)
    37  			ct++
    38  			baseMD.Reset()
    39  		}
    40  	} else {
    41  		baseMD.Write(z)
    42  		zstate, _ := marshaler.MarshalBinary()
    43  		for i := 0; i < int(limit); i++ {
    44  			md := newHash()
    45  			err := md.(encoding.BinaryUnmarshaler).UnmarshalBinary(zstate)
    46  			if err != nil {
    47  				panic(err)
    48  			}
    49  			binary.BigEndian.PutUint32(countBytes[:], ct)
    50  			md.Write(countBytes[:])
    51  			k = md.Sum(k)
    52  			ct++
    53  		}		
    54  	}
    55  
    56  	return k[:keyLen]
    57  }