├── .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 | [![Go Reference](https://pkg.go.dev/badge/github.com/microsoft/go-crypto-openssl/openssl.svg)](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 | --------------------------------------------------------------------------------