diff --git a/.gitmodules b/.gitmodules index 207e9e20..7db0ab04 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 "xcryptofork/xcrypto"] + path = xcryptofork/xcrypto + url = https://go.googlesource.com/crypto 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/xcryptofork/.git-go-patch b/xcryptofork/.git-go-patch new file mode 100644 index 00000000..7370ced3 --- /dev/null +++ b/xcryptofork/.git-go-patch @@ -0,0 +1,6 @@ +{ + "MinimumToolVersion": "v1.0.1", + "SubmoduleDir": "xcrypto", + "PatchesDir": "patches", + "StatusFileDir": "artifacts/go-patch" +} diff --git a/xcryptofork/.gitignore b/xcryptofork/.gitignore new file mode 100644 index 00000000..ee602e1e --- /dev/null +++ b/xcryptofork/.gitignore @@ -0,0 +1,2 @@ +# Ignore patching tool temp files. +artifacts/ diff --git a/xcryptofork/backend.go b/xcryptofork/backend.go new file mode 100644 index 00000000..9d7dcecc --- /dev/null +++ b/xcryptofork/backend.go @@ -0,0 +1,536 @@ +package xcryptofork + +import ( + "errors" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/token" + "io" + "path/filepath" + "strconv" + "strings" + + "github.com/microsoft/go-infra/stringutil" + "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 := stringutil.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/xcryptofork/backend_test.go b/xcryptofork/backend_test.go new file mode 100644 index 00000000..03704eb8 --- /dev/null +++ b/xcryptofork/backend_test.go @@ -0,0 +1,87 @@ +package xcryptofork + +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/xcryptofork/git.go b/xcryptofork/git.go new file mode 100644 index 00000000..2b77f214 --- /dev/null +++ b/xcryptofork/git.go @@ -0,0 +1,59 @@ +package xcryptofork + +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 +} 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/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/testdata/.gitattributes b/xcryptofork/testdata/.gitattributes new file mode 100644 index 00000000..5bbd0017 --- /dev/null +++ b/xcryptofork/testdata/.gitattributes @@ -0,0 +1,2 @@ +# Always use LF to simplify test output checking. +*.go text eol=lf diff --git a/xcryptofork/testdata/derivedapi.golden.go b/xcryptofork/testdata/derivedapi.golden.go new file mode 100644 index 00000000..fd158da0 --- /dev/null +++ b/xcryptofork/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/xcryptofork/testdata/exampleRealBackend/backend_test.go b/xcryptofork/testdata/exampleRealBackend/backend_test.go new file mode 100644 index 00000000..837cff47 --- /dev/null +++ b/xcryptofork/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/xcryptofork/testdata/exampleRealBackend/boring_linux.go b/xcryptofork/testdata/exampleRealBackend/boring_linux.go new file mode 100644 index 00000000..c4449f5d --- /dev/null +++ b/xcryptofork/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/xcryptofork/testdata/exampleRealBackend/cng_windows.go b/xcryptofork/testdata/exampleRealBackend/cng_windows.go new file mode 100644 index 00000000..a75e4664 --- /dev/null +++ b/xcryptofork/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/xcryptofork/testdata/exampleRealBackend/isrequirefips.go b/xcryptofork/testdata/exampleRealBackend/isrequirefips.go new file mode 100644 index 00000000..e5d7570d --- /dev/null +++ b/xcryptofork/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/xcryptofork/testdata/exampleRealBackend/nobackend.go b/xcryptofork/testdata/exampleRealBackend/nobackend.go new file mode 100644 index 00000000..53454725 --- /dev/null +++ b/xcryptofork/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/xcryptofork/testdata/exampleRealBackend/norequirefips.go b/xcryptofork/testdata/exampleRealBackend/norequirefips.go new file mode 100644 index 00000000..26bfb5f6 --- /dev/null +++ b/xcryptofork/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/xcryptofork/testdata/exampleRealBackend/openssl_linux.go b/xcryptofork/testdata/exampleRealBackend/openssl_linux.go new file mode 100644 index 00000000..181fa172 --- /dev/null +++ b/xcryptofork/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/xcryptofork/testdata/proxyDerivedAPI.golden/boring_linux_proxy.go b/xcryptofork/testdata/proxyDerivedAPI.golden/boring_linux_proxy.go new file mode 100644 index 00000000..cc39633f --- /dev/null +++ b/xcryptofork/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/xcryptofork/testdata/proxyDerivedAPI.golden/cng_windows_proxy.go b/xcryptofork/testdata/proxyDerivedAPI.golden/cng_windows_proxy.go new file mode 100644 index 00000000..6372b605 --- /dev/null +++ b/xcryptofork/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/xcryptofork/testdata/proxyDerivedAPI.golden/nobackend_proxy.go b/xcryptofork/testdata/proxyDerivedAPI.golden/nobackend_proxy.go new file mode 100644 index 00000000..43ebb8a4 --- /dev/null +++ b/xcryptofork/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/xcryptofork/testdata/proxyDerivedAPI.golden/openssl_linux_proxy.go b/xcryptofork/testdata/proxyDerivedAPI.golden/openssl_linux_proxy.go new file mode 100644 index 00000000..166aa2be --- /dev/null +++ b/xcryptofork/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) 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