Skip to content

Commit

Permalink
implement DSA sign and verify
Browse files Browse the repository at this point in the history
  • Loading branch information
qmuntal committed Oct 23, 2023
1 parent c4ea81d commit 0ed3c83
Show file tree
Hide file tree
Showing 3 changed files with 335 additions and 43 deletions.
252 changes: 216 additions & 36 deletions cng/das.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,36 +37,69 @@ type DSAParameters struct {
// 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) {
func GenerateDSAParameters(L int) (params DSAParameters, X, Y BigInt, err error) {
h, err := loadDSA()
if err != nil {
return DSAParameters{}, err
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{}, err
return DSAParameters{}, nil, nil, err
}
defer bcrypt.DestroyKey(hkey)

if err := bcrypt.FinalizeKeyPair(hkey, 0); err != nil {
return DSAParameters{}, err
return DSAParameters{}, nil, nil, err
}
return getDSAParameters(hkey, L)
return decodeDSAKey(hkey, L, true)
}

// DSAPrivateKey represents a DSA private key.
type DSAPrivateKey struct {
// PrivateKeyDSA represents a DSA private key.
type PrivateKeyDSA struct {
hkey bcrypt.KEY_HANDLE
}

func (k *DSAPrivateKey) finalize() {
func (k *PrivateKeyDSA) finalize() {
bcrypt.DestroyKey(k.hkey)
}

// GenerateDSAKey generates a new private DSA key using the given parameters.
func GenerateDSAKey(params DSAParameters) (*DSAPrivateKey, error) {
// 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
}
sizeBits, err := getUint32(bcrypt.HANDLE(k.hkey), bcrypt.KEY_LENGTH)
if err != nil {
return nil, err
}
params, _, Y, err := decodeDSAKey(k.hkey, int(sizeBits), 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
Expand All @@ -83,50 +116,197 @@ func GenerateDSAKey(params DSAParameters) (*DSAPrivateKey, error) {
bcrypt.DestroyKey(hkey)
return nil, err
}
k := &DSAPrivateKey{hkey}
runtime.SetFinalizer(k, (*DSAPrivateKey).finalize)
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
}

// getDSAParameters returns the DSA parameters for the given key.
func getDSAParameters(hkey bcrypt.KEY_HANDLE, L int) (DSAParameters, error) {
// 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
}
sig := make([]byte, size)
err = bcrypt.SignHash(priv.hkey, nil, hashed, sig, &size, 0)
if err != nil {
return nil, nil, err
}
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)
sig := make([]byte, 0, len(r)+len(s))
sig = append(sig, r...)
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
magic := bcrypt.DSA_PUBLIC_MAGIC
if private {
size += 20
magic = bcrypt.DSA_PRIVATE_MAGIC
}
blob = make([]byte, size)
hdr := bcrypt.DSA_KEY_BLOB{
Magic: 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, (*(*[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
magic := bcrypt.DSA_PUBLIC_MAGIC_V2
if private {
size += groupSize
magic = bcrypt.DSA_PRIVATE_MAGIC_V2
}
blob = make([]byte, size)
hdr := bcrypt.DSA_KEY_BLOB_V2{
Magic: magic,
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 must be equal to groupSize.
Count: [4]byte{0xff, 0xff, 0xff, 0xff}, // disables verification, we don't have the seed.
}
copy(blob, (*(*[sizeOfDSAV2BlobHeader]byte)(unsafe.Pointer(&hdr)))[:])
idx = int(sizeOfDSAV2BlobHeader + groupSize) // skip the seed
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, L int, 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 err error
if L <= 1024 {
var hdr bcrypt.DSA_KEY_BLOB
hdr, data, err = exporDSAKey(hkey, false)
hdr, data, err = exporDSAKey(hkey, private)
if err != nil {
return DSAParameters{}, err
return
}
magic := bcrypt.DSA_PUBLIC_MAGIC
if private {
magic = bcrypt.DSA_PRIVATE_MAGIC
}
if hdr.Magic != bcrypt.DSA_PUBLIC_MAGIC || hdr.KeySize*8 != uint32(L) {
return DSAParameters{}, errors.New("crypto/dsa: exported key is corrupted")
if hdr.Magic != magic || hdr.KeySize*8 != uint32(L) {
err = errors.New("crypto/dsa: exported key is corrupted")
return
}
return DSAParameters{
params = DSAParameters{
Q: hdr.Q[:],
P: consumeBigInt(hdr.KeySize),
G: consumeBigInt(hdr.KeySize),
}, nil
}
Y = consumeBigInt(hdr.KeySize)
if private {
X = consumeBigInt(20) // 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)
}
}
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
return params, X, Y, nil
}

// setDSAParameter sets the DSA parameters for the given key.
Expand Down
Loading

0 comments on commit 0ed3c83

Please sign in to comment.