From 1c959ea62c2641f6639e6abc237cebb7cd4cfd97 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Mon, 23 Oct 2023 15:46:55 +0200 Subject: [PATCH] implement DSA --- cng/das.go | 175 ------------ cng/dsa.go | 442 ++++++++++++++++++++++++++++++ cng/dsa_test.go | 124 ++++++++- internal/bcrypt/bcrypt_windows.go | 1 + 4 files changed, 560 insertions(+), 182 deletions(-) delete mode 100644 cng/das.go create mode 100644 cng/dsa.go diff --git a/cng/das.go b/cng/das.go deleted file mode 100644 index 70de62c..0000000 --- a/cng/das.go +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -//go:build windows -// +build windows - -package cng - -import ( - "errors" - "runtime" - "unsafe" - - "github.com/microsoft/go-crypto-winnative/internal/bcrypt" -) - -type dsaAlgorithm struct { - handle bcrypt.ALG_HANDLE -} - -func loadDSA() (h dsaAlgorithm, err error) { - v, err := loadOrStoreAlg(bcrypt.DSA_ALGORITHM, bcrypt.ALG_NONE_FLAG, "", func(h bcrypt.ALG_HANDLE) (interface{}, error) { - return dsaAlgorithm{h}, nil - }) - if err != nil { - return dsaAlgorithm{}, err - } - return v.(dsaAlgorithm), nil -} - -// DSAParameters contains the DSA parameters. -type DSAParameters struct { - P, Q, G BigInt -} - -// GenerateDSAParameters generates a set of DSA parameters for a key of size L bytes. -// If L is less than or equal to 1024, the parameters are generated according to FIPS 186-2. -// If L is greater than 1024, the parameters are generated according to FIPS 186-3. -// The returned parameters are suitable for use in GenerateKey. -func GenerateDSAParameters(L int) (DSAParameters, error) { - h, err := loadDSA() - if err != nil { - return DSAParameters{}, err - } - // To generate the parameters, we need to generate a key pair and then export the public key. - // The public key contains the parameters. We then discard the key pair. - var hkey bcrypt.KEY_HANDLE - if err := bcrypt.GenerateKeyPair(h.handle, &hkey, uint32(L), 0); err != nil { - return DSAParameters{}, err - } - defer bcrypt.DestroyKey(hkey) - - if err := bcrypt.FinalizeKeyPair(hkey, 0); err != nil { - return DSAParameters{}, err - } - return getDSAParameters(hkey, L) -} - -// DSAPrivateKey represents a DSA private key. -type DSAPrivateKey struct { - hkey bcrypt.KEY_HANDLE -} - -func (k *DSAPrivateKey) finalize() { - bcrypt.DestroyKey(k.hkey) -} - -// GenerateDSAKey generates a new private DSA key using the given parameters. -func GenerateDSAKey(params DSAParameters) (*DSAPrivateKey, error) { - h, err := loadDSA() - if err != nil { - return nil, err - } - var hkey bcrypt.KEY_HANDLE - if err := bcrypt.GenerateKeyPair(h.handle, &hkey, uint32(len(params.P))*8, 0); err != nil { - return nil, err - } - if err := setDSAParameter(hkey, params); err != nil { - bcrypt.DestroyKey(hkey) - return nil, err - } - if err := bcrypt.FinalizeKeyPair(hkey, 0); err != nil { - bcrypt.DestroyKey(hkey) - return nil, err - } - k := &DSAPrivateKey{hkey} - runtime.SetFinalizer(k, (*DSAPrivateKey).finalize) - return k, nil -} - -// getDSAParameters returns the DSA parameters for the given key. -func getDSAParameters(hkey bcrypt.KEY_HANDLE, L int) (DSAParameters, error) { - var data []byte - consumeBigInt := func(size uint32) BigInt { - b := data[:size] - data = data[size:] - return b - } - var err error - if L <= 1024 { - var hdr bcrypt.DSA_KEY_BLOB - hdr, data, err = exporDSAKey(hkey, false) - if err != nil { - return DSAParameters{}, err - } - if hdr.Magic != bcrypt.DSA_PUBLIC_MAGIC || hdr.KeySize*8 != uint32(L) { - return DSAParameters{}, errors.New("crypto/dsa: exported key is corrupted") - } - return DSAParameters{ - Q: hdr.Q[:], - P: consumeBigInt(hdr.KeySize), - G: consumeBigInt(hdr.KeySize), - }, nil - } - var hdr bcrypt.DSA_KEY_BLOB_V2 - hdr, data, err = exporDSAV2Key(hkey, false) - if err != nil { - return DSAParameters{}, err - } - if hdr.Magic != bcrypt.DSA_PUBLIC_MAGIC_V2 || hdr.KeySize*8 != uint32(L) { - return DSAParameters{}, errors.New("crypto/dsa: exported key is corrupted") - } - // Discard the seed, crypto/dsa doesn't use it. - consumeBigInt(hdr.SeedLength) - return DSAParameters{ - Q: consumeBigInt(hdr.GroupSize), - P: consumeBigInt(hdr.KeySize), - G: consumeBigInt(hdr.KeySize), - }, nil -} - -// setDSAParameter sets the DSA parameters for the given key. -func setDSAParameter(hkey bcrypt.KEY_HANDLE, params DSAParameters) error { - keySize := uint32(len(params.P)) - groupSize := uint32(len(params.Q)) - var blob []byte - var idx int - appendBigInt := func(b BigInt) { - copy(blob[idx:], b[:]) - idx += len(b) - } - if keySize*8 <= 1024 { - blob = make([]byte, sizeOfDSAParamsHeader+keySize*2) - hdr := bcrypt.DSA_PARAMETER_HEADER{ - Length: uint32(len(blob)), - Magic: bcrypt.DSA_PARAMETERS_MAGIC, - KeySize: keySize, - Count: [4]byte{0xff, 0xff, 0xff, 0xff}, // disables verification, we don't have the seed. - } - copy(hdr.Q[:], params.Q[:]) - copy(blob, (*(*[sizeOfDSAParamsHeader]byte)(unsafe.Pointer(&hdr)))[:]) - idx = int(sizeOfDSAParamsHeader) - appendBigInt(params.P) - appendBigInt(params.G) - } else { - blob = make([]byte, sizeOfDSAParamsV2Header+2*keySize+2*groupSize) - hdr := bcrypt.DSA_PARAMETER_HEADER_V2{ - Length: uint32(len(blob)), - Magic: bcrypt.DSA_PARAMETERS_MAGIC_V2, - KeySize: keySize, - GroupSize: groupSize, - HashAlgorithm: bcrypt.DSA_HASH_ALGORITHM_SHA256, - StandardVersion: bcrypt.DSA_FIPS186_3, - SeedLength: groupSize, // crypto/dsa doesn't use the seed, but it's size can't be zero. - Count: [4]byte{0xff, 0xff, 0xff, 0xff}, // disables verification, we don't have the seed. - } - copy(blob, (*(*[sizeOfDSAParamsV2Header]byte)(unsafe.Pointer(&hdr)))[:]) - idx = int(sizeOfDSAParamsV2Header + groupSize) // skip the seed - appendBigInt(params.Q) - appendBigInt(params.P) - appendBigInt(params.G) - - } - return bcrypt.SetProperty(bcrypt.HANDLE(hkey), utf16PtrFromString(bcrypt.DSA_PARAMETERS), blob, 0) -} diff --git a/cng/dsa.go b/cng/dsa.go new file mode 100644 index 0000000..09b43e6 --- /dev/null +++ b/cng/dsa.go @@ -0,0 +1,442 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//go:build windows +// +build windows + +package cng + +import ( + "errors" + "runtime" + "strconv" + "unsafe" + + "github.com/microsoft/go-crypto-winnative/internal/bcrypt" +) + +// As of FIPS 186-4 the maximum Q size is 32 bytes. +// +// See also: cbGroupSize at +// https://docs.microsoft.com/en-us/windows/desktop/api/bcrypt/ns-bcrypt-_bcrypt_dsa_key_blob_v2 +const maxGroupSize = 32 + +type dsaAlgorithm struct { + handle bcrypt.ALG_HANDLE +} + +func loadDSA() (h dsaAlgorithm, err error) { + v, err := loadOrStoreAlg(bcrypt.DSA_ALGORITHM, bcrypt.ALG_NONE_FLAG, "", func(h bcrypt.ALG_HANDLE) (interface{}, error) { + return dsaAlgorithm{h}, nil + }) + if err != nil { + return dsaAlgorithm{}, err + } + return v.(dsaAlgorithm), nil +} + +// DSAParameters contains the DSA parameters. +type DSAParameters struct { + P, Q, G BigInt +} + +// GenerateDSAParameters generates a set of DSA parameters for a key of size L bytes. +// If L is less than or equal to 1024, the parameters are generated according to FIPS 186-2. +// If L is greater than 1024, the parameters are generated according to FIPS 186-3. +// The returned parameters are suitable for use in GenerateKey. +func GenerateDSAParameters(L int) (params DSAParameters, X, Y BigInt, err error) { + h, err := loadDSA() + if err != nil { + return DSAParameters{}, nil, nil, err + } + // To generate the parameters, we need to generate a key pair and then export the public key. + // The public key contains the parameters. We then discard the key pair. + var hkey bcrypt.KEY_HANDLE + if err := bcrypt.GenerateKeyPair(h.handle, &hkey, uint32(L), 0); err != nil { + return DSAParameters{}, nil, nil, err + } + defer bcrypt.DestroyKey(hkey) + + if err := bcrypt.FinalizeKeyPair(hkey, 0); err != nil { + return DSAParameters{}, nil, nil, err + } + return decodeDSAKey(hkey, true) +} + +// PrivateKeyDSA represents a DSA private key. +type PrivateKeyDSA struct { + hkey bcrypt.KEY_HANDLE +} + +func (k *PrivateKeyDSA) finalize() { + bcrypt.DestroyKey(k.hkey) +} + +// Public returns the public key corresponding to priv. +func (k *PrivateKeyDSA) Public() (*PublicKeyDSA, error) { + defer runtime.KeepAlive(k) + h, err := loadDSA() + if err != nil { + return nil, err + } + params, _, Y, err := decodeDSAKey(k.hkey, false) + if err != nil { + return nil, err + } + hkey, err := encodeDSAKey(h.handle, params, nil, Y) + if err != nil { + return nil, err + } + pub := &PublicKeyDSA{hkey} + runtime.SetFinalizer(pub, (*PublicKeyDSA).finalize) + return pub, nil +} + +// PublicKeyDSA represents a DSA public key. +type PublicKeyDSA struct { + hkey bcrypt.KEY_HANDLE +} + +func (k *PublicKeyDSA) finalize() { + bcrypt.DestroyKey(k.hkey) +} + +// GenerateKeyDSA generates a new private DSA key using the given parameters. +func GenerateKeyDSA(params DSAParameters) (*PrivateKeyDSA, error) { + h, err := loadDSA() + if err != nil { + return nil, err + } + var hkey bcrypt.KEY_HANDLE + if err := bcrypt.GenerateKeyPair(h.handle, &hkey, uint32(len(params.P))*8, 0); err != nil { + return nil, err + } + if err := setDSAParameter(hkey, params); err != nil { + bcrypt.DestroyKey(hkey) + return nil, err + } + if err := bcrypt.FinalizeKeyPair(hkey, 0); err != nil { + bcrypt.DestroyKey(hkey) + return nil, err + } + k := &PrivateKeyDSA{hkey} + runtime.SetFinalizer(k, (*PrivateKeyDSA).finalize) + return k, nil +} + +// NewPrivateKeyDSA creates a new DSA private key from the given parameters. +func NewPrivateKeyDSA(params DSAParameters, X, Y BigInt) (*PrivateKeyDSA, error) { + h, err := loadDSA() + if err != nil { + return nil, err + } + hkey, err := encodeDSAKey(h.handle, params, X, Y) + if err != nil { + return nil, err + } + k := &PrivateKeyDSA{hkey} + runtime.SetFinalizer(k, (*PrivateKeyDSA).finalize) + return k, nil +} + +// NewPublicKeyDSA creates a new DSA public key from the given parameters. +func NewPublicKeyDSA(params DSAParameters, Y BigInt) (*PublicKeyDSA, error) { + h, err := loadDSA() + if err != nil { + return nil, err + } + hkey, err := encodeDSAKey(h.handle, params, nil, Y) + if err != nil { + return nil, err + } + k := &PublicKeyDSA{hkey} + runtime.SetFinalizer(k, (*PublicKeyDSA).finalize) + return k, nil +} + +// SignDSA signs a hash (which should be the result of hashing a larger message). +func SignDSA(priv *PrivateKeyDSA, hashed []byte) (r, s BigInt, err error) { + defer runtime.KeepAlive(priv) + size, err := getUint32(bcrypt.HANDLE(priv.hkey), bcrypt.SIGNATURE_LENGTH) + if err != nil { + return nil, nil, err + } + var buf [maxGroupSize]byte + hashed, err = dsaAdjustHashSize(priv.hkey, hashed, buf[:]) + if err != nil { + return nil, nil, err + } + sig := make([]byte, size) + err = bcrypt.SignHash(priv.hkey, nil, hashed, sig, &size, 0) + if err != nil { + return nil, nil, err + } + sig = sig[:size] + // BCRYPTSignHash generates DSA signatures in P1363 format, + // which is simply (r, s), each of them exactly half of the array. + if len(sig)%2 != 0 { + return nil, nil, errors.New("crypto/dsa: invalid signature size from bcrypt") + } + return sig[:len(sig)/2], sig[len(sig)/2:], nil +} + +// VerifyDSA verifies the signature in r, s of hashed using the public key, pub. +func VerifyDSA(pub *PublicKeyDSA, hashed []byte, r, s BigInt) bool { + defer runtime.KeepAlive(pub) + var buf [maxGroupSize]byte + hashed, err := dsaAdjustHashSize(pub.hkey, hashed, buf[:]) + if err != nil { + return false + } + size, err := getUint32(bcrypt.HANDLE(pub.hkey), bcrypt.SIGNATURE_LENGTH) + if err != nil { + return false + } + // r and s might be shorter than size + // if the original big number contained leading zeros, + // but they must not be longer than the public key size. + if len(r) > int(size/2) || len(s) > int(size/2) { + return false + } + sig := make([]byte, 0, 2*maxGroupSize) + prependZeros := func(nonZeroBytes int) { + if zeros := int(size/2) - nonZeroBytes; zeros > 0 { + sig = append(sig, make([]byte, zeros)...) + } + } + prependZeros(len(r)) + sig = append(sig, r...) + prependZeros(len(s)) + sig = append(sig, s...) + return keyVerify(pub.hkey, nil, hashed, sig, 0) == nil +} + +func encodeDSAKey(h bcrypt.ALG_HANDLE, params DSAParameters, X, Y BigInt) (bcrypt.KEY_HANDLE, error) { + keySize := uint32(len(params.P)) + groupSize := uint32(len(params.Q)) + private := X != nil + var blob []byte + var idx int + appendBigInt := func(b BigInt) { + copy(blob[idx:], b[:]) + idx += len(b) + } + if keySize*8 <= 1024 { + size := sizeOfDSABlobHeader + keySize*3 + hdr := bcrypt.DSA_KEY_BLOB{ + Magic: bcrypt.DSA_PUBLIC_MAGIC, + KeySize: keySize, + Count: [4]byte{0xff, 0xff, 0xff, 0xff}, // disables verification, we don't have the seed. + } + if private { + size += uint32(len(hdr.Q)) // private key is always 20 bytes + hdr.Magic = bcrypt.DSA_PRIVATE_MAGIC + } + for i := range hdr.Seed { + hdr.Seed[i] = 0xff + } + copy(hdr.Q[:], params.Q[:]) + blob = make([]byte, size) + copy(blob, (*(*[sizeOfDSABlobHeader]byte)(unsafe.Pointer(&hdr)))[:]) + idx = int(sizeOfDSABlobHeader) + appendBigInt(params.P) + appendBigInt(params.G) + appendBigInt(Y) + if private { + appendBigInt(X) + } + } else { + size := sizeOfDSAV2BlobHeader + 3*keySize + 2*groupSize + hashAlg := hashAlgFromGroup(int(groupSize)) + hdr := bcrypt.DSA_KEY_BLOB_V2{ + Magic: bcrypt.DSA_PUBLIC_MAGIC_V2, + KeySize: keySize, + GroupSize: groupSize, + HashAlgorithm: hashAlg, + StandardVersion: bcrypt.DSA_FIPS186_3, + SeedLength: groupSize, // crypto/dsa doesn't use the seed, but it must be equal to groupSize. + Count: [4]byte{0xff, 0xff, 0xff, 0xff}, // disables verification, we don't have the seed. + } + if private { + size += groupSize + hdr.Magic = bcrypt.DSA_PRIVATE_MAGIC_V2 + } + blob = make([]byte, size) + copy(blob, (*(*[sizeOfDSAV2BlobHeader]byte)(unsafe.Pointer(&hdr)))[:]) + idx = int(sizeOfDSAV2BlobHeader) + for i := idx; i < idx+int(groupSize); i++ { + blob[i] = 0xff + } + idx += int(groupSize) + appendBigInt(params.Q) + appendBigInt(params.P) + appendBigInt(params.G) + appendBigInt(Y) + if private { + appendBigInt(X) + } + } + kind := bcrypt.DSA_PUBLIC_BLOB + if private { + kind = bcrypt.DSA_PRIVATE_BLOB + } + var hkey bcrypt.KEY_HANDLE + err := bcrypt.ImportKeyPair(h, 0, utf16PtrFromString(kind), &hkey, blob, 0) + if err != nil { + return 0, err + } + return hkey, nil +} + +// decodeDSAKey decodes a DSA key. If private is true, the private exponent, X, is also returned. +func decodeDSAKey(hkey bcrypt.KEY_HANDLE, private bool) (params DSAParameters, X, Y BigInt, err error) { + var data []byte + consumeBigInt := func(size uint32) BigInt { + b := data[:size] + data = data[size:] + return b + } + var L uint32 + L, err = getUint32(bcrypt.HANDLE(hkey), bcrypt.KEY_LENGTH) + if err != nil { + return + } + if L <= 1024 { + var hdr bcrypt.DSA_KEY_BLOB + hdr, data, err = exporDSAKey(hkey, private) + if err != nil { + return + } + magic := bcrypt.DSA_PUBLIC_MAGIC + if private { + magic = bcrypt.DSA_PRIVATE_MAGIC + } + if hdr.Magic != magic || hdr.KeySize*8 != uint32(L) { + err = errors.New("crypto/dsa: exported key is corrupted") + return + } + params = DSAParameters{ + Q: hdr.Q[:], + P: consumeBigInt(hdr.KeySize), + G: consumeBigInt(hdr.KeySize), + } + Y = consumeBigInt(hdr.KeySize) + if private { + X = consumeBigInt(uint32(len(hdr.Q))) // private key is always 20 bytes + } + } else { + var hdr bcrypt.DSA_KEY_BLOB_V2 + hdr, data, err = exporDSAV2Key(hkey, private) + if err != nil { + return + } + magic := bcrypt.DSA_PUBLIC_MAGIC_V2 + if private { + magic = bcrypt.DSA_PRIVATE_MAGIC_V2 + } + if hdr.Magic != magic || hdr.KeySize*8 != uint32(L) { + err = errors.New("crypto/dsa: exported key is corrupted") + return + } + // Discard the seed, crypto/dsa doesn't use it. + consumeBigInt(hdr.SeedLength) + params = DSAParameters{ + Q: consumeBigInt(hdr.GroupSize), + P: consumeBigInt(hdr.KeySize), + G: consumeBigInt(hdr.KeySize), + } + Y = consumeBigInt(hdr.KeySize) + if private { + X = consumeBigInt(hdr.GroupSize) + } + } + return params, X, Y, nil +} + +// setDSAParameter sets the DSA parameters for the given key. +func setDSAParameter(hkey bcrypt.KEY_HANDLE, params DSAParameters) error { + keySize := uint32(len(params.P)) + groupSize := uint32(len(params.Q)) + var blob []byte + var idx int + appendBigInt := func(b BigInt) { + copy(blob[idx:], b[:]) + idx += len(b) + } + if keySize*8 <= 1024 { + blob = make([]byte, sizeOfDSAParamsHeader+keySize*2) + hdr := bcrypt.DSA_PARAMETER_HEADER{ + Length: uint32(len(blob)), + Magic: bcrypt.DSA_PARAMETERS_MAGIC, + KeySize: keySize, + Count: [4]byte{0xff, 0xff, 0xff, 0xff}, // disables verification, we don't have the seed. + } + copy(hdr.Q[:], params.Q[:]) + copy(blob, (*(*[sizeOfDSAParamsHeader]byte)(unsafe.Pointer(&hdr)))[:]) + idx = int(sizeOfDSAParamsHeader) + appendBigInt(params.P) + appendBigInt(params.G) + } else { + blob = make([]byte, sizeOfDSAParamsV2Header+2*keySize+2*groupSize) + hashAlg := hashAlgFromGroup(int(groupSize)) + hdr := bcrypt.DSA_PARAMETER_HEADER_V2{ + Length: uint32(len(blob)), + Magic: bcrypt.DSA_PARAMETERS_MAGIC_V2, + KeySize: keySize, + GroupSize: groupSize, + HashAlgorithm: hashAlg, + StandardVersion: bcrypt.DSA_FIPS186_3, + SeedLength: groupSize, // crypto/dsa doesn't use the seed, but CNG expects it to be groupSize. + Count: [4]byte{0xff, 0xff, 0xff, 0xff}, // disables verification, we don't have the seed. + } + copy(blob, (*(*[sizeOfDSAParamsV2Header]byte)(unsafe.Pointer(&hdr)))[:]) + idx = int(sizeOfDSAParamsV2Header + groupSize) // skip the seed + appendBigInt(params.Q) + appendBigInt(params.P) + appendBigInt(params.G) + + } + return bcrypt.SetProperty(bcrypt.HANDLE(hkey), utf16PtrFromString(bcrypt.DSA_PARAMETERS), blob, 0) +} + +func dsaAdjustHashSize(hkey bcrypt.KEY_HANDLE, hashed []byte, buf []byte) ([]byte, error) { + // Windows CNG requires that the hash output and Q match sizes, but we can better + // interoperate with other FIPS 186-3 implementations if we perform truncation + // here, before sending it to CNG. + // + // If, on the other hand, Q is too big, we need to left-pad the hash with zeroes + // (since it gets treated as a big-endian number). + params, _, _, err := decodeDSAKey(hkey, false) + if err != nil { + return nil, err + } + groupSize := len(params.Q) + if groupSize > len(buf) { + panic("output buffer too small") + } + if groupSize == len(hashed) { + return hashed, nil + } + if groupSize < len(hashed) { + return hashed[:groupSize], nil + } + zeroByteCount := groupSize - len(hashed) + for i := 0; i < zeroByteCount; i++ { + buf[i] = 0 + } + copy(buf[zeroByteCount:], hashed) + return buf[:groupSize], nil +} + +func hashAlgFromGroup(groupSize int) bcrypt.HASHALGORITHM_ENUM { + switch groupSize { + case 20: + return bcrypt.DSA_HASH_ALGORITHM_SHA1 + case 32: + return bcrypt.DSA_HASH_ALGORITHM_SHA256 + case 64: + return bcrypt.DSA_HASH_ALGORITHM_SHA512 + default: + panic("invalid group size: " + strconv.Itoa(groupSize)) + } +} diff --git a/cng/dsa_test.go b/cng/dsa_test.go index 747734b..16f00d6 100644 --- a/cng/dsa_test.go +++ b/cng/dsa_test.go @@ -7,6 +7,8 @@ package cng_test import ( + "crypto/dsa" + "hash" "math/big" "testing" @@ -15,15 +17,15 @@ import ( ) func TestGenerateDSAParameters(t *testing.T) { - testParameterGeneration(t, 1024, 160) - testParameterGeneration(t, 2048, 256) - testParameterGeneration(t, 3072, 256) + testGenerateDSAParameters(t, 1024, 160, cng.NewSHA1()) + testGenerateDSAParameters(t, 2048, 256, cng.NewSHA256()) + testGenerateDSAParameters(t, 3072, 256, cng.NewSHA256()) } -func testParameterGeneration(t *testing.T, L, N int) { - params, err := cng.GenerateDSAParameters(L) +func testGenerateDSAParameters(t *testing.T, L, N int, h hash.Hash) { + params, X, Y, err := cng.GenerateDSAParameters(L) if err != nil { - t.Errorf("%d: %d-%d", L, N, err) + t.Errorf("%d-%d: error generating parameters: %s", L, N, err) return } @@ -51,9 +53,117 @@ func testParameterGeneration(t *testing.T, L, N int) { t.Errorf("%d-%d: invalid generator", L, N) } - _, err = cng.GenerateDSAKey(params) + priv, err := cng.GenerateKeyDSA(params) if err != nil { t.Errorf("error generating key: %s", err) return } + + testDSASignAndVerify(t, L, priv, params, X, Y, h) +} + +func testDSASignAndVerify(t *testing.T, i int, priv *cng.PrivateKeyDSA, params cng.DSAParameters, X, Y cng.BigInt, h hash.Hash) { + hashed := []byte("testing") + + r, s, err := cng.SignDSA(priv, hashed[:]) + if err != nil { + t.Errorf("%d: error signing: %s", i, err) + return + } + pub, err := priv.Public() + if err != nil { + t.Errorf("%d: error getting public key: %s", i, err) + return + } + if !cng.VerifyDSA(pub, hashed[:], r, s) { + t.Errorf("%d: error verifying", i) + return + } + + // Test compatibility with crypto/dsa. + // TODO: This fails, sometimes... + priv1 := dsa.PrivateKey{ + PublicKey: dsa.PublicKey{ + Parameters: dsa.Parameters{ + P: bbig.Dec(params.P), + Q: bbig.Dec(params.Q), + G: bbig.Dec(params.G), + }, + Y: bbig.Dec(Y), + }, + X: bbig.Dec(X), + } + if !dsa.Verify(&priv1.PublicKey, hashed[:], bbig.Dec(r), bbig.Dec(s)) { + t.Errorf("%d: compat: crypto/dsa can't verify CNG signature", i) + } + r1, s1, err := dsa.Sign(cng.RandReader, &priv1, hashed[:]) + if err != nil { + t.Errorf("%d: error signing: %s", i, err) + return + } + if !cng.VerifyDSA(pub, hashed[:], bbig.Enc(r1), bbig.Enc(s1)) { + t.Errorf("%d: compat: CNG can't verify crypto/dsa signature", i) + return + } +} + +func fromHex(s string) *big.Int { + result, ok := new(big.Int).SetString(s, 16) + if !ok { + panic(s) + } + return result +} + +func TestDSASignAndVerify(t *testing.T) { + params := cng.DSAParameters{ + P: bbig.Enc(fromHex("A9B5B793FB4785793D246BAE77E8FF63CA52F442DA763C440259919FE1BC1D6065A9350637A04F75A2F039401D49F08E066C4D275A5A65DA5684BC563C14289D7AB8A67163BFBF79D85972619AD2CFF55AB0EE77A9002B0EF96293BDD0F42685EBB2C66C327079F6C98000FBCB79AACDE1BC6F9D5C7B1A97E3D9D54ED7951FEF")), + Q: bbig.Enc(fromHex("E1D3391245933D68A0714ED34BBCB7A1F422B9C1")), + G: bbig.Enc(fromHex("634364FC25248933D01D1993ECABD0657CC0CB2CEED7ED2E3E8AECDFCDC4A25C3B15E9E3B163ACA2984B5539181F3EFF1A5E8903D71D5B95DA4F27202B77D2C44B430BB53741A8D59A8F86887525C9F2A6A5980A195EAA7F2FF910064301DEF89D3AA213E1FAC7768D89365318E370AF54A112EFBA9246D9158386BA1B4EEFDA")), + } + Y := bbig.Enc(fromHex("32969E5780CFE1C849A1C276D7AEB4F38A23B591739AA2FE197349AEEBD31366AEE5EB7E6C6DDB7C57D02432B30DB5AA66D9884299FAA72568944E4EEDC92EA3FBC6F39F53412FBCC563208F7C15B737AC8910DBC2D9C9B8C001E72FDC40EB694AB1F06A5A2DBD18D9E36C66F31F566742F11EC0A52E9F7B89355C02FB5D32D2")) + X := bbig.Enc(fromHex("5078D4D29795CBE76D3AACFE48C9AF0BCDBEE91A")) + priv, err := cng.NewPrivateKeyDSA(params, X, Y) + if err != nil { + t.Fatalf("error generating key: %s", err) + } + + testDSASignAndVerify(t, 0, priv, params, X, Y, cng.NewSHA1()) +} + +func TestNewPublicKeyDSAWithBadPublicKey(t *testing.T) { + params := cng.DSAParameters{ + P: bbig.Enc(fromHex("A9B5B793FB4785793D246BAE77E8FF63CA52F442DA763C440259919FE1BC1D6065A9350637A04F75A2F039401D49F08E066C4D275A5A65DA5684BC563C14289D7AB8A67163BFBF79D85972619AD2CFF55AB0EE77A9002B0EF96293BDD0F42685EBB2C66C327079F6C98000FBCB79AACDE1BC6F9D5C7B1A97E3D9D54ED7951FEF")), + Q: bbig.Enc(fromHex("FA")), + G: bbig.Enc(fromHex("634364FC25248933D01D1993ECABD0657CC0CB2CEED7ED2E3E8AECDFCDC4A25C3B15E9E3B163ACA2984B5539181F3EFF1A5E8903D71D5B95DA4F27202B77D2C44B430BB53741A8D59A8F86887525C9F2A6A5980A195EAA7F2FF910064301DEF89D3AA213E1FAC7768D89365318E370AF54A112EFBA9246D9158386BA1B4EEFDA")), + } + Y := bbig.Enc(fromHex("32969E5780CFE1C849A1C276D7AEB4F38A23B591739AA2FE197349AEEBD31366AEE5EB7E6C6DDB7C57D02432B30DB5AA66D9884299FAA72568944E4EEDC92EA3FBC6F39F53412FBCC563208F7C15B737AC8910DBC2D9C9B8C001E72FDC40EB694AB1F06A5A2DBD18D9E36C66F31F566742F11EC0A52E9F7B89355C02FB5D32D2")) + + _, err := cng.NewPublicKeyDSA(params, Y) + if err == nil { + t.Errorf("Unexpected success with non-existent mod inverse of Q") + } +} + +func TestNewPrivateKeyDSAWithDegenerateKeys(t *testing.T) { + // Signing with degenerate private keys should not cause an infinite + // loop. + badKeys := []struct { + p, q, g, y, x string + }{ + {"00", "01", "00", "00", "00"}, + {"01", "ff", "00", "00", "00"}, + } + + for i, test := range badKeys { + params := cng.DSAParameters{ + P: bbig.Enc(fromHex(test.p)), + Q: bbig.Enc(fromHex(test.q)), + G: bbig.Enc(fromHex(test.g)), + } + _, err := cng.NewPrivateKeyDSA(params, bbig.Enc(fromHex(test.x)), bbig.Enc(fromHex(test.y))) + if err == nil { + t.Errorf("#%d: error generating key: %s", i, err) + } + } } diff --git a/internal/bcrypt/bcrypt_windows.go b/internal/bcrypt/bcrypt_windows.go index 8208243..01f7fa3 100644 --- a/internal/bcrypt/bcrypt_windows.go +++ b/internal/bcrypt/bcrypt_windows.go @@ -51,6 +51,7 @@ const ( CHAIN_MODE_GCM = "ChainingModeGCM" KEY_LENGTH = "KeyLength" KEY_LENGTHS = "KeyLengths" + SIGNATURE_LENGTH = "SignatureLength" BLOCK_LENGTH = "BlockLength" ECC_CURVE_NAME = "ECCCurveName" )