113 lines
3.1 KiB
Go
113 lines
3.1 KiB
Go
package crypto
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/ecdsa"
|
|
"crypto/elliptic"
|
|
"crypto/hmac"
|
|
"encoding/asn1"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"hash"
|
|
"math"
|
|
"math/big"
|
|
)
|
|
|
|
type ecdsaSignature struct {
|
|
R, S *big.Int
|
|
}
|
|
|
|
// ECDSAKey takes the given elliptic curve, and private key (d) byte slice
|
|
// and returns the private ECDSA key.
|
|
func ECDSAKey(curve elliptic.Curve, d []byte) *ecdsa.PrivateKey {
|
|
return ECDSAKeyFromPoint(curve, (&big.Int{}).SetBytes(d))
|
|
}
|
|
|
|
// ECDSAKeyFromPoint takes the given elliptic curve and point and returns the
|
|
// private and public keypair
|
|
func ECDSAKeyFromPoint(curve elliptic.Curve, d *big.Int) *ecdsa.PrivateKey {
|
|
pX, pY := curve.ScalarBaseMult(d.Bytes())
|
|
|
|
privKey := &ecdsa.PrivateKey{
|
|
PublicKey: ecdsa.PublicKey{
|
|
Curve: curve,
|
|
X: pX,
|
|
Y: pY,
|
|
},
|
|
D: d,
|
|
}
|
|
|
|
return privKey
|
|
}
|
|
|
|
// ECDSAPublicKey takes the provide curve and (x, y) coordinates and returns
|
|
// *ecdsa.PublicKey. Returns an error if the given points are not on the curve.
|
|
func ECDSAPublicKey(curve elliptic.Curve, x, y []byte) (*ecdsa.PublicKey, error) {
|
|
xPoint := (&big.Int{}).SetBytes(x)
|
|
yPoint := (&big.Int{}).SetBytes(y)
|
|
|
|
if !curve.IsOnCurve(xPoint, yPoint) {
|
|
return nil, fmt.Errorf("point(%v, %v) is not on the given curve", xPoint.String(), yPoint.String())
|
|
}
|
|
|
|
return &ecdsa.PublicKey{
|
|
Curve: curve,
|
|
X: xPoint,
|
|
Y: yPoint,
|
|
}, nil
|
|
}
|
|
|
|
// VerifySignature takes the provided public key, hash, and asn1 encoded signature and returns
|
|
// whether the given signature is valid.
|
|
func VerifySignature(key *ecdsa.PublicKey, hash []byte, signature []byte) (bool, error) {
|
|
var ecdsaSignature ecdsaSignature
|
|
|
|
_, err := asn1.Unmarshal(signature, &ecdsaSignature)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return ecdsa.Verify(key, hash, ecdsaSignature.R, ecdsaSignature.S), nil
|
|
}
|
|
|
|
// HMACKeyDerivation provides an implementation of a NIST-800-108 of a KDF (Key Derivation Function) in Counter Mode.
|
|
// For the purposes of this implantation HMAC is used as the PRF (Pseudorandom function), where the value of
|
|
// `r` is defined as a 4 byte counter.
|
|
func HMACKeyDerivation(hash func() hash.Hash, bitLen int, key []byte, label, context []byte) ([]byte, error) {
|
|
// verify that we won't overflow the counter
|
|
n := int64(math.Ceil((float64(bitLen) / 8) / float64(hash().Size())))
|
|
if n > 0x7FFFFFFF {
|
|
return nil, fmt.Errorf("unable to derive key of size %d using 32-bit counter", bitLen)
|
|
}
|
|
|
|
// verify the requested bit length is not larger then the length encoding size
|
|
if int64(bitLen) > 0x7FFFFFFF {
|
|
return nil, fmt.Errorf("bitLen is greater than 32-bits")
|
|
}
|
|
|
|
fixedInput := bytes.NewBuffer(nil)
|
|
fixedInput.Write(label)
|
|
fixedInput.WriteByte(0x00)
|
|
fixedInput.Write(context)
|
|
if err := binary.Write(fixedInput, binary.BigEndian, int32(bitLen)); err != nil {
|
|
return nil, fmt.Errorf("failed to write bit length to fixed input string: %v", err)
|
|
}
|
|
|
|
var output []byte
|
|
|
|
h := hmac.New(hash, key)
|
|
|
|
for i := int64(1); i <= n; i++ {
|
|
h.Reset()
|
|
if err := binary.Write(h, binary.BigEndian, int32(i)); err != nil {
|
|
return nil, err
|
|
}
|
|
_, err := h.Write(fixedInput.Bytes())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
output = append(output, h.Sum(nil)...)
|
|
}
|
|
|
|
return output[:bitLen/8], nil
|
|
}
|