Skip to content

Commit

Permalink
implement GenerateDSAParameters
Browse files Browse the repository at this point in the history
  • Loading branch information
qmuntal committed Oct 20, 2023
1 parent ebaf9de commit 147ed74
Show file tree
Hide file tree
Showing 5 changed files with 338 additions and 0 deletions.
7 changes: 7 additions & 0 deletions cng/cng.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package cng

import (
"encoding/binary"
"errors"
"math"
"runtime"
Expand Down Expand Up @@ -103,6 +104,12 @@ func getUint32(h bcrypt.HANDLE, name string) (uint32, error) {
return prop, err
}

func setUint32(h bcrypt.HANDLE, name string, val uint32) error {
var p [4]byte
binary.LittleEndian.PutUint32(p[:], val)
return bcrypt.SetProperty(h, utf16PtrFromString(name), p[:], 0)
}

const sizeOfKEY_LENGTHS_STRUCT = unsafe.Sizeof(bcrypt.KEY_LENGTHS_STRUCT{})

func getKeyLengths(h bcrypt.HANDLE) (lengths bcrypt.KEY_LENGTHS_STRUCT, err error) {
Expand Down
160 changes: 160 additions & 0 deletions cng/das.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// 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
}
59 changes: 59 additions & 0 deletions cng/dsa_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//go:build windows
// +build windows

package cng_test

import (
"math/big"
"testing"

"github.com/microsoft/go-crypto-winnative/cng"
"github.com/microsoft/go-crypto-winnative/cng/bbig"
)

func TestGenerateDSAParameters(t *testing.T) {
testParameterGeneration(t, 1024, 160)
testParameterGeneration(t, 2048, 256)
testParameterGeneration(t, 3072, 256)
}

func testParameterGeneration(t *testing.T, L, N int) {
params, err := cng.GenerateDSAParameters(L)
if err != nil {
t.Errorf("%d: %d-%d", L, N, err)
return
}

P := bbig.Dec(params.P)
Q := bbig.Dec(params.Q)
G := bbig.Dec(params.G)

if P.BitLen() != L {
t.Errorf("%d-%d: params.BitLen got:%d want:%d", L, N, P.BitLen(), L)
}

if Q.BitLen() != N {
t.Errorf("%d-%d: q.BitLen got:%d want:%d", L, N, Q.BitLen(), L)
}

one := new(big.Int)
one.SetInt64(1)
pm1 := new(big.Int).Sub(P, one)
quo, rem := new(big.Int).DivMod(pm1, Q, new(big.Int))
if rem.Sign() != 0 {
t.Errorf("%d-%d: p-1 mod q != 0", L, N)
}
x := new(big.Int).Exp(G, quo, P)
if x.Cmp(one) == 0 {
t.Errorf("%d-%d: invalid generator", L, N)
}

_, err = cng.GenerateKey(params)
if err != nil {
t.Errorf("error generating key: %s", err)
return
}
}
42 changes: 42 additions & 0 deletions cng/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,50 @@ const (
sizeOfECCBlobHeader = uint32(unsafe.Sizeof(bcrypt.ECCKEY_BLOB{}))
sizeOfRSABlobHeader = uint32(unsafe.Sizeof(bcrypt.RSAKEY_BLOB{}))
sizeOfKeyDataBlobHeader = uint32(unsafe.Sizeof(bcrypt.KEY_DATA_BLOB_HEADER{}))
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{}))
)

// exporDSAKey exports hkey into a bcrypt.DSA_KEY_BLOB header and data.
func exporDSAKey(hkey bcrypt.KEY_HANDLE, private bool) (bcrypt.DSA_KEY_BLOB, []byte, error) {
var magic string
if private {
magic = bcrypt.DSA_PRIVATE_BLOB
} else {
magic = bcrypt.DSA_PUBLIC_BLOB
}
blob, err := exportKey(hkey, magic)
if err != nil {
return bcrypt.DSA_KEY_BLOB{}, nil, err
}
if len(blob) < int(sizeOfDSABlobHeader) {
return bcrypt.DSA_KEY_BLOB{}, nil, errors.New("cng: exported key is corrupted")
}
hdr := (*(*bcrypt.DSA_KEY_BLOB)(unsafe.Pointer(&blob[0])))
return hdr, blob[sizeOfDSABlobHeader:], nil
}

// exporDSAV2Key exports hkey into a bcrypt.DSA_KEY_BLOB_V2 header and data.
func exporDSAV2Key(hkey bcrypt.KEY_HANDLE, private bool) (bcrypt.DSA_KEY_BLOB_V2, []byte, error) {
var magic string
if private {
magic = bcrypt.DSA_PRIVATE_BLOB
} else {
magic = bcrypt.DSA_PUBLIC_BLOB
}
blob, err := exportKey(hkey, magic)
if err != nil {
return bcrypt.DSA_KEY_BLOB_V2{}, nil, err
}
if len(blob) < int(sizeOfDSAV2BlobHeader) {
return bcrypt.DSA_KEY_BLOB_V2{}, nil, errors.New("cng: exported key is corrupted")
}
hdr := (*(*bcrypt.DSA_KEY_BLOB_V2)(unsafe.Pointer(&blob[0])))
return hdr, blob[sizeOfDSAV2BlobHeader:], nil
}

