Skip to content

Commit

Permalink
Merge pull request #62 from unreality/pin-timeout
Browse files Browse the repository at this point in the history
Add ability to remove PIN cache
  • Loading branch information
buptczq authored Mar 19, 2024
2 parents 1b46901 + 53431b7 commit 1e526e8
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 4 deletions.
108 changes: 104 additions & 4 deletions capi/wincapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,33 @@ const (
ALG_ECDSA_SHA256 = "1.2.840.10045.4.3.2"
ALG_ECDSA_SHA384 = "1.2.840.10045.4.3.3"
ALG_ECDSA_SHA512 = "1.2.840.10045.4.3.4"

NCRYPT_PIN_PROPERTY = "SmartCardPin"
)

const (
AT_KEYEXCHANGE = uint32(1)
AT_SIGNATURE = uint32(2)
CERT_NCRYPT_KEY_SPEC = uint32(0xFFFFFFFF)

X509_ASN_ENCODING = 0x1
PKCS_7_ASN_ENCODING = 0x10000
CRYPT_ACQUIRE_CACHE_FLAG = uint32(0x00000001)
CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG = uint32(0x00040000)
)

var (
modcrypt32 = syscall.NewLazyDLL("crypt32.dll")
modncrypt = syscall.NewLazyDLL("ncrypt.dll")
procCryptSignMessage = modcrypt32.NewProc("CryptSignMessage")
procCertDuplicateCertificateContext = modcrypt32.NewProc("CertDuplicateCertificateContext")
procCertGetCertificateContextProperty = modcrypt32.NewProc("CertGetCertificateContextProperty")
procCryptAcquireCertificatePrivateKey = modcrypt32.NewProc("CryptAcquireCertificatePrivateKey")
procNCryptSetProperty = modncrypt.NewProc("NCryptSetProperty")
)

var disablePINCache = true

type cryptoapiBlob struct {
DataSize uint32
Data uintptr
Expand Down Expand Up @@ -104,6 +122,67 @@ func certGetCertificateContextProperty(context *syscall.CertContext, dwPropId ui
return int(r0)
}

// nCryptSetPropertyString sets a string value for a named property for a CNG key storage object.
func nCryptSetPropertyString(hObject uintptr, pszProperty string, pbInput string, dwFlags uint32) (err error) {

pszPropertyPtr, _ := syscall.UTF16PtrFromString(pszProperty)

dataPtr := uintptr(0)
dataSize := uint32(0)
if pbInput != "" {
stringPtr, _ := syscall.UTF16PtrFromString(pbInput)
dataPtr = uintptr(unsafe.Pointer(stringPtr))
dataSize = uint32(len(pbInput))
}
dataSizePtr := uintptr(unsafe.Pointer(&dataSize))

r0, _, e1 := syscall.Syscall6(procNCryptSetProperty.Addr(), 5,
hObject,
uintptr(unsafe.Pointer(pszPropertyPtr)),
dataPtr,
dataSizePtr,
uintptr(dwFlags),
0,
)

if r0 != 0 {
return e1
}

if e1 != syscall.Errno(0) {
return e1
}
return nil
}

// cryptAcquireCertificatePrivateKey obtains the private key for a certificateContext, returning a CNG NCRYPT_KEY_HANDLE
// or a HCRYPTPROV depending on the flags given.
func cryptAcquireCertificatePrivateKey(certContext uintptr, flags uint32) (provContext uintptr, err error) {
pvParameters := uint32(0)
phCryptProvOrNCryptKey := uintptr(0)
pdwKeySpec := 0 // Can be 0, AT_KEYEXCHANGE, AT_SIGNATURE, or CERT_NCRYPT_KEY_SPEC
pfCallerFreeProvOrNCryptKey := false

r0, _, e1 := syscall.Syscall6(procCryptAcquireCertificatePrivateKey.Addr(), 6,
certContext,
uintptr(flags),
uintptr(unsafe.Pointer(&pvParameters)),
uintptr(unsafe.Pointer(&phCryptProvOrNCryptKey)),
uintptr(unsafe.Pointer(&pdwKeySpec)),
uintptr(unsafe.Pointer(&pfCallerFreeProvOrNCryptKey)),
)

if r0 == 0 {
return 0, fmt.Errorf("r0 was 0")
}

if e1 != syscall.Errno(0) {
return 0, e1
}

return phCryptProvOrNCryptKey, nil
}

type Certificate struct {
certContext uintptr
*x509.Certificate
Expand Down Expand Up @@ -185,10 +264,18 @@ func LoadUserCerts() ([]*Certificate, error) {
}

func Sign(alg string, cert *Certificate, data []byte) (*pkcs7.PKCS7, error) {
const (
X509_ASN_ENCODING = 0x1
PKCS_7_ASN_ENCODING = 0x10000
)
var nCryptHandle uintptr

if disablePINCache {
var err error
// Acquire a handle for the private key attached to this certificate
acquireFlags := uint32(CRYPT_ACQUIRE_CACHE_FLAG | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG)
nCryptHandle, err = cryptAcquireCertificatePrivateKey(cert.certContext, acquireFlags)
if err != nil {
return nil, err
}
}

algptr, err := syscall.BytePtrFromString(alg)
if err != nil {
return nil, err
Expand All @@ -203,5 +290,18 @@ func Sign(alg string, cert *Certificate, data []byte) (*pkcs7.PKCS7, error) {
if err != nil {
return nil, err
}

if disablePINCache && nCryptHandle != 0 {
// Set the PIN to NULL so we are prompted again
err = nCryptSetPropertyString(nCryptHandle, NCRYPT_PIN_PROPERTY, "", 0)
if err != nil {
return nil, fmt.Errorf("Could not set NCRYPT_PIN_PROPERTY: %v\n", err)
}
}

return pkcs7.Parse(sign)
}

func SetDisablePINCache(b bool) {
disablePINCache = b
}
4 changes: 4 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package main
import (
"context"
"flag"
"github.com/buptczq/WinCryptSSHAgent/capi"
"os"
"os/signal"
"path/filepath"
Expand Down Expand Up @@ -36,6 +37,7 @@ var applications = []app.Application{

var installHVService = flag.Bool("i", false, "Install Hyper-V Guest Communication Services")
var disableCapi = flag.Bool("disable-capi", false, "Disable Windows Crypto API")
var disablePINCache = flag.Bool("disable-pin-cache", false, "Clear the Smart Card PIN Cache after each operation")

func installService() {
if !utils.IsAdmin() {
Expand Down Expand Up @@ -122,6 +124,8 @@ func main() {
// context
ctx, cancel := context.WithCancel(context.Background())

capi.SetDisablePINCache(*disablePINCache)

// agent
var ag agent.Agent
if hvClient {
Expand Down

0 comments on commit 1e526e8

Please sign in to comment.