Initial import

This commit is contained in:
2025-09-04 14:14:02 +02:00
commit ac609a54c2
19 changed files with 1228 additions and 0 deletions

104
keyring_windows.go Normal file
View File

@@ -0,0 +1,104 @@
package secret
import (
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
var (
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
procCredRead = modadvapi32.NewProc("CredReadW")
procCredFree winproc = modadvapi32.NewProc("CredFree")
)
// Interface for syscall.Proc: helps testing
type winproc interface {
Call(a ...uintptr) (r1, r2 uintptr, lastErr error)
}
type keyring struct {
service string
}
func keyringProvider(service string) (Provider, error) {
return keyring{service}, nil
}
func (p keyring) GetSecret(key string) (value []byte, err error) {
return sysCredRead(p.service+":"+key, sysCRED_TYPE_GENERIC)
}
// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credentialw
type sysCREDENTIAL struct {
Flags uint32
Type uint32
TargetName *uint16
Comment *uint16
LastWritten windows.Filetime
CredentialBlobSize uint32
CredentialBlob uintptr
Persist uint32
AttributeCount uint32
Attributes uintptr
TargetAlias *uint16
UserName *uint16
}
// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credentialw
type sysCRED_TYPE uint32
const (
sysCRED_TYPE_GENERIC sysCRED_TYPE = 0x1
sysCRED_TYPE_DOMAIN_PASSWORD sysCRED_TYPE = 0x2
sysCRED_TYPE_DOMAIN_CERTIFICATE sysCRED_TYPE = 0x3
sysCRED_TYPE_DOMAIN_VISIBLE_PASSWORD sysCRED_TYPE = 0x4
sysCRED_TYPE_GENERIC_CERTIFICATE sysCRED_TYPE = 0x5
sysCRED_TYPE_DOMAIN_EXTENDED sysCRED_TYPE = 0x6
)
// https://docs.microsoft.com/en-us/windows/desktop/api/wincred/nf-wincred-credreadw
func sysCredRead(targetName string, typ sysCRED_TYPE) ([]byte, error) {
var pcred *sysCREDENTIAL
targetNamePtr, _ := windows.UTF16PtrFromString(targetName)
ret, _, err := syscall.SyscallN(
procCredRead.Addr(),
uintptr(unsafe.Pointer(targetNamePtr)),
uintptr(typ),
0,
uintptr(unsafe.Pointer(&pcred)),
)
if ret == 0 {
return nil, err
}
defer procCredFree.Call(uintptr(unsafe.Pointer(pcred)))
return goBytes(pcred.CredentialBlob, pcred.CredentialBlobSize), nil
}
// goBytes copies the given C byte array to a Go byte array (see `C.GoBytes`).
// This function avoids having cgo as dependency.
func goBytes(src uintptr, len uint32) []byte {
if src == uintptr(0) {
return []byte{}
}
rv := make([]byte, len)
copy(rv, *(*[]byte)(unsafe.Pointer(&struct {
Data unsafe.Pointer
Len int
Cap int
}{
Data: unsafe.Pointer(src),
Len: int(len),
Cap: int(len),
})))
/*
copy(rv, *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
Data: src,
Len: int(len),
Cap: int(len),
})))
*/
return rv
}