// exportRSAKey exports hkey into a bcrypt.ECCKEY_BLOB header and data.
func exportECCKey(hkey bcrypt.KEY_HANDLE, private bool) (bcrypt.ECCKEY_BLOB, []byte, error) {
var magic string
Expand Down
70 changes: 70 additions & 0 deletions internal/bcrypt/bcrypt_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const (
DES3_ALGORITHM = "3DES" // 3DES_ALGORITHM
TLS1_1_KDF_ALGORITHM = "TLS1_1_KDF"
TLS1_2_KDF_ALGORITHM = "TLS1_2_KDF"
DSA_ALGORITHM = "DSA"
)

const (
Expand Down Expand Up @@ -59,6 +60,8 @@ const (
RSAFULLPRIVATE_BLOB = "RSAFULLPRIVATEBLOB"
ECCPUBLIC_BLOB = "ECCPUBLICBLOB"
ECCPRIVATE_BLOB = "ECCPRIVATEBLOB"
DSA_PUBLIC_BLOB = "DSAPUBLICBLOB"
DSA_PRIVATE_BLOB = "DSAPRIVATEBLOB"
)

const (
Expand Down Expand Up @@ -109,6 +112,45 @@ const (
KDF_RAW_SECRET = "TRUNCATE"
)

const (
DSA_PARAMETERS = "DSAParameters"
)

type HASHALGORITHM_ENUM uint32

const (
DSA_HASH_ALGORITHM_SHA1 HASHALGORITHM_ENUM = iota
DSA_HASH_ALGORITHM_SHA256
DSA_HASH_ALGORITHM_SHA512
)

type DSAFIPSVERSION_ENUM uint32

const (
DSA_FIPS186_2 DSAFIPSVERSION_ENUM = iota
DSA_FIPS186_3
)

type DSA_PARAMETER_HEADER struct {
Length uint32
Magic KeyBlobMagicNumber
KeySize uint32
Count [4]uint8
Seed [20]uint8
Q [20]uint8
}

type DSA_PARAMETER_HEADER_V2 struct {
Length uint32
Magic KeyBlobMagicNumber
KeySize uint32
HashAlgorithm HASHALGORITHM_ENUM
StandardVersion DSAFIPSVERSION_ENUM
SeedLength uint32
GroupSize uint32
Count [4]uint8
}

type PadMode uint32

const (
Expand Down Expand Up @@ -137,6 +179,14 @@ const (

ECDH_PUBLIC_GENERIC_MAGIC KeyBlobMagicNumber = 0x504B4345
ECDH_PRIVATE_GENERIC_MAGIC KeyBlobMagicNumber = 0x564B4345

DSA_PARAMETERS_MAGIC KeyBlobMagicNumber = 0x4d505344
DSA_PUBLIC_MAGIC KeyBlobMagicNumber = 0x42505344
DSA_PRIVATE_MAGIC KeyBlobMagicNumber = 0x56505344

DSA_PARAMETERS_MAGIC_V2 KeyBlobMagicNumber = 0x324d5044
DSA_PUBLIC_MAGIC_V2 KeyBlobMagicNumber = 0x32425044
DSA_PRIVATE_MAGIC_V2 KeyBlobMagicNumber = 0x32565044
)

type (
Expand Down Expand Up @@ -223,6 +273,26 @@ type ECCKEY_BLOB struct {
KeySize uint32
}

// https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/ns-bcrypt-bcrypt_dsa_key_blob
type DSA_KEY_BLOB struct {
Magic KeyBlobMagicNumber
KeySize uint32
Count [4]uint8
Seed [20]uint8
Q [20]uint8
}

// https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/ns-bcrypt-bcrypt_dsa_key_blob_v2
type DSA_KEY_BLOB_V2 struct {
Magic KeyBlobMagicNumber
KeySize uint32
HashAlgorithm HASHALGORITHM_ENUM
StandardVersion DSAFIPSVERSION_ENUM
SeedLength uint32
GroupSize uint32
Count [4]uint8
}

func Encrypt(hKey KEY_HANDLE, plaintext []byte, pPaddingInfo unsafe.Pointer, pbIV []byte, pbOutput []byte, pcbResult *uint32, dwFlags PadMode) (s error) {
var pInput *byte
if len(plaintext) > 0 {
Expand Down

0 comments on commit 147ed74

Please sign in to comment.