get.pme.sh/pnats@v0.0.0-20240304004023-26bb5a137ed0/server/certstore/certstore_windows.go (about) 1 // Copyright 2022-2023 The NATS Authors 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 // 14 // Adapted, updated, and enhanced from CertToStore, https://github.com/google/certtostore/releases/tag/v1.0.2 15 // Apache License, Version 2.0, Copyright 2017 Google Inc. 16 17 package certstore 18 19 import ( 20 "bytes" 21 "crypto" 22 "crypto/ecdsa" 23 "crypto/elliptic" 24 "crypto/rsa" 25 "crypto/tls" 26 "crypto/x509" 27 "encoding/binary" 28 "fmt" 29 "io" 30 "math/big" 31 "reflect" 32 "sync" 33 "syscall" 34 "unicode/utf16" 35 "unsafe" 36 37 "golang.org/x/crypto/cryptobyte" 38 "golang.org/x/crypto/cryptobyte/asn1" 39 "golang.org/x/sys/windows" 40 ) 41 42 const ( 43 // wincrypt.h constants 44 winAcquireCached = 0x1 // CRYPT_ACQUIRE_CACHE_FLAG 45 winAcquireSilent = 0x40 // CRYPT_ACQUIRE_SILENT_FLAG 46 winAcquireOnlyNCryptKey = 0x40000 // CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG 47 winEncodingX509ASN = 1 // X509_ASN_ENCODING 48 winEncodingPKCS7 = 65536 // PKCS_7_ASN_ENCODING 49 winCertStoreProvSystem = 10 // CERT_STORE_PROV_SYSTEM 50 winCertStoreCurrentUser = uint32(winCertStoreCurrentUserID << winCompareShift) // CERT_SYSTEM_STORE_CURRENT_USER 51 winCertStoreLocalMachine = uint32(winCertStoreLocalMachineID << winCompareShift) // CERT_SYSTEM_STORE_LOCAL_MACHINE 52 winCertStoreCurrentUserID = 1 // CERT_SYSTEM_STORE_CURRENT_USER_ID 53 winCertStoreLocalMachineID = 2 // CERT_SYSTEM_STORE_LOCAL_MACHINE_ID 54 winInfoIssuerFlag = 4 // CERT_INFO_ISSUER_FLAG 55 winInfoSubjectFlag = 7 // CERT_INFO_SUBJECT_FLAG 56 winCompareNameStrW = 8 // CERT_COMPARE_NAME_STR_A 57 winCompareShift = 16 // CERT_COMPARE_SHIFT 58 59 // Reference https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certfindcertificateinstore 60 winFindIssuerStr = winCompareNameStrW<<winCompareShift | winInfoIssuerFlag // CERT_FIND_ISSUER_STR_W 61 winFindSubjectStr = winCompareNameStrW<<winCompareShift | winInfoSubjectFlag // CERT_FIND_SUBJECT_STR_W 62 63 winNcryptKeySpec = 0xFFFFFFFF // CERT_NCRYPT_KEY_SPEC 64 65 winBCryptPadPKCS1 uintptr = 0x2 66 winBCryptPadPSS uintptr = 0x8 // Modern TLS 1.2+ 67 winBCryptPadPSSSalt uint32 = 32 // default 20, 32 optimal for typical SHA256 hash 68 69 winRSA1Magic = 0x31415352 // "RSA1" BCRYPT_RSAPUBLIC_MAGIC 70 71 winECS1Magic = 0x31534345 // "ECS1" BCRYPT_ECDSA_PUBLIC_P256_MAGIC 72 winECS3Magic = 0x33534345 // "ECS3" BCRYPT_ECDSA_PUBLIC_P384_MAGIC 73 winECS5Magic = 0x35534345 // "ECS5" BCRYPT_ECDSA_PUBLIC_P521_MAGIC 74 75 winECK1Magic = 0x314B4345 // "ECK1" BCRYPT_ECDH_PUBLIC_P256_MAGIC 76 winECK3Magic = 0x334B4345 // "ECK3" BCRYPT_ECDH_PUBLIC_P384_MAGIC 77 winECK5Magic = 0x354B4345 // "ECK5" BCRYPT_ECDH_PUBLIC_P521_MAGIC 78 79 winCryptENotFound = 0x80092004 // CRYPT_E_NOT_FOUND 80 81 providerMSSoftware = "Microsoft Software Key Storage Provider" 82 ) 83 84 var ( 85 winBCryptRSAPublicBlob = winWide("RSAPUBLICBLOB") 86 winBCryptECCPublicBlob = winWide("ECCPUBLICBLOB") 87 88 winNCryptAlgorithmGroupProperty = winWide("Algorithm Group") // NCRYPT_ALGORITHM_GROUP_PROPERTY 89 winNCryptUniqueNameProperty = winWide("Unique Name") // NCRYPT_UNIQUE_NAME_PROPERTY 90 winNCryptECCCurveNameProperty = winWide("ECCCurveName") // NCRYPT_ECC_CURVE_NAME_PROPERTY 91 92 winCurveIDs = map[uint32]elliptic.Curve{ 93 winECS1Magic: elliptic.P256(), // BCRYPT_ECDSA_PUBLIC_P256_MAGIC 94 winECS3Magic: elliptic.P384(), // BCRYPT_ECDSA_PUBLIC_P384_MAGIC 95 winECS5Magic: elliptic.P521(), // BCRYPT_ECDSA_PUBLIC_P521_MAGIC 96 winECK1Magic: elliptic.P256(), // BCRYPT_ECDH_PUBLIC_P256_MAGIC 97 winECK3Magic: elliptic.P384(), // BCRYPT_ECDH_PUBLIC_P384_MAGIC 98 winECK5Magic: elliptic.P521(), // BCRYPT_ECDH_PUBLIC_P521_MAGIC 99 } 100 101 winCurveNames = map[string]elliptic.Curve{ 102 "nistP256": elliptic.P256(), // BCRYPT_ECC_CURVE_NISTP256 103 "nistP384": elliptic.P384(), // BCRYPT_ECC_CURVE_NISTP384 104 "nistP521": elliptic.P521(), // BCRYPT_ECC_CURVE_NISTP521 105 } 106 107 winAlgIDs = map[crypto.Hash]*uint16{ 108 crypto.SHA1: winWide("SHA1"), // BCRYPT_SHA1_ALGORITHM 109 crypto.SHA256: winWide("SHA256"), // BCRYPT_SHA256_ALGORITHM 110 crypto.SHA384: winWide("SHA384"), // BCRYPT_SHA384_ALGORITHM 111 crypto.SHA512: winWide("SHA512"), // BCRYPT_SHA512_ALGORITHM 112 } 113 114 // MY is well-known system store on Windows that holds personal certificates 115 winMyStore = winWide("MY") 116 117 // These DLLs must be available on all Windows hosts 118 winCrypt32 = windows.MustLoadDLL("crypt32.dll") 119 winNCrypt = windows.MustLoadDLL("ncrypt.dll") 120 121 winCertFindCertificateInStore = winCrypt32.MustFindProc("CertFindCertificateInStore") 122 winCryptAcquireCertificatePrivateKey = winCrypt32.MustFindProc("CryptAcquireCertificatePrivateKey") 123 winNCryptExportKey = winNCrypt.MustFindProc("NCryptExportKey") 124 winNCryptOpenStorageProvider = winNCrypt.MustFindProc("NCryptOpenStorageProvider") 125 winNCryptGetProperty = winNCrypt.MustFindProc("NCryptGetProperty") 126 winNCryptSignHash = winNCrypt.MustFindProc("NCryptSignHash") 127 128 winFnGetProperty = winGetProperty 129 ) 130 131 type winPKCS1PaddingInfo struct { 132 pszAlgID *uint16 133 } 134 135 type winPSSPaddingInfo struct { 136 pszAlgID *uint16 137 cbSalt uint32 138 } 139 140 // TLSConfig fulfills the same function as reading cert and key pair from pem files but 141 // sources the Windows certificate store instead 142 func TLSConfig(certStore StoreType, certMatchBy MatchByType, certMatch string, config *tls.Config) error { 143 var ( 144 leaf *x509.Certificate 145 leafCtx *windows.CertContext 146 pk *winKey 147 vOpts = x509.VerifyOptions{} 148 chains [][]*x509.Certificate 149 chain []*x509.Certificate 150 rawChain [][]byte 151 ) 152 153 // By StoreType, open a store 154 if certStore == windowsCurrentUser || certStore == windowsLocalMachine { 155 var scope uint32 156 cs, err := winOpenCertStore(providerMSSoftware) 157 if err != nil || cs == nil { 158 return err 159 } 160 if certStore == windowsCurrentUser { 161 scope = winCertStoreCurrentUser 162 } 163 if certStore == windowsLocalMachine { 164 scope = winCertStoreLocalMachine 165 } 166 167 // certByIssuer or certBySubject 168 if certMatchBy == matchBySubject || certMatchBy == MATCHBYEMPTY { 169 leaf, leafCtx, err = cs.certBySubject(certMatch, scope) 170 } else if certMatchBy == matchByIssuer { 171 leaf, leafCtx, err = cs.certByIssuer(certMatch, scope) 172 } else { 173 return ErrBadMatchByType 174 } 175 if err != nil { 176 // pass through error from cert search 177 return err 178 } 179 if leaf == nil || leafCtx == nil { 180 return ErrFailedCertSearch 181 } 182 pk, err = cs.certKey(leafCtx) 183 if err != nil { 184 return err 185 } 186 if pk == nil { 187 return ErrNoPrivateKeyStoreRef 188 } 189 } else { 190 return ErrBadCertStore 191 } 192 193 // Get intermediates in the cert store for the found leaf IFF there is a full chain of trust in the store 194 // otherwise just use leaf as the final chain. 195 // 196 // Using std lib Verify as a reliable way to get valid chains out of the win store for the leaf; however, 197 // using empty options since server TLS stanza could be TLS role as server identity or client identity. 198 chains, err := leaf.Verify(vOpts) 199 if err != nil || len(chains) == 0 { 200 chains = append(chains, []*x509.Certificate{leaf}) 201 } 202 203 // We have at least one verified chain so pop the first chain and remove the self-signed CA cert (if present) 204 // from the end of the chain 205 chain = chains[0] 206 if len(chain) > 1 { 207 chain = chain[:len(chain)-1] 208 } 209 210 // For tls.Certificate.Certificate need a [][]byte from []*x509.Certificate 211 // Approximate capacity for efficiency 212 rawChain = make([][]byte, 0, len(chain)) 213 for _, link := range chain { 214 rawChain = append(rawChain, link.Raw) 215 } 216 217 tlsCert := tls.Certificate{ 218 Certificate: rawChain, 219 PrivateKey: pk, 220 Leaf: leaf, 221 } 222 config.Certificates = []tls.Certificate{tlsCert} 223 224 // note: pk is a windows pointer (not freed by Go) but needs to live the life of the server for Signing. 225 // The cert context (leafCtx) windows pointer must not be freed underneath the pk so also life of the server. 226 return nil 227 } 228 229 // winWide returns a pointer to uint16 representing the equivalent 230 // to a Windows LPCWSTR. 231 func winWide(s string) *uint16 { 232 w := utf16.Encode([]rune(s)) 233 w = append(w, 0) 234 return &w[0] 235 } 236 237 // winOpenProvider gets a provider handle for subsequent calls 238 func winOpenProvider(provider string) (uintptr, error) { 239 var hProv uintptr 240 pname := winWide(provider) 241 // Open the provider, the last parameter is not used 242 r, _, err := winNCryptOpenStorageProvider.Call(uintptr(unsafe.Pointer(&hProv)), uintptr(unsafe.Pointer(pname)), 0) 243 if r == 0 { 244 return hProv, nil 245 } 246 return hProv, fmt.Errorf("NCryptOpenStorageProvider returned %X: %v", r, err) 247 } 248 249 // winFindCert wraps the CertFindCertificateInStore library call. Note that any cert context passed 250 // into prev will be freed. If no certificate was found, nil will be returned. 251 func winFindCert(store windows.Handle, enc, findFlags, findType uint32, para *uint16, prev *windows.CertContext) (*windows.CertContext, error) { 252 h, _, err := winCertFindCertificateInStore.Call( 253 uintptr(store), 254 uintptr(enc), 255 uintptr(findFlags), 256 uintptr(findType), 257 uintptr(unsafe.Pointer(para)), 258 uintptr(unsafe.Pointer(prev)), 259 ) 260 if h == 0 { 261 // Actual error, or simply not found? 262 if errno, ok := err.(syscall.Errno); ok && errno == winCryptENotFound { 263 return nil, ErrFailedCertSearch 264 } 265 return nil, ErrFailedCertSearch 266 } 267 // nolint:govet 268 return (*windows.CertContext)(unsafe.Pointer(h)), nil 269 } 270 271 // winCertStore is a store implementation for the Windows Certificate Store 272 type winCertStore struct { 273 Prov uintptr 274 ProvName string 275 stores map[string]*winStoreHandle 276 mu sync.Mutex 277 } 278 279 // winOpenCertStore creates a winCertStore 280 func winOpenCertStore(provider string) (*winCertStore, error) { 281 cngProv, err := winOpenProvider(provider) 282 if err != nil { 283 // pass through error from winOpenProvider 284 return nil, err 285 } 286 287 wcs := &winCertStore{ 288 Prov: cngProv, 289 ProvName: provider, 290 stores: make(map[string]*winStoreHandle), 291 } 292 293 return wcs, nil 294 } 295 296 // winCertContextToX509 creates an x509.Certificate from a Windows cert context. 297 func winCertContextToX509(ctx *windows.CertContext) (*x509.Certificate, error) { 298 var der []byte 299 slice := (*reflect.SliceHeader)(unsafe.Pointer(&der)) 300 slice.Data = uintptr(unsafe.Pointer(ctx.EncodedCert)) 301 slice.Len = int(ctx.Length) 302 slice.Cap = int(ctx.Length) 303 return x509.ParseCertificate(der) 304 } 305 306 // certByIssuer matches and returns the first certificate found by passed issuer. 307 // CertContext pointer returned allows subsequent key operations like Sign. Caller specifies 308 // current user's personal certs or local machine's personal certs using storeType. 309 // See CERT_FIND_ISSUER_STR description at https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certfindcertificateinstore 310 func (w *winCertStore) certByIssuer(issuer string, storeType uint32) (*x509.Certificate, *windows.CertContext, error) { 311 return w.certSearch(winFindIssuerStr, issuer, winMyStore, storeType) 312 } 313 314 // certBySubject matches and returns the first certificate found by passed subject field. 315 // CertContext pointer returned allows subsequent key operations like Sign. Caller specifies 316 // current user's personal certs or local machine's personal certs using storeType. 317 // See CERT_FIND_SUBJECT_STR description at https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-certfindcertificateinstore 318 func (w *winCertStore) certBySubject(subject string, storeType uint32) (*x509.Certificate, *windows.CertContext, error) { 319 return w.certSearch(winFindSubjectStr, subject, winMyStore, storeType) 320 } 321 322 // certSearch is a helper function to lookup certificates based on search type and match value. 323 // store is used to specify which store to perform the lookup in (system or user). 324 func (w *winCertStore) certSearch(searchType uint32, matchValue string, searchRoot *uint16, store uint32) (*x509.Certificate, *windows.CertContext, error) { 325 // store handle to "MY" store 326 h, err := w.storeHandle(store, searchRoot) 327 if err != nil { 328 return nil, nil, err 329 } 330 331 var prev *windows.CertContext 332 var cert *x509.Certificate 333 334 i, err := windows.UTF16PtrFromString(matchValue) 335 if err != nil { 336 return nil, nil, ErrFailedCertSearch 337 } 338 339 // pass 0 as the third parameter because it is not used 340 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa376064(v=vs.85).aspx 341 nc, err := winFindCert(h, winEncodingX509ASN|winEncodingPKCS7, 0, searchType, i, prev) 342 if err != nil { 343 return nil, nil, err 344 } 345 if nc != nil { 346 // certificate found 347 prev = nc 348 349 // Extract the DER-encoded certificate from the cert context 350 xc, err := winCertContextToX509(nc) 351 if err == nil { 352 cert = xc 353 } else { 354 return nil, nil, ErrFailedX509Extract 355 } 356 } else { 357 return nil, nil, ErrFailedCertSearch 358 } 359 360 if cert == nil { 361 return nil, nil, ErrFailedX509Extract 362 } 363 364 return cert, prev, nil 365 } 366 367 type winStoreHandle struct { 368 handle *windows.Handle 369 } 370 371 func winNewStoreHandle(provider uint32, store *uint16) (*winStoreHandle, error) { 372 var s winStoreHandle 373 if s.handle != nil { 374 return &s, nil 375 } 376 st, err := windows.CertOpenStore( 377 winCertStoreProvSystem, 378 0, 379 0, 380 provider, 381 uintptr(unsafe.Pointer(store))) 382 if err != nil { 383 return nil, ErrBadCryptoStoreProvider 384 } 385 s.handle = &st 386 return &s, nil 387 } 388 389 // winKey implements crypto.Signer and crypto.Decrypter for key based operations. 390 type winKey struct { 391 handle uintptr 392 pub crypto.PublicKey 393 Container string 394 AlgorithmGroup string 395 } 396 397 // Public exports a public key to implement crypto.Signer 398 func (k winKey) Public() crypto.PublicKey { 399 return k.pub 400 } 401 402 // Sign returns the signature of a hash to implement crypto.Signer 403 func (k winKey) Sign(_ io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { 404 switch k.AlgorithmGroup { 405 case "ECDSA", "ECDH": 406 return winSignECDSA(k.handle, digest) 407 case "RSA": 408 hf := opts.HashFunc() 409 algID, ok := winAlgIDs[hf] 410 if !ok { 411 return nil, ErrBadRSAHashAlgorithm 412 } 413 switch opts.(type) { 414 case *rsa.PSSOptions: 415 return winSignRSAPSSPadding(k.handle, digest, algID) 416 default: 417 return winSignRSAPKCS1Padding(k.handle, digest, algID) 418 } 419 default: 420 return nil, ErrBadSigningAlgorithm 421 } 422 } 423 424 func winSignECDSA(kh uintptr, digest []byte) ([]byte, error) { 425 var size uint32 426 // Obtain the size of the signature 427 r, _, _ := winNCryptSignHash.Call( 428 kh, 429 0, 430 uintptr(unsafe.Pointer(&digest[0])), 431 uintptr(len(digest)), 432 0, 433 0, 434 uintptr(unsafe.Pointer(&size)), 435 0) 436 if r != 0 { 437 return nil, ErrStoreECDSASigningError 438 } 439 440 // Obtain the signature data 441 buf := make([]byte, size) 442 r, _, _ = winNCryptSignHash.Call( 443 kh, 444 0, 445 uintptr(unsafe.Pointer(&digest[0])), 446 uintptr(len(digest)), 447 uintptr(unsafe.Pointer(&buf[0])), 448 uintptr(size), 449 uintptr(unsafe.Pointer(&size)), 450 0) 451 if r != 0 { 452 return nil, ErrStoreECDSASigningError 453 } 454 if len(buf) != int(size) { 455 return nil, ErrStoreECDSASigningError 456 } 457 458 return winPackECDSASigValue(bytes.NewReader(buf[:size]), len(digest)) 459 } 460 461 func winPackECDSASigValue(r io.Reader, digestLength int) ([]byte, error) { 462 sigR := make([]byte, digestLength) 463 if _, err := io.ReadFull(r, sigR); err != nil { 464 return nil, ErrStoreECDSASigningError 465 } 466 467 sigS := make([]byte, digestLength) 468 if _, err := io.ReadFull(r, sigS); err != nil { 469 return nil, ErrStoreECDSASigningError 470 } 471 472 var b cryptobyte.Builder 473 b.AddASN1(asn1.SEQUENCE, func(b *cryptobyte.Builder) { 474 b.AddASN1BigInt(new(big.Int).SetBytes(sigR)) 475 b.AddASN1BigInt(new(big.Int).SetBytes(sigS)) 476 }) 477 return b.Bytes() 478 } 479 480 func winSignRSAPKCS1Padding(kh uintptr, digest []byte, algID *uint16) ([]byte, error) { 481 // PKCS#1 v1.5 padding for some TLS 1.2 482 padInfo := winPKCS1PaddingInfo{pszAlgID: algID} 483 var size uint32 484 // Obtain the size of the signature 485 r, _, _ := winNCryptSignHash.Call( 486 kh, 487 uintptr(unsafe.Pointer(&padInfo)), 488 uintptr(unsafe.Pointer(&digest[0])), 489 uintptr(len(digest)), 490 0, 491 0, 492 uintptr(unsafe.Pointer(&size)), 493 winBCryptPadPKCS1) 494 if r != 0 { 495 return nil, ErrStoreRSASigningError 496 } 497 498 // Obtain the signature data 499 sig := make([]byte, size) 500 r, _, _ = winNCryptSignHash.Call( 501 kh, 502 uintptr(unsafe.Pointer(&padInfo)), 503 uintptr(unsafe.Pointer(&digest[0])), 504 uintptr(len(digest)), 505 uintptr(unsafe.Pointer(&sig[0])), 506 uintptr(size), 507 uintptr(unsafe.Pointer(&size)), 508 winBCryptPadPKCS1) 509 if r != 0 { 510 return nil, ErrStoreRSASigningError 511 } 512 513 return sig[:size], nil 514 } 515 516 func winSignRSAPSSPadding(kh uintptr, digest []byte, algID *uint16) ([]byte, error) { 517 // PSS padding for TLS 1.3 and some TLS 1.2 518 padInfo := winPSSPaddingInfo{pszAlgID: algID, cbSalt: winBCryptPadPSSSalt} 519 520 var size uint32 521 // Obtain the size of the signature 522 r, _, _ := winNCryptSignHash.Call( 523 kh, 524 uintptr(unsafe.Pointer(&padInfo)), 525 uintptr(unsafe.Pointer(&digest[0])), 526 uintptr(len(digest)), 527 0, 528 0, 529 uintptr(unsafe.Pointer(&size)), 530 winBCryptPadPSS) 531 if r != 0 { 532 return nil, ErrStoreRSASigningError 533 } 534 535 // Obtain the signature data 536 sig := make([]byte, size) 537 r, _, _ = winNCryptSignHash.Call( 538 kh, 539 uintptr(unsafe.Pointer(&padInfo)), 540 uintptr(unsafe.Pointer(&digest[0])), 541 uintptr(len(digest)), 542 uintptr(unsafe.Pointer(&sig[0])), 543 uintptr(size), 544 uintptr(unsafe.Pointer(&size)), 545 winBCryptPadPSS) 546 if r != 0 { 547 return nil, ErrStoreRSASigningError 548 } 549 550 return sig[:size], nil 551 } 552 553 // certKey wraps CryptAcquireCertificatePrivateKey. It obtains the CNG private 554 // key of a known certificate and returns a pointer to a winKey which implements 555 // both crypto.Signer. When a nil cert context is passed 556 // a nil key is intentionally returned, to model the expected behavior of a 557 // non-existent cert having no private key. 558 // https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptacquirecertificateprivatekey 559 func (w *winCertStore) certKey(cert *windows.CertContext) (*winKey, error) { 560 // Return early if a nil cert was passed. 561 if cert == nil { 562 return nil, nil 563 } 564 var ( 565 kh uintptr 566 spec uint32 567 mustFree int 568 ) 569 r, _, _ := winCryptAcquireCertificatePrivateKey.Call( 570 uintptr(unsafe.Pointer(cert)), 571 winAcquireCached|winAcquireSilent|winAcquireOnlyNCryptKey, 572 0, // Reserved, must be null. 573 uintptr(unsafe.Pointer(&kh)), 574 uintptr(unsafe.Pointer(&spec)), 575 uintptr(unsafe.Pointer(&mustFree)), 576 ) 577 // If the function succeeds, the return value is nonzero (TRUE). 578 if r == 0 { 579 return nil, ErrNoPrivateKeyStoreRef 580 } 581 if mustFree != 0 { 582 return nil, ErrNoPrivateKeyStoreRef 583 } 584 if spec != winNcryptKeySpec { 585 return nil, ErrNoPrivateKeyStoreRef 586 } 587 588 return winKeyMetadata(kh) 589 } 590 591 func winKeyMetadata(kh uintptr) (*winKey, error) { 592 // uc is used to populate the unique container name attribute of the private key 593 uc, err := winGetPropertyStr(kh, winNCryptUniqueNameProperty) 594 if err != nil { 595 // unable to determine key unique name 596 return nil, ErrExtractingPrivateKeyMetadata 597 } 598 599 alg, err := winGetPropertyStr(kh, winNCryptAlgorithmGroupProperty) 600 if err != nil { 601 // unable to determine key algorithm 602 return nil, ErrExtractingPrivateKeyMetadata 603 } 604 605 var pub crypto.PublicKey 606 607 switch alg { 608 case "ECDSA", "ECDH": 609 buf, err := winExport(kh, winBCryptECCPublicBlob) 610 if err != nil { 611 // failed to export ECC public key 612 return nil, ErrExtractingECCPublicKey 613 } 614 pub, err = unmarshalECC(buf, kh) 615 if err != nil { 616 return nil, ErrExtractingECCPublicKey 617 } 618 case "RSA": 619 buf, err := winExport(kh, winBCryptRSAPublicBlob) 620 if err != nil { 621 return nil, ErrExtractingRSAPublicKey 622 } 623 pub, err = winUnmarshalRSA(buf) 624 if err != nil { 625 return nil, ErrExtractingRSAPublicKey 626 } 627 default: 628 return nil, ErrBadPublicKeyAlgorithm 629 } 630 631 return &winKey{handle: kh, pub: pub, Container: uc, AlgorithmGroup: alg}, nil 632 } 633 634 func winGetProperty(kh uintptr, property *uint16) ([]byte, error) { 635 var strSize uint32 636 r, _, _ := winNCryptGetProperty.Call( 637 kh, 638 uintptr(unsafe.Pointer(property)), 639 0, 640 0, 641 uintptr(unsafe.Pointer(&strSize)), 642 0, 643 0) 644 if r != 0 { 645 return nil, ErrExtractPropertyFromKey 646 } 647 648 buf := make([]byte, strSize) 649 r, _, _ = winNCryptGetProperty.Call( 650 kh, 651 uintptr(unsafe.Pointer(property)), 652 uintptr(unsafe.Pointer(&buf[0])), 653 uintptr(strSize), 654 uintptr(unsafe.Pointer(&strSize)), 655 0, 656 0) 657 if r != 0 { 658 return nil, ErrExtractPropertyFromKey 659 } 660 661 return buf, nil 662 } 663 664 func winGetPropertyStr(kh uintptr, property *uint16) (string, error) { 665 buf, err := winFnGetProperty(kh, property) 666 if err != nil { 667 return "", ErrExtractPropertyFromKey 668 } 669 uc := bytes.ReplaceAll(buf, []byte{0x00}, []byte("")) 670 return string(uc), nil 671 } 672 673 func winExport(kh uintptr, blobType *uint16) ([]byte, error) { 674 var size uint32 675 // When obtaining the size of a public key, most parameters are not required 676 r, _, _ := winNCryptExportKey.Call( 677 kh, 678 0, 679 uintptr(unsafe.Pointer(blobType)), 680 0, 681 0, 682 0, 683 uintptr(unsafe.Pointer(&size)), 684 0) 685 if r != 0 { 686 return nil, ErrExtractingPublicKey 687 } 688 689 // Place the exported key in buf now that we know the size required 690 buf := make([]byte, size) 691 r, _, _ = winNCryptExportKey.Call( 692 kh, 693 0, 694 uintptr(unsafe.Pointer(blobType)), 695 0, 696 uintptr(unsafe.Pointer(&buf[0])), 697 uintptr(size), 698 uintptr(unsafe.Pointer(&size)), 699 0) 700 if r != 0 { 701 return nil, ErrExtractingPublicKey 702 } 703 return buf, nil 704 } 705 706 func unmarshalECC(buf []byte, kh uintptr) (*ecdsa.PublicKey, error) { 707 // BCRYPT_ECCKEY_BLOB from bcrypt.h 708 header := struct { 709 Magic uint32 710 Key uint32 711 }{} 712 713 r := bytes.NewReader(buf) 714 if err := binary.Read(r, binary.LittleEndian, &header); err != nil { 715 return nil, ErrExtractingECCPublicKey 716 } 717 718 curve, ok := winCurveIDs[header.Magic] 719 if !ok { 720 // Fix for b/185945636, where despite specifying the curve, nCrypt returns 721 // an incorrect response with BCRYPT_ECDSA_PUBLIC_GENERIC_MAGIC. 722 var err error 723 curve, err = winCurveName(kh) 724 if err != nil { 725 // unsupported header magic or cannot match the curve by name 726 return nil, err 727 } 728 } 729 730 keyX := make([]byte, header.Key) 731 if n, err := r.Read(keyX); n != int(header.Key) || err != nil { 732 // failed to read key X 733 return nil, ErrExtractingECCPublicKey 734 } 735 736 keyY := make([]byte, header.Key) 737 if n, err := r.Read(keyY); n != int(header.Key) || err != nil { 738 // failed to read key Y 739 return nil, ErrExtractingECCPublicKey 740 } 741 742 pub := &ecdsa.PublicKey{ 743 Curve: curve, 744 X: new(big.Int).SetBytes(keyX), 745 Y: new(big.Int).SetBytes(keyY), 746 } 747 return pub, nil 748 } 749 750 // winCurveName reads the curve name property and returns the corresponding curve. 751 func winCurveName(kh uintptr) (elliptic.Curve, error) { 752 cn, err := winGetPropertyStr(kh, winNCryptECCCurveNameProperty) 753 if err != nil { 754 // unable to determine the curve property name 755 return nil, ErrExtractPropertyFromKey 756 } 757 curve, ok := winCurveNames[cn] 758 if !ok { 759 // unknown curve name 760 return nil, ErrBadECCCurveName 761 } 762 return curve, nil 763 } 764 765 func winUnmarshalRSA(buf []byte) (*rsa.PublicKey, error) { 766 // BCRYPT_RSA_BLOB from bcrypt.h 767 header := struct { 768 Magic uint32 769 BitLength uint32 770 PublicExpSize uint32 771 ModulusSize uint32 772 UnusedPrime1 uint32 773 UnusedPrime2 uint32 774 }{} 775 776 r := bytes.NewReader(buf) 777 if err := binary.Read(r, binary.LittleEndian, &header); err != nil { 778 return nil, ErrExtractingRSAPublicKey 779 } 780 781 if header.Magic != winRSA1Magic { 782 // invalid header magic 783 return nil, ErrExtractingRSAPublicKey 784 } 785 786 if header.PublicExpSize > 8 { 787 // unsupported public exponent size 788 return nil, ErrExtractingRSAPublicKey 789 } 790 791 exp := make([]byte, 8) 792 if n, err := r.Read(exp[8-header.PublicExpSize:]); n != int(header.PublicExpSize) || err != nil { 793 // failed to read public exponent 794 return nil, ErrExtractingRSAPublicKey 795 } 796 797 mod := make([]byte, header.ModulusSize) 798 if n, err := r.Read(mod); n != int(header.ModulusSize) || err != nil { 799 // failed to read modulus 800 return nil, ErrExtractingRSAPublicKey 801 } 802 803 pub := &rsa.PublicKey{ 804 N: new(big.Int).SetBytes(mod), 805 E: int(binary.BigEndian.Uint64(exp)), 806 } 807 return pub, nil 808 } 809 810 // storeHandle returns a handle to a given cert store, opening the handle as needed. 811 func (w *winCertStore) storeHandle(provider uint32, store *uint16) (windows.Handle, error) { 812 w.mu.Lock() 813 defer w.mu.Unlock() 814 815 key := fmt.Sprintf("%d%s", provider, windows.UTF16PtrToString(store)) 816 var err error 817 if w.stores[key] == nil { 818 w.stores[key], err = winNewStoreHandle(provider, store) 819 if err != nil { 820 return 0, ErrBadCryptoStoreProvider 821 } 822 } 823 return *w.stores[key].handle, nil 824 } 825 826 // Verify interface conformance. 827 var _ credential = &winKey{}