//go:build windows package crypto import ( "fmt" "unsafe" "golang.org/x/sys/windows" ) // Unprotect calls Windows CryptUnprotectData on the given blob. entropy // is optional secondary entropy that must match what was passed to the // matching CryptProtectData call; pass nil if none was used. scope // selects between the current-user and local-machine master keys. // // The original WinAuth wrote both User and Machine DPAPI blobs without // extra entropy, so passing entropy=nil is what the legacy migration // needs in practice. func Unprotect(blob, entropy []byte, scope DPAPIScope) ([]byte, error) { var in windows.DataBlob in.Size = uint32(len(blob)) if len(blob) > 0 { in.Data = &blob[0] } var entIn *windows.DataBlob if len(entropy) > 0 { entIn = &windows.DataBlob{Size: uint32(len(entropy)), Data: &entropy[0]} } var flags uint32 if scope == DPAPIScopeLocalMachine { flags |= 0x4 // CRYPTPROTECT_LOCAL_MACHINE } var out windows.DataBlob if err := windows.CryptUnprotectData(&in, nil, entIn, 0, nil, flags, &out); err != nil { return nil, fmt.Errorf("dpapi: unprotect: %w", err) } defer windows.LocalFree(windows.Handle(unsafe.Pointer(out.Data))) if out.Size == 0 { return []byte{}, nil } result := make([]byte, out.Size) copy(result, unsafe.Slice(out.Data, out.Size)) return result, nil }