From b35570c9f432e648a78e25e253e41afdd535b244 Mon Sep 17 00:00:00 2001 From: Davis Goodin Date: Thu, 14 Sep 2023 23:43:17 -0700 Subject: [PATCH 1/5] Add xcryptobackendgen tool, x/crypto fork --- .gitmodules | 3 + cmd/xcryptobackendgen/.git-go-patch | 6 + cmd/xcryptobackendgen/.gitignore | 2 + cmd/xcryptobackendgen/main.go | 135 +++++ .../0001-Add-backends-with-sha3_356.patch | 130 +++++ cmd/xcryptobackendgen/xcrypto | 1 + go.mod | 1 + go.sum | 3 + internal/fork/backend.go | 535 ++++++++++++++++++ internal/fork/git.go | 59 ++ 10 files changed, 875 insertions(+) create mode 100644 cmd/xcryptobackendgen/.git-go-patch create mode 100644 cmd/xcryptobackendgen/.gitignore create mode 100644 cmd/xcryptobackendgen/main.go create mode 100644 cmd/xcryptobackendgen/patches/0001-Add-backends-with-sha3_356.patch create mode 160000 cmd/xcryptobackendgen/xcrypto create mode 100644 internal/fork/backend.go create mode 100644 internal/fork/git.go diff --git a/.gitmodules b/.gitmodules index 207e9e20..b3e0a20c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "cmd/fuzzcrypto/go-cose"] path = cmd/fuzzcrypto/go-cose url = https://github.com/veraison/go-cose +[submodule "cmd/xcryptobackendgen/xcrypto"] + path = cmd/xcryptobackendgen/xcrypto + url = https://go.googlesource.com/crypto diff --git a/cmd/xcryptobackendgen/.git-go-patch b/cmd/xcryptobackendgen/.git-go-patch new file mode 100644 index 00000000..7370ced3 --- /dev/null +++ b/cmd/xcryptobackendgen/.git-go-patch @@ -0,0 +1,6 @@ +{ + "MinimumToolVersion": "v1.0.1", + "SubmoduleDir": "xcrypto", + "PatchesDir": "patches", + "StatusFileDir": "artifacts/go-patch" +} diff --git a/cmd/xcryptobackendgen/.gitignore b/cmd/xcryptobackendgen/.gitignore new file mode 100644 index 00000000..ee602e1e --- /dev/null +++ b/cmd/xcryptobackendgen/.gitignore @@ -0,0 +1,2 @@ +# Ignore patching tool temp files. +artifacts/ diff --git a/cmd/xcryptobackendgen/main.go b/cmd/xcryptobackendgen/main.go new file mode 100644 index 00000000..db3ab2ce --- /dev/null +++ b/cmd/xcryptobackendgen/main.go @@ -0,0 +1,135 @@ +package main + +import ( + "errors" + "flag" + "fmt" + "log" + "os" + "path/filepath" + + "github.com/microsoft/go-infra/internal/fork" +) + +var forkRootDir = flag.String("fork", "", "Crypto fork root directory") +var backendDir = flag.String("backend", "", "Directory with Go files that implement the backend") +var outDir = flag.String( + "out", "", + "Output directory\n"+ + "Creates a copy of the fork in this directory and generates the backend there") + +var onlyAPI = flag.Bool( + "api", false, + "Only generate the API (nobackend)\n"+ + "This helps generate a clean API file for use in a toolset-agnostic x/crypto patch") + +var autoYes = flag.Bool("y", false, "delete old output and overwrite without prompting") + +func main() { + h := flag.Bool("h", false, "show help") + flag.Parse() + if *h { + flag.Usage() + return + } + rej := func(s string) { + fmt.Fprintln(flag.CommandLine.Output(), s) + flag.Usage() + os.Exit(1) + } + if *forkRootDir == "" { + rej("missing -fork") + } + if *backendDir == "" { + rej("missing -backend") + } + if err := run(); err != nil { + log.Fatalln(err) + } +} + +func run() error { + var proxyDir string + if *outDir == "" { + proxyDir = filepath.Join(*forkRootDir, fork.XCryptoBackendProxyPath) + fmt.Printf("Not specified: '-out'. Generating backend files in %q\n", proxyDir) + if err := fork.RemoveDirContent(proxyDir, !*autoYes); err != nil { + return err + } + } else { + proxyDir = filepath.Join(*outDir, fork.XCryptoBackendProxyPath) + fmt.Printf("Specified: '-out'. Creating copy of Git repo to generate proxy in %q\n", proxyDir) + if err := fork.RemoveDirContent(*outDir, !*autoYes); err != nil { + return err + } + if err := fork.GitCheckoutTo(*forkRootDir, *outDir); err != nil { + return err + } + } + // For now, use the nobackend as a source of truth for the API. This keeps + // maintenance cost low while only one Go toolset implements the API. + // + // When sharing the API among multiple Go toolset forks, it is probably + // better to make the API/placeholder itself be the source of truth, so it + // receives only intentional changes. + backends, err := fork.FindBackendFiles(*backendDir) + if err != nil { + return err + } + var backendAPI *fork.BackendFile + for _, b := range backends { + if b.Filename == filepath.Join(*backendDir, "nobackend.go") { + if err := b.APITrim(); err != nil { + return err + } + backendAPI = b + break + } + } + if backendAPI == nil { + for _, b := range backends { + log.Printf("Found backend: %v\n", b.Filename) + } + return errors.New("no backend found appears to be nobackend") + } + // Remove toolset-specific information about the API if only generating the API. + if *onlyAPI { + backendAPI.Constraint = "" + } + // Create a proxy for each backend. + for _, b := range backends { + if b == backendAPI { + // This is the unimplemented placeholder API, not a proxy. It's ready to write. + if err := writeBackend(b, filepath.Join(proxyDir, "nobackend.go")); err != nil { + return err + } + continue + } else if *onlyAPI { + continue + } + proxy, err := b.ProxyAPI(backendAPI) + if err != nil { + return err + } + err = writeBackend(proxy, filepath.Join(proxyDir, filepath.Base(b.Filename))) + if err != nil { + return err + } + } + return nil +} + +func writeBackend(b fork.FormattedWriterTo, path string) error { + if err := os.MkdirAll(filepath.Dir(path), 0o777); err != nil { + return err + } + apiFile, err := os.Create(path) + if err != nil { + return err + } + err = b.Format(apiFile) + if err2 := apiFile.Close(); err == nil { + err = err2 + } + return err +} diff --git a/cmd/xcryptobackendgen/patches/0001-Add-backends-with-sha3_356.patch b/cmd/xcryptobackendgen/patches/0001-Add-backends-with-sha3_356.patch new file mode 100644 index 00000000..0192352c --- /dev/null +++ b/cmd/xcryptobackendgen/patches/0001-Add-backends-with-sha3_356.patch @@ -0,0 +1,130 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Davis Goodin +Date: Mon, 28 Aug 2023 16:39:33 -0500 +Subject: [PATCH] Add sha3 with placeholder backend for compilation + +--- + hkdf/hkdf.go | 2 ++ + internal/backend/nobackend.go | 47 +++++++++++++++++++++++++++++++++++ + sha3/hashes.go | 19 ++++++++++++++ + 3 files changed, 68 insertions(+) + create mode 100644 internal/backend/nobackend.go + +diff --git a/hkdf/hkdf.go b/hkdf/hkdf.go +index dda3f143bec506..8700f761b3b6de 100644 +--- a/hkdf/hkdf.go ++++ b/hkdf/hkdf.go +@@ -13,6 +13,7 @@ package hkdf // import "golang.org/x/crypto/hkdf" + import ( + "crypto/hmac" + "errors" ++ "fmt" + "hash" + "io" + ) +@@ -88,6 +89,7 @@ func Expand(hash func() hash.Hash, pseudorandomKey, info []byte) io.Reader { + // New returns a Reader, from which keys can be read, using the given hash, + // secret, salt and context info. Salt and info can be nil. + func New(hash func() hash.Hash, secret, salt, info []byte) io.Reader { ++ fmt.Println("This is a not-so-subtle modification to x/crypto that you got automatically!") + prk := Extract(hash, secret, salt) + return Expand(hash, prk, info) + } +diff --git a/internal/backend/nobackend.go b/internal/backend/nobackend.go +new file mode 100644 +index 00000000000000..c0bc5b36756a20 +--- /dev/null ++++ b/internal/backend/nobackend.go +@@ -0,0 +1,47 @@ ++// Generated code. DO NOT EDIT. ++ ++package backend ++ ++import ( ++ "crypto" ++ "crypto/cipher" ++ "hash" ++ "io" ++) ++ ++const Enabled = false ++ ++func NewSHA1() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA224() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA256() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA384() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA512() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA3_224() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA3_256() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA3_384() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA3_512() hash.Hash { panic("cryptobackend: not available") } ++ ++func SHA1(p []byte) (sum [20]byte) { panic("cryptobackend: not available") } ++func SHA224(p []byte) (sum [28]byte) { panic("cryptobackend: not available") } ++func SHA256(p []byte) (sum [32]byte) { panic("cryptobackend: not available") } ++func SHA384(p []byte) (sum [48]byte) { panic("cryptobackend: not available") } ++func SHA512(p []byte) (sum [64]byte) { panic("cryptobackend: not available") } ++func SHA3_224(p []byte) (sum [28]byte) { panic("cryptobackend: not available") } ++func SHA3_256(p []byte) (sum [32]byte) { panic("cryptobackend: not available") } ++func SHA3_384(p []byte) (sum [48]byte) { panic("cryptobackend: not available") } ++func SHA3_512(p []byte) (sum [64]byte) { panic("cryptobackend: not available") } ++ ++func SupportsHash(h crypto.Hash) bool { panic("cryptobackend: not available") } ++ ++func NewHMAC(h func() hash.Hash, key []byte) hash.Hash { panic("cryptobackend: not available") } ++ ++func NewAESCipher(key []byte) (cipher.Block, error) { panic("cryptobackend: not available") } ++func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) { panic("cryptobackend: not available") } ++ ++func ExpandHKDF(h func() hash.Hash, pseudorandomKey, info []byte) (io.Reader, error) { ++ panic("cryptobackend: not available") ++} ++ ++func ExtractHKDF(h func() hash.Hash, secret, salt []byte) ([]byte, error) { ++ panic("cryptobackend: not available") ++} +diff --git a/sha3/hashes.go b/sha3/hashes.go +index 0d8043fd2a173d..28b94b633dc67e 100644 +--- a/sha3/hashes.go ++++ b/sha3/hashes.go +@@ -9,7 +9,10 @@ package sha3 + // bytes. + + import ( ++ "crypto" + "hash" ++ ++ "golang.org/x/crypto/internal/backend" + ) + + // New224 creates a new SHA3-224 hash. +@@ -26,6 +29,14 @@ func New224() hash.Hash { + // Its generic security strength is 256 bits against preimage attacks, + // and 128 bits against collision attacks. + func New256() hash.Hash { ++ if backend.Enabled { ++ if backend.SupportsHash(crypto.SHA3_256) { ++ println("using backend sha3_256") ++ return backend.NewSHA3_256() ++ } else { ++ println("backend doesn't support sha3_256") ++ } ++ } + if h := new256Asm(); h != nil { + return h + } +@@ -74,6 +85,14 @@ func Sum224(data []byte) (digest [28]byte) { + + // Sum256 returns the SHA3-256 digest of the data. + func Sum256(data []byte) (digest [32]byte) { ++ if backend.Enabled { ++ if backend.SupportsHash(crypto.SHA3_256) { ++ println("using backend sha3_256") ++ return backend.SHA3_256(data) ++ } else { ++ println("backend doesn't support sha3_256") ++ } ++ } + h := New256() + h.Write(data) + h.Sum(digest[:0]) diff --git a/cmd/xcryptobackendgen/xcrypto b/cmd/xcryptobackendgen/xcrypto new file mode 160000 index 00000000..f5c19557 --- /dev/null +++ b/cmd/xcryptobackendgen/xcrypto @@ -0,0 +1 @@ +Subproject commit f5c1955784c42f6481aa84e0c1f48d59db224be6 diff --git a/go.mod b/go.mod index 4b9b98ac..3467cd40 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5 golang.org/x/mod v0.6.0 golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 + golang.org/x/tools v0.1.12 ) require ( diff --git a/go.sum b/go.sum index 3e2e74d6..f520f950 100644 --- a/go.sum +++ b/go.sum @@ -236,6 +236,7 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -286,6 +287,8 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/fork/backend.go b/internal/fork/backend.go new file mode 100644 index 00000000..bf970a85 --- /dev/null +++ b/internal/fork/backend.go @@ -0,0 +1,535 @@ +package fork + +import ( + "errors" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/token" + "io" + "path/filepath" + "strconv" + "strings" + + "golang.org/x/tools/go/ast/astutil" +) + +// XCryptoBackendProxyPath is the path within an x/crypto fork of the backend proxy. +var XCryptoBackendProxyPath = filepath.Join("internal", "backend") + +// xCryptoBackendMapPrefix is the prefix for command comments. It would be nice +// to omit the " ", but the Go formatter adds it back in. (Sometimes? It does +// in VS Code. It doesn't seem like Go formatters should, though.) +const xCryptoBackendMapPrefix = "// xcrypto_backend_map:" + +func commands(n ast.Node) []string { + var cmds []string + ast.Inspect(n, func(n ast.Node) bool { + if n, ok := n.(*ast.Comment); !ok { + return true + } else if cmd, ok := strings.CutPrefix(n.Text, xCryptoBackendMapPrefix); !ok { + return true + } else { + cmds = append(cmds, cmd) + } + return false + }) + return cmds +} + +// FindBackendFiles returns the Go files that appear to be backends in the +// given directory. Returns the parsed trees rather than only the filenames: we +// parsed the file to determine if it's a backend, and the parsed data is +// useful later. +func FindBackendFiles(dir string) ([]*BackendFile, error) { + matches, err := filepath.Glob(filepath.Join(dir, "*.go")) + if err != nil { + return nil, err + } + var backends []*BackendFile + for _, match := range matches { + b, err := NewBackendFile(match) + if err != nil { + if errors.Is(err, errNotBackend) { + continue + } + return nil, err + } + backends = append(backends, b) + } + return backends, nil +} + +type FormattedWriterTo interface { + Format(w io.Writer) error +} + +var errNotBackend = errors.New("not a crypto backend file") + +type BackendFile struct { + // Filename is the absolute path to the original file. + Filename string + Constraint string + + f *ast.File + fset *token.FileSet + + enabledDecl *ast.ValueSpec +} + +func NewBackendFile(filename string) (*BackendFile, error) { + b := &BackendFile{ + Filename: filename, + fset: token.NewFileSet(), + } + f, err := parser.ParseFile(b.fset, filename, nil, parser.ParseComments) + if err != nil { + return nil, err + } + b.f = f + // Super simple heuristic that works for "crypto/internal/backend": does + // the file define "Enabled"? + enabledObj := f.Scope.Lookup("Enabled") + if enabledObj == nil { + return nil, errNotBackend + } + var ok bool + if b.enabledDecl, ok = enabledObj.Decl.(*ast.ValueSpec); !ok { + return nil, fmt.Errorf( + "found Enabled symbol, but not a ValueSpec: %q defined at %v", + enabledObj.Name, b.fset.Position(enabledObj.Pos())) + } + // Preserve the build constraint. + for _, cg := range f.Comments { + for _, c := range cg.List { + if strings.HasPrefix(c.Text, "//go:build ") { + b.Constraint = c.Text + break + } + } + } + return b, nil +} + +// APITrim changes b to include a placeholder API, following conventions that +// assume b is a "nobackend" crypto backend. The placeholder API is buildable, +// but panics if used. +func (b *BackendFile) APITrim() error { + var err error + localPackageType := make(map[string]*ast.TypeSpec) + _ = astutil.Apply(b.f, func(c *astutil.Cursor) bool { + switch n := (c.Node()).(type) { + // Only look into top-level declarations, nothing else. + case *ast.File, *ast.GenDecl: + return true + + case *ast.TypeSpec: + // Remove type names declared in this package and keep track of + // them to remove any functions that use them in another pass. + localPackageType[n.Name.Name] = n + c.Delete() + + case *ast.ValueSpec: + // Remove all var/const declarations other than Enabled. + declaresEnabled := false + for _, name := range n.Names { + if name.Name == "Enabled" { + declaresEnabled = true + } + } + if !declaresEnabled { + c.Delete() + } else if len(n.Names) != 1 { + err = fmt.Errorf( + "declaration for Enabled %v includes multiple names", + b.fset.Position(n.Pos())) + } + // We could detect "const RandReader = ..." and change it to + // "var RandReader io.Reader". go:linkname supports mapping a var + // to a const in this way. However, this is already accessible via + // "crypto/rand" and there is no need to provide direct access. + // So, simply leave it out. + } + return false + }, func(c *astutil.Cursor) bool { + switch n := (c.Node()).(type) { + case *ast.GenDecl: + // Removing a ValueSpec or TypeSpec could leave a node with zero + // specs. format.Node fails if there are zero specs. Clean it up. + if len(n.Specs) == 0 { + c.Delete() + } + } + return true + }) + if err != nil { + return err + } + _ = astutil.Apply(b.f, func(c *astutil.Cursor) bool { + switch n := (c.Node()).(type) { + case *ast.File: + return true + case *ast.FuncDecl: + // Remove unexported functions and all methods. + if !n.Name.IsExported() || n.Recv != nil { + c.Delete() + return false + } + var remove bool + ast.Inspect(n.Type, func(tn ast.Node) bool { + switch tn := tn.(type) { + case *ast.Ident: + if _, ok := localPackageType[tn.Name]; ok { + remove = true + return false + } + } + return true + }) + if remove { + c.Delete() + } + } + return false + }, nil) + return cleanImports(b.f) +} + +// ProxyAPI creates a proxy for b implementing each var/func in the given api. +// If b is missing some part of api, it is skipped and recorded in the returned +// BackendProxy to be included in a comment by Format. +// +// If a func in b uses the "noescape" command, the proxy includes +// "//go:noescape" on that func. +func (b *BackendFile) ProxyAPI(api *BackendFile) (*BackendProxy, error) { + p := &BackendProxy{ + backend: b, + api: api, + f: &ast.File{Name: b.f.Name}, + fset: token.NewFileSet(), + } + + // Keep track of the first err hit by each AST walk in this variable. + // Note that walks don't necessarily stop immediately when "return false" is + // used, so take care that an error isn't cleared out by a later iteration. + var err error + failFalse := func(walkErr error) bool { + if err == nil && walkErr != nil { + err = walkErr + } + return false + } + + // Copy the imports that are used to define the API. + // Ignore the imports used by b: those will include internal packages and + // backend-specific packages that we don't have access to. + ast.Inspect(api.f, func(n ast.Node) bool { + switch n := n.(type) { + case *ast.File: + return true + case *ast.GenDecl: + if n.Tok == token.IMPORT { + return true + } + case *ast.ImportSpec: + var name string + if n.Name != nil { + name = n.Name.Name + } + path, err := strconv.Unquote(n.Path.Value) + if err != nil { + return failFalse(err) + } + astutil.AddNamedImport(p.fset, p.f, name, path) + } + return false + }) + if err != nil { + return nil, err + } + + // Add unsafe import needed for go:linkname. + astutil.AddNamedImport(p.fset, p.f, "_", "unsafe") + + // Add Enabled const. + if len(b.enabledDecl.Values) != 1 { + return nil, fmt.Errorf( + "declaration for Enabled %v includes 0 or multiple values", + b.fset.Position(b.enabledDecl.Pos())) + } + v, err := deepCopyExpression(b.enabledDecl.Values[0]) + if err != nil { + return nil, err + } + p.f.Decls = append(p.f.Decls, &ast.GenDecl{ + Tok: token.CONST, + Specs: []ast.Spec{ + &ast.ValueSpec{ + Names: []*ast.Ident{{Name: "Enabled"}}, + Values: []ast.Expr{v}, + }, + }, + }) + + // For each API, find it in b. If exists, generate linkname "proxy" func. + ast.Inspect(api.f, func(n ast.Node) bool { + switch n := n.(type) { + case *ast.File: + return true + case *ast.FuncDecl: + apiFnType, err := deepCopyExpression(n.Type) + if err != nil { + return failFalse(err) + } + // Find the corresponding func in b. + o := b.f.Scope.Lookup(n.Name.Name) + if o == nil { + p.missing = append(p.missing, n) + p.f.Decls = append(p.f.Decls, + newPanicFunc(n, apiFnType, "not implemented by this backend")) + return false + } + fn, ok := o.Decl.(*ast.FuncDecl) + if !ok { + return failFalse(fmt.Errorf( + "found symbol, but not a function: %q defined at %v", + n.Name.Name, api.fset.Position(n.Pos()))) + } + comments := []*ast.Comment{ + {Text: "//go:linkname " + n.Name.Name + " crypto/internal/backend." + n.Name.Name}, + } + for _, cmd := range commands(fn) { + switch cmd { + case "noescape": + comments = append(comments, &ast.Comment{Text: "//go:noescape"}) + default: + return failFalse(fmt.Errorf("unknown command %q (%v)", cmd, b.fset.Position(n.Pos()))) + } + } + proxyFnType, err := deepCopyExpression(fn.Type) + if err != nil { + return failFalse(err) + } + proxyFn := &ast.FuncDecl{ + // Don't use the original data: make sure the token position is + // not copied. Including a non-zero position causes the + // formatter to write the comment in strange locations within + // the function declaration: it tries to reconcile specific + // token positions vs. the zero position of the comment. + Name: ast.NewIdent(n.Name.Name), + Type: proxyFnType, + Doc: &ast.CommentGroup{List: comments}, + } + p.f.Decls = append(p.f.Decls, proxyFn) + } + return false + }) + if err != nil { + return nil, err + } + + if err := cleanImports(p.f); err != nil { + return nil, err + } + return p, nil +} + +func (b *BackendFile) Format(w io.Writer) error { + io.WriteString(w, "// Generated code. DO NOT EDIT.\n\n") + if b.Constraint != "" { + io.WriteString(w, b.Constraint) + io.WriteString(w, "\n\n") + } + return write(b.f, b.fset, w) +} + +type BackendProxy struct { + backend *BackendFile + api *BackendFile + + f *ast.File + fset *token.FileSet + + missing []*ast.FuncDecl +} + +func (p *BackendProxy) Format(w io.Writer) error { + io.WriteString(w, "// Generated code. DO NOT EDIT.\n\n") + io.WriteString(w, "// This file implements a proxy that links into a specific crypto backend.\n\n") + if p.backend.Constraint != "" { + io.WriteString(w, p.backend.Constraint) + io.WriteString(w, "\n\n") + } + if len(p.missing) > 0 { + io.WriteString(w, "// The following functions defined in the API are not implemented by the backend and panic instead:\n//\n") + for _, fn := range p.missing { + io.WriteString(w, "//\t") + io.WriteString(w, fn.Name.Name) + io.WriteString(w, "\n") + } + io.WriteString(w, "\n") + } + return write(p.f, p.fset, w) +} + +func write(f *ast.File, fset *token.FileSet, w io.Writer) error { + // Force the printer to use the comments associated with the nodes by + // clearing the cache-like (but not just a cache) Comments slice. + f.Comments = nil + return format.Node(w, fset, f) +} + +func cleanImports(f *ast.File) error { + var err error + var cleanedImports []ast.Spec + _ = astutil.Apply(f, func(c *astutil.Cursor) bool { + switch n := (c.Node()).(type) { + case *ast.GenDecl: + // Support multiple import declarations. Import blocks can't be + // nested, so simply reset the slice. + if n.Tok == token.IMPORT { + cleanedImports = cleanedImports[:0] + } + case *ast.ImportSpec: + var p string + if p, err = strconv.Unquote(n.Path.Value); err != nil { + return false + } + if n.Name != nil && n.Name.Name == "_" || astutil.UsesImport(f, p) { + // Reset the position to remove unnecessary newlines when + // imports are omitted. + n.Path.ValuePos = 0 + cleanedImports = append(cleanedImports, n) + } + return false + } + return true + }, func(c *astutil.Cursor) bool { + switch n := (c.Node()).(type) { + case *ast.GenDecl: + if n.Tok == token.IMPORT { + n.Specs = cleanedImports + } + } + return true + }) + return nil +} + +func newPanicFunc(n *ast.FuncDecl, fnType *ast.FuncType, message string) *ast.FuncDecl { + return &ast.FuncDecl{ + Name: ast.NewIdent(n.Name.Name), + Type: fnType, + Doc: &ast.CommentGroup{ + List: []*ast.Comment{{Text: "// Not implemented by this backend."}}, + }, + Body: &ast.BlockStmt{ + List: []ast.Stmt{ + &ast.ExprStmt{ + X: &ast.CallExpr{ + Fun: &ast.Ident{Name: "panic"}, + Args: []ast.Expr{ + &ast.BasicLit{ + Kind: token.STRING, + Value: strconv.Quote(message), + }, + }, + }, + }, + }, + }, + } +} + +// Deep copy functions for the AST, but without copying token positions. + +func deepCopyFieldList(src *ast.FieldList) (*ast.FieldList, error) { + var dst ast.FieldList + for _, x := range src.List { + xCopy, err := deepCopyField(x) + if err != nil { + return nil, err + } + dst.List = append(dst.List, xCopy) + } + return &dst, nil +} + +func deepCopyField(src *ast.Field) (*ast.Field, error) { + var dst ast.Field + for _, n := range src.Names { + nCopy, err := deepCopyExpression(n) + if err != nil { + return nil, err + } + dst.Names = append(dst.Names, nCopy) + } + var err error + dst.Type, err = deepCopyExpression(src.Type) + if err != nil { + return nil, err + } + return &dst, nil +} + +func deepCopyExpression[T ast.Expr](src T) (T, error) { + var err error + var f func(ast.Expr) ast.Expr + f = func(src ast.Expr) ast.Expr { + if src == nil { + return nil + } + switch src := src.(type) { + + case *ast.ArrayType: + return &ast.ArrayType{ + Elt: f(src.Elt), + Len: f(src.Len), + } + + case *ast.FuncType: + if src.TypeParams != nil { + err = fmt.Errorf("unsupported type params %v", src.TypeParams) + return nil + } + var ft ast.FuncType + ft.Params, err = deepCopyFieldList(src.Params) + if err != nil { + return nil + } + ft.Results, err = deepCopyFieldList(src.Results) + if err != nil { + return nil + } + return &ft + + case *ast.Ident: + return ast.NewIdent(src.Name) + + case *ast.SelectorExpr: + return &ast.SelectorExpr{ + X: f(src.X), + Sel: f(src.Sel).(*ast.Ident), + } + + case *ast.BasicLit: + return &ast.BasicLit{ + Kind: src.Kind, + Value: src.Value, + } + + case *ast.StarExpr: + return &ast.StarExpr{ + X: f(src.X), + } + } + err = fmt.Errorf("unsupported expression type %T", src) + return nil + } + r := f(src) + if err != nil { + return *new(T), err + } + return r.(T), nil +} diff --git a/internal/fork/git.go b/internal/fork/git.go new file mode 100644 index 00000000..0bf11fa8 --- /dev/null +++ b/internal/fork/git.go @@ -0,0 +1,59 @@ +package fork + +import ( + "bufio" + "errors" + "fmt" + "log" + "os" + "os/exec" + "path/filepath" +) + +func GitCheckoutTo(gitDir, outDir string) error { + outDir, err := filepath.Abs(outDir) + if err != nil { + return err + } + cmd := exec.Command( + "git", + "checkout-index", + "--all", + "-f", + "--prefix="+outDir+"/", + ) + cmd.Dir = gitDir + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + log.Printf("In %#q, running %v", cmd.Dir, cmd) + return cmd.Run() +} + +func RemoveDirContent(dir string, prompt bool) error { + if prompt { + fmt.Printf("Delete %#q? [y/N] ", dir) + s := bufio.NewScanner(os.Stdin) + _ = s.Scan() + if s.Text() != "y" { + return fmt.Errorf("aborting: %q not %q\n", s.Text(), "y") + } + if err := s.Err(); err != nil { + return err + } + fmt.Println() + } + entries, err := os.ReadDir(dir) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + // Nothing to do. + return nil + } + return err + } + for _, entry := range entries { + if err := os.RemoveAll(filepath.Join(dir, entry.Name())); err != nil { + return err + } + } + return nil +} From 309573f97edf514e6a3c6a09f553aedec44cd0ec Mon Sep 17 00:00:00 2001 From: Davis Goodin Date: Thu, 14 Sep 2023 23:43:35 -0700 Subject: [PATCH 2/5] Add simple golden-style tests for xcryptobackendgen --- internal/fork/backend_test.go | 87 +++++++ internal/fork/testdata/.gitattributes | 2 + internal/fork/testdata/derivedapi.golden.go | 33 +++ .../exampleRealBackend/backend_test.go | 28 +++ .../exampleRealBackend/boring_linux.go | 135 ++++++++++ .../exampleRealBackend/cng_windows.go | 221 ++++++++++++++++ .../exampleRealBackend/isrequirefips.go | 9 + .../testdata/exampleRealBackend/nobackend.go | 120 +++++++++ .../exampleRealBackend/norequirefips.go | 9 + .../exampleRealBackend/openssl_linux.go | 238 ++++++++++++++++++ .../boring_linux_proxy.go | 54 ++++ .../cng_windows_proxy.go | 51 ++++ .../proxyDerivedAPI.golden/nobackend_proxy.go | 45 ++++ .../openssl_linux_proxy.go | 51 ++++ 14 files changed, 1083 insertions(+) create mode 100644 internal/fork/backend_test.go create mode 100644 internal/fork/testdata/.gitattributes create mode 100644 internal/fork/testdata/derivedapi.golden.go create mode 100644 internal/fork/testdata/exampleRealBackend/backend_test.go create mode 100644 internal/fork/testdata/exampleRealBackend/boring_linux.go create mode 100644 internal/fork/testdata/exampleRealBackend/cng_windows.go create mode 100644 internal/fork/testdata/exampleRealBackend/isrequirefips.go create mode 100644 internal/fork/testdata/exampleRealBackend/nobackend.go create mode 100644 internal/fork/testdata/exampleRealBackend/norequirefips.go create mode 100644 internal/fork/testdata/exampleRealBackend/openssl_linux.go create mode 100644 internal/fork/testdata/proxyDerivedAPI.golden/boring_linux_proxy.go create mode 100644 internal/fork/testdata/proxyDerivedAPI.golden/cng_windows_proxy.go create mode 100644 internal/fork/testdata/proxyDerivedAPI.golden/nobackend_proxy.go create mode 100644 internal/fork/testdata/proxyDerivedAPI.golden/openssl_linux_proxy.go diff --git a/internal/fork/backend_test.go b/internal/fork/backend_test.go new file mode 100644 index 00000000..ae6dc5ce --- /dev/null +++ b/internal/fork/backend_test.go @@ -0,0 +1,87 @@ +package fork + +import ( + "path/filepath" + "reflect" + "sort" + "strings" + "testing" + + "github.com/microsoft/go-infra/goldentest" +) + +func TestFindBackendFiles(t *testing.T) { + got, err := FindBackendFiles("testdata/exampleRealBackend") + if err != nil { + t.Fatal(err) + } + wantPaths := []string{ + "cng_windows.go", + "boring_linux.go", + "openssl_linux.go", + "nobackend.go", + } + for i, w := range wantPaths { + wantPaths[i] = filepath.Join("testdata", "exampleRealBackend", w) + } + var gotPaths []string + for _, b := range got { + gotPaths = append(gotPaths, b.Filename) + } + sort.Strings(wantPaths) + sort.Strings(gotPaths) + if !reflect.DeepEqual(gotPaths, wantPaths) { + t.Errorf("FindBackendFiles() got = %v, want %v", gotPaths, wantPaths) + } +} + +func TestPlaceholderGeneration(t *testing.T) { + b, err := NewBackendFile("testdata/exampleRealBackend/nobackend.go") + if err != nil { + t.Fatal(err) + } + if err := b.APITrim(); err != nil { + t.Fatal(err) + } + var sb strings.Builder + if err := b.Format(&sb); err != nil { + t.Fatal(err) + } + got := sb.String() + goldentest.Check(t, "go test internal/fork", "testdata/derivedapi.golden.go", got) +} + +func TestBackendFile_ProxyAPI(t *testing.T) { + // Note: This uses the golden output of TestPlaceholderGeneration as its input. + api, err := NewBackendFile("testdata/derivedapi.golden.go") + if err != nil { + t.Fatal(err) + } + + tests := []struct { + name string + }{ + {"boring_linux"}, + {"cng_windows"}, + {"openssl_linux"}, + {"nobackend"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + b, err := NewBackendFile("testdata/exampleRealBackend/" + tt.name + ".go") + if err != nil { + t.Fatal(err) + } + proxy, err := b.ProxyAPI(api) + if err != nil { + t.Fatal(err) + } + var sb strings.Builder + if err := proxy.Format(&sb); err != nil { + t.Fatal(err) + } + got := sb.String() + goldentest.Check(t, "go test internal/fork", "testdata/proxyDerivedAPI.golden/"+tt.name+"_proxy.go", got) + }) + } +} diff --git a/internal/fork/testdata/.gitattributes b/internal/fork/testdata/.gitattributes new file mode 100644 index 00000000..5bbd0017 --- /dev/null +++ b/internal/fork/testdata/.gitattributes @@ -0,0 +1,2 @@ +# Always use LF to simplify test output checking. +*.go text eol=lf diff --git a/internal/fork/testdata/derivedapi.golden.go b/internal/fork/testdata/derivedapi.golden.go new file mode 100644 index 00000000..fd158da0 --- /dev/null +++ b/internal/fork/testdata/derivedapi.golden.go @@ -0,0 +1,33 @@ +// Generated code. DO NOT EDIT. + +//go:build !(goexperiment.boringcrypto && linux && cgo && (amd64 || arm64) && !android && !msan) && !(goexperiment.cngcrypto && windows) && !(goexperiment.opensslcrypto && linux && cgo) + +package backend + +import ( + "crypto/cipher" + "hash" +) + +const Enabled = false + +func NewSHA1() hash.Hash { panic("cryptobackend: not available") } +func NewSHA224() hash.Hash { panic("cryptobackend: not available") } +func NewSHA256() hash.Hash { panic("cryptobackend: not available") } +func NewSHA384() hash.Hash { panic("cryptobackend: not available") } +func NewSHA512() hash.Hash { panic("cryptobackend: not available") } + +func NewSHA3_256() hash.Hash { panic("cryptobackend: not available") } + +func SHA1(p []byte) (sum [20]byte) { panic("cryptobackend: not available") } +func SHA224(p []byte) (sum [28]byte) { panic("cryptobackend: not available") } +func SHA256(p []byte) (sum [32]byte) { panic("cryptobackend: not available") } +func SHA384(p []byte) (sum [48]byte) { panic("cryptobackend: not available") } +func SHA512(p []byte) (sum [64]byte) { panic("cryptobackend: not available") } + +func SHA3_256(p []byte) (sum [64]byte) { panic("cryptobackend: not available") } + +func NewHMAC(h func() hash.Hash, key []byte) hash.Hash { panic("cryptobackend: not available") } + +func NewAESCipher(key []byte) (cipher.Block, error) { panic("cryptobackend: not available") } +func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) { panic("cryptobackend: not available") } diff --git a/internal/fork/testdata/exampleRealBackend/backend_test.go b/internal/fork/testdata/exampleRealBackend/backend_test.go new file mode 100644 index 00000000..837cff47 --- /dev/null +++ b/internal/fork/testdata/exampleRealBackend/backend_test.go @@ -0,0 +1,28 @@ +// 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 backend + +import "testing" + +// Test that Unreachable panics. +func TestUnreachable(t *testing.T) { + defer func() { + if Enabled { + if err := recover(); err == nil { + t.Fatal("expected Unreachable to panic") + } + } else { + if err := recover(); err != nil { + t.Fatalf("expected Unreachable to be a no-op") + } + } + }() + Unreachable() +} + +// Test that UnreachableExceptTests does not panic (this is a test). +func TestUnreachableExceptTests(t *testing.T) { + UnreachableExceptTests() +} diff --git a/internal/fork/testdata/exampleRealBackend/boring_linux.go b/internal/fork/testdata/exampleRealBackend/boring_linux.go new file mode 100644 index 00000000..c4449f5d --- /dev/null +++ b/internal/fork/testdata/exampleRealBackend/boring_linux.go @@ -0,0 +1,135 @@ +// 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. + +//go:build goexperiment.boringcrypto && linux && cgo && (amd64 || arm64) && !android && !msan + +// Package boring provides access to BoringCrypto implementation functions. +// Check the variable Enabled to find out whether BoringCrypto is available. +// If BoringCrypto is not available, the functions in this package all panic. +package backend + +import ( + "crypto" + "crypto/cipher" + "crypto/internal/boring" + "hash" +) + +const Enabled = true + +const RandReader = boring.RandReader + +func NewSHA1() hash.Hash { return boring.NewSHA1() } +func NewSHA224() hash.Hash { return boring.NewSHA224() } +func NewSHA256() hash.Hash { return boring.NewSHA256() } +func NewSHA384() hash.Hash { return boring.NewSHA384() } +func NewSHA512() hash.Hash { return boring.NewSHA512() } + +func SHA1(p []byte) (sum [20]byte) { return boring.SHA1(p) } +func SHA224(p []byte) (sum [28]byte) { return boring.SHA224(p) } +func SHA256(p []byte) (sum [32]byte) { return boring.SHA256(p) } +func SHA384(p []byte) (sum [48]byte) { return boring.SHA384(p) } +func SHA512(p []byte) (sum [64]byte) { return boring.SHA512(p) } + +func NewHMAC(h func() hash.Hash, key []byte) hash.Hash { return boring.NewHMAC(h, key) } + +func NewAESCipher(key []byte) (cipher.Block, error) { return boring.NewAESCipher(key) } +func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) { return boring.NewGCMTLS(c) } + +type PublicKeyECDSA = boring.PublicKeyECDSA +type PrivateKeyECDSA = boring.PrivateKeyECDSA + +func GenerateKeyECDSA(curve string) (X, Y, D boring.BigInt, err error) { + return boring.GenerateKeyECDSA(curve) +} + +func NewPrivateKeyECDSA(curve string, X, Y, D boring.BigInt) (*boring.PrivateKeyECDSA, error) { + return boring.NewPrivateKeyECDSA(curve, X, Y, D) +} + +func NewPublicKeyECDSA(curve string, X, Y boring.BigInt) (*boring.PublicKeyECDSA, error) { + return boring.NewPublicKeyECDSA(curve, X, Y) +} + +func SignMarshalECDSA(priv *boring.PrivateKeyECDSA, hash []byte) ([]byte, error) { + return boring.SignMarshalECDSA(priv, hash) +} + +func VerifyECDSA(pub *boring.PublicKeyECDSA, hash []byte, sig []byte) bool { + return boring.VerifyECDSA(pub, hash, sig) +} + +type PublicKeyRSA = boring.PublicKeyRSA +type PrivateKeyRSA = boring.PrivateKeyRSA + +func DecryptRSAOAEP(h, mgfHash hash.Hash, priv *boring.PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) { + return boring.DecryptRSAOAEP(h, mgfHash, priv, ciphertext, label) +} + +func DecryptRSAPKCS1(priv *boring.PrivateKeyRSA, ciphertext []byte) ([]byte, error) { + return boring.DecryptRSAPKCS1(priv, ciphertext) +} + +func DecryptRSANoPadding(priv *boring.PrivateKeyRSA, ciphertext []byte) ([]byte, error) { + return boring.DecryptRSANoPadding(priv, ciphertext) +} + +func EncryptRSAOAEP(h, mgfHash hash.Hash, pub *boring.PublicKeyRSA, msg, label []byte) ([]byte, error) { + return boring.EncryptRSAOAEP(h, mgfHash, pub, msg, label) +} + +func EncryptRSAPKCS1(pub *boring.PublicKeyRSA, msg []byte) ([]byte, error) { + return boring.EncryptRSAPKCS1(pub, msg) +} + +func EncryptRSANoPadding(pub *boring.PublicKeyRSA, msg []byte) ([]byte, error) { + return boring.EncryptRSANoPadding(pub, msg) +} + +func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv boring.BigInt, err error) { + return boring.GenerateKeyRSA(bits) +} + +func NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv boring.BigInt) (*boring.PrivateKeyRSA, error) { + return boring.NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv) +} + +func NewPublicKeyRSA(N, E boring.BigInt) (*boring.PublicKeyRSA, error) { + return boring.NewPublicKeyRSA(N, E) +} + +func SignRSAPKCS1v15(priv *boring.PrivateKeyRSA, h crypto.Hash, hashed []byte) ([]byte, error) { + return boring.SignRSAPKCS1v15(priv, h, hashed) +} + +func SignRSAPSS(priv *boring.PrivateKeyRSA, h crypto.Hash, hashed []byte, saltLen int) ([]byte, error) { + return boring.SignRSAPSS(priv, h, hashed, saltLen) +} + +func VerifyRSAPKCS1v15(pub *boring.PublicKeyRSA, h crypto.Hash, hashed, sig []byte) error { + return boring.VerifyRSAPKCS1v15(pub, h, hashed, sig) +} + +func VerifyRSAPSS(pub *boring.PublicKeyRSA, h crypto.Hash, hashed, sig []byte, saltLen int) error { + return boring.VerifyRSAPSS(pub, h, hashed, sig, saltLen) +} + +type PublicKeyECDH = boring.PublicKeyECDH +type PrivateKeyECDH = boring.PrivateKeyECDH + +func ECDH(priv *boring.PrivateKeyECDH, pub *boring.PublicKeyECDH) ([]byte, error) { + return boring.ECDH(priv, pub) +} + +func GenerateKeyECDH(curve string) (*boring.PrivateKeyECDH, []byte, error) { + return boring.GenerateKeyECDH(curve) +} + +func NewPrivateKeyECDH(curve string, bytes []byte) (*boring.PrivateKeyECDH, error) { + return boring.NewPrivateKeyECDH(curve, bytes) +} + +func NewPublicKeyECDH(curve string, bytes []byte) (*boring.PublicKeyECDH, error) { + return boring.NewPublicKeyECDH(curve, bytes) +} diff --git a/internal/fork/testdata/exampleRealBackend/cng_windows.go b/internal/fork/testdata/exampleRealBackend/cng_windows.go new file mode 100644 index 00000000..a75e4664 --- /dev/null +++ b/internal/fork/testdata/exampleRealBackend/cng_windows.go @@ -0,0 +1,221 @@ +// 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. + +//go:build goexperiment.cngcrypto && windows + +// Package cng provides access to CNGCrypto implementation functions. +// Check the variable Enabled to find out whether CNGCrypto is available. +// If CNGCrypto is not available, the functions in this package all panic. +package backend + +import ( + "crypto" + "crypto/cipher" + "crypto/internal/boring/sig" + "hash" + _ "unsafe" + + "github.com/microsoft/go-crypto-winnative/cng" +) + +// Enabled controls whether FIPS crypto is enabled. +const Enabled = true + +func init() { + // 1: FIPS required: abort the process if the system is not in FIPS mode. + // other values: continue regardless of system-configured FIPS mode. + if v, _, ok := envGoFIPS(); ok && v == "1" { + enabled, err := cng.FIPS() + if err != nil { + panic("cngcrypto: unknown FIPS mode: " + err.Error()) + } + if !enabled { + panic("cngcrypto: not in FIPS mode") + } + } + sig.BoringCrypto() +} + +const RandReader = cng.RandReader + +func NewSHA1() hash.Hash { + return cng.NewSHA1() +} + +func NewSHA224() hash.Hash { panic("cngcrypto: not available") } + +func NewSHA256() hash.Hash { + return cng.NewSHA256() +} + +func NewSHA384() hash.Hash { + return cng.NewSHA384() +} + +func NewSHA512() hash.Hash { + return cng.NewSHA512() +} + +func NewSHA3_256() hash.Hash { + return cng.NewSHA3_256() +} + +// xcrypto_backend_map:noescape +func SHA1(p []byte) (sum [20]byte) { + return cng.SHA1(p) +} + +// xcrypto_backend_map:noescape +func SHA224(p []byte) (sum [28]byte) { panic("cngcrypto: not available") } + +// xcrypto_backend_map:noescape +func SHA256(p []byte) (sum [32]byte) { + return cng.SHA256(p) +} + +// xcrypto_backend_map:noescape +func SHA384(p []byte) (sum [48]byte) { + return cng.SHA384(p) +} + +// xcrypto_backend_map:noescape +func SHA512(p []byte) (sum [64]byte) { + return cng.SHA512(p) +} + +// xcrypto_backend_map:noescape +func SHA3_256(p []byte) (sum [32]byte) { + return cng.SHA3_256(p) +} + +func NewHMAC(h func() hash.Hash, key []byte) hash.Hash { + return cng.NewHMAC(h, key) +} + +func NewAESCipher(key []byte) (cipher.Block, error) { + return cng.NewAESCipher(key) +} + +func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) { + return cng.NewGCMTLS(c) +} + +type PublicKeyECDSA = cng.PublicKeyECDSA +type PrivateKeyECDSA = cng.PrivateKeyECDSA + +func GenerateKeyECDSA(curve string) (X, Y, D cng.BigInt, err error) { + return cng.GenerateKeyECDSA(curve) +} + +func NewPrivateKeyECDSA(curve string, X, Y, D cng.BigInt) (*cng.PrivateKeyECDSA, error) { + return cng.NewPrivateKeyECDSA(curve, X, Y, D) +} + +func NewPublicKeyECDSA(curve string, X, Y cng.BigInt) (*cng.PublicKeyECDSA, error) { + return cng.NewPublicKeyECDSA(curve, X, Y) +} + +//go:linkname encodeSignature crypto/ecdsa.encodeSignature +func encodeSignature(r, s []byte) ([]byte, error) + +//go:linkname parseSignature crypto/ecdsa.parseSignature +func parseSignature(sig []byte) (r, s []byte, err error) + +func SignMarshalECDSA(priv *cng.PrivateKeyECDSA, hash []byte) ([]byte, error) { + r, s, err := cng.SignECDSA(priv, hash) + if err != nil { + return nil, err + } + return encodeSignature(r, s) +} + +func VerifyECDSA(pub *cng.PublicKeyECDSA, hash []byte, sig []byte) bool { + rBytes, sBytes, err := parseSignature(sig) + if err != nil { + return false + } + return cng.VerifyECDSA(pub, hash, cng.BigInt(rBytes), cng.BigInt(sBytes)) +} + +func SignECDSA(priv *cng.PrivateKeyECDSA, hash []byte) (r, s cng.BigInt, err error) { + return cng.SignECDSA(priv, hash) +} + +func VerifyECDSARaw(pub *cng.PublicKeyECDSA, hash []byte, r, s cng.BigInt) bool { + return cng.VerifyECDSA(pub, hash, r, s) +} + +type PublicKeyRSA = cng.PublicKeyRSA +type PrivateKeyRSA = cng.PrivateKeyRSA + +func DecryptRSAOAEP(h, mgfHash hash.Hash, priv *cng.PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) { + return cng.DecryptRSAOAEP(h, priv, ciphertext, label) +} + +func DecryptRSAPKCS1(priv *cng.PrivateKeyRSA, ciphertext []byte) ([]byte, error) { + return cng.DecryptRSAPKCS1(priv, ciphertext) +} + +func DecryptRSANoPadding(priv *cng.PrivateKeyRSA, ciphertext []byte) ([]byte, error) { + return cng.DecryptRSANoPadding(priv, ciphertext) +} + +func EncryptRSAOAEP(h, mgfHash hash.Hash, pub *cng.PublicKeyRSA, msg, label []byte) ([]byte, error) { + return cng.EncryptRSAOAEP(h, pub, msg, label) +} + +func EncryptRSAPKCS1(pub *cng.PublicKeyRSA, msg []byte) ([]byte, error) { + return cng.EncryptRSAPKCS1(pub, msg) +} + +func EncryptRSANoPadding(pub *cng.PublicKeyRSA, msg []byte) ([]byte, error) { + return cng.EncryptRSANoPadding(pub, msg) +} + +func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv cng.BigInt, err error) { + return cng.GenerateKeyRSA(bits) +} + +func NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv cng.BigInt) (*cng.PrivateKeyRSA, error) { + return cng.NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv) +} + +func NewPublicKeyRSA(N, E cng.BigInt) (*cng.PublicKeyRSA, error) { + return cng.NewPublicKeyRSA(N, E) +} + +func SignRSAPKCS1v15(priv *cng.PrivateKeyRSA, h crypto.Hash, hashed []byte) ([]byte, error) { + return cng.SignRSAPKCS1v15(priv, h, hashed) +} + +func SignRSAPSS(priv *cng.PrivateKeyRSA, h crypto.Hash, hashed []byte, saltLen int) ([]byte, error) { + return cng.SignRSAPSS(priv, h, hashed, saltLen) +} + +func VerifyRSAPKCS1v15(pub *cng.PublicKeyRSA, h crypto.Hash, hashed, sig []byte) error { + return cng.VerifyRSAPKCS1v15(pub, h, hashed, sig) +} + +func VerifyRSAPSS(pub *cng.PublicKeyRSA, h crypto.Hash, hashed, sig []byte, saltLen int) error { + return cng.VerifyRSAPSS(pub, h, hashed, sig, saltLen) +} + +type PrivateKeyECDH = cng.PrivateKeyECDH +type PublicKeyECDH = cng.PublicKeyECDH + +func ECDH(priv *cng.PrivateKeyECDH, pub *cng.PublicKeyECDH) ([]byte, error) { + return cng.ECDH(priv, pub) +} + +func GenerateKeyECDH(curve string) (*cng.PrivateKeyECDH, []byte, error) { + return cng.GenerateKeyECDH(curve) +} + +func NewPrivateKeyECDH(curve string, bytes []byte) (*cng.PrivateKeyECDH, error) { + return cng.NewPrivateKeyECDH(curve, bytes) +} + +func NewPublicKeyECDH(curve string, bytes []byte) (*cng.PublicKeyECDH, error) { + return cng.NewPublicKeyECDH(curve, bytes) +} diff --git a/internal/fork/testdata/exampleRealBackend/isrequirefips.go b/internal/fork/testdata/exampleRealBackend/isrequirefips.go new file mode 100644 index 00000000..e5d7570d --- /dev/null +++ b/internal/fork/testdata/exampleRealBackend/isrequirefips.go @@ -0,0 +1,9 @@ +// Copyright 2022 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 requirefips + +package backend + +const isRequireFIPS = true diff --git a/internal/fork/testdata/exampleRealBackend/nobackend.go b/internal/fork/testdata/exampleRealBackend/nobackend.go new file mode 100644 index 00000000..53454725 --- /dev/null +++ b/internal/fork/testdata/exampleRealBackend/nobackend.go @@ -0,0 +1,120 @@ +// 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. + +// Do not edit the build constraint by hand. It is generated by "backendgen.go". + +//go:build !(goexperiment.boringcrypto && linux && cgo && (amd64 || arm64) && !android && !msan) && !(goexperiment.cngcrypto && windows) && !(goexperiment.opensslcrypto && linux && cgo) + +package backend + +import ( + "crypto" + "crypto/cipher" + "hash" +) + +const Enabled = false + +type BigInt = []uint + +type randReader int + +func (randReader) Read(b []byte) (int, error) { panic("cryptobackend: not available") } + +const RandReader = randReader(0) + +func NewSHA1() hash.Hash { panic("cryptobackend: not available") } +func NewSHA224() hash.Hash { panic("cryptobackend: not available") } +func NewSHA256() hash.Hash { panic("cryptobackend: not available") } +func NewSHA384() hash.Hash { panic("cryptobackend: not available") } +func NewSHA512() hash.Hash { panic("cryptobackend: not available") } + +func NewSHA3_256() hash.Hash { panic("cryptobackend: not available") } + +func SHA1(p []byte) (sum [20]byte) { panic("cryptobackend: not available") } +func SHA224(p []byte) (sum [28]byte) { panic("cryptobackend: not available") } +func SHA256(p []byte) (sum [32]byte) { panic("cryptobackend: not available") } +func SHA384(p []byte) (sum [48]byte) { panic("cryptobackend: not available") } +func SHA512(p []byte) (sum [64]byte) { panic("cryptobackend: not available") } + +func SHA3_256(p []byte) (sum [64]byte) { panic("cryptobackend: not available") } + +func NewHMAC(h func() hash.Hash, key []byte) hash.Hash { panic("cryptobackend: not available") } + +func NewAESCipher(key []byte) (cipher.Block, error) { panic("cryptobackend: not available") } +func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) { panic("cryptobackend: not available") } + +type PublicKeyECDSA struct{ _ int } +type PrivateKeyECDSA struct{ _ int } + +func GenerateKeyECDSA(curve string) (X, Y, D BigInt, err error) { + panic("cryptobackend: not available") +} +func NewPrivateKeyECDSA(curve string, X, Y, D BigInt) (*PrivateKeyECDSA, error) { + panic("cryptobackend: not available") +} +func NewPublicKeyECDSA(curve string, X, Y BigInt) (*PublicKeyECDSA, error) { + panic("cryptobackend: not available") +} +func SignMarshalECDSA(priv *PrivateKeyECDSA, hash []byte) ([]byte, error) { + panic("cryptobackend: not available") +} +func VerifyECDSA(pub *PublicKeyECDSA, hash []byte, sig []byte) bool { + panic("cryptobackend: not available") +} + +type PublicKeyRSA struct{ _ int } +type PrivateKeyRSA struct{ _ int } + +func DecryptRSAOAEP(h, mgfHash hash.Hash, priv *PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) { + panic("cryptobackend: not available") +} +func DecryptRSAPKCS1(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) { + panic("cryptobackend: not available") +} +func DecryptRSANoPadding(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) { + panic("cryptobackend: not available") +} +func EncryptRSAOAEP(h, mgfHash hash.Hash, pub *PublicKeyRSA, msg, label []byte) ([]byte, error) { + panic("cryptobackend: not available") +} +func EncryptRSAPKCS1(pub *PublicKeyRSA, msg []byte) ([]byte, error) { + panic("cryptobackend: not available") +} +func EncryptRSANoPadding(pub *PublicKeyRSA, msg []byte) ([]byte, error) { + panic("cryptobackend: not available") +} +func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv BigInt, err error) { + panic("cryptobackend: not available") +} +func NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv BigInt) (*PrivateKeyRSA, error) { + panic("cryptobackend: not available") +} +func NewPublicKeyRSA(N, E BigInt) (*PublicKeyRSA, error) { + panic("cryptobackend: not available") +} +func SignRSAPKCS1v15(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte) ([]byte, error) { + panic("cryptobackend: not available") +} +func SignRSAPSS(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte, saltLen int) ([]byte, error) { + panic("cryptobackend: not available") +} +func VerifyRSAPKCS1v15(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte) error { + panic("cryptobackend: not available") +} +func VerifyRSAPSS(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte, saltLen int) error { + panic("cryptobackend: not available") +} + +type PublicKeyECDH struct{} +type PrivateKeyECDH struct{} + +func ECDH(*PrivateKeyECDH, *PublicKeyECDH) ([]byte, error) { panic("cryptobackend: not available") } +func GenerateKeyECDH(string) (*PrivateKeyECDH, []byte, error) { panic("cryptobackend: not available") } +func NewPrivateKeyECDH(string, []byte) (*PrivateKeyECDH, error) { + panic("cryptobackend: not available") +} +func NewPublicKeyECDH(string, []byte) (*PublicKeyECDH, error) { panic("cryptobackend: not available") } +func (*PublicKeyECDH) Bytes() []byte { panic("cryptobackend: not available") } +func (*PrivateKeyECDH) PublicKey() (*PublicKeyECDH, error) { panic("cryptobackend: not available") } diff --git a/internal/fork/testdata/exampleRealBackend/norequirefips.go b/internal/fork/testdata/exampleRealBackend/norequirefips.go new file mode 100644 index 00000000..26bfb5f6 --- /dev/null +++ b/internal/fork/testdata/exampleRealBackend/norequirefips.go @@ -0,0 +1,9 @@ +// Copyright 2022 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 !requirefips + +package backend + +const isRequireFIPS = false diff --git a/internal/fork/testdata/exampleRealBackend/openssl_linux.go b/internal/fork/testdata/exampleRealBackend/openssl_linux.go new file mode 100644 index 00000000..181fa172 --- /dev/null +++ b/internal/fork/testdata/exampleRealBackend/openssl_linux.go @@ -0,0 +1,238 @@ +// 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. + +//go:build goexperiment.opensslcrypto && linux && cgo + +// Package openssl provides access to OpenSSLCrypto implementation functions. +// Check the variable Enabled to find out whether OpenSSLCrypto is available. +// If OpenSSLCrypto is not available, the functions in this package all panic. +package backend + +import ( + "crypto" + "crypto/cipher" + "crypto/internal/boring/sig" + "hash" + "syscall" +) + +// Enabled controls whether FIPS crypto is enabled. +const Enabled = true + +// knownVersions is a list of supported and well-known libcrypto.so suffixes in decreasing version order. +// FreeBSD library version numbering does not directly align to the version of OpenSSL. +// Its preferred search order is 11 -> 111. +// Some distributions use 1.0.0 and others (such as Debian) 1.0.2 to refer to the same OpenSSL 1.0.2 version. +// Fedora derived distros use different naming for the version 1.0.x. +var knownVersions = [...]string{"3", "1.1", "11", "111", "1.0.2", "1.0.0", "10"} + +func init() { + version, _ := syscall.Getenv("GO_OPENSSL_VERSION_OVERRIDE") + if version == "" { + var fallbackVersion string + for _, v := range knownVersions { + exists, fips := openssl.CheckVersion(v) + if exists && fips { + version = v + break + } + if exists && fallbackVersion == "" { + fallbackVersion = v + } + } + if version == "" && fallbackVersion != "" { + version = fallbackVersion + } + } + if err := openssl.Init(version); err != nil { + panic("opensslcrypto: can't initialize OpenSSL " + version + ": " + err.Error()) + } + // 0: FIPS opt-out: abort the process if it is enabled and can't be disabled. + // 1: FIPS required: abort the process if it is not enabled and can't be enabled. + // other values: do not override OpenSSL configured FIPS mode. + var fips string + if v, _, ok := envGoFIPS(); ok { + fips = v + } else if systemFIPSMode() { + // System configuration can only force FIPS mode. + fips = "1" + } + switch fips { + case "0": + if openssl.FIPS() { + if err := openssl.SetFIPS(false); err != nil { + panic("opensslcrypto: can't disable FIPS mode for " + openssl.VersionText() + ": " + err.Error()) + } + } + case "1": + if !openssl.FIPS() { + if err := openssl.SetFIPS(true); err != nil { + panic("opensslcrypto: can't enable FIPS mode for " + openssl.VersionText() + ": " + err.Error()) + } + } + } + sig.BoringCrypto() +} + +func systemFIPSMode() bool { + var fd int + for { + var err error + fd, err = syscall.Open("/proc/sys/crypto/fips_enabled", syscall.O_RDONLY, 0) + if err == nil { + break + } + switch err { + case syscall.EINTR: + continue + case syscall.ENOENT: + return false + default: + // If there is an error reading we could either panic or assume FIPS is not enabled. + // Panicking would be too disruptive for apps that don't require FIPS. + // If an app wants to be 100% sure that is running in FIPS mode + // it should use boring.Enabled() or GOFIPS=1. + return false + } + } + defer syscall.Close(fd) + var tmp [1]byte + n, err := syscall.Read(fd, tmp[:]) + if n != 1 || err != nil { + // We return false instead of panicing for the same reason as before. + return false + } + // fips_enabled can be either '0' or '1'. + return tmp[0] == '1' +} + +const RandReader = openssl.RandReader + +func NewSHA1() hash.Hash { return openssl.NewSHA1() } +func NewSHA224() hash.Hash { return openssl.NewSHA224() } +func NewSHA256() hash.Hash { return openssl.NewSHA256() } +func NewSHA384() hash.Hash { return openssl.NewSHA384() } +func NewSHA512() hash.Hash { return openssl.NewSHA512() } + +func NewSHA3_256() hash.Hash { return openssl.NewSHA3_256() } + +// xcrypto_backend_map:noescape +func SHA1(p []byte) (sum [20]byte) { return openssl.SHA1(p) } + +// xcrypto_backend_map:noescape +func SHA224(p []byte) (sum [28]byte) { return openssl.SHA224(p) } + +// xcrypto_backend_map:noescape +func SHA256(p []byte) (sum [32]byte) { return openssl.SHA256(p) } + +// xcrypto_backend_map:noescape +func SHA384(p []byte) (sum [48]byte) { return openssl.SHA384(p) } + +// xcrypto_backend_map:noescape +func SHA512(p []byte) (sum [64]byte) { return openssl.SHA512(p) } + +// xcrypto_backend_map:noescape +func SHA3_256(p []byte) (sum [32]byte) { return openssl.SHA3_256(p) } + +func NewHMAC(h func() hash.Hash, key []byte) hash.Hash { return openssl.NewHMAC(h, key) } + +func NewAESCipher(key []byte) (cipher.Block, error) { return openssl.NewAESCipher(key) } +func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) { return openssl.NewGCMTLS(c) } + +type PublicKeyECDSA = openssl.PublicKeyECDSA +type PrivateKeyECDSA = openssl.PrivateKeyECDSA + +func GenerateKeyECDSA(curve string) (X, Y, D openssl.BigInt, err error) { + return openssl.GenerateKeyECDSA(curve) +} + +func NewPrivateKeyECDSA(curve string, X, Y, D openssl.BigInt) (*openssl.PrivateKeyECDSA, error) { + return openssl.NewPrivateKeyECDSA(curve, X, Y, D) +} + +func NewPublicKeyECDSA(curve string, X, Y openssl.BigInt) (*openssl.PublicKeyECDSA, error) { + return openssl.NewPublicKeyECDSA(curve, X, Y) +} + +func SignMarshalECDSA(priv *openssl.PrivateKeyECDSA, hash []byte) ([]byte, error) { + return openssl.SignMarshalECDSA(priv, hash) +} + +func VerifyECDSA(pub *openssl.PublicKeyECDSA, hash []byte, sig []byte) bool { + return openssl.VerifyECDSA(pub, hash, sig) +} + +type PublicKeyRSA = openssl.PublicKeyRSA +type PrivateKeyRSA = openssl.PrivateKeyRSA + +func DecryptRSAOAEP(h, mgfHash hash.Hash, priv *openssl.PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) { + return openssl.DecryptRSAOAEP(h, mgfHash, priv, ciphertext, label) +} + +func DecryptRSAPKCS1(priv *openssl.PrivateKeyRSA, ciphertext []byte) ([]byte, error) { + return openssl.DecryptRSAPKCS1(priv, ciphertext) +} + +func DecryptRSANoPadding(priv *openssl.PrivateKeyRSA, ciphertext []byte) ([]byte, error) { + return openssl.DecryptRSANoPadding(priv, ciphertext) +} + +func EncryptRSAOAEP(h, mgfHash hash.Hash, pub *openssl.PublicKeyRSA, msg, label []byte) ([]byte, error) { + return openssl.EncryptRSAOAEP(h, mgfHash, pub, msg, label) +} + +func EncryptRSAPKCS1(pub *openssl.PublicKeyRSA, msg []byte) ([]byte, error) { + return openssl.EncryptRSAPKCS1(pub, msg) +} + +func EncryptRSANoPadding(pub *openssl.PublicKeyRSA, msg []byte) ([]byte, error) { + return openssl.EncryptRSANoPadding(pub, msg) +} + +func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv openssl.BigInt, err error) { + return openssl.GenerateKeyRSA(bits) +} + +func NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv openssl.BigInt) (*openssl.PrivateKeyRSA, error) { + return openssl.NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv) +} + +func NewPublicKeyRSA(N, E openssl.BigInt) (*openssl.PublicKeyRSA, error) { + return openssl.NewPublicKeyRSA(N, E) +} + +func SignRSAPKCS1v15(priv *openssl.PrivateKeyRSA, h crypto.Hash, hashed []byte) ([]byte, error) { + return openssl.SignRSAPKCS1v15(priv, h, hashed) +} + +func SignRSAPSS(priv *openssl.PrivateKeyRSA, h crypto.Hash, hashed []byte, saltLen int) ([]byte, error) { + return openssl.SignRSAPSS(priv, h, hashed, saltLen) +} + +func VerifyRSAPKCS1v15(pub *openssl.PublicKeyRSA, h crypto.Hash, hashed, sig []byte) error { + return openssl.VerifyRSAPKCS1v15(pub, h, hashed, sig) +} + +func VerifyRSAPSS(pub *openssl.PublicKeyRSA, h crypto.Hash, hashed, sig []byte, saltLen int) error { + return openssl.VerifyRSAPSS(pub, h, hashed, sig, saltLen) +} + +type PublicKeyECDH = openssl.PublicKeyECDH +type PrivateKeyECDH = openssl.PrivateKeyECDH + +func ECDH(priv *openssl.PrivateKeyECDH, pub *openssl.PublicKeyECDH) ([]byte, error) { + return openssl.ECDH(priv, pub) +} + +func GenerateKeyECDH(curve string) (*openssl.PrivateKeyECDH, []byte, error) { + return openssl.GenerateKeyECDH(curve) +} + +func NewPrivateKeyECDH(curve string, bytes []byte) (*openssl.PrivateKeyECDH, error) { + return openssl.NewPrivateKeyECDH(curve, bytes) +} + +func NewPublicKeyECDH(curve string, bytes []byte) (*openssl.PublicKeyECDH, error) { + return openssl.NewPublicKeyECDH(curve, bytes) +} diff --git a/internal/fork/testdata/proxyDerivedAPI.golden/boring_linux_proxy.go b/internal/fork/testdata/proxyDerivedAPI.golden/boring_linux_proxy.go new file mode 100644 index 00000000..cc39633f --- /dev/null +++ b/internal/fork/testdata/proxyDerivedAPI.golden/boring_linux_proxy.go @@ -0,0 +1,54 @@ +// Generated code. DO NOT EDIT. + +// This file implements a proxy that links into a specific crypto backend. + +//go:build goexperiment.boringcrypto && linux && cgo && (amd64 || arm64) && !android && !msan + +// The following functions defined in the API are not implemented by the backend and panic instead: +// +// NewSHA3_256 +// SHA3_256 + +package backend + +import ( + "crypto/cipher" + _ "unsafe" + "hash" +) + +const Enabled = true +//go:linkname NewSHA1 crypto/internal/backend.NewSHA1 +func NewSHA1() hash.Hash +//go:linkname NewSHA224 crypto/internal/backend.NewSHA224 +func NewSHA224() hash.Hash +//go:linkname NewSHA256 crypto/internal/backend.NewSHA256 +func NewSHA256() hash.Hash +//go:linkname NewSHA384 crypto/internal/backend.NewSHA384 +func NewSHA384() hash.Hash +//go:linkname NewSHA512 crypto/internal/backend.NewSHA512 +func NewSHA512() hash.Hash +// Not implemented by this backend. +func NewSHA3_256() hash.Hash { + panic("not implemented by this backend") +} +//go:linkname SHA1 crypto/internal/backend.SHA1 +func SHA1(p []byte) (sum [20]byte) +//go:linkname SHA224 crypto/internal/backend.SHA224 +func SHA224(p []byte) (sum [28]byte) +//go:linkname SHA256 crypto/internal/backend.SHA256 +func SHA256(p []byte) (sum [32]byte) +//go:linkname SHA384 crypto/internal/backend.SHA384 +func SHA384(p []byte) (sum [48]byte) +//go:linkname SHA512 crypto/internal/backend.SHA512 +func SHA512(p []byte) (sum [64]byte) +// Not implemented by this backend. +func SHA3_256(p []byte) (sum [64]byte) { + panic("not implemented by this backend") +} +//go:linkname NewHMAC crypto/internal/backend.NewHMAC +func NewHMAC(h func() hash.Hash, key []byte) hash.Hash +//go:linkname NewAESCipher crypto/internal/backend.NewAESCipher +func NewAESCipher(key []byte) (cipher.Block, error) +//go:linkname NewGCMTLS crypto/internal/backend.NewGCMTLS +func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) diff --git a/internal/fork/testdata/proxyDerivedAPI.golden/cng_windows_proxy.go b/internal/fork/testdata/proxyDerivedAPI.golden/cng_windows_proxy.go new file mode 100644 index 00000000..6372b605 --- /dev/null +++ b/internal/fork/testdata/proxyDerivedAPI.golden/cng_windows_proxy.go @@ -0,0 +1,51 @@ +// Generated code. DO NOT EDIT. + +// This file implements a proxy that links into a specific crypto backend. + +//go:build goexperiment.cngcrypto && windows + +package backend + +import ( + "crypto/cipher" + _ "unsafe" + "hash" +) + +const Enabled = true +//go:linkname NewSHA1 crypto/internal/backend.NewSHA1 +func NewSHA1() hash.Hash +//go:linkname NewSHA224 crypto/internal/backend.NewSHA224 +func NewSHA224() hash.Hash +//go:linkname NewSHA256 crypto/internal/backend.NewSHA256 +func NewSHA256() hash.Hash +//go:linkname NewSHA384 crypto/internal/backend.NewSHA384 +func NewSHA384() hash.Hash +//go:linkname NewSHA512 crypto/internal/backend.NewSHA512 +func NewSHA512() hash.Hash +//go:linkname NewSHA3_256 crypto/internal/backend.NewSHA3_256 +func NewSHA3_256() hash.Hash +//go:linkname SHA1 crypto/internal/backend.SHA1 +//go:noescape +func SHA1(p []byte) (sum [20]byte) +//go:linkname SHA224 crypto/internal/backend.SHA224 +//go:noescape +func SHA224(p []byte) (sum [28]byte) +//go:linkname SHA256 crypto/internal/backend.SHA256 +//go:noescape +func SHA256(p []byte) (sum [32]byte) +//go:linkname SHA384 crypto/internal/backend.SHA384 +//go:noescape +func SHA384(p []byte) (sum [48]byte) +//go:linkname SHA512 crypto/internal/backend.SHA512 +//go:noescape +func SHA512(p []byte) (sum [64]byte) +//go:linkname SHA3_256 crypto/internal/backend.SHA3_256 +//go:noescape +func SHA3_256(p []byte) (sum [32]byte) +//go:linkname NewHMAC crypto/internal/backend.NewHMAC +func NewHMAC(h func() hash.Hash, key []byte) hash.Hash +//go:linkname NewAESCipher crypto/internal/backend.NewAESCipher +func NewAESCipher(key []byte) (cipher.Block, error) +//go:linkname NewGCMTLS crypto/internal/backend.NewGCMTLS +func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) diff --git a/internal/fork/testdata/proxyDerivedAPI.golden/nobackend_proxy.go b/internal/fork/testdata/proxyDerivedAPI.golden/nobackend_proxy.go new file mode 100644 index 00000000..43ebb8a4 --- /dev/null +++ b/internal/fork/testdata/proxyDerivedAPI.golden/nobackend_proxy.go @@ -0,0 +1,45 @@ +// Generated code. DO NOT EDIT. + +// This file implements a proxy that links into a specific crypto backend. + +//go:build !(goexperiment.boringcrypto && linux && cgo && (amd64 || arm64) && !android && !msan) && !(goexperiment.cngcrypto && windows) && !(goexperiment.opensslcrypto && linux && cgo) + +package backend + +import ( + "crypto/cipher" + _ "unsafe" + "hash" +) + +const Enabled = false +//go:linkname NewSHA1 crypto/internal/backend.NewSHA1 +func NewSHA1() hash.Hash +//go:linkname NewSHA224 crypto/internal/backend.NewSHA224 +func NewSHA224() hash.Hash +//go:linkname NewSHA256 crypto/internal/backend.NewSHA256 +func NewSHA256() hash.Hash +//go:linkname NewSHA384 crypto/internal/backend.NewSHA384 +func NewSHA384() hash.Hash +//go:linkname NewSHA512 crypto/internal/backend.NewSHA512 +func NewSHA512() hash.Hash +//go:linkname NewSHA3_256 crypto/internal/backend.NewSHA3_256 +func NewSHA3_256() hash.Hash +//go:linkname SHA1 crypto/internal/backend.SHA1 +func SHA1(p []byte) (sum [20]byte) +//go:linkname SHA224 crypto/internal/backend.SHA224 +func SHA224(p []byte) (sum [28]byte) +//go:linkname SHA256 crypto/internal/backend.SHA256 +func SHA256(p []byte) (sum [32]byte) +//go:linkname SHA384 crypto/internal/backend.SHA384 +func SHA384(p []byte) (sum [48]byte) +//go:linkname SHA512 crypto/internal/backend.SHA512 +func SHA512(p []byte) (sum [64]byte) +//go:linkname SHA3_256 crypto/internal/backend.SHA3_256 +func SHA3_256(p []byte) (sum [64]byte) +//go:linkname NewHMAC crypto/internal/backend.NewHMAC +func NewHMAC(h func() hash.Hash, key []byte) hash.Hash +//go:linkname NewAESCipher crypto/internal/backend.NewAESCipher +func NewAESCipher(key []byte) (cipher.Block, error) +//go:linkname NewGCMTLS crypto/internal/backend.NewGCMTLS +func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) diff --git a/internal/fork/testdata/proxyDerivedAPI.golden/openssl_linux_proxy.go b/internal/fork/testdata/proxyDerivedAPI.golden/openssl_linux_proxy.go new file mode 100644 index 00000000..166aa2be --- /dev/null +++ b/internal/fork/testdata/proxyDerivedAPI.golden/openssl_linux_proxy.go @@ -0,0 +1,51 @@ +// Generated code. DO NOT EDIT. + +// This file implements a proxy that links into a specific crypto backend. + +//go:build goexperiment.opensslcrypto && linux && cgo + +package backend + +import ( + "crypto/cipher" + _ "unsafe" + "hash" +) + +const Enabled = true +//go:linkname NewSHA1 crypto/internal/backend.NewSHA1 +func NewSHA1() hash.Hash +//go:linkname NewSHA224 crypto/internal/backend.NewSHA224 +func NewSHA224() hash.Hash +//go:linkname NewSHA256 crypto/internal/backend.NewSHA256 +func NewSHA256() hash.Hash +//go:linkname NewSHA384 crypto/internal/backend.NewSHA384 +func NewSHA384() hash.Hash +//go:linkname NewSHA512 crypto/internal/backend.NewSHA512 +func NewSHA512() hash.Hash +//go:linkname NewSHA3_256 crypto/internal/backend.NewSHA3_256 +func NewSHA3_256() hash.Hash +//go:linkname SHA1 crypto/internal/backend.SHA1 +//go:noescape +func SHA1(p []byte) (sum [20]byte) +//go:linkname SHA224 crypto/internal/backend.SHA224 +//go:noescape +func SHA224(p []byte) (sum [28]byte) +//go:linkname SHA256 crypto/internal/backend.SHA256 +//go:noescape +func SHA256(p []byte) (sum [32]byte) +//go:linkname SHA384 crypto/internal/backend.SHA384 +//go:noescape +func SHA384(p []byte) (sum [48]byte) +//go:linkname SHA512 crypto/internal/backend.SHA512 +//go:noescape +func SHA512(p []byte) (sum [64]byte) +//go:linkname SHA3_256 crypto/internal/backend.SHA3_256 +//go:noescape +func SHA3_256(p []byte) (sum [32]byte) +//go:linkname NewHMAC crypto/internal/backend.NewHMAC +func NewHMAC(h func() hash.Hash, key []byte) hash.Hash +//go:linkname NewAESCipher crypto/internal/backend.NewAESCipher +func NewAESCipher(key []byte) (cipher.Block, error) +//go:linkname NewGCMTLS crypto/internal/backend.NewGCMTLS +func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) From 1a692e445eeb82d29ea43ffb0f30af56518c40c0 Mon Sep 17 00:00:00 2001 From: Davis Goodin Date: Wed, 20 Sep 2023 22:17:27 -0700 Subject: [PATCH 3/5] Fix xcrypto submodule commit: point upstream --- cmd/xcryptobackendgen/xcrypto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/xcryptobackendgen/xcrypto b/cmd/xcryptobackendgen/xcrypto index f5c19557..3f0842a4 160000 --- a/cmd/xcryptobackendgen/xcrypto +++ b/cmd/xcryptobackendgen/xcrypto @@ -1 +1 @@ -Subproject commit f5c1955784c42f6481aa84e0c1f48d59db224be6 +Subproject commit 3f0842a46434ea6f56bf6e684c2b83d90e9cff07 From 3f703c1b2fea1839c2bbeee15f273a5c54354e30 Mon Sep 17 00:00:00 2001 From: Davis Goodin Date: Thu, 21 Sep 2023 01:50:37 -0500 Subject: [PATCH 4/5] Export fork code for use in _core --- .gitmodules | 2 +- cmd/xcryptobackendgen/main.go | 135 ------------------ cmd/xcryptobackendgen/xcrypto | 1 - .../.git-go-patch | 0 .../.gitignore | 0 {internal/fork => xcryptofork}/backend.go | 5 +- .../fork => xcryptofork}/backend_test.go | 2 +- {internal/fork => xcryptofork}/git.go | 2 +- .../0001-Add-backends-with-sha3_356.patch | 0 .../testdata/.gitattributes | 0 .../testdata/derivedapi.golden.go | 0 .../exampleRealBackend/backend_test.go | 0 .../exampleRealBackend/boring_linux.go | 0 .../exampleRealBackend/cng_windows.go | 0 .../exampleRealBackend/isrequirefips.go | 0 .../testdata/exampleRealBackend/nobackend.go | 0 .../exampleRealBackend/norequirefips.go | 0 .../exampleRealBackend/openssl_linux.go | 0 .../boring_linux_proxy.go | 0 .../cng_windows_proxy.go | 0 .../proxyDerivedAPI.golden/nobackend_proxy.go | 0 .../openssl_linux_proxy.go | 0 22 files changed, 6 insertions(+), 141 deletions(-) delete mode 100644 cmd/xcryptobackendgen/main.go delete mode 160000 cmd/xcryptobackendgen/xcrypto rename {cmd/xcryptobackendgen => xcryptofork}/.git-go-patch (100%) rename {cmd/xcryptobackendgen => xcryptofork}/.gitignore (100%) rename {internal/fork => xcryptofork}/backend.go (98%) rename {internal/fork => xcryptofork}/backend_test.go (98%) rename {internal/fork => xcryptofork}/git.go (98%) rename {cmd/xcryptobackendgen => xcryptofork}/patches/0001-Add-backends-with-sha3_356.patch (100%) rename {internal/fork => xcryptofork}/testdata/.gitattributes (100%) rename {internal/fork => xcryptofork}/testdata/derivedapi.golden.go (100%) rename {internal/fork => xcryptofork}/testdata/exampleRealBackend/backend_test.go (100%) rename {internal/fork => xcryptofork}/testdata/exampleRealBackend/boring_linux.go (100%) rename {internal/fork => xcryptofork}/testdata/exampleRealBackend/cng_windows.go (100%) rename {internal/fork => xcryptofork}/testdata/exampleRealBackend/isrequirefips.go (100%) rename {internal/fork => xcryptofork}/testdata/exampleRealBackend/nobackend.go (100%) rename {internal/fork => xcryptofork}/testdata/exampleRealBackend/norequirefips.go (100%) rename {internal/fork => xcryptofork}/testdata/exampleRealBackend/openssl_linux.go (100%) rename {internal/fork => xcryptofork}/testdata/proxyDerivedAPI.golden/boring_linux_proxy.go (100%) rename {internal/fork => xcryptofork}/testdata/proxyDerivedAPI.golden/cng_windows_proxy.go (100%) rename {internal/fork => xcryptofork}/testdata/proxyDerivedAPI.golden/nobackend_proxy.go (100%) rename {internal/fork => xcryptofork}/testdata/proxyDerivedAPI.golden/openssl_linux_proxy.go (100%) diff --git a/.gitmodules b/.gitmodules index b3e0a20c..fa096dd9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,5 +2,5 @@ path = cmd/fuzzcrypto/go-cose url = https://github.com/veraison/go-cose [submodule "cmd/xcryptobackendgen/xcrypto"] - path = cmd/xcryptobackendgen/xcrypto + path = xcryptofork/xcrypto url = https://go.googlesource.com/crypto diff --git a/cmd/xcryptobackendgen/main.go b/cmd/xcryptobackendgen/main.go deleted file mode 100644 index db3ab2ce..00000000 --- a/cmd/xcryptobackendgen/main.go +++ /dev/null @@ -1,135 +0,0 @@ -package main - -import ( - "errors" - "flag" - "fmt" - "log" - "os" - "path/filepath" - - "github.com/microsoft/go-infra/internal/fork" -) - -var forkRootDir = flag.String("fork", "", "Crypto fork root directory") -var backendDir = flag.String("backend", "", "Directory with Go files that implement the backend") -var outDir = flag.String( - "out", "", - "Output directory\n"+ - "Creates a copy of the fork in this directory and generates the backend there") - -var onlyAPI = flag.Bool( - "api", false, - "Only generate the API (nobackend)\n"+ - "This helps generate a clean API file for use in a toolset-agnostic x/crypto patch") - -var autoYes = flag.Bool("y", false, "delete old output and overwrite without prompting") - -func main() { - h := flag.Bool("h", false, "show help") - flag.Parse() - if *h { - flag.Usage() - return - } - rej := func(s string) { - fmt.Fprintln(flag.CommandLine.Output(), s) - flag.Usage() - os.Exit(1) - } - if *forkRootDir == "" { - rej("missing -fork") - } - if *backendDir == "" { - rej("missing -backend") - } - if err := run(); err != nil { - log.Fatalln(err) - } -} - -func run() error { - var proxyDir string - if *outDir == "" { - proxyDir = filepath.Join(*forkRootDir, fork.XCryptoBackendProxyPath) - fmt.Printf("Not specified: '-out'. Generating backend files in %q\n", proxyDir) - if err := fork.RemoveDirContent(proxyDir, !*autoYes); err != nil { - return err - } - } else { - proxyDir = filepath.Join(*outDir, fork.XCryptoBackendProxyPath) - fmt.Printf("Specified: '-out'. Creating copy of Git repo to generate proxy in %q\n", proxyDir) - if err := fork.RemoveDirContent(*outDir, !*autoYes); err != nil { - return err - } - if err := fork.GitCheckoutTo(*forkRootDir, *outDir); err != nil { - return err - } - } - // For now, use the nobackend as a source of truth for the API. This keeps - // maintenance cost low while only one Go toolset implements the API. - // - // When sharing the API among multiple Go toolset forks, it is probably - // better to make the API/placeholder itself be the source of truth, so it - // receives only intentional changes. - backends, err := fork.FindBackendFiles(*backendDir) - if err != nil { - return err - } - var backendAPI *fork.BackendFile - for _, b := range backends { - if b.Filename == filepath.Join(*backendDir, "nobackend.go") { - if err := b.APITrim(); err != nil { - return err - } - backendAPI = b - break - } - } - if backendAPI == nil { - for _, b := range backends { - log.Printf("Found backend: %v\n", b.Filename) - } - return errors.New("no backend found appears to be nobackend") - } - // Remove toolset-specific information about the API if only generating the API. - if *onlyAPI { - backendAPI.Constraint = "" - } - // Create a proxy for each backend. - for _, b := range backends { - if b == backendAPI { - // This is the unimplemented placeholder API, not a proxy. It's ready to write. - if err := writeBackend(b, filepath.Join(proxyDir, "nobackend.go")); err != nil { - return err - } - continue - } else if *onlyAPI { - continue - } - proxy, err := b.ProxyAPI(backendAPI) - if err != nil { - return err - } - err = writeBackend(proxy, filepath.Join(proxyDir, filepath.Base(b.Filename))) - if err != nil { - return err - } - } - return nil -} - -func writeBackend(b fork.FormattedWriterTo, path string) error { - if err := os.MkdirAll(filepath.Dir(path), 0o777); err != nil { - return err - } - apiFile, err := os.Create(path) - if err != nil { - return err - } - err = b.Format(apiFile) - if err2 := apiFile.Close(); err == nil { - err = err2 - } - return err -} diff --git a/cmd/xcryptobackendgen/xcrypto b/cmd/xcryptobackendgen/xcrypto deleted file mode 160000 index 3f0842a4..00000000 --- a/cmd/xcryptobackendgen/xcrypto +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3f0842a46434ea6f56bf6e684c2b83d90e9cff07 diff --git a/cmd/xcryptobackendgen/.git-go-patch b/xcryptofork/.git-go-patch similarity index 100% rename from cmd/xcryptobackendgen/.git-go-patch rename to xcryptofork/.git-go-patch diff --git a/cmd/xcryptobackendgen/.gitignore b/xcryptofork/.gitignore similarity index 100% rename from cmd/xcryptobackendgen/.gitignore rename to xcryptofork/.gitignore diff --git a/internal/fork/backend.go b/xcryptofork/backend.go similarity index 98% rename from internal/fork/backend.go rename to xcryptofork/backend.go index bf970a85..9d7dcecc 100644 --- a/internal/fork/backend.go +++ b/xcryptofork/backend.go @@ -1,4 +1,4 @@ -package fork +package xcryptofork import ( "errors" @@ -12,6 +12,7 @@ import ( "strconv" "strings" + "github.com/microsoft/go-infra/stringutil" "golang.org/x/tools/go/ast/astutil" ) @@ -28,7 +29,7 @@ func commands(n ast.Node) []string { ast.Inspect(n, func(n ast.Node) bool { if n, ok := n.(*ast.Comment); !ok { return true - } else if cmd, ok := strings.CutPrefix(n.Text, xCryptoBackendMapPrefix); !ok { + } else if cmd, ok := stringutil.CutPrefix(n.Text, xCryptoBackendMapPrefix); !ok { return true } else { cmds = append(cmds, cmd) diff --git a/internal/fork/backend_test.go b/xcryptofork/backend_test.go similarity index 98% rename from internal/fork/backend_test.go rename to xcryptofork/backend_test.go index ae6dc5ce..03704eb8 100644 --- a/internal/fork/backend_test.go +++ b/xcryptofork/backend_test.go @@ -1,4 +1,4 @@ -package fork +package xcryptofork import ( "path/filepath" diff --git a/internal/fork/git.go b/xcryptofork/git.go similarity index 98% rename from internal/fork/git.go rename to xcryptofork/git.go index 0bf11fa8..2b77f214 100644 --- a/internal/fork/git.go +++ b/xcryptofork/git.go @@ -1,4 +1,4 @@ -package fork +package xcryptofork import ( "bufio" diff --git a/cmd/xcryptobackendgen/patches/0001-Add-backends-with-sha3_356.patch b/xcryptofork/patches/0001-Add-backends-with-sha3_356.patch similarity index 100% rename from cmd/xcryptobackendgen/patches/0001-Add-backends-with-sha3_356.patch rename to xcryptofork/patches/0001-Add-backends-with-sha3_356.patch diff --git a/internal/fork/testdata/.gitattributes b/xcryptofork/testdata/.gitattributes similarity index 100% rename from internal/fork/testdata/.gitattributes rename to xcryptofork/testdata/.gitattributes diff --git a/internal/fork/testdata/derivedapi.golden.go b/xcryptofork/testdata/derivedapi.golden.go similarity index 100% rename from internal/fork/testdata/derivedapi.golden.go rename to xcryptofork/testdata/derivedapi.golden.go diff --git a/internal/fork/testdata/exampleRealBackend/backend_test.go b/xcryptofork/testdata/exampleRealBackend/backend_test.go similarity index 100% rename from internal/fork/testdata/exampleRealBackend/backend_test.go rename to xcryptofork/testdata/exampleRealBackend/backend_test.go diff --git a/internal/fork/testdata/exampleRealBackend/boring_linux.go b/xcryptofork/testdata/exampleRealBackend/boring_linux.go similarity index 100% rename from internal/fork/testdata/exampleRealBackend/boring_linux.go rename to xcryptofork/testdata/exampleRealBackend/boring_linux.go diff --git a/internal/fork/testdata/exampleRealBackend/cng_windows.go b/xcryptofork/testdata/exampleRealBackend/cng_windows.go similarity index 100% rename from internal/fork/testdata/exampleRealBackend/cng_windows.go rename to xcryptofork/testdata/exampleRealBackend/cng_windows.go diff --git a/internal/fork/testdata/exampleRealBackend/isrequirefips.go b/xcryptofork/testdata/exampleRealBackend/isrequirefips.go similarity index 100% rename from internal/fork/testdata/exampleRealBackend/isrequirefips.go rename to xcryptofork/testdata/exampleRealBackend/isrequirefips.go diff --git a/internal/fork/testdata/exampleRealBackend/nobackend.go b/xcryptofork/testdata/exampleRealBackend/nobackend.go similarity index 100% rename from internal/fork/testdata/exampleRealBackend/nobackend.go rename to xcryptofork/testdata/exampleRealBackend/nobackend.go diff --git a/internal/fork/testdata/exampleRealBackend/norequirefips.go b/xcryptofork/testdata/exampleRealBackend/norequirefips.go similarity index 100% rename from internal/fork/testdata/exampleRealBackend/norequirefips.go rename to xcryptofork/testdata/exampleRealBackend/norequirefips.go diff --git a/internal/fork/testdata/exampleRealBackend/openssl_linux.go b/xcryptofork/testdata/exampleRealBackend/openssl_linux.go similarity index 100% rename from internal/fork/testdata/exampleRealBackend/openssl_linux.go rename to xcryptofork/testdata/exampleRealBackend/openssl_linux.go diff --git a/internal/fork/testdata/proxyDerivedAPI.golden/boring_linux_proxy.go b/xcryptofork/testdata/proxyDerivedAPI.golden/boring_linux_proxy.go similarity index 100% rename from internal/fork/testdata/proxyDerivedAPI.golden/boring_linux_proxy.go rename to xcryptofork/testdata/proxyDerivedAPI.golden/boring_linux_proxy.go diff --git a/internal/fork/testdata/proxyDerivedAPI.golden/cng_windows_proxy.go b/xcryptofork/testdata/proxyDerivedAPI.golden/cng_windows_proxy.go similarity index 100% rename from internal/fork/testdata/proxyDerivedAPI.golden/cng_windows_proxy.go rename to xcryptofork/testdata/proxyDerivedAPI.golden/cng_windows_proxy.go diff --git a/internal/fork/testdata/proxyDerivedAPI.golden/nobackend_proxy.go b/xcryptofork/testdata/proxyDerivedAPI.golden/nobackend_proxy.go similarity index 100% rename from internal/fork/testdata/proxyDerivedAPI.golden/nobackend_proxy.go rename to xcryptofork/testdata/proxyDerivedAPI.golden/nobackend_proxy.go diff --git a/internal/fork/testdata/proxyDerivedAPI.golden/openssl_linux_proxy.go b/xcryptofork/testdata/proxyDerivedAPI.golden/openssl_linux_proxy.go similarity index 100% rename from internal/fork/testdata/proxyDerivedAPI.golden/openssl_linux_proxy.go rename to xcryptofork/testdata/proxyDerivedAPI.golden/openssl_linux_proxy.go From ff73e9ebad2854aba04e39e4322fa930c8dd0add Mon Sep 17 00:00:00 2001 From: Davis Goodin Date: Thu, 28 Sep 2023 23:20:37 -0500 Subject: [PATCH 5/5] Fix submodule, add patches --- .gitmodules | 2 +- .../patches/0001-Add-backend-proxies.patch | 394 ++++++++++++++++++ .../0001-Add-backends-with-sha3_356.patch | 130 ------ ...ent-algorithms-using-backend-proxies.patch | 214 ++++++++++ xcryptofork/xcrypto | 1 + 5 files changed, 610 insertions(+), 131 deletions(-) create mode 100644 xcryptofork/patches/0001-Add-backend-proxies.patch delete mode 100644 xcryptofork/patches/0001-Add-backends-with-sha3_356.patch create mode 100644 xcryptofork/patches/0002-Implement-algorithms-using-backend-proxies.patch create mode 160000 xcryptofork/xcrypto diff --git a/.gitmodules b/.gitmodules index fa096dd9..7db0ab04 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "cmd/fuzzcrypto/go-cose"] path = cmd/fuzzcrypto/go-cose url = https://github.com/veraison/go-cose -[submodule "cmd/xcryptobackendgen/xcrypto"] +[submodule "xcryptofork/xcrypto"] path = xcryptofork/xcrypto url = https://go.googlesource.com/crypto diff --git a/xcryptofork/patches/0001-Add-backend-proxies.patch b/xcryptofork/patches/0001-Add-backend-proxies.patch new file mode 100644 index 00000000..b37e3f99 --- /dev/null +++ b/xcryptofork/patches/0001-Add-backend-proxies.patch @@ -0,0 +1,394 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Davis Goodin +Date: Thu, 28 Sep 2023 23:11:12 -0500 +Subject: [PATCH] Add backend proxies + +Add full backend proxies to internal/backend. These are copies of proxies +generated using a development version of the microsoft/go repository. They +should be regenerated when microsoft/go uses this repository. + +To share the x/crypto patches, it may be better to use a single placeholder +backend that acts as nobackend without any build tags. This would let the repo +build and provide an API that every backend needs to implement without being +tied to a particular fork of Go. +--- + internal/backend/boring_linux.go | 106 ++++++++++++++++++++++++++++++ + internal/backend/cng_windows.go | 91 +++++++++++++++++++++++++ + internal/backend/nobackend.go | 66 +++++++++++++++++++ + internal/backend/openssl_linux.go | 83 +++++++++++++++++++++++ + 4 files changed, 346 insertions(+) + create mode 100644 internal/backend/boring_linux.go + create mode 100644 internal/backend/cng_windows.go + create mode 100644 internal/backend/nobackend.go + create mode 100644 internal/backend/openssl_linux.go + +diff --git a/internal/backend/boring_linux.go b/internal/backend/boring_linux.go +new file mode 100644 +index 00000000000000..1ad8f21187872e +--- /dev/null ++++ b/internal/backend/boring_linux.go +@@ -0,0 +1,106 @@ ++// Generated code. DO NOT EDIT. ++ ++// This file implements a proxy that links into a specific crypto backend. ++ ++//go:build goexperiment.boringcrypto && linux && cgo && (amd64 || arm64) && !android && !msan ++ ++// The following functions defined in the API are not implemented by the backend and panic instead: ++// ++// NewSHA3_224 ++// NewSHA3_256 ++// NewSHA3_384 ++// NewSHA3_512 ++// SHA3_224 ++// SHA3_256 ++// SHA3_384 ++// SHA3_512 ++ ++package backend ++ ++import ( ++ "crypto" ++ _ "unsafe" ++ "io" ++ "hash" ++ "crypto/cipher" ++) ++ ++const Enabled = true ++//go:linkname SupportsHash crypto/internal/backend.SupportsHash ++func SupportsHash(h crypto.Hash) bool ++//go:linkname NewSHA1 crypto/internal/backend.NewSHA1 ++func NewSHA1() hash.Hash ++//go:linkname NewSHA224 crypto/internal/backend.NewSHA224 ++func NewSHA224() hash.Hash ++//go:linkname NewSHA256 crypto/internal/backend.NewSHA256 ++func NewSHA256() hash.Hash ++//go:linkname NewSHA384 crypto/internal/backend.NewSHA384 ++func NewSHA384() hash.Hash ++//go:linkname NewSHA512 crypto/internal/backend.NewSHA512 ++func NewSHA512() hash.Hash ++// Not implemented by this backend. ++func NewSHA3_224() hash.Hash { ++ panic("not implemented by this backend") ++} ++// Not implemented by this backend. ++func NewSHA3_256() hash.Hash { ++ panic("not implemented by this backend") ++} ++// Not implemented by this backend. ++func NewSHA3_384() hash.Hash { ++ panic("not implemented by this backend") ++} ++// Not implemented by this backend. ++func NewSHA3_512() hash.Hash { ++ panic("not implemented by this backend") ++} ++//go:linkname SHA1 crypto/internal/backend.SHA1 ++func SHA1(p []byte) (sum [20]byte) ++//go:linkname SHA224 crypto/internal/backend.SHA224 ++func SHA224(p []byte) (sum [28]byte) ++//go:linkname SHA256 crypto/internal/backend.SHA256 ++func SHA256(p []byte) (sum [32]byte) ++//go:linkname SHA384 crypto/internal/backend.SHA384 ++func SHA384(p []byte) (sum [48]byte) ++//go:linkname SHA512 crypto/internal/backend.SHA512 ++func SHA512(p []byte) (sum [64]byte) ++// Not implemented by this backend. ++func SHA3_224(p []byte) (sum [28]byte) { ++ panic("not implemented by this backend") ++} ++// Not implemented by this backend. ++func SHA3_256(p []byte) (sum [32]byte) { ++ panic("not implemented by this backend") ++} ++// Not implemented by this backend. ++func SHA3_384(p []byte) (sum [48]byte) { ++ panic("not implemented by this backend") ++} ++// Not implemented by this backend. ++func SHA3_512(p []byte) (sum [64]byte) { ++ panic("not implemented by this backend") ++} ++//go:linkname NewHMAC crypto/internal/backend.NewHMAC ++func NewHMAC(h func() hash.Hash, key []byte) hash.Hash ++//go:linkname NewAESCipher crypto/internal/backend.NewAESCipher ++func NewAESCipher(key []byte) (cipher.Block, error) ++//go:linkname NewGCMTLS crypto/internal/backend.NewGCMTLS ++func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) ++//go:linkname SupportsHKDF crypto/internal/backend.SupportsHKDF ++func SupportsHKDF() bool ++//go:linkname ExpandHKDF crypto/internal/backend.ExpandHKDF ++func ExpandHKDF(h func() hash.Hash, pseudorandomKey, info []byte) (io.Reader, error) ++//go:linkname ExtractHKDF crypto/internal/backend.ExtractHKDF ++func ExtractHKDF(h func() hash.Hash, secret, salt []byte) ([]byte, error) ++//go:linkname SupportsTLS1PRF crypto/internal/backend.SupportsTLS1PRF ++func SupportsTLS1PRF() bool ++//go:linkname TLS1PRF crypto/internal/backend.TLS1PRF ++func TLS1PRF(result, secret, label, seed []byte, h func() hash.Hash) error ++//go:linkname SupportsPBKDF2 crypto/internal/backend.SupportsPBKDF2 ++func SupportsPBKDF2() bool ++//go:linkname PBKDF2 crypto/internal/backend.PBKDF2 ++func PBKDF2(password, salt []byte, iter, keyLen int, h func() hash.Hash) ([]byte, error) ++//go:linkname NewMD4 crypto/internal/backend.NewMD4 ++func NewMD4() hash.Hash ++//go:linkname MD4 crypto/internal/backend.MD4 ++func MD4(p []byte) (sum [16]byte) +diff --git a/internal/backend/cng_windows.go b/internal/backend/cng_windows.go +new file mode 100644 +index 00000000000000..c64f733cfc3572 +--- /dev/null ++++ b/internal/backend/cng_windows.go +@@ -0,0 +1,91 @@ ++// Generated code. DO NOT EDIT. ++ ++// This file implements a proxy that links into a specific crypto backend. ++ ++//go:build goexperiment.cngcrypto && windows ++ ++// The following functions defined in the API are not implemented by the backend and panic instead: ++// ++// NewSHA3_224 ++// SHA3_224 ++ ++package backend ++ ++import ( ++ "crypto" ++ _ "unsafe" ++ "io" ++ "hash" ++ "crypto/cipher" ++) ++ ++const Enabled = true ++//go:linkname SupportsHash crypto/internal/backend.SupportsHash ++func SupportsHash(h crypto.Hash) bool ++//go:linkname NewSHA1 crypto/internal/backend.NewSHA1 ++func NewSHA1() hash.Hash ++//go:linkname NewSHA224 crypto/internal/backend.NewSHA224 ++func NewSHA224() hash.Hash ++//go:linkname NewSHA256 crypto/internal/backend.NewSHA256 ++func NewSHA256() hash.Hash ++//go:linkname NewSHA384 crypto/internal/backend.NewSHA384 ++func NewSHA384() hash.Hash ++//go:linkname NewSHA512 crypto/internal/backend.NewSHA512 ++func NewSHA512() hash.Hash ++// Not implemented by this backend. ++func NewSHA3_224() hash.Hash { ++ panic("not implemented by this backend") ++} ++//go:linkname NewSHA3_256 crypto/internal/backend.NewSHA3_256 ++func NewSHA3_256() hash.Hash ++//go:linkname NewSHA3_384 crypto/internal/backend.NewSHA3_384 ++func NewSHA3_384() hash.Hash ++//go:linkname NewSHA3_512 crypto/internal/backend.NewSHA3_512 ++func NewSHA3_512() hash.Hash ++//go:linkname SHA1 crypto/internal/backend.SHA1 ++func SHA1(p []byte) (sum [20]byte) ++//go:linkname SHA224 crypto/internal/backend.SHA224 ++func SHA224(p []byte) (sum [28]byte) ++//go:linkname SHA256 crypto/internal/backend.SHA256 ++func SHA256(p []byte) (sum [32]byte) ++//go:linkname SHA384 crypto/internal/backend.SHA384 ++func SHA384(p []byte) (sum [48]byte) ++//go:linkname SHA512 crypto/internal/backend.SHA512 ++func SHA512(p []byte) (sum [64]byte) ++// Not implemented by this backend. ++func SHA3_224(p []byte) (sum [28]byte) { ++ panic("not implemented by this backend") ++} ++//go:linkname SHA3_256 crypto/internal/backend.SHA3_256 ++//go:noescape ++func SHA3_256(p []byte) (sum [32]byte) ++//go:linkname SHA3_384 crypto/internal/backend.SHA3_384 ++//go:noescape ++func SHA3_384(p []byte) (sum [48]byte) ++//go:linkname SHA3_512 crypto/internal/backend.SHA3_512 ++//go:noescape ++func SHA3_512(p []byte) (sum [64]byte) ++//go:linkname NewHMAC crypto/internal/backend.NewHMAC ++func NewHMAC(h func() hash.Hash, key []byte) hash.Hash ++//go:linkname NewAESCipher crypto/internal/backend.NewAESCipher ++func NewAESCipher(key []byte) (cipher.Block, error) ++//go:linkname NewGCMTLS crypto/internal/backend.NewGCMTLS ++func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) ++//go:linkname SupportsHKDF crypto/internal/backend.SupportsHKDF ++func SupportsHKDF() bool ++//go:linkname ExpandHKDF crypto/internal/backend.ExpandHKDF ++func ExpandHKDF(h func() hash.Hash, pseudorandomKey, info []byte) (io.Reader, error) ++//go:linkname ExtractHKDF crypto/internal/backend.ExtractHKDF ++func ExtractHKDF(h func() hash.Hash, secret, salt []byte) ([]byte, error) ++//go:linkname SupportsTLS1PRF crypto/internal/backend.SupportsTLS1PRF ++func SupportsTLS1PRF() bool ++//go:linkname TLS1PRF crypto/internal/backend.TLS1PRF ++func TLS1PRF(result, secret, label, seed []byte, h func() hash.Hash) error ++//go:linkname SupportsPBKDF2 crypto/internal/backend.SupportsPBKDF2 ++func SupportsPBKDF2() bool ++//go:linkname PBKDF2 crypto/internal/backend.PBKDF2 ++func PBKDF2(password, salt []byte, iter, keyLen int, h func() hash.Hash) ([]byte, error) ++//go:linkname NewMD4 crypto/internal/backend.NewMD4 ++func NewMD4() hash.Hash ++//go:linkname MD4 crypto/internal/backend.MD4 ++func MD4(p []byte) (sum [16]byte) +diff --git a/internal/backend/nobackend.go b/internal/backend/nobackend.go +new file mode 100644 +index 00000000000000..4b397e6ed41314 +--- /dev/null ++++ b/internal/backend/nobackend.go +@@ -0,0 +1,66 @@ ++// Generated code. DO NOT EDIT. ++ ++//go:build !(goexperiment.boringcrypto && linux && cgo && (amd64 || arm64) && !android && !msan) && !(goexperiment.cngcrypto && windows) && !(goexperiment.opensslcrypto && linux && cgo) ++ ++package backend ++ ++import ( ++ "crypto" ++ "crypto/cipher" ++ "hash" ++ "io" ++) ++ ++const Enabled = false ++ ++func SupportsHash(h crypto.Hash) bool { panic("cryptobackend: not available") } ++ ++func NewSHA1() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA224() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA256() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA384() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA512() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA3_224() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA3_256() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA3_384() hash.Hash { panic("cryptobackend: not available") } ++func NewSHA3_512() hash.Hash { panic("cryptobackend: not available") } ++ ++func SHA1(p []byte) (sum [20]byte) { panic("cryptobackend: not available") } ++func SHA224(p []byte) (sum [28]byte) { panic("cryptobackend: not available") } ++func SHA256(p []byte) (sum [32]byte) { panic("cryptobackend: not available") } ++func SHA384(p []byte) (sum [48]byte) { panic("cryptobackend: not available") } ++func SHA512(p []byte) (sum [64]byte) { panic("cryptobackend: not available") } ++func SHA3_224(p []byte) (sum [28]byte) { panic("cryptobackend: not available") } ++func SHA3_256(p []byte) (sum [32]byte) { panic("cryptobackend: not available") } ++func SHA3_384(p []byte) (sum [48]byte) { panic("cryptobackend: not available") } ++func SHA3_512(p []byte) (sum [64]byte) { panic("cryptobackend: not available") } ++ ++func NewHMAC(h func() hash.Hash, key []byte) hash.Hash { panic("cryptobackend: not available") } ++ ++func NewAESCipher(key []byte) (cipher.Block, error) { panic("cryptobackend: not available") } ++func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) { panic("cryptobackend: not available") } ++ ++func SupportsHKDF() bool { panic("cryptobackend: not available") } ++ ++func ExpandHKDF(h func() hash.Hash, pseudorandomKey, info []byte) (io.Reader, error) { ++ panic("cryptobackend: not available") ++} ++ ++func ExtractHKDF(h func() hash.Hash, secret, salt []byte) ([]byte, error) { ++ panic("cryptobackend: not available") ++} ++ ++func SupportsTLS1PRF() bool { panic("cryptobackend: not available") } ++ ++func TLS1PRF(result, secret, label, seed []byte, h func() hash.Hash) error { ++ panic("cryptobackend: not available") ++} ++ ++func SupportsPBKDF2() bool { panic("cryptobackend: not available") } ++ ++func PBKDF2(password, salt []byte, iter, keyLen int, h func() hash.Hash) ([]byte, error) { ++ panic("cryptobackend: not available") ++} ++ ++func NewMD4() hash.Hash { panic("cryptobackend: not available") } ++func MD4(p []byte) (sum [16]byte) { panic("cryptobackend: not available") } +diff --git a/internal/backend/openssl_linux.go b/internal/backend/openssl_linux.go +new file mode 100644 +index 00000000000000..1dcac8848eec6a +--- /dev/null ++++ b/internal/backend/openssl_linux.go +@@ -0,0 +1,83 @@ ++// Generated code. DO NOT EDIT. ++ ++// This file implements a proxy that links into a specific crypto backend. ++ ++//go:build goexperiment.opensslcrypto && linux && cgo ++ ++package backend ++ ++import ( ++ "crypto" ++ _ "unsafe" ++ "io" ++ "hash" ++ "crypto/cipher" ++) ++ ++const Enabled = true ++//go:linkname SupportsHash crypto/internal/backend.SupportsHash ++func SupportsHash(h crypto.Hash) bool ++//go:linkname NewSHA1 crypto/internal/backend.NewSHA1 ++func NewSHA1() hash.Hash ++//go:linkname NewSHA224 crypto/internal/backend.NewSHA224 ++func NewSHA224() hash.Hash ++//go:linkname NewSHA256 crypto/internal/backend.NewSHA256 ++func NewSHA256() hash.Hash ++//go:linkname NewSHA384 crypto/internal/backend.NewSHA384 ++func NewSHA384() hash.Hash ++//go:linkname NewSHA512 crypto/internal/backend.NewSHA512 ++func NewSHA512() hash.Hash ++//go:linkname NewSHA3_224 crypto/internal/backend.NewSHA3_224 ++func NewSHA3_224() hash.Hash ++//go:linkname NewSHA3_256 crypto/internal/backend.NewSHA3_256 ++func NewSHA3_256() hash.Hash ++//go:linkname NewSHA3_384 crypto/internal/backend.NewSHA3_384 ++func NewSHA3_384() hash.Hash ++//go:linkname NewSHA3_512 crypto/internal/backend.NewSHA3_512 ++func NewSHA3_512() hash.Hash ++//go:linkname SHA1 crypto/internal/backend.SHA1 ++func SHA1(p []byte) (sum [20]byte) ++//go:linkname SHA224 crypto/internal/backend.SHA224 ++func SHA224(p []byte) (sum [28]byte) ++//go:linkname SHA256 crypto/internal/backend.SHA256 ++func SHA256(p []byte) (sum [32]byte) ++//go:linkname SHA384 crypto/internal/backend.SHA384 ++func SHA384(p []byte) (sum [48]byte) ++//go:linkname SHA512 crypto/internal/backend.SHA512 ++func SHA512(p []byte) (sum [64]byte) ++//go:linkname SHA3_224 crypto/internal/backend.SHA3_224 ++//go:noescape ++func SHA3_224(p []byte) (sum [28]byte) ++//go:linkname SHA3_256 crypto/internal/backend.SHA3_256 ++//go:noescape ++func SHA3_256(p []byte) (sum [32]byte) ++//go:linkname SHA3_384 crypto/internal/backend.SHA3_384 ++//go:noescape ++func SHA3_384(p []byte) (sum [48]byte) ++//go:linkname SHA3_512 crypto/internal/backend.SHA3_512 ++//go:noescape ++func SHA3_512(p []byte) (sum [64]byte) ++//go:linkname NewHMAC crypto/internal/backend.NewHMAC ++func NewHMAC(h func() hash.Hash, key []byte) hash.Hash ++//go:linkname NewAESCipher crypto/internal/backend.NewAESCipher ++func NewAESCipher(key []byte) (cipher.Block, error) ++//go:linkname NewGCMTLS crypto/internal/backend.NewGCMTLS ++func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) ++//go:linkname SupportsHKDF crypto/internal/backend.SupportsHKDF ++func SupportsHKDF() bool ++//go:linkname ExpandHKDF crypto/internal/backend.ExpandHKDF ++func ExpandHKDF(h func() hash.Hash, pseudorandomKey, info []byte) (io.Reader, error) ++//go:linkname ExtractHKDF crypto/internal/backend.ExtractHKDF ++func ExtractHKDF(h func() hash.Hash, secret, salt []byte) ([]byte, error) ++//go:linkname SupportsTLS1PRF crypto/internal/backend.SupportsTLS1PRF ++func SupportsTLS1PRF() bool ++//go:linkname TLS1PRF crypto/internal/backend.TLS1PRF ++func TLS1PRF(result, secret, label, seed []byte, h func() hash.Hash) error ++//go:linkname SupportsPBKDF2 crypto/internal/backend.SupportsPBKDF2 ++func SupportsPBKDF2() bool ++//go:linkname PBKDF2 crypto/internal/backend.PBKDF2 ++func PBKDF2(password, salt []byte, iter, keyLen int, h func() hash.Hash) ([]byte, error) ++//go:linkname NewMD4 crypto/internal/backend.NewMD4 ++func NewMD4() hash.Hash ++//go:linkname MD4 crypto/internal/backend.MD4 ++func MD4(p []byte) (sum [16]byte) diff --git a/xcryptofork/patches/0001-Add-backends-with-sha3_356.patch b/xcryptofork/patches/0001-Add-backends-with-sha3_356.patch deleted file mode 100644 index 0192352c..00000000 --- a/xcryptofork/patches/0001-Add-backends-with-sha3_356.patch +++ /dev/null @@ -1,130 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Davis Goodin -Date: Mon, 28 Aug 2023 16:39:33 -0500 -Subject: [PATCH] Add sha3 with placeholder backend for compilation - ---- - hkdf/hkdf.go | 2 ++ - internal/backend/nobackend.go | 47 +++++++++++++++++++++++++++++++++++ - sha3/hashes.go | 19 ++++++++++++++ - 3 files changed, 68 insertions(+) - create mode 100644 internal/backend/nobackend.go - -diff --git a/hkdf/hkdf.go b/hkdf/hkdf.go -index dda3f143bec506..8700f761b3b6de 100644 ---- a/hkdf/hkdf.go -+++ b/hkdf/hkdf.go -@@ -13,6 +13,7 @@ package hkdf // import "golang.org/x/crypto/hkdf" - import ( - "crypto/hmac" - "errors" -+ "fmt" - "hash" - "io" - ) -@@ -88,6 +89,7 @@ func Expand(hash func() hash.Hash, pseudorandomKey, info []byte) io.Reader { - // New returns a Reader, from which keys can be read, using the given hash, - // secret, salt and context info. Salt and info can be nil. - func New(hash func() hash.Hash, secret, salt, info []byte) io.Reader { -+ fmt.Println("This is a not-so-subtle modification to x/crypto that you got automatically!") - prk := Extract(hash, secret, salt) - return Expand(hash, prk, info) - } -diff --git a/internal/backend/nobackend.go b/internal/backend/nobackend.go -new file mode 100644 -index 00000000000000..c0bc5b36756a20 ---- /dev/null -+++ b/internal/backend/nobackend.go -@@ -0,0 +1,47 @@ -+// Generated code. DO NOT EDIT. -+ -+package backend -+ -+import ( -+ "crypto" -+ "crypto/cipher" -+ "hash" -+ "io" -+) -+ -+const Enabled = false -+ -+func NewSHA1() hash.Hash { panic("cryptobackend: not available") } -+func NewSHA224() hash.Hash { panic("cryptobackend: not available") } -+func NewSHA256() hash.Hash { panic("cryptobackend: not available") } -+func NewSHA384() hash.Hash { panic("cryptobackend: not available") } -+func NewSHA512() hash.Hash { panic("cryptobackend: not available") } -+func NewSHA3_224() hash.Hash { panic("cryptobackend: not available") } -+func NewSHA3_256() hash.Hash { panic("cryptobackend: not available") } -+func NewSHA3_384() hash.Hash { panic("cryptobackend: not available") } -+func NewSHA3_512() hash.Hash { panic("cryptobackend: not available") } -+ -+func SHA1(p []byte) (sum [20]byte) { panic("cryptobackend: not available") } -+func SHA224(p []byte) (sum [28]byte) { panic("cryptobackend: not available") } -+func SHA256(p []byte) (sum [32]byte) { panic("cryptobackend: not available") } -+func SHA384(p []byte) (sum [48]byte) { panic("cryptobackend: not available") } -+func SHA512(p []byte) (sum [64]byte) { panic("cryptobackend: not available") } -+func SHA3_224(p []byte) (sum [28]byte) { panic("cryptobackend: not available") } -+func SHA3_256(p []byte) (sum [32]byte) { panic("cryptobackend: not available") } -+func SHA3_384(p []byte) (sum [48]byte) { panic("cryptobackend: not available") } -+func SHA3_512(p []byte) (sum [64]byte) { panic("cryptobackend: not available") } -+ -+func SupportsHash(h crypto.Hash) bool { panic("cryptobackend: not available") } -+ -+func NewHMAC(h func() hash.Hash, key []byte) hash.Hash { panic("cryptobackend: not available") } -+ -+func NewAESCipher(key []byte) (cipher.Block, error) { panic("cryptobackend: not available") } -+func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) { panic("cryptobackend: not available") } -+ -+func ExpandHKDF(h func() hash.Hash, pseudorandomKey, info []byte) (io.Reader, error) { -+ panic("cryptobackend: not available") -+} -+ -+func ExtractHKDF(h func() hash.Hash, secret, salt []byte) ([]byte, error) { -+ panic("cryptobackend: not available") -+} -diff --git a/sha3/hashes.go b/sha3/hashes.go -index 0d8043fd2a173d..28b94b633dc67e 100644 ---- a/sha3/hashes.go -+++ b/sha3/hashes.go -@@ -9,7 +9,10 @@ package sha3 - // bytes. - - import ( -+ "crypto" - "hash" -+ -+ "golang.org/x/crypto/internal/backend" - ) - - // New224 creates a new SHA3-224 hash. -@@ -26,6 +29,14 @@ func New224() hash.Hash { - // Its generic security strength is 256 bits against preimage attacks, - // and 128 bits against collision attacks. - func New256() hash.Hash { -+ if backend.Enabled { -+ if backend.SupportsHash(crypto.SHA3_256) { -+ println("using backend sha3_256") -+ return backend.NewSHA3_256() -+ } else { -+ println("backend doesn't support sha3_256") -+ } -+ } - if h := new256Asm(); h != nil { - return h - } -@@ -74,6 +85,14 @@ func Sum224(data []byte) (digest [28]byte) { - - // Sum256 returns the SHA3-256 digest of the data. - func Sum256(data []byte) (digest [32]byte) { -+ if backend.Enabled { -+ if backend.SupportsHash(crypto.SHA3_256) { -+ println("using backend sha3_256") -+ return backend.SHA3_256(data) -+ } else { -+ println("backend doesn't support sha3_256") -+ } -+ } - h := New256() - h.Write(data) - h.Sum(digest[:0]) diff --git a/xcryptofork/patches/0002-Implement-algorithms-using-backend-proxies.patch b/xcryptofork/patches/0002-Implement-algorithms-using-backend-proxies.patch new file mode 100644 index 00000000..9349997b --- /dev/null +++ b/xcryptofork/patches/0002-Implement-algorithms-using-backend-proxies.patch @@ -0,0 +1,214 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Davis Goodin +Date: Thu, 28 Sep 2023 23:12:08 -0500 +Subject: [PATCH] Implement algorithms using backend proxies + +Implement: + +* hkdf +* md4 +* pbkdf2 +* several sha3 hashes +--- + hkdf/hkdf.go | 16 ++++++++++++++++ + md4/md4.go | 9 +++++++++ + pbkdf2/pbkdf2.go | 9 +++++++++ + sha3/hashes.go | 27 +++++++++++++++++++++++++++ + 4 files changed, 61 insertions(+) + +diff --git a/hkdf/hkdf.go b/hkdf/hkdf.go +index dda3f143bec506..13180f5c71a24d 100644 +--- a/hkdf/hkdf.go ++++ b/hkdf/hkdf.go +@@ -15,6 +15,8 @@ import ( + "errors" + "hash" + "io" ++ ++ "golang.org/x/crypto/internal/backend" + ) + + // Extract generates a pseudorandom key for use with Expand from an input secret +@@ -24,6 +26,13 @@ import ( + // Expand invocations and different context values. Most common scenarios, + // including the generation of multiple keys, should use New instead. + func Extract(hash func() hash.Hash, secret, salt []byte) []byte { ++ if backend.Enabled && backend.SupportsHKDF() { ++ key, err := backend.ExtractHKDF(hash, secret, salt) ++ if err != nil { ++ panic("x/crypto/hkdf: " + err.Error()) ++ } ++ return key ++ } + if salt == nil { + salt = make([]byte, hash().Size()) + } +@@ -81,6 +90,13 @@ func (f *hkdf) Read(p []byte) (int, error) { + // random or pseudorandom cryptographically strong key. See RFC 5869, Section + // 3.3. Most common scenarios will want to use New instead. + func Expand(hash func() hash.Hash, pseudorandomKey, info []byte) io.Reader { ++ if backend.Enabled && backend.SupportsHKDF() { ++ r, err := backend.ExpandHKDF(hash, pseudorandomKey, info) ++ if err != nil { ++ panic("x/crypto/hkdf: " + err.Error()) ++ } ++ return r ++ } + expander := hmac.New(hash, pseudorandomKey) + return &hkdf{expander, expander.Size(), info, 1, nil, nil} + } +diff --git a/md4/md4.go b/md4/md4.go +index 59d3480693050f..846043f0a5915d 100644 +--- a/md4/md4.go ++++ b/md4/md4.go +@@ -12,6 +12,8 @@ package md4 // import "golang.org/x/crypto/md4" + import ( + "crypto" + "hash" ++ ++ "golang.org/x/crypto/internal/backend" + ) + + func init() { +@@ -51,6 +53,9 @@ func (d *digest) Reset() { + + // New returns a new hash.Hash computing the MD4 checksum. + func New() hash.Hash { ++ if backend.Enabled && backend.SupportsHash(crypto.MD4) { ++ return backend.NewMD4() ++ } + d := new(digest) + d.Reset() + return d +@@ -87,6 +92,10 @@ func (d *digest) Write(p []byte) (nn int, err error) { + } + + func (d0 *digest) Sum(in []byte) []byte { ++ if backend.Enabled && backend.SupportsHash(crypto.MD4) { ++ result := backend.MD4(in) ++ return result[:] ++ } + // Make a copy of d0, so that caller can keep writing and summing. + d := new(digest) + *d = *d0 +diff --git a/pbkdf2/pbkdf2.go b/pbkdf2/pbkdf2.go +index 904b57e01d7a50..3d432e5c013be5 100644 +--- a/pbkdf2/pbkdf2.go ++++ b/pbkdf2/pbkdf2.go +@@ -21,6 +21,8 @@ package pbkdf2 // import "golang.org/x/crypto/pbkdf2" + import ( + "crypto/hmac" + "hash" ++ ++ "golang.org/x/crypto/internal/backend" + ) + + // Key derives a key from the password, salt and iteration count, returning a +@@ -40,6 +42,13 @@ import ( + // Using a higher iteration count will increase the cost of an exhaustive + // search but will also make derivation proportionally slower. + func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte { ++ if backend.Enabled && backend.SupportsPBKDF2() { ++ key, err := backend.PBKDF2(password, salt, iter, keyLen, h) ++ if err != nil { ++ panic("x/crypto/pbkdf2: " + err.Error()) ++ } ++ return key ++ } + prf := hmac.New(h, password) + hashLen := prf.Size() + numBlocks := (keyLen + hashLen - 1) / hashLen +diff --git a/sha3/hashes.go b/sha3/hashes.go +index 0d8043fd2a173d..f7640244595139 100644 +--- a/sha3/hashes.go ++++ b/sha3/hashes.go +@@ -9,13 +9,19 @@ package sha3 + // bytes. + + import ( ++ "crypto" + "hash" ++ ++ "golang.org/x/crypto/internal/backend" + ) + + // New224 creates a new SHA3-224 hash. + // Its generic security strength is 224 bits against preimage attacks, + // and 112 bits against collision attacks. + func New224() hash.Hash { ++ if backend.Enabled && backend.SupportsHash(crypto.SHA3_224) { ++ return backend.NewSHA3_224() ++ } + if h := new224Asm(); h != nil { + return h + } +@@ -26,6 +32,9 @@ func New224() hash.Hash { + // Its generic security strength is 256 bits against preimage attacks, + // and 128 bits against collision attacks. + func New256() hash.Hash { ++ if backend.Enabled && backend.SupportsHash(crypto.SHA3_256) { ++ return backend.NewSHA3_256() ++ } + if h := new256Asm(); h != nil { + return h + } +@@ -36,6 +45,9 @@ func New256() hash.Hash { + // Its generic security strength is 384 bits against preimage attacks, + // and 192 bits against collision attacks. + func New384() hash.Hash { ++ if backend.Enabled && backend.SupportsHash(crypto.SHA3_384) { ++ return backend.NewSHA3_384() ++ } + if h := new384Asm(); h != nil { + return h + } +@@ -46,6 +58,9 @@ func New384() hash.Hash { + // Its generic security strength is 512 bits against preimage attacks, + // and 256 bits against collision attacks. + func New512() hash.Hash { ++ if backend.Enabled && backend.SupportsHash(crypto.SHA3_512) { ++ return backend.NewSHA3_512() ++ } + if h := new512Asm(); h != nil { + return h + } +@@ -66,6 +81,9 @@ func NewLegacyKeccak512() hash.Hash { return &state{rate: 72, outputLen: 64, dsb + + // Sum224 returns the SHA3-224 digest of the data. + func Sum224(data []byte) (digest [28]byte) { ++ if backend.Enabled && backend.SupportsHash(crypto.SHA3_224) { ++ return backend.SHA3_224(data) ++ } + h := New224() + h.Write(data) + h.Sum(digest[:0]) +@@ -74,6 +92,9 @@ func Sum224(data []byte) (digest [28]byte) { + + // Sum256 returns the SHA3-256 digest of the data. + func Sum256(data []byte) (digest [32]byte) { ++ if backend.Enabled && backend.SupportsHash(crypto.SHA3_256) { ++ return backend.SHA3_256(data) ++ } + h := New256() + h.Write(data) + h.Sum(digest[:0]) +@@ -82,6 +103,9 @@ func Sum256(data []byte) (digest [32]byte) { + + // Sum384 returns the SHA3-384 digest of the data. + func Sum384(data []byte) (digest [48]byte) { ++ if backend.Enabled && backend.SupportsHash(crypto.SHA3_384) { ++ return backend.SHA3_384(data) ++ } + h := New384() + h.Write(data) + h.Sum(digest[:0]) +@@ -90,6 +114,9 @@ func Sum384(data []byte) (digest [48]byte) { + + // Sum512 returns the SHA3-512 digest of the data. + func Sum512(data []byte) (digest [64]byte) { ++ if backend.Enabled && backend.SupportsHash(crypto.SHA3_512) { ++ return backend.SHA3_512(data) ++ } + h := New512() + h.Write(data) + h.Sum(digest[:0]) diff --git a/xcryptofork/xcrypto b/xcryptofork/xcrypto new file mode 160000 index 00000000..3f0842a4 --- /dev/null +++ b/xcryptofork/xcrypto @@ -0,0 +1 @@ +Subproject commit 3f0842a46434ea6f56bf6e684c2b83d90e9cff07