初版功能完成
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
//go:build windows
|
||||
|
||||
// Package dpapi protects Windows Node secrets using the operating system DPAPI.
|
||||
package dpapi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var entropy = []byte("RemLink-v1-NodeIdentity")
|
||||
|
||||
// Protector uses machine-scoped DPAPI so Console/service identities can read
|
||||
// the package-local identity after Windows restart. Directory ACLs remain required.
|
||||
type Protector struct{}
|
||||
|
||||
func (Protector) Protect(plain []byte) ([]byte, error) {
|
||||
if len(plain) == 0 {
|
||||
return nil, errors.New("DPAPI plaintext must not be empty")
|
||||
}
|
||||
input := blob(plain)
|
||||
extra := blob(entropy)
|
||||
var output windows.DataBlob
|
||||
name, err := windows.UTF16PtrFromString("RemLink Node Identity")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
flags := uint32(windows.CRYPTPROTECT_LOCAL_MACHINE | windows.CRYPTPROTECT_UI_FORBIDDEN)
|
||||
if err := windows.CryptProtectData(&input, name, &extra, 0, nil, flags, &output); err != nil {
|
||||
return nil, fmt.Errorf("protect secret with DPAPI: %w", err)
|
||||
}
|
||||
return copyAndFree(output)
|
||||
}
|
||||
|
||||
func (Protector) Unprotect(ciphertext []byte) ([]byte, error) {
|
||||
if len(ciphertext) == 0 {
|
||||
return nil, errors.New("DPAPI ciphertext must not be empty")
|
||||
}
|
||||
input := blob(ciphertext)
|
||||
extra := blob(entropy)
|
||||
var output windows.DataBlob
|
||||
flags := uint32(windows.CRYPTPROTECT_UI_FORBIDDEN)
|
||||
if err := windows.CryptUnprotectData(&input, nil, &extra, 0, nil, flags, &output); err != nil {
|
||||
return nil, fmt.Errorf("unprotect secret with DPAPI: %w", err)
|
||||
}
|
||||
return copyAndFree(output)
|
||||
}
|
||||
|
||||
func blob(value []byte) windows.DataBlob {
|
||||
return windows.DataBlob{Size: uint32(len(value)), Data: &value[0]}
|
||||
}
|
||||
|
||||
func copyAndFree(value windows.DataBlob) ([]byte, error) {
|
||||
if value.Data == nil || value.Size == 0 {
|
||||
return nil, errors.New("DPAPI returned empty output")
|
||||
}
|
||||
defer windows.LocalFree(windows.Handle(uintptr(unsafe.Pointer(value.Data))))
|
||||
result := make([]byte, int(value.Size))
|
||||
copy(result, unsafe.Slice(value.Data, int(value.Size)))
|
||||
return result, nil
|
||||
}
|
||||
Reference in New Issue
Block a user