From 6903ab32dd387b3b32a84623b9cce45ccc846e75 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Sat, 21 Oct 2023 15:28:58 +0200 Subject: [PATCH] implement crypto/dsa --- cng/das.go | 160 ---------- cng/dsa.go | 473 ++++++++++++++++++++++++++++++ cng/dsa_test.go | 128 +++++++- cng/keys.go | 2 +- internal/bcrypt/bcrypt_windows.go | 1 + 5 files changed, 597 insertions(+), 167 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 6361ed5..0000000 --- a/cng/das.go +++ /dev/null @@ -1,160 +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 -} - -type DSAParameters struct { - P, Q, G, S BigInt -} - -func GenerateDSAParameters(l int) (DSAParameters, error) { - h, err := loadDSA() - if err != nil { - return DSAParameters{}, err - } - 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 - } - if l <= 1024 { - 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") - } - consumeBigInt := func(size uint32) BigInt { - b := data[:size] - data = data[size:] - return b - } - return DSAParameters{ - S: hdr.Seed[:], - Q: hdr.Q[:], - P: consumeBigInt(hdr.KeySize), - G: consumeBigInt(hdr.KeySize), - }, nil - } - 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") - } - consumeBigInt := func(size uint32) BigInt { - b := data[:size] - data = data[size:] - return b - } - return DSAParameters{ - S: consumeBigInt(hdr.SeedLength), - Q: consumeBigInt(hdr.GroupSize), - P: consumeBigInt(hdr.KeySize), - G: consumeBigInt(hdr.KeySize), - }, nil -} - -type DSAPrivateKey struct { - hkey bcrypt.KEY_HANDLE -} - -func (k *DSAPrivateKey) finalize() { - bcrypt.DestroyKey(k.hkey) -} - -func GenerateKey(params DSAParameters) (*DSAPrivateKey, error) { - h, err := loadDSA() - if err != nil { - return nil, err - } - keySize := uint32(len(params.P)) - groupSize := uint32(len(params.Q)) - seedSize := uint32(len(params.S)) - var hkey bcrypt.KEY_HANDLE - if err := bcrypt.GenerateKeyPair(h.handle, &hkey, keySize*8, 0); err != nil { - return nil, err - } - var blob []byte - if keySize*8 <= 1024 { - blob = make([]byte, sizeOfDSAParamsHeader+keySize*2) - hdr := bcrypt.DSA_PARAMETER_HEADER{ - Length: uint32(len(blob)), - Magic: bcrypt.DSA_PARAMETERS_MAGIC_V2, - KeySize: keySize, - //Count: , - } - copy(hdr.Seed[:], params.S[:]) - copy(hdr.Q[:], params.Q[:]) - copy(blob, (*(*[sizeOfDSAParamsHeader]byte)(unsafe.Pointer(&hdr)))[:]) - i := int(sizeOfDSAParamsHeader) - copy(blob[i:], params.P[:]) - i += len(params.P) - copy(blob[i:], params.G[:]) - } else { - blob = make([]byte, sizeOfDSAParamsV2Header+keySize*2+groupSize+seedSize) - 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: seedSize, - //Count: , - } - copy(blob, (*(*[sizeOfDSAParamsV2Header]byte)(unsafe.Pointer(&hdr)))[:]) - i := int(sizeOfDSAParamsV2Header) - copy(blob[i:], params.S[:]) - i += len(params.S) - copy(blob[i:], params.Q[:]) - i += len(params.Q) - copy(blob[i:], params.P[:]) - i += len(params.P) - copy(blob[i:], params.G[:]) - - } - // TODO: This still not works... - if err := bcrypt.SetProperty(bcrypt.HANDLE(hkey), utf16PtrFromString(bcrypt.DSA_PARAMETERS), blob, 0); 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 -} diff --git a/cng/dsa.go b/cng/dsa.go new file mode 100644 index 0000000..efe46bd --- /dev/null +++ b/cng/dsa.go @@ -0,0 +1,473 @@ +// 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 + +// crypto/dsa doesn't support passing the seed around, but CNG expects it. +// CNG will skip seed verification if the count and seed parameters is all 0xff bytes. +var ( + dsaCountNil = [4]byte{0xff, 0xff, 0xff, 0xff} + dsaSeedNil = [maxGroupSize]byte{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + } +) + +type dsaAlgorithm struct { + handle bcrypt.ALG_HANDLE + allowedKeyLengths bcrypt.KEY_LENGTHS_STRUCT +} + +func loadDSA() (h dsaAlgorithm, err error) { + v, err := loadOrStoreAlg(bcrypt.DSA_ALGORITHM, bcrypt.ALG_NONE_FLAG, "", func(h bcrypt.ALG_HANDLE) (interface{}, error) { + lengths, err := getKeyLengths(bcrypt.HANDLE(h)) + if err != nil { + return nil, err + } + return dsaAlgorithm{h, lengths}, nil + }) + if err != nil { + return dsaAlgorithm{}, err + } + return v.(dsaAlgorithm), nil +} + +// DSAParameters contains the DSA parameters. +type DSAParameters struct { + P, Q, G BigInt +} + +func (p DSAParameters) keySize() uint32 { + return uint32(len(p.P)) +} + +func (p DSAParameters) groupSize() uint32 { + return uint32(len(p.Q)) +} + +// 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, err error) { + h, err := loadDSA() + if err != nil { + return DSAParameters{}, err + } + if !keyIsAllowed(h.allowedKeyLengths, uint32(L)) { + return DSAParameters{}, errors.New("crypto/dsa: invalid key size") + } + // 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 + } + params, _, _, err = decodeDSAKey(hkey, false) + return params, err +} + +// PrivateKeyDSA represents a DSA private key. +type PrivateKeyDSA struct { + hkey bcrypt.KEY_HANDLE +} + +func (k *PrivateKeyDSA) finalize() { + bcrypt.DestroyKey(k.hkey) +} + +func (k *PrivateKeyDSA) Data() (params DSAParameters, X, Y BigInt, err error) { + defer runtime.KeepAlive(k) + return decodeDSAKey(k.hkey, true) +} + +// PublicKeyDSA represents a DSA public key. +type PublicKeyDSA struct { + hkey bcrypt.KEY_HANDLE +} + +func (k *PublicKeyDSA) finalize() { + bcrypt.DestroyKey(k.hkey) +} + +func (k *PublicKeyDSA) Data() (params DSAParameters, Y BigInt, err error) { + defer runtime.KeepAlive(k) + params, _, Y, err = decodeDSAKey(k.hkey, false) + return +} + +// 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 + } + keySize := params.keySize() + if !keyIsAllowed(h.allowedKeyLengths, keySize*8) { + return nil, errors.New("crypto/dsa: invalid key size") + } + var hkey bcrypt.KEY_HANDLE + if err := bcrypt.GenerateKeyPair(h.handle, &hkey, keySize*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 + } + keySize := params.keySize() + if !keyIsAllowed(h.allowedKeyLengths, keySize*8) { + return nil, errors.New("crypto/dsa: invalid key size") + } + 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 + } + keySize := params.keySize() + if !keyIsAllowed(h.allowedKeyLengths, keySize*8) { + return nil, errors.New("crypto/dsa: invalid key size") + } + 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 := params.keySize() + groupSize := params.groupSize() + private := X != nil + var blob []byte + if keySize*8 <= 1024 { + size := sizeOfDSABlobHeader + keySize*3 + hdr := bcrypt.DSA_KEY_BLOB{ + Magic: bcrypt.DSA_PUBLIC_MAGIC, + KeySize: keySize, + Count: dsaCountNil, + } + if private { + size += uint32(len(hdr.Q)) // private key is always 20 bytes + hdr.Magic = bcrypt.DSA_PRIVATE_MAGIC + } + copy(hdr.Seed[:], dsaSeedNil[:]) + copy(hdr.Q[:], params.Q[:]) + blob = make([]byte, size) + copy(blob, (*(*[sizeOfDSABlobHeader]byte)(unsafe.Pointer(&hdr)))[:]) + data := blob[sizeOfDSABlobHeader:] + if err := encodeBigInt(data, []sizedBigInt{ + {params.P, keySize}, + {params.G, keySize}, + {Y, keySize}, + {X, groupSize}, + }); err != nil { + return 0, err + } + } 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: dsaCountNil, + } + if private { + size += groupSize + hdr.Magic = bcrypt.DSA_PRIVATE_MAGIC_V2 + } + blob = make([]byte, size) + copy(blob, (*(*[sizeOfDSAV2BlobHeader]byte)(unsafe.Pointer(&hdr)))[:]) + data := blob[sizeOfDSAV2BlobHeader:] + if err := encodeBigInt(data, []sizedBigInt{ + {dsaSeedNil[:], groupSize}, + {params.Q, groupSize}, + {params.P, keySize}, + {params.G, keySize}, + {Y, keySize}, + {X, groupSize}, + }); err != nil { + return 0, err + } + } + 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 := params.keySize() + groupSize := params.groupSize() + var blob []byte + 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: dsaCountNil, + } + copy(hdr.Seed[:], dsaSeedNil[:]) + copy(hdr.Q[:], params.Q[:]) + copy(blob, (*(*[sizeOfDSAParamsHeader]byte)(unsafe.Pointer(&hdr)))[:]) + data := blob[sizeOfDSAParamsHeader:] + if err := encodeBigInt(data, []sizedBigInt{ + {params.P, keySize}, + {params.G, keySize}, + }); err != nil { + return err + } + } 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: dsaCountNil, + } + copy(blob, (*(*[sizeOfDSAParamsV2Header]byte)(unsafe.Pointer(&hdr)))[:]) + data := blob[sizeOfDSAParamsV2Header:] + if err := encodeBigInt(data, []sizedBigInt{ + {dsaSeedNil[:], groupSize}, + {params.Q, groupSize}, + {params.P, keySize}, + {params.G, keySize}, + }); err != nil { + return err + } + + } + 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 := int(params.groupSize()) + 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 bb29711..fc004ac 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) { +func testGenerateDSAParameters(t *testing.T, L, N int, h hash.Hash) { params, 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,123 @@ func testParameterGeneration(t *testing.T, L, N int) { t.Errorf("%d-%d: invalid generator", L, N) } - _, err = cng.GenerateKey(params) + priv, err := cng.GenerateKeyDSA(params) if err != nil { t.Errorf("error generating key: %s", err) return } + + testDSASignAndVerify(t, L, priv, h) +} + +func testDSASignAndVerify(t *testing.T, i int, priv *cng.PrivateKeyDSA, h hash.Hash) { + hashed := []byte("testing") + h.Write(hashed) + hashed = h.Sum(nil) + r, s, err := cng.SignDSA(priv, hashed[:]) + if err != nil { + t.Errorf("%d: error signing: %s", i, err) + return + } + params, X, Y, err := priv.Data() + if err != nil { + t.Errorf("%d: error exporting key: %s", i, err) + return + } + pub, err := cng.NewPublicKeyDSA(params, Y) + 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, 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/cng/keys.go b/cng/keys.go index 9184e4b..916ee94 100644 --- a/cng/keys.go +++ b/cng/keys.go @@ -20,7 +20,7 @@ const ( sizeOfDSABlobHeader = uint32(unsafe.Sizeof(bcrypt.DSA_KEY_BLOB{})) sizeOfDSAV2BlobHeader = uint32(unsafe.Sizeof(bcrypt.DSA_KEY_BLOB_V2{})) sizeOfDSAParamsHeader = uint32(unsafe.Sizeof(bcrypt.DSA_PARAMETER_HEADER{})) - sizeOfDSAParamsV2Header = uint32(unsafe.Sizeof(bcrypt.DSA_PARAMETER_HEADER{})) + sizeOfDSAParamsV2Header = uint32(unsafe.Sizeof(bcrypt.DSA_PARAMETER_HEADER_V2{})) ) // exporDSAKey exports hkey into a bcrypt.DSA_KEY_BLOB header and data. 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" )