├── .devcontainer
└── devcontainer.json
├── .github
├── dependabot.yml
└── workflows
│ ├── codeql.yml
│ └── test.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── LICENSE
├── Makefile
├── README.md
├── SECURITY.md
├── SUPPORT.md
├── cmd
└── checkheader
│ └── main.go
├── docs
└── go-openssl-compat.md
├── go.mod
├── misc
├── README.md
├── go.mod
├── go.sum
└── xts_test.go
├── openssl
├── aes.go
├── aes_test.go
├── bbig
│ ├── big.go
│ └── bridge
│ │ └── bridge.go
├── big.go
├── ecdh.go
├── ecdh_test.go
├── ecdsa.go
├── ecdsa_test.go
├── evpkey.go
├── goopenssl.c
├── goopenssl.h
├── hmac.go
├── hmac_test.go
├── internal
│ └── subtle
│ │ ├── aliasing.go
│ │ └── aliasing_test.go
├── openssl.go
├── openssl_funcs.h
├── openssl_lock_setup.c
├── openssl_test.go
├── rand.go
├── rand_test.go
├── rsa.go
├── rsa_test.go
├── sha.go
└── sha_test.go
└── scripts
├── openssl-3.cnf
└── openssl.sh
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | // For format details, see https://aka.ms/devcontainer.json.
2 |
3 | {
4 | "name": "Go",
5 | "containerEnv": {
6 | "GO_OPENSSL_VERSION_OVERRIDE": "1.1.1"
7 | },
8 | "features": {
9 | "ghcr.io/devcontainers/features/go:1": {}
10 | },
11 | "onCreateCommand": "sudo sh ${containerWorkspaceFolder}/scripts/openssl.sh ${GO_OPENSSL_VERSION_OVERRIDE}"
12 | }
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # Copyright (c) Microsoft Corporation.
2 | # Licensed under the MIT License.
3 |
4 | version: 2
5 | updates:
6 | - package-ecosystem: github-actions
7 | directory: "/"
8 | schedule:
9 | interval: daily
10 | open-pull-requests-limit: 10
11 | - package-ecosystem: gomod
12 | directory: "/"
13 | schedule:
14 | interval: daily
15 | open-pull-requests-limit: 10
16 |
--------------------------------------------------------------------------------
/.github/workflows/codeql.yml:
--------------------------------------------------------------------------------
1 | name: "CodeQL"
2 |
3 | on:
4 | push:
5 | branches: [ "main" ]
6 | pull_request:
7 | branches: [ "main" ]
8 | schedule:
9 | - cron: 19 21 * * 6
10 |
11 | jobs:
12 | analyze:
13 | name: Analyze
14 | # Runner size impacts CodeQL analysis time. To learn more, please see:
15 | # - https://gh.io/recommended-hardware-resources-for-running-codeql
16 | # - https://gh.io/supported-runners-and-hardware-resources
17 | # - https://gh.io/using-larger-runners
18 | # Consider using larger runners for possible analysis time improvements.
19 | runs-on: ubuntu-22.04
20 | timeout-minutes: 360
21 | permissions:
22 | security-events: write
23 |
24 | strategy:
25 | fail-fast: false
26 | matrix:
27 | language: [ 'c-cpp', 'go' ]
28 |
29 | steps:
30 | - name: Checkout repository
31 | uses: actions/checkout@v4
32 |
33 | # Initializes the CodeQL tools for scanning.
34 | - name: Initialize CodeQL
35 | uses: github/codeql-action/init@v3
36 | with:
37 | languages: ${{ matrix.language }}
38 |
39 | - name: Autobuild
40 | uses: github/codeql-action/autobuild@v3
41 |
42 | - name: Perform CodeQL Analysis
43 | uses: github/codeql-action/analyze@v3
44 | with:
45 | category: /language:${{matrix.language}}
46 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | on: [push, pull_request]
2 | name: Test
3 | jobs:
4 | test:
5 | strategy:
6 | fail-fast: false
7 | matrix:
8 | go-version: [1.17.x]
9 | openssl-version-build: [1.0.2, 1.1.0, 1.1.1, 3.0.1]
10 | openssl-version-test: [1.0.2, 1.1.0, 1.1.1, 3.0.1]
11 | runs-on: ubuntu-20.04
12 | steps:
13 | - name: Install build tools
14 | run: sudo apt-get install -y build-essential
15 | - name: Install Go
16 | uses: actions/setup-go@v2
17 | with:
18 | go-version: ${{ matrix.go-version }}
19 | - name: Checkout code
20 | uses: actions/checkout@v2
21 | - name: Install OpenSSL - Build
22 | run: sudo sh ./scripts/openssl.sh ${{ matrix.openssl-version-build }}
23 | - name: Check headers
24 | working-directory: ./cmd/checkheader
25 | run: go run . --ossl-include /usr/local/src/openssl-${{ matrix.openssl-version-build }}/include ../../openssl/openssl_funcs.h
26 | if: ${{ matrix.openssl-version-build == matrix.openssl-version-test }}
27 | - name: Set OpenSSL config and prove FIPS
28 | run: |
29 | sudo cp ./scripts/openssl-3.cnf /usr/local/ssl/openssl.cnf
30 | go test -v -count 0 ./openssl | grep -q "FIPS enabled: true"
31 | if: ${{ matrix.openssl-version-build == '3.0.1' }}
32 | env:
33 | GO_OPENSSL_VERSION_OVERRIDE: ${{ matrix.openssl-version-build }}
34 | - name: Run Test - Build
35 | # Run each test 10 times so the garbage collector chimes in
36 | # and exercises the multiple finalizers we use.
37 | # This can detect use-after-free and double-free issues.
38 | run: go test -gcflags=all=-d=checkptr -count 10 -v ./...
39 | env:
40 | GO_OPENSSL_VERSION_OVERRIDE: ${{ matrix.openssl-version-build }}
41 | - name: Run Test - Build - No version override
42 | run: go test -gcflags=all=-d=checkptr -v ./...
43 | - name: Run Test - Misc
44 | run: go test -gcflags=all=-d=checkptr -v .
45 | working-directory: ./misc
46 | env:
47 | GO_OPENSSL_VERSION_OVERRIDE: ${{ matrix.openssl-version-build }}
48 | - name: Build Test - Build
49 | run: go test -o test -c ./openssl
50 | if: ${{ matrix.openssl-version-build != matrix.openssl-version-test }}
51 | env:
52 | GO_OPENSSL_VERSION_OVERRIDE: ${{ matrix.openssl-version-build }}
53 | - name: Install OpenSSL - Test
54 | run: sudo sh ./scripts/openssl.sh ${{ matrix.openssl-version-test }}
55 | if: ${{ matrix.openssl-version-build != matrix.openssl-version-test }}
56 | - name: Run Test 2 - Test
57 | run: ./test -test.v
58 | if: ${{ matrix.openssl-version-build != matrix.openssl-version-test }}
59 | env:
60 | GO_OPENSSL_VERSION_OVERRIDE: ${{ matrix.openssl-version-test }}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 | *.o
8 |
9 | # Test binary, built with `go test -c`
10 | *.test
11 |
12 | # Output of the go coverage tool, specifically when used with LiteIDE
13 | *.out
14 |
15 | # Dependency directories (remove the comment below to include it)
16 | # vendor/
17 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Microsoft Open Source Code of Conduct
2 |
3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
4 |
5 | Resources:
6 |
7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Microsoft Corporation.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # Purpose: Makefile for building the C code for CodeQL analysis.
2 | build:
3 | cc -c openssl/*.c
4 | echo "This task is only useful for CodeQL analysis. You don't need to run it."
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # go-crypto-openssl
2 |
3 | [](https://pkg.go.dev/github.com/microsoft/go-crypto-openssl/openssl)
4 |
5 | The `openssl` package implements Go crypto primitives using OpenSSL shared libraries and cgo. When configured correctly, OpenSSL can be executed in FIPS mode, making the `openssl` package FIPS compliant.
6 |
7 | The `openssl` package is designed to be used as a drop-in replacement for the [boring](https://pkg.go.dev/crypto/internal/boring) package in order to facilitate integrating `openssl` inside a forked Go toolchain.
8 |
9 | > [!IMPORTANT]
10 | > Starting with Go 1.21, the [Microsoft build of Go](https://github.com/microsoft/go) uses [golang-fips/openssl](https://github.com/golang-fips/openssl) instead of this module.
11 | >
12 | > This module was used by the Microsoft build of Go in 1.20 and earlier.
13 | > These versions are all unsupported as of 2024-02-06.
14 |
15 | ## Disclaimer
16 |
17 | A program directly or indirectly using this package in FIPS mode can claim it is using a FIPS-certified cryptographic module (OpenSSL), but it can't claim the program as a whole is FIPS certified without passing the certification process, nor claim it is FIPS compliant without ensuring all crypto APIs and workflows are implemented in a FIPS-compliant manner.
18 |
19 | ## Background
20 |
21 | See [the golang-fips/openssl README.md](https://github.com/golang-fips/openssl/blob/v2/README.md) for more information about the history and motivation behind this package.
22 |
23 | ## Features
24 |
25 | ### Multiple OpenSSL versions supported
26 |
27 | The `openssl` package has support for multiple OpenSSL versions.
28 |
29 | OpenSSL versions 1.0.2, 1.1.0, 1.1.1 and 3.0.1 pass a small set of automatic tests that ensure they can be built and that there are no major regressions.
30 | These tests do not validate the cryptographic correctness of the `openssl` package.
31 |
32 | On top of that, the Microsoft CI built and tested a subset of the supported OpenSSL versions as part of the [Microsoft build of Go](https://github.com/microsoft/go) release process.
33 | These tests were much more exhaustive and validated that a specific OpenSSL version can produce working applications.
34 | Only OpenSSL 1.1.1 went through this process.
35 |
36 | > [!NOTE]
37 | > This module is not used by any active branches of the Microsoft build of Go, so those Microsoft CI tests no longer run.
38 | > Active branches use and test [golang-fips/openssl](https://github.com/golang-fips/openssl).
39 |
40 | Versions not listed above are not supported by this module.
41 |
42 | Due to the stronger API stability promises of OpenSSL 3.x, this module may work with untested 3.x versions.
43 |
44 | ### Dynamic OpenSSL loading
45 |
46 | The OpenSSL shared library `libcrypto` is loaded at runtime using [dlopen](https://man7.org/linux/man-pages/man3/dlopen.3.html) when calling `openssl.Init`. Therefore, dlopen's shared library search conventions also apply here.
47 |
48 | The `libcrypto` shared library file name varies among different platforms, so a best effort is done to find and load the right file:
49 |
50 | - The base name is always `libcrypto.so`.
51 | - Well-known version strings are appended to the base name, until the file is found, in the following order: `3` -> `1.1` -> `11` -> `111` -> `1.0.2` -> `1.0.0`.
52 |
53 | This algorithm can be overridden by setting the environment variable `GO_OPENSSL_VERSION_OVERRIDE` to the desired version string. For example, `GO_OPENSSL_VERSION_OVERRIDE="1.1.1k-fips"` makes the runtime look for the shared library `libcrypto.so.1.1.1k-fips` before running the checks for well-known versions.
54 |
55 | ### Building without OpenSSL headers
56 |
57 | The `openssl` package does not use any symbol from the OpenSSL headers. There is no need that have them installed to build an application which imports this library.
58 |
59 | Microsoft CI verifies that all the functions and constants defined in our headers match the ones in the OpenSSL headers for every supported OpenSSL version.
60 |
61 | ### Portable OpenSSL
62 |
63 | The OpenSSL bindings are implemented in such a way that the OpenSSL version used when building a program does not have to match with the OpenSSL version used when running it.
64 |
65 | This feature does not require any additional configuration, but it only works with OpenSSL versions known and supported by the Go toolchain.
66 |
67 | ## Limitations
68 |
69 | OpenSSL is used for a given build only in limited circumstances:
70 |
71 | - The platform must be GOOS=linux.
72 | - The build must have cgo enabled.
73 | - The android build tag must not be specified.
74 |
75 | ## Acknowledgements
76 |
77 | The work done to support FIPS compatibility mode leverages code and ideas from other open-source projects:
78 |
79 | - All crypto stubs are a mirror of Google's [dev.boringcrypto branch](https://github.com/golang/go/tree/dev.boringcrypto) and the release branch ports of that branch.
80 | - The mapping between BoringSSL and OpenSSL APIs is taken from Fedora's [Go fork](https://pagure.io/go).
81 | - The portable OpenSSL implementation is ported from Microsoft's [.NET runtime](https://github.com/dotnet/runtime) cryptography module.
82 |
83 | ## Contributing
84 |
85 | This project welcomes contributions and suggestions. Most contributions require you to agree to a
86 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
87 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
88 |
89 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide
90 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
91 | provided by the bot. You will only need to do this once across all repos using our CLA.
92 |
93 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
94 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
95 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
96 |
97 | ## Trademarks
98 |
99 | This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft
100 | trademarks or logos is subject to and must follow
101 | [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).
102 | Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
103 | Any use of third-party trademarks or logos are subject to those third-party's policies.
104 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Security
4 |
5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
6 |
7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below.
8 |
9 | ## Reporting Security Issues
10 |
11 | **Please do not report security vulnerabilities through public GitHub issues.**
12 |
13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).
14 |
15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).
16 |
17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
18 |
19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
20 |
21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
22 | * Full paths of source file(s) related to the manifestation of the issue
23 | * The location of the affected source code (tag/branch/commit or direct URL)
24 | * Any special configuration required to reproduce the issue
25 | * Step-by-step instructions to reproduce the issue
26 | * Proof-of-concept or exploit code (if possible)
27 | * Impact of the issue, including how an attacker might exploit the issue
28 |
29 | This information will help us triage your report more quickly.
30 |
31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs.
32 |
33 | ## Preferred Languages
34 |
35 | We prefer all communications to be in English.
36 |
37 | ## Policy
38 |
39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
40 |
41 |
--------------------------------------------------------------------------------
/SUPPORT.md:
--------------------------------------------------------------------------------
1 | # Support
2 |
3 | For help and questions about the Go programming language and tools, visit the official website: .
4 |
5 | ## How to file issues and get help
6 |
7 | This project uses GitHub Issues to track bugs and feature requests. Please search the existing
8 | issues before filing new issues to avoid duplicates. For new issues, [file your bug or
9 | feature request as a new Issue][file].
10 |
11 | For help and questions about the microsoft/go project, please [file an issue][file].
12 |
13 | ## Microsoft Support Policy
14 |
15 | Support for this project is limited to the resources listed above.
16 |
17 | [file]: https://github.com/microsoft/go/issues/new/choose
18 |
--------------------------------------------------------------------------------
/cmd/checkheader/main.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | package main
5 |
6 | import (
7 | "bufio"
8 | "flag"
9 | "fmt"
10 | "io"
11 | "log"
12 | "os"
13 | "os/exec"
14 | "strings"
15 | )
16 |
17 | // The checking program implements all the checks at compile time, so there is no need to run it.
18 | // The generation of the checking program follows these rules:
19 | // - Lines are added in order of appearance.
20 | // - Blank lines are discarded.
21 | // - Comments are discarded unless they contain a C directive, i.e #include, #if or #endif.
22 | // - Typedefs following this pattern "typedef void* GO_%name%_PTR" are translated into "#define %name% GO_%name%_PTR".
23 | // - Enums are validated against their definition in the OpenSSL headers. Example:
24 | // "enum { GO_EVP_CTRL_GCM_SET_TAG = 0x11 }" => "_Static_assert(EVP_CTRL_GCM_SET_TAG == 0x11);"
25 | // - Function macros are validated against their definition in the OpenSSL headers. Example:
26 | // "DEFINEFUNC(int, RAND_bytes, (unsigned char *a0, int a1), (a0, a1))" => "int(*__check_0)(unsigned char *, int) = RAND_bytes;"
27 | // - Function macros can be excluded when checking old OpenSSL versions by prepending '/*check:from=%version%*/', %version% being a version string such as '1.1.1' or '3.0.0'.
28 |
29 | const description = `
30 | Example: A check operation:
31 | go run ./cmd/checkheader --ossl-include /usr/local/src/openssl-1.1.1/include ./openssl/openssl_funcs.h
32 | Checkheader generates a C program, the compilation of which verifies types and functions defined in the target
33 | header file match the definitions in --ossl-include.
34 | `
35 |
36 | var osslInclude = flag.String("ossl-include", "", "OpenSSL include directory. Required.")
37 | var work = flag.Bool("work", false, "print the name of the temporary C program file and do not delete it when exiting.")
38 |
39 | func main() {
40 | flag.Usage = func() {
41 | fmt.Fprintf(flag.CommandLine.Output(), "\nUsage:\n")
42 | flag.PrintDefaults()
43 | fmt.Fprintf(flag.CommandLine.Output(), "%s\n\n", description)
44 | }
45 | flag.Parse()
46 | if flag.NArg() != 1 {
47 | flag.Usage()
48 | os.Exit(1)
49 | }
50 | if *osslInclude == "" {
51 | fmt.Fprintln(flag.CommandLine.Output(), "required flag not provided: --ossl-include")
52 | flag.Usage()
53 | os.Exit(1)
54 | }
55 | if _, err := os.Stat(*osslInclude); err != nil {
56 | log.Fatalf("OpenSSL include directory not found: %v\n", err)
57 | }
58 | s, err := generate(flag.Arg(0))
59 | if err != nil {
60 | log.Fatal(err)
61 | }
62 | err = gccRun(s)
63 | if err != nil {
64 | log.Fatal(err)
65 | }
66 | }
67 |
68 | func gccRun(program string) error {
69 | f, err := os.CreateTemp("", "go-crypto-openssl-*.c")
70 | if err != nil {
71 | log.Fatal(err)
72 | }
73 | name := f.Name()
74 | if !*work {
75 | defer os.Remove(name)
76 | } else {
77 | defer log.Println(name)
78 | }
79 | if _, err = f.WriteString(program); err != nil {
80 | log.Fatal(err)
81 | }
82 | if err := f.Close(); err != nil {
83 | log.Fatal(err)
84 | }
85 | // gcc will fail to compile the generated C header if
86 | // any of the static checks fails. If it succeed it means
87 | // the checked header matches the OpenSSL definitions.
88 | p := exec.Command("gcc",
89 | "-c", // skip linking
90 | "-Werror", // promote all warnings to errors
91 | "-Wno-deprecated-declarations", // deprecation warnings are expected
92 | "-isystem", *osslInclude, // OpenSSL include from --ossl-include must be preferred over system includes
93 | "-o", "/dev/null", // discard output
94 | name)
95 | p.Stdout = os.Stdout
96 | p.Stderr = os.Stderr
97 | return p.Run()
98 | }
99 |
100 | func generate(header string) (string, error) {
101 | f, err := os.Open(header)
102 | if err != nil {
103 | return "", err
104 | }
105 | defer f.Close()
106 | var b strings.Builder
107 | sc := bufio.NewScanner(f)
108 | var i int
109 | var enum bool
110 | for sc.Scan() {
111 | l := strings.TrimSpace(sc.Text())
112 | if enum {
113 | if !strings.HasPrefix(l, "}") {
114 | tryConvertEnum(&b, l)
115 | } else {
116 | enum = false
117 | }
118 | continue
119 | }
120 | if strings.HasPrefix(l, "enum {") || strings.HasPrefix(l, "typedef enum {") {
121 | enum = true
122 | continue
123 | }
124 | if tryConvertDirective(&b, l) {
125 | continue
126 | }
127 | if tryConvertTypedef(&b, l) {
128 | continue
129 | }
130 | if tryConvertDefineFunc(&b, l, i) {
131 | i++
132 | }
133 | }
134 | if err := sc.Err(); err != nil {
135 | return "", err
136 | }
137 | return b.String(), nil
138 | }
139 |
140 | func tryConvertDirective(w io.Writer, l string) bool {
141 | if strings.HasPrefix(l, "// #") {
142 | fmt.Fprintln(w, l[len("// "):])
143 | return true
144 | }
145 | return false
146 | }
147 |
148 | // tryConvertTypedef converts a typedef contained in the line l
149 | // into a #define pointing to the corresponding OpenSSL type.
150 | // Only void* typedefs starting with GO_ are converted.
151 | // If l does not contain a typedef it does nothing and returns false.
152 | func tryConvertTypedef(w io.Writer, l string) bool {
153 | if !strings.HasPrefix(l, "typedef void* GO_") {
154 | return false
155 | }
156 | // Replace custom opaque pointer typedef with the proper OpenSSL type
157 | // so gcc does not complain about pointer mismatch.
158 | i1 := strings.Index(l, "GO_")
159 | i2 := strings.Index(l, "_PTR")
160 | if i2 < 0 {
161 | log.Println("unexpected line in typedef: " + l)
162 | return false
163 | }
164 | name := l[i1+len("GO_") : i2]
165 | fmt.Fprintf(w, "#define GO_%s_PTR %s*\n", name, name)
166 | return true
167 | }
168 |
169 | // tryConvertEnum adds a static check which verifies that
170 | // the enum contained in the line l
171 | // matches the corresponding OpenSSL value.
172 | // Only enum names starting with GO_ are converted.
173 | func tryConvertEnum(w io.Writer, l string) {
174 | if !strings.HasPrefix(l, "GO_") {
175 | return
176 | }
177 | if l[len(l)-1] == ',' {
178 | l = l[:len(l)-1]
179 | }
180 | split := strings.SplitN(l, " = ", 2)
181 | if len(split) < 2 {
182 | log.Printf("unexpected enum definition in function line: %s\n", l)
183 | return
184 | }
185 | name := split[0][len("GO_"):]
186 | fmt.Fprintf(w, "#ifdef %s\n", name)
187 | fmt.Fprintf(w, "_Static_assert(%s == %s, \"%s\");\n", name, split[1], name)
188 | fmt.Fprintln(w, "#endif")
189 | }
190 |
191 | // tryConvertDefineFunc adds a static check which verifies that
192 | // the function definition macro contained in the line l
193 | // matches the corresponding OpenSSL function signature.
194 | // If l does not contain a function definition macro
195 | // it does nothing and returns false.
196 | // i is used to create a unique name: if tryConvertDefineFunc returns true,
197 | // the same value of i must not be passed again in a future call.
198 | // The value of i should be generated by a counter.
199 | func tryConvertDefineFunc(w io.Writer, l string, i int) bool {
200 | var versionCond string
201 | if strings.HasPrefix(l, "/*check:from=") {
202 | i1 := strings.Index(l, "=")
203 | i2 := strings.Index(l, "*/")
204 | if i1 < 0 || i2 < 0 {
205 | log.Fatalln("unexpected 'check:from' condition: " + l)
206 | }
207 | from := l[i1+1 : i2]
208 | switch from {
209 | case "1.1.0":
210 | versionCond = "OPENSSL_VERSION_NUMBER >= 0x10100000L"
211 | case "1.1.1":
212 | versionCond = "OPENSSL_VERSION_NUMBER >= 0x10101000L"
213 | case "3.0.0":
214 | versionCond = "OPENSSL_VERSION_NUMBER >= 0x30000000L"
215 | default:
216 | log.Println("unexpected 'check:from' version" + l)
217 | return false
218 | }
219 | if l[i2+2] != ' ' {
220 | log.Fatalln("missing space between 'check:from' condition and function macro: " + l)
221 | }
222 | l = l[i2+3:]
223 | }
224 | if !strings.HasPrefix(l, "DEFINEFUNC") {
225 | return false
226 | }
227 | i1 := strings.IndexByte(l, '(')
228 | // The first ")," match is always the end of the argument list parameter.
229 | // We are not interested in the last parameter and parsing them would complicate the algorithm.
230 | // Matching against ')' is not enough as it also appears when the argument list parameter contains function pointers.
231 | i2 := strings.Index(l, "),")
232 | if i1 < 0 || i2 < 0 {
233 | log.Println("unexpected argument list in function line: " + l)
234 | return false
235 | }
236 | subs := l[i1+1 : i2+1]
237 | writeCheck := func(ret, name, args string) {
238 | fmt.Fprintf(w, "%s(*__check_%d)%s = %s;\n", ret, i, args, name)
239 | }
240 | writeDefineFunc := func(cond string) {
241 | args := strings.SplitN(subs, ",", 3)
242 | if len(args) < 3 {
243 | log.Printf("wrong number of function macro arguments in line: %s\n", l)
244 | return
245 | }
246 | fnret, fnname, fnargs := args[0], args[1], args[2]
247 | if cond != "" {
248 | fmt.Fprintf(w, "#if %s\n", cond)
249 | }
250 | if fnret == "" || fnname == "" || fnargs == "" {
251 | log.Printf("empty function macro arguments in line: %s\n", l)
252 | return
253 | }
254 | writeCheck(fnret, fnname, fnargs)
255 | if cond != "" {
256 | fmt.Fprintln(w, "#endif")
257 | }
258 | }
259 | writeDefineFuncRename := func(cond string) {
260 | args := strings.SplitN(subs, ",", 4)
261 | if len(args) < 4 {
262 | log.Printf("wrong number of function macro arguments in line: %s\n", l)
263 | return
264 | }
265 | fnret, fnname, fnoldname, fnargs := args[0], args[1], args[2], args[3]
266 | if fnret == "" || fnname == "" || fnoldname == "" || fnargs == "" {
267 | log.Printf("empty function macro arguments in line: %s\n", l)
268 | return
269 | }
270 | fmt.Fprintf(w, "#if %s\n", cond)
271 | writeCheck(fnret, fnoldname, fnargs)
272 | fmt.Fprintln(w, "#else")
273 | writeCheck(fnret, fnname, fnargs)
274 | fmt.Fprintln(w, "#endif")
275 | }
276 | if versionCond != "" {
277 | fmt.Fprintf(w, "#if %s\n", versionCond)
278 | }
279 | switch l[:i1] {
280 | case "DEFINEFUNC":
281 | writeDefineFunc("")
282 | case "DEFINEFUNC_LEGACY_1_0":
283 | writeDefineFunc("OPENSSL_VERSION_NUMBER < 0x10100000L")
284 | case "DEFINEFUNC_LEGACY_1":
285 | writeDefineFunc("OPENSSL_VERSION_NUMBER < 0x30000000L")
286 | case "DEFINEFUNC_1_1":
287 | writeDefineFunc("OPENSSL_VERSION_NUMBER >= 0x10100000L")
288 | case "DEFINEFUNC_3_0":
289 | writeDefineFunc("OPENSSL_VERSION_NUMBER >= 0x30000000L")
290 | case "DEFINEFUNC_RENAMED_1_1":
291 | writeDefineFuncRename("OPENSSL_VERSION_NUMBER < 0x10100000L")
292 | case "DEFINEFUNC_RENAMED_3_0":
293 | writeDefineFuncRename("OPENSSL_VERSION_NUMBER < 0x30000000L")
294 | default:
295 | log.Printf("unexpected function macro in line: %s\n", l)
296 | }
297 | if versionCond != "" {
298 | fmt.Fprintln(w, "#endif")
299 | }
300 | return true
301 | }
302 |
--------------------------------------------------------------------------------
/docs/go-openssl-compat.md:
--------------------------------------------------------------------------------
1 | # Compatibility of dev.boringcrypto and OpenSSL
2 |
3 | This document lists implementation decisions taken to smooth out differences between dev.boringcrypto expectations and OpenSSL behavior.
4 |
5 | ## Background
6 |
7 | dev.boringcrypto branch delegates core cryptographic algorithms to BoringSSL. Go does not support pluggable crypto backends, so in order to support BoringSSL, the BoringCrypto branch contains non-trivial modifications to several `crypto` packages. These modifications are not generic abstractions over crypto algorithms as they specifically target BoringSSL compatibility.
8 |
9 | The BoringSSL library was forked from OpenSSL 1.0.2 beta, a version that `go-crypto-openssl` supports and that already makes heavy use of the EVP interface. This fact facilitates the translation from BoringSSL to OpenSSL functions. Yet, dev.boringcrypto implements some algorithms using functions that are either deprecated or do not exist in the newer OpenSSL versions we support.
10 |
11 | In this situation where there is no direct mapping, we try to combine several OpenSSL functions so the security is not compromised, with a trade-off between speed and maintainability.
12 |
13 | ## Implementation decisions
14 |
15 | ### AES-GCM encryption in TLS mode
16 |
17 | #### Background
18 |
19 | AES-GCM with Additional Data (aka AEAD) is a symmetric block cipher whose text-book encryption inputs are:
20 | - initialization vector (aka nonce or iv)
21 | - plaintext
22 | - additional authenticated data
23 |
24 | And the outputs are:
25 | - ciphertext
26 | - authentication tag
27 |
28 | Go abstracts this algorithm using the `Seal` method in the [cipher.AEAD](https://pkg.go.dev/crypto/cipher#AEAD) interface:
29 |
30 | `Seal(dst, nonce, plaintext, additionalData []byte) []byte`
31 |
32 | > `dst` is just a backing memory buffer to reduce allocations. The returned byte slice is `dst` with the ciphertext and the authentication tag appended.
33 |
34 | When AES-GCM is used to encrypt a TLS payload (aka TLS mode), FIPS 140-2 IG A.5 ["Key/IV Pair Uniqueness * Requirements from SP 800-38D"](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf), and the general security consensus, requires constructing the IV parameter deterministically by concatenating two fields:
35 | - Fixed 4 bytes field which identifies the encryption context and is reused in different Seal operations using the same key.
36 | - Explicit 8 bytes field which identifies the Seal operation within an encryption context. This field must not be reused in the same encryption context and should be set using a counter incremented in every Seal operation within an encryption context.
37 |
38 | The [FIPS 140-2 Implementation Guidance](https://csrc.nist.gov/csrc/media/projects/cryptographic-module-validation-program/documents/fips140-2/fips1402ig.pdf) requires that the explicit IV counter does not exceed the 2^64-1 value.
39 |
40 | As it is implemented in BoringCrypto, the Go TLS stack is responsible for constructing a valid, unique Key/IV pair. This is passed to the `Seal` method (backed by BoringSSL or OpenSSL) with the expectation that the nonce (IV) is honored.
41 |
42 | #### BoringSSL
43 |
44 | BoringSSL implements the AES-GCM TLS encryption using the [EVP_AEAD_CTX_seal](https://man.openbsd.org/EVP_AEAD_CTX_seal.3) one-shot function, which has a one-to-one mapping with Go's `Seal` method and also enforces that the provided nonce matches the FIPS 140-2 IG A.5 requirements. So, BoringSSL honors Go's IV and at the same time ensures it is secure.
45 |
46 | #### OpenSSL
47 |
48 | OpenSSL does not provide the `EVP_AEAD_CTX_seal` function, so AES-GCM should be implemented using several `EVP_ENCRYPT*` functions, as described [here](https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption). The problem with this recipe is that it does not enforce the additional TLS requirements: it just blindly uses whatever IV is passed.
49 |
50 | This is because OpenSSL doesn't expect that recipe to be used for AES-GCM TLS encryption. It provides instead a control parameter `EVP_CTRL_AEAD_TLS1_AAD` that when applied to an encryption context, makes it work in TLS-mode and completely changes the encryption workflow (more details [here](https://beta.openssl.org/docs/manmaster/man3/EVP_CipherInit_ex.html#tlsivfixed-OSSL_CIPHER_PARAM_AEAD_TLS1_IV_FIXED-octet-string)).
51 |
52 | The main difference is that the explicit IV field is no longer managed by the caller but by the OpenSSL encryption context, incrementing it on every Seal operation and applying some additional checks. This means that OpenSSL won't honor the IV constructed by Go, and therefore it will make Go send corrupted packages to the wire.
53 |
54 | #### Assumptions
55 |
56 | 1. Go constructs IVs that are FIPS 140-2 compliant. BoringCrypto also works under this assumption.
57 |
58 | #### Options
59 |
60 | 1. Use OpenSSL AES-GCM in TLS mode, which means IV construction is secured inside OpenSSL context but Go expectations are not met. Therefore, it requires the following changes to Go TLS stack:
61 | - In `go-crypto-openssl`, change `NewGCMTLS` to accept the 4 bytes fixed IV field.
62 | - In `crypto/tls`:
63 | - Define a new AEAD-like interface, i.e. `AEAD2`, whose `Seal` method does not accept an IV and returns a byte slice with iv+ciphertext+tag.
64 | - Construct the AES-GCM TLS cipher using the new method.
65 | - Don't generate an IV when the cipher implements the AEAD2 interface.
66 | - Use the AEAD2 interface if available when encrypting.
67 | - Test and validate the new path.
68 |
69 | 2. Use OpenSSL AES-GCM in non-TLS mode, which means Go constructs the IV and OpenSSL honors it, but we miss some required security checks. Therefore, we would have to implement them ourselves, namely:
70 | - Enforce strictly monotonically increasing explicit nonces.
71 | - Enforce explicit nonce values to be less than 2^64 - 1.
72 | - Test the new checks.
73 |
74 | #### Resolution
75 |
76 | We will implement option 2.
77 |
78 | Reasoning:
79 | - The additional security checks are easy to implement.
80 | - The changes are contained in `go-crypto-openssl`.
81 | - Option 1 would require non-trivial patching of the Go TLS stack, which we would need to keep up-to-date when `crypto/tls` changes.
82 | - Option 1 has higher chances to introduce a behavior chance or a security bug.
83 |
84 | Drawbacks:
85 | - OpenSSL might implement additional IV checks in future versions when running in TLS mode, and we will not benefit from them. We will have to keep an eye out for changes in this area.
86 | - Adding security checks goes outside the ideal scope of `go-crypto-openssl`, which is just to act at as a thin layer between Go and OpenSSL APIs without real security knowledge.
87 |
88 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/microsoft/go-crypto-openssl
2 |
3 | go 1.17
4 |
--------------------------------------------------------------------------------
/misc/README.md:
--------------------------------------------------------------------------------
1 | # misc
2 |
3 | This folder contains tests which depend on external modules.
4 | These are in a separate module to avoid polluting the main module dependency graph.
5 |
--------------------------------------------------------------------------------
/misc/go.mod:
--------------------------------------------------------------------------------
1 | module misc
2 |
3 | go 1.16
4 |
5 | require (
6 | github.com/microsoft/go-crypto-openssl v0.2.5
7 | golang.org/x/crypto v0.5.0
8 | )
9 |
10 | replace github.com/microsoft/go-crypto-openssl => ../
11 |
--------------------------------------------------------------------------------
/misc/go.sum:
--------------------------------------------------------------------------------
1 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
2 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
3 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
4 | golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
5 | golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
6 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
7 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
8 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
9 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
10 | golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
11 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
12 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
13 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
14 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
15 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
16 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
17 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
18 | golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
19 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
20 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
21 | golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
22 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
23 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
24 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
25 | golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
26 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
27 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
28 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
29 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
30 |
--------------------------------------------------------------------------------
/misc/xts_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | //go:build linux && !android
5 | // +build linux,!android
6 |
7 | package misc
8 |
9 | import (
10 | "bytes"
11 | "encoding/hex"
12 | "testing"
13 |
14 | "github.com/microsoft/go-crypto-openssl/openssl"
15 | "golang.org/x/crypto/xts"
16 | )
17 |
18 | func TestMain(m *testing.M) {
19 | openssl.Init()
20 | }
21 |
22 | // The following vectors have been copied from
23 | // https://github.com/golang/crypto/blob/3d872d042823aed41f28af3b13beb27c0c9b1e35/xts/xts_test.go.
24 | var xtsTestVectors = []struct {
25 | key string
26 | sector uint64
27 | plaintext string
28 | ciphertext string
29 | }{
30 | {
31 | "0000000000000000000000000000000000000000000000000000000000000000",
32 | 0,
33 | "0000000000000000000000000000000000000000000000000000000000000000",
34 | "917cf69ebd68b2ec9b9fe9a3eadda692cd43d2f59598ed858c02c2652fbf922e",
35 | }, {
36 | "1111111111111111111111111111111122222222222222222222222222222222",
37 | 0x3333333333,
38 | "4444444444444444444444444444444444444444444444444444444444444444",
39 | "c454185e6a16936e39334038acef838bfb186fff7480adc4289382ecd6d394f0",
40 | }, {
41 | "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f022222222222222222222222222222222",
42 | 0x3333333333,
43 | "4444444444444444444444444444444444444444444444444444444444444444",
44 | "af85336b597afc1a900b2eb21ec949d292df4c047e0b21532186a5971a227a89",
45 | }, {
46 | "2718281828459045235360287471352631415926535897932384626433832795",
47 | 0,
48 | "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
49 | "27a7479befa1d476489f308cd4cfa6e2a96e4bbe3208ff25287dd3819616e89cc78cf7f5e543445f8333d8fa7f56000005279fa5d8b5e4ad40e736ddb4d35412328063fd2aab53e5ea1e0a9f332500a5df9487d07a5c92cc512c8866c7e860ce93fdf166a24912b422976146ae20ce846bb7dc9ba94a767aaef20c0d61ad02655ea92dc4c4e41a8952c651d33174be51a10c421110e6d81588ede82103a252d8a750e8768defffed9122810aaeb99f9172af82b604dc4b8e51bcb08235a6f4341332e4ca60482a4ba1a03b3e65008fc5da76b70bf1690db4eae29c5f1badd03c5ccf2a55d705ddcd86d449511ceb7ec30bf12b1fa35b913f9f747a8afd1b130e94bff94effd01a91735ca1726acd0b197c4e5b03393697e126826fb6bbde8ecc1e08298516e2c9ed03ff3c1b7860f6de76d4cecd94c8119855ef5297ca67e9f3e7ff72b1e99785ca0a7e7720c5b36dc6d72cac9574c8cbbc2f801e23e56fd344b07f22154beba0f08ce8891e643ed995c94d9a69c9f1b5f499027a78572aeebd74d20cc39881c213ee770b1010e4bea718846977ae119f7a023ab58cca0ad752afe656bb3c17256a9f6e9bf19fdd5a38fc82bbe872c5539edb609ef4f79c203ebb140f2e583cb2ad15b4aa5b655016a8449277dbd477ef2c8d6c017db738b18deb4a427d1923ce3ff262735779a418f20a282df920147beabe421ee5319d0568",
50 | }, {
51 | "2718281828459045235360287471352631415926535897932384626433832795",
52 | 1,
53 | "27a7479befa1d476489f308cd4cfa6e2a96e4bbe3208ff25287dd3819616e89cc78cf7f5e543445f8333d8fa7f56000005279fa5d8b5e4ad40e736ddb4d35412328063fd2aab53e5ea1e0a9f332500a5df9487d07a5c92cc512c8866c7e860ce93fdf166a24912b422976146ae20ce846bb7dc9ba94a767aaef20c0d61ad02655ea92dc4c4e41a8952c651d33174be51a10c421110e6d81588ede82103a252d8a750e8768defffed9122810aaeb99f9172af82b604dc4b8e51bcb08235a6f4341332e4ca60482a4ba1a03b3e65008fc5da76b70bf1690db4eae29c5f1badd03c5ccf2a55d705ddcd86d449511ceb7ec30bf12b1fa35b913f9f747a8afd1b130e94bff94effd01a91735ca1726acd0b197c4e5b03393697e126826fb6bbde8ecc1e08298516e2c9ed03ff3c1b7860f6de76d4cecd94c8119855ef5297ca67e9f3e7ff72b1e99785ca0a7e7720c5b36dc6d72cac9574c8cbbc2f801e23e56fd344b07f22154beba0f08ce8891e643ed995c94d9a69c9f1b5f499027a78572aeebd74d20cc39881c213ee770b1010e4bea718846977ae119f7a023ab58cca0ad752afe656bb3c17256a9f6e9bf19fdd5a38fc82bbe872c5539edb609ef4f79c203ebb140f2e583cb2ad15b4aa5b655016a8449277dbd477ef2c8d6c017db738b18deb4a427d1923ce3ff262735779a418f20a282df920147beabe421ee5319d0568",
54 | "264d3ca8512194fec312c8c9891f279fefdd608d0c027b60483a3fa811d65ee59d52d9e40ec5672d81532b38b6b089ce951f0f9c35590b8b978d175213f329bb1c2fd30f2f7f30492a61a532a79f51d36f5e31a7c9a12c286082ff7d2394d18f783e1a8e72c722caaaa52d8f065657d2631fd25bfd8e5baad6e527d763517501c68c5edc3cdd55435c532d7125c8614deed9adaa3acade5888b87bef641c4c994c8091b5bcd387f3963fb5bc37aa922fbfe3df4e5b915e6eb514717bdd2a74079a5073f5c4bfd46adf7d282e7a393a52579d11a028da4d9cd9c77124f9648ee383b1ac763930e7162a8d37f350b2f74b8472cf09902063c6b32e8c2d9290cefbd7346d1c779a0df50edcde4531da07b099c638e83a755944df2aef1aa31752fd323dcb710fb4bfbb9d22b925bc3577e1b8949e729a90bbafeacf7f7879e7b1147e28ba0bae940db795a61b15ecf4df8db07b824bb062802cc98a9545bb2aaeed77cb3fc6db15dcd7d80d7d5bc406c4970a3478ada8899b329198eb61c193fb6275aa8ca340344a75a862aebe92eee1ce032fd950b47d7704a3876923b4ad62844bf4a09c4dbe8b4397184b7471360c9564880aedddb9baa4af2e75394b08cd32ff479c57a07d3eab5d54de5f9738b8d27f27a9f0ab11799d7b7ffefb2704c95c6ad12c39f1e867a4b7b1d7818a4b753dfd2a89ccb45e001a03a867b187f225dd",
55 | }, {
56 | "27182818284590452353602874713526624977572470936999595749669676273141592653589793238462643383279502884197169399375105820974944592",
57 | 0xff,
58 | "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
59 | "1c3b3a102f770386e4836c99e370cf9bea00803f5e482357a4ae12d414a3e63b5d31e276f8fe4a8d66b317f9ac683f44680a86ac35adfc3345befecb4bb188fd5776926c49a3095eb108fd1098baec70aaa66999a72a82f27d848b21d4a741b0c5cd4d5fff9dac89aeba122961d03a757123e9870f8acf1000020887891429ca2a3e7a7d7df7b10355165c8b9a6d0a7de8b062c4500dc4cd120c0f7418dae3d0b5781c34803fa75421c790dfe1de1834f280d7667b327f6c8cd7557e12ac3a0f93ec05c52e0493ef31a12d3d9260f79a289d6a379bc70c50841473d1a8cc81ec583e9645e07b8d9670655ba5bbcfecc6dc3966380ad8fecb17b6ba02469a020a84e18e8f84252070c13e9f1f289be54fbc481457778f616015e1327a02b140f1505eb309326d68378f8374595c849d84f4c333ec4423885143cb47bd71c5edae9be69a2ffeceb1bec9de244fbe15992b11b77c040f12bd8f6a975a44a0f90c29a9abc3d4d893927284c58754cce294529f8614dcd2aba991925fedc4ae74ffac6e333b93eb4aff0479da9a410e4450e0dd7ae4c6e2910900575da401fc07059f645e8b7e9bfdef33943054ff84011493c27b3429eaedb4ed5376441a77ed43851ad77f16f541dfd269d50d6a5f14fb0aab1cbb4c1550be97f7ab4066193c4caa773dad38014bd2092fa755c824bb5e54c4f36ffda9fcea70b9c6e693e148c151",
60 | },
61 | }
62 |
63 | func fromHex(s string) []byte {
64 | ret, err := hex.DecodeString(s)
65 | if err != nil {
66 | panic("xts: invalid hex in test")
67 | }
68 | return ret
69 | }
70 |
71 | func TestXTS(t *testing.T) {
72 | for i, test := range xtsTestVectors {
73 | c, err := xts.NewCipher(openssl.NewAESCipher, fromHex(test.key))
74 | if err != nil {
75 | t.Errorf("#%d: failed to create cipher: %s", i, err)
76 | continue
77 | }
78 | plaintext := fromHex(test.plaintext)
79 | ciphertext := make([]byte, len(plaintext))
80 | c.Encrypt(ciphertext, plaintext, test.sector)
81 |
82 | expectedCiphertext := fromHex(test.ciphertext)
83 | if !bytes.Equal(ciphertext, expectedCiphertext) {
84 | t.Errorf("#%d: encrypted failed, got: %x, want: %x", i, ciphertext, expectedCiphertext)
85 | continue
86 | }
87 |
88 | decrypted := make([]byte, len(ciphertext))
89 | c.Decrypt(decrypted, ciphertext, test.sector)
90 | if !bytes.Equal(decrypted, plaintext) {
91 | t.Errorf("#%d: decryption failed, got: %x, want: %x", i, decrypted, plaintext)
92 | }
93 | }
94 | }
95 |
96 | func TestXTSShorterCiphertext(t *testing.T) {
97 | c, err := xts.NewCipher(openssl.NewAESCipher, make([]byte, 32))
98 | if err != nil {
99 | t.Fatalf("NewCipher failed: %s", err)
100 | }
101 |
102 | plaintext := make([]byte, 32)
103 | encrypted := make([]byte, 48)
104 | decrypted := make([]byte, 48)
105 |
106 | c.Encrypt(encrypted, plaintext, 0)
107 | c.Decrypt(decrypted, encrypted[:len(plaintext)], 0)
108 |
109 | if !bytes.Equal(plaintext, decrypted[:len(plaintext)]) {
110 | t.Errorf("En/Decryption is not inverse, got: %x, want: %x", decrypted[:len(plaintext)], plaintext)
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/openssl/aes.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | //go:build linux && !android
5 | // +build linux,!android
6 |
7 | package openssl
8 |
9 | // #include "goopenssl.h"
10 | import "C"
11 | import (
12 | "crypto/cipher"
13 | "errors"
14 | "runtime"
15 | "strconv"
16 | "unsafe"
17 |
18 | "github.com/microsoft/go-crypto-openssl/openssl/internal/subtle"
19 | )
20 |
21 | type aesKeySizeError int
22 |
23 | func (k aesKeySizeError) Error() string {
24 | return "crypto/aes: invalid key size " + strconv.Itoa(int(k))
25 | }
26 |
27 | const aesBlockSize = 16
28 |
29 | type aesCipher struct {
30 | key []byte
31 | enc_ctx C.GO_EVP_CIPHER_CTX_PTR
32 | dec_ctx C.GO_EVP_CIPHER_CTX_PTR
33 | cipher C.GO_EVP_CIPHER_PTR
34 | }
35 |
36 | type extraModes interface {
37 | // Copied out of crypto/aes/modes.go.
38 | NewCBCEncrypter(iv []byte) cipher.BlockMode
39 | NewCBCDecrypter(iv []byte) cipher.BlockMode
40 | NewCTR(iv []byte) cipher.Stream
41 | NewGCM(nonceSize, tagSize int) (cipher.AEAD, error)
42 |
43 | // Invented for BoringCrypto.
44 | NewGCMTLS() (cipher.AEAD, error)
45 | }
46 |
47 | var _ extraModes = (*aesCipher)(nil)
48 |
49 | func NewAESCipher(key []byte) (cipher.Block, error) {
50 | c := &aesCipher{key: make([]byte, len(key))}
51 | copy(c.key, key)
52 |
53 | switch len(c.key) * 8 {
54 | case 128:
55 | c.cipher = C.go_openssl_EVP_aes_128_ecb()
56 | case 192:
57 | c.cipher = C.go_openssl_EVP_aes_192_ecb()
58 | case 256:
59 | c.cipher = C.go_openssl_EVP_aes_256_ecb()
60 | default:
61 | return nil, errors.New("crypto/cipher: Invalid key size")
62 | }
63 |
64 | runtime.SetFinalizer(c, (*aesCipher).finalize)
65 |
66 | return c, nil
67 | }
68 |
69 | func (c *aesCipher) finalize() {
70 | if c.enc_ctx != nil {
71 | C.go_openssl_EVP_CIPHER_CTX_free(c.enc_ctx)
72 | }
73 | if c.dec_ctx != nil {
74 | C.go_openssl_EVP_CIPHER_CTX_free(c.dec_ctx)
75 | }
76 | }
77 |
78 | func (c *aesCipher) BlockSize() int { return aesBlockSize }
79 |
80 | func (c *aesCipher) Encrypt(dst, src []byte) {
81 | if subtle.InexactOverlap(dst, src) {
82 | panic("crypto/cipher: invalid buffer overlap")
83 | }
84 | if len(src) < aesBlockSize {
85 | panic("crypto/aes: input not full block")
86 | }
87 | if len(dst) < aesBlockSize {
88 | panic("crypto/aes: output not full block")
89 | }
90 |
91 | if c.enc_ctx == nil {
92 | var err error
93 | c.enc_ctx, err = newCipherCtx(c.cipher, C.GO_AES_ENCRYPT, c.key, nil)
94 | if err != nil {
95 | panic(err)
96 | }
97 | }
98 |
99 | C.go_openssl_EVP_EncryptUpdate_wrapper(c.enc_ctx, base(dst), base(src), aesBlockSize)
100 | runtime.KeepAlive(c)
101 | }
102 |
103 | func (c *aesCipher) Decrypt(dst, src []byte) {
104 | if subtle.InexactOverlap(dst, src) {
105 | panic("crypto/cipher: invalid buffer overlap")
106 | }
107 | if len(src) < aesBlockSize {
108 | panic("crypto/aes: input not full block")
109 | }
110 | if len(dst) < aesBlockSize {
111 | panic("crypto/aes: output not full block")
112 | }
113 | if c.dec_ctx == nil {
114 | var err error
115 | c.dec_ctx, err = newCipherCtx(c.cipher, C.GO_AES_DECRYPT, c.key, nil)
116 | if err != nil {
117 | panic(err)
118 | }
119 | // Disable standard block padding detection,
120 | // src is always multiple of the block size.
121 | if C.go_openssl_EVP_CIPHER_CTX_set_padding(c.dec_ctx, 0) != 1 {
122 | panic("crypto/cipher: unable to set padding")
123 | }
124 | }
125 | C.go_openssl_EVP_DecryptUpdate_wrapper(c.dec_ctx, base(dst), base(src), aesBlockSize)
126 | runtime.KeepAlive(c)
127 | }
128 |
129 | type aesCBC struct {
130 | ctx C.GO_EVP_CIPHER_CTX_PTR
131 | }
132 |
133 | func (x *aesCBC) BlockSize() int { return aesBlockSize }
134 |
135 | func (x *aesCBC) CryptBlocks(dst, src []byte) {
136 | if subtle.InexactOverlap(dst, src) {
137 | panic("crypto/cipher: invalid buffer overlap")
138 | }
139 | if len(src)%aesBlockSize != 0 {
140 | panic("crypto/cipher: input not full blocks")
141 | }
142 | if len(dst) < len(src) {
143 | panic("crypto/cipher: output smaller than input")
144 | }
145 | if len(src) > 0 {
146 | if C.go_openssl_EVP_CipherUpdate_wrapper(x.ctx, base(dst), base(src), C.int(len(src))) != 1 {
147 | panic("crypto/cipher: CipherUpdate failed")
148 | }
149 | runtime.KeepAlive(x)
150 | }
151 | }
152 |
153 | func (x *aesCBC) SetIV(iv []byte) {
154 | if len(iv) != aesBlockSize {
155 | panic("cipher: incorrect length IV")
156 | }
157 | if C.go_openssl_EVP_CipherInit_ex(x.ctx, nil, nil, nil, base(iv), -1) != 1 {
158 | panic("cipher: unable to initialize EVP cipher ctx")
159 | }
160 | }
161 |
162 | func (c *aesCipher) NewCBCEncrypter(iv []byte) cipher.BlockMode {
163 | x := new(aesCBC)
164 |
165 | var cipher C.GO_EVP_CIPHER_PTR
166 | switch len(c.key) * 8 {
167 | case 128:
168 | cipher = C.go_openssl_EVP_aes_128_cbc()
169 | case 192:
170 | cipher = C.go_openssl_EVP_aes_192_cbc()
171 | case 256:
172 | cipher = C.go_openssl_EVP_aes_256_cbc()
173 | default:
174 | panic("openssl: unsupported key length")
175 | }
176 | var err error
177 | x.ctx, err = newCipherCtx(cipher, C.GO_AES_ENCRYPT, c.key, iv)
178 | if err != nil {
179 | panic(err)
180 | }
181 |
182 | runtime.SetFinalizer(x, (*aesCBC).finalize)
183 |
184 | return x
185 | }
186 |
187 | func (c *aesCBC) finalize() {
188 | C.go_openssl_EVP_CIPHER_CTX_free(c.ctx)
189 | }
190 |
191 | func (c *aesCipher) NewCBCDecrypter(iv []byte) cipher.BlockMode {
192 | x := new(aesCBC)
193 |
194 | var cipher C.GO_EVP_CIPHER_PTR
195 | switch len(c.key) * 8 {
196 | case 128:
197 | cipher = C.go_openssl_EVP_aes_128_cbc()
198 | case 192:
199 | cipher = C.go_openssl_EVP_aes_192_cbc()
200 | case 256:
201 | cipher = C.go_openssl_EVP_aes_256_cbc()
202 | default:
203 | panic("openssl: unsupported key length")
204 | }
205 |
206 | var err error
207 | x.ctx, err = newCipherCtx(cipher, C.GO_AES_DECRYPT, c.key, iv)
208 | if err != nil {
209 | panic(err)
210 | }
211 | if C.go_openssl_EVP_CIPHER_CTX_set_padding(x.ctx, 0) != 1 {
212 | panic("cipher: unable to set padding")
213 | }
214 |
215 | runtime.SetFinalizer(x, (*aesCBC).finalize)
216 | return x
217 | }
218 |
219 | type aesCTR struct {
220 | ctx C.GO_EVP_CIPHER_CTX_PTR
221 | }
222 |
223 | func (x *aesCTR) XORKeyStream(dst, src []byte) {
224 | if subtle.InexactOverlap(dst, src) {
225 | panic("crypto/cipher: invalid buffer overlap")
226 | }
227 | if len(dst) < len(src) {
228 | panic("crypto/cipher: output smaller than input")
229 | }
230 | if len(src) == 0 {
231 | return
232 | }
233 | C.go_openssl_EVP_EncryptUpdate_wrapper(x.ctx, base(dst), base(src), C.int(len(src)))
234 | runtime.KeepAlive(x)
235 | }
236 |
237 | func (c *aesCipher) NewCTR(iv []byte) cipher.Stream {
238 | x := new(aesCTR)
239 |
240 | var cipher C.GO_EVP_CIPHER_PTR
241 | switch len(c.key) * 8 {
242 | case 128:
243 | cipher = C.go_openssl_EVP_aes_128_ctr()
244 | case 192:
245 | cipher = C.go_openssl_EVP_aes_192_ctr()
246 | case 256:
247 | cipher = C.go_openssl_EVP_aes_256_ctr()
248 | default:
249 | panic("openssl: unsupported key length")
250 | }
251 | var err error
252 | x.ctx, err = newCipherCtx(cipher, C.GO_AES_ENCRYPT, c.key, iv)
253 | if err != nil {
254 | panic(err)
255 | }
256 |
257 | runtime.SetFinalizer(x, (*aesCTR).finalize)
258 |
259 | return x
260 | }
261 |
262 | func (c *aesCTR) finalize() {
263 | C.go_openssl_EVP_CIPHER_CTX_free(c.ctx)
264 | }
265 |
266 | type cipherGCMTLS uint8
267 |
268 | const (
269 | cipherGCMTLSNone cipherGCMTLS = iota
270 | cipherGCMTLS12
271 | cipherGCMTLS13
272 | )
273 |
274 | type aesGCM struct {
275 | ctx C.GO_EVP_CIPHER_CTX_PTR
276 | tls cipherGCMTLS
277 | // minNextNonce is the minimum value that the next nonce can be, enforced by
278 | // all TLS modes.
279 | minNextNonce uint64
280 | // mask is the nonce mask used in TLS 1.3 mode.
281 | mask uint64
282 | // maskInitialized is true if mask has been initialized. This happens during
283 | // the first Seal. The initialized mask may be 0. Used by TLS 1.3 mode.
284 | maskInitialized bool
285 | }
286 |
287 | const (
288 | gcmTagSize = 16
289 | gcmStandardNonceSize = 12
290 | // TLS 1.2 additional data is constructed as:
291 | //
292 | // additional_data = seq_num(8) + TLSCompressed.type(1) + TLSCompressed.version(2) + TLSCompressed.length(2);
293 | gcmTls12AddSize = 13
294 | // TLS 1.3 additional data is constructed as:
295 | //
296 | // additional_data = TLSCiphertext.opaque_type(1) || TLSCiphertext.legacy_record_version(2) || TLSCiphertext.length(2)
297 | gcmTls13AddSize = 5
298 | gcmTlsFixedNonceSize = 4
299 | )
300 |
301 | type aesNonceSizeError int
302 |
303 | func (n aesNonceSizeError) Error() string {
304 | return "crypto/aes: invalid GCM nonce size " + strconv.Itoa(int(n))
305 | }
306 |
307 | type noGCM struct {
308 | cipher.Block
309 | }
310 |
311 | func (c *aesCipher) NewGCM(nonceSize, tagSize int) (cipher.AEAD, error) {
312 | if nonceSize != gcmStandardNonceSize && tagSize != gcmTagSize {
313 | return nil, errors.New("crypto/aes: GCM tag and nonce sizes can't be non-standard at the same time")
314 | }
315 | // Fall back to standard library for GCM with non-standard nonce or tag size.
316 | if nonceSize != gcmStandardNonceSize {
317 | return cipher.NewGCMWithNonceSize(&noGCM{c}, nonceSize)
318 | }
319 | if tagSize != gcmTagSize {
320 | return cipher.NewGCMWithTagSize(&noGCM{c}, tagSize)
321 | }
322 | return c.newGCM(cipherGCMTLSNone)
323 | }
324 |
325 | // NewGCMTLS returns a GCM cipher specific to TLS
326 | // and should not be used for non-TLS purposes.
327 | func NewGCMTLS(c cipher.Block) (cipher.AEAD, error) {
328 | return c.(*aesCipher).NewGCMTLS()
329 | }
330 |
331 | func (c *aesCipher) NewGCMTLS() (cipher.AEAD, error) {
332 | return c.newGCM(cipherGCMTLS12)
333 | }
334 |
335 | // NewGCMTLS13 returns a GCM cipher specific to TLS 1.3 and should not be used
336 | // for non-TLS purposes.
337 | func NewGCMTLS13(c cipher.Block) (cipher.AEAD, error) {
338 | return c.(*aesCipher).NewGCMTLS13()
339 | }
340 |
341 | func (c *aesCipher) NewGCMTLS13() (cipher.AEAD, error) {
342 | return c.newGCM(cipherGCMTLS13)
343 | }
344 |
345 | func (c *aesCipher) newGCM(tls cipherGCMTLS) (cipher.AEAD, error) {
346 | var cipher C.GO_EVP_CIPHER_PTR
347 | switch len(c.key) * 8 {
348 | case 128:
349 | cipher = C.go_openssl_EVP_aes_128_gcm()
350 | case 192:
351 | cipher = C.go_openssl_EVP_aes_192_gcm()
352 | case 256:
353 | cipher = C.go_openssl_EVP_aes_256_gcm()
354 | default:
355 | panic("openssl: unsupported key length")
356 | }
357 | ctx, err := newCipherCtx(cipher, -1, c.key, nil)
358 | if err != nil {
359 | return nil, err
360 | }
361 | g := &aesGCM{ctx: ctx, tls: tls}
362 | runtime.SetFinalizer(g, (*aesGCM).finalize)
363 | return g, nil
364 | }
365 |
366 | func (g *aesGCM) finalize() {
367 | C.go_openssl_EVP_CIPHER_CTX_free(g.ctx)
368 | }
369 |
370 | func (g *aesGCM) NonceSize() int {
371 | return gcmStandardNonceSize
372 | }
373 |
374 | func (g *aesGCM) Overhead() int {
375 | return gcmTagSize
376 | }
377 |
378 | // base returns the address of the underlying array in b,
379 | // being careful not to panic when b has zero length.
380 | func base(b []byte) *C.uchar {
381 | if len(b) == 0 {
382 | return nil
383 | }
384 | return (*C.uchar)(unsafe.Pointer(&b[0]))
385 | }
386 |
387 | func (g *aesGCM) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
388 | if len(nonce) != gcmStandardNonceSize {
389 | panic("cipher: incorrect nonce length given to GCM")
390 | }
391 | if uint64(len(plaintext)) > ((1<<32)-2)*aesBlockSize || len(plaintext)+gcmTagSize < len(plaintext) {
392 | panic("cipher: message too large for GCM")
393 | }
394 | if len(dst)+len(plaintext)+gcmTagSize < len(dst) {
395 | panic("cipher: message too large for buffer")
396 | }
397 | if g.tls != cipherGCMTLSNone {
398 | if g.tls == cipherGCMTLS12 && len(additionalData) != gcmTls12AddSize {
399 | panic("cipher: incorrect additional data length given to GCM TLS 1.2")
400 | } else if g.tls == cipherGCMTLS13 && len(additionalData) != gcmTls13AddSize {
401 | panic("cipher: incorrect additional data length given to GCM TLS 1.3")
402 | }
403 | counter := bigUint64(nonce[gcmTlsFixedNonceSize:])
404 | if g.tls == cipherGCMTLS13 {
405 | // In TLS 1.3, the counter in the nonce has a mask and requires
406 | // further decoding.
407 | if !g.maskInitialized {
408 | // According to TLS 1.3 nonce construction details at
409 | // https://tools.ietf.org/html/rfc8446#section-5.3:
410 | //
411 | // the first record transmitted under a particular traffic
412 | // key MUST use sequence number 0.
413 | //
414 | // The padded sequence number is XORed with [a mask].
415 | //
416 | // The resulting quantity (of length iv_length) is used as
417 | // the per-record nonce.
418 | //
419 | // We need to convert from the given nonce to sequence numbers
420 | // to keep track of minNextNonce and enforce the counter
421 | // maximum. On the first call, we know counter^mask is 0^mask,
422 | // so we can simply store it as the mask.
423 | g.mask = counter
424 | g.maskInitialized = true
425 | }
426 | counter ^= g.mask
427 | }
428 | // BoringCrypto enforces strictly monotonically increasing explicit nonces
429 | // and to fail after 2^64 - 1 keys as per FIPS 140-2 IG A.5,
430 | // but OpenSSL does not perform this check, so it is implemented here.
431 | const maxUint64 = 1<<64 - 1
432 | if counter == maxUint64 {
433 | panic("cipher: nonce counter must be less than 2^64 - 1")
434 | }
435 | if counter < g.minNextNonce {
436 | panic("cipher: nonce counter must be strictly monotonically increasing")
437 | }
438 | defer func() {
439 | g.minNextNonce = counter + 1
440 | }()
441 | }
442 |
443 | // Make room in dst to append plaintext+overhead.
444 | ret, out := sliceForAppend(dst, len(plaintext)+gcmTagSize)
445 |
446 | // Check delayed until now to make sure len(dst) is accurate.
447 | if subtle.InexactOverlap(out, plaintext) {
448 | panic("cipher: invalid buffer overlap")
449 | }
450 |
451 | // Encrypt additional data.
452 | // When sealing a TLS payload, OpenSSL app sets the additional data using
453 | // 'EVP_CIPHER_CTX_ctrl(g.ctx, C.EVP_CTRL_AEAD_TLS1_AAD, C.EVP_AEAD_TLS1_AAD_LEN, base(additionalData))'.
454 | // This makes the explicit nonce component to monotonically increase on every Seal operation without
455 | // relying in the explicit nonce being securely set externally,
456 | // and it also gives some interesting speed gains.
457 | // Unfortunately we can't use it because Go expects AEAD.Seal to honor the provided nonce.
458 | if C.go_openssl_EVP_CIPHER_CTX_seal_wrapper(g.ctx, base(out), base(nonce),
459 | base(plaintext), C.int(len(plaintext)),
460 | base(additionalData), C.int(len(additionalData))) != 1 {
461 |
462 | panic(fail("EVP_CIPHER_CTX_seal"))
463 | }
464 | runtime.KeepAlive(g)
465 | return ret
466 | }
467 |
468 | var errOpen = errors.New("cipher: message authentication failed")
469 |
470 | func (g *aesGCM) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
471 | if len(nonce) != gcmStandardNonceSize {
472 | panic("cipher: incorrect nonce length given to GCM")
473 | }
474 | if len(ciphertext) < gcmTagSize {
475 | return nil, errOpen
476 | }
477 | if uint64(len(ciphertext)) > ((1<<32)-2)*aesBlockSize+gcmTagSize {
478 | return nil, errOpen
479 | }
480 | // BoringCrypto does not do any TLS check when decrypting, neither do we.
481 |
482 | tag := ciphertext[len(ciphertext)-gcmTagSize:]
483 | ciphertext = ciphertext[:len(ciphertext)-gcmTagSize]
484 |
485 | // Make room in dst to append ciphertext without tag.
486 | ret, out := sliceForAppend(dst, len(ciphertext))
487 |
488 | // Check delayed until now to make sure len(dst) is accurate.
489 | if subtle.InexactOverlap(out, ciphertext) {
490 | panic("cipher: invalid buffer overlap")
491 | }
492 |
493 | if C.go_openssl_EVP_CIPHER_CTX_open_wrapper(g.ctx, base(out), base(nonce),
494 | base(ciphertext), C.int(len(ciphertext)),
495 | base(additionalData), C.int(len(additionalData)), base(tag)) != 1 {
496 |
497 | for i := range out {
498 | out[i] = 0
499 | }
500 | return nil, errOpen
501 | }
502 | runtime.KeepAlive(g)
503 | return ret, nil
504 | }
505 |
506 | // sliceForAppend is a mirror of crypto/cipher.sliceForAppend.
507 | func sliceForAppend(in []byte, n int) (head, tail []byte) {
508 | if total := len(in) + n; cap(in) >= total {
509 | head = in[:total]
510 | } else {
511 | head = make([]byte, total)
512 | copy(head, in)
513 | }
514 | tail = head[len(in):]
515 | return
516 | }
517 |
518 | func newCipherCtx(cipher C.GO_EVP_CIPHER_PTR, mode C.int, key, iv []byte) (C.GO_EVP_CIPHER_CTX_PTR, error) {
519 | ctx := C.go_openssl_EVP_CIPHER_CTX_new()
520 | if ctx == nil {
521 | return nil, fail("unable to create EVP cipher ctx")
522 | }
523 | if C.go_openssl_EVP_CipherInit_ex(ctx, cipher, nil, base(key), base(iv), mode) != 1 {
524 | C.go_openssl_EVP_CIPHER_CTX_free(ctx)
525 | return nil, fail("unable to initialize EVP cipher ctx")
526 | }
527 | return ctx, nil
528 | }
529 |
530 | func bigUint64(b []byte) uint64 {
531 | _ = b[7] // bounds check hint to compiler; see go.dev/issue/14808
532 | return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
533 | uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
534 | }
535 |
--------------------------------------------------------------------------------
/openssl/aes_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | //go:build linux && !android
5 | // +build linux,!android
6 |
7 | package openssl
8 |
9 | import (
10 | "bytes"
11 | "crypto/cipher"
12 | "testing"
13 | )
14 |
15 | func TestNewGCMNonce(t *testing.T) {
16 | key := []byte("D249BF6DEC97B1EBD69BC4D6B3A3C49D")
17 | ci, err := NewAESCipher(key)
18 | if err != nil {
19 | t.Fatal(err)
20 | }
21 | c := ci.(*aesCipher)
22 | _, err = c.NewGCM(gcmStandardNonceSize-1, gcmTagSize-1)
23 | if err == nil {
24 | t.Error("expected error for non-standard tag and nonce size at the same time, got none")
25 | }
26 | _, err = c.NewGCM(gcmStandardNonceSize-1, gcmTagSize)
27 | if err != nil {
28 | t.Errorf("expected no error for non-standard nonce size with standard tag size, got: %#v", err)
29 | }
30 | _, err = c.NewGCM(gcmStandardNonceSize, gcmTagSize-1)
31 | if err != nil {
32 | t.Errorf("expected no error for standard tag size, got: %#v", err)
33 | }
34 | _, err = c.NewGCM(gcmStandardNonceSize, gcmTagSize)
35 | if err != nil {
36 | t.Errorf("expected no error for standard tag / nonce size, got: %#v", err)
37 | }
38 | }
39 |
40 | func TestSealAndOpen(t *testing.T) {
41 | key := []byte("D249BF6DEC97B1EBD69BC4D6B3A3C49D")
42 | ci, err := NewAESCipher(key)
43 | if err != nil {
44 | t.Fatal(err)
45 | }
46 | c := ci.(*aesCipher)
47 | gcm, err := c.NewGCM(gcmStandardNonceSize, gcmTagSize)
48 | if err != nil {
49 | t.Fatal(err)
50 | }
51 | nonce := []byte{0x91, 0xc7, 0xa7, 0x54, 0x52, 0xef, 0x10, 0xdb, 0x91, 0xa8, 0x6c, 0xf9}
52 | plainText := []byte{0x01, 0x02, 0x03}
53 | additionalData := []byte{0x05, 0x05, 0x07}
54 | sealed := gcm.Seal(nil, nonce, plainText, additionalData)
55 | decrypted, err := gcm.Open(nil, nonce, sealed, additionalData)
56 | if err != nil {
57 | t.Error(err)
58 | }
59 | if !bytes.Equal(decrypted, plainText) {
60 | t.Errorf("unexpected decrypted result\ngot: %#v\nexp: %#v", decrypted, plainText)
61 | }
62 | }
63 |
64 | func TestSealAndOpen_Empty(t *testing.T) {
65 | key := []byte("D249BF6DEC97B1EBD69BC4D6B3A3C49D")
66 | ci, err := NewAESCipher(key)
67 | if err != nil {
68 | t.Fatal(err)
69 | }
70 | c := ci.(*aesCipher)
71 | gcm, err := c.NewGCM(gcmStandardNonceSize, gcmTagSize)
72 | if err != nil {
73 | t.Fatal(err)
74 | }
75 | nonce := []byte{0x91, 0xc7, 0xa7, 0x54, 0x52, 0xef, 0x10, 0xdb, 0x91, 0xa8, 0x6c, 0xf9}
76 | sealed := gcm.Seal(nil, nonce, []byte{}, []byte{})
77 | decrypted, err := gcm.Open(nil, nonce, sealed, []byte{})
78 | if err != nil {
79 | t.Error(err)
80 | }
81 | if !bytes.Equal(decrypted, []byte{}) {
82 | t.Errorf("unexpected decrypted result\ngot: %#v\nexp: %#v", decrypted, []byte{})
83 | }
84 | }
85 |
86 | func TestSealAndOpenTLS(t *testing.T) {
87 | key := []byte("D249BF6DEC97B1EBD69BC4D6B3A3C49D")
88 | tests := []struct {
89 | name string
90 | tls string
91 | mask func(n *[12]byte)
92 | }{
93 | {"1.2", "1.2", nil},
94 | {"1.3", "1.3", nil},
95 | {"1.3_masked", "1.3", func(n *[12]byte) {
96 | // Arbitrary mask in the high bits.
97 | n[9] ^= 0x42
98 | // Mask the very first bit. This makes sure that if Seal doesn't
99 | // handle the mask, the counter appears to go backwards and panics
100 | // when it shouldn't.
101 | n[11] ^= 0x1
102 | }},
103 | }
104 | for _, tt := range tests {
105 | t.Run(tt.name, func(t *testing.T) {
106 | ci, err := NewAESCipher(key)
107 | if err != nil {
108 | t.Fatal(err)
109 | }
110 | var gcm cipher.AEAD
111 | switch tt.tls {
112 | case "1.2":
113 | gcm, err = NewGCMTLS(ci)
114 | case "1.3":
115 | gcm, err = NewGCMTLS13(ci)
116 | }
117 | if err != nil {
118 | t.Fatal(err)
119 | }
120 | nonce := [12]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
121 | nonce1 := [12]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
122 | nonce9 := [12]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9}
123 | nonce10 := [12]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10}
124 | nonceMax := [12]byte{0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255}
125 | if tt.mask != nil {
126 | for _, m := range []*[12]byte{&nonce, &nonce1, &nonce9, &nonce10, &nonceMax} {
127 | tt.mask(m)
128 | }
129 | }
130 | plainText := []byte{0x01, 0x02, 0x03}
131 | var additionalData []byte
132 | switch tt.tls {
133 | case "1.2":
134 | additionalData = make([]byte, 13)
135 | case "1.3":
136 | additionalData = []byte{23, 3, 3, 0, 0}
137 | }
138 | additionalData[len(additionalData)-2] = byte(len(plainText) >> 8)
139 | additionalData[len(additionalData)-1] = byte(len(plainText))
140 | sealed := gcm.Seal(nil, nonce[:], plainText, additionalData)
141 | assertPanic(t, func() {
142 | gcm.Seal(nil, nonce[:], plainText, additionalData)
143 | })
144 | sealed1 := gcm.Seal(nil, nonce1[:], plainText, additionalData)
145 | gcm.Seal(nil, nonce10[:], plainText, additionalData)
146 | assertPanic(t, func() {
147 | gcm.Seal(nil, nonce9[:], plainText, additionalData)
148 | })
149 | assertPanic(t, func() {
150 | gcm.Seal(nil, nonceMax[:], plainText, additionalData)
151 | })
152 | if bytes.Equal(sealed, sealed1) {
153 | t.Errorf("different nonces should produce different outputs\ngot: %#v\nexp: %#v", sealed, sealed1)
154 | }
155 | decrypted, err := gcm.Open(nil, nonce[:], sealed, additionalData)
156 | if err != nil {
157 | t.Error(err)
158 | }
159 | decrypted1, err := gcm.Open(nil, nonce1[:], sealed1, additionalData)
160 | if err != nil {
161 | t.Error(err)
162 | }
163 | if !bytes.Equal(decrypted, plainText) {
164 | t.Errorf("unexpected decrypted result\ngot: %#v\nexp: %#v", decrypted, plainText)
165 | }
166 | if !bytes.Equal(decrypted, decrypted1) {
167 | t.Errorf("unexpected decrypted result\ngot: %#v\nexp: %#v", decrypted, decrypted1)
168 | }
169 | })
170 | }
171 | }
172 |
173 | func TestSealAndOpenAuthenticationError(t *testing.T) {
174 | key := []byte("D249BF6DEC97B1EBD69BC4D6B3A3C49D")
175 | ci, err := NewAESCipher(key)
176 | if err != nil {
177 | t.Fatal(err)
178 | }
179 | c := ci.(*aesCipher)
180 | gcm, err := c.NewGCM(gcmStandardNonceSize, gcmTagSize)
181 | if err != nil {
182 | t.Fatal(err)
183 | }
184 | nonce := []byte{0x91, 0xc7, 0xa7, 0x54, 0x52, 0xef, 0x10, 0xdb, 0x91, 0xa8, 0x6c, 0xf9}
185 | plainText := []byte{0x01, 0x02, 0x03}
186 | additionalData := []byte{0x05, 0x05, 0x07}
187 | sealed := gcm.Seal(nil, nonce, plainText, additionalData)
188 | _, err = gcm.Open(nil, nonce, sealed, nil)
189 | if err != errOpen {
190 | t.Errorf("expected authentication error, got: %#v", err)
191 | }
192 | }
193 |
194 | func assertPanic(t *testing.T, f func()) {
195 | t.Helper()
196 | defer func() {
197 | if r := recover(); r == nil {
198 | t.Errorf("The code did not panic")
199 | }
200 | }()
201 | f()
202 | }
203 |
204 | func TestSealPanic(t *testing.T) {
205 | ci, err := NewAESCipher([]byte("D249BF6DEC97B1EBD69BC4D6B3A3C49D"))
206 | if err != nil {
207 | t.Fatal(err)
208 | }
209 | c := ci.(*aesCipher)
210 | gcm, err := c.NewGCM(gcmStandardNonceSize, gcmTagSize)
211 | if err != nil {
212 | t.Fatal(err)
213 | }
214 | assertPanic(t, func() {
215 | gcm.Seal(nil, make([]byte, gcmStandardNonceSize-1), []byte{0x01, 0x02, 0x03}, nil)
216 | })
217 | assertPanic(t, func() {
218 | // maxInt is implemented as math.MaxInt, but this constant
219 | // is only available since go1.17.
220 | // TODO: use math.MaxInt once go1.16 is no longer supported.
221 | maxInt := int((^uint(0)) >> 1)
222 | gcm.Seal(nil, make([]byte, gcmStandardNonceSize), make([]byte, maxInt), nil)
223 | })
224 | }
225 |
226 | func TestBlobEncryptBasicBlockEncryption(t *testing.T) {
227 | key := []byte{0x24, 0xcd, 0x8b, 0x13, 0x37, 0xc5, 0xc1, 0xb1, 0x0, 0xbb, 0x27, 0x40, 0x4f, 0xab, 0x5f, 0x7b, 0x2d, 0x0, 0x20, 0xf5, 0x1, 0x84, 0x4, 0xbf, 0xe3, 0xbd, 0xa1, 0xc4, 0xbf, 0x61, 0x2f, 0xc5}
228 | iv := []byte{0x91, 0xc7, 0xa7, 0x54, 0x52, 0xef, 0x10, 0xdb, 0x91, 0xa8, 0x6c, 0xf9, 0x79, 0xd5, 0xac, 0x74}
229 |
230 | block, err := NewAESCipher(key)
231 | if err != nil {
232 | t.Errorf("expected no error for aes.NewCipher, got: %s", err)
233 | }
234 |
235 | blockSize := block.BlockSize()
236 | if blockSize != 16 {
237 | t.Errorf("unexpected block size, expected 16 got: %d", blockSize)
238 | }
239 | var encryptor cipher.BlockMode
240 | if c, ok := block.(*aesCipher); ok {
241 | encryptor = c.NewCBCEncrypter(iv)
242 | if encryptor == nil {
243 | t.Error("unable to create new CBC encrypter")
244 | }
245 | }
246 |
247 | encrypted := make([]byte, 32)
248 |
249 | // First block. 16 bytes.
250 | srcBlock1 := bytes.Repeat([]byte{0x01}, 16)
251 | encryptor.CryptBlocks(encrypted, srcBlock1)
252 | if !bytes.Equal([]byte{
253 | 0x14, 0xb7, 0x3e, 0x2f, 0xd9, 0xe7, 0x69, 0x7e, 0xb7, 0xd2, 0xc3, 0x5b, 0x31, 0x9c, 0xf0, 0x59,
254 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
255 | }, encrypted) {
256 | t.Error("unexpected CryptBlocks result for first block")
257 | }
258 |
259 | // Second block. 16 bytes.
260 | srcBlock2 := bytes.Repeat([]byte{0x02}, 16)
261 | encryptor.CryptBlocks(encrypted[16:], srcBlock2)
262 | if !bytes.Equal([]byte{
263 | 0x14, 0xb7, 0x3e, 0x2f, 0xd9, 0xe7, 0x69, 0x7e, 0xb7, 0xd2, 0xc3, 0x5b, 0x31, 0x9c, 0xf0, 0x59,
264 | 0xbb, 0xd4, 0x95, 0x25, 0x21, 0x56, 0x87, 0x3b, 0xe6, 0x22, 0xe8, 0xd0, 0x19, 0xa8, 0xed, 0xcd,
265 | }, encrypted) {
266 | t.Error("unexpected CryptBlocks result for second block")
267 | }
268 |
269 | var decrypter cipher.BlockMode
270 | if c, ok := block.(*aesCipher); ok {
271 | decrypter = c.NewCBCDecrypter(iv)
272 | if decrypter == nil {
273 | t.Error("unable to create new CBC decrypter")
274 | }
275 | }
276 | plainText := append(srcBlock1, srcBlock2...)
277 | decrypted := make([]byte, len(plainText))
278 | decrypter.CryptBlocks(decrypted, encrypted[:16])
279 | decrypter.CryptBlocks(decrypted[16:], encrypted[16:])
280 | if !bytes.Equal(decrypted, plainText) {
281 | t.Errorf("unexpected decrypted result\ngot: %#v\nexp: %#v", decrypted, plainText)
282 | }
283 | }
284 |
285 | func testDecrypt(t *testing.T, resetNonce bool) {
286 | key := []byte{
287 | 0x24, 0xcd, 0x8b, 0x13, 0x37, 0xc5, 0xc1, 0xb1,
288 | 0x0, 0xbb, 0x27, 0x40, 0x4f, 0xab, 0x5f, 0x7b,
289 | 0x2d, 0x0, 0x20, 0xf5, 0x1, 0x84, 0x4, 0xbf,
290 | 0xe3, 0xbd, 0xa1, 0xc4, 0xbf, 0x61, 0x2f, 0xc5,
291 | }
292 |
293 | block, err := NewAESCipher(key)
294 | if err != nil {
295 | panic(err)
296 | }
297 |
298 | iv := []byte{
299 | 0x91, 0xc7, 0xa7, 0x54, 0x52, 0xef, 0x10, 0xdb,
300 | 0x91, 0xa8, 0x6c, 0xf9, 0x79, 0xd5, 0xac, 0x74,
301 | }
302 | var encrypter, decrypter cipher.BlockMode
303 | if c, ok := block.(*aesCipher); ok {
304 | encrypter = c.NewCBCEncrypter(iv)
305 | if encrypter == nil {
306 | t.Error("unable to create new CBC encrypter")
307 | }
308 | decrypter = c.NewCBCDecrypter(iv)
309 | if decrypter == nil {
310 | t.Error("unable to create new CBC decrypter")
311 | }
312 | if resetNonce {
313 | for i := range iv {
314 | iv[i] = 0
315 | }
316 | }
317 | }
318 |
319 | plainText := []byte{
320 | 0x54, 0x68, 0x65, 0x72, 0x65, 0x20, 0x69, 0x73,
321 | 0x20, 0x6f, 0x6e, 0x6c, 0x79, 0x20, 0x6f, 0x6e,
322 | 0x65, 0x20, 0x4c, 0x6f, 0x72, 0x64, 0x20, 0x6f,
323 | 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x52, 0x69,
324 | 0x6e, 0x67, 0x2c, 0x20, 0x6f, 0x6e, 0x6c, 0x79,
325 | 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x77, 0x68, 0x6f,
326 | 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x6e,
327 | 0x64, 0x20, 0x69, 0x74, 0x20, 0x74, 0x6f, 0x20,
328 | 0x68, 0x69, 0x73, 0x20, 0x77, 0x69, 0x6c, 0x6c,
329 | 0x2e, 0x20, 0x41, 0x6e, 0x64, 0x20, 0x68, 0x65,
330 | 0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x6e, 0x6f,
331 | 0x74, 0x20, 0x73, 0x68, 0x61, 0x72, 0x65, 0x20,
332 | 0x70, 0x6f, 0x77, 0x65, 0x72, 0x2e, 0x00, 0x00,
333 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
334 | }
335 | cipherText := make([]byte, len(plainText))
336 |
337 | encrypter.CryptBlocks(cipherText, plainText[:64])
338 | encrypter.CryptBlocks(cipherText[64:], plainText[64:])
339 |
340 | expectedCipherText := []byte{
341 | 23, 51, 192, 210, 170, 124, 30, 218,
342 | 176, 54, 70, 132, 141, 124, 3, 152,
343 | 47, 3, 37, 81, 187, 101, 197, 94,
344 | 11, 38, 128, 60, 112, 20, 235, 130,
345 | 111, 236, 176, 99, 121, 6, 221, 181,
346 | 190, 228, 150, 177, 218, 3, 196, 0,
347 | 5, 141, 169, 151, 3, 161, 64, 244,
348 | 231, 237, 252, 143, 111, 37, 68, 70,
349 | 11, 137, 220, 243, 195, 90, 182, 83,
350 | 96, 80, 122, 14, 93, 178, 62, 159,
351 | 25, 162, 200, 155, 21, 150, 6, 101,
352 | 21, 234, 12, 74, 190, 213, 159, 220,
353 | 111, 184, 94, 169, 188, 93, 38, 150,
354 | 3, 208, 185, 201, 212, 246, 238, 181,
355 | }
356 | if !bytes.Equal(expectedCipherText, cipherText) {
357 | t.Fail()
358 | }
359 |
360 | decrypted := make([]byte, len(plainText))
361 |
362 | decrypter.CryptBlocks(decrypted, cipherText[:64])
363 | decrypter.CryptBlocks(decrypted[64:], cipherText[64:])
364 |
365 | if len(decrypted) != len(plainText) {
366 | t.Fail()
367 | }
368 |
369 | if !bytes.Equal(plainText, decrypted) {
370 | t.Errorf("decryption incorrect\nexp %v, got %v\n", plainText, decrypted)
371 | }
372 | }
373 |
374 | func TestDecryptSimple(t *testing.T) {
375 | testDecrypt(t, false)
376 | }
377 |
378 | func TestDecryptInvariantReusableNonce(t *testing.T) {
379 | // Test that changing the iv slice after creating the encrypter
380 | // and decrypter doesn't change the encrypter/decrypter state."
381 | testDecrypt(t, true)
382 | }
383 |
384 | func Test_aesCipher_finalize(t *testing.T) {
385 | // Test that aesCipher.finalize does not panic if neither Encrypt nor Decrypt have been called.
386 | // This test is important because aesCipher.finalize contains logic that is normally not exercided while testing.
387 | // We can't used NewAESCipher here because the returned object will be automatically finalized by the GC
388 | // in case test execution takes long enough, and it can't be finalized twice.
389 | new(aesCipher).finalize()
390 | }
391 |
392 | func BenchmarkAES_Encrypt(b *testing.B) {
393 | key := []byte{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}
394 | in := []byte{0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d, 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34}
395 | c, err := NewAESCipher(key)
396 | if err != nil {
397 | b.Fatal("NewCipher:", err)
398 | }
399 | out := make([]byte, len(in))
400 | b.SetBytes(int64(len(out)))
401 | b.ResetTimer()
402 | b.ReportAllocs()
403 | for i := 0; i < b.N; i++ {
404 | c.Encrypt(out, in)
405 | }
406 | }
407 |
408 | func BenchmarkAES_Decrypt(b *testing.B) {
409 | key := []byte{0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}
410 | src := []byte{0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb, 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a, 0x0b, 0x32}
411 | c, err := NewAESCipher(key)
412 | if err != nil {
413 | b.Fatal("NewCipher:", err)
414 | }
415 | out := make([]byte, len(src))
416 | b.SetBytes(int64(len(src)))
417 | b.ResetTimer()
418 | b.ReportAllocs()
419 | for i := 0; i < b.N; i++ {
420 | c.Encrypt(out, src)
421 | }
422 | }
423 |
424 | func BenchmarkAESGCM_Open(b *testing.B) {
425 | const length = 64
426 | const keySize = 128 / 8
427 | buf := make([]byte, length)
428 |
429 | b.ReportAllocs()
430 | b.SetBytes(int64(len(buf)))
431 |
432 | var key = make([]byte, keySize)
433 | var nonce [12]byte
434 | var ad [13]byte
435 | c, _ := NewAESCipher(key)
436 | aesgcm, _ := c.(extraModes).NewGCM(gcmStandardNonceSize, gcmTagSize)
437 | var out []byte
438 |
439 | ct := aesgcm.Seal(nil, nonce[:], buf[:], ad[:])
440 |
441 | b.ResetTimer()
442 | for i := 0; i < b.N; i++ {
443 | out, _ = aesgcm.Open(out[:0], nonce[:], ct, ad[:])
444 | }
445 | }
446 |
447 | func BenchmarkAESGCM_Seal(b *testing.B) {
448 | const length = 64
449 | const keySize = 128 / 8
450 | buf := make([]byte, length)
451 |
452 | b.ReportAllocs()
453 | b.SetBytes(int64(len(buf)))
454 |
455 | var key = make([]byte, keySize)
456 | var nonce [12]byte
457 | var ad [13]byte
458 | c, _ := NewAESCipher(key)
459 | aesgcm, _ := c.(extraModes).NewGCM(gcmStandardNonceSize, gcmTagSize)
460 | var out []byte
461 |
462 | b.ResetTimer()
463 | for i := 0; i < b.N; i++ {
464 | out = aesgcm.Seal(out[:0], nonce[:], buf, ad[:])
465 | }
466 | }
467 |
--------------------------------------------------------------------------------
/openssl/bbig/big.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // This is a mirror of crypto/internal/boring/bbig/big.go.
6 |
7 | package bbig
8 |
9 | import (
10 | "math/big"
11 | "unsafe"
12 |
13 | "github.com/microsoft/go-crypto-openssl/openssl"
14 | )
15 |
16 | func Enc(b *big.Int) openssl.BigInt {
17 | if b == nil {
18 | return nil
19 | }
20 | x := b.Bits()
21 | if len(x) == 0 {
22 | return openssl.BigInt{}
23 | }
24 | // TODO: Use unsafe.Slice((*uint)(&x[0]), len(x)) once go1.16 is no longer supported.
25 | return (*(*[]uint)(unsafe.Pointer(&x)))[:len(x)]
26 | }
27 |
28 | func Dec(b openssl.BigInt) *big.Int {
29 | if b == nil {
30 | return nil
31 | }
32 | if len(b) == 0 {
33 | return new(big.Int)
34 | }
35 | // TODO: Use unsafe.Slice((*uint)(&b[0]), len(b)) once go1.16 is no longer supported.
36 | x := (*(*[]big.Word)(unsafe.Pointer(&b)))[:len(b)]
37 | return new(big.Int).SetBits(x)
38 | }
39 |
--------------------------------------------------------------------------------
/openssl/bbig/bridge/bridge.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | // These wrappers only exist for code reuse in places where we need the old pre-go1.19 signature.
5 |
6 | package bridge
7 |
8 | import (
9 | "encoding/asn1"
10 | "math/big"
11 |
12 | "github.com/microsoft/go-crypto-openssl/openssl"
13 | "github.com/microsoft/go-crypto-openssl/openssl/bbig"
14 | )
15 |
16 | func GenerateKeyECDSA(curve string) (X, Y, D *big.Int, err error) {
17 | x, y, d, err := openssl.GenerateKeyECDSA(curve)
18 | if err != nil {
19 | return nil, nil, nil, err
20 | }
21 | return bbig.Dec(x), bbig.Dec(y), bbig.Dec(d), nil
22 | }
23 |
24 | type ecdsaSignature struct {
25 | R, S *big.Int
26 | }
27 |
28 | func SignECDSA(priv *openssl.PrivateKeyECDSA, hash []byte) (r, s *big.Int, err error) {
29 | sig, err := openssl.SignMarshalECDSA(priv, hash)
30 | if err != nil {
31 | return nil, nil, err
32 | }
33 | var esig ecdsaSignature
34 | if _, err := asn1.Unmarshal(sig, &esig); err != nil {
35 | return nil, nil, err
36 | }
37 | return esig.R, esig.S, nil
38 | }
39 |
40 | func NewPrivateKeyECDSA(curve string, X, Y, D *big.Int) (*openssl.PrivateKeyECDSA, error) {
41 | return openssl.NewPrivateKeyECDSA(curve, bbig.Enc(X), bbig.Enc(Y), bbig.Enc(D))
42 | }
43 |
44 | func NewPublicKeyECDSA(curve string, X, Y *big.Int) (*openssl.PublicKeyECDSA, error) {
45 | return openssl.NewPublicKeyECDSA(curve, bbig.Enc(X), bbig.Enc(Y))
46 | }
47 |
48 | func VerifyECDSA(pub *openssl.PublicKeyECDSA, hash []byte, r, s *big.Int) bool {
49 | sig, err := asn1.Marshal(ecdsaSignature{r, s})
50 | if err != nil {
51 | return false
52 | }
53 | return openssl.VerifyECDSA(pub, hash, sig)
54 | }
55 |
56 | func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv *big.Int, err error) {
57 | bN, bE, bD, bP, bQ, bDp, bDq, bQinv, err1 := openssl.GenerateKeyRSA(bits)
58 | if err1 != nil {
59 | err = err1
60 | return
61 | }
62 | N = bbig.Dec(bN)
63 | E = bbig.Dec(bE)
64 | D = bbig.Dec(bD)
65 | P = bbig.Dec(bP)
66 | Q = bbig.Dec(bQ)
67 | Dp = bbig.Dec(bDp)
68 | Dq = bbig.Dec(bDq)
69 | Qinv = bbig.Dec(bQinv)
70 | return
71 | }
72 |
73 | func NewPublicKeyRSA(N, E *big.Int) (*openssl.PublicKeyRSA, error) {
74 | return openssl.NewPublicKeyRSA(bbig.Enc(N), bbig.Enc(E))
75 | }
76 |
77 | func NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv *big.Int) (*openssl.PrivateKeyRSA, error) {
78 | return openssl.NewPrivateKeyRSA(
79 | bbig.Enc(N), bbig.Enc(E), bbig.Enc(D),
80 | bbig.Enc(P), bbig.Enc(Q),
81 | bbig.Enc(Dp), bbig.Enc(Dq), bbig.Enc(Qinv),
82 | )
83 | }
84 |
--------------------------------------------------------------------------------
/openssl/big.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 | package openssl
4 |
5 | // This file does not have build constraints to
6 | // facilitate using BigInt in Go crypto.
7 | // Go crypto references BigInt unconditionally,
8 | // even if it is not finally used.
9 |
10 | // A BigInt is the raw words from a BigInt.
11 | // This definition allows us to avoid importing math/big.
12 | // Conversion between BigInt and *big.Int is in openssl/bbig.
13 | type BigInt []uint
14 |
--------------------------------------------------------------------------------
/openssl/ecdh.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | //go:build linux && !android
5 | // +build linux,!android
6 |
7 | package openssl
8 |
9 | // #include "goopenssl.h"
10 | import "C"
11 | import (
12 | "errors"
13 | "runtime"
14 | )
15 |
16 | type PublicKeyECDH struct {
17 | _pkey C.GO_EVP_PKEY_PTR
18 | bytes []byte
19 |
20 | // priv is only set when PublicKeyECDH is derived from a private key,
21 | // in which case priv's finalizer is responsible for freeing _pkey.
22 | // This ensures priv is not finalized while the public key is alive,
23 | // which could cause use-after-free and double-free behavior.
24 | //
25 | // We could avoid this altogether if using EVP_PKEY_up_ref
26 | // when instantiating a derived public key, unfortunately
27 | // it is not available on OpenSSL 1.0.2.
28 | priv *PrivateKeyECDH
29 | }
30 |
31 | func (k *PublicKeyECDH) finalize() {
32 | if k.priv == nil {
33 | C.go_openssl_EVP_PKEY_free(k._pkey)
34 | }
35 | }
36 |
37 | type PrivateKeyECDH struct {
38 | _pkey C.GO_EVP_PKEY_PTR
39 | }
40 |
41 | func (k *PrivateKeyECDH) finalize() {
42 | C.go_openssl_EVP_PKEY_free(k._pkey)
43 | }
44 |
45 | func NewPublicKeyECDH(curve string, bytes []byte) (*PublicKeyECDH, error) {
46 | if len(bytes) < 1 {
47 | return nil, errors.New("NewPublicKeyECDH: missing key")
48 | }
49 | nid, err := curveNID(curve)
50 | if err != nil {
51 | return nil, err
52 | }
53 | key := C.go_openssl_EC_KEY_new_by_curve_name(nid)
54 | if key == nil {
55 | return nil, newOpenSSLError("EC_KEY_new_by_curve_name")
56 | }
57 | var k *PublicKeyECDH
58 | defer func() {
59 | if k == nil {
60 | C.go_openssl_EC_KEY_free(key)
61 | }
62 | }()
63 | if vMajor == 1 && vMinor == 0 {
64 | // EC_KEY_oct2key does not exist on OpenSSL 1.0.2,
65 | // we have to simulate it.
66 | group := C.go_openssl_EC_KEY_get0_group(key)
67 | pt := C.go_openssl_EC_POINT_new(group)
68 | if pt == nil {
69 | return nil, newOpenSSLError("EC_POINT_new")
70 | }
71 | defer C.go_openssl_EC_POINT_free(pt)
72 | if C.go_openssl_EC_POINT_oct2point(group, pt, base(bytes), C.size_t(len(bytes)), nil) != 1 {
73 | return nil, errors.New("point not on curve")
74 | }
75 | if C.go_openssl_EC_KEY_set_public_key(key, pt) != 1 {
76 | return nil, newOpenSSLError("EC_KEY_set_public_key")
77 | }
78 | } else {
79 | if C.go_openssl_EC_KEY_oct2key(key, base(bytes), C.size_t(len(bytes)), nil) != 1 {
80 | return nil, newOpenSSLError("EC_KEY_oct2key")
81 | }
82 | }
83 | pkey, err := newEVPPKEY(key)
84 | if err != nil {
85 | return nil, err
86 | }
87 | k = &PublicKeyECDH{pkey, append([]byte(nil), bytes...), nil}
88 | runtime.SetFinalizer(k, (*PublicKeyECDH).finalize)
89 | return k, nil
90 | }
91 |
92 | func (k *PublicKeyECDH) Bytes() []byte { return k.bytes }
93 |
94 | func NewPrivateKeyECDH(curve string, bytes []byte) (*PrivateKeyECDH, error) {
95 | nid, err := curveNID(curve)
96 | if err != nil {
97 | return nil, err
98 | }
99 | b := bytesToBN(bytes)
100 | if b == nil {
101 | return nil, newOpenSSLError("BN_bin2bn")
102 | }
103 | defer C.go_openssl_BN_free(b)
104 | key := C.go_openssl_EC_KEY_new_by_curve_name(nid)
105 | if key == nil {
106 | return nil, newOpenSSLError("EC_KEY_new_by_curve_name")
107 | }
108 | var pkey C.GO_EVP_PKEY_PTR
109 | defer func() {
110 | if pkey == nil {
111 | C.go_openssl_EC_KEY_free(key)
112 | }
113 | }()
114 | if C.go_openssl_EC_KEY_set_private_key(key, b) != 1 {
115 | return nil, newOpenSSLError("EC_KEY_set_private_key")
116 | }
117 | pkey, err = newEVPPKEY(key)
118 | if err != nil {
119 | return nil, err
120 | }
121 | k := &PrivateKeyECDH{pkey}
122 | runtime.SetFinalizer(k, (*PrivateKeyECDH).finalize)
123 | return k, nil
124 | }
125 |
126 | func (k *PrivateKeyECDH) PublicKey() (*PublicKeyECDH, error) {
127 | defer runtime.KeepAlive(k)
128 | key := C.go_openssl_EVP_PKEY_get1_EC_KEY(k._pkey)
129 | if key == nil {
130 | return nil, newOpenSSLError("EVP_PKEY_get1_EC_KEY")
131 | }
132 | defer C.go_openssl_EC_KEY_free(key)
133 | group := C.go_openssl_EC_KEY_get0_group(key)
134 | if group == nil {
135 | return nil, newOpenSSLError("EC_KEY_get0_group")
136 | }
137 | pt := C.go_openssl_EC_KEY_get0_public_key(key)
138 | if pt == nil {
139 | // The public key will be nil if k has been generated using
140 | // NewPrivateKeyECDH instead of GenerateKeyECDH.
141 | //
142 | // OpenSSL does not expose any method to generate the public
143 | // key from the private key [1], so we have to calculate it here
144 | // https://github.com/openssl/openssl/issues/18437#issuecomment-1144717206
145 | pt = C.go_openssl_EC_POINT_new(group)
146 | if pt == nil {
147 | return nil, newOpenSSLError("EC_POINT_new")
148 | }
149 | defer C.go_openssl_EC_POINT_free(pt)
150 | kbig := C.go_openssl_EC_KEY_get0_private_key(key)
151 | if C.go_openssl_EC_POINT_mul(group, pt, kbig, nil, nil, nil) == 0 {
152 | return nil, newOpenSSLError("EC_POINT_mul")
153 | }
154 | }
155 | n := C.go_openssl_EC_POINT_point2oct(group, pt, C.GO_POINT_CONVERSION_UNCOMPRESSED, nil, 0, nil)
156 | if n == 0 {
157 | return nil, newOpenSSLError("EC_POINT_point2oct")
158 | }
159 | bytes := make([]byte, n)
160 | n = C.go_openssl_EC_POINT_point2oct(group, pt, C.GO_POINT_CONVERSION_UNCOMPRESSED, base(bytes), C.size_t(len(bytes)), nil)
161 | if int(n) != len(bytes) {
162 | return nil, newOpenSSLError("EC_POINT_point2oct")
163 | }
164 | pub := &PublicKeyECDH{k._pkey, bytes, k}
165 | // Note: Same as in NewPublicKeyECDH regarding finalizer and KeepAlive.
166 | runtime.SetFinalizer(pub, (*PublicKeyECDH).finalize)
167 | return pub, nil
168 | }
169 |
170 | func ECDH(priv *PrivateKeyECDH, pub *PublicKeyECDH) ([]byte, error) {
171 | defer runtime.KeepAlive(priv)
172 | defer runtime.KeepAlive(pub)
173 | ctx := C.go_openssl_EVP_PKEY_CTX_new(priv._pkey, nil)
174 | if ctx == nil {
175 | return nil, newOpenSSLError("EVP_PKEY_CTX_new")
176 | }
177 | defer C.go_openssl_EVP_PKEY_CTX_free(ctx)
178 | if C.go_openssl_EVP_PKEY_derive_init(ctx) != 1 {
179 | return nil, newOpenSSLError("EVP_PKEY_derive_init")
180 | }
181 | if C.go_openssl_EVP_PKEY_derive_set_peer(ctx, pub._pkey) != 1 {
182 | return nil, newOpenSSLError("EVP_PKEY_derive_set_peer")
183 | }
184 | var outLen C.size_t
185 | if C.go_openssl_EVP_PKEY_derive(ctx, nil, &outLen) != 1 {
186 | return nil, newOpenSSLError("EVP_PKEY_derive_init")
187 | }
188 | out := make([]byte, outLen)
189 | if C.go_openssl_EVP_PKEY_derive(ctx, base(out), &outLen) != 1 {
190 | return nil, newOpenSSLError("EVP_PKEY_derive_init")
191 | }
192 | return out, nil
193 | }
194 |
195 | func GenerateKeyECDH(curve string) (*PrivateKeyECDH, []byte, error) {
196 | pkey, err := generateEVPPKey(C.GO_EVP_PKEY_EC, 0, curve)
197 | if err != nil {
198 | return nil, nil, err
199 | }
200 | var k *PrivateKeyECDH
201 | defer func() {
202 | if k == nil {
203 | C.go_openssl_EVP_PKEY_free(pkey)
204 | }
205 | }()
206 | key := C.go_openssl_EVP_PKEY_get1_EC_KEY(pkey)
207 | if key == nil {
208 | return nil, nil, newOpenSSLError("EVP_PKEY_get1_EC_KEY")
209 | }
210 | defer C.go_openssl_EC_KEY_free(key)
211 | b := C.go_openssl_EC_KEY_get0_private_key(key)
212 | if b == nil {
213 | return nil, nil, newOpenSSLError("EC_KEY_get0_private_key")
214 | }
215 | bits := C.go_openssl_EVP_PKEY_get_bits(pkey)
216 | out := make([]byte, (bits+7)/8)
217 | if C.go_openssl_BN_bn2binpad(b, base(out), C.int(len(out))) == 0 {
218 | return nil, nil, newOpenSSLError("BN_bn2binpad")
219 | }
220 | k = &PrivateKeyECDH{pkey}
221 | runtime.SetFinalizer(k, (*PrivateKeyECDH).finalize)
222 | return k, out, nil
223 | }
224 |
--------------------------------------------------------------------------------
/openssl/ecdh_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | //go:build linux && !android
5 | // +build linux,!android
6 |
7 | package openssl_test
8 |
9 | import (
10 | "bytes"
11 | "encoding/hex"
12 | "testing"
13 |
14 | "github.com/microsoft/go-crypto-openssl/openssl"
15 | )
16 |
17 | func TestECDH(t *testing.T) {
18 | for _, tt := range []string{"P-256", "P-384", "P-521"} {
19 | t.Run(tt, func(t *testing.T) {
20 | name := tt
21 | aliceKey, alicPrivBytes, err := openssl.GenerateKeyECDH(name)
22 | if err != nil {
23 | t.Fatal(err)
24 | }
25 | bobKey, _, err := openssl.GenerateKeyECDH(name)
26 | if err != nil {
27 | t.Fatal(err)
28 | }
29 |
30 | alicePubKeyFromPriv, err := aliceKey.PublicKey()
31 | if err != nil {
32 | t.Fatal(err)
33 | }
34 | alicePubBytes := alicePubKeyFromPriv.Bytes()
35 | want := len(alicPrivBytes)
36 | var got int
37 | if tt == "X25519" {
38 | got = len(alicePubBytes)
39 | } else {
40 | got = (len(alicePubBytes) - 1) / 2 // subtract encoding prefix and divide by the number of components
41 | }
42 | if want != got {
43 | t.Fatalf("public key size mismatch: want: %v, got: %v", want, got)
44 | }
45 | alicePubKey, err := openssl.NewPublicKeyECDH(name, alicePubBytes)
46 | if err != nil {
47 | t.Error(err)
48 | }
49 |
50 | bobPubKeyFromPriv, err := bobKey.PublicKey()
51 | if err != nil {
52 | t.Error(err)
53 | }
54 | _, err = openssl.NewPublicKeyECDH(name, bobPubKeyFromPriv.Bytes())
55 | if err != nil {
56 | t.Error(err)
57 | }
58 |
59 | bobSecret, err := openssl.ECDH(bobKey, alicePubKey)
60 | if err != nil {
61 | t.Fatal(err)
62 | }
63 | aliceSecret, err := openssl.ECDH(aliceKey, bobPubKeyFromPriv)
64 | if err != nil {
65 | t.Fatal(err)
66 | }
67 |
68 | if !bytes.Equal(bobSecret, aliceSecret) {
69 | t.Error("two ECDH computations came out different")
70 | }
71 | })
72 | }
73 | }
74 |
75 | // The following vectors have been copied from
76 | // https://github.com/golang/go/blob/bb0d8297d76cb578baad8fa1485565d9acf44cc5/src/crypto/ecdh/ecdh_test.go.
77 |
78 | var ecdhvectors = []struct {
79 | Name string
80 | PrivateKey, PublicKey string
81 | PeerPublicKey string
82 | SharedSecret string
83 | }{
84 | // NIST vectors from CAVS 14.1, ECC CDH Primitive (SP800-56A).
85 | {
86 | Name: "P-256",
87 | PrivateKey: "7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534",
88 | PublicKey: "04ead218590119e8876b29146ff89ca61770c4edbbf97d38ce385ed281d8a6b230" +
89 | "28af61281fd35e2fa7002523acc85a429cb06ee6648325389f59edfce1405141",
90 | PeerPublicKey: "04700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287" +
91 | "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac",
92 | SharedSecret: "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b",
93 | },
94 | {
95 | Name: "P-384",
96 | PrivateKey: "3cc3122a68f0d95027ad38c067916ba0eb8c38894d22e1b15618b6818a661774ad463b205da88cf699ab4d43c9cf98a1",
97 | PublicKey: "049803807f2f6d2fd966cdd0290bd410c0190352fbec7ff6247de1302df86f25d34fe4a97bef60cff548355c015dbb3e5f" +
98 | "ba26ca69ec2f5b5d9dad20cc9da711383a9dbe34ea3fa5a2af75b46502629ad54dd8b7d73a8abb06a3a3be47d650cc99",
99 | PeerPublicKey: "04a7c76b970c3b5fe8b05d2838ae04ab47697b9eaf52e764592efda27fe7513272734466b400091adbf2d68c58e0c50066" +
100 | "ac68f19f2e1cb879aed43a9969b91a0839c4c38a49749b661efedf243451915ed0905a32b060992b468c64766fc8437a",
101 | SharedSecret: "5f9d29dc5e31a163060356213669c8ce132e22f57c9a04f40ba7fcead493b457e5621e766c40a2e3d4d6a04b25e533f1",
102 | },
103 | // For some reason all field elements in the test vector (both scalars and
104 | // base field elements), but not the shared secret output, have two extra
105 | // leading zero bytes (which in big-endian are irrelevant). Removed here.
106 | {
107 | Name: "P-521",
108 | PrivateKey: "017eecc07ab4b329068fba65e56a1f8890aa935e57134ae0ffcce802735151f4eac6564f6ee9974c5e6887a1fefee5743ae2241bfeb95d5ce31ddcb6f9edb4d6fc47",
109 | PublicKey: "0400602f9d0cf9e526b29e22381c203c48a886c2b0673033366314f1ffbcba240ba42f4ef38a76174635f91e6b4ed34275eb01c8467d05ca80315bf1a7bbd945f550a5" +
110 | "01b7c85f26f5d4b2d7355cf6b02117659943762b6d1db5ab4f1dbc44ce7b2946eb6c7de342962893fd387d1b73d7a8672d1f236961170b7eb3579953ee5cdc88cd2d",
111 | PeerPublicKey: "0400685a48e86c79f0f0875f7bc18d25eb5fc8c0b07e5da4f4370f3a9490340854334b1e1b87fa395464c60626124a4e70d0f785601d37c09870ebf176666877a2046d" +
112 | "01ba52c56fc8776d9e8f5db4f0cc27636d0b741bbe05400697942e80b739884a83bde99e0f6716939e632bc8986fa18dccd443a348b6c3e522497955a4f3c302f676",
113 | SharedSecret: "005fc70477c3e63bc3954bd0df3ea0d1f41ee21746ed95fc5e1fdf90930d5e136672d72cc770742d1711c3c3a4c334a0ad9759436a4d3c5bf6e74b9578fac148c831",
114 | },
115 | }
116 |
117 | func TestVectors(t *testing.T) {
118 | for _, tt := range ecdhvectors {
119 | t.Run(tt.Name, func(t *testing.T) {
120 | key, err := openssl.NewPrivateKeyECDH(tt.Name, hexDecode(t, tt.PrivateKey))
121 | if err != nil {
122 | t.Fatal(err)
123 | }
124 | pub, err := key.PublicKey()
125 | if err != nil {
126 | t.Fatal(err)
127 | }
128 | if !bytes.Equal(pub.Bytes(), hexDecode(t, tt.PublicKey)) {
129 | t.Error("public key derived from the private key does not match")
130 | }
131 | peer, err := openssl.NewPublicKeyECDH(tt.Name, hexDecode(t, tt.PeerPublicKey))
132 | if err != nil {
133 | t.Fatal(err)
134 | }
135 | secret, err := openssl.ECDH(key, peer)
136 | if err != nil {
137 | t.Fatal(err)
138 | }
139 | if !bytes.Equal(secret, hexDecode(t, tt.SharedSecret)) {
140 | t.Error("shared secret does not match")
141 | }
142 | })
143 | }
144 | }
145 |
146 | func hexDecode(t *testing.T, s string) []byte {
147 | b, err := hex.DecodeString(s)
148 | if err != nil {
149 | t.Fatal("invalid hex string:", s)
150 | }
151 | return b
152 | }
153 |
--------------------------------------------------------------------------------
/openssl/ecdsa.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | //go:build linux && !android
5 | // +build linux,!android
6 |
7 | package openssl
8 |
9 | // #include "goopenssl.h"
10 | import "C"
11 | import (
12 | "errors"
13 | "runtime"
14 | )
15 |
16 | type PrivateKeyECDSA struct {
17 | // _pkey MUST NOT be accessed directly. Instead, use the withKey method.
18 | _pkey C.GO_EVP_PKEY_PTR
19 | }
20 |
21 | func (k *PrivateKeyECDSA) finalize() {
22 | C.go_openssl_EVP_PKEY_free(k._pkey)
23 | }
24 |
25 | func (k *PrivateKeyECDSA) withKey(f func(C.GO_EVP_PKEY_PTR) C.int) C.int {
26 | defer runtime.KeepAlive(k)
27 | return f(k._pkey)
28 | }
29 |
30 | type PublicKeyECDSA struct {
31 | // _pkey MUST NOT be accessed directly. Instead, use the withKey method.
32 | _pkey C.GO_EVP_PKEY_PTR
33 | }
34 |
35 | func (k *PublicKeyECDSA) finalize() {
36 | C.go_openssl_EVP_PKEY_free(k._pkey)
37 | }
38 |
39 | func (k *PublicKeyECDSA) withKey(f func(C.GO_EVP_PKEY_PTR) C.int) C.int {
40 | defer runtime.KeepAlive(k)
41 | return f(k._pkey)
42 | }
43 |
44 | var errUnknownCurve = errors.New("openssl: unknown elliptic curve")
45 | var errUnsupportedCurve = errors.New("openssl: unsupported elliptic curve")
46 |
47 | func curveNID(curve string) (C.int, error) {
48 | switch curve {
49 | case "P-224":
50 | return C.GO_NID_secp224r1, nil
51 | case "P-256":
52 | return C.GO_NID_X9_62_prime256v1, nil
53 | case "P-384":
54 | return C.GO_NID_secp384r1, nil
55 | case "P-521":
56 | return C.GO_NID_secp521r1, nil
57 | }
58 | return 0, errUnknownCurve
59 | }
60 |
61 | func NewPublicKeyECDSA(curve string, X, Y BigInt) (*PublicKeyECDSA, error) {
62 | pkey, err := newECKey(curve, X, Y, nil)
63 | if err != nil {
64 | return nil, err
65 | }
66 | k := &PublicKeyECDSA{_pkey: pkey}
67 | // Note: Because of the finalizer, any time k.key is passed to cgo,
68 | // that call must be followed by a call to runtime.KeepAlive(k),
69 | // to make sure k is not collected (and finalized) before the cgo
70 | // call returns.
71 | runtime.SetFinalizer(k, (*PublicKeyECDSA).finalize)
72 | return k, nil
73 | }
74 |
75 | func newECKey(curve string, X, Y, D BigInt) (C.GO_EVP_PKEY_PTR, error) {
76 | nid, err := curveNID(curve)
77 | if err != nil {
78 | return nil, err
79 | }
80 | var bx, by, bd C.GO_BIGNUM_PTR
81 | defer func() {
82 | if bx != nil {
83 | C.go_openssl_BN_free(bx)
84 | }
85 | if by != nil {
86 | C.go_openssl_BN_free(by)
87 | }
88 | if bd != nil {
89 | C.go_openssl_BN_free(bd)
90 | }
91 | }()
92 | bx = bigToBN(X)
93 | by = bigToBN(Y)
94 | bd = bigToBN(D)
95 | if bx == nil || by == nil || (D != nil && bd == nil) {
96 | return nil, newOpenSSLError("BN_lebin2bn failed")
97 | }
98 | key := C.go_openssl_EC_KEY_new_by_curve_name(nid)
99 | if key == nil {
100 | return nil, newOpenSSLError("EC_KEY_new_by_curve_name failed")
101 | }
102 | var pkey C.GO_EVP_PKEY_PTR
103 | defer func() {
104 | if pkey == nil {
105 | defer C.go_openssl_EC_KEY_free(key)
106 | }
107 | }()
108 | if C.go_openssl_EC_KEY_set_public_key_affine_coordinates(key, bx, by) != 1 {
109 | return nil, newOpenSSLError("EC_KEY_set_public_key_affine_coordinates failed")
110 | }
111 | if D != nil && C.go_openssl_EC_KEY_set_private_key(key, bd) != 1 {
112 | return nil, newOpenSSLError("EC_KEY_set_private_key failed")
113 | }
114 | pkey, err = newEVPPKEY(key)
115 | if err != nil {
116 | return nil, err
117 | }
118 | return pkey, nil
119 | }
120 |
121 | func NewPrivateKeyECDSA(curve string, X, Y, D BigInt) (*PrivateKeyECDSA, error) {
122 | pkey, err := newECKey(curve, X, Y, D)
123 | if err != nil {
124 | return nil, err
125 | }
126 | k := &PrivateKeyECDSA{_pkey: pkey}
127 | // Note: Because of the finalizer, any time k.key is passed to cgo,
128 | // that call must be followed by a call to runtime.KeepAlive(k),
129 | // to make sure k is not collected (and finalized) before the cgo
130 | // call returns.
131 | runtime.SetFinalizer(k, (*PrivateKeyECDSA).finalize)
132 | return k, nil
133 | }
134 |
135 | func SignMarshalECDSA(priv *PrivateKeyECDSA, hash []byte) ([]byte, error) {
136 | return evpSign(priv.withKey, 0, 0, 0, hash)
137 | }
138 |
139 | func VerifyECDSA(pub *PublicKeyECDSA, hash []byte, sig []byte) bool {
140 | return evpVerify(pub.withKey, 0, 0, 0, sig, hash) == nil
141 | }
142 |
143 | func GenerateKeyECDSA(curve string) (X, Y, D BigInt, err error) {
144 | pkey, err := generateEVPPKey(C.GO_EVP_PKEY_EC, 0, curve)
145 | if err != nil {
146 | return nil, nil, nil, err
147 | }
148 | defer C.go_openssl_EVP_PKEY_free(pkey)
149 | key := C.go_openssl_EVP_PKEY_get1_EC_KEY(pkey)
150 | if key == nil {
151 | return nil, nil, nil, newOpenSSLError("EVP_PKEY_get1_EC_KEY failed")
152 | }
153 | defer C.go_openssl_EC_KEY_free(key)
154 | group := C.go_openssl_EC_KEY_get0_group(key)
155 | pt := C.go_openssl_EC_KEY_get0_public_key(key)
156 | bd := C.go_openssl_EC_KEY_get0_private_key(key)
157 | if pt == nil || bd == nil {
158 | return nil, nil, nil, newOpenSSLError("EC_KEY_get0_private_key failed")
159 | }
160 | bx := C.go_openssl_BN_new()
161 | if bx == nil {
162 | return nil, nil, nil, newOpenSSLError("BN_new failed")
163 | }
164 | defer C.go_openssl_BN_free(bx)
165 | by := C.go_openssl_BN_new()
166 | if by == nil {
167 | return nil, nil, nil, newOpenSSLError("BN_new failed")
168 | }
169 | defer C.go_openssl_BN_free(by)
170 | if C.go_openssl_EC_POINT_get_affine_coordinates_GFp(group, pt, bx, by, nil) == 0 {
171 | return nil, nil, nil, newOpenSSLError("EC_POINT_get_affine_coordinates_GFp failed")
172 | }
173 | return bnToBig(bx), bnToBig(by), bnToBig(bd), nil
174 | }
175 |
--------------------------------------------------------------------------------
/openssl/ecdsa_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | //go:build linux && !android
5 | // +build linux,!android
6 |
7 | package openssl_test
8 |
9 | import (
10 | "crypto/ecdsa"
11 | "crypto/elliptic"
12 | "testing"
13 |
14 | "github.com/microsoft/go-crypto-openssl/openssl/bbig/bridge"
15 | )
16 |
17 | func testAllCurves(t *testing.T, f func(*testing.T, elliptic.Curve)) {
18 | tests := []struct {
19 | name string
20 | curve elliptic.Curve
21 | }{
22 | {"P256", elliptic.P256()},
23 | {"P224", elliptic.P224()},
24 | {"P384", elliptic.P384()},
25 | {"P521", elliptic.P521()},
26 | }
27 | for _, test := range tests {
28 | curve := test.curve
29 | t.Run(test.name, func(t *testing.T) {
30 | t.Parallel()
31 | f(t, curve)
32 | })
33 | }
34 | }
35 |
36 | func TestECDSAKeyGeneration(t *testing.T) {
37 | testAllCurves(t, testECDSAKeyGeneration)
38 | }
39 |
40 | func testECDSAKeyGeneration(t *testing.T, c elliptic.Curve) {
41 | priv, err := generateKeycurve(c)
42 | if err != nil {
43 | t.Fatal(err)
44 | }
45 | if !c.IsOnCurve(priv.PublicKey.X, priv.PublicKey.Y) {
46 | t.Errorf("public key invalid: %s", err)
47 | }
48 | }
49 |
50 | func TestECDSASignAndVerify(t *testing.T) {
51 | testAllCurves(t, testECDSASignAndVerify)
52 | }
53 |
54 | func testECDSASignAndVerify(t *testing.T, c elliptic.Curve) {
55 | key, err := generateKeycurve(c)
56 | if err != nil {
57 | t.Fatal(err)
58 | }
59 |
60 | priv, err := bridge.NewPrivateKeyECDSA(key.Params().Name, key.X, key.Y, key.D)
61 | if err != nil {
62 | t.Fatal(err)
63 | }
64 | hashed := []byte("testing")
65 | r, s, err := bridge.SignECDSA(priv, hashed)
66 | if err != nil {
67 | t.Errorf("error signing: %s", err)
68 | return
69 | }
70 |
71 | pub, err := bridge.NewPublicKeyECDSA(key.Params().Name, key.X, key.Y)
72 | if err != nil {
73 | t.Fatal(err)
74 | }
75 | if !bridge.VerifyECDSA(pub, hashed, r, s) {
76 | t.Errorf("Verify failed")
77 | }
78 | hashed[0] ^= 0xff
79 | if bridge.VerifyECDSA(pub, hashed, r, s) {
80 | t.Errorf("Verify succeeded despite intentionally invalid hash!")
81 | }
82 | }
83 |
84 | func generateKeycurve(c elliptic.Curve) (*ecdsa.PrivateKey, error) {
85 | x, y, d, err := bridge.GenerateKeyECDSA(c.Params().Name)
86 | if err != nil {
87 | return nil, err
88 | }
89 | return &ecdsa.PrivateKey{PublicKey: ecdsa.PublicKey{Curve: c, X: x, Y: y}, D: d}, nil
90 | }
91 |
--------------------------------------------------------------------------------
/openssl/evpkey.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | //go:build linux && !android
5 | // +build linux,!android
6 |
7 | package openssl
8 |
9 | // #include "goopenssl.h"
10 | import "C"
11 | import (
12 | "crypto"
13 | "errors"
14 | "hash"
15 | "unsafe"
16 | )
17 |
18 | // hashToMD converts a hash.Hash implementation from this package to a GO_EVP_MD_PTR.
19 | func hashToMD(h hash.Hash) C.GO_EVP_MD_PTR {
20 | switch h.(type) {
21 | case *sha1Hash:
22 | return C.go_openssl_EVP_sha1()
23 | case *sha224Hash:
24 | return C.go_openssl_EVP_sha224()
25 | case *sha256Hash:
26 | return C.go_openssl_EVP_sha256()
27 | case *sha384Hash:
28 | return C.go_openssl_EVP_sha384()
29 | case *sha512Hash:
30 | return C.go_openssl_EVP_sha512()
31 | }
32 | return nil
33 | }
34 |
35 | // cryptoHashToMD converts a crypto.Hash to a GO_EVP_MD_PTR.
36 | func cryptoHashToMD(ch crypto.Hash) C.GO_EVP_MD_PTR {
37 | switch ch {
38 | case crypto.MD5:
39 | return C.go_openssl_EVP_md5()
40 | case crypto.MD5SHA1:
41 | if vMajor == 1 && vMinor == 0 {
42 | // MD5SHA1 is not implemented in OpenSSL 1.0.2.
43 | // It is implemented in higher versions but without FIPS support.
44 | // It is considered a deprecated digest, not approved by FIPS 140-2
45 | // and only used in pre-TLS 1.2, so we would rather not support it
46 | // if using 1.0.2 than than implement something that is not properly validated.
47 | return nil
48 | }
49 | return C.go_openssl_EVP_md5_sha1()
50 | case crypto.SHA1:
51 | return C.go_openssl_EVP_sha1()
52 | case crypto.SHA224:
53 | return C.go_openssl_EVP_sha224()
54 | case crypto.SHA256:
55 | return C.go_openssl_EVP_sha256()
56 | case crypto.SHA384:
57 | return C.go_openssl_EVP_sha384()
58 | case crypto.SHA512:
59 | return C.go_openssl_EVP_sha512()
60 | }
61 | return nil
62 | }
63 |
64 | func generateEVPPKey(id C.int, bits int, curve string) (C.GO_EVP_PKEY_PTR, error) {
65 | if (bits == 0 && curve == "") || (bits != 0 && curve != "") {
66 | return nil, fail("incorrect generateEVPPKey parameters")
67 | }
68 | ctx := C.go_openssl_EVP_PKEY_CTX_new_id(id, nil)
69 | if ctx == nil {
70 | return nil, newOpenSSLError("EVP_PKEY_CTX_new_id failed")
71 | }
72 | defer C.go_openssl_EVP_PKEY_CTX_free(ctx)
73 | if C.go_openssl_EVP_PKEY_keygen_init(ctx) != 1 {
74 | return nil, newOpenSSLError("EVP_PKEY_keygen_init failed")
75 | }
76 | if bits != 0 {
77 | if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, id, -1, C.GO_EVP_PKEY_CTRL_RSA_KEYGEN_BITS, C.int(bits), nil) != 1 {
78 | return nil, newOpenSSLError("EVP_PKEY_CTX_ctrl failed")
79 | }
80 | }
81 | if curve != "" {
82 | nid, err := curveNID(curve)
83 | if err != nil {
84 | return nil, err
85 | }
86 | if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, id, -1, C.GO_EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID, nid, nil) != 1 {
87 | return nil, newOpenSSLError("EVP_PKEY_CTX_ctrl failed")
88 | }
89 | }
90 | var pkey C.GO_EVP_PKEY_PTR
91 | if C.go_openssl_EVP_PKEY_keygen(ctx, &pkey) != 1 {
92 | return nil, newOpenSSLError("EVP_PKEY_keygen failed")
93 | }
94 | return pkey, nil
95 | }
96 |
97 | type withKeyFunc func(func(C.GO_EVP_PKEY_PTR) C.int) C.int
98 | type initFunc func(C.GO_EVP_PKEY_CTX_PTR) error
99 | type cryptFunc func(C.GO_EVP_PKEY_CTX_PTR, *C.uchar, *C.size_t, *C.uchar, C.size_t) error
100 | type verifyFunc func(C.GO_EVP_PKEY_CTX_PTR, *C.uchar, C.size_t, *C.uchar, C.size_t) error
101 |
102 | func setupEVP(withKey withKeyFunc, padding C.int,
103 | h, mgfHash hash.Hash, label []byte, saltLen C.int, ch crypto.Hash,
104 | init initFunc) (_ C.GO_EVP_PKEY_CTX_PTR, err error) {
105 | var ctx C.GO_EVP_PKEY_CTX_PTR
106 | withKey(func(pkey C.GO_EVP_PKEY_PTR) C.int {
107 | ctx = C.go_openssl_EVP_PKEY_CTX_new(pkey, nil)
108 | return 1
109 | })
110 | if ctx == nil {
111 | return nil, newOpenSSLError("EVP_PKEY_CTX_new failed")
112 | }
113 | defer func() {
114 | if err != nil {
115 | if ctx != nil {
116 | C.go_openssl_EVP_PKEY_CTX_free(ctx)
117 | ctx = nil
118 | }
119 | }
120 | }()
121 | if err := init(ctx); err != nil {
122 | return nil, err
123 | }
124 | if padding == 0 {
125 | return ctx, nil
126 | }
127 | // Each padding type has its own requirements in terms of when to apply the padding,
128 | // so it can't be just set at this point.
129 | setPadding := func() error {
130 | if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, C.GO_EVP_PKEY_RSA, -1, C.GO_EVP_PKEY_CTRL_RSA_PADDING, padding, nil) != 1 {
131 | return newOpenSSLError("EVP_PKEY_CTX_ctrl failed")
132 | }
133 | return nil
134 | }
135 | switch padding {
136 | case C.GO_RSA_PKCS1_OAEP_PADDING:
137 | md := hashToMD(h)
138 | if md == nil {
139 | return nil, errors.New("crypto/rsa: unsupported hash function")
140 | }
141 | var mgfMD C.GO_EVP_MD_PTR
142 | if mgfHash != nil {
143 | // mgfHash is optional, but if it is set it must match a supported hash function.
144 | mgfMD = hashToMD(mgfHash)
145 | if mgfMD == nil {
146 | return nil, errors.New("crypto/rsa: unsupported hash function")
147 | }
148 | }
149 | // setPadding must happen before setting EVP_PKEY_CTRL_RSA_OAEP_MD.
150 | if err := setPadding(); err != nil {
151 | return nil, err
152 | }
153 | if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, C.GO_EVP_PKEY_RSA, -1, C.GO_EVP_PKEY_CTRL_RSA_OAEP_MD, 0, unsafe.Pointer(md)) != 1 {
154 | return nil, newOpenSSLError("EVP_PKEY_CTX_ctrl failed")
155 | }
156 | if mgfHash != nil {
157 | if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, C.GO_EVP_PKEY_RSA, -1, C.GO_EVP_PKEY_CTRL_RSA_MGF1_MD, 0, unsafe.Pointer(mgfMD)) != 1 {
158 | return nil, newOpenSSLError("EVP_PKEY_CTX_ctrl failed")
159 | }
160 | }
161 | // ctx takes ownership of label, so malloc a copy for OpenSSL to free.
162 | // OpenSSL 1.1.1 and higher does not take ownership of the label if the length is zero,
163 | // so better avoid the allocation.
164 | var clabel *C.uchar
165 | if len(label) > 0 {
166 | // Go guarantees C.malloc never returns nil.
167 | clabel = (*C.uchar)(C.malloc(C.size_t(len(label))))
168 | copy((*[1 << 30]byte)(unsafe.Pointer(clabel))[:len(label)], label)
169 | }
170 | var ret C.int
171 | if vMajor == 1 {
172 | ret = C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, C.GO_EVP_PKEY_RSA, -1, C.GO_EVP_PKEY_CTRL_RSA_OAEP_LABEL, C.int(len(label)), unsafe.Pointer(clabel))
173 | } else {
174 | // OpenSSL 3 implements EVP_PKEY_CTX_set0_rsa_oaep_label as a function,
175 | // instead of a macro around EVP_PKEY_CTX_ctrl, and it takes a different
176 | // code path when the implementation is provided by FIPS provider.
177 | ret = C.go_openssl_EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, unsafe.Pointer(clabel), C.int(len(label)))
178 | }
179 | if ret != 1 {
180 | if clabel != nil {
181 | C.free(unsafe.Pointer(clabel))
182 | }
183 | return nil, newOpenSSLError("EVP_PKEY_CTX_ctrl failed")
184 | }
185 | case C.GO_RSA_PKCS1_PSS_PADDING:
186 | md := cryptoHashToMD(ch)
187 | if md == nil {
188 | return nil, errors.New("crypto/rsa: unsupported hash function")
189 | }
190 | if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, C.GO_EVP_PKEY_RSA, -1, C.GO_EVP_PKEY_CTRL_MD, 0, unsafe.Pointer(md)) != 1 {
191 | return nil, newOpenSSLError("EVP_PKEY_CTX_ctrl failed")
192 | }
193 | // setPadding must happen after setting EVP_PKEY_CTRL_MD.
194 | if err := setPadding(); err != nil {
195 | return nil, err
196 | }
197 | if saltLen != 0 {
198 | if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, C.GO_EVP_PKEY_RSA, -1, C.GO_EVP_PKEY_CTRL_RSA_PSS_SALTLEN, saltLen, nil) != 1 {
199 | return nil, newOpenSSLError("EVP_PKEY_CTX_ctrl failed")
200 | }
201 | }
202 |
203 | case C.GO_RSA_PKCS1_PADDING:
204 | if ch != 0 {
205 | // We support unhashed messages.
206 | md := cryptoHashToMD(ch)
207 | if md == nil {
208 | return nil, errors.New("crypto/rsa: unsupported hash function")
209 | }
210 | if C.go_openssl_EVP_PKEY_CTX_ctrl(ctx, -1, -1, C.GO_EVP_PKEY_CTRL_MD, 0, unsafe.Pointer(md)) != 1 {
211 | return nil, newOpenSSLError("EVP_PKEY_CTX_ctrl failed")
212 | }
213 | if err := setPadding(); err != nil {
214 | return nil, err
215 | }
216 | }
217 | default:
218 | if err := setPadding(); err != nil {
219 | return nil, err
220 | }
221 | }
222 | return ctx, nil
223 | }
224 |
225 | func cryptEVP(withKey withKeyFunc, padding C.int,
226 | h, mgfHash hash.Hash, label []byte, saltLen C.int, ch crypto.Hash,
227 | init initFunc, crypt cryptFunc, in []byte) ([]byte, error) {
228 |
229 | ctx, err := setupEVP(withKey, padding, h, mgfHash, label, saltLen, ch, init)
230 | if err != nil {
231 | return nil, err
232 | }
233 | defer C.go_openssl_EVP_PKEY_CTX_free(ctx)
234 | pkeySize := withKey(func(pkey C.GO_EVP_PKEY_PTR) C.int {
235 | return C.go_openssl_EVP_PKEY_get_size(pkey)
236 | })
237 | outLen := C.size_t(pkeySize)
238 | out := make([]byte, pkeySize)
239 | if err := crypt(ctx, base(out), &outLen, base(in), C.size_t(len(in))); err != nil {
240 | return nil, err
241 | }
242 | // The size returned by EVP_PKEY_get_size() is only preliminary and not exact,
243 | // so the final contents of the out buffer may be smaller.
244 | return out[:outLen], nil
245 | }
246 |
247 | func verifyEVP(withKey withKeyFunc, padding C.int,
248 | h hash.Hash, label []byte, saltLen C.int, ch crypto.Hash,
249 | init initFunc, verify verifyFunc,
250 | sig, in []byte) error {
251 |
252 | ctx, err := setupEVP(withKey, padding, h, nil, label, saltLen, ch, init)
253 | if err != nil {
254 | return err
255 | }
256 | defer C.go_openssl_EVP_PKEY_CTX_free(ctx)
257 | return verify(ctx, base(sig), C.size_t(len(sig)), base(in), C.size_t(len(in)))
258 | }
259 |
260 | func evpEncrypt(withKey withKeyFunc, padding C.int, h, mgfHash hash.Hash, label, msg []byte) ([]byte, error) {
261 | encryptInit := func(ctx C.GO_EVP_PKEY_CTX_PTR) error {
262 | if ret := C.go_openssl_EVP_PKEY_encrypt_init(ctx); ret != 1 {
263 | return newOpenSSLError("EVP_PKEY_encrypt_init failed")
264 | }
265 | return nil
266 | }
267 | encrypt := func(ctx C.GO_EVP_PKEY_CTX_PTR, out *C.uchar, outLen *C.size_t, in *C.uchar, inLen C.size_t) error {
268 | if ret := C.go_openssl_EVP_PKEY_encrypt(ctx, out, outLen, in, inLen); ret != 1 {
269 | return newOpenSSLError("EVP_PKEY_encrypt failed")
270 | }
271 | return nil
272 | }
273 | return cryptEVP(withKey, padding, h, mgfHash, label, 0, 0, encryptInit, encrypt, msg)
274 | }
275 |
276 | func evpDecrypt(withKey withKeyFunc, padding C.int, h, mgfHash hash.Hash, label, msg []byte) ([]byte, error) {
277 | decryptInit := func(ctx C.GO_EVP_PKEY_CTX_PTR) error {
278 | if ret := C.go_openssl_EVP_PKEY_decrypt_init(ctx); ret != 1 {
279 | return newOpenSSLError("EVP_PKEY_decrypt_init failed")
280 | }
281 | return nil
282 | }
283 | decrypt := func(ctx C.GO_EVP_PKEY_CTX_PTR, out *C.uchar, outLen *C.size_t, in *C.uchar, inLen C.size_t) error {
284 | if ret := C.go_openssl_EVP_PKEY_decrypt(ctx, out, outLen, in, inLen); ret != 1 {
285 | return newOpenSSLError("EVP_PKEY_decrypt failed")
286 | }
287 | return nil
288 | }
289 | return cryptEVP(withKey, padding, h, mgfHash, label, 0, 0, decryptInit, decrypt, msg)
290 | }
291 |
292 | func evpSign(withKey withKeyFunc, padding C.int, saltLen C.int, h crypto.Hash, hashed []byte) ([]byte, error) {
293 | signtInit := func(ctx C.GO_EVP_PKEY_CTX_PTR) error {
294 | if ret := C.go_openssl_EVP_PKEY_sign_init(ctx); ret != 1 {
295 | return newOpenSSLError("EVP_PKEY_sign_init failed")
296 | }
297 | return nil
298 | }
299 | sign := func(ctx C.GO_EVP_PKEY_CTX_PTR, out *C.uchar, outLen *C.size_t, in *C.uchar, inLen C.size_t) error {
300 | if ret := C.go_openssl_EVP_PKEY_sign(ctx, out, outLen, in, inLen); ret != 1 {
301 | return newOpenSSLError("EVP_PKEY_sign failed")
302 | }
303 | return nil
304 | }
305 | return cryptEVP(withKey, padding, nil, nil, nil, saltLen, h, signtInit, sign, hashed)
306 | }
307 |
308 | func evpVerify(withKey withKeyFunc, padding C.int, saltLen C.int, h crypto.Hash, sig, hashed []byte) error {
309 | verifyInit := func(ctx C.GO_EVP_PKEY_CTX_PTR) error {
310 | if ret := C.go_openssl_EVP_PKEY_verify_init(ctx); ret != 1 {
311 | return newOpenSSLError("EVP_PKEY_verify_init failed")
312 | }
313 | return nil
314 | }
315 | verify := func(ctx C.GO_EVP_PKEY_CTX_PTR, out *C.uchar, outLen C.size_t, in *C.uchar, inLen C.size_t) error {
316 | if ret := C.go_openssl_EVP_PKEY_verify(ctx, out, outLen, in, inLen); ret != 1 {
317 | return newOpenSSLError("EVP_PKEY_verify failed")
318 | }
319 | return nil
320 | }
321 | return verifyEVP(withKey, padding, nil, nil, saltLen, h, verifyInit, verify, sig, hashed)
322 | }
323 |
324 | func newEVPPKEY(key C.GO_EC_KEY_PTR) (C.GO_EVP_PKEY_PTR, error) {
325 | pkey := C.go_openssl_EVP_PKEY_new()
326 | if pkey == nil {
327 | return nil, newOpenSSLError("EVP_PKEY_new failed")
328 | }
329 | if C.go_openssl_EVP_PKEY_assign(pkey, C.GO_EVP_PKEY_EC, (unsafe.Pointer)(key)) != 1 {
330 | C.go_openssl_EVP_PKEY_free(pkey)
331 | return nil, newOpenSSLError("EVP_PKEY_assign failed")
332 | }
333 | return pkey, nil
334 | }
335 |
--------------------------------------------------------------------------------
/openssl/goopenssl.c:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | //go:build linux && !android
5 | // +build linux,!android
6 |
7 | #include "goopenssl.h"
8 |
9 | #include
10 | #include
11 |
12 | int
13 | go_openssl_fips_enabled(void* handle)
14 | {
15 | // For OpenSSL 1.x.
16 | int (*FIPS_mode)(void);
17 | FIPS_mode = (int (*)(void))dlsym(handle, "FIPS_mode");
18 | if (FIPS_mode != NULL)
19 | return FIPS_mode();
20 |
21 | // For OpenSSL 3.x.
22 | int (*EVP_default_properties_is_fips_enabled)(void*);
23 | int (*OSSL_PROVIDER_available)(void*, const char*);
24 | EVP_default_properties_is_fips_enabled = (int (*)(void*))dlsym(handle, "EVP_default_properties_is_fips_enabled");
25 | OSSL_PROVIDER_available = (int (*)(void*, const char*))dlsym(handle, "OSSL_PROVIDER_available");
26 | if (EVP_default_properties_is_fips_enabled != NULL && OSSL_PROVIDER_available != NULL &&
27 | EVP_default_properties_is_fips_enabled(NULL) == 1 && OSSL_PROVIDER_available(NULL, "fips") == 1)
28 | return 1;
29 |
30 | return 0;
31 | }
32 |
33 | static unsigned long
34 | version_num(void* handle)
35 | {
36 | unsigned long (*fn)(void);
37 | // OPENSSL_version_num is defined in OpenSSL 1.1.0 and 1.1.1.
38 | fn = (unsigned long (*)(void))dlsym(handle, "OpenSSL_version_num");
39 | if (fn != NULL)
40 | return fn();
41 |
42 | // SSLeay is defined in OpenSSL 1.0.2.
43 | fn = (unsigned long (*)(void))dlsym(handle, "SSLeay");
44 | if (fn != NULL)
45 | return fn();
46 |
47 | return 0;
48 | }
49 |
50 | int
51 | go_openssl_version_major(void* handle)
52 | {
53 | unsigned int (*fn)(void);
54 | // OPENSSL_version_major is supported since OpenSSL 3.
55 | fn = (unsigned int (*)(void))dlsym(handle, "OPENSSL_version_major");
56 | if (fn != NULL)
57 | return (int)fn();
58 |
59 | // If OPENSSL_version_major is not defined, try with OpenSSL 1 functions.
60 | unsigned long num = version_num(handle);
61 | if (num < 0x10000000L || num >= 0x20000000L)
62 | return -1;
63 |
64 | return 1;
65 | }
66 |
67 | int
68 | go_openssl_version_minor(void* handle)
69 | {
70 | unsigned int (*fn)(void);
71 | // OPENSSL_version_major is supported since OpenSSL 3.
72 | fn = (unsigned int (*)(void))dlsym(handle, "OPENSSL_version_minor");
73 | if (fn != NULL)
74 | return (int)fn();
75 |
76 | // If OPENSSL_version_major is not defined, try with OpenSSL 1 functions.
77 | unsigned long num = version_num(handle);
78 | // OpenSSL version number follows this schema:
79 | // MNNFFPPS: major minor fix patch status.
80 | if (num < 0x10000000L || num >= 0x10200000L)
81 | {
82 | // We only support minor version 0 and 1,
83 | // so there is no need to implement an algorithm
84 | // that decodes the version number into individual components.
85 | return -1;
86 | }
87 |
88 | if (num >= 0x10100000L)
89 | return 1;
90 |
91 | return 0;
92 | }
93 |
94 | // Approach taken from .Net System.Security.Cryptography.Native
95 | // https://github.com/dotnet/runtime/blob/f64246ce08fb7a58221b2b7c8e68f69c02522b0d/src/libraries/Native/Unix/System.Security.Cryptography.Native/opensslshim.c
96 |
97 | #define DEFINEFUNC(ret, func, args, argscall) ret (*_g_##func)args;
98 | #define DEFINEFUNC_LEGACY_1_0(ret, func, args, argscall) DEFINEFUNC(ret, func, args, argscall)
99 | #define DEFINEFUNC_LEGACY_1(ret, func, args, argscall) DEFINEFUNC(ret, func, args, argscall)
100 | #define DEFINEFUNC_1_1(ret, func, args, argscall) DEFINEFUNC(ret, func, args, argscall)
101 | #define DEFINEFUNC_3_0(ret, func, args, argscall) DEFINEFUNC(ret, func, args, argscall)
102 | #define DEFINEFUNC_RENAMED_1_1(ret, func, oldfunc, args, argscall) DEFINEFUNC(ret, func, args, argscall)
103 | #define DEFINEFUNC_RENAMED_3_0(ret, func, oldfunc, args, argscall) DEFINEFUNC(ret, func, args, argscall)
104 |
105 | FOR_ALL_OPENSSL_FUNCTIONS
106 |
107 | #undef DEFINEFUNC
108 | #undef DEFINEFUNC_LEGACY_1_0
109 | #undef DEFINEFUNC_LEGACY_1
110 | #undef DEFINEFUNC_1_1
111 | #undef DEFINEFUNC_3_0
112 | #undef DEFINEFUNC_RENAMED_1_1
113 | #undef DEFINEFUNC_RENAMED_3_0
114 |
115 | // Load all the functions stored in FOR_ALL_OPENSSL_FUNCTIONS
116 | // and assign them to their corresponding function pointer
117 | // defined in goopenssl.h.
118 | void
119 | go_openssl_load_functions(void* handle, int major, int minor)
120 | {
121 | #define DEFINEFUNC_INTERNAL(name, func) \
122 | _g_##name = dlsym(handle, func); \
123 | if (_g_##name == NULL) { fprintf(stderr, "Cannot get required symbol " #func " from libcrypto version %d.%d\n", major, minor); abort(); }
124 | #define DEFINEFUNC(ret, func, args, argscall) \
125 | DEFINEFUNC_INTERNAL(func, #func)
126 | #define DEFINEFUNC_LEGACY_1_0(ret, func, args, argscall) \
127 | if (major == 1 && minor == 0) \
128 | { \
129 | DEFINEFUNC_INTERNAL(func, #func) \
130 | }
131 | #define DEFINEFUNC_LEGACY_1(ret, func, args, argscall) \
132 | if (major == 1) \
133 | { \
134 | DEFINEFUNC_INTERNAL(func, #func) \
135 | }
136 | #define DEFINEFUNC_1_1(ret, func, args, argscall) \
137 | if (major == 3 || (major == 1 && minor == 1)) \
138 | { \
139 | DEFINEFUNC_INTERNAL(func, #func) \
140 | }
141 | #define DEFINEFUNC_3_0(ret, func, args, argscall) \
142 | if (major == 3) \
143 | { \
144 | DEFINEFUNC_INTERNAL(func, #func) \
145 | }
146 | #define DEFINEFUNC_RENAMED_1_1(ret, func, oldfunc, args, argscall) \
147 | if (major == 1 && minor == 0) \
148 | { \
149 | DEFINEFUNC_INTERNAL(func, #oldfunc) \
150 | } \
151 | else \
152 | { \
153 | DEFINEFUNC_INTERNAL(func, #func) \
154 | }
155 | #define DEFINEFUNC_RENAMED_3_0(ret, func, oldfunc, args, argscall) \
156 | if (major == 1) \
157 | { \
158 | DEFINEFUNC_INTERNAL(func, #oldfunc) \
159 | } \
160 | else \
161 | { \
162 | DEFINEFUNC_INTERNAL(func, #func) \
163 | }
164 |
165 | FOR_ALL_OPENSSL_FUNCTIONS
166 |
167 | #undef DEFINEFUNC
168 | #undef DEFINEFUNC_LEGACY_1_0
169 | #undef DEFINEFUNC_LEGACY_1
170 | #undef DEFINEFUNC_1_1
171 | #undef DEFINEFUNC_3_0
172 | #undef DEFINEFUNC_RENAMED_1_1
173 | #undef DEFINEFUNC_RENAMED_3_0
174 | }
175 |
--------------------------------------------------------------------------------
/openssl/goopenssl.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | // This header file describes the OpenSSL ABI as built for use in Go.
5 |
6 | #include // size_t
7 |
8 | #include "openssl_funcs.h"
9 |
10 | int go_openssl_fips_enabled(void* handle);
11 | int go_openssl_version_major(void* handle);
12 | int go_openssl_version_minor(void* handle);
13 | int go_openssl_thread_setup(void);
14 | void go_openssl_load_functions(void* handle, int major, int minor);
15 |
16 | // Define pointers to all the used OpenSSL functions.
17 | // Calling C function pointers from Go is currently not supported.
18 | // It is possible to circumvent this by using a C function wrapper.
19 | // https://pkg.go.dev/cmd/cgo
20 | #define DEFINEFUNC(ret, func, args, argscall) \
21 | extern ret (*_g_##func)args; \
22 | static inline ret go_openssl_##func args \
23 | { \
24 | return _g_##func argscall; \
25 | }
26 | #define DEFINEFUNC_LEGACY_1_0(ret, func, args, argscall) \
27 | DEFINEFUNC(ret, func, args, argscall)
28 | #define DEFINEFUNC_LEGACY_1(ret, func, args, argscall) \
29 | DEFINEFUNC(ret, func, args, argscall)
30 | #define DEFINEFUNC_1_1(ret, func, args, argscall) \
31 | DEFINEFUNC(ret, func, args, argscall)
32 | #define DEFINEFUNC_3_0(ret, func, args, argscall) \
33 | DEFINEFUNC(ret, func, args, argscall)
34 | #define DEFINEFUNC_RENAMED_1_1(ret, func, oldfunc, args, argscall) \
35 | DEFINEFUNC(ret, func, args, argscall)
36 | #define DEFINEFUNC_RENAMED_3_0(ret, func, oldfunc, args, argscall) \
37 | DEFINEFUNC(ret, func, args, argscall)
38 |
39 | FOR_ALL_OPENSSL_FUNCTIONS
40 |
41 | #undef DEFINEFUNC
42 | #undef DEFINEFUNC_LEGACY_1_0
43 | #undef DEFINEFUNC_LEGACY_1
44 | #undef DEFINEFUNC_1_1
45 | #undef DEFINEFUNC_3_0
46 | #undef DEFINEFUNC_RENAMED_1_1
47 | #undef DEFINEFUNC_RENAMED_3_0
48 |
49 | // go_shaX is a SHA generic wrapper which hash p into out.
50 | // One shot sha functions are expected to be fast, so
51 | // we maximize performance by batching all cgo calls.
52 | static inline int
53 | go_shaX(GO_EVP_MD_PTR md, void *p, size_t n, void *out)
54 | {
55 | GO_EVP_MD_CTX_PTR ctx = go_openssl_EVP_MD_CTX_new();
56 | go_openssl_EVP_DigestInit_ex(ctx, md, NULL);
57 | int ret = go_openssl_EVP_DigestUpdate(ctx, p, n) &&
58 | go_openssl_EVP_DigestFinal_ex(ctx, out, NULL);
59 | go_openssl_EVP_MD_CTX_free(ctx);
60 | return ret;
61 | }
62 |
63 | // These wrappers allocate out_len on the C stack to avoid having to pass a pointer from Go, which would escape to the heap.
64 | // Use them only in situations where the output length can be safely discarded.
65 | static inline int
66 | go_openssl_EVP_EncryptUpdate_wrapper(GO_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, const unsigned char *in, int in_len)
67 | {
68 | int len;
69 | return go_openssl_EVP_EncryptUpdate(ctx, out, &len, in, in_len);
70 | }
71 |
72 | static inline int
73 | go_openssl_EVP_DecryptUpdate_wrapper(GO_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, const unsigned char *in, int in_len)
74 | {
75 | int len;
76 | return go_openssl_EVP_DecryptUpdate(ctx, out, &len, in, in_len);
77 | }
78 |
79 | static inline int
80 | go_openssl_EVP_CipherUpdate_wrapper(GO_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, const unsigned char *in, int in_len)
81 | {
82 | int len;
83 | return go_openssl_EVP_CipherUpdate(ctx, out, &len, in, in_len);
84 | }
85 |
86 |
87 | // These wrappers allocate out_len on the C stack, and check that it matches the expected
88 | // value, to avoid having to pass a pointer from Go, which would escape to the heap.
89 |
90 | static inline int
91 | go_openssl_EVP_CIPHER_CTX_seal_wrapper(const GO_EVP_CIPHER_CTX_PTR ctx,
92 | unsigned char *out,
93 | const unsigned char *nonce,
94 | const unsigned char *in, int in_len,
95 | const unsigned char *aad, int aad_len)
96 | {
97 | if (in_len == 0) in = (const unsigned char *)"";
98 | if (aad_len == 0) aad = (const unsigned char *)"";
99 |
100 | if (go_openssl_EVP_CipherInit_ex(ctx, NULL, NULL, NULL, nonce, GO_AES_ENCRYPT) != 1)
101 | return 0;
102 |
103 | int discard_len, out_len;
104 | if (go_openssl_EVP_EncryptUpdate(ctx, NULL, &discard_len, aad, aad_len) != 1
105 | || go_openssl_EVP_EncryptUpdate(ctx, out, &out_len, in, in_len) != 1
106 | || go_openssl_EVP_EncryptFinal_ex(ctx, out + out_len, &discard_len) != 1)
107 | {
108 | return 0;
109 | }
110 |
111 | if (in_len != out_len)
112 | return 0;
113 |
114 | return go_openssl_EVP_CIPHER_CTX_ctrl(ctx, GO_EVP_CTRL_GCM_GET_TAG, 16, out + out_len);
115 | };
116 |
117 | static inline int
118 | go_openssl_EVP_CIPHER_CTX_open_wrapper(const GO_EVP_CIPHER_CTX_PTR ctx,
119 | unsigned char *out,
120 | const unsigned char *nonce,
121 | const unsigned char *in, int in_len,
122 | const unsigned char *aad, int aad_len,
123 | const unsigned char *tag)
124 | {
125 | if (in_len == 0) in = (const unsigned char *)"";
126 | if (aad_len == 0) aad = (const unsigned char *)"";
127 |
128 | if (go_openssl_EVP_CipherInit_ex(ctx, NULL, NULL, NULL, nonce, GO_AES_DECRYPT) != 1)
129 | return 0;
130 |
131 | int discard_len, out_len;
132 | if (go_openssl_EVP_DecryptUpdate(ctx, NULL, &discard_len, aad, aad_len) != 1
133 | || go_openssl_EVP_DecryptUpdate(ctx, out, &out_len, in, in_len) != 1)
134 | {
135 | return 0;
136 | }
137 |
138 | if (go_openssl_EVP_CIPHER_CTX_ctrl(ctx, GO_EVP_CTRL_GCM_SET_TAG, 16, (unsigned char *)(tag)) != 1)
139 | return 0;
140 |
141 | if (go_openssl_EVP_DecryptFinal_ex(ctx, out + out_len, &discard_len) != 1)
142 | return 0;
143 |
144 | if (out_len != in_len)
145 | return 0;
146 |
147 | return 1;
148 | };
--------------------------------------------------------------------------------
/openssl/hmac.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | //go:build linux && !android
5 | // +build linux,!android
6 |
7 | package openssl
8 |
9 | // #include "goopenssl.h"
10 | import "C"
11 | import (
12 | "hash"
13 | "runtime"
14 | "unsafe"
15 | )
16 |
17 | var (
18 | paramAlgHMAC = C.CString("HMAC")
19 | paramDigest = C.CString("digest")
20 | )
21 |
22 | // NewHMAC returns a new HMAC using OpenSSL.
23 | // The function h must return a hash implemented by
24 | // OpenSSL (for example, h could be openssl.NewSHA256).
25 | // If h is not recognized, NewHMAC returns nil.
26 | func NewHMAC(h func() hash.Hash, key []byte) hash.Hash {
27 | ch := h()
28 | md := hashToMD(ch)
29 | if md == nil {
30 | return nil
31 | }
32 |
33 | var hkey []byte
34 | if len(key) > 0 {
35 | // Note: Could hash down long keys here using EVP_Digest.
36 | hkey = make([]byte, len(key))
37 | copy(hkey, key)
38 | } else {
39 | // This is supported in OpenSSL/Standard lib and as such
40 | // we must support it here. When using HMAC with a null key
41 | // HMAC_Init will try and reuse the key from the ctx. This is
42 | // not the bahavior previously implemented, so as a workaround
43 | // we pass an "empty" key.
44 | hkey = make([]byte, C.GO_EVP_MAX_MD_SIZE)
45 | }
46 | switch vMajor {
47 | case 1:
48 | return newHMAC1(hkey, ch, md)
49 | case 3:
50 | return newHMAC3(hkey, ch, md)
51 | default:
52 | panic(errUnsuportedVersion())
53 | }
54 | }
55 |
56 | // hmac1 implements hash.Hash
57 | // using functions available in OpenSSL 1.
58 | type hmac1 struct {
59 | md C.GO_EVP_MD_PTR
60 | ctx C.GO_HMAC_CTX_PTR
61 | size int
62 | blockSize int
63 | key []byte
64 | sum []byte
65 | }
66 |
67 | func newHMAC1(key []byte, h hash.Hash, md C.GO_EVP_MD_PTR) *hmac1 {
68 | hmac := &hmac1{
69 | md: md,
70 | size: h.Size(),
71 | blockSize: h.BlockSize(),
72 | key: key,
73 | ctx: hmac1CtxNew(),
74 | }
75 | runtime.SetFinalizer(hmac, (*hmac1).finalize)
76 | hmac.Reset()
77 | return hmac
78 | }
79 |
80 | func (h *hmac1) Reset() {
81 | hmac1CtxReset(h.ctx)
82 |
83 | if C.go_openssl_HMAC_Init_ex(h.ctx, unsafe.Pointer(&h.key[0]), C.int(len(h.key)), h.md, nil) == 0 {
84 | panic("openssl: HMAC_Init failed")
85 | }
86 | if size := C.go_openssl_EVP_MD_get_size(h.md); size != C.int(h.size) {
87 | println("openssl: HMAC size:", size, "!=", h.size)
88 | panic("openssl: HMAC size mismatch")
89 | }
90 | runtime.KeepAlive(h) // Next line will keep h alive too; just making doubly sure.
91 | h.sum = nil
92 | }
93 |
94 | func (h *hmac1) finalize() {
95 | hmac1CtxFree(h.ctx)
96 | }
97 |
98 | func (h *hmac1) Write(p []byte) (int, error) {
99 | if len(p) > 0 {
100 | C.go_openssl_HMAC_Update(h.ctx, base(p), C.size_t(len(p)))
101 | }
102 | runtime.KeepAlive(h)
103 | return len(p), nil
104 | }
105 |
106 | func (h *hmac1) Size() int {
107 | return h.size
108 | }
109 |
110 | func (h *hmac1) BlockSize() int {
111 | return h.blockSize
112 | }
113 |
114 | func (h *hmac1) Sum(in []byte) []byte {
115 | if h.sum == nil {
116 | size := h.Size()
117 | h.sum = make([]byte, size)
118 | }
119 | // Make copy of context because Go hash.Hash mandates
120 | // that Sum has no effect on the underlying stream.
121 | // In particular it is OK to Sum, then Write more, then Sum again,
122 | // and the second Sum acts as if the first didn't happen.
123 | ctx2 := hmac1CtxNew()
124 | defer hmac1CtxFree(ctx2)
125 | if C.go_openssl_HMAC_CTX_copy(ctx2, h.ctx) == 0 {
126 | panic("openssl: HMAC_CTX_copy failed")
127 | }
128 | C.go_openssl_HMAC_Final(ctx2, base(h.sum), nil)
129 | return append(in, h.sum...)
130 | }
131 |
132 | func hmac1CtxNew() C.GO_HMAC_CTX_PTR {
133 | if vMajor == 1 && vMinor == 0 {
134 | // 0x120 is the sizeof value when building against OpenSSL 1.0.2 on Ubuntu 16.04.
135 | ctx := (C.GO_HMAC_CTX_PTR)(C.malloc(0x120))
136 | if ctx != nil {
137 | C.go_openssl_HMAC_CTX_init(ctx)
138 | }
139 | return ctx
140 | }
141 | return C.go_openssl_HMAC_CTX_new()
142 | }
143 |
144 | func hmac1CtxReset(ctx C.GO_HMAC_CTX_PTR) {
145 | if ctx == nil {
146 | return
147 | }
148 | if vMajor == 1 && vMinor == 0 {
149 | C.go_openssl_HMAC_CTX_cleanup(ctx)
150 | C.go_openssl_HMAC_CTX_init(ctx)
151 | return
152 | }
153 | C.go_openssl_HMAC_CTX_reset(ctx)
154 | }
155 |
156 | func hmac1CtxFree(ctx C.GO_HMAC_CTX_PTR) {
157 | if ctx == nil {
158 | return
159 | }
160 | if vMajor == 1 && vMinor == 0 {
161 | C.go_openssl_HMAC_CTX_cleanup(ctx)
162 | C.free(unsafe.Pointer(ctx))
163 | return
164 | }
165 | C.go_openssl_HMAC_CTX_free(ctx)
166 | }
167 |
168 | // hmac3 implements hash.Hash
169 | // using functions available in OpenSSL 3.
170 | type hmac3 struct {
171 | md C.GO_EVP_MAC_PTR
172 | ctx C.GO_EVP_MAC_CTX_PTR
173 | params [2]C.OSSL_PARAM
174 | size int
175 | blockSize int
176 | key []byte
177 | sum []byte
178 | }
179 |
180 | func newHMAC3(key []byte, h hash.Hash, md C.GO_EVP_MD_PTR) *hmac3 {
181 | mac := C.go_openssl_EVP_MAC_fetch(nil, paramAlgHMAC, nil)
182 | ctx := C.go_openssl_EVP_MAC_CTX_new(mac)
183 | if ctx == nil {
184 | panic("openssl: EVP_MAC_CTX_new failed")
185 | }
186 | digest := C.go_openssl_EVP_MD_get0_name(md)
187 | params := [2]C.OSSL_PARAM{
188 | C.go_openssl_OSSL_PARAM_construct_utf8_string(paramDigest, digest, 0),
189 | C.go_openssl_OSSL_PARAM_construct_end(),
190 | }
191 | hmac := &hmac3{
192 | md: mac,
193 | ctx: ctx,
194 | params: params,
195 | size: h.Size(),
196 | blockSize: h.BlockSize(),
197 | key: key,
198 | }
199 | runtime.SetFinalizer(hmac, (*hmac3).finalize)
200 | hmac.Reset()
201 | return hmac
202 | }
203 |
204 | func (h *hmac3) Reset() {
205 | if C.go_openssl_EVP_MAC_init(h.ctx, base(h.key), C.size_t(len(h.key)), &h.params[0]) == 0 {
206 | panic(newOpenSSLError("EVP_MAC_init failed"))
207 | }
208 | runtime.KeepAlive(h) // Next line will keep h alive too; just making doubly sure.
209 | h.sum = nil
210 | }
211 |
212 | func (h *hmac3) finalize() {
213 | C.go_openssl_EVP_MAC_free(h.md)
214 | if h.ctx == nil {
215 | return
216 | }
217 | C.go_openssl_EVP_MAC_CTX_free(h.ctx)
218 | }
219 |
220 | func (h *hmac3) Write(p []byte) (int, error) {
221 | if len(p) > 0 {
222 | C.go_openssl_EVP_MAC_update(h.ctx, base(p), C.size_t(len(p)))
223 | }
224 | runtime.KeepAlive(h)
225 | return len(p), nil
226 | }
227 |
228 | func (h *hmac3) Size() int {
229 | return h.size
230 | }
231 |
232 | func (h *hmac3) BlockSize() int {
233 | return h.blockSize
234 | }
235 |
236 | func (h *hmac3) Sum(in []byte) []byte {
237 | if h.sum == nil {
238 | size := h.Size()
239 | h.sum = make([]byte, size)
240 | }
241 | // Make copy of context because Go hash.Hash mandates
242 | // that Sum has no effect on the underlying stream.
243 | // In particular it is OK to Sum, then Write more, then Sum again,
244 | // and the second Sum acts as if the first didn't happen.
245 | ctx2 := C.go_openssl_EVP_MAC_CTX_dup(h.ctx)
246 | if ctx2 == nil {
247 | panic("openssl: EVP_MAC_CTX_dup failed")
248 | }
249 | defer C.go_openssl_EVP_MAC_CTX_free(ctx2)
250 | C.go_openssl_EVP_MAC_final(ctx2, base(h.sum), nil, C.size_t(len(h.sum)))
251 | return append(in, h.sum...)
252 | }
253 |
--------------------------------------------------------------------------------
/openssl/hmac_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | //go:build linux && !android
5 | // +build linux,!android
6 |
7 | package openssl
8 |
9 | import (
10 | "bytes"
11 | "hash"
12 | "testing"
13 | )
14 |
15 | func TestHMAC(t *testing.T) {
16 | var tests = []struct {
17 | name string
18 | fn func() hash.Hash
19 | }{
20 | {"sha1", NewSHA1},
21 | {"sha224", NewSHA224},
22 | {"sha256", NewSHA256},
23 | {"sha384", NewSHA384},
24 | {"sha512", NewSHA512},
25 | }
26 | for _, tt := range tests {
27 | t.Run(tt.name, func(t *testing.T) {
28 | t.Parallel()
29 | h := NewHMAC(tt.fn, nil)
30 | h.Write([]byte("hello"))
31 | sumHello := h.Sum(nil)
32 |
33 | h = NewHMAC(tt.fn, nil)
34 | h.Write([]byte("hello world"))
35 | sumHelloWorld := h.Sum(nil)
36 |
37 | // Test that Sum has no effect on future Sum or Write operations.
38 | // This is a bit unusual as far as usage, but it's allowed
39 | // by the definition of Go hash.Hash, and some clients expect it to work.
40 | h = NewHMAC(tt.fn, nil)
41 | h.Write([]byte("hello"))
42 | if sum := h.Sum(nil); !bytes.Equal(sum, sumHello) {
43 | t.Fatalf("1st Sum after hello = %x, want %x", sum, sumHello)
44 | }
45 | if sum := h.Sum(nil); !bytes.Equal(sum, sumHello) {
46 | t.Fatalf("2nd Sum after hello = %x, want %x", sum, sumHello)
47 | }
48 |
49 | h.Write([]byte(" world"))
50 | if sum := h.Sum(nil); !bytes.Equal(sum, sumHelloWorld) {
51 | t.Fatalf("1st Sum after hello world = %x, want %x", sum, sumHelloWorld)
52 | }
53 | if sum := h.Sum(nil); !bytes.Equal(sum, sumHelloWorld) {
54 | t.Fatalf("2nd Sum after hello world = %x, want %x", sum, sumHelloWorld)
55 | }
56 |
57 | h.Reset()
58 | h.Write([]byte("hello"))
59 | if sum := h.Sum(nil); !bytes.Equal(sum, sumHello) {
60 | t.Fatalf("Sum after Reset + hello = %x, want %x", sum, sumHello)
61 | }
62 | })
63 | }
64 | }
65 |
66 | func BenchmarkHMACSHA256_32(b *testing.B) {
67 | b.StopTimer()
68 | key := make([]byte, 32)
69 | buf := make([]byte, 32)
70 | h := NewHMAC(NewSHA256, key)
71 | b.SetBytes(int64(len(buf)))
72 | b.StartTimer()
73 | b.ReportAllocs()
74 | for i := 0; i < b.N; i++ {
75 | h.Write(buf)
76 | mac := h.Sum(nil)
77 | h.Reset()
78 | buf[0] = mac[0]
79 | }
80 | }
81 |
82 | func BenchmarkHMACNewWriteSum(b *testing.B) {
83 | b.StopTimer()
84 | buf := make([]byte, 32)
85 | b.SetBytes(int64(len(buf)))
86 | b.StartTimer()
87 | b.ReportAllocs()
88 | for i := 0; i < b.N; i++ {
89 | h := NewHMAC(NewSHA256, make([]byte, 32))
90 | h.Write(buf)
91 | mac := h.Sum(nil)
92 | buf[0] = mac[0]
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/openssl/internal/subtle/aliasing.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | // Package subtle implements functions that are often useful in cryptographic
6 | // code but require careful thought to use correctly.
7 | //
8 | // This is a mirror of golang.org/x/crypto/internal/subtle.
9 | package subtle
10 |
11 | import "unsafe"
12 |
13 | // AnyOverlap reports whether x and y share memory at any (not necessarily
14 | // corresponding) index. The memory beyond the slice length is ignored.
15 | func AnyOverlap(x, y []byte) bool {
16 | return len(x) > 0 && len(y) > 0 &&
17 | uintptr(unsafe.Pointer(&x[0])) <= uintptr(unsafe.Pointer(&y[len(y)-1])) &&
18 | uintptr(unsafe.Pointer(&y[0])) <= uintptr(unsafe.Pointer(&x[len(x)-1]))
19 | }
20 |
21 | // InexactOverlap reports whether x and y share memory at any non-corresponding
22 | // index. The memory beyond the slice length is ignored. Note that x and y can
23 | // have different lengths and still not have any inexact overlap.
24 | //
25 | // InexactOverlap can be used to implement the requirements of the crypto/cipher
26 | // AEAD, Block, BlockMode and Stream interfaces.
27 | func InexactOverlap(x, y []byte) bool {
28 | if len(x) == 0 || len(y) == 0 || &x[0] == &y[0] {
29 | return false
30 | }
31 | return AnyOverlap(x, y)
32 | }
33 |
--------------------------------------------------------------------------------
/openssl/internal/subtle/aliasing_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | package subtle_test
6 |
7 | import (
8 | "testing"
9 |
10 | "github.com/microsoft/go-crypto-openssl/openssl/internal/subtle"
11 | )
12 |
13 | var a, b [100]byte
14 |
15 | var aliasingTests = []struct {
16 | x, y []byte
17 | anyOverlap, inexactOverlap bool
18 | }{
19 | {a[:], b[:], false, false},
20 | {a[:], b[:0], false, false},
21 | {a[:], b[:50], false, false},
22 | {a[40:50], a[50:60], false, false},
23 | {a[40:50], a[60:70], false, false},
24 | {a[:51], a[50:], true, true},
25 | {a[:], a[:], true, false},
26 | {a[:50], a[:60], true, false},
27 | {a[:], nil, false, false},
28 | {nil, nil, false, false},
29 | {a[:], a[:0], false, false},
30 | {a[:10], a[:10:20], true, false},
31 | {a[:10], a[5:10:20], true, true},
32 | }
33 |
34 | func testAliasing(t *testing.T, i int, x, y []byte, anyOverlap, inexactOverlap bool) {
35 | any := subtle.AnyOverlap(x, y)
36 | if any != anyOverlap {
37 | t.Errorf("%d: wrong AnyOverlap result, expected %v, got %v", i, anyOverlap, any)
38 | }
39 | inexact := subtle.InexactOverlap(x, y)
40 | if inexact != inexactOverlap {
41 | t.Errorf("%d: wrong InexactOverlap result, expected %v, got %v", i, inexactOverlap, any)
42 | }
43 | }
44 |
45 | func TestAliasing(t *testing.T) {
46 | for i, tt := range aliasingTests {
47 | testAliasing(t, i, tt.x, tt.y, tt.anyOverlap, tt.inexactOverlap)
48 | testAliasing(t, i, tt.y, tt.x, tt.anyOverlap, tt.inexactOverlap)
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/openssl/openssl.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | //go:build linux && !android
5 | // +build linux,!android
6 |
7 | // Package openssl provides access to OpenSSL cryptographic functions.
8 | package openssl
9 |
10 | // #include "goopenssl.h"
11 | // #include
12 | // #cgo LDFLAGS: -ldl
13 | import "C"
14 | import (
15 | "errors"
16 | "math/bits"
17 | "strconv"
18 | "strings"
19 | "sync"
20 | "syscall"
21 | "unsafe"
22 | )
23 |
24 | var (
25 | providerNameFips = C.CString("fips")
26 | providerNameDefault = C.CString("default")
27 | )
28 |
29 | var (
30 | initOnce sync.Once
31 | // errInit is set when first calling Init().
32 | errInit error
33 | // vMajor and vMinor hold the major/minor OpenSSL version.
34 | // It is only populated if Init has been called.
35 | vMajor, vMinor int
36 | )
37 |
38 | // knownVersions is a list of supported and well-known libcrypto.so suffixes in decreasing version order.
39 | //
40 | // FreeBSD library version numbering does not directly align to the version of OpenSSL.
41 | // Its preferred search order is 11 -> 111.
42 | //
43 | // Some distributions use 1.0.0 and others (such as Debian) 1.0.2 to refer to the same OpenSSL 1.0.2 version.
44 | //
45 | // Fedora derived distros use different naming for the version 1.0.x.
46 | var knownVersions = [...]string{"3", "1.1", "11", "111", "1.0.2", "1.0.0", "10"}
47 |
48 | func errUnsuportedVersion() error {
49 | return errors.New("openssl: OpenSSL version: " + strconv.Itoa(vMajor) + "." + strconv.Itoa(vMinor))
50 | }
51 |
52 | // Init loads and initializes OpenSSL.
53 | // It must be called before any other OpenSSL call.
54 | //
55 | // Only the first call to Init is effective,
56 | // subsequent calls will return the same error result as the one from the first call.
57 | //
58 | // If GO_OPENSSL_VERSION_OVERRIDE environment variable is not empty, its value will be appended to the OpenSSL shared library name
59 | // as a version suffix when calling dlopen. For example, "GO_OPENSSL_VERSION_OVERRIDE=1.1.1k-fips"
60 | // makes Init look for the shared library libcrypto.so.1.1.1k-fips.
61 | // If GO_OPENSSL_VERSION_OVERRIDE environment variable is empty, Init will try to load the OpenSSL shared library
62 | // using a list if supported and well-known version suffixes, going from higher to lower versions.
63 | func Init() error {
64 | initOnce.Do(func() {
65 | version, _ := syscall.Getenv("GO_OPENSSL_VERSION_OVERRIDE")
66 | handle, err := loadLibrary(version)
67 | if err != nil {
68 | errInit = err
69 | return
70 | }
71 |
72 | vMajor = int(C.go_openssl_version_major(handle))
73 | vMinor = int(C.go_openssl_version_minor(handle))
74 | if vMajor == -1 || vMinor == -1 {
75 | errInit = errors.New("openssl: can't retrieve OpenSSL version")
76 | return
77 | }
78 | var supported bool
79 | if vMajor == 1 {
80 | supported = vMinor == 0 || vMinor == 1
81 | } else if vMajor == 3 {
82 | // OpenSSL team guarantees API and ABI compatibility within the same major version since OpenSSL 3.
83 | supported = true
84 | }
85 | if !supported {
86 | errInit = errUnsuportedVersion()
87 | return
88 | }
89 |
90 | C.go_openssl_load_functions(handle, C.int(vMajor), C.int(vMinor))
91 | C.go_openssl_OPENSSL_init()
92 | if vMajor == 1 && vMinor == 0 {
93 | if C.go_openssl_thread_setup() != 1 {
94 | errInit = newOpenSSLError("openssl: thread setup")
95 | return
96 | }
97 | C.go_openssl_OPENSSL_add_all_algorithms_conf()
98 | C.go_openssl_ERR_load_crypto_strings()
99 | } else {
100 | flags := C.uint64_t(C.GO_OPENSSL_INIT_ADD_ALL_CIPHERS | C.GO_OPENSSL_INIT_ADD_ALL_DIGESTS | C.GO_OPENSSL_INIT_LOAD_CONFIG | C.GO_OPENSSL_INIT_LOAD_CRYPTO_STRINGS)
101 | if C.go_openssl_OPENSSL_init_crypto(flags, nil) != 1 {
102 | errInit = newOpenSSLError("openssl: init crypto")
103 | return
104 | }
105 | }
106 | })
107 | return errInit
108 | }
109 |
110 | func dlopen(version string) unsafe.Pointer {
111 | cv := C.CString("libcrypto.so." + version)
112 | defer C.free(unsafe.Pointer(cv))
113 | return C.dlopen(cv, C.RTLD_LAZY|C.RTLD_LOCAL)
114 | }
115 |
116 | func loadLibrary(version string) (unsafe.Pointer, error) {
117 | if version != "" {
118 | // If version is specified try to load it or error out.
119 | handle := dlopen(version)
120 | if handle == nil {
121 | errstr := C.GoString(C.dlerror())
122 | return nil, errors.New("openssl: can't load libcrypto.so." + version + ": " + errstr)
123 | }
124 | return handle, nil
125 | }
126 | var fallbackHandle unsafe.Pointer
127 | for _, v := range knownVersions {
128 | handle := dlopen(v)
129 | if handle == nil {
130 | continue
131 | }
132 | if C.go_openssl_fips_enabled(handle) == 1 {
133 | // Found a FIPS enabled version, use it.
134 | if fallbackHandle != nil {
135 | // If we found a FIPS enabled version but we already have a fallback
136 | // version, close the fallback version.
137 | C.dlclose(fallbackHandle)
138 | }
139 | return handle, nil
140 | }
141 | if fallbackHandle == nil {
142 | // Remember the first version that exists but is not FIPS enabled
143 | // in case we don't find any FIPS enabled version.
144 | fallbackHandle = handle
145 | } else {
146 | C.dlclose(handle)
147 | }
148 | }
149 | if fallbackHandle != nil {
150 | return fallbackHandle, nil
151 | }
152 | return nil, errors.New("openssl: can't load libcrypto.so using any known version suffix")
153 | }
154 |
155 | // FIPS returns true if OpenSSL is running in FIPS mode, else returns false.
156 | func FIPS() bool {
157 | switch vMajor {
158 | case 1:
159 | return C.go_openssl_FIPS_mode() == 1
160 | case 3:
161 | if C.go_openssl_EVP_default_properties_is_fips_enabled(nil) == 0 {
162 | return false
163 | }
164 | // EVP_default_properties_is_fips_enabled can return true even if the FIPS provider isn't loaded,
165 | // it is only based on the default properties.
166 | return C.go_openssl_OSSL_PROVIDER_available(nil, providerNameFips) == 1
167 | default:
168 | panic(errUnsuportedVersion())
169 | }
170 | }
171 |
172 | // SetFIPS enables or disables FIPS mode.
173 | //
174 | // On OpenSSL 3, the `fips` provider is loaded if enabled is true,
175 | // else the `default` provider is loaded.
176 | func SetFIPS(enabled bool) error {
177 | var mode C.int
178 | if enabled {
179 | mode = C.int(1)
180 | } else {
181 | mode = C.int(0)
182 | }
183 | switch vMajor {
184 | case 1:
185 | if C.go_openssl_FIPS_mode_set(mode) != 1 {
186 | return newOpenSSLError("openssl: FIPS_mode_set")
187 | }
188 | return nil
189 | case 3:
190 | var provName *C.char
191 | if enabled {
192 | provName = providerNameFips
193 | } else {
194 | provName = providerNameDefault
195 | }
196 | // Check if provName is not loaded.
197 | if C.go_openssl_OSSL_PROVIDER_available(nil, provName) == 0 {
198 | // If not, fallback to provName provider.
199 | if C.go_openssl_OSSL_PROVIDER_load(nil, provName) == nil {
200 | return newOpenSSLError("openssl: OSSL_PROVIDER_load")
201 | }
202 | // Make sure we now have a provider available.
203 | if C.go_openssl_OSSL_PROVIDER_available(nil, provName) == 0 {
204 | return fail("SetFIPS(" + strconv.FormatBool(enabled) + ") not supported")
205 | }
206 | }
207 | if C.go_openssl_EVP_default_properties_enable_fips(nil, mode) != 1 {
208 | return newOpenSSLError("openssl: EVP_default_properties_enable_fips")
209 | }
210 | return nil
211 | default:
212 | panic(errUnsuportedVersion())
213 | }
214 | }
215 |
216 | // VersionText returns the version text of the OpenSSL currently loaded.
217 | func VersionText() string {
218 | return C.GoString(C.go_openssl_OpenSSL_version(0))
219 | }
220 |
221 | func newOpenSSLError(msg string) error {
222 | var b strings.Builder
223 | var e C.ulong
224 |
225 | b.WriteString(msg)
226 | b.WriteString("\nopenssl error(s):\n")
227 |
228 | for {
229 | e = C.go_openssl_ERR_get_error()
230 | if e == 0 {
231 | break
232 | }
233 | var buf [256]byte
234 | C.go_openssl_ERR_error_string_n(e, (*C.char)(unsafe.Pointer(&buf[0])), 256)
235 | b.Write(buf[:])
236 | b.WriteByte('\n')
237 | }
238 | return errors.New(b.String())
239 | }
240 |
241 | type fail string
242 |
243 | func (e fail) Error() string { return "openssl: " + string(e) + " failed" }
244 |
245 | const wordBytes = bits.UintSize / 8
246 |
247 | func wbase(b BigInt) *C.uchar {
248 | if len(b) == 0 {
249 | return nil
250 | }
251 | return (*C.uchar)(unsafe.Pointer(&b[0]))
252 | }
253 |
254 | func bytesToBN(x []byte) C.GO_BIGNUM_PTR {
255 | if len(x) == 0 {
256 | return nil
257 | }
258 | return C.go_openssl_BN_bin2bn(base(x), C.int(len(x)), nil)
259 | }
260 |
261 | func bigToBN(x BigInt) C.GO_BIGNUM_PTR {
262 | if len(x) == 0 {
263 | return nil
264 | }
265 | return C.go_openssl_BN_lebin2bn(wbase(x), C.int(len(x)*wordBytes), nil)
266 | }
267 |
268 | func bnToBig(bn C.GO_BIGNUM_PTR) BigInt {
269 | if bn == nil {
270 | return nil
271 | }
272 | x := make(BigInt, C.go_openssl_BN_num_bits(bn))
273 | if C.go_openssl_BN_bn2lebinpad(bn, wbase(x), C.int(len(x)*wordBytes)) == 0 {
274 | panic("openssl: bignum conversion failed")
275 | }
276 | return x
277 | }
278 |
279 | // noescape hides a pointer from escape analysis. noescape is
280 | // the identity function but escape analysis doesn't think the
281 | // output depends on the input. noescape is inlined and currently
282 | // compiles down to zero instructions.
283 | // USE CAREFULLY!
284 | //
285 | //go:nosplit
286 | func noescape(p unsafe.Pointer) unsafe.Pointer {
287 | x := uintptr(p)
288 | return unsafe.Pointer(x ^ 0)
289 | }
290 |
291 | var zero byte
292 |
293 | // addr converts p to its base addr, including a noescape along the way.
294 | // If p is nil, addr returns a non-nil pointer, so that the result can always
295 | // be dereferenced.
296 | //
297 | //go:nosplit
298 | func addr(p []byte) *byte {
299 | if len(p) == 0 {
300 | return &zero
301 | }
302 | return (*byte)(noescape(unsafe.Pointer(&p[0])))
303 | }
304 |
--------------------------------------------------------------------------------
/openssl/openssl_funcs.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | // To make this header standalone (so that building Go does not require
5 | // having a full set of OpenSSL headers), the struct details are not here.
6 | // Instead, while testing the openssl module, we generate and compile a C program
7 | // that checks that the function signatures match the OpenSSL equivalents.
8 | // The generation of the checking program depends on the declaration
9 | // forms used below, which includes commented directives (#include, #if and #endif)
10 | // and comments starting with `check:`.
11 |
12 | #include // size_t
13 | #include // uint64_t
14 |
15 | // #include
16 | enum {
17 | GO_OPENSSL_INIT_LOAD_CRYPTO_STRINGS = 0x00000002L,
18 | GO_OPENSSL_INIT_ADD_ALL_CIPHERS = 0x00000004L,
19 | GO_OPENSSL_INIT_ADD_ALL_DIGESTS = 0x00000008L,
20 | GO_OPENSSL_INIT_LOAD_CONFIG = 0x00000040L
21 | };
22 |
23 | // #include
24 | enum {
25 | GO_AES_ENCRYPT = 1,
26 | GO_AES_DECRYPT = 0
27 | };
28 |
29 | // #include
30 | enum {
31 | GO_EVP_CTRL_GCM_GET_TAG = 0x10,
32 | GO_EVP_CTRL_GCM_SET_TAG = 0x11,
33 | GO_EVP_PKEY_CTRL_MD = 1,
34 | GO_EVP_PKEY_RSA = 6,
35 | GO_EVP_PKEY_EC = 408,
36 | GO_EVP_MAX_MD_SIZE = 64
37 | };
38 |
39 | // #include
40 | enum {
41 | GO_EVP_PKEY_CTRL_EC_PARAMGEN_CURVE_NID = 0x1001,
42 | };
43 |
44 | typedef enum {
45 | GO_POINT_CONVERSION_UNCOMPRESSED = 4,
46 | } point_conversion_form_t;
47 |
48 | // #include
49 | enum {
50 | GO_NID_X9_62_prime256v1 = 415,
51 | GO_NID_secp224r1 = 713,
52 | GO_NID_secp384r1 = 715,
53 | GO_NID_secp521r1 = 716
54 | };
55 |
56 | // #include
57 | enum {
58 | GO_RSA_PKCS1_PADDING = 1,
59 | GO_RSA_NO_PADDING = 3,
60 | GO_RSA_PKCS1_OAEP_PADDING = 4,
61 | GO_RSA_PKCS1_PSS_PADDING = 6,
62 | GO_RSA_PSS_SALTLEN_DIGEST = -1,
63 | GO_RSA_PSS_SALTLEN_AUTO = -2,
64 | GO_RSA_PSS_SALTLEN_MAX_SIGN = -2,
65 | GO_RSA_PSS_SALTLEN_MAX = -3,
66 | GO_EVP_PKEY_CTRL_RSA_PADDING = 0x1001,
67 | GO_EVP_PKEY_CTRL_RSA_PSS_SALTLEN = 0x1002,
68 | GO_EVP_PKEY_CTRL_RSA_KEYGEN_BITS = 0x1003,
69 | GO_EVP_PKEY_CTRL_RSA_MGF1_MD = 0x1005,
70 | GO_EVP_PKEY_CTRL_RSA_OAEP_MD = 0x1009,
71 | GO_EVP_PKEY_CTRL_RSA_OAEP_LABEL = 0x100A
72 | };
73 |
74 | typedef void* GO_EVP_CIPHER_PTR;
75 | typedef void* GO_EVP_CIPHER_CTX_PTR;
76 | typedef void* GO_EVP_PKEY_PTR;
77 | typedef void* GO_EVP_PKEY_CTX_PTR;
78 | typedef void* GO_EVP_MD_PTR;
79 | typedef void* GO_EVP_MD_CTX_PTR;
80 | typedef void* GO_HMAC_CTX_PTR;
81 | typedef void* GO_OPENSSL_INIT_SETTINGS_PTR;
82 | typedef void* GO_OSSL_LIB_CTX_PTR;
83 | typedef void* GO_OSSL_PROVIDER_PTR;
84 | typedef void* GO_ENGINE_PTR;
85 | typedef void* GO_BIGNUM_PTR;
86 | typedef void* GO_BN_CTX_PTR;
87 | typedef void* GO_EC_KEY_PTR;
88 | typedef void* GO_EC_POINT_PTR;
89 | typedef void* GO_EC_GROUP_PTR;
90 | typedef void* GO_RSA_PTR;
91 | typedef void* GO_EVP_MAC_PTR;
92 | typedef void* GO_EVP_MAC_CTX_PTR;
93 |
94 | // OSSL_PARAM does not follow the GO_FOO_PTR pattern
95 | // because it is not passed around as a pointer but on the stack.
96 | // We can't abstract it away by using a void*.
97 | // Copied from
98 | // https://github.com/openssl/openssl/blob/fcae2ae4f675def607d338b7945b9af1dd9bb746/include/openssl/core.h#L82-L88.
99 | typedef struct {
100 | const char *key;
101 | unsigned int data_type;
102 | void *data;
103 | size_t data_size;
104 | size_t return_size;
105 | } OSSL_PARAM;
106 |
107 | // List of all functions from the libcrypto that are used in this package.
108 | // Forgetting to add a function here results in build failure with message reporting the function
109 | // that needs to be added.
110 | //
111 | // The purpose of FOR_ALL_OPENSSL_FUNCTIONS is to define all libcrypto functions
112 | // without depending on the openssl headers so it is easier to use this package
113 | // with an openssl version different that the one used at build time.
114 | //
115 | // The following macros may not be defined at this point,
116 | // they are not resolved here but just accumulated in FOR_ALL_OPENSSL_FUNCTIONS.
117 | //
118 | // DEFINEFUNC defines and loads openssl functions that can be directly called from Go as their signatures match
119 | // the OpenSSL API and do not require special logic.
120 | // The process will be aborted if the function can't be loaded.
121 | //
122 | // DEFINEFUNC_LEGACY_1_0 acts like DEFINEFUNC but only aborts the process if the function can't be loaded
123 | // when using 1.0.x. This indicates the function is required when using 1.0.x, but is unused when using later versions.
124 | // It also might not exist in later versions.
125 | //
126 | // DEFINEFUNC_LEGACY_1 acts like DEFINEFUNC but only aborts the process if the function can't be loaded
127 | // when using 1.x. This indicates the function is required when using 1.x, but is unused when using later versions.
128 | // It also might not exist in later versions.
129 | //
130 | // DEFINEFUNC_1_1 acts like DEFINEFUNC but only aborts the process if function can't be loaded
131 | // when using 1.1.0 or higher.
132 | //
133 | // DEFINEFUNC_3_0 acts like DEFINEFUNC but only aborts the process if function can't be loaded
134 | // when using 3.0.0 or higher.
135 | //
136 | // DEFINEFUNC_RENAMED_1_1 acts like DEFINEFUNC but tries to load the function using the new name when using >= 1.1.x
137 | // and the old name when using 1.0.2. In both cases the function will have the new name.
138 | //
139 | // DEFINEFUNC_RENAMED_3_0 acts like DEFINEFUNC but tries to load the function using the new name when using >= 3.x
140 | // and the old name when using 1.x. In both cases the function will have the new name.
141 | //
142 | // #include
143 | // #include
144 | // #include
145 | // #include
146 | // #include
147 | // #include
148 | // #include
149 | // #if OPENSSL_VERSION_NUMBER >= 0x30000000L
150 | // #include
151 | // #endif
152 | #define FOR_ALL_OPENSSL_FUNCTIONS \
153 | DEFINEFUNC(unsigned long, ERR_get_error, (void), ()) \
154 | DEFINEFUNC(void, ERR_error_string_n, (unsigned long e, char *buf, size_t len), (e, buf, len)) \
155 | DEFINEFUNC_RENAMED_1_1(const char *, OpenSSL_version, SSLeay_version, (int type), (type)) \
156 | DEFINEFUNC(void, OPENSSL_init, (void), ()) \
157 | DEFINEFUNC_LEGACY_1_0(void, ERR_load_crypto_strings, (void), ()) \
158 | DEFINEFUNC_LEGACY_1_0(int, CRYPTO_num_locks, (void), ()) \
159 | DEFINEFUNC_LEGACY_1_0(void, CRYPTO_set_id_callback, (unsigned long (*id_function)(void)), (id_function)) \
160 | DEFINEFUNC_LEGACY_1_0(void, CRYPTO_set_locking_callback, (void (*locking_function)(int mode, int n, const char *file, int line)), (locking_function)) \
161 | DEFINEFUNC_LEGACY_1_0(void, OPENSSL_add_all_algorithms_conf, (void), ()) \
162 | DEFINEFUNC_1_1(int, OPENSSL_init_crypto, (uint64_t ops, const GO_OPENSSL_INIT_SETTINGS_PTR settings), (ops, settings)) \
163 | DEFINEFUNC_LEGACY_1(int, FIPS_mode, (void), ()) \
164 | DEFINEFUNC_LEGACY_1(int, FIPS_mode_set, (int r), (r)) \
165 | DEFINEFUNC_3_0(int, EVP_default_properties_is_fips_enabled, (GO_OSSL_LIB_CTX_PTR libctx), (libctx)) \
166 | DEFINEFUNC_3_0(int, EVP_default_properties_enable_fips, (GO_OSSL_LIB_CTX_PTR libctx, int enable), (libctx, enable)) \
167 | DEFINEFUNC_3_0(GO_OSSL_PROVIDER_PTR, OSSL_PROVIDER_load, (GO_OSSL_LIB_CTX_PTR libctx, const char *name), (libctx, name)) \
168 | DEFINEFUNC_3_0(int, OSSL_PROVIDER_available, (GO_OSSL_LIB_CTX_PTR libctx, const char *name), (libctx, name)) \
169 | DEFINEFUNC(int, RAND_bytes, (unsigned char* arg0, int arg1), (arg0, arg1)) \
170 | DEFINEFUNC(int, EVP_DigestInit, (GO_EVP_MD_CTX_PTR ctx, const GO_EVP_MD_PTR type), (ctx, type)) \
171 | DEFINEFUNC(int, EVP_DigestInit_ex, (GO_EVP_MD_CTX_PTR ctx, const GO_EVP_MD_PTR type, GO_ENGINE_PTR impl), (ctx, type, impl)) \
172 | DEFINEFUNC(int, EVP_DigestUpdate, (GO_EVP_MD_CTX_PTR ctx, const void *d, size_t cnt), (ctx, d, cnt)) \
173 | DEFINEFUNC(int, EVP_DigestFinal_ex, (GO_EVP_MD_CTX_PTR ctx, unsigned char *md, unsigned int *s), (ctx, md, s)) \
174 | DEFINEFUNC(int, EVP_DigestFinal, (GO_EVP_MD_CTX_PTR ctx, unsigned char *md, unsigned int *s), (ctx, md, s)) \
175 | DEFINEFUNC_RENAMED_1_1(GO_EVP_MD_CTX_PTR, EVP_MD_CTX_new, EVP_MD_CTX_create, (), ()) \
176 | DEFINEFUNC_RENAMED_1_1(void, EVP_MD_CTX_free, EVP_MD_CTX_destroy, (GO_EVP_MD_CTX_PTR ctx), (ctx)) \
177 | DEFINEFUNC(int, EVP_MD_CTX_copy_ex, (GO_EVP_MD_CTX_PTR out, const GO_EVP_MD_CTX_PTR in), (out, in)) \
178 | DEFINEFUNC(int, EVP_MD_CTX_copy, (GO_EVP_MD_CTX_PTR out, const GO_EVP_MD_CTX_PTR in), (out, in)) \
179 | DEFINEFUNC_RENAMED_1_1(int, EVP_MD_CTX_reset, EVP_MD_CTX_cleanup, (GO_EVP_MD_CTX_PTR ctx), (ctx)) \
180 | DEFINEFUNC_3_0(const char *, EVP_MD_get0_name, (const GO_EVP_MD_PTR md), (md)) \
181 | DEFINEFUNC(const GO_EVP_MD_PTR, EVP_md5, (void), ()) \
182 | DEFINEFUNC(const GO_EVP_MD_PTR, EVP_sha1, (void), ()) \
183 | DEFINEFUNC(const GO_EVP_MD_PTR, EVP_sha224, (void), ()) \
184 | DEFINEFUNC(const GO_EVP_MD_PTR, EVP_sha256, (void), ()) \
185 | DEFINEFUNC(const GO_EVP_MD_PTR, EVP_sha384, (void), ()) \
186 | DEFINEFUNC(const GO_EVP_MD_PTR, EVP_sha512, (void), ()) \
187 | DEFINEFUNC_1_1(const GO_EVP_MD_PTR, EVP_md5_sha1, (void), ()) \
188 | DEFINEFUNC_RENAMED_3_0(int, EVP_MD_get_size, EVP_MD_size, (const GO_EVP_MD_PTR arg0), (arg0)) \
189 | DEFINEFUNC_LEGACY_1_0(void, HMAC_CTX_init, (GO_HMAC_CTX_PTR arg0), (arg0)) \
190 | DEFINEFUNC_LEGACY_1_0(void, HMAC_CTX_cleanup, (GO_HMAC_CTX_PTR arg0), (arg0)) \
191 | DEFINEFUNC(int, HMAC_Init_ex, (GO_HMAC_CTX_PTR arg0, const void *arg1, int arg2, const GO_EVP_MD_PTR arg3, GO_ENGINE_PTR arg4), (arg0, arg1, arg2, arg3, arg4)) \
192 | DEFINEFUNC(int, HMAC_Update, (GO_HMAC_CTX_PTR arg0, const unsigned char *arg1, size_t arg2), (arg0, arg1, arg2)) \
193 | DEFINEFUNC(int, HMAC_Final, (GO_HMAC_CTX_PTR arg0, unsigned char *arg1, unsigned int *arg2), (arg0, arg1, arg2)) \
194 | DEFINEFUNC(int, HMAC_CTX_copy, (GO_HMAC_CTX_PTR dest, GO_HMAC_CTX_PTR src), (dest, src)) \
195 | DEFINEFUNC_1_1(void, HMAC_CTX_free, (GO_HMAC_CTX_PTR arg0), (arg0)) \
196 | DEFINEFUNC_1_1(GO_HMAC_CTX_PTR, HMAC_CTX_new, (void), ()) \
197 | DEFINEFUNC_1_1(int, HMAC_CTX_reset, (GO_HMAC_CTX_PTR arg0), (arg0)) \
198 | DEFINEFUNC(GO_EVP_CIPHER_CTX_PTR, EVP_CIPHER_CTX_new, (void), ()) \
199 | DEFINEFUNC(int, EVP_CIPHER_CTX_set_padding, (GO_EVP_CIPHER_CTX_PTR x, int padding), (x, padding)) \
200 | DEFINEFUNC(int, EVP_CipherInit_ex, (GO_EVP_CIPHER_CTX_PTR ctx, const GO_EVP_CIPHER_PTR type, GO_ENGINE_PTR impl, const unsigned char *key, const unsigned char *iv, int enc), (ctx, type, impl, key, iv, enc)) \
201 | DEFINEFUNC(int, EVP_CipherUpdate, (GO_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, int *outl, const unsigned char *in, int inl), (ctx, out, outl, in, inl)) \
202 | DEFINEFUNC(GO_BIGNUM_PTR, BN_new, (void), ()) \
203 | DEFINEFUNC(void, BN_free, (GO_BIGNUM_PTR arg0), (arg0)) \
204 | DEFINEFUNC(void, BN_clear_free, (GO_BIGNUM_PTR arg0), (arg0)) \
205 | DEFINEFUNC(int, BN_num_bits, (const GO_BIGNUM_PTR arg0), (arg0)) \
206 | DEFINEFUNC(GO_BIGNUM_PTR, BN_bin2bn, (const unsigned char *arg0, int arg1, GO_BIGNUM_PTR arg2), (arg0, arg1, arg2)) \
207 | DEFINEFUNC(int, BN_bn2bin, (const GO_BIGNUM_PTR arg0, unsigned char *arg1), (arg0, arg1)) \
208 | /* bn_lebin2bn, bn_bn2lebinpad and BN_bn2binpad are not exported in any OpenSSL 1.0.2, but they exist. */ \
209 | /*check:from=1.1.0*/ DEFINEFUNC_RENAMED_1_1(GO_BIGNUM_PTR, BN_lebin2bn, bn_lebin2bn, (const unsigned char *s, int len, GO_BIGNUM_PTR ret), (s, len, ret)) \
210 | /*check:from=1.1.0*/ DEFINEFUNC_RENAMED_1_1(int, BN_bn2lebinpad, bn_bn2lebinpad, (const GO_BIGNUM_PTR a, unsigned char *to, int tolen), (a, to, tolen)) \
211 | /*check:from=1.1.0*/ DEFINEFUNC_RENAMED_1_1(int, BN_bn2binpad, bn_bn2binpad, (const GO_BIGNUM_PTR a, unsigned char *to, int tolen), (a, to, tolen)) \
212 | DEFINEFUNC(void, EC_GROUP_free, (GO_EC_GROUP_PTR arg0), (arg0)) \
213 | DEFINEFUNC(GO_EC_POINT_PTR, EC_POINT_new, (const GO_EC_GROUP_PTR arg0), (arg0)) \
214 | DEFINEFUNC(void, EC_POINT_free, (GO_EC_POINT_PTR arg0), (arg0)) \
215 | DEFINEFUNC(int, EC_POINT_get_affine_coordinates_GFp, (const GO_EC_GROUP_PTR arg0, const GO_EC_POINT_PTR arg1, GO_BIGNUM_PTR arg2, GO_BIGNUM_PTR arg3, GO_BN_CTX_PTR arg4), (arg0, arg1, arg2, arg3, arg4)) \
216 | DEFINEFUNC(size_t, EC_POINT_point2oct, (const GO_EC_GROUP_PTR group, const GO_EC_POINT_PTR p, point_conversion_form_t form, unsigned char *buf, size_t len, GO_BN_CTX_PTR ctx), (group, p, form, buf, len, ctx)) \
217 | DEFINEFUNC_LEGACY_1_0(int, EC_POINT_oct2point, (const GO_EC_GROUP_PTR group, GO_EC_POINT_PTR p, const unsigned char *buf, size_t len, GO_BN_CTX_PTR ctx), (group, p, buf, len, ctx)) \
218 | DEFINEFUNC(int, EC_POINT_mul, (const GO_EC_GROUP_PTR group, GO_EC_POINT_PTR r, const GO_BIGNUM_PTR n, const GO_EC_POINT_PTR q, const GO_BIGNUM_PTR m, GO_BN_CTX_PTR ctx), (group, r, n, q, m, ctx)) \
219 | DEFINEFUNC(GO_EC_KEY_PTR, EC_KEY_new_by_curve_name, (int arg0), (arg0)) \
220 | DEFINEFUNC(int, EC_KEY_set_public_key_affine_coordinates, (GO_EC_KEY_PTR key, GO_BIGNUM_PTR x, GO_BIGNUM_PTR y), (key, x, y)) \
221 | DEFINEFUNC_LEGACY_1_0(int, EC_KEY_set_public_key, (GO_EC_KEY_PTR key, const GO_EC_POINT_PTR pub), (key, pub)) \
222 | DEFINEFUNC(void, EC_KEY_free, (GO_EC_KEY_PTR arg0), (arg0)) \
223 | DEFINEFUNC(const GO_EC_GROUP_PTR, EC_KEY_get0_group, (const GO_EC_KEY_PTR arg0), (arg0)) \
224 | DEFINEFUNC(int, EC_KEY_set_private_key, (GO_EC_KEY_PTR arg0, const GO_BIGNUM_PTR arg1), (arg0, arg1)) \
225 | DEFINEFUNC_1_1(int, EC_KEY_oct2key, (GO_EC_KEY_PTR eckey, const unsigned char *buf, size_t len, GO_BN_CTX_PTR ctx), (eckey, buf, len, ctx)) \
226 | DEFINEFUNC(const GO_BIGNUM_PTR, EC_KEY_get0_private_key, (const GO_EC_KEY_PTR arg0), (arg0)) \
227 | DEFINEFUNC(const GO_EC_POINT_PTR, EC_KEY_get0_public_key, (const GO_EC_KEY_PTR arg0), (arg0)) \
228 | DEFINEFUNC(GO_RSA_PTR, RSA_new, (void), ()) \
229 | DEFINEFUNC(void, RSA_free, (GO_RSA_PTR arg0), (arg0)) \
230 | DEFINEFUNC_1_1(int, RSA_set0_factors, (GO_RSA_PTR rsa, GO_BIGNUM_PTR p, GO_BIGNUM_PTR q), (rsa, p, q)) \
231 | DEFINEFUNC_1_1(int, RSA_set0_crt_params, (GO_RSA_PTR rsa, GO_BIGNUM_PTR dmp1, GO_BIGNUM_PTR dmp2,GO_BIGNUM_PTR iqmp), (rsa, dmp1, dmp2, iqmp)) \
232 | DEFINEFUNC_1_1(void, RSA_get0_crt_params, (const GO_RSA_PTR r, const GO_BIGNUM_PTR *dmp1, const GO_BIGNUM_PTR *dmq1, const GO_BIGNUM_PTR *iqmp), (r, dmp1, dmq1, iqmp)) \
233 | DEFINEFUNC_1_1(int, RSA_set0_key, (GO_RSA_PTR r, GO_BIGNUM_PTR n, GO_BIGNUM_PTR e, GO_BIGNUM_PTR d), (r, n, e, d)) \
234 | DEFINEFUNC_1_1(void, RSA_get0_factors, (const GO_RSA_PTR rsa, const GO_BIGNUM_PTR *p, const GO_BIGNUM_PTR *q), (rsa, p, q)) \
235 | DEFINEFUNC_1_1(void, RSA_get0_key, (const GO_RSA_PTR rsa, const GO_BIGNUM_PTR *n, const GO_BIGNUM_PTR *e, const GO_BIGNUM_PTR *d), (rsa, n, e, d)) \
236 | DEFINEFUNC(int, EVP_EncryptInit_ex, (GO_EVP_CIPHER_CTX_PTR ctx, const GO_EVP_CIPHER_PTR type, GO_ENGINE_PTR impl, const unsigned char *key, const unsigned char *iv), (ctx, type, impl, key, iv)) \
237 | DEFINEFUNC(int, EVP_EncryptUpdate, (GO_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, int *outl, const unsigned char *in, int inl), (ctx, out, outl, in, inl)) \
238 | DEFINEFUNC(int, EVP_EncryptFinal_ex, (GO_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, int *outl), (ctx, out, outl)) \
239 | DEFINEFUNC(int, EVP_DecryptUpdate, (GO_EVP_CIPHER_CTX_PTR ctx, unsigned char *out, int *outl, const unsigned char *in, int inl), (ctx, out, outl, in, inl)) \
240 | DEFINEFUNC(int, EVP_DecryptFinal_ex, (GO_EVP_CIPHER_CTX_PTR ctx, unsigned char *outm, int *outl), (ctx, outm, outl)) \
241 | DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_128_gcm, (void), ()) \
242 | DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_128_cbc, (void), ()) \
243 | DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_128_ctr, (void), ()) \
244 | DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_128_ecb, (void), ()) \
245 | DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_192_gcm, (void), ()) \
246 | DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_192_cbc, (void), ()) \
247 | DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_192_ctr, (void), ()) \
248 | DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_192_ecb, (void), ()) \
249 | DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_256_cbc, (void), ()) \
250 | DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_256_ctr, (void), ()) \
251 | DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_256_ecb, (void), ()) \
252 | DEFINEFUNC(const GO_EVP_CIPHER_PTR, EVP_aes_256_gcm, (void), ()) \
253 | DEFINEFUNC(void, EVP_CIPHER_CTX_free, (GO_EVP_CIPHER_CTX_PTR arg0), (arg0)) \
254 | DEFINEFUNC(int, EVP_CIPHER_CTX_ctrl, (GO_EVP_CIPHER_CTX_PTR ctx, int type, int arg, void *ptr), (ctx, type, arg, ptr)) \
255 | DEFINEFUNC(GO_EVP_PKEY_PTR, EVP_PKEY_new, (void), ()) \
256 | /* EVP_PKEY_size and EVP_PKEY_get_bits pkey parameter is const since OpenSSL 1.1.1. */ \
257 | /* Exclude it from headercheck tool when using previous OpenSSL versions. */ \
258 | /*check:from=1.1.1*/ DEFINEFUNC_RENAMED_3_0(int, EVP_PKEY_get_size, EVP_PKEY_size, (const GO_EVP_PKEY_PTR pkey), (pkey)) \
259 | /*check:from=1.1.1*/ DEFINEFUNC_RENAMED_3_0(int, EVP_PKEY_get_bits, EVP_PKEY_bits, (const GO_EVP_PKEY_PTR pkey), (pkey)) \
260 | DEFINEFUNC(void, EVP_PKEY_free, (GO_EVP_PKEY_PTR arg0), (arg0)) \
261 | DEFINEFUNC(GO_EC_KEY_PTR, EVP_PKEY_get1_EC_KEY, (GO_EVP_PKEY_PTR pkey), (pkey)) \
262 | DEFINEFUNC(GO_RSA_PTR, EVP_PKEY_get1_RSA, (GO_EVP_PKEY_PTR pkey), (pkey)) \
263 | DEFINEFUNC(int, EVP_PKEY_assign, (GO_EVP_PKEY_PTR pkey, int type, void *key), (pkey, type, key)) \
264 | DEFINEFUNC(int, EVP_PKEY_verify, (GO_EVP_PKEY_CTX_PTR ctx, const unsigned char *sig, size_t siglen, const unsigned char *tbs, size_t tbslen), (ctx, sig, siglen, tbs, tbslen)) \
265 | DEFINEFUNC(GO_EVP_PKEY_CTX_PTR, EVP_PKEY_CTX_new, (GO_EVP_PKEY_PTR arg0, GO_ENGINE_PTR arg1), (arg0, arg1)) \
266 | DEFINEFUNC(GO_EVP_PKEY_CTX_PTR, EVP_PKEY_CTX_new_id, (int id, GO_ENGINE_PTR e), (id, e)) \
267 | DEFINEFUNC(int, EVP_PKEY_keygen_init, (GO_EVP_PKEY_CTX_PTR ctx), (ctx)) \
268 | DEFINEFUNC(int, EVP_PKEY_keygen, (GO_EVP_PKEY_CTX_PTR ctx, GO_EVP_PKEY_PTR *ppkey), (ctx, ppkey)) \
269 | DEFINEFUNC(void, EVP_PKEY_CTX_free, (GO_EVP_PKEY_CTX_PTR arg0), (arg0)) \
270 | DEFINEFUNC(int, EVP_PKEY_CTX_ctrl, (GO_EVP_PKEY_CTX_PTR ctx, int keytype, int optype, int cmd, int p1, void *p2), (ctx, keytype, optype, cmd, p1, p2)) \
271 | DEFINEFUNC(int, EVP_PKEY_decrypt, (GO_EVP_PKEY_CTX_PTR arg0, unsigned char *arg1, size_t *arg2, const unsigned char *arg3, size_t arg4), (arg0, arg1, arg2, arg3, arg4)) \
272 | DEFINEFUNC(int, EVP_PKEY_encrypt, (GO_EVP_PKEY_CTX_PTR arg0, unsigned char *arg1, size_t *arg2, const unsigned char *arg3, size_t arg4), (arg0, arg1, arg2, arg3, arg4)) \
273 | DEFINEFUNC(int, EVP_PKEY_decrypt_init, (GO_EVP_PKEY_CTX_PTR arg0), (arg0)) \
274 | DEFINEFUNC(int, EVP_PKEY_encrypt_init, (GO_EVP_PKEY_CTX_PTR arg0), (arg0)) \
275 | DEFINEFUNC(int, EVP_PKEY_sign_init, (GO_EVP_PKEY_CTX_PTR arg0), (arg0)) \
276 | DEFINEFUNC(int, EVP_PKEY_verify_init, (GO_EVP_PKEY_CTX_PTR arg0), (arg0)) \
277 | DEFINEFUNC(int, EVP_PKEY_sign, (GO_EVP_PKEY_CTX_PTR arg0, unsigned char *arg1, size_t *arg2, const unsigned char *arg3, size_t arg4), (arg0, arg1, arg2, arg3, arg4)) \
278 | DEFINEFUNC(int, EVP_PKEY_derive_init, (GO_EVP_PKEY_CTX_PTR ctx), (ctx)) \
279 | DEFINEFUNC(int, EVP_PKEY_derive_set_peer, (GO_EVP_PKEY_CTX_PTR ctx, GO_EVP_PKEY_PTR peer), (ctx, peer)) \
280 | DEFINEFUNC(int, EVP_PKEY_derive, (GO_EVP_PKEY_CTX_PTR ctx, unsigned char *key, size_t *keylen), (ctx, key, keylen)) \
281 | DEFINEFUNC_3_0(GO_EVP_MAC_PTR, EVP_MAC_fetch, (GO_OSSL_LIB_CTX_PTR ctx, const char *algorithm, const char *properties), (ctx, algorithm, properties)) \
282 | DEFINEFUNC_3_0(void, EVP_MAC_free, (GO_EVP_MAC_PTR mac), (mac)) \
283 | DEFINEFUNC_3_0(GO_EVP_MAC_CTX_PTR, EVP_MAC_CTX_new, (GO_EVP_MAC_PTR arg0), (arg0)) \
284 | DEFINEFUNC_3_0(void, EVP_MAC_CTX_free, (GO_EVP_MAC_CTX_PTR arg0), (arg0)) \
285 | DEFINEFUNC_3_0(GO_EVP_MAC_CTX_PTR, EVP_MAC_CTX_dup, (const GO_EVP_MAC_CTX_PTR arg0), (arg0)) \
286 | DEFINEFUNC_3_0(int, EVP_MAC_init, (GO_EVP_MAC_CTX_PTR ctx, const unsigned char *key, size_t keylen, const OSSL_PARAM params[]), (ctx, key, keylen, params)) \
287 | DEFINEFUNC_3_0(int, EVP_MAC_update, (GO_EVP_MAC_CTX_PTR ctx, const unsigned char *data, size_t datalen), (ctx, data, datalen)) \
288 | DEFINEFUNC_3_0(int, EVP_MAC_final, (GO_EVP_MAC_CTX_PTR ctx, unsigned char *out, size_t *outl, size_t outsize), (ctx, out, outl, outsize)) \
289 | DEFINEFUNC_3_0(OSSL_PARAM, OSSL_PARAM_construct_utf8_string, (const char *key, char *buf, size_t bsize), (key, buf, bsize)) \
290 | DEFINEFUNC_3_0(OSSL_PARAM, OSSL_PARAM_construct_end, (void), ()) \
291 | DEFINEFUNC_3_0(int, EVP_PKEY_CTX_set0_rsa_oaep_label, (GO_EVP_PKEY_CTX_PTR ctx, void *label, int len), (ctx, label, len)) \
292 |
293 |
--------------------------------------------------------------------------------
/openssl/openssl_lock_setup.c:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | //go:build linux && !android
5 | // +build linux,!android
6 |
7 | #include "goopenssl.h"
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | #define _GNU_SOURCE
15 | #include
16 |
17 | #define MUTEX_TYPE pthread_mutex_t
18 | #define MUTEX_SETUP(x) pthread_mutex_init(&(x), NULL)
19 | #define MUTEX_CLEANUP(x) pthread_mutex_destroy(&(x))
20 | #define MUTEX_LOCK(x) pthread_mutex_lock(&(x))
21 | #define MUTEX_UNLOCK(x) pthread_mutex_unlock(&(x))
22 | #define THREAD_ID pthread_self()
23 | #define CRYPTO_LOCK 0x01
24 |
25 | /* This array will store all of the mutexes available to OpenSSL. */
26 | static MUTEX_TYPE *mutex_buf = NULL;
27 |
28 | static void locking_function(int mode, int n, const char *file, int line)
29 | {
30 | if(mode & CRYPTO_LOCK)
31 | MUTEX_LOCK(mutex_buf[n]);
32 | else
33 | MUTEX_UNLOCK(mutex_buf[n]);
34 | }
35 |
36 | static unsigned long id_function(void)
37 | {
38 | return ((unsigned long)syscall(__NR_gettid));
39 | }
40 |
41 | int go_openssl_thread_setup(void)
42 | {
43 | int i;
44 |
45 | mutex_buf = malloc(go_openssl_CRYPTO_num_locks() * sizeof(MUTEX_TYPE));
46 | if(!mutex_buf)
47 | return 0;
48 | for(i = 0; i < go_openssl_CRYPTO_num_locks(); i++)
49 | MUTEX_SETUP(mutex_buf[i]);
50 | go_openssl_CRYPTO_set_id_callback(id_function);
51 | go_openssl_CRYPTO_set_locking_callback(locking_function);
52 | return 1;
53 | }
54 |
--------------------------------------------------------------------------------
/openssl/openssl_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | //go:build linux && !android
5 | // +build linux,!android
6 |
7 | package openssl
8 |
9 | import (
10 | "fmt"
11 | "os"
12 | "testing"
13 | )
14 |
15 | func TestMain(m *testing.M) {
16 | err := Init()
17 | if err != nil {
18 | // An error here could mean that this Linux distro does not have a supported OpenSSL version
19 | // or that there is a bug in the Init code.
20 | panic(err)
21 | }
22 | _ = SetFIPS(true) // Skip the error as we still want to run the tests on machines without FIPS support.
23 | fmt.Println("OpenSSL version:", VersionText())
24 | fmt.Println("FIPS enabled:", FIPS())
25 | os.Exit(m.Run())
26 | }
27 |
--------------------------------------------------------------------------------
/openssl/rand.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | //go:build linux && !android
5 | // +build linux,!android
6 |
7 | package openssl
8 |
9 | // #include "goopenssl.h"
10 | import "C"
11 | import "unsafe"
12 |
13 | type randReader int
14 |
15 | func (randReader) Read(b []byte) (int, error) {
16 | // Note: RAND_bytes should never fail; the return value exists only for historical reasons.
17 | // We check it even so.
18 | if len(b) > 0 && C.go_openssl_RAND_bytes((*C.uchar)(unsafe.Pointer(&b[0])), C.int(len(b))) == 0 {
19 | return 0, fail("RAND_bytes")
20 | }
21 | return len(b), nil
22 | }
23 |
24 | const RandReader = randReader(0)
25 |
--------------------------------------------------------------------------------
/openssl/rand_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | //go:build linux && !android
5 | // +build linux,!android
6 |
7 | package openssl
8 |
9 | import (
10 | "testing"
11 | )
12 |
13 | func TestRand(t *testing.T) {
14 | _, err := RandReader.Read(make([]byte, 5))
15 | if err != nil {
16 | t.Fatal(err)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/openssl/rsa.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | //go:build linux && !android
5 | // +build linux,!android
6 |
7 | package openssl
8 |
9 | // #include "goopenssl.h"
10 | import "C"
11 | import (
12 | "crypto"
13 | "crypto/subtle"
14 | "errors"
15 | "hash"
16 | "runtime"
17 | "unsafe"
18 | )
19 |
20 | func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv BigInt, err error) {
21 | bad := func(e error) (N, E, D, P, Q, Dp, Dq, Qinv BigInt, err error) {
22 | return nil, nil, nil, nil, nil, nil, nil, nil, e
23 | }
24 | pkey, err := generateEVPPKey(C.GO_EVP_PKEY_RSA, bits, "")
25 | if err != nil {
26 | return bad(err)
27 | }
28 | defer C.go_openssl_EVP_PKEY_free(pkey)
29 | key := C.go_openssl_EVP_PKEY_get1_RSA(pkey)
30 | if key == nil {
31 | return bad(newOpenSSLError("EVP_PKEY_get1_RSA failed"))
32 | }
33 | defer C.go_openssl_RSA_free(key)
34 | N, E, D = rsaGetKey(key)
35 | P, Q = rsaGetFactors(key)
36 | Dp, Dq, Qinv = rsaGetCRTParams(key)
37 | return
38 | }
39 |
40 | type PublicKeyRSA struct {
41 | // _pkey MUST NOT be accessed directly. Instead, use the withKey method.
42 | _pkey C.GO_EVP_PKEY_PTR
43 | }
44 |
45 | func NewPublicKeyRSA(N, E BigInt) (*PublicKeyRSA, error) {
46 | key := C.go_openssl_RSA_new()
47 | if key == nil {
48 | return nil, newOpenSSLError("RSA_new failed")
49 | }
50 | if !rsaSetKey(key, N, E, nil) {
51 | return nil, fail("RSA_set0_key")
52 | }
53 | pkey := C.go_openssl_EVP_PKEY_new()
54 | if pkey == nil {
55 | C.go_openssl_RSA_free(key)
56 | return nil, newOpenSSLError("EVP_PKEY_new failed")
57 | }
58 | if C.go_openssl_EVP_PKEY_assign(pkey, C.GO_EVP_PKEY_RSA, (unsafe.Pointer)(key)) != 1 {
59 | C.go_openssl_RSA_free(key)
60 | C.go_openssl_EVP_PKEY_free(pkey)
61 | return nil, newOpenSSLError("EVP_PKEY_assign failed")
62 | }
63 | k := &PublicKeyRSA{_pkey: pkey}
64 | runtime.SetFinalizer(k, (*PublicKeyRSA).finalize)
65 | return k, nil
66 | }
67 |
68 | func (k *PublicKeyRSA) finalize() {
69 | C.go_openssl_EVP_PKEY_free(k._pkey)
70 | }
71 |
72 | func (k *PublicKeyRSA) withKey(f func(C.GO_EVP_PKEY_PTR) C.int) C.int {
73 | // Because of the finalizer, any time _pkey is passed to cgo, that call must
74 | // be followed by a call to runtime.KeepAlive, to make sure k is not
75 | // collected (and finalized) before the cgo call returns.
76 | defer runtime.KeepAlive(k)
77 | return f(k._pkey)
78 | }
79 |
80 | type PrivateKeyRSA struct {
81 | // _pkey MUST NOT be accessed directly. Instead, use the withKey method.
82 | _pkey C.GO_EVP_PKEY_PTR
83 | }
84 |
85 | func NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv BigInt) (*PrivateKeyRSA, error) {
86 | key := C.go_openssl_RSA_new()
87 | if key == nil {
88 | return nil, newOpenSSLError("RSA_new failed")
89 | }
90 | if !rsaSetKey(key, N, E, D) {
91 | return nil, fail("RSA_set0_key")
92 | }
93 | if P != nil && Q != nil {
94 | if !rsaSetFactors(key, P, Q) {
95 | return nil, fail("RSA_set0_factors")
96 | }
97 | }
98 | if Dp != nil && Dq != nil && Qinv != nil {
99 | if !rsaSetCRTParams(key, Dp, Dq, Qinv) {
100 | return nil, fail("RSA_set0_crt_params")
101 | }
102 | }
103 | pkey := C.go_openssl_EVP_PKEY_new()
104 | if pkey == nil {
105 | C.go_openssl_RSA_free(key)
106 | return nil, newOpenSSLError("EVP_PKEY_new failed")
107 | }
108 | if C.go_openssl_EVP_PKEY_assign(pkey, C.GO_EVP_PKEY_RSA, (unsafe.Pointer)(key)) != 1 {
109 | C.go_openssl_RSA_free(key)
110 | C.go_openssl_EVP_PKEY_free(pkey)
111 | return nil, newOpenSSLError("EVP_PKEY_assign failed")
112 | }
113 | k := &PrivateKeyRSA{_pkey: pkey}
114 | runtime.SetFinalizer(k, (*PrivateKeyRSA).finalize)
115 | return k, nil
116 | }
117 |
118 | func (k *PrivateKeyRSA) finalize() {
119 | C.go_openssl_EVP_PKEY_free(k._pkey)
120 | }
121 |
122 | func (k *PrivateKeyRSA) withKey(f func(C.GO_EVP_PKEY_PTR) C.int) C.int {
123 | // Because of the finalizer, any time _pkey is passed to cgo, that call must
124 | // be followed by a call to runtime.KeepAlive, to make sure k is not
125 | // collected (and finalized) before the cgo call returns.
126 | defer runtime.KeepAlive(k)
127 | return f(k._pkey)
128 | }
129 |
130 | func DecryptRSAOAEP(h hash.Hash, priv *PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) {
131 | return evpDecrypt(priv.withKey, C.GO_RSA_PKCS1_OAEP_PADDING, h, nil, label, ciphertext)
132 | }
133 |
134 | func EncryptRSAOAEP(h hash.Hash, pub *PublicKeyRSA, msg, label []byte) ([]byte, error) {
135 | return evpEncrypt(pub.withKey, C.GO_RSA_PKCS1_OAEP_PADDING, h, nil, label, msg)
136 | }
137 |
138 | func DecryptRSAOAEPWithMGF1Hash(h, mgfHash hash.Hash, priv *PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) {
139 | return evpDecrypt(priv.withKey, C.GO_RSA_PKCS1_OAEP_PADDING, h, mgfHash, label, ciphertext)
140 | }
141 |
142 | func EncryptRSAOAEPWithMGF1Hash(h, mgfHash hash.Hash, pub *PublicKeyRSA, msg, label []byte) ([]byte, error) {
143 | return evpEncrypt(pub.withKey, C.GO_RSA_PKCS1_OAEP_PADDING, h, mgfHash, label, msg)
144 | }
145 |
146 | func DecryptRSAPKCS1(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) {
147 | return evpDecrypt(priv.withKey, C.GO_RSA_PKCS1_PADDING, nil, nil, nil, ciphertext)
148 | }
149 |
150 | func EncryptRSAPKCS1(pub *PublicKeyRSA, msg []byte) ([]byte, error) {
151 | return evpEncrypt(pub.withKey, C.GO_RSA_PKCS1_PADDING, nil, nil, nil, msg)
152 | }
153 |
154 | func DecryptRSANoPadding(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) {
155 | ret, err := evpDecrypt(priv.withKey, C.GO_RSA_NO_PADDING, nil, nil, nil, ciphertext)
156 | if err != nil {
157 | return nil, err
158 | }
159 | // We could return here, but the Go standard library test expects DecryptRSANoPadding to verify the result
160 | // in order to defend against errors in the CRT computation.
161 | //
162 | // The following code tries to replicate the verification implemented in the upstream function decryptAndCheck, found at
163 | // https://github.com/golang/go/blob/9de1ac6ac2cad3871760d0aa288f5ca713afd0a6/src/crypto/rsa/rsa.go#L569-L582.
164 | pub := &PublicKeyRSA{_pkey: priv._pkey}
165 | // A private EVP_PKEY can be used as a public key as it contains the public information.
166 | enc, err := EncryptRSANoPadding(pub, ret)
167 | if err != nil {
168 | return nil, err
169 | }
170 | // Upstream does not do a constant time comparison because it works with math/big instead of byte slices,
171 | // and math/big does not support constant-time arithmetic yet. See #20654 for more info.
172 | if subtle.ConstantTimeCompare(ciphertext, enc) != 1 {
173 | return nil, errors.New("rsa: internal error")
174 | }
175 | return ret, nil
176 | }
177 |
178 | func EncryptRSANoPadding(pub *PublicKeyRSA, msg []byte) ([]byte, error) {
179 | return evpEncrypt(pub.withKey, C.GO_RSA_NO_PADDING, nil, nil, nil, msg)
180 | }
181 |
182 | func saltLength(saltLen int, sign bool) (C.int, error) {
183 | // A salt length of -2 is valid in OpenSSL, but not in crypto/rsa, so reject
184 | // it, and lengths < -2, before we convert to the OpenSSL sentinel values.
185 | if saltLen <= -2 {
186 | return 0, errors.New("crypto/rsa: PSSOptions.SaltLength cannot be negative")
187 | }
188 | // OpenSSL uses sentinel salt length values like Go crypto does,
189 | // but the values don't fully match for rsa.PSSSaltLengthAuto (0).
190 | if saltLen == 0 {
191 | if sign {
192 | if vMajor == 1 {
193 | // OpenSSL 1.x uses -2 to mean maximal size when signing where Go crypto uses 0.
194 | return C.GO_RSA_PSS_SALTLEN_MAX_SIGN, nil
195 | }
196 | // OpenSSL 3.x deprecated RSA_PSS_SALTLEN_MAX_SIGN
197 | // and uses -3 to mean maximal size when signing where Go crypto uses 0.
198 | return C.GO_RSA_PSS_SALTLEN_MAX, nil
199 | }
200 | // OpenSSL uses -2 to mean auto-detect size when verifying where Go crypto uses 0.
201 | return C.GO_RSA_PSS_SALTLEN_AUTO, nil
202 | }
203 | return C.int(saltLen), nil
204 | }
205 |
206 | func SignRSAPSS(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte, saltLen int) ([]byte, error) {
207 | cSaltLen, err := saltLength(saltLen, true)
208 | if err != nil {
209 | return nil, err
210 | }
211 | return evpSign(priv.withKey, C.GO_RSA_PKCS1_PSS_PADDING, cSaltLen, h, hashed)
212 | }
213 |
214 | func VerifyRSAPSS(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte, saltLen int) error {
215 | cSaltLen, err := saltLength(saltLen, false)
216 | if err != nil {
217 | return err
218 | }
219 | return evpVerify(pub.withKey, C.GO_RSA_PKCS1_PSS_PADDING, cSaltLen, h, sig, hashed)
220 | }
221 |
222 | func SignRSAPKCS1v15(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte) ([]byte, error) {
223 | return evpSign(priv.withKey, C.GO_RSA_PKCS1_PADDING, 0, h, hashed)
224 | }
225 |
226 | func VerifyRSAPKCS1v15(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte) error {
227 | if pub.withKey(func(pkey C.GO_EVP_PKEY_PTR) C.int {
228 | size := C.go_openssl_EVP_PKEY_get_size(pkey)
229 | if len(sig) < int(size) {
230 | return 0
231 | }
232 | return 1
233 | }) == 0 {
234 | return errors.New("crypto/rsa: verification error")
235 | }
236 | return evpVerify(pub.withKey, C.GO_RSA_PKCS1_PADDING, 0, h, sig, hashed)
237 | }
238 |
239 | // rsa_st_1_0_2 is rsa_st memory layout in OpenSSL 1.0.2.
240 | type rsa_st_1_0_2 struct {
241 | _ C.int
242 | _ C.long
243 | _ [2]unsafe.Pointer
244 | n, e, d C.GO_BIGNUM_PTR
245 | p, q C.GO_BIGNUM_PTR
246 | dmp1, dmq1, iqmp C.GO_BIGNUM_PTR
247 | // It contains more fields, but we are not interesed on them.
248 | }
249 |
250 | func bnSet(b1 *C.GO_BIGNUM_PTR, b2 BigInt) {
251 | if b2 == nil {
252 | return
253 | }
254 | if *b1 != nil {
255 | C.go_openssl_BN_clear_free(*b1)
256 | }
257 | *b1 = bigToBN(b2)
258 | }
259 |
260 | func rsaSetKey(key C.GO_RSA_PTR, n, e, d BigInt) bool {
261 | if vMajor == 1 && vMinor == 0 {
262 | r := (*rsa_st_1_0_2)(unsafe.Pointer(key))
263 | //r.d and d will be nil for public keys.
264 | if (r.n == nil && n == nil) ||
265 | (r.e == nil && e == nil) {
266 | return false
267 | }
268 | bnSet(&r.n, n)
269 | bnSet(&r.e, e)
270 | bnSet(&r.d, d)
271 | return true
272 | }
273 | return C.go_openssl_RSA_set0_key(key, bigToBN(n), bigToBN(e), bigToBN(d)) == 1
274 | }
275 |
276 | func rsaSetFactors(key C.GO_RSA_PTR, p, q BigInt) bool {
277 | if vMajor == 1 && vMinor == 0 {
278 | r := (*rsa_st_1_0_2)(unsafe.Pointer(key))
279 | if (r.p == nil && p == nil) ||
280 | (r.q == nil && q == nil) {
281 | return false
282 | }
283 | bnSet(&r.p, p)
284 | bnSet(&r.q, q)
285 | return true
286 | }
287 | return C.go_openssl_RSA_set0_factors(key, bigToBN(p), bigToBN(q)) == 1
288 | }
289 |
290 | func rsaSetCRTParams(key C.GO_RSA_PTR, dmp1, dmq1, iqmp BigInt) bool {
291 | if vMajor == 1 && vMinor == 0 {
292 | r := (*rsa_st_1_0_2)(unsafe.Pointer(key))
293 | if (r.dmp1 == nil && dmp1 == nil) ||
294 | (r.dmq1 == nil && dmq1 == nil) ||
295 | (r.iqmp == nil && iqmp == nil) {
296 | return false
297 | }
298 | bnSet(&r.dmp1, dmp1)
299 | bnSet(&r.dmq1, dmq1)
300 | bnSet(&r.iqmp, iqmp)
301 | return true
302 | }
303 | return C.go_openssl_RSA_set0_crt_params(key, bigToBN(dmp1), bigToBN(dmq1), bigToBN(iqmp)) == 1
304 | }
305 |
306 | func rsaGetKey(key C.GO_RSA_PTR) (BigInt, BigInt, BigInt) {
307 | var n, e, d C.GO_BIGNUM_PTR
308 | if vMajor == 1 && vMinor == 0 {
309 | r := (*rsa_st_1_0_2)(unsafe.Pointer(key))
310 | n, e, d = r.n, r.e, r.d
311 | } else {
312 | C.go_openssl_RSA_get0_key(key, &n, &e, &d)
313 | }
314 | return bnToBig(n), bnToBig(e), bnToBig(d)
315 | }
316 |
317 | func rsaGetFactors(key C.GO_RSA_PTR) (BigInt, BigInt) {
318 | var p, q C.GO_BIGNUM_PTR
319 | if vMajor == 1 && vMinor == 0 {
320 | r := (*rsa_st_1_0_2)(unsafe.Pointer(key))
321 | p, q = r.p, r.q
322 | } else {
323 | C.go_openssl_RSA_get0_factors(key, &p, &q)
324 | }
325 | return bnToBig(p), bnToBig(q)
326 | }
327 |
328 | func rsaGetCRTParams(key C.GO_RSA_PTR) (BigInt, BigInt, BigInt) {
329 | var dmp1, dmq1, iqmp C.GO_BIGNUM_PTR
330 | if vMajor == 1 && vMinor == 0 {
331 | r := (*rsa_st_1_0_2)(unsafe.Pointer(key))
332 | dmp1, dmq1, iqmp = r.dmp1, r.dmq1, r.iqmp
333 | } else {
334 | C.go_openssl_RSA_get0_crt_params(key, &dmp1, &dmq1, &iqmp)
335 | }
336 | return bnToBig(dmp1), bnToBig(dmq1), bnToBig(iqmp)
337 | }
338 |
--------------------------------------------------------------------------------
/openssl/rsa_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | //go:build linux && !android
5 | // +build linux,!android
6 |
7 | package openssl_test
8 |
9 | import (
10 | "bytes"
11 | "crypto"
12 | "crypto/rsa"
13 | "math/big"
14 | "strconv"
15 | "testing"
16 |
17 | "github.com/microsoft/go-crypto-openssl/openssl"
18 | "github.com/microsoft/go-crypto-openssl/openssl/bbig/bridge"
19 | )
20 |
21 | func TestRSAKeyGeneration(t *testing.T) {
22 | for _, size := range []int{2048, 3072} {
23 | t.Run(strconv.Itoa(size), func(t *testing.T) {
24 | t.Parallel()
25 | priv, pub := newRSAKey(t, size)
26 | msg := []byte("hi!")
27 | enc, err := openssl.EncryptRSAPKCS1(pub, msg)
28 | if err != nil {
29 | t.Fatalf("EncryptPKCS1v15: %v", err)
30 | }
31 | dec, err := openssl.DecryptRSAPKCS1(priv, enc)
32 | if err != nil {
33 | t.Fatalf("DecryptPKCS1v15: %v", err)
34 | }
35 | if !bytes.Equal(dec, msg) {
36 | t.Fatalf("got:%x want:%x", dec, msg)
37 | }
38 | })
39 | }
40 | }
41 |
42 | func TestEncryptDecryptOAEP(t *testing.T) {
43 | sha256 := openssl.NewSHA256()
44 | msg := []byte("hi!")
45 | label := []byte("ho!")
46 | priv, pub := newRSAKey(t, 2048)
47 | enc, err := openssl.EncryptRSAOAEP(sha256, pub, msg, label)
48 | if err != nil {
49 | t.Fatal(err)
50 | }
51 | dec, err := openssl.DecryptRSAOAEP(sha256, priv, enc, label)
52 | if err != nil {
53 | t.Fatal(err)
54 | }
55 | if !bytes.Equal(dec, msg) {
56 | t.Errorf("got:%x want:%x", dec, msg)
57 | }
58 | sha1 := openssl.NewSHA1()
59 | _, err = openssl.DecryptRSAOAEP(sha1, priv, enc, label)
60 | if err == nil {
61 | t.Error("decrypt failure expected due to hash mismatch")
62 | }
63 | }
64 |
65 | func TestEncryptDecryptOAEPWithMGF1Hash(t *testing.T) {
66 | sha1 := openssl.NewSHA1()
67 | sha256 := openssl.NewSHA256()
68 | msg := []byte("hi!")
69 | label := []byte("ho!")
70 | priv, pub := newRSAKey(t, 2048)
71 | enc, err := openssl.EncryptRSAOAEPWithMGF1Hash(sha256, sha1, pub, msg, label)
72 | if err != nil {
73 | t.Fatal(err)
74 | }
75 | dec, err := openssl.DecryptRSAOAEPWithMGF1Hash(sha256, sha1, priv, enc, label)
76 | if err != nil {
77 | t.Fatal(err)
78 | }
79 | if !bytes.Equal(dec, msg) {
80 | t.Errorf("got:%x want:%x", dec, msg)
81 | }
82 | _, err = openssl.DecryptRSAOAEPWithMGF1Hash(sha256, sha256, priv, enc, label)
83 | if err == nil {
84 | t.Error("decrypt failure expected due to mgf1 hash mismatch")
85 | }
86 | }
87 |
88 | func TestEncryptDecryptOAEP_WrongLabel(t *testing.T) {
89 | sha256 := openssl.NewSHA256()
90 | msg := []byte("hi!")
91 | priv, pub := newRSAKey(t, 2048)
92 | enc, err := openssl.EncryptRSAOAEP(sha256, pub, msg, []byte("ho!"))
93 | if err != nil {
94 | t.Fatal(err)
95 | }
96 | dec, err := openssl.DecryptRSAOAEP(sha256, priv, enc, []byte("wrong!"))
97 | if err == nil {
98 | t.Errorf("error expected")
99 | }
100 | if dec != nil {
101 | t.Errorf("got:%x want: nil", dec)
102 | }
103 | }
104 |
105 | func TestSignVerifyPKCS1v15(t *testing.T) {
106 | sha256 := openssl.NewSHA256()
107 | priv, pub := newRSAKey(t, 2048)
108 | sha256.Write([]byte("hi!"))
109 | hashed := sha256.Sum(nil)
110 | signed, err := openssl.SignRSAPKCS1v15(priv, crypto.SHA256, hashed)
111 | if err != nil {
112 | t.Fatal(err)
113 | }
114 | err = openssl.VerifyRSAPKCS1v15(pub, crypto.SHA256, hashed, signed)
115 | if err != nil {
116 | t.Fatal(err)
117 | }
118 | }
119 |
120 | func TestSignVerifyPKCS1v15_Unhashed(t *testing.T) {
121 | msg := []byte("hi!")
122 | priv, pub := newRSAKey(t, 2048)
123 | signed, err := openssl.SignRSAPKCS1v15(priv, 0, msg)
124 | if err != nil {
125 | t.Fatal(err)
126 | }
127 | err = openssl.VerifyRSAPKCS1v15(pub, 0, msg, signed)
128 | if err != nil {
129 | t.Fatal(err)
130 | }
131 | }
132 |
133 | func TestSignVerifyPKCS1v15_Invalid(t *testing.T) {
134 | sha256 := openssl.NewSHA256()
135 | msg := []byte("hi!")
136 | priv, pub := newRSAKey(t, 2048)
137 | sha256.Write(msg)
138 | hashed := sha256.Sum(nil)
139 | signed, err := openssl.SignRSAPKCS1v15(priv, crypto.SHA256, hashed)
140 | if err != nil {
141 | t.Fatal(err)
142 | }
143 | err = openssl.VerifyRSAPKCS1v15(pub, crypto.SHA256, msg, signed)
144 | if err == nil {
145 | t.Fatal("error expected")
146 | }
147 | }
148 |
149 | func TestSignVerifyRSAPSS(t *testing.T) {
150 | // Test cases taken from
151 | // https://github.com/golang/go/blob/54182ff54a687272dd7632c3a963e036ce03cb7c/src/crypto/rsa/pss_test.go#L200.
152 | const keyBits = 2048
153 | var saltLengthCombinations = []struct {
154 | signSaltLength, verifySaltLength int
155 | good bool
156 | }{
157 | {rsa.PSSSaltLengthAuto, rsa.PSSSaltLengthAuto, true},
158 | {rsa.PSSSaltLengthEqualsHash, rsa.PSSSaltLengthAuto, true},
159 | {rsa.PSSSaltLengthEqualsHash, rsa.PSSSaltLengthEqualsHash, true},
160 | {rsa.PSSSaltLengthEqualsHash, 8, false},
161 | {rsa.PSSSaltLengthAuto, rsa.PSSSaltLengthEqualsHash, false},
162 | {8, 8, true},
163 | {rsa.PSSSaltLengthAuto, keyBits/8 - 2 - 32, true}, // simulate Go PSSSaltLengthAuto algorithm (32 = sha256 size)
164 | {rsa.PSSSaltLengthAuto, 20, false},
165 | {rsa.PSSSaltLengthAuto, -2, false},
166 | }
167 | sha256 := openssl.NewSHA256()
168 | priv, pub := newRSAKey(t, keyBits)
169 | sha256.Write([]byte("testing"))
170 | hashed := sha256.Sum(nil)
171 | for i, test := range saltLengthCombinations {
172 | signed, err := openssl.SignRSAPSS(priv, crypto.SHA256, hashed, test.signSaltLength)
173 | if err != nil {
174 | t.Errorf("#%d: error while signing: %s", i, err)
175 | continue
176 | }
177 | err = openssl.VerifyRSAPSS(pub, crypto.SHA256, hashed, signed, test.verifySaltLength)
178 | if (err == nil) != test.good {
179 | t.Errorf("#%d: bad result, wanted: %t, got: %s", i, test.good, err)
180 | }
181 | }
182 | }
183 |
184 | func newRSAKey(t *testing.T, size int) (*openssl.PrivateKeyRSA, *openssl.PublicKeyRSA) {
185 | t.Helper()
186 | N, E, D, P, Q, Dp, Dq, Qinv, err := bridge.GenerateKeyRSA(size)
187 | if err != nil {
188 | t.Fatalf("GenerateKeyRSA(%d): %v", size, err)
189 | }
190 | priv, err := bridge.NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv)
191 | if err != nil {
192 | t.Fatalf("NewPrivateKeyRSA(%d): %v", size, err)
193 | }
194 | pub, err := bridge.NewPublicKeyRSA(N, E)
195 | if err != nil {
196 | t.Fatalf("NewPublicKeyRSA(%d): %v", size, err)
197 | }
198 | return priv, pub
199 | }
200 |
201 | func fromBase36(base36 string) *big.Int {
202 | i, ok := new(big.Int).SetString(base36, 36)
203 | if !ok {
204 | panic("bad number: " + base36)
205 | }
206 | return i
207 | }
208 |
209 | func BenchmarkEncryptRSAPKCS1(b *testing.B) {
210 | b.StopTimer()
211 | // Public key length should be at least of 2048 bits, else OpenSSL will report an error when running in FIPS mode.
212 | n := fromBase36("14314132931241006650998084889274020608918049032671858325988396851334124245188214251956198731333464217832226406088020736932173064754214329009979944037640912127943488972644697423190955557435910767690712778463524983667852819010259499695177313115447116110358524558307947613422897787329221478860907963827160223559690523660574329011927531289655711860504630573766609239332569210831325633840174683944553667352219670930408593321661375473885147973879086994006440025257225431977751512374815915392249179976902953721486040787792801849818254465486633791826766873076617116727073077821584676715609985777563958286637185868165868520557")
213 | test2048PubKey, err := bridge.NewPublicKeyRSA(n, big.NewInt(3))
214 | if err != nil {
215 | b.Fatal(err)
216 | }
217 | b.StartTimer()
218 | b.ReportAllocs()
219 | for i := 0; i < b.N; i++ {
220 | if _, err := openssl.EncryptRSAPKCS1(test2048PubKey, []byte("testing")); err != nil {
221 | b.Fatal(err)
222 | }
223 | }
224 | }
225 |
226 | func BenchmarkGenerateKeyRSA(b *testing.B) {
227 | b.ReportAllocs()
228 | for i := 0; i < b.N; i++ {
229 | _, _, _, _, _, _, _, _, err := bridge.GenerateKeyRSA(2048)
230 | if err != nil {
231 | b.Fatal(err)
232 | }
233 | }
234 | }
235 |
--------------------------------------------------------------------------------
/openssl/sha.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | //go:build linux && !android
5 | // +build linux,!android
6 |
7 | package openssl
8 |
9 | // #include "goopenssl.h"
10 | import "C"
11 | import (
12 | "crypto"
13 | "errors"
14 | "hash"
15 | "runtime"
16 | "strconv"
17 | "unsafe"
18 | )
19 |
20 | // NOTE: Implementation ported from https://go-review.googlesource.com/c/go/+/404295.
21 | // The cgo calls in this file are arranged to avoid marking the parameters as escaping.
22 | // To do that, we call noescape (including via addr).
23 | // We must also make sure that the data pointer arguments have the form unsafe.Pointer(&...)
24 | // so that cgo does not annotate them with cgoCheckPointer calls. If it did that, it might look
25 | // beyond the byte slice and find Go pointers in unprocessed parts of a larger allocation.
26 | // To do both of these simultaneously, the idiom is unsafe.Pointer(&*addr(p)),
27 | // where addr returns the base pointer of p, substituting a non-nil pointer for nil,
28 | // and applying a noescape along the way.
29 | // This is all to preserve compatibility with the allocation behavior of the non-openssl implementations.
30 |
31 | func shaX(md C.GO_EVP_MD_PTR, p []byte, sum []byte) bool {
32 | return C.go_shaX(md, unsafe.Pointer(&*addr(p)), C.size_t(len(p)), noescape(unsafe.Pointer(&sum[0]))) != 0
33 | }
34 |
35 | func SHA1(p []byte) (sum [20]byte) {
36 | if !shaX(C.go_openssl_EVP_sha1(), p, sum[:]) {
37 | panic("openssl: SHA1 failed")
38 | }
39 | return
40 | }
41 |
42 | func SHA224(p []byte) (sum [28]byte) {
43 | if !shaX(C.go_openssl_EVP_sha224(), p, sum[:]) {
44 | panic("openssl: SHA224 failed")
45 | }
46 | return
47 | }
48 |
49 | func SHA256(p []byte) (sum [32]byte) {
50 | if !shaX(C.go_openssl_EVP_sha256(), p, sum[:]) {
51 | panic("openssl: SHA256 failed")
52 | }
53 | return
54 | }
55 |
56 | func SHA384(p []byte) (sum [48]byte) {
57 | if !shaX(C.go_openssl_EVP_sha384(), p, sum[:]) {
58 | panic("openssl: SHA384 failed")
59 | }
60 | return
61 | }
62 |
63 | func SHA512(p []byte) (sum [64]byte) {
64 | if !shaX(C.go_openssl_EVP_sha512(), p, sum[:]) {
65 | panic("openssl: SHA512 failed")
66 | }
67 | return
68 | }
69 |
70 | type evpHash struct {
71 | md C.GO_EVP_MD_PTR
72 | ctx C.GO_EVP_MD_CTX_PTR
73 | // ctx2 is used in evpHash.sum to avoid changing
74 | // the state of ctx. Having it here allows reusing the
75 | // same allocated object multiple times.
76 | ctx2 C.GO_EVP_MD_CTX_PTR
77 | size int
78 | blockSize int
79 | }
80 |
81 | func newEvpHash(ch crypto.Hash, size, blockSize int) *evpHash {
82 | md := cryptoHashToMD(ch)
83 | if md == nil {
84 | panic("openssl: unsupported hash function: " + strconv.Itoa(int(ch)))
85 | }
86 | ctx := C.go_openssl_EVP_MD_CTX_new()
87 | ctx2 := C.go_openssl_EVP_MD_CTX_new()
88 | h := &evpHash{
89 | md: md,
90 | ctx: ctx,
91 | ctx2: ctx2,
92 | size: size,
93 | blockSize: blockSize,
94 | }
95 | runtime.SetFinalizer(h, (*evpHash).finalize)
96 | h.Reset()
97 | return h
98 | }
99 |
100 | func (h *evpHash) finalize() {
101 | C.go_openssl_EVP_MD_CTX_free(h.ctx)
102 | C.go_openssl_EVP_MD_CTX_free(h.ctx2)
103 | }
104 |
105 | func (h *evpHash) Reset() {
106 | // There is no need to reset h.ctx2 because it is always reset after
107 | // use in evpHash.sum.
108 | // Calling EVP_DigestInit on an already initialized EVP_MD_CTX results in
109 | // memory leak on OpenSSL 1.0.2, use EVP_DigestInit_ex instead.
110 | if C.go_openssl_EVP_DigestInit_ex(h.ctx, h.md, nil) != 1 {
111 | panic("openssl: EVP_DigestInit_ex failed")
112 | }
113 | runtime.KeepAlive(h)
114 | }
115 |
116 | func (h *evpHash) Write(p []byte) (int, error) {
117 | if len(p) > 0 && C.go_openssl_EVP_DigestUpdate(h.ctx, unsafe.Pointer(&*addr(p)), C.size_t(len(p))) != 1 {
118 | panic("openssl: EVP_DigestUpdate failed")
119 | }
120 | runtime.KeepAlive(h)
121 | return len(p), nil
122 | }
123 |
124 | func (h *evpHash) WriteString(s string) (int, error) {
125 | // TODO: use unsafe.StringData once we drop support
126 | // for go1.19 and earlier.
127 | hdr := (*struct {
128 | Data *byte
129 | Len int
130 | })(unsafe.Pointer(&s))
131 | if len(s) > 0 && C.go_openssl_EVP_DigestUpdate(h.ctx, unsafe.Pointer(hdr.Data), C.size_t(len(s))) == 0 {
132 | panic("openssl: EVP_DigestUpdate failed")
133 | }
134 | runtime.KeepAlive(h)
135 | return len(s), nil
136 | }
137 |
138 | func (h *evpHash) WriteByte(c byte) error {
139 | if C.go_openssl_EVP_DigestUpdate(h.ctx, unsafe.Pointer(&c), 1) == 0 {
140 | panic("openssl: EVP_DigestUpdate failed")
141 | }
142 | runtime.KeepAlive(h)
143 | return nil
144 | }
145 |
146 | func (h *evpHash) Size() int {
147 | return h.size
148 | }
149 |
150 | func (h *evpHash) BlockSize() int {
151 | return h.blockSize
152 | }
153 |
154 | func (h *evpHash) sum(out []byte) {
155 | // Make copy of context because Go hash.Hash mandates
156 | // that Sum has no effect on the underlying stream.
157 | // In particular it is OK to Sum, then Write more, then Sum again,
158 | // and the second Sum acts as if the first didn't happen.
159 | if C.go_openssl_EVP_MD_CTX_copy(h.ctx2, h.ctx) != 1 {
160 | panic("openssl: EVP_MD_CTX_copy failed")
161 | }
162 | if C.go_openssl_EVP_DigestFinal(h.ctx2, (*C.uchar)(noescape(unsafe.Pointer(base(out)))), nil) != 1 {
163 | panic("openssl: EVP_DigestFinal failed")
164 | }
165 | runtime.KeepAlive(h)
166 | }
167 |
168 | // shaState returns a pointer to the internal sha structure.
169 | //
170 | // The EVP_MD_CTX memory layout has changed in OpenSSL 3
171 | // and the property holding the internal structure is no longer md_data but algctx.
172 | func (h *evpHash) shaState() unsafe.Pointer {
173 | switch vMajor {
174 | case 1:
175 | // https://github.com/openssl/openssl/blob/0418e993c717a6863f206feaa40673a261de7395/crypto/evp/evp_local.h#L12.
176 | type mdCtx struct {
177 | _ [2]unsafe.Pointer
178 | _ C.ulong
179 | md_data unsafe.Pointer
180 | }
181 | return (*mdCtx)(unsafe.Pointer(h.ctx)).md_data
182 | case 3:
183 | // https://github.com/openssl/openssl/blob/5675a5aaf6a2e489022bcfc18330dae9263e598e/crypto/evp/evp_local.h#L16.
184 | type mdCtx struct {
185 | _ [3]unsafe.Pointer
186 | _ C.ulong
187 | _ [3]unsafe.Pointer
188 | algctx unsafe.Pointer
189 | }
190 | return (*mdCtx)(unsafe.Pointer(h.ctx)).algctx
191 | default:
192 | panic(errUnsuportedVersion())
193 | }
194 | }
195 |
196 | // NewSHA1 returns a new SHA1 hash.
197 | func NewSHA1() hash.Hash {
198 | return &sha1Hash{
199 | evpHash: newEvpHash(crypto.SHA1, 20, 64),
200 | }
201 | }
202 |
203 | type sha1Hash struct {
204 | *evpHash
205 | out [20]byte
206 | }
207 |
208 | func (h *sha1Hash) Sum(in []byte) []byte {
209 | h.sum(h.out[:])
210 | return append(in, h.out[:]...)
211 | }
212 |
213 | // sha1State layout is taken from
214 | // https://github.com/openssl/openssl/blob/0418e993c717a6863f206feaa40673a261de7395/include/openssl/sha.h#L34.
215 | type sha1State struct {
216 | h [5]uint32
217 | nl, nh uint32
218 | x [64]byte
219 | nx uint32
220 | }
221 |
222 | const (
223 | sha1Magic = "sha\x01"
224 | sha1MarshaledSize = len(sha1Magic) + 5*4 + 64 + 8
225 | )
226 |
227 | func (h *sha1Hash) MarshalBinary() ([]byte, error) {
228 | d := (*sha1State)(h.shaState())
229 | if d == nil {
230 | return nil, errors.New("crypto/sha1: can't retrieve hash state")
231 | }
232 | b := make([]byte, 0, sha1MarshaledSize)
233 | b = append(b, sha1Magic...)
234 | b = appendUint32(b, d.h[0])
235 | b = appendUint32(b, d.h[1])
236 | b = appendUint32(b, d.h[2])
237 | b = appendUint32(b, d.h[3])
238 | b = appendUint32(b, d.h[4])
239 | b = append(b, d.x[:d.nx]...)
240 | b = b[:len(b)+len(d.x)-int(d.nx)] // already zero
241 | b = appendUint64(b, uint64(d.nl)>>3|uint64(d.nh)<<29)
242 | return b, nil
243 | }
244 |
245 | func (h *sha1Hash) UnmarshalBinary(b []byte) error {
246 | if len(b) < len(sha1Magic) || string(b[:len(sha1Magic)]) != sha1Magic {
247 | return errors.New("crypto/sha1: invalid hash state identifier")
248 | }
249 | if len(b) != sha1MarshaledSize {
250 | return errors.New("crypto/sha1: invalid hash state size")
251 | }
252 | d := (*sha1State)(h.shaState())
253 | if d == nil {
254 | return errors.New("crypto/sha1: can't retrieve hash state")
255 | }
256 | b = b[len(sha1Magic):]
257 | b, d.h[0] = consumeUint32(b)
258 | b, d.h[1] = consumeUint32(b)
259 | b, d.h[2] = consumeUint32(b)
260 | b, d.h[3] = consumeUint32(b)
261 | b, d.h[4] = consumeUint32(b)
262 | b = b[copy(d.x[:], b):]
263 | _, n := consumeUint64(b)
264 | d.nl = uint32(n << 3)
265 | d.nh = uint32(n >> 29)
266 | d.nx = uint32(n) % 64
267 | return nil
268 | }
269 |
270 | // NewSHA224 returns a new SHA224 hash.
271 | func NewSHA224() hash.Hash {
272 | return &sha224Hash{
273 | evpHash: newEvpHash(crypto.SHA224, 224/8, 64),
274 | }
275 | }
276 |
277 | type sha224Hash struct {
278 | *evpHash
279 | out [224 / 8]byte
280 | }
281 |
282 | func (h *sha224Hash) Sum(in []byte) []byte {
283 | h.sum(h.out[:])
284 | return append(in, h.out[:]...)
285 | }
286 |
287 | // NewSHA256 returns a new SHA256 hash.
288 | func NewSHA256() hash.Hash {
289 | return &sha256Hash{
290 | evpHash: newEvpHash(crypto.SHA256, 256/8, 64),
291 | }
292 | }
293 |
294 | type sha256Hash struct {
295 | *evpHash
296 | out [256 / 8]byte
297 | }
298 |
299 | func (h *sha256Hash) Sum(in []byte) []byte {
300 | h.sum(h.out[:])
301 | return append(in, h.out[:]...)
302 | }
303 |
304 | const (
305 | magic224 = "sha\x02"
306 | magic256 = "sha\x03"
307 | marshaledSize256 = len(magic256) + 8*4 + 64 + 8
308 | )
309 |
310 | // sha256State layout is taken from
311 | // https://github.com/openssl/openssl/blob/0418e993c717a6863f206feaa40673a261de7395/include/openssl/sha.h#L51.
312 | type sha256State struct {
313 | h [8]uint32
314 | nl, nh uint32
315 | x [64]byte
316 | nx uint32
317 | }
318 |
319 | func (h *sha224Hash) MarshalBinary() ([]byte, error) {
320 | d := (*sha256State)(h.shaState())
321 | if d == nil {
322 | return nil, errors.New("crypto/sha256: can't retrieve hash state")
323 | }
324 | b := make([]byte, 0, marshaledSize256)
325 | b = append(b, magic224...)
326 | b = appendUint32(b, d.h[0])
327 | b = appendUint32(b, d.h[1])
328 | b = appendUint32(b, d.h[2])
329 | b = appendUint32(b, d.h[3])
330 | b = appendUint32(b, d.h[4])
331 | b = appendUint32(b, d.h[5])
332 | b = appendUint32(b, d.h[6])
333 | b = appendUint32(b, d.h[7])
334 | b = append(b, d.x[:d.nx]...)
335 | b = b[:len(b)+len(d.x)-int(d.nx)] // already zero
336 | b = appendUint64(b, uint64(d.nl)>>3|uint64(d.nh)<<29)
337 | return b, nil
338 | }
339 |
340 | func (h *sha256Hash) MarshalBinary() ([]byte, error) {
341 | d := (*sha256State)(h.shaState())
342 | if d == nil {
343 | return nil, errors.New("crypto/sha256: can't retrieve hash state")
344 | }
345 | b := make([]byte, 0, marshaledSize256)
346 | b = append(b, magic256...)
347 | b = appendUint32(b, d.h[0])
348 | b = appendUint32(b, d.h[1])
349 | b = appendUint32(b, d.h[2])
350 | b = appendUint32(b, d.h[3])
351 | b = appendUint32(b, d.h[4])
352 | b = appendUint32(b, d.h[5])
353 | b = appendUint32(b, d.h[6])
354 | b = appendUint32(b, d.h[7])
355 | b = append(b, d.x[:d.nx]...)
356 | b = b[:len(b)+len(d.x)-int(d.nx)] // already zero
357 | b = appendUint64(b, uint64(d.nl)>>3|uint64(d.nh)<<29)
358 | return b, nil
359 | }
360 |
361 | func (h *sha224Hash) UnmarshalBinary(b []byte) error {
362 | if len(b) < len(magic224) || string(b[:len(magic224)]) != magic224 {
363 | return errors.New("crypto/sha256: invalid hash state identifier")
364 | }
365 | if len(b) != marshaledSize256 {
366 | return errors.New("crypto/sha256: invalid hash state size")
367 | }
368 | d := (*sha256State)(h.shaState())
369 | if d == nil {
370 | return errors.New("crypto/sha256: can't retrieve hash state")
371 | }
372 | b = b[len(magic224):]
373 | b, d.h[0] = consumeUint32(b)
374 | b, d.h[1] = consumeUint32(b)
375 | b, d.h[2] = consumeUint32(b)
376 | b, d.h[3] = consumeUint32(b)
377 | b, d.h[4] = consumeUint32(b)
378 | b, d.h[5] = consumeUint32(b)
379 | b, d.h[6] = consumeUint32(b)
380 | b, d.h[7] = consumeUint32(b)
381 | b = b[copy(d.x[:], b):]
382 | _, n := consumeUint64(b)
383 | d.nl = uint32(n << 3)
384 | d.nh = uint32(n >> 29)
385 | d.nx = uint32(n) % 64
386 | return nil
387 | }
388 |
389 | func (h *sha256Hash) UnmarshalBinary(b []byte) error {
390 | if len(b) < len(magic256) || string(b[:len(magic256)]) != magic256 {
391 | return errors.New("crypto/sha256: invalid hash state identifier")
392 | }
393 | if len(b) != marshaledSize256 {
394 | return errors.New("crypto/sha256: invalid hash state size")
395 | }
396 | d := (*sha256State)(h.shaState())
397 | if d == nil {
398 | return errors.New("crypto/sha256: can't retrieve hash state")
399 | }
400 | b = b[len(magic256):]
401 | b, d.h[0] = consumeUint32(b)
402 | b, d.h[1] = consumeUint32(b)
403 | b, d.h[2] = consumeUint32(b)
404 | b, d.h[3] = consumeUint32(b)
405 | b, d.h[4] = consumeUint32(b)
406 | b, d.h[5] = consumeUint32(b)
407 | b, d.h[6] = consumeUint32(b)
408 | b, d.h[7] = consumeUint32(b)
409 | b = b[copy(d.x[:], b):]
410 | _, n := consumeUint64(b)
411 | d.nl = uint32(n << 3)
412 | d.nh = uint32(n >> 29)
413 | d.nx = uint32(n) % 64
414 | return nil
415 | }
416 |
417 | // NewSHA384 returns a new SHA384 hash.
418 | func NewSHA384() hash.Hash {
419 | return &sha384Hash{
420 | evpHash: newEvpHash(crypto.SHA384, 384/8, 128),
421 | }
422 | }
423 |
424 | type sha384Hash struct {
425 | *evpHash
426 | out [384 / 8]byte
427 | }
428 |
429 | func (h *sha384Hash) Sum(in []byte) []byte {
430 | h.sum(h.out[:])
431 | return append(in, h.out[:]...)
432 | }
433 |
434 | // NewSHA512 returns a new SHA512 hash.
435 | func NewSHA512() hash.Hash {
436 | return &sha512Hash{
437 | evpHash: newEvpHash(crypto.SHA512, 512/8, 128),
438 | }
439 | }
440 |
441 | type sha512Hash struct {
442 | *evpHash
443 | out [512 / 8]byte
444 | }
445 |
446 | func (h *sha512Hash) Sum(in []byte) []byte {
447 | h.sum(h.out[:])
448 | return append(in, h.out[:]...)
449 | }
450 |
451 | // sha256State layout is taken from
452 | // https://github.com/openssl/openssl/blob/0418e993c717a6863f206feaa40673a261de7395/include/openssl/sha.h#L95.
453 | type sha512State struct {
454 | h [8]uint64
455 | nl, nh uint64
456 | x [128]byte
457 | nx uint32
458 | }
459 |
460 | const (
461 | magic384 = "sha\x04"
462 | magic512_224 = "sha\x05"
463 | magic512_256 = "sha\x06"
464 | magic512 = "sha\x07"
465 | marshaledSize512 = len(magic512) + 8*8 + 128 + 8
466 | )
467 |
468 | func (h *sha384Hash) MarshalBinary() ([]byte, error) {
469 | d := (*sha512State)(h.shaState())
470 | if d == nil {
471 | return nil, errors.New("crypto/sha512: can't retrieve hash state")
472 | }
473 | b := make([]byte, 0, marshaledSize512)
474 | b = append(b, magic384...)
475 | b = appendUint64(b, d.h[0])
476 | b = appendUint64(b, d.h[1])
477 | b = appendUint64(b, d.h[2])
478 | b = appendUint64(b, d.h[3])
479 | b = appendUint64(b, d.h[4])
480 | b = appendUint64(b, d.h[5])
481 | b = appendUint64(b, d.h[6])
482 | b = appendUint64(b, d.h[7])
483 | b = append(b, d.x[:d.nx]...)
484 | b = b[:len(b)+len(d.x)-int(d.nx)] // already zero
485 | b = appendUint64(b, d.nl>>3|d.nh<<61)
486 | return b, nil
487 | }
488 |
489 | func (h *sha512Hash) MarshalBinary() ([]byte, error) {
490 | d := (*sha512State)(h.shaState())
491 | if d == nil {
492 | return nil, errors.New("crypto/sha512: can't retrieve hash state")
493 | }
494 | b := make([]byte, 0, marshaledSize512)
495 | b = append(b, magic512...)
496 | b = appendUint64(b, d.h[0])
497 | b = appendUint64(b, d.h[1])
498 | b = appendUint64(b, d.h[2])
499 | b = appendUint64(b, d.h[3])
500 | b = appendUint64(b, d.h[4])
501 | b = appendUint64(b, d.h[5])
502 | b = appendUint64(b, d.h[6])
503 | b = appendUint64(b, d.h[7])
504 | b = append(b, d.x[:d.nx]...)
505 | b = b[:len(b)+len(d.x)-int(d.nx)] // already zero
506 | b = appendUint64(b, d.nl>>3|d.nh<<61)
507 | return b, nil
508 | }
509 |
510 | func (h *sha384Hash) UnmarshalBinary(b []byte) error {
511 | if len(b) < len(magic512) {
512 | return errors.New("crypto/sha512: invalid hash state identifier")
513 | }
514 | if string(b[:len(magic384)]) != magic384 {
515 | return errors.New("crypto/sha512: invalid hash state identifier")
516 | }
517 | if len(b) != marshaledSize512 {
518 | return errors.New("crypto/sha512: invalid hash state size")
519 | }
520 | d := (*sha512State)(h.shaState())
521 | if d == nil {
522 | return errors.New("crypto/sha512: can't retrieve hash state")
523 | }
524 | b = b[len(magic512):]
525 | b, d.h[0] = consumeUint64(b)
526 | b, d.h[1] = consumeUint64(b)
527 | b, d.h[2] = consumeUint64(b)
528 | b, d.h[3] = consumeUint64(b)
529 | b, d.h[4] = consumeUint64(b)
530 | b, d.h[5] = consumeUint64(b)
531 | b, d.h[6] = consumeUint64(b)
532 | b, d.h[7] = consumeUint64(b)
533 | b = b[copy(d.x[:], b):]
534 | _, n := consumeUint64(b)
535 | d.nl = n << 3
536 | d.nh = n >> 61
537 | d.nx = uint32(n) % 128
538 | return nil
539 | }
540 |
541 | func (h *sha512Hash) UnmarshalBinary(b []byte) error {
542 | if len(b) < len(magic512) {
543 | return errors.New("crypto/sha512: invalid hash state identifier")
544 | }
545 | if string(b[:len(magic512)]) != magic512 {
546 | return errors.New("crypto/sha512: invalid hash state identifier")
547 | }
548 | if len(b) != marshaledSize512 {
549 | return errors.New("crypto/sha512: invalid hash state size")
550 | }
551 | d := (*sha512State)(h.shaState())
552 | if d == nil {
553 | return errors.New("crypto/sha512: can't retrieve hash state")
554 | }
555 | b = b[len(magic512):]
556 | b, d.h[0] = consumeUint64(b)
557 | b, d.h[1] = consumeUint64(b)
558 | b, d.h[2] = consumeUint64(b)
559 | b, d.h[3] = consumeUint64(b)
560 | b, d.h[4] = consumeUint64(b)
561 | b, d.h[5] = consumeUint64(b)
562 | b, d.h[6] = consumeUint64(b)
563 | b, d.h[7] = consumeUint64(b)
564 | b = b[copy(d.x[:], b):]
565 | _, n := consumeUint64(b)
566 | d.nl = n << 3
567 | d.nh = n >> 61
568 | d.nx = uint32(n) % 128
569 | return nil
570 | }
571 |
572 | // appendUint64 appends x into b as a big endian byte sequence.
573 | func appendUint64(b []byte, x uint64) []byte {
574 | return append(b,
575 | byte(x>>56),
576 | byte(x>>48),
577 | byte(x>>40),
578 | byte(x>>32),
579 | byte(x>>24),
580 | byte(x>>16),
581 | byte(x>>8),
582 | byte(x),
583 | )
584 | }
585 |
586 | // appendUint32 appends x into b as a big endian byte sequence.
587 | func appendUint32(b []byte, x uint32) []byte {
588 | return append(b, byte(x>>24), byte(x>>16), byte(x>>8), byte(x))
589 | }
590 |
591 | // consumeUint64 reads a big endian uint64 number from b.
592 | func consumeUint64(b []byte) ([]byte, uint64) {
593 | _ = b[7]
594 | x := uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
595 | uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
596 | return b[8:], x
597 | }
598 |
599 | // consumeUint32 reads a big endian uint32 number from b.
600 | func consumeUint32(b []byte) ([]byte, uint32) {
601 | _ = b[3]
602 | x := uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
603 | return b[4:], x
604 | }
605 |
--------------------------------------------------------------------------------
/openssl/sha_test.go:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation.
2 | // Licensed under the MIT License.
3 |
4 | //go:build linux && !android
5 | // +build linux,!android
6 |
7 | package openssl
8 |
9 | import (
10 | "bytes"
11 | "encoding"
12 | "hash"
13 | "io"
14 | "testing"
15 | )
16 |
17 | func TestSha(t *testing.T) {
18 | msg := []byte("testig")
19 | var tests = []struct {
20 | name string
21 | fn func() hash.Hash
22 | }{
23 | {"sha1", NewSHA1},
24 | {"sha224", NewSHA224},
25 | {"sha256", NewSHA256},
26 | {"sha384", NewSHA384},
27 | {"sha512", NewSHA512},
28 | }
29 | for _, tt := range tests {
30 | t.Run(tt.name, func(t *testing.T) {
31 | t.Parallel()
32 | h := tt.fn()
33 | initSum := h.Sum(nil)
34 | n, err := h.Write(msg)
35 | if err != nil {
36 | t.Fatal(err)
37 | }
38 | if n != len(msg) {
39 | t.Errorf("got: %d, want: %d", n, len(msg))
40 | }
41 | sum := h.Sum(nil)
42 | if size := h.Size(); len(sum) != size {
43 | t.Errorf("got: %d, want: %d", len(sum), size)
44 | }
45 | if bytes.Equal(sum, initSum) {
46 | t.Error("Write didn't change internal hash state")
47 | }
48 |
49 | state, err := h.(encoding.BinaryMarshaler).MarshalBinary()
50 | if err != nil {
51 | t.Errorf("could not marshal: %v", err)
52 | }
53 | h2 := tt.fn()
54 | if err := h2.(encoding.BinaryUnmarshaler).UnmarshalBinary(state); err != nil {
55 | t.Errorf("could not unmarshal: %v", err)
56 | }
57 | if actual, actual2 := h.Sum(nil), h2.Sum(nil); !bytes.Equal(actual, actual2) {
58 | t.Errorf("0x%x != marshaled 0x%x", actual, actual2)
59 | }
60 |
61 | h.Reset()
62 | sum = h.Sum(nil)
63 | if !bytes.Equal(sum, initSum) {
64 | t.Errorf("got:%x want:%x", sum, initSum)
65 | }
66 |
67 | bw := h.(io.ByteWriter)
68 | for i := 0; i < len(msg); i++ {
69 | bw.WriteByte(msg[i])
70 | }
71 | h.Reset()
72 | sum = h.Sum(nil)
73 | if !bytes.Equal(sum, initSum) {
74 | t.Errorf("got:%x want:%x", sum, initSum)
75 | }
76 |
77 | h.(io.StringWriter).WriteString(string(msg))
78 | h.Reset()
79 | sum = h.Sum(nil)
80 | if !bytes.Equal(sum, initSum) {
81 | t.Errorf("got:%x want:%x", sum, initSum)
82 | }
83 | })
84 | }
85 | }
86 |
87 | func TestSHA_OneShot(t *testing.T) {
88 | msg := []byte("testing")
89 | var tests = []struct {
90 | name string
91 | want func() hash.Hash
92 | oneShot func([]byte) []byte
93 | }{
94 | {"sha1", NewSHA1, func(p []byte) []byte {
95 | b := SHA1(p)
96 | return b[:]
97 | }},
98 | {"sha224", NewSHA224, func(p []byte) []byte {
99 | b := SHA224(p)
100 | return b[:]
101 | }},
102 | {"sha256", NewSHA256, func(p []byte) []byte {
103 | b := SHA256(p)
104 | return b[:]
105 | }},
106 | {"sha384", NewSHA384, func(p []byte) []byte {
107 | b := SHA384(p)
108 | return b[:]
109 | }},
110 | {"sha512", NewSHA512, func(p []byte) []byte {
111 | b := SHA512(p)
112 | return b[:]
113 | }},
114 | }
115 | for _, tt := range tests {
116 | t.Run(tt.name, func(t *testing.T) {
117 | got := tt.oneShot(msg)
118 | h := tt.want()
119 | h.Write(msg)
120 | want := h.Sum(nil)
121 | if !bytes.Equal(got[:], want) {
122 | t.Errorf("got:%x want:%x", got, want)
123 | }
124 | })
125 | }
126 | }
127 |
128 | type cgoData struct {
129 | Data [16]byte
130 | Ptr *cgoData
131 | }
132 |
133 | func TestCgo(t *testing.T) {
134 | // Test that Write does not cause cgo to scan the entire cgoData struct for pointers.
135 | // The scan (if any) should be limited to the [16]byte.
136 | defer func() {
137 | if err := recover(); err != nil {
138 | t.Error(err)
139 | }
140 | }()
141 | d := new(cgoData)
142 | d.Ptr = d
143 | h := NewSHA256()
144 | h.Write(d.Data[:])
145 | h.Sum(nil)
146 |
147 | SHA256(d.Data[:])
148 | }
149 |
150 | func BenchmarkHash8Bytes(b *testing.B) {
151 | b.StopTimer()
152 | h := NewSHA256()
153 | sum := make([]byte, h.Size())
154 | size := 8
155 | buf := make([]byte, size)
156 | b.StartTimer()
157 | b.SetBytes(int64(size))
158 | b.ReportAllocs()
159 | for i := 0; i < b.N; i++ {
160 | h.Reset()
161 | h.Write(buf)
162 | h.Sum(sum[:0])
163 | }
164 | }
165 |
166 | func BenchmarkSHA256(b *testing.B) {
167 | b.StopTimer()
168 | size := 8
169 | buf := make([]byte, size)
170 | b.StartTimer()
171 | b.SetBytes(int64(size))
172 | b.ReportAllocs()
173 | for i := 0; i < b.N; i++ {
174 | SHA256(buf)
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/scripts/openssl-3.cnf:
--------------------------------------------------------------------------------
1 | # Source: https://www.openssl.org/docs/man3.0/man7/fips_module.html
2 |
3 | config_diagnostics = 1
4 | openssl_conf = openssl_init
5 |
6 | .include /usr/local/ssl/fipsmodule.cnf
7 |
8 | [openssl_init]
9 | providers = provider_sect
10 | alg_section = algorithm_sect
11 |
12 | [provider_sect]
13 | fips = fips_sect
14 | default = default_sect
15 |
16 | [default_sect]
17 | activate = 1
18 |
19 | [algorithm_sect]
20 | default_properties = fips=yes
--------------------------------------------------------------------------------
/scripts/openssl.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # Copyright (c) Microsoft Corporation.
4 | # Licensed under the MIT License.
5 |
6 | set -eux
7 |
8 | version=$1
9 |
10 | case "$version" in
11 | "1.0.2")
12 | tag="OpenSSL_1_0_2u"
13 | sha256="82fa58e3f273c53128c6fe7e3635ec8cda1319a10ce1ad50a987c3df0deeef05"
14 | config="shared"
15 | make="build_libs"
16 | install=""
17 | ;;
18 | "1.1.0")
19 | tag="OpenSSL_1_1_0l"
20 | sha256="e2acf0cf58d9bff2b42f2dc0aee79340c8ffe2c5e45d3ca4533dd5d4f5775b1d"
21 | config="shared"
22 | make="build_libs"
23 | install=""
24 | ;;
25 | "1.1.1")
26 | tag="OpenSSL_1_1_1m"
27 | sha256="36ae24ad7cf0a824d0b76ac08861262e47ec541e5d0f20e6d94bab90b2dab360"
28 | config="shared"
29 | make="build_libs"
30 | install=""
31 | ;;
32 | "3.0.1")
33 | tag="openssl-3.0.1";
34 | sha256="2a9dcf05531e8be96c296259e817edc41619017a4bf3e229b4618a70103251d5"
35 | config="enable-fips"
36 | make="build_libs"
37 | install="install_fips"
38 | ;;
39 | *)
40 | echo >&2 "error: unsupported OpenSSL version '$version'"
41 | exit 1 ;;
42 | esac
43 |
44 | cd /usr/local/src
45 | wget -O "$tag.tar.gz" "https://github.com/openssl/openssl/archive/refs/tags/$tag.tar.gz"
46 | echo "$sha256 $tag.tar.gz" | sha256sum -c -
47 | rm -rf "openssl-$tag"
48 | tar -xzf "$tag.tar.gz"
49 |
50 | rm -rf "openssl-$version"
51 | mv "openssl-$tag" "openssl-$version"
52 |
53 | cd "openssl-$version"
54 | ./config $config
55 | make -j$(nproc) $make
56 | if [ -n "$install" ]; then
57 | make $install
58 | fi
59 |
60 | cp -H ./libcrypto.so "/usr/lib/libcrypto.so.${version}"
61 |
--------------------------------------------------------------------------------