Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support TLS PRF #44

Merged
merged 3 commits into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions cng/tls1prf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//go:build windows
// +build windows

package cng

import (
"errors"
"hash"
"unsafe"

"github.com/microsoft/go-crypto-winnative/internal/bcrypt"
)

func loadTLS1PRF(id string) (bcrypt.ALG_HANDLE, error) {
h, err := loadOrStoreAlg(id, 0, "", func(h bcrypt.ALG_HANDLE) (interface{}, error) {
return h, nil
})
if err != nil {
return 0, err
}
return h.(bcrypt.ALG_HANDLE), nil
}

func TLS1PRF(secret, label, seed []byte, keyLen int, h func() hash.Hash) ([]byte, error) {
// TLS 1.0/1.1 PRF uses MD5SHA1.
algID := bcrypt.TLS1_1_KDF_ALGORITHM
var hashID string
if h != nil {
// If h is specified, assume the caller wants to use TLS 1.2 PRF.
// TLS 1.0/1.1 PRF doesn't allow specifying the hash function.
if hashID = hashToID(h()); hashID == "" {
return nil, errors.New("cng: unsupported hash function")
}
algID = bcrypt.TLS1_2_KDF_ALGORITHM
}

alg, err := loadTLS1PRF(algID)
if err != nil {
return nil, err
}
var kh bcrypt.KEY_HANDLE
if err := bcrypt.GenerateSymmetricKey(alg, &kh, nil, secret, 0); err != nil {
return nil, err
}

buffers := make([]bcrypt.Buffer, 0, 3)
if len(label) > 0 {
buffers = append(buffers, bcrypt.Buffer{
Type: bcrypt.KDF_TLS_PRF_LABEL,
Data: uintptr(unsafe.Pointer(&label[0])),
Length: uint32(len(label)),
})
}
if len(seed) > 0 {
buffers = append(buffers, bcrypt.Buffer{
Type: bcrypt.KDF_TLS_PRF_SEED,
Data: uintptr(unsafe.Pointer(&seed[0])),
Length: uint32(len(seed)),
})
}
if algID == bcrypt.TLS1_2_KDF_ALGORITHM {
u16HashID := utf16FromString(hashID)
buffers = append(buffers, bcrypt.Buffer{
Type: bcrypt.KDF_HASH_ALGORITHM,
Data: uintptr(unsafe.Pointer(&u16HashID[0])),
Length: uint32(len(u16HashID) * 2),
})
}
params := &bcrypt.BufferDesc{
Count: uint32(len(buffers)),
Buffers: &buffers[0],
}
out := make([]byte, keyLen)
var size uint32
err = bcrypt.KeyDerivation(kh, params, out, &size, 0)
if err != nil {
return nil, err
}
return out[:size], nil
}
164 changes: 164 additions & 0 deletions cng/tls1prf_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//go:build windows
// +build windows

package cng_test

import (
"bytes"
"hash"
"testing"

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

type tls1prfTest struct {
hash func() hash.Hash
secret []byte
label []byte
seed []byte
out []byte
}

var tls1prfTests = []tls1prfTest{
// TLS 1.0/1.1 test generated with CNG and cross-validated
// with OpenSSL.
{
nil,
[]byte{
0x9b, 0xbe, 0x43, 0x6b, 0xa9, 0x40, 0xf0, 0x17,
0xb1, 0x76, 0x52, 0x84, 0x9a, 0x71, 0xdb, 0x35,
},
[]byte{
0x74, 0x65, 0x73, 0x74, 0x20, 0x6c, 0x61, 0x62,
0x65, 0x6c},
[]byte{
0xa0, 0xba, 0x9f, 0x93, 0x6c, 0xda, 0x31, 0x18,
0x27, 0xa6, 0xf7, 0x96, 0xff, 0xd5, 0x19, 0x8c,
},
[]byte{
0x66, 0x17, 0x40, 0xe6, 0xf9, 0x8b, 0xc9, 0x01,
},
},
// Tests from https://mailarchive.ietf.org/arch/msg/tls/fzVCzk-z3FShgGJ6DOXqM1ydxms/
{
cng.NewSHA256,
[]byte{
0x9b, 0xbe, 0x43, 0x6b, 0xa9, 0x40, 0xf0, 0x17,
0xb1, 0x76, 0x52, 0x84, 0x9a, 0x71, 0xdb, 0x35,
},
[]byte{
0x74, 0x65, 0x73, 0x74, 0x20, 0x6c, 0x61, 0x62,
0x65, 0x6c},
[]byte{
0xa0, 0xba, 0x9f, 0x93, 0x6c, 0xda, 0x31, 0x18,
0x27, 0xa6, 0xf7, 0x96, 0xff, 0xd5, 0x19, 0x8c,
},
[]byte{
0xe3, 0xf2, 0x29, 0xba, 0x72, 0x7b, 0xe1, 0x7b,
0x8d, 0x12, 0x26, 0x20, 0x55, 0x7c, 0xd4, 0x53,
0xc2, 0xaa, 0xb2, 0x1d, 0x07, 0xc3, 0xd4, 0x95,
0x32, 0x9b, 0x52, 0xd4, 0xe6, 0x1e, 0xdb, 0x5a,
0x6b, 0x30, 0x17, 0x91, 0xe9, 0x0d, 0x35, 0xc9,
0xc9, 0xa4, 0x6b, 0x4e, 0x14, 0xba, 0xf9, 0xaf,
0x0f, 0xa0, 0x22, 0xf7, 0x07, 0x7d, 0xef, 0x17,
0xab, 0xfd, 0x37, 0x97, 0xc0, 0x56, 0x4b, 0xab,
0x4f, 0xbc, 0x91, 0x66, 0x6e, 0x9d, 0xef, 0x9b,
0x97, 0xfc, 0xe3, 0x4f, 0x79, 0x67, 0x89, 0xba,
0xa4, 0x80, 0x82, 0xd1, 0x22, 0xee, 0x42, 0xc5,
0xa7, 0x2e, 0x5a, 0x51, 0x10, 0xff, 0xf7, 0x01,
0x87, 0x34, 0x7b, 0x66,
},
},
{
cng.NewSHA384,
[]byte{
0xb8, 0x0b, 0x73, 0x3d, 0x6c, 0xee, 0xfc, 0xdc,
0x71, 0x56, 0x6e, 0xa4, 0x8e, 0x55, 0x67, 0xdf,
},
[]byte{
0x74, 0x65, 0x73, 0x74, 0x20, 0x6c, 0x61, 0x62,
0x65, 0x6c},
[]byte{
0xcd, 0x66, 0x5c, 0xf6, 0xa8, 0x44, 0x7d, 0xd6,
0xff, 0x8b, 0x27, 0x55, 0x5e, 0xdb, 0x74, 0x65,
},
[]byte{
0x7b, 0x0c, 0x18, 0xe9, 0xce, 0xd4, 0x10, 0xed,
0x18, 0x04, 0xf2, 0xcf, 0xa3, 0x4a, 0x33, 0x6a,
0x1c, 0x14, 0xdf, 0xfb, 0x49, 0x00, 0xbb, 0x5f,
0xd7, 0x94, 0x21, 0x07, 0xe8, 0x1c, 0x83, 0xcd,
0xe9, 0xca, 0x0f, 0xaa, 0x60, 0xbe, 0x9f, 0xe3,
0x4f, 0x82, 0xb1, 0x23, 0x3c, 0x91, 0x46, 0xa0,
0xe5, 0x34, 0xcb, 0x40, 0x0f, 0xed, 0x27, 0x00,
0x88, 0x4f, 0x9d, 0xc2, 0x36, 0xf8, 0x0e, 0xdd,
0x8b, 0xfa, 0x96, 0x11, 0x44, 0xc9, 0xe8, 0xd7,
0x92, 0xec, 0xa7, 0x22, 0xa7, 0xb3, 0x2f, 0xc3,
0xd4, 0x16, 0xd4, 0x73, 0xeb, 0xc2, 0xc5, 0xfd,
0x4a, 0xbf, 0xda, 0xd0, 0x5d, 0x91, 0x84, 0x25,
0x9b, 0x5b, 0xf8, 0xcd, 0x4d, 0x90, 0xfa, 0x0d,
0x31, 0xe2, 0xde, 0xc4, 0x79, 0xe4, 0xf1, 0xa2,
0x60, 0x66, 0xf2, 0xee, 0xa9, 0xa6, 0x92, 0x36,
0xa3, 0xe5, 0x26, 0x55, 0xc9, 0xe9, 0xae, 0xe6,
0x91, 0xc8, 0xf3, 0xa2, 0x68, 0x54, 0x30, 0x8d,
0x5e, 0xaa, 0x3b, 0xe8, 0x5e, 0x09, 0x90, 0x70,
0x3d, 0x73, 0xe5, 0x6f,
},
},
{
cng.NewSHA512,
[]byte{
0xb0, 0x32, 0x35, 0x23, 0xc1, 0x85, 0x35, 0x99,
0x58, 0x4d, 0x88, 0x56, 0x8b, 0xbb, 0x05, 0xeb,
},
[]byte{
0x74, 0x65, 0x73, 0x74, 0x20, 0x6c, 0x61, 0x62,
0x65, 0x6c,
},
[]byte{
0xd4, 0x64, 0x0e, 0x12, 0xe4, 0xbc, 0xdb, 0xfb,
0x43, 0x7f, 0x03, 0xe6, 0xae, 0x41, 0x8e, 0xe5,
},
[]byte{
0x12, 0x61, 0xf5, 0x88, 0xc7, 0x98, 0xc5, 0xc2,
0x01, 0xff, 0x03, 0x6e, 0x7a, 0x9c, 0xb5, 0xed,
0xcd, 0x7f, 0xe3, 0xf9, 0x4c, 0x66, 0x9a, 0x12,
0x2a, 0x46, 0x38, 0xd7, 0xd5, 0x08, 0xb2, 0x83,
0x04, 0x2d, 0xf6, 0x78, 0x98, 0x75, 0xc7, 0x14,
0x7e, 0x90, 0x6d, 0x86, 0x8b, 0xc7, 0x5c, 0x45,
0xe2, 0x0e, 0xb4, 0x0c, 0x1c, 0xf4, 0xa1, 0x71,
0x3b, 0x27, 0x37, 0x1f, 0x68, 0x43, 0x25, 0x92,
0xf7, 0xdc, 0x8e, 0xa8, 0xef, 0x22, 0x3e, 0x12,
0xea, 0x85, 0x07, 0x84, 0x13, 0x11, 0xbf, 0x68,
0x65, 0x3d, 0x0c, 0xfc, 0x40, 0x56, 0xd8, 0x11,
0xf0, 0x25, 0xc4, 0x5d, 0xdf, 0xa6, 0xe6, 0xfe,
0xc7, 0x02, 0xf0, 0x54, 0xb4, 0x09, 0xd6, 0xf2,
0x8d, 0xd0, 0xa3, 0x23, 0x3e, 0x49, 0x8d, 0xa4,
0x1a, 0x3e, 0x75, 0xc5, 0x63, 0x0e, 0xed, 0xbe,
0x22, 0xfe, 0x25, 0x4e, 0x33, 0xa1, 0xb0, 0xe9,
0xf6, 0xb9, 0x82, 0x66, 0x75, 0xbe, 0xc7, 0xd0,
0x1a, 0x84, 0x56, 0x58, 0xdc, 0x9c, 0x39, 0x75,
0x45, 0x40, 0x1d, 0x40, 0xb9, 0xf4, 0x6c, 0x7a,
0x40, 0x0e, 0xe1, 0xb8, 0xf8, 0x1c, 0xa0, 0xa6,
0x0d, 0x1a, 0x39, 0x7a, 0x10, 0x28, 0xbf, 0xf5,
0xd2, 0xef, 0x50, 0x66, 0x12, 0x68, 0x42, 0xfb,
0x8d, 0xa4, 0x19, 0x76, 0x32, 0xbd, 0xb5, 0x4f,
0xf6, 0x63, 0x3f, 0x86, 0xbb, 0xc8, 0x36, 0xe6,
0x40, 0xd4, 0xd8, 0x98,
},
},
}

func TestTLS1PRF(t *testing.T) {
for i, tt := range tls1prfTests {
out, err := cng.TLS1PRF(tt.secret, tt.label, tt.seed, len(tt.out), tt.hash)
if err != nil {
t.Errorf("test %d: error deriving TLS 1.2 PRF: %v.", i, err)
}
if !bytes.Equal(out, tt.out) {
t.Errorf("test %d: incorrect key output: have %v, need %v.", i, out, tt.out)
}
}
}
45 changes: 25 additions & 20 deletions internal/bcrypt/bcrypt_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,25 @@ import (
)

const (
SHA1_ALGORITHM = "SHA1"
SHA256_ALGORITHM = "SHA256"
SHA384_ALGORITHM = "SHA384"
SHA512_ALGORITHM = "SHA512"
SHA3_256_ALGORITHM = "SHA3-256"
SHA3_384_ALGORITHM = "SHA3-384"
SHA3_512_ALGORITHM = "SHA3-512"
AES_ALGORITHM = "AES"
RSA_ALGORITHM = "RSA"
MD4_ALGORITHM = "MD4"
MD5_ALGORITHM = "MD5"
ECDSA_ALGORITHM = "ECDSA"
ECDH_ALGORITHM = "ECDH"
HKDF_ALGORITHM = "HKDF"
PBKDF2_ALGORITHM = "PBKDF2"
DES_ALGORITHM = "DES"
DES3_ALGORITHM = "3DES" // 3DES_ALGORITHM
SHA1_ALGORITHM = "SHA1"
SHA256_ALGORITHM = "SHA256"
SHA384_ALGORITHM = "SHA384"
SHA512_ALGORITHM = "SHA512"
SHA3_256_ALGORITHM = "SHA3-256"
SHA3_384_ALGORITHM = "SHA3-384"
SHA3_512_ALGORITHM = "SHA3-512"
AES_ALGORITHM = "AES"
RSA_ALGORITHM = "RSA"
MD4_ALGORITHM = "MD4"
MD5_ALGORITHM = "MD5"
ECDSA_ALGORITHM = "ECDSA"
ECDH_ALGORITHM = "ECDH"
HKDF_ALGORITHM = "HKDF"
PBKDF2_ALGORITHM = "PBKDF2"
DES_ALGORITHM = "DES"
DES3_ALGORITHM = "3DES" // 3DES_ALGORITHM
TLS1_1_KDF_ALGORITHM = "TLS1_1_KDF"
TLS1_2_KDF_ALGORITHM = "TLS1_2_KDF"
)

const (
Expand Down Expand Up @@ -66,9 +68,12 @@ const (
)

const (
KDF_HASH_ALGORITHM = 0x0
KDF_ITERATION_COUNT = 0x10
KDF_SALT = 0xF
KDF_HASH_ALGORITHM = 0x0
KDF_TLS_PRF_LABEL = 0x4
KDF_TLS_PRF_SEED = 0x5
KDF_TLS_PRF_PROTOCOL = 0x6
KDF_ITERATION_COUNT = 0x10
KDF_SALT = 0xF
)

const (
Expand Down