├── .github ├── actions │ └── go-test-setup │ │ └── action.yml └── workflows │ ├── automerge.yml │ ├── go-check.yml │ ├── go-test-ubuntu-22.04.yml │ ├── go-test.yml │ ├── release-check.yml │ ├── releaser.yml │ ├── stale.yml │ └── tagpush.yml ├── .gitignore ├── AUTHORS ├── LICENSE ├── README.md ├── alloc.go ├── bio.go ├── build.go ├── build_static.go ├── cert.go ├── cert_test.go ├── ciphers.go ├── ciphers_gcm.go ├── ciphers_test.go ├── conn.go ├── ctx.go ├── ctx_test.go ├── dh.go ├── dh_test.go ├── dhparam.go ├── digest.go ├── engine.go ├── extension.c ├── fips.go ├── go.mod ├── go.sum ├── hmac.go ├── hmac_test.go ├── hostname.c ├── hostname.go ├── http.go ├── init.go ├── init_posix.go ├── init_windows.go ├── key.go ├── key_test.go ├── mapping.go ├── md4.go ├── md4_test.go ├── md5.go ├── md5_test.go ├── net.go ├── nid.go ├── object.go ├── pem.go ├── sha1.go ├── sha1_test.go ├── sha256.go ├── sha256_test.go ├── shim.c ├── shim.h ├── sni.c ├── ssl.go ├── ssl_test.go ├── tickets.go ├── utils ├── errors.go └── future.go └── version.json /.github/actions/go-test-setup/action.yml: -------------------------------------------------------------------------------- 1 | name: Go Test Setup 2 | description: Set up the environment for go test 3 | runs: 4 | using: "composite" 5 | steps: 6 | - name: Common setup 7 | shell: bash 8 | run: | 9 | echo 'CGO_ENABLED=1' >> $GITHUB_ENV 10 | - name: Windows setup 11 | shell: bash 12 | if: ${{ runner.os == 'Windows' }} 13 | run: | 14 | pacman -S --noconfirm mingw-w64-x86_64-toolchain mingw-w64-i686-toolchain 15 | echo '/c/msys64/mingw64/bin' >> $GITHUB_PATH 16 | echo 'PATH_386=/c/msys64/mingw32/bin:${{ env.PATH_386 }}' >> $GITHUB_ENV 17 | - name: Linux setup 18 | shell: bash 19 | if: ${{ runner.os == 'Linux' }} 20 | run: | 21 | sudo apt-get install gcc-multilib 22 | sudo dpkg --add-architecture i386 23 | sudo apt-get update 24 | sudo apt-get install libssl-dev:i386 25 | echo 'CC_FOR_linux_386=i686-w64-mingw32-gcc' 26 | -------------------------------------------------------------------------------- /.github/workflows/automerge.yml: -------------------------------------------------------------------------------- 1 | # File managed by web3-bot. DO NOT EDIT. 2 | # See https://github.com/protocol/.github/ for details. 3 | 4 | name: Automerge 5 | on: [ pull_request ] 6 | 7 | jobs: 8 | automerge: 9 | uses: protocol/.github/.github/workflows/automerge.yml@master 10 | with: 11 | job: 'automerge' 12 | -------------------------------------------------------------------------------- /.github/workflows/go-check.yml: -------------------------------------------------------------------------------- 1 | # File managed by web3-bot. DO NOT EDIT. 2 | # See https://github.com/protocol/.github/ for details. 3 | 4 | on: [push, pull_request] 5 | name: Go Checks 6 | 7 | jobs: 8 | unit: 9 | runs-on: ubuntu-latest 10 | name: All 11 | env: 12 | RUNGOGENERATE: false 13 | steps: 14 | - uses: actions/checkout@v3 15 | with: 16 | submodules: recursive 17 | - uses: actions/setup-go@v3 18 | with: 19 | go-version: "1.19.x" 20 | - name: Run repo-specific setup 21 | uses: ./.github/actions/go-check-setup 22 | if: hashFiles('./.github/actions/go-check-setup') != '' 23 | - name: Read config 24 | if: hashFiles('./.github/workflows/go-check-config.json') != '' 25 | run: | 26 | if jq -re .gogenerate ./.github/workflows/go-check-config.json; then 27 | echo "RUNGOGENERATE=true" >> $GITHUB_ENV 28 | fi 29 | - name: Install staticcheck 30 | run: go install honnef.co/go/tools/cmd/staticcheck@376210a89477dedbe6fdc4484b233998650d7b3c # 2022.1.3 (v0.3.3) 31 | - name: Check that go.mod is tidy 32 | uses: protocol/multiple-go-modules@v1.2 33 | with: 34 | run: | 35 | go mod tidy 36 | if [[ -n $(git ls-files --other --exclude-standard --directory -- go.sum) ]]; then 37 | echo "go.sum was added by go mod tidy" 38 | exit 1 39 | fi 40 | git diff --exit-code -- go.sum go.mod 41 | - name: gofmt 42 | if: ${{ success() || failure() }} # run this step even if the previous one failed 43 | run: | 44 | out=$(gofmt -s -l .) 45 | if [[ -n "$out" ]]; then 46 | echo $out | awk '{print "::error file=" $0 ",line=0,col=0::File is not gofmt-ed."}' 47 | exit 1 48 | fi 49 | - name: go vet 50 | if: ${{ success() || failure() }} # run this step even if the previous one failed 51 | uses: protocol/multiple-go-modules@v1.2 52 | with: 53 | run: go vet ./... 54 | - name: staticcheck 55 | if: ${{ success() || failure() }} # run this step even if the previous one failed 56 | uses: protocol/multiple-go-modules@v1.2 57 | with: 58 | run: | 59 | set -o pipefail 60 | staticcheck ./... | sed -e 's@\(.*\)\.go@./\1.go@g' 61 | - name: go generate 62 | uses: protocol/multiple-go-modules@v1.2 63 | if: (success() || failure()) && env.RUNGOGENERATE == 'true' 64 | with: 65 | run: | 66 | git clean -fd # make sure there aren't untracked files / directories 67 | go generate ./... 68 | # check if go generate modified or added any files 69 | if ! $(git add . && git diff-index HEAD --exit-code --quiet); then 70 | echo "go generated caused changes to the repository:" 71 | git status --short 72 | exit 1 73 | fi 74 | -------------------------------------------------------------------------------- /.github/workflows/go-test-ubuntu-22.04.yml: -------------------------------------------------------------------------------- 1 | # See: 2 | # https://github.com/libp2p/go-openssl/pull/25 3 | # https://github.com/protocol/.github/issues/349 4 | # for details. 5 | on: [push, pull_request] 6 | name: Go Test 7 | 8 | jobs: 9 | unit: 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | os: [ "ubuntu-22.04" ] 14 | go: [ "1.17.x", "1.18.x" ] 15 | env: 16 | COVERAGES: "" 17 | runs-on: ${{ matrix.os }} 18 | name: ${{ matrix.os }} (go ${{ matrix.go }}) 19 | steps: 20 | - uses: actions/checkout@v2 21 | with: 22 | submodules: recursive 23 | - uses: actions/setup-go@v2 24 | with: 25 | go-version: ${{ matrix.go }} 26 | - name: Go information 27 | run: | 28 | go version 29 | go env 30 | - name: Use msys2 on windows 31 | if: startsWith(matrix.os, 'windows') 32 | shell: bash 33 | # The executable for msys2 is also called bash.cmd 34 | # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md#shells 35 | # If we prepend its location to the PATH 36 | # subsequent 'shell: bash' steps will use msys2 instead of gitbash 37 | run: echo "C:/msys64/usr/bin" >> $GITHUB_PATH 38 | - name: Run repo-specific setup 39 | uses: ./.github/actions/go-test-setup 40 | if: hashFiles('./.github/actions/go-test-setup') != '' 41 | - name: Run tests 42 | uses: protocol/multiple-go-modules@v1.2 43 | with: 44 | # Use -coverpkg=./..., so that we include cross-package coverage. 45 | # If package ./A imports ./B, and ./A's tests also cover ./B, 46 | # this means ./B's coverage will be significantly higher than 0%. 47 | run: go test -v -coverprofile=module-coverage.txt -coverpkg=./... ./... 48 | - name: Run tests (32 bit) 49 | if: startsWith(matrix.os, 'macos') == false # can't run 32 bit tests on OSX. 50 | uses: protocol/multiple-go-modules@v1.2 51 | env: 52 | GOARCH: 386 53 | with: 54 | run: | 55 | export "PATH=${{ env.PATH_386 }}:$PATH" 56 | go test -v ./... 57 | - name: Run tests with race detector 58 | if: startsWith(matrix.os, 'ubuntu') # speed things up. Windows and OSX VMs are slow 59 | uses: protocol/multiple-go-modules@v1.2 60 | with: 61 | run: go test -v -race ./... 62 | - name: Collect coverage files 63 | shell: bash 64 | run: echo "COVERAGES=$(find . -type f -name 'module-coverage.txt' | tr -s '\n' ',' | sed 's/,$//')" >> $GITHUB_ENV 65 | - name: Upload coverage to Codecov 66 | uses: codecov/codecov-action@f32b3a3741e1053eb607407145bc9619351dc93b # v2.1.0 67 | with: 68 | files: '${{ env.COVERAGES }}' 69 | env_vars: OS=${{ matrix.os }}, GO=${{ matrix.go }} 70 | -------------------------------------------------------------------------------- /.github/workflows/go-test.yml: -------------------------------------------------------------------------------- 1 | # File managed by web3-bot. DO NOT EDIT. 2 | # See https://github.com/protocol/.github/ for details. 3 | 4 | on: [push, pull_request] 5 | name: Go Test 6 | 7 | jobs: 8 | unit: 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | os: [ "ubuntu", "windows", "macos" ] 13 | go: [ "1.18.x", "1.19.x" ] 14 | env: 15 | COVERAGES: "" 16 | runs-on: ${{ format('{0}-latest', matrix.os) }} 17 | name: ${{ matrix.os }} (go ${{ matrix.go }}) 18 | steps: 19 | - uses: actions/checkout@v3 20 | with: 21 | submodules: recursive 22 | - uses: actions/setup-go@v3 23 | with: 24 | go-version: ${{ matrix.go }} 25 | - name: Go information 26 | run: | 27 | go version 28 | go env 29 | - name: Use msys2 on windows 30 | if: ${{ matrix.os == 'windows' }} 31 | shell: bash 32 | # The executable for msys2 is also called bash.cmd 33 | # https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md#shells 34 | # If we prepend its location to the PATH 35 | # subsequent 'shell: bash' steps will use msys2 instead of gitbash 36 | run: echo "C:/msys64/usr/bin" >> $GITHUB_PATH 37 | - name: Run repo-specific setup 38 | uses: ./.github/actions/go-test-setup 39 | if: hashFiles('./.github/actions/go-test-setup') != '' 40 | - name: Run tests 41 | uses: protocol/multiple-go-modules@v1.2 42 | with: 43 | # Use -coverpkg=./..., so that we include cross-package coverage. 44 | # If package ./A imports ./B, and ./A's tests also cover ./B, 45 | # this means ./B's coverage will be significantly higher than 0%. 46 | run: go test -v -shuffle=on -coverprofile=module-coverage.txt -coverpkg=./... ./... 47 | - name: Run tests (32 bit) 48 | if: ${{ matrix.os != 'macos' }} # can't run 32 bit tests on OSX. 49 | uses: protocol/multiple-go-modules@v1.2 50 | env: 51 | GOARCH: 386 52 | with: 53 | run: | 54 | export "PATH=${{ env.PATH_386 }}:$PATH" 55 | go test -v -shuffle=on ./... 56 | - name: Run tests with race detector 57 | if: ${{ matrix.os == 'ubuntu' }} # speed things up. Windows and OSX VMs are slow 58 | uses: protocol/multiple-go-modules@v1.2 59 | with: 60 | run: go test -v -race ./... 61 | - name: Collect coverage files 62 | shell: bash 63 | run: echo "COVERAGES=$(find . -type f -name 'module-coverage.txt' | tr -s '\n' ',' | sed 's/,$//')" >> $GITHUB_ENV 64 | - name: Upload coverage to Codecov 65 | uses: codecov/codecov-action@81cd2dc8148241f03f5839d295e000b8f761e378 # v3.1.0 66 | with: 67 | files: '${{ env.COVERAGES }}' 68 | env_vars: OS=${{ matrix.os }}, GO=${{ matrix.go }} 69 | -------------------------------------------------------------------------------- /.github/workflows/release-check.yml: -------------------------------------------------------------------------------- 1 | # File managed by web3-bot. DO NOT EDIT. 2 | # See https://github.com/protocol/.github/ for details. 3 | 4 | name: Release Checker 5 | on: 6 | pull_request: 7 | paths: [ 'version.json' ] 8 | 9 | jobs: 10 | release-check: 11 | uses: protocol/.github/.github/workflows/release-check.yml@master 12 | -------------------------------------------------------------------------------- /.github/workflows/releaser.yml: -------------------------------------------------------------------------------- 1 | # File managed by web3-bot. DO NOT EDIT. 2 | # See https://github.com/protocol/.github/ for details. 3 | 4 | name: Releaser 5 | on: 6 | push: 7 | paths: [ 'version.json' ] 8 | 9 | jobs: 10 | releaser: 11 | uses: protocol/.github/.github/workflows/releaser.yml@master 12 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Close and mark stale issue 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | 7 | jobs: 8 | stale: 9 | 10 | runs-on: ubuntu-latest 11 | permissions: 12 | issues: write 13 | pull-requests: write 14 | 15 | steps: 16 | - uses: actions/stale@v3 17 | with: 18 | repo-token: ${{ secrets.GITHUB_TOKEN }} 19 | stale-issue-message: 'Oops, seems like we needed more information for this issue, please comment with more details or this issue will be closed in 7 days.' 20 | close-issue-message: 'This issue was closed because it is missing author input.' 21 | stale-issue-label: 'kind/stale' 22 | any-of-labels: 'need/author-input' 23 | exempt-issue-labels: 'need/triage,need/community-input,need/maintainer-input,need/maintainers-input,need/analysis,status/blocked,status/in-progress,status/ready,status/deferred,status/inactive' 24 | days-before-issue-stale: 6 25 | days-before-issue-close: 7 26 | enable-statistics: true 27 | -------------------------------------------------------------------------------- /.github/workflows/tagpush.yml: -------------------------------------------------------------------------------- 1 | # File managed by web3-bot. DO NOT EDIT. 2 | # See https://github.com/protocol/.github/ for details. 3 | 4 | name: Tag Push Checker 5 | on: 6 | push: 7 | tags: 8 | - v* 9 | 10 | jobs: 11 | releaser: 12 | uses: protocol/.github/.github/workflows/tagpush.yml@master 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | openssl.test 2 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Andrew Brampton 2 | Anton Baklanov 3 | Carlos Martín Nieto 4 | Charles Strahan 5 | Christopher Dudley 6 | Christopher Fredericks 7 | Colin Misare 8 | dequis 9 | Gabriel Russell 10 | Giulio 11 | Jakob Unterwurzacher 12 | Juuso Haavisto 13 | kujenga 14 | Phus Lu 15 | Russ Egan 16 | Ryan Hileman 17 | Scott J. Goldman 18 | Scott Kidder 19 | Space Monkey, Inc 20 | Stephen Gallagher 21 | Viacheslav Biriukov 22 | Zack Owens 23 | Ramesh Rayaprolu 24 | Paras Shah 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and 10 | distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 13 | owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities 16 | that control, are controlled by, or are under common control with that entity. 17 | For the purposes of this definition, "control" means (i) the power, direct or 18 | indirect, to cause the direction or management of such entity, whether by 19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 20 | outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising 23 | permissions granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, including 26 | but not limited to software source code, documentation source, and configuration 27 | files. 28 | 29 | "Object" form shall mean any form resulting from mechanical transformation or 30 | translation of a Source form, including but not limited to compiled object code, 31 | generated documentation, and conversions to other media types. 32 | 33 | "Work" shall mean the work of authorship, whether in Source or Object form, made 34 | available under the License, as indicated by a copyright notice that is included 35 | in or attached to the work (an example is provided in the Appendix below). 36 | 37 | "Derivative Works" shall mean any work, whether in Source or Object form, that 38 | is based on (or derived from) the Work and for which the editorial revisions, 39 | annotations, elaborations, or other modifications represent, as a whole, an 40 | original work of authorship. For the purposes of this License, Derivative Works 41 | shall not include works that remain separable from, or merely link (or bind by 42 | name) to the interfaces of, the Work and Derivative Works thereof. 43 | 44 | "Contribution" shall mean any work of authorship, including the original version 45 | of the Work and any modifications or additions to that Work or Derivative Works 46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 47 | by the copyright owner or by an individual or Legal Entity authorized to submit 48 | on behalf of the copyright owner. For the purposes of this definition, 49 | "submitted" means any form of electronic, verbal, or written communication sent 50 | to the Licensor or its representatives, including but not limited to 51 | communication on electronic mailing lists, source code control systems, and 52 | issue tracking systems that are managed by, or on behalf of, the Licensor for 53 | the purpose of discussing and improving the Work, but excluding communication 54 | that is conspicuously marked or otherwise designated in writing by the copyright 55 | owner as "Not a Contribution." 56 | 57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 58 | of whom a Contribution has been received by Licensor and subsequently 59 | incorporated within the Work. 60 | 61 | 2. Grant of Copyright License. 62 | 63 | Subject to the terms and conditions of this License, each Contributor hereby 64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 65 | irrevocable copyright license to reproduce, prepare Derivative Works of, 66 | publicly display, publicly perform, sublicense, and distribute the Work and such 67 | Derivative Works in Source or Object form. 68 | 69 | 3. Grant of Patent License. 70 | 71 | Subject to the terms and conditions of this License, each Contributor hereby 72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 73 | irrevocable (except as stated in this section) patent license to make, have 74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 75 | such license applies only to those patent claims licensable by such Contributor 76 | that are necessarily infringed by their Contribution(s) alone or by combination 77 | of their Contribution(s) with the Work to which such Contribution(s) was 78 | submitted. If You institute patent litigation against any entity (including a 79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 80 | Contribution incorporated within the Work constitutes direct or contributory 81 | patent infringement, then any patent licenses granted to You under this License 82 | for that Work shall terminate as of the date such litigation is filed. 83 | 84 | 4. Redistribution. 85 | 86 | You may reproduce and distribute copies of the Work or Derivative Works thereof 87 | in any medium, with or without modifications, and in Source or Object form, 88 | provided that You meet the following conditions: 89 | 90 | You must give any other recipients of the Work or Derivative Works a copy of 91 | this License; and 92 | You must cause any modified files to carry prominent notices stating that You 93 | changed the files; and 94 | You must retain, in the Source form of any Derivative Works that You distribute, 95 | all copyright, patent, trademark, and attribution notices from the Source form 96 | of the Work, excluding those notices that do not pertain to any part of the 97 | Derivative Works; and 98 | If the Work includes a "NOTICE" text file as part of its distribution, then any 99 | Derivative Works that You distribute must include a readable copy of the 100 | attribution notices contained within such NOTICE file, excluding those notices 101 | that do not pertain to any part of the Derivative Works, in at least one of the 102 | following places: within a NOTICE text file distributed as part of the 103 | Derivative Works; within the Source form or documentation, if provided along 104 | with the Derivative Works; or, within a display generated by the Derivative 105 | Works, if and wherever such third-party notices normally appear. The contents of 106 | the NOTICE file are for informational purposes only and do not modify the 107 | License. You may add Your own attribution notices within Derivative Works that 108 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 109 | provided that such additional attribution notices cannot be construed as 110 | modifying the License. 111 | You may add Your own copyright statement to Your modifications and may provide 112 | additional or different license terms and conditions for use, reproduction, or 113 | distribution of Your modifications, or for any such Derivative Works as a whole, 114 | provided Your use, reproduction, and distribution of the Work otherwise complies 115 | with the conditions stated in this License. 116 | 117 | 5. Submission of Contributions. 118 | 119 | Unless You explicitly state otherwise, any Contribution intentionally submitted 120 | for inclusion in the Work by You to the Licensor shall be under the terms and 121 | conditions of this License, without any additional terms or conditions. 122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 123 | any separate license agreement you may have executed with Licensor regarding 124 | such Contributions. 125 | 126 | 6. Trademarks. 127 | 128 | This License does not grant permission to use the trade names, trademarks, 129 | service marks, or product names of the Licensor, except as required for 130 | reasonable and customary use in describing the origin of the Work and 131 | reproducing the content of the NOTICE file. 132 | 133 | 7. Disclaimer of Warranty. 134 | 135 | Unless required by applicable law or agreed to in writing, Licensor provides the 136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 138 | including, without limitation, any warranties or conditions of TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 140 | solely responsible for determining the appropriateness of using or 141 | redistributing the Work and assume any risks associated with Your exercise of 142 | permissions under this License. 143 | 144 | 8. Limitation of Liability. 145 | 146 | In no event and under no legal theory, whether in tort (including negligence), 147 | contract, or otherwise, unless required by applicable law (such as deliberate 148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 149 | liable to You for damages, including any direct, indirect, special, incidental, 150 | or consequential damages of any character arising as a result of this License or 151 | out of the use or inability to use the Work (including but not limited to 152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 153 | any and all other commercial damages or losses), even if such Contributor has 154 | been advised of the possibility of such damages. 155 | 156 | 9. Accepting Warranty or Additional Liability. 157 | 158 | While redistributing the Work or Derivative Works thereof, You may choose to 159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 160 | other liability obligations and/or rights consistent with this License. However, 161 | in accepting such obligations, You may act only on Your own behalf and on Your 162 | sole responsibility, not on behalf of any other Contributor, and only if You 163 | agree to indemnify, defend, and hold each Contributor harmless for any liability 164 | incurred by, or claims asserted against, such Contributor by reason of your 165 | accepting any such warranty or additional liability. 166 | 167 | END OF TERMS AND CONDITIONS 168 | 169 | APPENDIX: How to apply the Apache License to your work 170 | 171 | To apply the Apache License to your work, attach the following boilerplate 172 | notice, with the fields enclosed by brackets "[]" replaced with your own 173 | identifying information. (Don't include the brackets!) The text should be 174 | enclosed in the appropriate comment syntax for the file format. We also 175 | recommend that a file or class name and description of purpose be included on 176 | the same "printed page" as the copyright notice for easier identification within 177 | third-party archives. 178 | 179 | Copyright [yyyy] [name of copyright owner] 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ⚠️⚠️⚠️ 2 | 3 | **Status:** Archived and not maintained. Please see https://github.com/libp2p/go-openssl/issues/41 for details. 4 | 5 | ⚠️⚠️⚠️ 6 | 7 | # OpenSSL bindings for Go 8 | 9 | Forked from https://github.com/spacemonkeygo/openssl (unmaintained) to add: 10 | 11 | 1. FreeBSD support. 12 | 2. Key equality checking. 13 | 3. A function to get the size of signatures produced by a key. 14 | 15 | --- 16 | 17 | Please see http://godoc.org/github.com/libp2p/go-openssl for more info 18 | 19 | --- 20 | 21 | ### License 22 | 23 | Copyright (C) 2017. See AUTHORS. 24 | 25 | Licensed under the Apache License, Version 2.0 (the "License"); 26 | you may not use this file except in compliance with the License. 27 | You may obtain a copy of the License at 28 | 29 | http://www.apache.org/licenses/LICENSE-2.0 30 | 31 | Unless required by applicable law or agreed to in writing, software 32 | distributed under the License is distributed on an "AS IS" BASIS, 33 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 34 | See the License for the specific language governing permissions and 35 | limitations under the License. 36 | 37 | ### Using on macOS 38 | 1. Install [homebrew](http://brew.sh/) 39 | 2. `$ brew install openssl` or `$ brew install openssl@1.1` 40 | 41 | ### Using on Windows 42 | 1. Install [mingw-w64](http://mingw-w64.sourceforge.net/) 43 | 2. Install [pkg-config-lite](http://sourceforge.net/projects/pkgconfiglite) 44 | 3. Build (or install precompiled) openssl for mingw32-w64 45 | 4. Set __PKG\_CONFIG\_PATH__ to the directory containing openssl.pc 46 | (i.e. c:\mingw64\mingw64\lib\pkgconfig) 47 | -------------------------------------------------------------------------------- /alloc.go: -------------------------------------------------------------------------------- 1 | package openssl 2 | 3 | // #include "shim.h" 4 | import "C" 5 | 6 | import ( 7 | "unsafe" 8 | 9 | "github.com/mattn/go-pointer" 10 | ) 11 | 12 | //export go_ssl_crypto_ex_free 13 | func go_ssl_crypto_ex_free( 14 | parent *C.void, ptr unsafe.Pointer, 15 | cryptoData *C.CRYPTO_EX_DATA, idx C.int, 16 | argl C.long, argp *C.void, 17 | ) { 18 | pointer.Unref(ptr) 19 | } 20 | -------------------------------------------------------------------------------- /bio.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | // #include "shim.h" 18 | import "C" 19 | 20 | import ( 21 | "errors" 22 | "io" 23 | "reflect" 24 | "sync" 25 | "unsafe" 26 | ) 27 | 28 | const ( 29 | SSLRecordSize = 16 * 1024 30 | ) 31 | 32 | func nonCopyGoBytes(ptr uintptr, length int) []byte { 33 | var slice []byte 34 | header := (*reflect.SliceHeader)(unsafe.Pointer(&slice)) 35 | header.Cap = length 36 | header.Len = length 37 | header.Data = ptr 38 | return slice 39 | } 40 | 41 | func nonCopyCString(data *C.char, size C.int) []byte { 42 | return nonCopyGoBytes(uintptr(unsafe.Pointer(data)), int(size)) 43 | } 44 | 45 | var writeBioMapping = newMapping() 46 | 47 | type writeBio struct { 48 | data_mtx sync.Mutex 49 | op_mtx sync.Mutex 50 | buf []byte 51 | release_buffers bool 52 | } 53 | 54 | func loadWritePtr(b *C.BIO) *writeBio { 55 | t := token(C.X_BIO_get_data(b)) 56 | return (*writeBio)(writeBioMapping.Get(t)) 57 | } 58 | 59 | func bioClearRetryFlags(b *C.BIO) { 60 | C.X_BIO_clear_flags(b, C.BIO_FLAGS_RWS|C.BIO_FLAGS_SHOULD_RETRY) 61 | } 62 | 63 | func bioSetRetryRead(b *C.BIO) { 64 | C.X_BIO_set_flags(b, C.BIO_FLAGS_READ|C.BIO_FLAGS_SHOULD_RETRY) 65 | } 66 | 67 | //export go_write_bio_write 68 | func go_write_bio_write(b *C.BIO, data *C.char, size C.int) (rc C.int) { 69 | defer func() { 70 | if err := recover(); err != nil { 71 | logger.Critf("openssl: writeBioWrite panic'd: %v", err) 72 | rc = -1 73 | } 74 | }() 75 | ptr := loadWritePtr(b) 76 | if ptr == nil || data == nil || size < 0 { 77 | return -1 78 | } 79 | ptr.data_mtx.Lock() 80 | defer ptr.data_mtx.Unlock() 81 | bioClearRetryFlags(b) 82 | ptr.buf = append(ptr.buf, nonCopyCString(data, size)...) 83 | return size 84 | } 85 | 86 | //export go_write_bio_ctrl 87 | func go_write_bio_ctrl(b *C.BIO, cmd C.int, arg1 C.long, arg2 unsafe.Pointer) ( 88 | rc C.long) { 89 | defer func() { 90 | if err := recover(); err != nil { 91 | logger.Critf("openssl: writeBioCtrl panic'd: %v", err) 92 | rc = -1 93 | } 94 | }() 95 | switch cmd { 96 | case C.BIO_CTRL_WPENDING: 97 | return writeBioPending(b) 98 | case C.BIO_CTRL_DUP, C.BIO_CTRL_FLUSH: 99 | return 1 100 | default: 101 | return 0 102 | } 103 | } 104 | 105 | func writeBioPending(b *C.BIO) C.long { 106 | ptr := loadWritePtr(b) 107 | if ptr == nil { 108 | return 0 109 | } 110 | ptr.data_mtx.Lock() 111 | defer ptr.data_mtx.Unlock() 112 | return C.long(len(ptr.buf)) 113 | } 114 | 115 | func (wb *writeBio) WriteTo(w io.Writer) (rv int64, err error) { 116 | wb.op_mtx.Lock() 117 | defer wb.op_mtx.Unlock() 118 | 119 | // write whatever data we currently have 120 | wb.data_mtx.Lock() 121 | data := wb.buf 122 | wb.data_mtx.Unlock() 123 | 124 | if len(data) == 0 { 125 | return 0, nil 126 | } 127 | n, err := w.Write(data) 128 | 129 | // subtract however much data we wrote from the buffer 130 | wb.data_mtx.Lock() 131 | wb.buf = wb.buf[:copy(wb.buf, wb.buf[n:])] 132 | if wb.release_buffers && len(wb.buf) == 0 { 133 | wb.buf = nil 134 | } 135 | wb.data_mtx.Unlock() 136 | 137 | return int64(n), err 138 | } 139 | 140 | func (wb *writeBio) Disconnect(b *C.BIO) { 141 | if loadWritePtr(b) == wb { 142 | writeBioMapping.Del(token(C.X_BIO_get_data(b))) 143 | C.X_BIO_set_data(b, nil) 144 | } 145 | } 146 | 147 | func (wb *writeBio) MakeCBIO() *C.BIO { 148 | rv := C.X_BIO_new_write_bio() 149 | token := writeBioMapping.Add(unsafe.Pointer(wb)) 150 | C.X_BIO_set_data(rv, unsafe.Pointer(token)) 151 | return rv 152 | } 153 | 154 | var readBioMapping = newMapping() 155 | 156 | type readBio struct { 157 | data_mtx sync.Mutex 158 | op_mtx sync.Mutex 159 | buf []byte 160 | eof bool 161 | release_buffers bool 162 | } 163 | 164 | func loadReadPtr(b *C.BIO) *readBio { 165 | return (*readBio)(readBioMapping.Get(token(C.X_BIO_get_data(b)))) 166 | } 167 | 168 | //export go_read_bio_read 169 | func go_read_bio_read(b *C.BIO, data *C.char, size C.int) (rc C.int) { 170 | defer func() { 171 | if err := recover(); err != nil { 172 | logger.Critf("openssl: go_read_bio_read panic'd: %v", err) 173 | rc = -1 174 | } 175 | }() 176 | ptr := loadReadPtr(b) 177 | if ptr == nil || size < 0 { 178 | return -1 179 | } 180 | ptr.data_mtx.Lock() 181 | defer ptr.data_mtx.Unlock() 182 | bioClearRetryFlags(b) 183 | if len(ptr.buf) == 0 { 184 | if ptr.eof { 185 | return 0 186 | } 187 | bioSetRetryRead(b) 188 | return -1 189 | } 190 | if size == 0 || data == nil { 191 | return C.int(len(ptr.buf)) 192 | } 193 | n := copy(nonCopyCString(data, size), ptr.buf) 194 | ptr.buf = ptr.buf[:copy(ptr.buf, ptr.buf[n:])] 195 | if ptr.release_buffers && len(ptr.buf) == 0 { 196 | ptr.buf = nil 197 | } 198 | return C.int(n) 199 | } 200 | 201 | //export go_read_bio_ctrl 202 | func go_read_bio_ctrl(b *C.BIO, cmd C.int, arg1 C.long, arg2 unsafe.Pointer) ( 203 | rc C.long) { 204 | 205 | defer func() { 206 | if err := recover(); err != nil { 207 | logger.Critf("openssl: readBioCtrl panic'd: %v", err) 208 | rc = -1 209 | } 210 | }() 211 | switch cmd { 212 | case C.BIO_CTRL_PENDING: 213 | return readBioPending(b) 214 | case C.BIO_CTRL_DUP, C.BIO_CTRL_FLUSH: 215 | return 1 216 | default: 217 | return 0 218 | } 219 | } 220 | 221 | func readBioPending(b *C.BIO) C.long { 222 | ptr := loadReadPtr(b) 223 | if ptr == nil { 224 | return 0 225 | } 226 | ptr.data_mtx.Lock() 227 | defer ptr.data_mtx.Unlock() 228 | return C.long(len(ptr.buf)) 229 | } 230 | 231 | func (rb *readBio) ReadFromOnce(r io.Reader) (n int, err error) { 232 | rb.op_mtx.Lock() 233 | defer rb.op_mtx.Unlock() 234 | 235 | // make sure we have a destination that fits at least one SSL record 236 | rb.data_mtx.Lock() 237 | if cap(rb.buf) < len(rb.buf)+SSLRecordSize { 238 | new_buf := make([]byte, len(rb.buf), len(rb.buf)+SSLRecordSize) 239 | copy(new_buf, rb.buf) 240 | rb.buf = new_buf 241 | } 242 | dst := rb.buf[len(rb.buf):cap(rb.buf)] 243 | dst_slice := rb.buf 244 | rb.data_mtx.Unlock() 245 | 246 | n, err = r.Read(dst) 247 | rb.data_mtx.Lock() 248 | defer rb.data_mtx.Unlock() 249 | if n > 0 { 250 | if len(dst_slice) != len(rb.buf) { 251 | // someone shrunk the buffer, so we read in too far ahead and we 252 | // need to slide backwards 253 | copy(rb.buf[len(rb.buf):len(rb.buf)+n], dst) 254 | } 255 | rb.buf = rb.buf[:len(rb.buf)+n] 256 | } 257 | return n, err 258 | } 259 | 260 | func (rb *readBio) MakeCBIO() *C.BIO { 261 | rv := C.X_BIO_new_read_bio() 262 | token := readBioMapping.Add(unsafe.Pointer(rb)) 263 | C.X_BIO_set_data(rv, unsafe.Pointer(token)) 264 | return rv 265 | } 266 | 267 | func (rb *readBio) Disconnect(b *C.BIO) { 268 | if loadReadPtr(b) == rb { 269 | readBioMapping.Del(token(C.X_BIO_get_data(b))) 270 | C.X_BIO_set_data(b, nil) 271 | } 272 | } 273 | 274 | func (rb *readBio) MarkEOF() { 275 | rb.data_mtx.Lock() 276 | defer rb.data_mtx.Unlock() 277 | rb.eof = true 278 | } 279 | 280 | type anyBio C.BIO 281 | 282 | func asAnyBio(b *C.BIO) *anyBio { return (*anyBio)(b) } 283 | 284 | func (b *anyBio) Read(buf []byte) (n int, err error) { 285 | if len(buf) == 0 { 286 | return 0, nil 287 | } 288 | n = int(C.X_BIO_read((*C.BIO)(b), unsafe.Pointer(&buf[0]), C.int(len(buf)))) 289 | if n <= 0 { 290 | return 0, io.EOF 291 | } 292 | return n, nil 293 | } 294 | 295 | func (b *anyBio) Write(buf []byte) (written int, err error) { 296 | if len(buf) == 0 { 297 | return 0, nil 298 | } 299 | n := int(C.X_BIO_write((*C.BIO)(b), unsafe.Pointer(&buf[0]), 300 | C.int(len(buf)))) 301 | if n != len(buf) { 302 | return n, errors.New("BIO write failed") 303 | } 304 | return n, nil 305 | } 306 | -------------------------------------------------------------------------------- /build.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build !openssl_static 16 | 17 | package openssl 18 | 19 | // #cgo linux windows freebsd openbsd solaris pkg-config: libssl libcrypto 20 | // #cgo linux freebsd openbsd solaris CFLAGS: -Wno-deprecated-declarations 21 | // #cgo darwin CFLAGS: -I/usr/local/opt/openssl@1.1/include -I/usr/local/opt/openssl/include -Wno-deprecated-declarations 22 | // #cgo darwin LDFLAGS: -L/usr/local/opt/openssl@1.1/lib -L/usr/local/opt/openssl/lib -lssl -lcrypto 23 | // #cgo windows CFLAGS: -DWIN32_LEAN_AND_MEAN 24 | import "C" 25 | -------------------------------------------------------------------------------- /build_static.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build openssl_static 16 | 17 | package openssl 18 | 19 | // #cgo linux windows freebsd openbsd solaris pkg-config: --static libssl libcrypto 20 | // #cgo linux freebsd openbsd solaris CFLAGS: -Wno-deprecated-declarations 21 | // #cgo darwin CFLAGS: -I/usr/local/opt/openssl@1.1/include -I/usr/local/opt/openssl/include -Wno-deprecated-declarations 22 | // #cgo darwin LDFLAGS: -L/usr/local/opt/openssl@1.1/lib -L/usr/local/opt/openssl/lib -lssl -lcrypto 23 | // #cgo windows CFLAGS: -DWIN32_LEAN_AND_MEAN 24 | import "C" 25 | -------------------------------------------------------------------------------- /cert.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | // #include "shim.h" 18 | import "C" 19 | 20 | import ( 21 | "errors" 22 | "io" 23 | "math/big" 24 | "runtime" 25 | "time" 26 | "unsafe" 27 | ) 28 | 29 | type EVP_MD int 30 | 31 | const ( 32 | EVP_NULL EVP_MD = iota 33 | EVP_MD5 EVP_MD = iota 34 | EVP_MD4 EVP_MD = iota 35 | EVP_SHA EVP_MD = iota 36 | EVP_SHA1 EVP_MD = iota 37 | EVP_DSS EVP_MD = iota 38 | EVP_DSS1 EVP_MD = iota 39 | EVP_MDC2 EVP_MD = iota 40 | EVP_RIPEMD160 EVP_MD = iota 41 | EVP_SHA224 EVP_MD = iota 42 | EVP_SHA256 EVP_MD = iota 43 | EVP_SHA384 EVP_MD = iota 44 | EVP_SHA512 EVP_MD = iota 45 | ) 46 | 47 | // X509_Version represents a version on an x509 certificate. 48 | type X509_Version int 49 | 50 | // Specify constants for x509 versions because the standard states that they 51 | // are represented internally as one lower than the common version name. 52 | const ( 53 | X509_V1 X509_Version = 0 54 | X509_V3 X509_Version = 2 55 | ) 56 | 57 | type Certificate struct { 58 | x *C.X509 59 | Issuer *Certificate 60 | ref interface{} 61 | pubKey PublicKey 62 | } 63 | 64 | type CertificateInfo struct { 65 | Serial *big.Int 66 | Issued time.Duration 67 | Expires time.Duration 68 | Country string 69 | Organization string 70 | CommonName string 71 | } 72 | 73 | type Name struct { 74 | name *C.X509_NAME 75 | } 76 | 77 | // Allocate and return a new Name object. 78 | func NewName() (*Name, error) { 79 | n := C.X509_NAME_new() 80 | if n == nil { 81 | return nil, errors.New("could not create x509 name") 82 | } 83 | name := &Name{name: n} 84 | runtime.SetFinalizer(name, func(n *Name) { 85 | C.X509_NAME_free(n.name) 86 | }) 87 | return name, nil 88 | } 89 | 90 | // AddTextEntry appends a text entry to an X509 NAME. 91 | func (n *Name) AddTextEntry(field, value string) error { 92 | cfield := C.CString(field) 93 | defer C.free(unsafe.Pointer(cfield)) 94 | cvalue := (*C.uchar)(unsafe.Pointer(C.CString(value))) 95 | defer C.free(unsafe.Pointer(cvalue)) 96 | ret := C.X509_NAME_add_entry_by_txt( 97 | n.name, cfield, C.MBSTRING_ASC, cvalue, -1, -1, 0) 98 | if ret != 1 { 99 | return errors.New("failed to add x509 name text entry") 100 | } 101 | return nil 102 | } 103 | 104 | // AddTextEntries allows adding multiple entries to a name in one call. 105 | func (n *Name) AddTextEntries(entries map[string]string) error { 106 | for f, v := range entries { 107 | if err := n.AddTextEntry(f, v); err != nil { 108 | return err 109 | } 110 | } 111 | return nil 112 | } 113 | 114 | // GetEntry returns a name entry based on NID. If no entry, then ("", false) is 115 | // returned. 116 | func (n *Name) GetEntry(nid NID) (entry string, ok bool) { 117 | entrylen := C.X509_NAME_get_text_by_NID(n.name, C.int(nid), nil, 0) 118 | if entrylen == -1 { 119 | return "", false 120 | } 121 | buf := (*C.char)(C.malloc(C.size_t(entrylen + 1))) 122 | defer C.free(unsafe.Pointer(buf)) 123 | C.X509_NAME_get_text_by_NID(n.name, C.int(nid), buf, entrylen+1) 124 | return C.GoStringN(buf, entrylen), true 125 | } 126 | 127 | // NewCertificate generates a basic certificate based 128 | // on the provided CertificateInfo struct 129 | func NewCertificate(info *CertificateInfo, key PublicKey) (*Certificate, error) { 130 | c := &Certificate{x: C.X509_new()} 131 | runtime.SetFinalizer(c, func(c *Certificate) { 132 | C.X509_free(c.x) 133 | }) 134 | 135 | name, err := c.GetSubjectName() 136 | if err != nil { 137 | return nil, err 138 | } 139 | err = name.AddTextEntries(map[string]string{ 140 | "C": info.Country, 141 | "O": info.Organization, 142 | "CN": info.CommonName, 143 | }) 144 | if err != nil { 145 | return nil, err 146 | } 147 | // self-issue for now 148 | if err := c.SetIssuerName(name); err != nil { 149 | return nil, err 150 | } 151 | if err := c.SetSerial(info.Serial); err != nil { 152 | return nil, err 153 | } 154 | if err := c.SetIssueDate(info.Issued); err != nil { 155 | return nil, err 156 | } 157 | if err := c.SetExpireDate(info.Expires); err != nil { 158 | return nil, err 159 | } 160 | if err := c.SetPubKey(key); err != nil { 161 | return nil, err 162 | } 163 | return c, nil 164 | } 165 | 166 | func (c *Certificate) GetSubjectName() (*Name, error) { 167 | n := C.X509_get_subject_name(c.x) 168 | if n == nil { 169 | return nil, errors.New("failed to get subject name") 170 | } 171 | return &Name{name: n}, nil 172 | } 173 | 174 | func (c *Certificate) GetIssuerName() (*Name, error) { 175 | n := C.X509_get_issuer_name(c.x) 176 | if n == nil { 177 | return nil, errors.New("failed to get issuer name") 178 | } 179 | return &Name{name: n}, nil 180 | } 181 | 182 | func (c *Certificate) SetSubjectName(name *Name) error { 183 | if C.X509_set_subject_name(c.x, name.name) != 1 { 184 | return errors.New("failed to set subject name") 185 | } 186 | return nil 187 | } 188 | 189 | // SetIssuer updates the stored Issuer cert 190 | // and the internal x509 Issuer Name of a certificate. 191 | // The stored Issuer reference is used when adding extensions. 192 | func (c *Certificate) SetIssuer(issuer *Certificate) error { 193 | name, err := issuer.GetSubjectName() 194 | if err != nil { 195 | return err 196 | } 197 | if err = c.SetIssuerName(name); err != nil { 198 | return err 199 | } 200 | c.Issuer = issuer 201 | return nil 202 | } 203 | 204 | // SetIssuerName populates the issuer name of a certificate. 205 | // Use SetIssuer instead, if possible. 206 | func (c *Certificate) SetIssuerName(name *Name) error { 207 | if C.X509_set_issuer_name(c.x, name.name) != 1 { 208 | return errors.New("failed to set subject name") 209 | } 210 | return nil 211 | } 212 | 213 | // SetSerial sets the serial of a certificate. 214 | func (c *Certificate) SetSerial(serial *big.Int) error { 215 | sno := C.ASN1_INTEGER_new() 216 | defer C.ASN1_INTEGER_free(sno) 217 | bn := C.BN_new() 218 | defer C.BN_free(bn) 219 | 220 | serialBytes := serial.Bytes() 221 | if bn = C.BN_bin2bn((*C.uchar)(unsafe.Pointer(&serialBytes[0])), C.int(len(serialBytes)), bn); bn == nil { 222 | return errors.New("failed to set serial") 223 | } 224 | if sno = C.BN_to_ASN1_INTEGER(bn, sno); sno == nil { 225 | return errors.New("failed to set serial") 226 | } 227 | if C.X509_set_serialNumber(c.x, sno) != 1 { 228 | return errors.New("failed to set serial") 229 | } 230 | return nil 231 | } 232 | 233 | // SetIssueDate sets the certificate issue date relative to the current time. 234 | func (c *Certificate) SetIssueDate(when time.Duration) error { 235 | offset := C.long(when / time.Second) 236 | result := C.X509_gmtime_adj(C.X_X509_get0_notBefore(c.x), offset) 237 | if result == nil { 238 | return errors.New("failed to set issue date") 239 | } 240 | return nil 241 | } 242 | 243 | // SetExpireDate sets the certificate issue date relative to the current time. 244 | func (c *Certificate) SetExpireDate(when time.Duration) error { 245 | offset := C.long(when / time.Second) 246 | result := C.X509_gmtime_adj(C.X_X509_get0_notAfter(c.x), offset) 247 | if result == nil { 248 | return errors.New("failed to set expire date") 249 | } 250 | return nil 251 | } 252 | 253 | // SetPubKey assigns a new public key to a certificate. 254 | func (c *Certificate) SetPubKey(pubKey PublicKey) error { 255 | c.pubKey = pubKey 256 | if C.X509_set_pubkey(c.x, pubKey.evpPKey()) != 1 { 257 | return errors.New("failed to set public key") 258 | } 259 | return nil 260 | } 261 | 262 | // Sign a certificate using a private key and a digest name. 263 | // Accepted digest names are 'sha256', 'sha384', and 'sha512'. 264 | func (c *Certificate) Sign(privKey PrivateKey, digest EVP_MD) error { 265 | switch digest { 266 | case EVP_SHA256: 267 | case EVP_SHA384: 268 | case EVP_SHA512: 269 | default: 270 | return errors.New("unsupported digest; " + 271 | "you're probably looking for 'EVP_SHA256' or 'EVP_SHA512'") 272 | } 273 | return c.insecureSign(privKey, digest) 274 | } 275 | 276 | func (c *Certificate) insecureSign(privKey PrivateKey, digest EVP_MD) error { 277 | var md *C.EVP_MD = getDigestFunction(digest) 278 | if C.X509_sign(c.x, privKey.evpPKey(), md) <= 0 { 279 | return errors.New("failed to sign certificate") 280 | } 281 | return nil 282 | } 283 | 284 | func getDigestFunction(digest EVP_MD) (md *C.EVP_MD) { 285 | switch digest { 286 | // please don't use these digest functions 287 | case EVP_NULL: 288 | md = C.X_EVP_md_null() 289 | case EVP_MD5: 290 | md = C.X_EVP_md5() 291 | case EVP_SHA: 292 | md = C.X_EVP_sha() 293 | case EVP_SHA1: 294 | md = C.X_EVP_sha1() 295 | case EVP_DSS: 296 | md = C.X_EVP_dss() 297 | case EVP_DSS1: 298 | md = C.X_EVP_dss1() 299 | case EVP_RIPEMD160: 300 | md = C.X_EVP_ripemd160() 301 | case EVP_SHA224: 302 | md = C.X_EVP_sha224() 303 | // you actually want one of these 304 | case EVP_SHA256: 305 | md = C.X_EVP_sha256() 306 | case EVP_SHA384: 307 | md = C.X_EVP_sha384() 308 | case EVP_SHA512: 309 | md = C.X_EVP_sha512() 310 | } 311 | return md 312 | } 313 | 314 | // Add an extension to a certificate. 315 | // Extension constants are NID_* as found in openssl. 316 | func (c *Certificate) AddExtension(nid NID, value string) error { 317 | issuer := c 318 | if c.Issuer != nil { 319 | issuer = c.Issuer 320 | } 321 | var ctx C.X509V3_CTX 322 | C.X509V3_set_ctx(&ctx, c.x, issuer.x, nil, nil, 0) 323 | ex := C.X509V3_EXT_conf_nid(nil, &ctx, C.int(nid), C.CString(value)) 324 | if ex == nil { 325 | return errors.New("failed to create x509v3 extension") 326 | } 327 | defer C.X509_EXTENSION_free(ex) 328 | if C.X509_add_ext(c.x, ex, -1) <= 0 { 329 | return errors.New("failed to add x509v3 extension") 330 | } 331 | return nil 332 | } 333 | 334 | // AddCustomExtension add custom extenstion to the certificate. 335 | func (c *Certificate) AddCustomExtension(nid NID, value []byte) error { 336 | val := (*C.char)(C.CBytes(value)) 337 | defer C.free(unsafe.Pointer(val)) 338 | if int(C.add_custom_ext(c.x, C.int(nid), val, C.int(len(value)))) == 0 { 339 | return errors.New("unable to add extension") 340 | } 341 | return nil 342 | } 343 | 344 | // Wraps AddExtension using a map of NID to text extension. 345 | // Will return without finishing if it encounters an error. 346 | func (c *Certificate) AddExtensions(extensions map[NID]string) error { 347 | for nid, value := range extensions { 348 | if err := c.AddExtension(nid, value); err != nil { 349 | return err 350 | } 351 | } 352 | return nil 353 | } 354 | 355 | // LoadCertificateFromPEM loads an X509 certificate from a PEM-encoded block. 356 | func LoadCertificateFromPEM(pem_block []byte) (*Certificate, error) { 357 | if len(pem_block) == 0 { 358 | return nil, errors.New("empty pem block") 359 | } 360 | runtime.LockOSThread() 361 | defer runtime.UnlockOSThread() 362 | bio := C.BIO_new_mem_buf(unsafe.Pointer(&pem_block[0]), 363 | C.int(len(pem_block))) 364 | cert := C.PEM_read_bio_X509(bio, nil, nil, nil) 365 | C.BIO_free(bio) 366 | if cert == nil { 367 | return nil, errorFromErrorQueue() 368 | } 369 | x := &Certificate{x: cert} 370 | runtime.SetFinalizer(x, func(x *Certificate) { 371 | C.X509_free(x.x) 372 | }) 373 | return x, nil 374 | } 375 | 376 | // MarshalPEM converts the X509 certificate to PEM-encoded format 377 | func (c *Certificate) MarshalPEM() (pem_block []byte, err error) { 378 | bio := C.BIO_new(C.BIO_s_mem()) 379 | if bio == nil { 380 | return nil, errors.New("failed to allocate memory BIO") 381 | } 382 | defer C.BIO_free(bio) 383 | if int(C.PEM_write_bio_X509(bio, c.x)) != 1 { 384 | return nil, errors.New("failed dumping certificate") 385 | } 386 | return io.ReadAll(asAnyBio(bio)) 387 | } 388 | 389 | // PublicKey returns the public key embedded in the X509 certificate. 390 | func (c *Certificate) PublicKey() (PublicKey, error) { 391 | pkey := C.X509_get_pubkey(c.x) 392 | if pkey == nil { 393 | return nil, errors.New("no public key found") 394 | } 395 | key := &pKey{key: pkey} 396 | runtime.SetFinalizer(key, func(key *pKey) { 397 | C.EVP_PKEY_free(key.key) 398 | }) 399 | return key, nil 400 | } 401 | 402 | // GetSerialNumberHex returns the certificate's serial number in hex format 403 | func (c *Certificate) GetSerialNumberHex() (serial string) { 404 | asn1_i := C.X509_get_serialNumber(c.x) 405 | bignum := C.ASN1_INTEGER_to_BN(asn1_i, nil) 406 | hex := C.BN_bn2hex(bignum) 407 | serial = C.GoString(hex) 408 | C.BN_free(bignum) 409 | C.X_OPENSSL_free(unsafe.Pointer(hex)) 410 | return 411 | } 412 | 413 | // GetVersion returns the X509 version of the certificate. 414 | func (c *Certificate) GetVersion() X509_Version { 415 | return X509_Version(C.X_X509_get_version(c.x)) 416 | } 417 | 418 | // SetVersion sets the X509 version of the certificate. 419 | func (c *Certificate) SetVersion(version X509_Version) error { 420 | cvers := C.long(version) 421 | if C.X_X509_set_version(c.x, cvers) != 1 { 422 | return errors.New("failed to set certificate version") 423 | } 424 | return nil 425 | } 426 | 427 | // GetExtensionValue returns the value of the given NID's extension. 428 | func (c *Certificate) GetExtensionValue(nid NID) []byte { 429 | dataLength := C.int(0) 430 | val := C.get_extention(c.x, C.int(nid), &dataLength) 431 | return C.GoBytes(unsafe.Pointer(val), dataLength) 432 | } 433 | -------------------------------------------------------------------------------- /cert_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | import ( 18 | "math/big" 19 | "testing" 20 | "time" 21 | ) 22 | 23 | func TestCertGenerate(t *testing.T) { 24 | key, err := GenerateRSAKey(768) 25 | if err != nil { 26 | t.Fatal(err) 27 | } 28 | info := &CertificateInfo{ 29 | Serial: big.NewInt(int64(1)), 30 | Issued: 0, 31 | Expires: 24 * time.Hour, 32 | Country: "US", 33 | Organization: "Test", 34 | CommonName: "localhost", 35 | } 36 | cert, err := NewCertificate(info, key) 37 | if err != nil { 38 | t.Fatal(err) 39 | } 40 | if err := cert.Sign(key, EVP_SHA256); err != nil { 41 | t.Fatal(err) 42 | } 43 | } 44 | 45 | func TestCAGenerate(t *testing.T) { 46 | cakey, err := GenerateRSAKey(768) 47 | if err != nil { 48 | t.Fatal(err) 49 | } 50 | info := &CertificateInfo{ 51 | Serial: big.NewInt(int64(1)), 52 | Issued: 0, 53 | Expires: 24 * time.Hour, 54 | Country: "US", 55 | Organization: "Test CA", 56 | CommonName: "CA", 57 | } 58 | ca, err := NewCertificate(info, cakey) 59 | if err != nil { 60 | t.Fatal(err) 61 | } 62 | if err := ca.AddExtensions(map[NID]string{ 63 | NID_basic_constraints: "critical,CA:TRUE", 64 | NID_key_usage: "critical,keyCertSign,cRLSign", 65 | NID_subject_key_identifier: "hash", 66 | NID_netscape_cert_type: "sslCA", 67 | }); err != nil { 68 | t.Fatal(err) 69 | } 70 | if err := ca.Sign(cakey, EVP_SHA256); err != nil { 71 | t.Fatal(err) 72 | } 73 | key, err := GenerateRSAKey(768) 74 | if err != nil { 75 | t.Fatal(err) 76 | } 77 | info = &CertificateInfo{ 78 | Serial: big.NewInt(int64(1)), 79 | Issued: 0, 80 | Expires: 24 * time.Hour, 81 | Country: "US", 82 | Organization: "Test", 83 | CommonName: "localhost", 84 | } 85 | cert, err := NewCertificate(info, key) 86 | if err != nil { 87 | t.Fatal(err) 88 | } 89 | if err := cert.AddExtensions(map[NID]string{ 90 | NID_basic_constraints: "critical,CA:FALSE", 91 | NID_key_usage: "keyEncipherment", 92 | NID_ext_key_usage: "serverAuth", 93 | }); err != nil { 94 | t.Fatal(err) 95 | } 96 | if err := cert.SetIssuer(ca); err != nil { 97 | t.Fatal(err) 98 | } 99 | if err := cert.Sign(cakey, EVP_SHA256); err != nil { 100 | t.Fatal(err) 101 | } 102 | } 103 | 104 | func TestCertGetNameEntry(t *testing.T) { 105 | key, err := GenerateRSAKey(768) 106 | if err != nil { 107 | t.Fatal(err) 108 | } 109 | info := &CertificateInfo{ 110 | Serial: big.NewInt(int64(1)), 111 | Issued: 0, 112 | Expires: 24 * time.Hour, 113 | Country: "US", 114 | Organization: "Test", 115 | CommonName: "localhost", 116 | } 117 | cert, err := NewCertificate(info, key) 118 | if err != nil { 119 | t.Fatal(err) 120 | } 121 | name, err := cert.GetSubjectName() 122 | if err != nil { 123 | t.Fatal(err) 124 | } 125 | entry, ok := name.GetEntry(NID_commonName) 126 | if !ok { 127 | t.Fatal("no common name") 128 | } 129 | if entry != "localhost" { 130 | t.Fatalf("expected localhost; got %q", entry) 131 | } 132 | entry, ok = name.GetEntry(NID_localityName) 133 | if ok { 134 | t.Fatal("did not expect a locality name") 135 | } 136 | if entry != "" { 137 | t.Fatalf("entry should be empty; got %q", entry) 138 | } 139 | } 140 | 141 | func TestCertVersion(t *testing.T) { 142 | key, err := GenerateRSAKey(768) 143 | if err != nil { 144 | t.Fatal(err) 145 | } 146 | info := &CertificateInfo{ 147 | Serial: big.NewInt(int64(1)), 148 | Issued: 0, 149 | Expires: 24 * time.Hour, 150 | Country: "US", 151 | Organization: "Test", 152 | CommonName: "localhost", 153 | } 154 | cert, err := NewCertificate(info, key) 155 | if err != nil { 156 | t.Fatal(err) 157 | } 158 | if err := cert.SetVersion(X509_V3); err != nil { 159 | t.Fatal(err) 160 | } 161 | if vers := cert.GetVersion(); vers != X509_V3 { 162 | t.Fatalf("bad version: %d", vers) 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /ciphers.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | // #include "shim.h" 18 | import "C" 19 | 20 | import ( 21 | "errors" 22 | "fmt" 23 | "runtime" 24 | "unsafe" 25 | ) 26 | 27 | const ( 28 | GCM_TAG_MAXLEN = 16 29 | ) 30 | 31 | type CipherCtx interface { 32 | Cipher() *Cipher 33 | BlockSize() int 34 | KeySize() int 35 | IVSize() int 36 | } 37 | 38 | type Cipher struct { 39 | ptr *C.EVP_CIPHER 40 | } 41 | 42 | func (c *Cipher) Nid() NID { 43 | return NID(C.X_EVP_CIPHER_nid(c.ptr)) 44 | } 45 | 46 | func (c *Cipher) ShortName() (string, error) { 47 | return Nid2ShortName(c.Nid()) 48 | } 49 | 50 | func (c *Cipher) BlockSize() int { 51 | return int(C.X_EVP_CIPHER_block_size(c.ptr)) 52 | } 53 | 54 | func (c *Cipher) KeySize() int { 55 | return int(C.X_EVP_CIPHER_key_length(c.ptr)) 56 | } 57 | 58 | func (c *Cipher) IVSize() int { 59 | return int(C.X_EVP_CIPHER_iv_length(c.ptr)) 60 | } 61 | 62 | func Nid2ShortName(nid NID) (string, error) { 63 | sn := C.OBJ_nid2sn(C.int(nid)) 64 | if sn == nil { 65 | return "", fmt.Errorf("NID %d not found", nid) 66 | } 67 | return C.GoString(sn), nil 68 | } 69 | 70 | func GetCipherByName(name string) (*Cipher, error) { 71 | cname := C.CString(name) 72 | defer C.free(unsafe.Pointer(cname)) 73 | p := C.EVP_get_cipherbyname(cname) 74 | if p == nil { 75 | return nil, fmt.Errorf("Cipher %v not found", name) 76 | } 77 | // we can consider ciphers to use static mem; don't need to free 78 | return &Cipher{ptr: p}, nil 79 | } 80 | 81 | func GetCipherByNid(nid NID) (*Cipher, error) { 82 | sn, err := Nid2ShortName(nid) 83 | if err != nil { 84 | return nil, err 85 | } 86 | return GetCipherByName(sn) 87 | } 88 | 89 | type cipherCtx struct { 90 | ctx *C.EVP_CIPHER_CTX 91 | } 92 | 93 | func newCipherCtx() (*cipherCtx, error) { 94 | cctx := C.EVP_CIPHER_CTX_new() 95 | if cctx == nil { 96 | return nil, errors.New("failed to allocate cipher context") 97 | } 98 | ctx := &cipherCtx{cctx} 99 | runtime.SetFinalizer(ctx, func(ctx *cipherCtx) { 100 | C.EVP_CIPHER_CTX_free(ctx.ctx) 101 | }) 102 | return ctx, nil 103 | } 104 | 105 | func (ctx *cipherCtx) applyKeyAndIV(key, iv []byte) error { 106 | var kptr, iptr *C.uchar 107 | if key != nil { 108 | if len(key) != ctx.KeySize() { 109 | return fmt.Errorf("bad key size (%d bytes instead of %d)", 110 | len(key), ctx.KeySize()) 111 | } 112 | kptr = (*C.uchar)(&key[0]) 113 | } 114 | if iv != nil { 115 | if len(iv) != ctx.IVSize() { 116 | return fmt.Errorf("bad IV size (%d bytes instead of %d)", 117 | len(iv), ctx.IVSize()) 118 | } 119 | iptr = (*C.uchar)(&iv[0]) 120 | } 121 | if kptr != nil || iptr != nil { 122 | var res C.int 123 | if C.X_EVP_CIPHER_CTX_encrypting(ctx.ctx) != 0 { 124 | res = C.EVP_EncryptInit_ex(ctx.ctx, nil, nil, kptr, iptr) 125 | } else { 126 | res = C.EVP_DecryptInit_ex(ctx.ctx, nil, nil, kptr, iptr) 127 | } 128 | if res != 1 { 129 | return errors.New("failed to apply key/IV") 130 | } 131 | } 132 | return nil 133 | } 134 | 135 | func (ctx *cipherCtx) Cipher() *Cipher { 136 | return &Cipher{ptr: C.X_EVP_CIPHER_CTX_cipher(ctx.ctx)} 137 | } 138 | 139 | func (ctx *cipherCtx) BlockSize() int { 140 | return int(C.X_EVP_CIPHER_CTX_block_size(ctx.ctx)) 141 | } 142 | 143 | func (ctx *cipherCtx) KeySize() int { 144 | return int(C.X_EVP_CIPHER_CTX_key_length(ctx.ctx)) 145 | } 146 | 147 | func (ctx *cipherCtx) IVSize() int { 148 | return int(C.X_EVP_CIPHER_CTX_iv_length(ctx.ctx)) 149 | } 150 | 151 | func (ctx *cipherCtx) SetPadding(pad bool) { 152 | if pad { 153 | C.X_EVP_CIPHER_CTX_set_padding(ctx.ctx, 1) 154 | } else { 155 | C.X_EVP_CIPHER_CTX_set_padding(ctx.ctx, 0) 156 | } 157 | } 158 | 159 | func (ctx *cipherCtx) setCtrl(code, arg int) error { 160 | res := C.EVP_CIPHER_CTX_ctrl(ctx.ctx, C.int(code), C.int(arg), nil) 161 | if res != 1 { 162 | return fmt.Errorf("failed to set code %d to %d [result %d]", 163 | code, arg, res) 164 | } 165 | return nil 166 | } 167 | 168 | func (ctx *cipherCtx) setCtrlBytes(code, arg int, value []byte) error { 169 | res := C.EVP_CIPHER_CTX_ctrl(ctx.ctx, C.int(code), C.int(arg), 170 | unsafe.Pointer(&value[0])) 171 | if res != 1 { 172 | return fmt.Errorf("failed to set code %d with arg %d to %x [result %d]", 173 | code, arg, value, res) 174 | } 175 | return nil 176 | } 177 | 178 | func (ctx *cipherCtx) getCtrlInt(code, arg int) (int, error) { 179 | var returnVal C.int 180 | res := C.EVP_CIPHER_CTX_ctrl(ctx.ctx, C.int(code), C.int(arg), 181 | unsafe.Pointer(&returnVal)) 182 | if res != 1 { 183 | return 0, fmt.Errorf("failed to get code %d with arg %d [result %d]", 184 | code, arg, res) 185 | } 186 | return int(returnVal), nil 187 | } 188 | 189 | func (ctx *cipherCtx) getCtrlBytes(code, arg, expectsize int) ([]byte, error) { 190 | returnVal := make([]byte, expectsize) 191 | res := C.EVP_CIPHER_CTX_ctrl(ctx.ctx, C.int(code), C.int(arg), 192 | unsafe.Pointer(&returnVal[0])) 193 | if res != 1 { 194 | return nil, fmt.Errorf("failed to get code %d with arg %d [result %d]", 195 | code, arg, res) 196 | } 197 | return returnVal, nil 198 | } 199 | 200 | type EncryptionCipherCtx interface { 201 | CipherCtx 202 | 203 | // pass in plaintext, get back ciphertext. can be called 204 | // multiple times as needed 205 | EncryptUpdate(input []byte) ([]byte, error) 206 | 207 | // call after all plaintext has been passed in; may return 208 | // additional ciphertext if needed to finish off a block 209 | // or extra padding information 210 | EncryptFinal() ([]byte, error) 211 | } 212 | 213 | type DecryptionCipherCtx interface { 214 | CipherCtx 215 | 216 | // pass in ciphertext, get back plaintext. can be called 217 | // multiple times as needed 218 | DecryptUpdate(input []byte) ([]byte, error) 219 | 220 | // call after all ciphertext has been passed in; may return 221 | // additional plaintext if needed to finish off a block 222 | DecryptFinal() ([]byte, error) 223 | } 224 | 225 | type encryptionCipherCtx struct { 226 | *cipherCtx 227 | } 228 | 229 | type decryptionCipherCtx struct { 230 | *cipherCtx 231 | } 232 | 233 | func newEncryptionCipherCtx(c *Cipher, e *Engine, key, iv []byte) ( 234 | *encryptionCipherCtx, error) { 235 | if c == nil { 236 | return nil, errors.New("null cipher not allowed") 237 | } 238 | ctx, err := newCipherCtx() 239 | if err != nil { 240 | return nil, err 241 | } 242 | var eptr *C.ENGINE 243 | if e != nil { 244 | eptr = e.e 245 | } 246 | if C.EVP_EncryptInit_ex(ctx.ctx, c.ptr, eptr, nil, nil) != 1 { 247 | return nil, errors.New("failed to initialize cipher context") 248 | } 249 | err = ctx.applyKeyAndIV(key, iv) 250 | if err != nil { 251 | return nil, err 252 | } 253 | return &encryptionCipherCtx{cipherCtx: ctx}, nil 254 | } 255 | 256 | func newDecryptionCipherCtx(c *Cipher, e *Engine, key, iv []byte) ( 257 | *decryptionCipherCtx, error) { 258 | if c == nil { 259 | return nil, errors.New("null cipher not allowed") 260 | } 261 | ctx, err := newCipherCtx() 262 | if err != nil { 263 | return nil, err 264 | } 265 | var eptr *C.ENGINE 266 | if e != nil { 267 | eptr = e.e 268 | } 269 | if C.EVP_DecryptInit_ex(ctx.ctx, c.ptr, eptr, nil, nil) != 1 { 270 | return nil, errors.New("failed to initialize cipher context") 271 | } 272 | err = ctx.applyKeyAndIV(key, iv) 273 | if err != nil { 274 | return nil, err 275 | } 276 | return &decryptionCipherCtx{cipherCtx: ctx}, nil 277 | } 278 | 279 | func NewEncryptionCipherCtx(c *Cipher, e *Engine, key, iv []byte) ( 280 | EncryptionCipherCtx, error) { 281 | return newEncryptionCipherCtx(c, e, key, iv) 282 | } 283 | 284 | func NewDecryptionCipherCtx(c *Cipher, e *Engine, key, iv []byte) ( 285 | DecryptionCipherCtx, error) { 286 | return newDecryptionCipherCtx(c, e, key, iv) 287 | } 288 | 289 | func (ctx *encryptionCipherCtx) EncryptUpdate(input []byte) ([]byte, error) { 290 | if len(input) == 0 { 291 | return nil, nil 292 | } 293 | outbuf := make([]byte, len(input)+ctx.BlockSize()) 294 | outlen := C.int(len(outbuf)) 295 | res := C.EVP_EncryptUpdate(ctx.ctx, (*C.uchar)(&outbuf[0]), &outlen, 296 | (*C.uchar)(&input[0]), C.int(len(input))) 297 | if res != 1 { 298 | return nil, fmt.Errorf("failed to encrypt [result %d]", res) 299 | } 300 | return outbuf[:outlen], nil 301 | } 302 | 303 | func (ctx *decryptionCipherCtx) DecryptUpdate(input []byte) ([]byte, error) { 304 | if len(input) == 0 { 305 | return nil, nil 306 | } 307 | outbuf := make([]byte, len(input)+ctx.BlockSize()) 308 | outlen := C.int(len(outbuf)) 309 | res := C.EVP_DecryptUpdate(ctx.ctx, (*C.uchar)(&outbuf[0]), &outlen, 310 | (*C.uchar)(&input[0]), C.int(len(input))) 311 | if res != 1 { 312 | return nil, fmt.Errorf("failed to decrypt [result %d]", res) 313 | } 314 | return outbuf[:outlen], nil 315 | } 316 | 317 | func (ctx *encryptionCipherCtx) EncryptFinal() ([]byte, error) { 318 | outbuf := make([]byte, ctx.BlockSize()) 319 | var outlen C.int 320 | if C.EVP_EncryptFinal_ex(ctx.ctx, (*C.uchar)(&outbuf[0]), &outlen) != 1 { 321 | return nil, errors.New("encryption failed") 322 | } 323 | return outbuf[:outlen], nil 324 | } 325 | 326 | func (ctx *decryptionCipherCtx) DecryptFinal() ([]byte, error) { 327 | outbuf := make([]byte, ctx.BlockSize()) 328 | var outlen C.int 329 | if C.EVP_DecryptFinal_ex(ctx.ctx, (*C.uchar)(&outbuf[0]), &outlen) != 1 { 330 | // this may mean the tag failed to verify- all previous plaintext 331 | // returned must be considered faked and invalid 332 | return nil, errors.New("decryption failed") 333 | } 334 | return outbuf[:outlen], nil 335 | } 336 | -------------------------------------------------------------------------------- /ciphers_gcm.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | // #include 18 | import "C" 19 | 20 | import ( 21 | "errors" 22 | "fmt" 23 | ) 24 | 25 | type AuthenticatedEncryptionCipherCtx interface { 26 | EncryptionCipherCtx 27 | 28 | // data passed in to ExtraData() is part of the final output; it is 29 | // not encrypted itself, but is part of the authenticated data. when 30 | // decrypting or authenticating, pass back with the decryption 31 | // context's ExtraData() 32 | ExtraData([]byte) error 33 | 34 | // use after finalizing encryption to get the authenticating tag 35 | GetTag() ([]byte, error) 36 | } 37 | 38 | type AuthenticatedDecryptionCipherCtx interface { 39 | DecryptionCipherCtx 40 | 41 | // pass in any extra data that was added during encryption with the 42 | // encryption context's ExtraData() 43 | ExtraData([]byte) error 44 | 45 | // use before finalizing decryption to tell the library what the 46 | // tag is expected to be 47 | SetTag([]byte) error 48 | } 49 | 50 | type authEncryptionCipherCtx struct { 51 | *encryptionCipherCtx 52 | } 53 | 54 | type authDecryptionCipherCtx struct { 55 | *decryptionCipherCtx 56 | } 57 | 58 | func getGCMCipher(blocksize int) (*Cipher, error) { 59 | var cipherptr *C.EVP_CIPHER 60 | switch blocksize { 61 | case 256: 62 | cipherptr = C.EVP_aes_256_gcm() 63 | case 192: 64 | cipherptr = C.EVP_aes_192_gcm() 65 | case 128: 66 | cipherptr = C.EVP_aes_128_gcm() 67 | default: 68 | return nil, fmt.Errorf("unknown block size %d", blocksize) 69 | } 70 | return &Cipher{ptr: cipherptr}, nil 71 | } 72 | 73 | func NewGCMEncryptionCipherCtx(blocksize int, e *Engine, key, iv []byte) ( 74 | AuthenticatedEncryptionCipherCtx, error) { 75 | cipher, err := getGCMCipher(blocksize) 76 | if err != nil { 77 | return nil, err 78 | } 79 | ctx, err := newEncryptionCipherCtx(cipher, e, key, nil) 80 | if err != nil { 81 | return nil, err 82 | } 83 | if len(iv) > 0 { 84 | err := ctx.setCtrl(C.EVP_CTRL_GCM_SET_IVLEN, len(iv)) 85 | if err != nil { 86 | return nil, fmt.Errorf("could not set IV len to %d: %s", 87 | len(iv), err) 88 | } 89 | if C.EVP_EncryptInit_ex(ctx.ctx, nil, nil, nil, 90 | (*C.uchar)(&iv[0])) != 1 { 91 | return nil, errors.New("failed to apply IV") 92 | } 93 | } 94 | return &authEncryptionCipherCtx{encryptionCipherCtx: ctx}, nil 95 | } 96 | 97 | func NewGCMDecryptionCipherCtx(blocksize int, e *Engine, key, iv []byte) ( 98 | AuthenticatedDecryptionCipherCtx, error) { 99 | cipher, err := getGCMCipher(blocksize) 100 | if err != nil { 101 | return nil, err 102 | } 103 | ctx, err := newDecryptionCipherCtx(cipher, e, key, nil) 104 | if err != nil { 105 | return nil, err 106 | } 107 | if len(iv) > 0 { 108 | err := ctx.setCtrl(C.EVP_CTRL_GCM_SET_IVLEN, len(iv)) 109 | if err != nil { 110 | return nil, fmt.Errorf("could not set IV len to %d: %s", 111 | len(iv), err) 112 | } 113 | if C.EVP_DecryptInit_ex(ctx.ctx, nil, nil, nil, 114 | (*C.uchar)(&iv[0])) != 1 { 115 | return nil, errors.New("failed to apply IV") 116 | } 117 | } 118 | return &authDecryptionCipherCtx{decryptionCipherCtx: ctx}, nil 119 | } 120 | 121 | func (ctx *authEncryptionCipherCtx) ExtraData(aad []byte) error { 122 | if aad == nil { 123 | return nil 124 | } 125 | var outlen C.int 126 | if C.EVP_EncryptUpdate(ctx.ctx, nil, &outlen, (*C.uchar)(&aad[0]), 127 | C.int(len(aad))) != 1 { 128 | return errors.New("failed to add additional authenticated data") 129 | } 130 | return nil 131 | } 132 | 133 | func (ctx *authDecryptionCipherCtx) ExtraData(aad []byte) error { 134 | if aad == nil { 135 | return nil 136 | } 137 | var outlen C.int 138 | if C.EVP_DecryptUpdate(ctx.ctx, nil, &outlen, (*C.uchar)(&aad[0]), 139 | C.int(len(aad))) != 1 { 140 | return errors.New("failed to add additional authenticated data") 141 | } 142 | return nil 143 | } 144 | 145 | func (ctx *authEncryptionCipherCtx) GetTag() ([]byte, error) { 146 | return ctx.getCtrlBytes(C.EVP_CTRL_GCM_GET_TAG, GCM_TAG_MAXLEN, 147 | GCM_TAG_MAXLEN) 148 | } 149 | 150 | func (ctx *authDecryptionCipherCtx) SetTag(tag []byte) error { 151 | return ctx.setCtrlBytes(C.EVP_CTRL_GCM_SET_TAG, len(tag), tag) 152 | } 153 | -------------------------------------------------------------------------------- /ciphers_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | import ( 18 | "bytes" 19 | "fmt" 20 | "strings" 21 | "testing" 22 | ) 23 | 24 | func expectError(t *testing.T, err error, msg string) { 25 | if err == nil { 26 | t.Fatalf("Expected error containing %#v, but got none", msg) 27 | } 28 | if !strings.Contains(err.Error(), msg) { 29 | t.Fatalf("Expected error containing %#v, but got %s", msg, err) 30 | } 31 | } 32 | 33 | func TestBadInputs(t *testing.T) { 34 | _, err := NewGCMEncryptionCipherCtx(256, nil, 35 | []byte("abcdefghijklmnopqrstuvwxyz"), nil) 36 | expectError(t, err, "bad key size") 37 | _, err = NewGCMEncryptionCipherCtx(128, nil, 38 | []byte("abcdefghijklmnopqrstuvwxyz"), nil) 39 | expectError(t, err, "bad key size") 40 | _, err = NewGCMEncryptionCipherCtx(200, nil, 41 | []byte("abcdefghijklmnopqrstuvwxy"), nil) 42 | expectError(t, err, "unknown block size") 43 | c, err := GetCipherByName("AES-128-CBC") 44 | if err != nil { 45 | t.Fatal("Could not look up AES-128-CBC") 46 | } 47 | _, err = NewEncryptionCipherCtx(c, nil, []byte("abcdefghijklmnop"), 48 | []byte("abc")) 49 | expectError(t, err, "bad IV size") 50 | } 51 | 52 | func doEncryption(key, iv, aad, plaintext []byte, blocksize, bufsize int) ( 53 | ciphertext, tag []byte, err error) { 54 | ectx, err := NewGCMEncryptionCipherCtx(blocksize, nil, key, iv) 55 | if err != nil { 56 | return nil, nil, fmt.Errorf("Failed making GCM encryption ctx: %s", err) 57 | } 58 | err = ectx.ExtraData(aad) 59 | if err != nil { 60 | return nil, nil, fmt.Errorf("Failed to add authenticated data: %s", 61 | err) 62 | } 63 | plainb := bytes.NewBuffer(plaintext) 64 | cipherb := new(bytes.Buffer) 65 | for plainb.Len() > 0 { 66 | moar, err := ectx.EncryptUpdate(plainb.Next(bufsize)) 67 | if err != nil { 68 | return nil, nil, fmt.Errorf("Failed to perform an encryption: %s", 69 | err) 70 | } 71 | cipherb.Write(moar) 72 | } 73 | moar, err := ectx.EncryptFinal() 74 | if err != nil { 75 | return nil, nil, fmt.Errorf("Failed to finalize encryption: %s", err) 76 | } 77 | cipherb.Write(moar) 78 | tag, err = ectx.GetTag() 79 | if err != nil { 80 | return nil, nil, fmt.Errorf("Failed to get GCM tag: %s", err) 81 | } 82 | return cipherb.Bytes(), tag, nil 83 | } 84 | 85 | func doDecryption(key, iv, aad, ciphertext, tag []byte, blocksize, 86 | bufsize int) (plaintext []byte, err error) { 87 | dctx, err := NewGCMDecryptionCipherCtx(blocksize, nil, key, iv) 88 | if err != nil { 89 | return nil, fmt.Errorf("Failed making GCM decryption ctx: %s", err) 90 | } 91 | aadbuf := bytes.NewBuffer(aad) 92 | for aadbuf.Len() > 0 { 93 | err = dctx.ExtraData(aadbuf.Next(bufsize)) 94 | if err != nil { 95 | return nil, fmt.Errorf("Failed to add authenticated data: %s", err) 96 | } 97 | } 98 | plainb := new(bytes.Buffer) 99 | cipherb := bytes.NewBuffer(ciphertext) 100 | for cipherb.Len() > 0 { 101 | moar, err := dctx.DecryptUpdate(cipherb.Next(bufsize)) 102 | if err != nil { 103 | return nil, fmt.Errorf("Failed to perform a decryption: %s", err) 104 | } 105 | plainb.Write(moar) 106 | } 107 | err = dctx.SetTag(tag) 108 | if err != nil { 109 | return nil, fmt.Errorf("Failed to set expected GCM tag: %s", err) 110 | } 111 | moar, err := dctx.DecryptFinal() 112 | if err != nil { 113 | return nil, fmt.Errorf("Failed to finalize decryption: %s", err) 114 | } 115 | plainb.Write(moar) 116 | return plainb.Bytes(), nil 117 | } 118 | 119 | func checkEqual(t *testing.T, output []byte, original string) { 120 | output_s := string(output) 121 | if output_s != original { 122 | t.Fatalf("output != original! %#v != %#v", output_s, original) 123 | } 124 | } 125 | 126 | func TestGCM(t *testing.T) { 127 | aad := []byte("foo bar baz") 128 | key := []byte("nobody can guess this i'm sure..") // len=32 129 | iv := []byte("just a bunch of bytes") 130 | plaintext := "Long long ago, in a land far away..." 131 | 132 | blocksizes_to_test := []int{256, 192, 128} 133 | 134 | // best for this to have no common factors with blocksize, so that the 135 | // buffering layer inside the CIPHER_CTX gets exercised 136 | bufsize := 33 137 | 138 | if len(plaintext)%8 == 0 { 139 | plaintext += "!" // make sure padding is exercised 140 | } 141 | 142 | for _, bsize := range blocksizes_to_test { 143 | subkey := key[:bsize/8] 144 | ciphertext, tag, err := doEncryption(subkey, iv, aad, []byte(plaintext), 145 | bsize, bufsize) 146 | if err != nil { 147 | t.Fatalf("Encryption with b=%d: %s", bsize, err) 148 | } 149 | plaintext_out, err := doDecryption(subkey, iv, aad, ciphertext, tag, 150 | bsize, bufsize) 151 | if err != nil { 152 | t.Fatalf("Decryption with b=%d: %s", bsize, err) 153 | } 154 | checkEqual(t, plaintext_out, plaintext) 155 | } 156 | } 157 | 158 | func TestGCMWithNoAAD(t *testing.T) { 159 | key := []byte("0000111122223333") 160 | iv := []byte("9999") 161 | plaintext := "ABORT ABORT ABORT DANGAR" 162 | 163 | ciphertext, tag, err := doEncryption(key, iv, nil, []byte(plaintext), 164 | 128, 32) 165 | if err != nil { 166 | t.Fatal("Encryption failure:", err) 167 | } 168 | plaintext_out, err := doDecryption(key, iv, nil, ciphertext, tag, 128, 129) 169 | if err != nil { 170 | t.Fatal("Decryption failure:", err) 171 | } 172 | checkEqual(t, plaintext_out, plaintext) 173 | } 174 | 175 | func TestBadTag(t *testing.T) { 176 | key := []byte("abcdefghijklmnop") 177 | iv := []byte("v7239qjfv3qr793fuaj") 178 | plaintext := "The red rooster has flown the coop I REPEAT" + 179 | "the red rooster has flown the coop!!1!" 180 | 181 | ciphertext, tag, err := doEncryption(key, iv, nil, []byte(plaintext), 182 | 128, 32) 183 | if err != nil { 184 | t.Fatal("Encryption failure:", err) 185 | } 186 | // flip the last bit 187 | tag[len(tag)-1] ^= 1 188 | if _, err := doDecryption(key, iv, nil, ciphertext, tag, 128, 129); err == nil { 189 | t.Fatal("Expected error for bad tag, but got none") 190 | } 191 | // flip it back, try again just to make sure 192 | tag[len(tag)-1] ^= 1 193 | plaintextOut, err := doDecryption(key, iv, nil, ciphertext, tag, 128, 129) 194 | if err != nil { 195 | t.Fatal("Decryption failure:", err) 196 | } 197 | checkEqual(t, plaintextOut, plaintext) 198 | } 199 | 200 | func TestBadCiphertext(t *testing.T) { 201 | key := []byte("hard boiled eggs & bacon") 202 | iv := []byte("x") // it's not a very /good/ IV, is it 203 | aad := []byte("mu") 204 | plaintext := "Roger roger bingo charlie, we have a niner fourteen tango" 205 | 206 | ciphertext, tag, err := doEncryption(key, iv, aad, []byte(plaintext), 207 | 192, 1) 208 | if err != nil { 209 | t.Fatal("Encryption failure:", err) 210 | } 211 | // flip the last bit 212 | ciphertext[len(ciphertext)-1] ^= 1 213 | if _, err := doDecryption(key, iv, aad, ciphertext, tag, 192, 192); err == nil { 214 | t.Fatal("Expected error for bad ciphertext, but got none") 215 | } 216 | // flip it back, try again just to make sure 217 | ciphertext[len(ciphertext)-1] ^= 1 218 | plaintextOut, err := doDecryption(key, iv, aad, ciphertext, tag, 192, 192) 219 | if err != nil { 220 | t.Fatal("Decryption failure:", err) 221 | } 222 | checkEqual(t, plaintextOut, plaintext) 223 | } 224 | 225 | func TestBadAAD(t *testing.T) { 226 | key := []byte("Ive got a lovely buncha coconuts") 227 | iv := []byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab") 228 | aad := []byte("Hi i am a plain") 229 | plaintext := "Whatever." 230 | 231 | ciphertext, tag, err := doEncryption(key, iv, aad, []byte(plaintext), 232 | 256, 256) 233 | if err != nil { 234 | t.Fatal("Encryption failure:", err) 235 | } 236 | // flip the last bit 237 | aad[len(aad)-1] ^= 1 238 | if _, err := doDecryption(key, iv, aad, ciphertext, tag, 256, 256); err == nil { 239 | t.Fatal("Expected error for bad AAD, but got none") 240 | } 241 | // flip it back, try again just to make sure 242 | aad[len(aad)-1] ^= 1 243 | plaintextOut, err := doDecryption(key, iv, aad, ciphertext, tag, 256, 256) 244 | if err != nil { 245 | t.Fatal("Decryption failure:", err) 246 | } 247 | checkEqual(t, plaintextOut, plaintext) 248 | } 249 | 250 | func TestNonAuthenticatedEncryption(t *testing.T) { 251 | key := []byte("never gonna give you up, never g") 252 | iv := []byte("onna let you dow") 253 | plaintext1 := "n, never gonna run around" 254 | plaintext2 := " and desert you" 255 | 256 | cipher, err := GetCipherByName("aes-256-cbc") 257 | if err != nil { 258 | t.Fatal("Could not get cipher: ", err) 259 | } 260 | 261 | eCtx, err := NewEncryptionCipherCtx(cipher, nil, key, iv) 262 | if err != nil { 263 | t.Fatal("Could not create encryption context: ", err) 264 | } 265 | cipherbytes, err := eCtx.EncryptUpdate([]byte(plaintext1)) 266 | if err != nil { 267 | t.Fatal("EncryptUpdate(plaintext1) failure: ", err) 268 | } 269 | ciphertext := string(cipherbytes) 270 | cipherbytes, err = eCtx.EncryptUpdate([]byte(plaintext2)) 271 | if err != nil { 272 | t.Fatal("EncryptUpdate(plaintext2) failure: ", err) 273 | } 274 | ciphertext += string(cipherbytes) 275 | cipherbytes, err = eCtx.EncryptFinal() 276 | if err != nil { 277 | t.Fatal("EncryptFinal() failure: ", err) 278 | } 279 | ciphertext += string(cipherbytes) 280 | 281 | dCtx, err := NewDecryptionCipherCtx(cipher, nil, key, iv) 282 | if err != nil { 283 | t.Fatal("Could not create decryption context: ", err) 284 | } 285 | plainbytes, err := dCtx.DecryptUpdate([]byte(ciphertext[:15])) 286 | if err != nil { 287 | t.Fatal("DecryptUpdate(ciphertext part 1) failure: ", err) 288 | } 289 | plainOutput := string(plainbytes) 290 | plainbytes, err = dCtx.DecryptUpdate([]byte(ciphertext[15:])) 291 | if err != nil { 292 | t.Fatal("DecryptUpdate(ciphertext part 2) failure: ", err) 293 | } 294 | plainOutput += string(plainbytes) 295 | plainbytes, err = dCtx.DecryptFinal() 296 | if err != nil { 297 | t.Fatal("DecryptFinal() failure: ", err) 298 | } 299 | plainOutput += string(plainbytes) 300 | 301 | checkEqual(t, []byte(plainOutput), plaintext1+plaintext2) 302 | } 303 | -------------------------------------------------------------------------------- /ctx_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | import ( 18 | "testing" 19 | "time" 20 | ) 21 | 22 | func TestCtxTimeoutOption(t *testing.T) { 23 | ctx, _ := NewCtx() 24 | oldTimeout1 := ctx.GetTimeout() 25 | newTimeout1 := oldTimeout1 + (time.Duration(99) * time.Second) 26 | oldTimeout2 := ctx.SetTimeout(newTimeout1) 27 | newTimeout2 := ctx.GetTimeout() 28 | if oldTimeout1 != oldTimeout2 { 29 | t.Error("SetTimeout() returns something undocumented") 30 | } 31 | if newTimeout1 != newTimeout2 { 32 | t.Error("SetTimeout() does not save anything to ctx") 33 | } 34 | } 35 | 36 | func TestCtxSessCacheSizeOption(t *testing.T) { 37 | ctx, _ := NewCtx() 38 | oldSize1 := ctx.SessGetCacheSize() 39 | newSize1 := oldSize1 + 42 40 | oldSize2 := ctx.SessSetCacheSize(newSize1) 41 | newSize2 := ctx.SessGetCacheSize() 42 | if oldSize1 != oldSize2 { 43 | t.Error("SessSetCacheSize() returns something undocumented") 44 | } 45 | if newSize1 != newSize2 { 46 | t.Error("SessSetCacheSize() does not save anything to ctx") 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /dh.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | // #include "shim.h" 18 | import "C" 19 | import ( 20 | "errors" 21 | "unsafe" 22 | ) 23 | 24 | // DeriveSharedSecret derives a shared secret using a private key and a peer's 25 | // public key. 26 | // The specific algorithm that is used depends on the types of the 27 | // keys, but it is most commonly a variant of Diffie-Hellman. 28 | func DeriveSharedSecret(private PrivateKey, public PublicKey) ([]byte, error) { 29 | // Create context for the shared secret derivation 30 | dhCtx := C.EVP_PKEY_CTX_new(private.evpPKey(), nil) 31 | if dhCtx == nil { 32 | return nil, errors.New("failed creating shared secret derivation context") 33 | } 34 | defer C.EVP_PKEY_CTX_free(dhCtx) 35 | 36 | // Initialize the context 37 | if int(C.EVP_PKEY_derive_init(dhCtx)) != 1 { 38 | return nil, errors.New("failed initializing shared secret derivation context") 39 | } 40 | 41 | // Provide the peer's public key 42 | if int(C.EVP_PKEY_derive_set_peer(dhCtx, public.evpPKey())) != 1 { 43 | return nil, errors.New("failed adding peer public key to context") 44 | } 45 | 46 | // Determine how large of a buffer we need for the shared secret 47 | var buffLen C.size_t 48 | if int(C.EVP_PKEY_derive(dhCtx, nil, &buffLen)) != 1 { 49 | return nil, errors.New("failed determining shared secret length") 50 | } 51 | 52 | // Allocate a buffer 53 | buffer := C.X_OPENSSL_malloc(buffLen) 54 | if buffer == nil { 55 | return nil, errors.New("failed allocating buffer for shared secret") 56 | } 57 | defer C.X_OPENSSL_free(buffer) 58 | 59 | // Derive the shared secret 60 | if int(C.EVP_PKEY_derive(dhCtx, (*C.uchar)(buffer), &buffLen)) != 1 { 61 | return nil, errors.New("failed deriving the shared secret") 62 | } 63 | 64 | secret := C.GoBytes(unsafe.Pointer(buffer), C.int(buffLen)) 65 | return secret, nil 66 | } 67 | -------------------------------------------------------------------------------- /dh_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | import ( 18 | "bytes" 19 | "testing" 20 | ) 21 | 22 | func TestECDH(t *testing.T) { 23 | t.Parallel() 24 | 25 | myKey, err := GenerateECKey(Prime256v1) 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | peerKey, err := GenerateECKey(Prime256v1) 30 | if err != nil { 31 | t.Fatal(err) 32 | } 33 | 34 | mySecret, err := DeriveSharedSecret(myKey, peerKey) 35 | if err != nil { 36 | t.Fatal(err) 37 | } 38 | theirSecret, err := DeriveSharedSecret(peerKey, myKey) 39 | if err != nil { 40 | t.Fatal(err) 41 | } 42 | 43 | if !bytes.Equal(mySecret, theirSecret) { 44 | t.Fatal("shared secrets are different") 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /dhparam.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | // #include "shim.h" 18 | import "C" 19 | 20 | import ( 21 | "errors" 22 | "runtime" 23 | "unsafe" 24 | ) 25 | 26 | type DH struct { 27 | dh *C.struct_dh_st 28 | } 29 | 30 | // LoadDHParametersFromPEM loads the Diffie-Hellman parameters from 31 | // a PEM-encoded block. 32 | func LoadDHParametersFromPEM(pem_block []byte) (*DH, error) { 33 | if len(pem_block) == 0 { 34 | return nil, errors.New("empty pem block") 35 | } 36 | bio := C.BIO_new_mem_buf(unsafe.Pointer(&pem_block[0]), 37 | C.int(len(pem_block))) 38 | if bio == nil { 39 | return nil, errors.New("failed creating bio") 40 | } 41 | defer C.BIO_free(bio) 42 | 43 | params := C.PEM_read_bio_DHparams(bio, nil, nil, nil) 44 | if params == nil { 45 | return nil, errors.New("failed reading dh parameters") 46 | } 47 | dhparams := &DH{dh: params} 48 | runtime.SetFinalizer(dhparams, func(dhparams *DH) { 49 | C.DH_free(dhparams.dh) 50 | }) 51 | return dhparams, nil 52 | } 53 | 54 | // SetDHParameters sets the DH group (DH parameters) used to 55 | // negotiate an emphemeral DH key during handshaking. 56 | func (c *Ctx) SetDHParameters(dh *DH) error { 57 | runtime.LockOSThread() 58 | defer runtime.UnlockOSThread() 59 | 60 | if int(C.X_SSL_CTX_set_tmp_dh(c.ctx, dh.dh)) != 1 { 61 | return errorFromErrorQueue() 62 | } 63 | return nil 64 | } 65 | -------------------------------------------------------------------------------- /digest.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | // #include "shim.h" 18 | import "C" 19 | 20 | import ( 21 | "fmt" 22 | "unsafe" 23 | ) 24 | 25 | // Digest represents and openssl message digest. 26 | type Digest struct { 27 | ptr *C.EVP_MD 28 | } 29 | 30 | // GetDigestByName returns the Digest with the name or nil and an error if the 31 | // digest was not found. 32 | func GetDigestByName(name string) (*Digest, error) { 33 | cname := C.CString(name) 34 | defer C.free(unsafe.Pointer(cname)) 35 | p := C.X_EVP_get_digestbyname(cname) 36 | if p == nil { 37 | return nil, fmt.Errorf("Digest %v not found", name) 38 | } 39 | // we can consider digests to use static mem; don't need to free 40 | return &Digest{ptr: p}, nil 41 | } 42 | 43 | // GetDigestByName returns the Digest with the NID or nil and an error if the 44 | // digest was not found. 45 | func GetDigestByNid(nid NID) (*Digest, error) { 46 | sn, err := Nid2ShortName(nid) 47 | if err != nil { 48 | return nil, err 49 | } 50 | return GetDigestByName(sn) 51 | } 52 | -------------------------------------------------------------------------------- /engine.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | /* 18 | #include "openssl/engine.h" 19 | */ 20 | import "C" 21 | 22 | import ( 23 | "fmt" 24 | "runtime" 25 | "unsafe" 26 | ) 27 | 28 | type Engine struct { 29 | e *C.ENGINE 30 | } 31 | 32 | func EngineById(name string) (*Engine, error) { 33 | cname := C.CString(name) 34 | defer C.free(unsafe.Pointer(cname)) 35 | e := &Engine{ 36 | e: C.ENGINE_by_id(cname), 37 | } 38 | if e.e == nil { 39 | return nil, fmt.Errorf("engine %s missing", name) 40 | } 41 | if C.ENGINE_init(e.e) == 0 { 42 | C.ENGINE_free(e.e) 43 | return nil, fmt.Errorf("engine %s not initialized", name) 44 | } 45 | runtime.SetFinalizer(e, func(e *Engine) { 46 | C.ENGINE_finish(e.e) 47 | C.ENGINE_free(e.e) 48 | }) 49 | return e, nil 50 | } 51 | -------------------------------------------------------------------------------- /extension.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | #include 5 | 6 | const unsigned char * get_extention(X509 *x, int NID, int *data_len){ 7 | int loc; 8 | ASN1_OCTET_STRING *octet_str; 9 | long xlen; 10 | int tag, xclass; 11 | 12 | loc = X509_get_ext_by_NID( x, NID, -1); 13 | X509_EXTENSION *ex = X509_get_ext(x, loc); 14 | octet_str = X509_EXTENSION_get_data(ex); 15 | *data_len = octet_str->length; 16 | return octet_str->data; 17 | } 18 | 19 | // Copied from https://github.com/libtor/openssl/blob/master/demos/x509/mkcert.c#L153 20 | int add_custom_ext(X509 *cert, int nid,unsigned char *value, int len) 21 | { 22 | X509_EXTENSION *ex; 23 | ASN1_OCTET_STRING *os = ASN1_OCTET_STRING_new(); 24 | ASN1_OCTET_STRING_set(os,value,len); 25 | X509V3_CTX ctx; 26 | /* This sets the 'context' of the extensions. */ 27 | /* No configuration database */ 28 | X509V3_set_ctx_nodb(&ctx); 29 | /* Issuer and subject certs: both the target since it is self signed, 30 | * no request and no CRL 31 | */ 32 | X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0); 33 | // ref http://openssl.6102.n7.nabble.com/Adding-a-custom-extension-to-a-CSR-td47446.html 34 | ex = X509_EXTENSION_create_by_NID( NULL, nid, 0, os); 35 | if (!X509_add_ext(cert,ex,-1)) 36 | return 0; 37 | 38 | X509_EXTENSION_free(ex); 39 | return 1; 40 | } -------------------------------------------------------------------------------- /fips.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | /* 18 | #include 19 | 20 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L 21 | int FIPS_mode_set(int ONOFF) { 22 | return 0; 23 | } 24 | #endif 25 | 26 | */ 27 | import "C" 28 | import ( 29 | "errors" 30 | "runtime" 31 | ) 32 | 33 | // FIPSModeSet enables a FIPS 140-2 validated mode of operation. 34 | // https://wiki.openssl.org/index.php/FIPS_mode_set() 35 | // This call has been deleted from OpenSSL 3.0. 36 | func FIPSModeSet(mode bool) error { 37 | runtime.LockOSThread() 38 | defer runtime.UnlockOSThread() 39 | 40 | if C.OPENSSL_VERSION_NUMBER >= 0x30000000 { 41 | return errors.New("FIPS_mode_set() has been deleted from OpenSSL 3.0") 42 | } 43 | 44 | var r C.int 45 | if mode { 46 | r = C.FIPS_mode_set(1) 47 | } else { 48 | r = C.FIPS_mode_set(0) 49 | } 50 | if r != 1 { 51 | return errorFromErrorQueue() 52 | } 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/libp2p/go-openssl 2 | 3 | require ( 4 | github.com/mattn/go-pointer v0.0.1 5 | github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 6 | ) 7 | 8 | require golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb // indirect 9 | 10 | go 1.18 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0= 2 | github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc= 3 | github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU= 4 | github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= 5 | golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k= 6 | golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 7 | -------------------------------------------------------------------------------- /hmac.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | // #include "shim.h" 18 | import "C" 19 | 20 | import ( 21 | "errors" 22 | "runtime" 23 | "unsafe" 24 | ) 25 | 26 | type HMAC struct { 27 | ctx *C.HMAC_CTX 28 | engine *Engine 29 | md *C.EVP_MD 30 | } 31 | 32 | func NewHMAC(key []byte, digestAlgorithm EVP_MD) (*HMAC, error) { 33 | return NewHMACWithEngine(key, digestAlgorithm, nil) 34 | } 35 | 36 | func NewHMACWithEngine(key []byte, digestAlgorithm EVP_MD, e *Engine) (*HMAC, error) { 37 | var md *C.EVP_MD = getDigestFunction(digestAlgorithm) 38 | h := &HMAC{engine: e, md: md} 39 | h.ctx = C.X_HMAC_CTX_new() 40 | if h.ctx == nil { 41 | return nil, errors.New("unable to allocate HMAC_CTX") 42 | } 43 | 44 | var c_e *C.ENGINE 45 | if e != nil { 46 | c_e = e.e 47 | } 48 | if rc := C.X_HMAC_Init_ex(h.ctx, 49 | unsafe.Pointer(&key[0]), 50 | C.int(len(key)), 51 | md, 52 | c_e); rc != 1 { 53 | C.X_HMAC_CTX_free(h.ctx) 54 | return nil, errors.New("failed to initialize HMAC_CTX") 55 | } 56 | 57 | runtime.SetFinalizer(h, func(h *HMAC) { h.Close() }) 58 | return h, nil 59 | } 60 | 61 | func (h *HMAC) Close() { 62 | C.X_HMAC_CTX_free(h.ctx) 63 | } 64 | 65 | func (h *HMAC) Write(data []byte) (n int, err error) { 66 | if len(data) == 0 { 67 | return 0, nil 68 | } 69 | if rc := C.X_HMAC_Update(h.ctx, (*C.uchar)(unsafe.Pointer(&data[0])), 70 | C.size_t(len(data))); rc != 1 { 71 | return 0, errors.New("failed to update HMAC") 72 | } 73 | return len(data), nil 74 | } 75 | 76 | func (h *HMAC) Reset() error { 77 | if C.X_HMAC_Init_ex(h.ctx, nil, 0, nil, nil) != 1 { 78 | return errors.New("failed to reset HMAC_CTX") 79 | } 80 | return nil 81 | } 82 | 83 | func (h *HMAC) Final() (result []byte, err error) { 84 | mdLength := C.X_EVP_MD_size(h.md) 85 | result = make([]byte, mdLength) 86 | if rc := C.X_HMAC_Final(h.ctx, (*C.uchar)(unsafe.Pointer(&result[0])), 87 | (*C.uint)(unsafe.Pointer(&mdLength))); rc != 1 { 88 | return nil, errors.New("failed to finalized HMAC") 89 | } 90 | return result, h.Reset() 91 | } 92 | -------------------------------------------------------------------------------- /hmac_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | import ( 18 | "crypto/hmac" 19 | "crypto/sha256" 20 | "encoding/hex" 21 | "testing" 22 | ) 23 | 24 | func TestSHA256HMAC(t *testing.T) { 25 | key := []byte("d741787cc61851af045ccd37") 26 | data := []byte("5912EEFD-59EC-43E3-ADB8-D5325AEC3271") 27 | 28 | h, err := NewHMAC(key, EVP_SHA256) 29 | if err != nil { 30 | t.Fatalf("Unable to create new HMAC: %s", err) 31 | } 32 | if _, err := h.Write(data); err != nil { 33 | t.Fatalf("Unable to write data into HMAC: %s", err) 34 | } 35 | 36 | var actualHMACBytes []byte 37 | if actualHMACBytes, err = h.Final(); err != nil { 38 | t.Fatalf("Error while finalizing HMAC: %s", err) 39 | } 40 | actualString := hex.EncodeToString(actualHMACBytes) 41 | 42 | // generate HMAC with built-in crypto lib 43 | mac := hmac.New(sha256.New, key) 44 | mac.Write(data) 45 | expectedString := hex.EncodeToString(mac.Sum(nil)) 46 | 47 | if expectedString != actualString { 48 | t.Errorf("HMAC was incorrect: expected=%s, actual=%s", expectedString, actualString) 49 | } 50 | } 51 | 52 | func BenchmarkSHA256HMAC(b *testing.B) { 53 | key := []byte("d741787cc61851af045ccd37") 54 | data := []byte("5912EEFD-59EC-43E3-ADB8-D5325AEC3271") 55 | 56 | h, err := NewHMAC(key, EVP_SHA256) 57 | if err != nil { 58 | b.Fatalf("Unable to create new HMAC: %s", err) 59 | } 60 | 61 | b.ResetTimer() 62 | for i := 0; i < b.N; i++ { 63 | if _, err := h.Write(data); err != nil { 64 | b.Fatalf("Unable to write data into HMAC: %s", err) 65 | } 66 | 67 | var err error 68 | if _, err = h.Final(); err != nil { 69 | b.Fatalf("Error while finalizing HMAC: %s", err) 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /hostname.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Go-OpenSSL notice: 3 | * This file is required for all OpenSSL versions prior to 1.1.0. This simply 4 | * provides the new 1.1.0 X509_check_* methods for hostname validation if they 5 | * don't already exist. 6 | */ 7 | 8 | #include 9 | 10 | #ifndef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT 11 | 12 | /* portions from x509v3.h and v3_utl.c */ 13 | /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL 14 | * project. 15 | */ 16 | /* ==================================================================== 17 | * Copyright (c) 1999-2003 The OpenSSL Project. All rights reserved. 18 | * 19 | * Redistribution and use in source and binary forms, with or without 20 | * modification, are permitted provided that the following conditions 21 | * are met: 22 | * 23 | * 1. Redistributions of source code must retain the above copyright 24 | * notice, this list of conditions and the following disclaimer. 25 | * 26 | * 2. Redistributions in binary form must reproduce the above copyright 27 | * notice, this list of conditions and the following disclaimer in 28 | * the documentation and/or other materials provided with the 29 | * distribution. 30 | * 31 | * 3. All advertising materials mentioning features or use of this 32 | * software must display the following acknowledgment: 33 | * "This product includes software developed by the OpenSSL Project 34 | * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" 35 | * 36 | * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to 37 | * endorse or promote products derived from this software without 38 | * prior written permission. For written permission, please contact 39 | * licensing@OpenSSL.org. 40 | * 41 | * 5. Products derived from this software may not be called "OpenSSL" 42 | * nor may "OpenSSL" appear in their names without prior written 43 | * permission of the OpenSSL Project. 44 | * 45 | * 6. Redistributions of any form whatsoever must retain the following 46 | * acknowledgment: 47 | * "This product includes software developed by the OpenSSL Project 48 | * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" 49 | * 50 | * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY 51 | * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 52 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 53 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR 54 | * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 55 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 56 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 57 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 58 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 59 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 60 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 61 | * OF THE POSSIBILITY OF SUCH DAMAGE. 62 | * ==================================================================== 63 | * 64 | * This product includes cryptographic software written by Eric Young 65 | * (eay@cryptsoft.com). This product includes software written by Tim 66 | * Hudson (tjh@cryptsoft.com). 67 | * 68 | */ 69 | /* X509 v3 extension utilities */ 70 | 71 | #include 72 | #include 73 | #include 74 | #include 75 | #include 76 | 77 | #define X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT 0x1 78 | #define X509_CHECK_FLAG_NO_WILDCARDS 0x2 79 | 80 | typedef int (*equal_fn)(const unsigned char *pattern, size_t pattern_len, 81 | const unsigned char *subject, size_t subject_len); 82 | 83 | /* Compare while ASCII ignoring case. */ 84 | static int equal_nocase(const unsigned char *pattern, size_t pattern_len, 85 | const unsigned char *subject, size_t subject_len) 86 | { 87 | if (pattern_len != subject_len) 88 | return 0; 89 | while (pattern_len) 90 | { 91 | unsigned char l = *pattern; 92 | unsigned char r = *subject; 93 | /* The pattern must not contain NUL characters. */ 94 | if (l == 0) 95 | return 0; 96 | if (l != r) 97 | { 98 | if ('A' <= l && l <= 'Z') 99 | l = (l - 'A') + 'a'; 100 | if ('A' <= r && r <= 'Z') 101 | r = (r - 'A') + 'a'; 102 | if (l != r) 103 | return 0; 104 | } 105 | ++pattern; 106 | ++subject; 107 | --pattern_len; 108 | } 109 | return 1; 110 | } 111 | 112 | /* Compare using memcmp. */ 113 | static int equal_case(const unsigned char *pattern, size_t pattern_len, 114 | const unsigned char *subject, size_t subject_len) 115 | { 116 | /* The pattern must not contain NUL characters. */ 117 | if (memchr(pattern, '\0', pattern_len) != NULL) 118 | return 0; 119 | if (pattern_len != subject_len) 120 | return 0; 121 | return !memcmp(pattern, subject, pattern_len); 122 | } 123 | 124 | /* RFC 5280, section 7.5, requires that only the domain is compared in 125 | a case-insensitive manner. */ 126 | static int equal_email(const unsigned char *a, size_t a_len, 127 | const unsigned char *b, size_t b_len) 128 | { 129 | size_t i = a_len; 130 | if (a_len != b_len) 131 | return 0; 132 | /* We search backwards for the '@' character, so that we do 133 | not have to deal with quoted local-parts. The domain part 134 | is compared in a case-insensitive manner. */ 135 | while (i > 0) 136 | { 137 | --i; 138 | if (a[i] == '@' || b[i] == '@') 139 | { 140 | if (!equal_nocase(a + i, a_len - i, 141 | b + i, a_len - i)) 142 | return 0; 143 | break; 144 | } 145 | } 146 | if (i == 0) 147 | i = a_len; 148 | return equal_case(a, i, b, i); 149 | } 150 | 151 | /* Compare the prefix and suffix with the subject, and check that the 152 | characters in-between are valid. */ 153 | static int wildcard_match(const unsigned char *prefix, size_t prefix_len, 154 | const unsigned char *suffix, size_t suffix_len, 155 | const unsigned char *subject, size_t subject_len) 156 | { 157 | const unsigned char *wildcard_start; 158 | const unsigned char *wildcard_end; 159 | const unsigned char *p; 160 | if (subject_len < prefix_len + suffix_len) 161 | return 0; 162 | if (!equal_nocase(prefix, prefix_len, subject, prefix_len)) 163 | return 0; 164 | wildcard_start = subject + prefix_len; 165 | wildcard_end = subject + (subject_len - suffix_len); 166 | if (!equal_nocase(wildcard_end, suffix_len, suffix, suffix_len)) 167 | return 0; 168 | /* The wildcard must match at least one character. */ 169 | if (wildcard_start == wildcard_end) 170 | return 0; 171 | /* Check that the part matched by the wildcard contains only 172 | permitted characters and only matches a single label. */ 173 | for (p = wildcard_start; p != wildcard_end; ++p) 174 | if (!(('0' <= *p && *p <= '9') || 175 | ('A' <= *p && *p <= 'Z') || 176 | ('a' <= *p && *p <= 'z') || 177 | *p == '-')) 178 | return 0; 179 | return 1; 180 | } 181 | 182 | /* Checks if the memory region consistens of [0-9A-Za-z.-]. */ 183 | static int valid_domain_characters(const unsigned char *p, size_t len) 184 | { 185 | while (len) 186 | { 187 | if (!(('0' <= *p && *p <= '9') || 188 | ('A' <= *p && *p <= 'Z') || 189 | ('a' <= *p && *p <= 'z') || 190 | *p == '-' || *p == '.')) 191 | return 0; 192 | ++p; 193 | --len; 194 | } 195 | return 1; 196 | } 197 | 198 | /* Find the '*' in a wildcard pattern. If no such character is found 199 | or the pattern is otherwise invalid, returns NULL. */ 200 | static const unsigned char *wildcard_find_star(const unsigned char *pattern, 201 | size_t pattern_len) 202 | { 203 | const unsigned char *star = memchr(pattern, '*', pattern_len); 204 | size_t dot_count = 0; 205 | const unsigned char *suffix_start; 206 | size_t suffix_length; 207 | if (star == NULL) 208 | return NULL; 209 | suffix_start = star + 1; 210 | suffix_length = (pattern + pattern_len) - (star + 1); 211 | if (!(valid_domain_characters(pattern, star - pattern) && 212 | valid_domain_characters(suffix_start, suffix_length))) 213 | return NULL; 214 | /* Check that the suffix matches at least two labels. */ 215 | while (suffix_length) 216 | { 217 | if (*suffix_start == '.') 218 | ++dot_count; 219 | ++suffix_start; 220 | --suffix_length; 221 | } 222 | if (dot_count < 2) 223 | return NULL; 224 | return star; 225 | } 226 | 227 | /* Compare using wildcards. */ 228 | static int equal_wildcard(const unsigned char *pattern, size_t pattern_len, 229 | const unsigned char *subject, size_t subject_len) 230 | { 231 | const unsigned char *star = wildcard_find_star(pattern, pattern_len); 232 | if (star == NULL) 233 | return equal_nocase(pattern, pattern_len, 234 | subject, subject_len); 235 | return wildcard_match(pattern, star - pattern, 236 | star + 1, (pattern + pattern_len) - star - 1, 237 | subject, subject_len); 238 | } 239 | 240 | /* Compare an ASN1_STRING to a supplied string. If they match 241 | * return 1. If cmp_type > 0 only compare if string matches the 242 | * type, otherwise convert it to UTF8. 243 | */ 244 | 245 | static int do_check_string(ASN1_STRING *a, int cmp_type, equal_fn equal, 246 | const unsigned char *b, size_t blen) 247 | { 248 | if (!a->data || !a->length) 249 | return 0; 250 | if (cmp_type > 0) 251 | { 252 | if (cmp_type != a->type) 253 | return 0; 254 | if (cmp_type == V_ASN1_IA5STRING) 255 | return equal(a->data, a->length, b, blen); 256 | if (a->length == (int)blen && !memcmp(a->data, b, blen)) 257 | return 1; 258 | else 259 | return 0; 260 | } 261 | else 262 | { 263 | int astrlen, rv; 264 | unsigned char *astr; 265 | astrlen = ASN1_STRING_to_UTF8(&astr, a); 266 | if (astrlen < 0) 267 | return -1; 268 | rv = equal(astr, astrlen, b, blen); 269 | OPENSSL_free(astr); 270 | return rv; 271 | } 272 | } 273 | 274 | static int do_x509_check(X509 *x, const unsigned char *chk, size_t chklen, 275 | unsigned int flags, int check_type) 276 | { 277 | STACK_OF(GENERAL_NAME) *gens = NULL; 278 | X509_NAME *name = NULL; 279 | int i; 280 | int cnid; 281 | int alt_type; 282 | equal_fn equal; 283 | if (check_type == GEN_EMAIL) 284 | { 285 | cnid = NID_pkcs9_emailAddress; 286 | alt_type = V_ASN1_IA5STRING; 287 | equal = equal_email; 288 | } 289 | else if (check_type == GEN_DNS) 290 | { 291 | cnid = NID_commonName; 292 | alt_type = V_ASN1_IA5STRING; 293 | if (flags & X509_CHECK_FLAG_NO_WILDCARDS) 294 | equal = equal_nocase; 295 | else 296 | equal = equal_wildcard; 297 | } 298 | else 299 | { 300 | cnid = 0; 301 | alt_type = V_ASN1_OCTET_STRING; 302 | equal = equal_case; 303 | } 304 | 305 | if (chklen == 0) 306 | chklen = strlen((const char *)chk); 307 | 308 | gens = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL); 309 | if (gens) 310 | { 311 | int rv = 0; 312 | for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) 313 | { 314 | GENERAL_NAME *gen; 315 | ASN1_STRING *cstr; 316 | gen = sk_GENERAL_NAME_value(gens, i); 317 | if(gen->type != check_type) 318 | continue; 319 | if (check_type == GEN_EMAIL) 320 | cstr = gen->d.rfc822Name; 321 | else if (check_type == GEN_DNS) 322 | cstr = gen->d.dNSName; 323 | else 324 | cstr = gen->d.iPAddress; 325 | if (do_check_string(cstr, alt_type, equal, chk, chklen)) 326 | { 327 | rv = 1; 328 | break; 329 | } 330 | } 331 | GENERAL_NAMES_free(gens); 332 | if (rv) 333 | return 1; 334 | if (!(flags & X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT) || !cnid) 335 | return 0; 336 | } 337 | i = -1; 338 | name = X509_get_subject_name(x); 339 | while((i = X509_NAME_get_index_by_NID(name, cnid, i)) >= 0) 340 | { 341 | X509_NAME_ENTRY *ne; 342 | ASN1_STRING *str; 343 | ne = X509_NAME_get_entry(name, i); 344 | str = X509_NAME_ENTRY_get_data(ne); 345 | if (do_check_string(str, -1, equal, chk, chklen)) 346 | return 1; 347 | } 348 | return 0; 349 | } 350 | 351 | #if OPENSSL_VERSION_NUMBER < 0x1000200fL 352 | 353 | int X509_check_host(X509 *x, const unsigned char *chk, size_t chklen, 354 | unsigned int flags, char **peername) 355 | { 356 | return do_x509_check(x, chk, chklen, flags, GEN_DNS); 357 | } 358 | 359 | int X509_check_email(X509 *x, const unsigned char *chk, size_t chklen, 360 | unsigned int flags) 361 | { 362 | return do_x509_check(x, chk, chklen, flags, GEN_EMAIL); 363 | } 364 | 365 | int X509_check_ip(X509 *x, const unsigned char *chk, size_t chklen, 366 | unsigned int flags) 367 | { 368 | return do_x509_check(x, chk, chklen, flags, GEN_IPADD); 369 | } 370 | 371 | #endif /* OPENSSL_VERSION_NUMBER < 0x1000200fL */ 372 | 373 | #endif 374 | -------------------------------------------------------------------------------- /hostname.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | /* 18 | #include 19 | #include 20 | #if OPENSSL_VERSION_NUMBER >= 0x30000000L 21 | #include 22 | typedef const char x509char; 23 | #else 24 | #include 25 | 26 | #ifndef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT 27 | #define X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT 0x1 28 | #define X509_CHECK_FLAG_NO_WILDCARDS 0x2 29 | 30 | extern int X509_check_host(X509 *x, const unsigned char *chk, size_t chklen, 31 | unsigned int flags, char **peername); 32 | extern int X509_check_email(X509 *x, const unsigned char *chk, size_t chklen, 33 | unsigned int flags); 34 | extern int X509_check_ip(X509 *x, const unsigned char *chk, size_t chklen, 35 | unsigned int flags); 36 | typedef const unsigned char x509char; 37 | #else 38 | typedef const char x509char; 39 | #endif 40 | #endif 41 | */ 42 | import "C" 43 | 44 | import ( 45 | "errors" 46 | "net" 47 | "unsafe" 48 | ) 49 | 50 | var ( 51 | ValidationError = errors.New("host validation error") //lint:ignore ST1012 rename may cause breaking changes; research before renaming. 52 | ) 53 | 54 | type CheckFlags int 55 | 56 | const ( 57 | AlwaysCheckSubject CheckFlags = C.X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT 58 | NoWildcards CheckFlags = C.X509_CHECK_FLAG_NO_WILDCARDS 59 | ) 60 | 61 | // CheckHost checks that the X509 certificate is signed for the provided 62 | // host name. See http://www.openssl.org/docs/crypto/X509_check_host.html for 63 | // more. Note that CheckHost does not check the IP field. See VerifyHostname. 64 | // Specifically returns ValidationError if the Certificate didn't match but 65 | // there was no internal error. 66 | func (c *Certificate) CheckHost(host string, flags CheckFlags) error { 67 | chost := unsafe.Pointer(C.CString(host)) 68 | defer C.free(chost) 69 | 70 | rv := C.X509_check_host(c.x, (*C.x509char)(chost), C.size_t(len(host)), 71 | C.uint(flags), nil) 72 | if rv > 0 { 73 | return nil 74 | } 75 | if rv == 0 { 76 | return ValidationError 77 | } 78 | return errors.New("hostname validation had an internal failure") 79 | } 80 | 81 | // CheckEmail checks that the X509 certificate is signed for the provided 82 | // email address. See http://www.openssl.org/docs/crypto/X509_check_host.html 83 | // for more. 84 | // Specifically returns ValidationError if the Certificate didn't match but 85 | // there was no internal error. 86 | func (c *Certificate) CheckEmail(email string, flags CheckFlags) error { 87 | cemail := unsafe.Pointer(C.CString(email)) 88 | defer C.free(cemail) 89 | rv := C.X509_check_email(c.x, (*C.x509char)(cemail), C.size_t(len(email)), 90 | C.uint(flags)) 91 | if rv > 0 { 92 | return nil 93 | } 94 | if rv == 0 { 95 | return ValidationError 96 | } 97 | return errors.New("email validation had an internal failure") 98 | } 99 | 100 | // CheckIP checks that the X509 certificate is signed for the provided 101 | // IP address. See http://www.openssl.org/docs/crypto/X509_check_host.html 102 | // for more. 103 | // Specifically returns ValidationError if the Certificate didn't match but 104 | // there was no internal error. 105 | func (c *Certificate) CheckIP(ip net.IP, flags CheckFlags) error { 106 | // X509_check_ip will fail to validate the 16-byte representation of an IPv4 107 | // address, so convert to the 4-byte representation. 108 | if ip4 := ip.To4(); ip4 != nil { 109 | ip = ip4 110 | } 111 | 112 | cip := unsafe.Pointer(&ip[0]) 113 | rv := C.X509_check_ip(c.x, (*C.uchar)(cip), C.size_t(len(ip)), 114 | C.uint(flags)) 115 | if rv > 0 { 116 | return nil 117 | } 118 | if rv == 0 { 119 | return ValidationError 120 | } 121 | return errors.New("ip validation had an internal failure") 122 | } 123 | 124 | // VerifyHostname is a combination of CheckHost and CheckIP. If the provided 125 | // hostname looks like an IP address, it will be checked as an IP address, 126 | // otherwise it will be checked as a hostname. 127 | // Specifically returns ValidationError if the Certificate didn't match but 128 | // there was no internal error. 129 | func (c *Certificate) VerifyHostname(host string) error { 130 | var ip net.IP 131 | if len(host) >= 3 && host[0] == '[' && host[len(host)-1] == ']' { 132 | ip = net.ParseIP(host[1 : len(host)-1]) 133 | } else { 134 | ip = net.ParseIP(host) 135 | } 136 | if ip != nil { 137 | return c.CheckIP(ip, 0) 138 | } 139 | return c.CheckHost(host, 0) 140 | } 141 | -------------------------------------------------------------------------------- /http.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | import ( 18 | "net/http" 19 | ) 20 | 21 | // ListenAndServeTLS will take an http.Handler and serve it using OpenSSL over 22 | // the given tcp address, configured to use the provided cert and key files. 23 | func ListenAndServeTLS(addr string, cert_file string, key_file string, 24 | handler http.Handler) error { 25 | return ServerListenAndServeTLS( 26 | &http.Server{Addr: addr, Handler: handler}, cert_file, key_file) 27 | } 28 | 29 | // ServerListenAndServeTLS will take an http.Server and serve it using OpenSSL 30 | // configured to use the provided cert and key files. 31 | func ServerListenAndServeTLS(srv *http.Server, 32 | cert_file, key_file string) error { 33 | addr := srv.Addr 34 | if addr == "" { 35 | addr = ":https" 36 | } 37 | 38 | ctx, err := NewCtxFromFiles(cert_file, key_file) 39 | if err != nil { 40 | return err 41 | } 42 | 43 | l, err := Listen("tcp", addr, ctx) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | return srv.Serve(l) 49 | } 50 | 51 | // TODO: http client integration 52 | // holy crap, getting this integrated nicely with the Go stdlib HTTP client 53 | // stack so that it does proxying, connection pooling, and most importantly 54 | // hostname verification is really hard. So much stuff is hardcoded to just use 55 | // the built-in TLS lib. I think to get this to work either some crazy 56 | // hacktackery beyond me, an almost straight up fork of the HTTP client, or 57 | // serious stdlib internal refactoring is necessary. 58 | // even more so, good luck getting openssl to use the operating system default 59 | // root certificates if the user doesn't provide any. sadlol 60 | // NOTE: if you're going to try and write your own round tripper, at least use 61 | // openssl.Dial, or equivalent logic 62 | -------------------------------------------------------------------------------- /init.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /* 16 | Package openssl is a light wrapper around OpenSSL for Go. 17 | 18 | It strives to provide a near-drop-in replacement for the Go standard library 19 | tls package, while allowing for: 20 | 21 | # Performance 22 | 23 | OpenSSL is battle-tested and optimized C. While Go's built-in library shows 24 | great promise, it is still young and in some places, inefficient. This simple 25 | OpenSSL wrapper can often do at least 2x with the same cipher and protocol. 26 | 27 | On my lappytop, I get the following benchmarking speeds: 28 | 29 | BenchmarkSHA1Large_openssl 1000 2611282 ns/op 401.56 MB/s 30 | BenchmarkSHA1Large_stdlib 500 3963983 ns/op 264.53 MB/s 31 | BenchmarkSHA1Small_openssl 1000000 3476 ns/op 0.29 MB/s 32 | BenchmarkSHA1Small_stdlib 5000000 550 ns/op 1.82 MB/s 33 | BenchmarkSHA256Large_openssl 200 8085314 ns/op 129.69 MB/s 34 | BenchmarkSHA256Large_stdlib 100 18948189 ns/op 55.34 MB/s 35 | BenchmarkSHA256Small_openssl 1000000 4262 ns/op 0.23 MB/s 36 | BenchmarkSHA256Small_stdlib 1000000 1444 ns/op 0.69 MB/s 37 | BenchmarkOpenSSLThroughput 100000 21634 ns/op 47.33 MB/s 38 | BenchmarkStdlibThroughput 50000 58974 ns/op 17.36 MB/s 39 | 40 | # Interoperability 41 | 42 | Many systems support OpenSSL with a variety of plugins and modules for things, 43 | such as hardware acceleration in embedded devices. 44 | 45 | # Greater flexibility and configuration 46 | 47 | OpenSSL allows for far greater configuration of corner cases and backwards 48 | compatibility (such as support of SSLv2). You shouldn't be using SSLv2 if you 49 | can help but, but sometimes you can't help it. 50 | 51 | # Security 52 | 53 | Yeah yeah, Heartbleed. But according to the author of the standard library's 54 | TLS implementation, Go's TLS library is vulnerable to timing attacks. And 55 | whether or not OpenSSL received the appropriate amount of scrutiny 56 | pre-Heartbleed, it sure is receiving it now. 57 | 58 | # Usage 59 | 60 | Starting an HTTP server that uses OpenSSL is very easy. It's as simple as: 61 | 62 | log.Fatal(openssl.ListenAndServeTLS( 63 | ":8443", "my_server.crt", "my_server.key", myHandler)) 64 | 65 | Getting a net.Listener that uses OpenSSL is also easy: 66 | 67 | ctx, err := openssl.NewCtxFromFiles("my_server.crt", "my_server.key") 68 | if err != nil { 69 | log.Fatal(err) 70 | } 71 | l, err := openssl.Listen("tcp", ":7777", ctx) 72 | 73 | Making a client connection is straightforward too: 74 | 75 | ctx, err := NewCtx() 76 | if err != nil { 77 | log.Fatal(err) 78 | } 79 | err = ctx.LoadVerifyLocations("/etc/ssl/certs/ca-certificates.crt", "") 80 | if err != nil { 81 | log.Fatal(err) 82 | } 83 | conn, err := openssl.Dial("tcp", "localhost:7777", ctx, 0) 84 | 85 | Help wanted: To get this library to work with net/http's client, we 86 | had to fork net/http. It would be nice if an alternate http client library 87 | supported the generality needed to use OpenSSL instead of crypto/tls. 88 | */ 89 | package openssl 90 | 91 | // #include "shim.h" 92 | import "C" 93 | 94 | import ( 95 | "fmt" 96 | "strings" 97 | ) 98 | 99 | func init() { 100 | if rc := C.X_shim_init(); rc != 0 { 101 | panic(fmt.Errorf("x_shim_init failed with %d", rc)) 102 | } 103 | } 104 | 105 | // errorFromErrorQueue needs to run in the same OS thread as the operation 106 | // that caused the possible error 107 | func errorFromErrorQueue() error { 108 | var errs []string 109 | for { 110 | err := C.ERR_get_error() 111 | if err == 0 { 112 | break 113 | } 114 | errs = append(errs, fmt.Sprintf("%s:%s:%s", 115 | C.GoString(C.ERR_lib_error_string(err)), 116 | C.GoString(C.ERR_func_error_string(err)), 117 | C.GoString(C.ERR_reason_error_string(err)))) 118 | } 119 | return fmt.Errorf("SSL errors: %s", strings.Join(errs, "\n")) 120 | } 121 | -------------------------------------------------------------------------------- /init_posix.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build (linux || darwin || solaris || freebsd || openbsd) && !windows 16 | 17 | package openssl 18 | 19 | /* 20 | #include 21 | #include 22 | #include 23 | 24 | pthread_mutex_t* goopenssl_locks; 25 | 26 | int go_init_locks() { 27 | int rc = 0; 28 | int nlock; 29 | int i; 30 | int locks_needed = CRYPTO_num_locks(); 31 | 32 | goopenssl_locks = (pthread_mutex_t*)malloc( 33 | sizeof(pthread_mutex_t) * locks_needed); 34 | if (!goopenssl_locks) { 35 | return ENOMEM; 36 | } 37 | for (nlock = 0; nlock < locks_needed; ++nlock) { 38 | rc = pthread_mutex_init(&goopenssl_locks[nlock], NULL); 39 | if (rc != 0) { 40 | break; 41 | } 42 | } 43 | 44 | if (rc != 0) { 45 | for (i = nlock - 1; i >= 0; --i) { 46 | pthread_mutex_destroy(&goopenssl_locks[i]); 47 | } 48 | free(goopenssl_locks); 49 | goopenssl_locks = NULL; 50 | } 51 | return rc; 52 | } 53 | 54 | void go_thread_locking_callback(int mode, int n, const char *file, 55 | int line) { 56 | if (mode & CRYPTO_LOCK) { 57 | pthread_mutex_lock(&goopenssl_locks[n]); 58 | } else { 59 | pthread_mutex_unlock(&goopenssl_locks[n]); 60 | } 61 | } 62 | 63 | unsigned long go_thread_id_callback(void) { 64 | return (unsigned long)pthread_self(); 65 | } 66 | */ 67 | import "C" 68 | -------------------------------------------------------------------------------- /init_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build windows 16 | 17 | package openssl 18 | 19 | /* 20 | #include 21 | #include 22 | #include 23 | 24 | CRITICAL_SECTION* goopenssl_locks; 25 | 26 | int go_init_locks() { 27 | int rc = 0; 28 | int nlock; 29 | int i; 30 | int locks_needed = CRYPTO_num_locks(); 31 | 32 | goopenssl_locks = (CRITICAL_SECTION*)malloc( 33 | sizeof(*goopenssl_locks) * locks_needed); 34 | if (!goopenssl_locks) { 35 | return ENOMEM; 36 | } 37 | for (nlock = 0; nlock < locks_needed; ++nlock) { 38 | InitializeCriticalSection(&goopenssl_locks[nlock]); 39 | } 40 | 41 | return 0; 42 | } 43 | 44 | void go_thread_locking_callback(int mode, int n, const char *file, 45 | int line) { 46 | if (mode & CRYPTO_LOCK) { 47 | EnterCriticalSection(&goopenssl_locks[n]); 48 | } else { 49 | LeaveCriticalSection(&goopenssl_locks[n]); 50 | } 51 | } 52 | 53 | unsigned long go_thread_id_callback(void) { 54 | return (unsigned long)GetCurrentThreadId(); 55 | } 56 | */ 57 | import "C" 58 | -------------------------------------------------------------------------------- /key.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | // #include "shim.h" 18 | import "C" 19 | 20 | import ( 21 | "errors" 22 | "io" 23 | "runtime" 24 | "unsafe" 25 | ) 26 | 27 | var ( // some (effectively) constants for tests to refer to 28 | ed25519_support = C.X_ED25519_SUPPORT != 0 29 | ) 30 | 31 | type Method *C.EVP_MD 32 | 33 | var ( 34 | SHA1_Method Method = C.X_EVP_sha1() 35 | SHA256_Method Method = C.X_EVP_sha256() 36 | SHA512_Method Method = C.X_EVP_sha512() 37 | ) 38 | 39 | // Constants for the various key types. 40 | // Mapping of name -> NID taken from openssl/evp.h 41 | const ( 42 | KeyTypeNone = NID_undef 43 | KeyTypeRSA = NID_rsaEncryption 44 | KeyTypeRSA2 = NID_rsa 45 | KeyTypeDSA = NID_dsa 46 | KeyTypeDSA1 = NID_dsa_2 47 | KeyTypeDSA2 = NID_dsaWithSHA 48 | KeyTypeDSA3 = NID_dsaWithSHA1 49 | KeyTypeDSA4 = NID_dsaWithSHA1_2 50 | KeyTypeDH = NID_dhKeyAgreement 51 | KeyTypeDHX = NID_dhpublicnumber 52 | KeyTypeEC = NID_X9_62_id_ecPublicKey 53 | KeyTypeHMAC = NID_hmac 54 | KeyTypeCMAC = NID_cmac 55 | KeyTypeTLS1PRF = NID_tls1_prf 56 | KeyTypeHKDF = NID_hkdf 57 | KeyTypeX25519 = NID_X25519 58 | KeyTypeX448 = NID_X448 59 | KeyTypeED25519 = NID_ED25519 60 | KeyTypeED448 = NID_ED448 61 | ) 62 | 63 | type PublicKey interface { 64 | // Verifies the data signature using PKCS1.15 65 | VerifyPKCS1v15(method Method, data, sig []byte) error 66 | 67 | // MarshalPKIXPublicKeyPEM converts the public key to PEM-encoded PKIX 68 | // format 69 | MarshalPKIXPublicKeyPEM() (pem_block []byte, err error) 70 | 71 | // MarshalPKIXPublicKeyDER converts the public key to DER-encoded PKIX 72 | // format 73 | MarshalPKIXPublicKeyDER() (der_block []byte, err error) 74 | 75 | // KeyType returns an identifier for what kind of key is represented by this 76 | // object. 77 | KeyType() NID 78 | 79 | // BaseType returns an identifier for what kind of key is represented 80 | // by this object. 81 | // Keys that share same algorithm but use different legacy formats 82 | // will have the same BaseType. 83 | // 84 | // For example, a key with a `KeyType() == KeyTypeRSA` and a key with a 85 | // `KeyType() == KeyTypeRSA2` would both have `BaseType() == KeyTypeRSA`. 86 | BaseType() NID 87 | 88 | // Equal compares the key with the passed in key. 89 | Equal(key PublicKey) bool 90 | 91 | // Size returns the size (in bytes) of signatures created with this key. 92 | Size() int 93 | 94 | evpPKey() *C.EVP_PKEY 95 | } 96 | 97 | type PrivateKey interface { 98 | PublicKey 99 | 100 | // Signs the data using PKCS1.15 101 | SignPKCS1v15(Method, []byte) ([]byte, error) 102 | 103 | // MarshalPKCS1PrivateKeyPEM converts the private key to PEM-encoded PKCS1 104 | // format 105 | MarshalPKCS1PrivateKeyPEM() (pem_block []byte, err error) 106 | 107 | // MarshalPKCS1PrivateKeyDER converts the private key to DER-encoded PKCS1 108 | // format 109 | MarshalPKCS1PrivateKeyDER() (der_block []byte, err error) 110 | } 111 | 112 | type pKey struct { 113 | key *C.EVP_PKEY 114 | } 115 | 116 | func (key *pKey) evpPKey() *C.EVP_PKEY { return key.key } 117 | 118 | func (key *pKey) Equal(other PublicKey) bool { 119 | return C.EVP_PKEY_cmp(key.key, other.evpPKey()) == 1 120 | } 121 | 122 | func (key *pKey) KeyType() NID { 123 | return NID(C.EVP_PKEY_id(key.key)) 124 | } 125 | 126 | func (key *pKey) Size() int { 127 | return int(C.EVP_PKEY_size(key.key)) 128 | } 129 | 130 | func (key *pKey) BaseType() NID { 131 | return NID(C.EVP_PKEY_base_id(key.key)) 132 | } 133 | 134 | func (key *pKey) SignPKCS1v15(method Method, data []byte) ([]byte, error) { 135 | 136 | ctx := C.X_EVP_MD_CTX_new() 137 | defer C.X_EVP_MD_CTX_free(ctx) 138 | 139 | if key.KeyType() == KeyTypeED25519 { 140 | // do ED specific one-shot sign 141 | 142 | if method != nil || len(data) == 0 { 143 | return nil, errors.New("signpkcs1v15: 0-length data or non-null digest") 144 | } 145 | 146 | if C.X_EVP_DigestSignInit(ctx, nil, nil, nil, key.key) != 1 { 147 | return nil, errors.New("signpkcs1v15: failed to init signature") 148 | } 149 | 150 | // evp signatures are 64 bytes 151 | sig := make([]byte, 64) 152 | var sigblen C.size_t = 64 153 | if C.X_EVP_DigestSign(ctx, 154 | (*C.uchar)(unsafe.Pointer(&sig[0])), 155 | &sigblen, 156 | (*C.uchar)(unsafe.Pointer(&data[0])), 157 | C.size_t(len(data))) != 1 { 158 | return nil, errors.New("signpkcs1v15: failed to do one-shot signature") 159 | } 160 | 161 | return sig[:sigblen], nil 162 | } else { 163 | if C.X_EVP_SignInit(ctx, method) != 1 { 164 | return nil, errors.New("signpkcs1v15: failed to init signature") 165 | } 166 | if len(data) > 0 { 167 | if C.X_EVP_SignUpdate( 168 | ctx, unsafe.Pointer(&data[0]), C.uint(len(data))) != 1 { 169 | return nil, errors.New("signpkcs1v15: failed to update signature") 170 | } 171 | } 172 | sig := make([]byte, C.X_EVP_PKEY_size(key.key)) 173 | var sigblen C.uint 174 | if C.X_EVP_SignFinal(ctx, 175 | (*C.uchar)(unsafe.Pointer(&sig[0])), &sigblen, key.key) != 1 { 176 | return nil, errors.New("signpkcs1v15: failed to finalize signature") 177 | } 178 | return sig[:sigblen], nil 179 | } 180 | } 181 | 182 | func (key *pKey) VerifyPKCS1v15(method Method, data, sig []byte) error { 183 | ctx := C.X_EVP_MD_CTX_new() 184 | defer C.X_EVP_MD_CTX_free(ctx) 185 | 186 | if len(sig) == 0 { 187 | return errors.New("verifypkcs1v15: 0-length sig") 188 | } 189 | 190 | if key.KeyType() == KeyTypeED25519 { 191 | // do ED specific one-shot sign 192 | 193 | if method != nil || len(data) == 0 { 194 | return errors.New("verifypkcs1v15: 0-length data or non-null digest") 195 | } 196 | 197 | if C.X_EVP_DigestVerifyInit(ctx, nil, nil, nil, key.key) != 1 { 198 | return errors.New("verifypkcs1v15: failed to init verify") 199 | } 200 | 201 | if C.X_EVP_DigestVerify(ctx, 202 | (*C.uchar)(unsafe.Pointer(&sig[0])), 203 | C.size_t(len(sig)), 204 | (*C.uchar)(unsafe.Pointer(&data[0])), 205 | C.size_t(len(data))) != 1 { 206 | return errors.New("verifypkcs1v15: failed to do one-shot verify") 207 | } 208 | 209 | return nil 210 | 211 | } else { 212 | if C.X_EVP_VerifyInit(ctx, method) != 1 { 213 | return errors.New("verifypkcs1v15: failed to init verify") 214 | } 215 | if len(data) > 0 { 216 | if C.X_EVP_VerifyUpdate( 217 | ctx, unsafe.Pointer(&data[0]), C.uint(len(data))) != 1 { 218 | return errors.New("verifypkcs1v15: failed to update verify") 219 | } 220 | } 221 | if C.X_EVP_VerifyFinal(ctx, 222 | (*C.uchar)(unsafe.Pointer(&sig[0])), C.uint(len(sig)), key.key) != 1 { 223 | return errors.New("verifypkcs1v15: failed to finalize verify") 224 | } 225 | return nil 226 | } 227 | } 228 | 229 | func (key *pKey) MarshalPKCS1PrivateKeyPEM() (pem_block []byte, 230 | err error) { 231 | bio := C.BIO_new(C.BIO_s_mem()) 232 | if bio == nil { 233 | return nil, errors.New("failed to allocate memory BIO") 234 | } 235 | defer C.BIO_free(bio) 236 | 237 | // PEM_write_bio_PrivateKey_traditional will use the key-specific PKCS1 238 | // format if one is available for that key type, otherwise it will encode 239 | // to a PKCS8 key. 240 | if int(C.X_PEM_write_bio_PrivateKey_traditional(bio, key.key, nil, nil, 241 | C.int(0), nil, nil)) != 1 { 242 | return nil, errors.New("failed dumping private key") 243 | } 244 | 245 | return io.ReadAll(asAnyBio(bio)) 246 | } 247 | 248 | func (key *pKey) MarshalPKCS1PrivateKeyDER() (der_block []byte, 249 | err error) { 250 | bio := C.BIO_new(C.BIO_s_mem()) 251 | if bio == nil { 252 | return nil, errors.New("failed to allocate memory BIO") 253 | } 254 | defer C.BIO_free(bio) 255 | 256 | if int(C.i2d_PrivateKey_bio(bio, key.key)) != 1 { 257 | return nil, errors.New("failed dumping private key der") 258 | } 259 | 260 | return io.ReadAll(asAnyBio(bio)) 261 | } 262 | 263 | func (key *pKey) MarshalPKIXPublicKeyPEM() (pem_block []byte, 264 | err error) { 265 | bio := C.BIO_new(C.BIO_s_mem()) 266 | if bio == nil { 267 | return nil, errors.New("failed to allocate memory BIO") 268 | } 269 | defer C.BIO_free(bio) 270 | 271 | if int(C.PEM_write_bio_PUBKEY(bio, key.key)) != 1 { 272 | return nil, errors.New("failed dumping public key pem") 273 | } 274 | 275 | return io.ReadAll(asAnyBio(bio)) 276 | } 277 | 278 | func (key *pKey) MarshalPKIXPublicKeyDER() (der_block []byte, 279 | err error) { 280 | bio := C.BIO_new(C.BIO_s_mem()) 281 | if bio == nil { 282 | return nil, errors.New("failed to allocate memory BIO") 283 | } 284 | defer C.BIO_free(bio) 285 | 286 | if int(C.i2d_PUBKEY_bio(bio, key.key)) != 1 { 287 | return nil, errors.New("failed dumping public key der") 288 | } 289 | 290 | return io.ReadAll(asAnyBio(bio)) 291 | } 292 | 293 | // LoadPrivateKeyFromPEM loads a private key from a PEM-encoded block. 294 | func LoadPrivateKeyFromPEM(pem_block []byte) (PrivateKey, error) { 295 | if len(pem_block) == 0 { 296 | return nil, errors.New("empty pem block") 297 | } 298 | bio := C.BIO_new_mem_buf(unsafe.Pointer(&pem_block[0]), 299 | C.int(len(pem_block))) 300 | if bio == nil { 301 | return nil, errors.New("failed creating bio") 302 | } 303 | defer C.BIO_free(bio) 304 | 305 | key := C.PEM_read_bio_PrivateKey(bio, nil, nil, nil) 306 | if key == nil { 307 | return nil, errors.New("failed reading private key") 308 | } 309 | 310 | p := &pKey{key: key} 311 | runtime.SetFinalizer(p, func(p *pKey) { 312 | C.X_EVP_PKEY_free(p.key) 313 | }) 314 | return p, nil 315 | } 316 | 317 | // LoadPrivateKeyFromPEMWithPassword loads a private key from a PEM-encoded block. 318 | func LoadPrivateKeyFromPEMWithPassword(pem_block []byte, password string) ( 319 | PrivateKey, error) { 320 | if len(pem_block) == 0 { 321 | return nil, errors.New("empty pem block") 322 | } 323 | bio := C.BIO_new_mem_buf(unsafe.Pointer(&pem_block[0]), 324 | C.int(len(pem_block))) 325 | if bio == nil { 326 | return nil, errors.New("failed creating bio") 327 | } 328 | defer C.BIO_free(bio) 329 | cs := C.CString(password) 330 | defer C.free(unsafe.Pointer(cs)) 331 | key := C.PEM_read_bio_PrivateKey(bio, nil, nil, unsafe.Pointer(cs)) 332 | if key == nil { 333 | return nil, errors.New("failed reading private key") 334 | } 335 | 336 | p := &pKey{key: key} 337 | runtime.SetFinalizer(p, func(p *pKey) { 338 | C.X_EVP_PKEY_free(p.key) 339 | }) 340 | return p, nil 341 | } 342 | 343 | // LoadPrivateKeyFromDER loads a private key from a DER-encoded block. 344 | func LoadPrivateKeyFromDER(der_block []byte) (PrivateKey, error) { 345 | if len(der_block) == 0 { 346 | return nil, errors.New("empty der block") 347 | } 348 | bio := C.BIO_new_mem_buf(unsafe.Pointer(&der_block[0]), 349 | C.int(len(der_block))) 350 | if bio == nil { 351 | return nil, errors.New("failed creating bio") 352 | } 353 | defer C.BIO_free(bio) 354 | 355 | key := C.d2i_PrivateKey_bio(bio, nil) 356 | if key == nil { 357 | return nil, errors.New("failed reading private key der") 358 | } 359 | 360 | p := &pKey{key: key} 361 | runtime.SetFinalizer(p, func(p *pKey) { 362 | C.X_EVP_PKEY_free(p.key) 363 | }) 364 | return p, nil 365 | } 366 | 367 | // LoadPrivateKeyFromPEMWidthPassword loads a private key from a PEM-encoded block. 368 | // Backwards-compatible with typo 369 | func LoadPrivateKeyFromPEMWidthPassword(pem_block []byte, password string) ( 370 | PrivateKey, error) { 371 | return LoadPrivateKeyFromPEMWithPassword(pem_block, password) 372 | } 373 | 374 | // LoadPublicKeyFromPEM loads a public key from a PEM-encoded block. 375 | func LoadPublicKeyFromPEM(pem_block []byte) (PublicKey, error) { 376 | if len(pem_block) == 0 { 377 | return nil, errors.New("empty pem block") 378 | } 379 | bio := C.BIO_new_mem_buf(unsafe.Pointer(&pem_block[0]), 380 | C.int(len(pem_block))) 381 | if bio == nil { 382 | return nil, errors.New("failed creating bio") 383 | } 384 | defer C.BIO_free(bio) 385 | 386 | key := C.PEM_read_bio_PUBKEY(bio, nil, nil, nil) 387 | if key == nil { 388 | return nil, errors.New("failed reading public key der") 389 | } 390 | 391 | p := &pKey{key: key} 392 | runtime.SetFinalizer(p, func(p *pKey) { 393 | C.X_EVP_PKEY_free(p.key) 394 | }) 395 | return p, nil 396 | } 397 | 398 | // LoadPublicKeyFromDER loads a public key from a DER-encoded block. 399 | func LoadPublicKeyFromDER(der_block []byte) (PublicKey, error) { 400 | if len(der_block) == 0 { 401 | return nil, errors.New("empty der block") 402 | } 403 | bio := C.BIO_new_mem_buf(unsafe.Pointer(&der_block[0]), 404 | C.int(len(der_block))) 405 | if bio == nil { 406 | return nil, errors.New("failed creating bio") 407 | } 408 | defer C.BIO_free(bio) 409 | 410 | key := C.d2i_PUBKEY_bio(bio, nil) 411 | if key == nil { 412 | return nil, errors.New("failed reading public key der") 413 | } 414 | 415 | p := &pKey{key: key} 416 | runtime.SetFinalizer(p, func(p *pKey) { 417 | C.X_EVP_PKEY_free(p.key) 418 | }) 419 | return p, nil 420 | } 421 | 422 | // GenerateRSAKey generates a new RSA private key with an exponent of 3. 423 | func GenerateRSAKey(bits int) (PrivateKey, error) { 424 | return GenerateRSAKeyWithExponent(bits, 3) 425 | } 426 | 427 | // GenerateRSAKeyWithExponent generates a new RSA private key. 428 | func GenerateRSAKeyWithExponent(bits int, exponent int) (PrivateKey, error) { 429 | rsa := C.RSA_generate_key(C.int(bits), C.ulong(exponent), nil, nil) 430 | if rsa == nil { 431 | return nil, errors.New("failed to generate RSA key") 432 | } 433 | key := C.X_EVP_PKEY_new() 434 | if key == nil { 435 | return nil, errors.New("failed to allocate EVP_PKEY") 436 | } 437 | if C.X_EVP_PKEY_assign_charp(key, C.EVP_PKEY_RSA, (*C.char)(unsafe.Pointer(rsa))) != 1 { 438 | C.X_EVP_PKEY_free(key) 439 | return nil, errors.New("failed to assign RSA key") 440 | } 441 | p := &pKey{key: key} 442 | runtime.SetFinalizer(p, func(p *pKey) { 443 | C.X_EVP_PKEY_free(p.key) 444 | }) 445 | return p, nil 446 | } 447 | 448 | // GenerateECKey generates a new elliptic curve private key on the speicified 449 | // curve. 450 | func GenerateECKey(curve EllipticCurve) (PrivateKey, error) { 451 | 452 | // Create context for parameter generation 453 | paramCtx := C.EVP_PKEY_CTX_new_id(C.EVP_PKEY_EC, nil) 454 | if paramCtx == nil { 455 | return nil, errors.New("failed creating EC parameter generation context") 456 | } 457 | defer C.EVP_PKEY_CTX_free(paramCtx) 458 | 459 | // Intialize the parameter generation 460 | if int(C.EVP_PKEY_paramgen_init(paramCtx)) != 1 { 461 | return nil, errors.New("failed initializing EC parameter generation context") 462 | } 463 | 464 | // Set curve in EC parameter generation context 465 | if int(C.X_EVP_PKEY_CTX_set_ec_paramgen_curve_nid(paramCtx, C.int(curve))) != 1 { 466 | return nil, errors.New("failed setting curve in EC parameter generation context") 467 | } 468 | 469 | // Create parameter object 470 | var params *C.EVP_PKEY 471 | if int(C.EVP_PKEY_paramgen(paramCtx, ¶ms)) != 1 { 472 | return nil, errors.New("failed creating EC key generation parameters") 473 | } 474 | defer C.EVP_PKEY_free(params) 475 | 476 | // Create context for the key generation 477 | keyCtx := C.EVP_PKEY_CTX_new(params, nil) 478 | if keyCtx == nil { 479 | return nil, errors.New("failed creating EC key generation context") 480 | } 481 | defer C.EVP_PKEY_CTX_free(keyCtx) 482 | 483 | // Generate the key 484 | var privKey *C.EVP_PKEY 485 | if int(C.EVP_PKEY_keygen_init(keyCtx)) != 1 { 486 | return nil, errors.New("failed initializing EC key generation context") 487 | } 488 | if int(C.EVP_PKEY_keygen(keyCtx, &privKey)) != 1 { 489 | return nil, errors.New("failed generating EC private key") 490 | } 491 | 492 | p := &pKey{key: privKey} 493 | runtime.SetFinalizer(p, func(p *pKey) { 494 | C.X_EVP_PKEY_free(p.key) 495 | }) 496 | return p, nil 497 | } 498 | 499 | // GenerateED25519Key generates a Ed25519 key 500 | func GenerateED25519Key() (PrivateKey, error) { 501 | // Key context 502 | keyCtx := C.EVP_PKEY_CTX_new_id(C.X_EVP_PKEY_ED25519, nil) 503 | if keyCtx == nil { 504 | return nil, errors.New("failed creating EC parameter generation context") 505 | } 506 | defer C.EVP_PKEY_CTX_free(keyCtx) 507 | 508 | // Generate the key 509 | var privKey *C.EVP_PKEY 510 | if int(C.EVP_PKEY_keygen_init(keyCtx)) != 1 { 511 | return nil, errors.New("failed initializing ED25519 key generation context") 512 | } 513 | if int(C.EVP_PKEY_keygen(keyCtx, &privKey)) != 1 { 514 | return nil, errors.New("failed generating ED25519 private key") 515 | } 516 | 517 | p := &pKey{key: privKey} 518 | runtime.SetFinalizer(p, func(p *pKey) { 519 | C.X_EVP_PKEY_free(p.key) 520 | }) 521 | return p, nil 522 | } 523 | -------------------------------------------------------------------------------- /key_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | import ( 18 | "bytes" 19 | "crypto/ecdsa" 20 | "crypto/rsa" 21 | "crypto/tls" 22 | "crypto/x509" 23 | "encoding/hex" 24 | pem_pkg "encoding/pem" 25 | "os" 26 | "testing" 27 | ) 28 | 29 | func TestMarshal(t *testing.T) { 30 | key, err := LoadPrivateKeyFromPEM(keyBytes) 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | cert, err := LoadCertificateFromPEM(certBytes) 35 | if err != nil { 36 | t.Fatal(err) 37 | } 38 | 39 | if !key.Equal(key) { 40 | t.Fatal("key not equal to itself") 41 | } 42 | 43 | privateBlock, _ := pem_pkg.Decode(keyBytes) 44 | key, err = LoadPrivateKeyFromDER(privateBlock.Bytes) 45 | if err != nil { 46 | t.Fatal(err) 47 | } 48 | 49 | pem, err := cert.MarshalPEM() 50 | if err != nil { 51 | t.Fatal(err) 52 | } 53 | if !bytes.Equal(pem, certBytes) { 54 | os.WriteFile("generated", pem, 0644) 55 | os.WriteFile("hardcoded", certBytes, 0644) 56 | t.Fatal("invalid cert pem bytes") 57 | } 58 | 59 | pem, err = key.MarshalPKCS1PrivateKeyPEM() 60 | if err != nil { 61 | t.Fatal(err) 62 | } 63 | if !bytes.Equal(pem, keyBytes) { 64 | os.WriteFile("generated", pem, 0644) 65 | os.WriteFile("hardcoded", keyBytes, 0644) 66 | t.Fatal("invalid private key pem bytes") 67 | } 68 | tls_cert, err := tls.X509KeyPair(certBytes, keyBytes) 69 | if err != nil { 70 | t.Fatal(err) 71 | } 72 | tls_key, ok := tls_cert.PrivateKey.(*rsa.PrivateKey) 73 | if !ok { 74 | t.Fatal("FASDFASDF") 75 | } 76 | _ = tls_key 77 | 78 | der, err := key.MarshalPKCS1PrivateKeyDER() 79 | if err != nil { 80 | t.Fatal(err) 81 | } 82 | tls_der := x509.MarshalPKCS1PrivateKey(tls_key) 83 | if !bytes.Equal(der, tls_der) { 84 | t.Fatalf("invalid private key der bytes: %s\n v.s. %s\n", 85 | hex.Dump(der), hex.Dump(tls_der)) 86 | } 87 | 88 | der, err = key.MarshalPKIXPublicKeyDER() 89 | if err != nil { 90 | t.Fatal(err) 91 | } 92 | tls_der, err = x509.MarshalPKIXPublicKey(&tls_key.PublicKey) 93 | if err != nil { 94 | t.Fatal(err) 95 | } 96 | if !bytes.Equal(der, tls_der) { 97 | os.WriteFile("generated", []byte(hex.Dump(der)), 0644) 98 | os.WriteFile("hardcoded", []byte(hex.Dump(tls_der)), 0644) 99 | t.Fatal("invalid public key der bytes") 100 | } 101 | 102 | pem, err = key.MarshalPKIXPublicKeyPEM() 103 | if err != nil { 104 | t.Fatal(err) 105 | } 106 | tls_pem := pem_pkg.EncodeToMemory(&pem_pkg.Block{ 107 | Type: "PUBLIC KEY", Bytes: tls_der}) 108 | if !bytes.Equal(pem, tls_pem) { 109 | os.WriteFile("generated", pem, 0644) 110 | os.WriteFile("hardcoded", tls_pem, 0644) 111 | t.Fatal("invalid public key pem bytes") 112 | } 113 | 114 | loaded_pubkey_from_pem, err := LoadPublicKeyFromPEM(pem) 115 | if err != nil { 116 | t.Fatal(err) 117 | } 118 | 119 | loaded_pubkey_from_der, err := LoadPublicKeyFromDER(der) 120 | if err != nil { 121 | t.Fatal(err) 122 | } 123 | 124 | new_der_from_pem, err := loaded_pubkey_from_pem.MarshalPKIXPublicKeyDER() 125 | if err != nil { 126 | t.Fatal(err) 127 | } 128 | 129 | new_der_from_der, err := loaded_pubkey_from_der.MarshalPKIXPublicKeyDER() 130 | if err != nil { 131 | t.Fatal(err) 132 | } 133 | 134 | if !bytes.Equal(new_der_from_der, tls_der) { 135 | os.WriteFile("generated", []byte(hex.Dump(new_der_from_der)), 0644) 136 | os.WriteFile("hardcoded", []byte(hex.Dump(tls_der)), 0644) 137 | t.Fatal("invalid public key der bytes") 138 | } 139 | 140 | if !bytes.Equal(new_der_from_pem, tls_der) { 141 | os.WriteFile("generated", []byte(hex.Dump(new_der_from_pem)), 0644) 142 | os.WriteFile("hardcoded", []byte(hex.Dump(tls_der)), 0644) 143 | t.Fatal("invalid public key der bytes") 144 | } 145 | } 146 | 147 | func TestGenerate(t *testing.T) { 148 | key, err := GenerateRSAKey(2048) 149 | if err != nil { 150 | t.Fatal(err) 151 | } 152 | _, err = key.MarshalPKIXPublicKeyPEM() 153 | if err != nil { 154 | t.Fatal(err) 155 | } 156 | _, err = key.MarshalPKCS1PrivateKeyPEM() 157 | if err != nil { 158 | t.Fatal(err) 159 | } 160 | _, err = GenerateRSAKeyWithExponent(1024, 65537) 161 | if err != nil { 162 | t.Fatal(err) 163 | } 164 | } 165 | 166 | func TestGenerateEC(t *testing.T) { 167 | key, err := GenerateECKey(Prime256v1) 168 | if err != nil { 169 | t.Fatal(err) 170 | } 171 | _, err = key.MarshalPKIXPublicKeyPEM() 172 | if err != nil { 173 | t.Fatal(err) 174 | } 175 | _, err = key.MarshalPKCS1PrivateKeyPEM() 176 | if err != nil { 177 | t.Fatal(err) 178 | } 179 | } 180 | 181 | func TestGenerateEd25519(t *testing.T) { 182 | if !ed25519_support { 183 | t.SkipNow() 184 | } 185 | 186 | key, err := GenerateED25519Key() 187 | if err != nil { 188 | t.Fatal(err) 189 | } 190 | if _, err = key.MarshalPKIXPublicKeyPEM(); err != nil { 191 | t.Fatal(err) 192 | } 193 | } 194 | 195 | func TestSign(t *testing.T) { 196 | key, _ := GenerateRSAKey(1024) 197 | data := []byte("the quick brown fox jumps over the lazy dog") 198 | _, err := key.SignPKCS1v15(SHA1_Method, data) 199 | if err != nil { 200 | t.Fatal(err) 201 | } 202 | _, err = key.SignPKCS1v15(SHA256_Method, data) 203 | if err != nil { 204 | t.Fatal(err) 205 | } 206 | _, err = key.SignPKCS1v15(SHA512_Method, data) 207 | if err != nil { 208 | t.Fatal(err) 209 | } 210 | } 211 | 212 | func TestSignEC(t *testing.T) { 213 | t.Parallel() 214 | 215 | key, err := GenerateECKey(Prime256v1) 216 | if err != nil { 217 | t.Fatal(err) 218 | } 219 | data := []byte("the quick brown fox jumps over the lazy dog") 220 | 221 | t.Run("sha1", func(t *testing.T) { 222 | t.Parallel() 223 | sig, err := key.SignPKCS1v15(SHA1_Method, data) 224 | if err != nil { 225 | t.Fatal(err) 226 | } 227 | err = key.VerifyPKCS1v15(SHA1_Method, data, sig) 228 | if err != nil { 229 | t.Fatal(err) 230 | } 231 | }) 232 | 233 | t.Run("sha256", func(t *testing.T) { 234 | t.Parallel() 235 | sig, err := key.SignPKCS1v15(SHA256_Method, data) 236 | if err != nil { 237 | t.Fatal(err) 238 | } 239 | err = key.VerifyPKCS1v15(SHA256_Method, data, sig) 240 | if err != nil { 241 | t.Fatal(err) 242 | } 243 | }) 244 | 245 | t.Run("sha512", func(t *testing.T) { 246 | t.Parallel() 247 | sig, err := key.SignPKCS1v15(SHA512_Method, data) 248 | if err != nil { 249 | t.Fatal(err) 250 | } 251 | err = key.VerifyPKCS1v15(SHA512_Method, data, sig) 252 | if err != nil { 253 | t.Fatal(err) 254 | } 255 | }) 256 | } 257 | 258 | func TestSignED25519(t *testing.T) { 259 | if !ed25519_support { 260 | t.SkipNow() 261 | } 262 | 263 | t.Parallel() 264 | 265 | key, err := GenerateED25519Key() 266 | if err != nil { 267 | t.Fatal(err) 268 | } 269 | data := []byte("the quick brown fox jumps over the lazy dog") 270 | 271 | t.Run("new", func(t *testing.T) { 272 | t.Parallel() 273 | sig, err := key.SignPKCS1v15(nil, data) 274 | if err != nil { 275 | t.Fatal(err) 276 | } 277 | 278 | err = key.VerifyPKCS1v15(nil, data, sig) 279 | if err != nil { 280 | t.Fatal(err) 281 | } 282 | 283 | }) 284 | } 285 | 286 | func TestMarshalEC(t *testing.T) { 287 | if _, err := LoadPrivateKeyFromPEM(prime256v1KeyBytes); err != nil { 288 | t.Fatal(err) 289 | } 290 | cert, err := LoadCertificateFromPEM(prime256v1CertBytes) 291 | if err != nil { 292 | t.Fatal(err) 293 | } 294 | 295 | privateBlock, _ := pem_pkg.Decode(prime256v1KeyBytes) 296 | key, err := LoadPrivateKeyFromDER(privateBlock.Bytes) 297 | if err != nil { 298 | t.Fatal(err) 299 | } 300 | 301 | pem, err := cert.MarshalPEM() 302 | if err != nil { 303 | t.Fatal(err) 304 | } 305 | if !bytes.Equal(pem, prime256v1CertBytes) { 306 | os.WriteFile("generated", pem, 0644) 307 | os.WriteFile("hardcoded", prime256v1CertBytes, 0644) 308 | t.Fatal("invalid cert pem bytes") 309 | } 310 | 311 | pem, err = key.MarshalPKCS1PrivateKeyPEM() 312 | if err != nil { 313 | t.Fatal(err) 314 | } 315 | if !bytes.Equal(pem, prime256v1KeyBytes) { 316 | os.WriteFile("generated", pem, 0644) 317 | os.WriteFile("hardcoded", prime256v1KeyBytes, 0644) 318 | t.Fatal("invalid private key pem bytes") 319 | } 320 | tls_cert, err := tls.X509KeyPair(prime256v1CertBytes, prime256v1KeyBytes) 321 | if err != nil { 322 | t.Fatal(err) 323 | } 324 | tls_key, ok := tls_cert.PrivateKey.(*ecdsa.PrivateKey) 325 | if !ok { 326 | t.Fatal("FASDFASDF") 327 | } 328 | _ = tls_key 329 | 330 | der, err := key.MarshalPKCS1PrivateKeyDER() 331 | if err != nil { 332 | t.Fatal(err) 333 | } 334 | tls_der, err := x509.MarshalECPrivateKey(tls_key) 335 | if err != nil { 336 | t.Fatal(err) 337 | } 338 | if !bytes.Equal(der, tls_der) { 339 | t.Fatalf("invalid private key der bytes: %s\n v.s. %s\n", 340 | hex.Dump(der), hex.Dump(tls_der)) 341 | } 342 | 343 | der, err = key.MarshalPKIXPublicKeyDER() 344 | if err != nil { 345 | t.Fatal(err) 346 | } 347 | tls_der, err = x509.MarshalPKIXPublicKey(&tls_key.PublicKey) 348 | if err != nil { 349 | t.Fatal(err) 350 | } 351 | if !bytes.Equal(der, tls_der) { 352 | os.WriteFile("generated", []byte(hex.Dump(der)), 0644) 353 | os.WriteFile("hardcoded", []byte(hex.Dump(tls_der)), 0644) 354 | t.Fatal("invalid public key der bytes") 355 | } 356 | 357 | pem, err = key.MarshalPKIXPublicKeyPEM() 358 | if err != nil { 359 | t.Fatal(err) 360 | } 361 | tls_pem := pem_pkg.EncodeToMemory(&pem_pkg.Block{ 362 | Type: "PUBLIC KEY", Bytes: tls_der}) 363 | if !bytes.Equal(pem, tls_pem) { 364 | os.WriteFile("generated", pem, 0644) 365 | os.WriteFile("hardcoded", tls_pem, 0644) 366 | t.Fatal("invalid public key pem bytes") 367 | } 368 | 369 | loaded_pubkey_from_pem, err := LoadPublicKeyFromPEM(pem) 370 | if err != nil { 371 | t.Fatal(err) 372 | } 373 | 374 | loaded_pubkey_from_der, err := LoadPublicKeyFromDER(der) 375 | if err != nil { 376 | t.Fatal(err) 377 | } 378 | 379 | new_der_from_pem, err := loaded_pubkey_from_pem.MarshalPKIXPublicKeyDER() 380 | if err != nil { 381 | t.Fatal(err) 382 | } 383 | 384 | new_der_from_der, err := loaded_pubkey_from_der.MarshalPKIXPublicKeyDER() 385 | if err != nil { 386 | t.Fatal(err) 387 | } 388 | 389 | if !bytes.Equal(new_der_from_der, tls_der) { 390 | os.WriteFile("generated", []byte(hex.Dump(new_der_from_der)), 0644) 391 | os.WriteFile("hardcoded", []byte(hex.Dump(tls_der)), 0644) 392 | t.Fatal("invalid public key der bytes") 393 | } 394 | 395 | if !bytes.Equal(new_der_from_pem, tls_der) { 396 | os.WriteFile("generated", []byte(hex.Dump(new_der_from_pem)), 0644) 397 | os.WriteFile("hardcoded", []byte(hex.Dump(tls_der)), 0644) 398 | t.Fatal("invalid public key der bytes") 399 | } 400 | } 401 | 402 | func TestMarshalEd25519(t *testing.T) { 403 | if !ed25519_support { 404 | t.SkipNow() 405 | } 406 | 407 | if _, err := LoadPrivateKeyFromPEM(ed25519KeyBytes); err != nil { 408 | t.Fatal(err) 409 | } 410 | cert, err := LoadCertificateFromPEM(ed25519CertBytes) 411 | if err != nil { 412 | t.Fatal(err) 413 | } 414 | 415 | privateBlock, _ := pem_pkg.Decode(ed25519KeyBytes) 416 | key, err := LoadPrivateKeyFromDER(privateBlock.Bytes) 417 | if err != nil { 418 | t.Fatal(err) 419 | } 420 | 421 | pem, err := cert.MarshalPEM() 422 | if err != nil { 423 | t.Fatal(err) 424 | } 425 | if !bytes.Equal(pem, ed25519CertBytes) { 426 | os.WriteFile("generated", pem, 0644) 427 | os.WriteFile("hardcoded", ed25519CertBytes, 0644) 428 | t.Fatal("invalid cert pem bytes") 429 | } 430 | 431 | // NOTE: Ed25519 cannot be marshalled to PEM. 432 | 433 | if _, err := key.MarshalPKCS1PrivateKeyDER(); err != nil { 434 | t.Fatal(err) 435 | } 436 | 437 | der, err := key.MarshalPKIXPublicKeyDER() 438 | if err != nil { 439 | t.Fatal(err) 440 | } 441 | 442 | pem, err = key.MarshalPKIXPublicKeyPEM() 443 | if err != nil { 444 | t.Fatal(err) 445 | } 446 | 447 | loadedPubkeyFromPem, err := LoadPublicKeyFromPEM(pem) 448 | if err != nil { 449 | t.Fatal(err) 450 | } 451 | 452 | loadedPubkeyFromDer, err := LoadPublicKeyFromDER(der) 453 | if err != nil { 454 | t.Fatal(err) 455 | } 456 | 457 | _, err = loadedPubkeyFromPem.MarshalPKIXPublicKeyDER() 458 | if err != nil { 459 | t.Fatal(err) 460 | } 461 | 462 | _, err = loadedPubkeyFromDer.MarshalPKIXPublicKeyDER() 463 | if err != nil { 464 | t.Fatal(err) 465 | } 466 | } 467 | -------------------------------------------------------------------------------- /mapping.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | import ( 18 | "sync" 19 | "unsafe" 20 | ) 21 | 22 | // #include 23 | import "C" 24 | 25 | type mapping struct { 26 | lock sync.Mutex 27 | values map[token]unsafe.Pointer 28 | } 29 | 30 | func newMapping() *mapping { 31 | return &mapping{ 32 | values: make(map[token]unsafe.Pointer), 33 | } 34 | } 35 | 36 | type token unsafe.Pointer 37 | 38 | func (m *mapping) Add(x unsafe.Pointer) token { 39 | res := token(C.malloc(1)) 40 | 41 | m.lock.Lock() 42 | m.values[res] = x 43 | m.lock.Unlock() 44 | 45 | return res 46 | } 47 | 48 | func (m *mapping) Get(x token) unsafe.Pointer { 49 | m.lock.Lock() 50 | res := m.values[x] 51 | m.lock.Unlock() 52 | 53 | return res 54 | } 55 | 56 | func (m *mapping) Del(x token) { 57 | m.lock.Lock() 58 | delete(m.values, x) 59 | m.lock.Unlock() 60 | 61 | C.free(unsafe.Pointer(x)) 62 | } 63 | -------------------------------------------------------------------------------- /md4.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | // #include "shim.h" 18 | import "C" 19 | 20 | import ( 21 | "errors" 22 | "runtime" 23 | "unsafe" 24 | ) 25 | 26 | type MD4Hash struct { 27 | ctx *C.EVP_MD_CTX 28 | engine *Engine 29 | } 30 | 31 | func NewMD4Hash() (*MD4Hash, error) { return NewMD4HashWithEngine(nil) } 32 | 33 | func NewMD4HashWithEngine(e *Engine) (*MD4Hash, error) { 34 | hash := &MD4Hash{engine: e} 35 | hash.ctx = C.X_EVP_MD_CTX_new() 36 | if hash.ctx == nil { 37 | return nil, errors.New("openssl: md4: unable to allocate ctx") 38 | } 39 | runtime.SetFinalizer(hash, func(hash *MD4Hash) { hash.Close() }) 40 | if err := hash.Reset(); err != nil { 41 | return nil, err 42 | } 43 | return hash, nil 44 | } 45 | 46 | func (s *MD4Hash) Close() { 47 | if s.ctx != nil { 48 | C.X_EVP_MD_CTX_free(s.ctx) 49 | s.ctx = nil 50 | } 51 | } 52 | 53 | func (s *MD4Hash) Reset() error { 54 | runtime.LockOSThread() 55 | defer runtime.UnlockOSThread() 56 | if C.X_EVP_DigestInit_ex(s.ctx, C.X_EVP_md4(), engineRef(s.engine)) != 1 { 57 | return errors.New("openssl: md4: cannot init digest ctx: " + 58 | errorFromErrorQueue().Error()) 59 | } 60 | return nil 61 | } 62 | 63 | func (s *MD4Hash) Write(p []byte) (n int, err error) { 64 | if len(p) == 0 { 65 | return 0, nil 66 | } 67 | if C.X_EVP_DigestUpdate(s.ctx, unsafe.Pointer(&p[0]), 68 | C.size_t(len(p))) != 1 { 69 | return 0, errors.New("openssl: md4: cannot update digest") 70 | } 71 | return len(p), nil 72 | } 73 | 74 | func (s *MD4Hash) Sum() (result [16]byte, err error) { 75 | if C.X_EVP_DigestFinal_ex(s.ctx, 76 | (*C.uchar)(unsafe.Pointer(&result[0])), nil) != 1 { 77 | return result, errors.New("openssl: md4: cannot finalize ctx") 78 | } 79 | return result, s.Reset() 80 | } 81 | 82 | func MD4(data []byte) (result [16]byte, err error) { 83 | hash, err := NewMD4Hash() 84 | if err != nil { 85 | return result, err 86 | } 87 | defer hash.Close() 88 | if _, err := hash.Write(data); err != nil { 89 | return result, err 90 | } 91 | return hash.Sum() 92 | } 93 | -------------------------------------------------------------------------------- /md4_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | import ( 18 | "crypto/rand" 19 | "encoding/hex" 20 | "io" 21 | "testing" 22 | ) 23 | 24 | var md4Examples = []struct{ out, in string }{ 25 | {"31d6cfe0d16ae931b73c59d7e0c089c0", ""}, 26 | {"93875419eca14bbf961e412147839d04", "99"}, 27 | {"89b4c9a073ae963f95370a9a1e897a74", "7b89"}, 28 | {"be47aa5b399de162db079674047af65b", "803c78"}, 29 | {"6d97329845e30e79335fef1eb9d76d5e", "79b08de9"}, 30 | {"7115059e7a77e50cf722afbaad19611d", "42251df47b"}, 31 | {"468d3dfe53a140993d10cb977708c22c", "bd0ae6faa6e8"}, 32 | {"97f951156d4fe99e935b437ea5535a85", "ce511e2fd6e596"}, 33 | {"e50e5be10795a54d45aba39e6af2ecd5", "3f526ded71688ef0"}, 34 | {"c6c6d4ba2070c842f4d4388e3919a75c", "68aa2699f3fd154a5c"}, 35 | {"821fac95ab8a7443e3e0a47711e3e4ae", "133fc4081c3c9d0fe962"}, 36 | {"ac9e63ee02c6fdc097de1e2735cd1415", "f2b43f4e0e4448054e57fc"}, 37 | {"db82e7f6c26468e41b9f34082dcbf976", "ba8d2fd4323e389a23477216"}, 38 | {"bdd168bbe205e36bb852518113247a9e", "3b3b832ff6ff259ab028689bc5"}, 39 | {"c08b6b2eeab0bf3234314eb6a573cd9e", "ef0746f1fba74e018d718e74bdf1"}, 40 | {"0c2b7a7701bbe40f4668677cf3cc9bb9", "d3fd98ccbf59e8f61277ec9a668212"}, 41 | {"ce5087675d227d65de1ae02db2c8aec4", "551705b22d1b12c7056018d8a8468a7b"}, 42 | {"8130c37467524e6ab64999c4bde575a4", "d075bb0ee09f90399508e27d3059619abf"}, 43 | {"736cea235c6e67772a05d5c4c24ed5bb", "53a1880520136e99a5e42235a706ca929c5c"}, 44 | {"cdd0dc65d134efe608113ad57c053e82", "b15c56e59f525c60d563a0064866b2a8550aa8"}, 45 | {"29105f10e7570f7242e0b8f3a3514f82", "f4e6b432479abb4095cd7e4788ce9cf077acc932"}, 46 | {"33f1a70b35b51f42704aec6a01d06f8f", "6b0e95b9a09996809a1ac25142be3d46a01b78c26d"}, 47 | {"bf4a3ebf686409832d92ac0ecf70fc6e", "ee0f76cd3e233058ba311fefc089ce1d9217ab3ae229"}, 48 | {"5359d01e422ce2617f516e48b1693003", "57788a4e5514fbec6a7db6abf3f8d41cba2c843496467b"}, 49 | {"a8ad7335fa62b8f4eb40f711b2793972", "840ca6f027703fe7119bba138bfc399d8c9725854b7a36c1"}, 50 | {"5bf38c84aa17043ca1ac3b6a0d70d6f6", "a8340d43b3c2f19b963f16989b60aab83a5e90415f205fdd99"}, 51 | {"612e96a3a484716f8648874b1b3e8b16", "e66e7141838666e06e01d45dc9eaab610bf46710287b9ad6b40e"}, 52 | {"d436e950d55f25548c86ebf065a5d23e", "e8af98c8e8d86f7df0d65e225c0d48c075fafc27733d28b4a53077"}, 53 | {"b48d7af2252ec0107f5631736d756ff9", "34554eb3f2df01135aab9a157c0de8c5804c9df20b8241b83b7999e1"}, 54 | {"bcd8447e2c1de19c076c8b7a04f7469e", "af7ecf59b66eea345800d48e00e2953eb654efbf433abb27ad2c497a08"}, 55 | {"1eeb6f74cf827b50d285d6749404c5e3", "c0f89522341128dc5e5e73c8b96775d7eb3d550d9786cc88b23479e14d11"}, 56 | {"6e593341e62194911d5cc31e39835f27", "c5e4bc73821faa34adf9468441ffd97520a96cd5debda4d51edcaaf2b23fbd"}, 57 | } 58 | 59 | func skipIfMD4Unsupported(t testing.TB) { 60 | t.Helper() 61 | 62 | hash, err := NewMD4Hash() 63 | if err != nil { 64 | t.Skip("MD4 is not supported by OpenSSL") 65 | } 66 | hash.Close() 67 | } 68 | 69 | func TestMD4Examples(t *testing.T) { 70 | skipIfMD4Unsupported(t) 71 | 72 | for _, ex := range md4Examples { 73 | buf, err := hex.DecodeString(ex.in) 74 | if err != nil { 75 | t.Fatal(err) 76 | } 77 | 78 | got, err := MD4(buf) 79 | if err != nil { 80 | t.Fatal(err) 81 | } 82 | 83 | if hgot := hex.EncodeToString(got[:]); hgot != ex.out { 84 | t.Fatalf("%s: %s != %s", ex.in, hgot, ex.out) 85 | } 86 | } 87 | } 88 | 89 | func TestMD4Writer(t *testing.T) { 90 | skipIfMD4Unsupported(t) 91 | 92 | ohash, err := NewMD4Hash() 93 | if err != nil { 94 | t.Fatal(err) 95 | } 96 | 97 | for _, ex := range md4Examples { 98 | if err := ohash.Reset(); err != nil { 99 | t.Fatal(err) 100 | } 101 | 102 | buf, err := hex.DecodeString(ex.in) 103 | if err != nil { 104 | t.Fatal(err) 105 | } 106 | 107 | if _, err := ohash.Write(buf); err != nil { 108 | t.Fatal(err) 109 | } 110 | 111 | got, err := ohash.Sum() 112 | if err != nil { 113 | t.Fatal(err) 114 | } 115 | 116 | if hgot := hex.EncodeToString(got[:]); hgot != ex.out { 117 | t.Fatalf("%s: %s != %s", ex.in, hgot, ex.out) 118 | } 119 | } 120 | } 121 | 122 | type md4func func([]byte) 123 | 124 | func benchmarkMD4(b *testing.B, length int64, fn md4func) { 125 | buf := make([]byte, length) 126 | if _, err := io.ReadFull(rand.Reader, buf); err != nil { 127 | b.Fatal(err) 128 | } 129 | b.SetBytes(length) 130 | b.ResetTimer() 131 | for i := 0; i < b.N; i++ { 132 | fn(buf) 133 | } 134 | } 135 | 136 | func BenchmarkMD4Large_openssl(b *testing.B) { 137 | skipIfMD4Unsupported(b) 138 | 139 | benchmarkMD4(b, 1024*1024, func(buf []byte) { MD4(buf) }) 140 | } 141 | 142 | func BenchmarkMD4Small_openssl(b *testing.B) { 143 | skipIfMD4Unsupported(b) 144 | 145 | benchmarkMD4(b, 1, func(buf []byte) { MD4(buf) }) 146 | } 147 | -------------------------------------------------------------------------------- /md5.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | // #include "shim.h" 18 | import "C" 19 | 20 | import ( 21 | "errors" 22 | "runtime" 23 | "unsafe" 24 | ) 25 | 26 | type MD5Hash struct { 27 | ctx *C.EVP_MD_CTX 28 | engine *Engine 29 | } 30 | 31 | func NewMD5Hash() (*MD5Hash, error) { return NewMD5HashWithEngine(nil) } 32 | 33 | func NewMD5HashWithEngine(e *Engine) (*MD5Hash, error) { 34 | hash := &MD5Hash{engine: e} 35 | hash.ctx = C.X_EVP_MD_CTX_new() 36 | if hash.ctx == nil { 37 | return nil, errors.New("openssl: md5: unable to allocate ctx") 38 | } 39 | runtime.SetFinalizer(hash, func(hash *MD5Hash) { hash.Close() }) 40 | if err := hash.Reset(); err != nil { 41 | return nil, err 42 | } 43 | return hash, nil 44 | } 45 | 46 | func (s *MD5Hash) Close() { 47 | if s.ctx != nil { 48 | C.X_EVP_MD_CTX_free(s.ctx) 49 | s.ctx = nil 50 | } 51 | } 52 | 53 | func (s *MD5Hash) Reset() error { 54 | if C.X_EVP_DigestInit_ex(s.ctx, C.X_EVP_md5(), engineRef(s.engine)) != 1 { 55 | return errors.New("openssl: md5: cannot init digest ctx") 56 | } 57 | return nil 58 | } 59 | 60 | func (s *MD5Hash) Write(p []byte) (n int, err error) { 61 | if len(p) == 0 { 62 | return 0, nil 63 | } 64 | if C.X_EVP_DigestUpdate(s.ctx, unsafe.Pointer(&p[0]), 65 | C.size_t(len(p))) != 1 { 66 | return 0, errors.New("openssl: md5: cannot update digest") 67 | } 68 | return len(p), nil 69 | } 70 | 71 | func (s *MD5Hash) Sum() (result [16]byte, err error) { 72 | if C.X_EVP_DigestFinal_ex(s.ctx, 73 | (*C.uchar)(unsafe.Pointer(&result[0])), nil) != 1 { 74 | return result, errors.New("openssl: md5: cannot finalize ctx") 75 | } 76 | return result, s.Reset() 77 | } 78 | 79 | func MD5(data []byte) (result [16]byte, err error) { 80 | hash, err := NewMD5Hash() 81 | if err != nil { 82 | return result, err 83 | } 84 | defer hash.Close() 85 | if _, err := hash.Write(data); err != nil { 86 | return result, err 87 | } 88 | return hash.Sum() 89 | } 90 | -------------------------------------------------------------------------------- /md5_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | import ( 18 | "crypto/md5" 19 | "crypto/rand" 20 | "io" 21 | "testing" 22 | ) 23 | 24 | func TestMD5(t *testing.T) { 25 | for i := 0; i < 100; i++ { 26 | buf := make([]byte, 10*1024-i) 27 | if _, err := io.ReadFull(rand.Reader, buf); err != nil { 28 | t.Fatal(err) 29 | } 30 | 31 | expected := md5.Sum(buf) 32 | got, err := MD5(buf) 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | 37 | if expected != got { 38 | t.Fatalf("exp:%x got:%x", expected, got) 39 | } 40 | } 41 | } 42 | 43 | func TestMD5Writer(t *testing.T) { 44 | ohash, err := NewMD5Hash() 45 | if err != nil { 46 | t.Fatal(err) 47 | } 48 | hash := md5.New() 49 | 50 | for i := 0; i < 100; i++ { 51 | if err := ohash.Reset(); err != nil { 52 | t.Fatal(err) 53 | } 54 | hash.Reset() 55 | buf := make([]byte, 10*1024-i) 56 | if _, err := io.ReadFull(rand.Reader, buf); err != nil { 57 | t.Fatal(err) 58 | } 59 | 60 | if _, err := ohash.Write(buf); err != nil { 61 | t.Fatal(err) 62 | } 63 | if _, err := hash.Write(buf); err != nil { 64 | t.Fatal(err) 65 | } 66 | 67 | var got, exp [16]byte 68 | 69 | hash.Sum(exp[:0]) 70 | got, err := ohash.Sum() 71 | if err != nil { 72 | t.Fatal(err) 73 | } 74 | 75 | if got != exp { 76 | t.Fatalf("exp:%x got:%x", exp, got) 77 | } 78 | } 79 | } 80 | 81 | type md5func func([]byte) 82 | 83 | func benchmarkMD5(b *testing.B, length int64, fn md5func) { 84 | buf := make([]byte, length) 85 | if _, err := io.ReadFull(rand.Reader, buf); err != nil { 86 | b.Fatal(err) 87 | } 88 | b.SetBytes(length) 89 | b.ResetTimer() 90 | for i := 0; i < b.N; i++ { 91 | fn(buf) 92 | } 93 | } 94 | 95 | func BenchmarkMD5Large_openssl(b *testing.B) { 96 | benchmarkMD5(b, 1024*1024, func(buf []byte) { MD5(buf) }) 97 | } 98 | 99 | func BenchmarkMD5Large_stdlib(b *testing.B) { 100 | benchmarkMD5(b, 1024*1024, func(buf []byte) { md5.Sum(buf) }) 101 | } 102 | 103 | func BenchmarkMD5Small_openssl(b *testing.B) { 104 | benchmarkMD5(b, 1, func(buf []byte) { MD5(buf) }) 105 | } 106 | 107 | func BenchmarkMD5Small_stdlib(b *testing.B) { 108 | benchmarkMD5(b, 1, func(buf []byte) { md5.Sum(buf) }) 109 | } 110 | -------------------------------------------------------------------------------- /net.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | import ( 18 | "errors" 19 | "net" 20 | "time" 21 | ) 22 | 23 | type listener struct { 24 | net.Listener 25 | ctx *Ctx 26 | } 27 | 28 | func (l *listener) Accept() (c net.Conn, err error) { 29 | c, err = l.Listener.Accept() 30 | if err != nil { 31 | return nil, err 32 | } 33 | ssl_c, err := Server(c, l.ctx) 34 | if err != nil { 35 | c.Close() 36 | return nil, err 37 | } 38 | return ssl_c, nil 39 | } 40 | 41 | // NewListener wraps an existing net.Listener such that all accepted 42 | // connections are wrapped as OpenSSL server connections using the provided 43 | // context ctx. 44 | func NewListener(inner net.Listener, ctx *Ctx) net.Listener { 45 | return &listener{ 46 | Listener: inner, 47 | ctx: ctx} 48 | } 49 | 50 | // Listen is a wrapper around net.Listen that wraps incoming connections with 51 | // an OpenSSL server connection using the provided context ctx. 52 | func Listen(network, laddr string, ctx *Ctx) (net.Listener, error) { 53 | if ctx == nil { 54 | return nil, errors.New("no ssl context provided") 55 | } 56 | l, err := net.Listen(network, laddr) 57 | if err != nil { 58 | return nil, err 59 | } 60 | return NewListener(l, ctx), nil 61 | } 62 | 63 | type DialFlags int 64 | 65 | const ( 66 | InsecureSkipHostVerification DialFlags = 1 << iota 67 | DisableSNI 68 | ) 69 | 70 | // Dial will connect to network/address and then wrap the corresponding 71 | // underlying connection with an OpenSSL client connection using context ctx. 72 | // If flags includes InsecureSkipHostVerification, the server certificate's 73 | // hostname will not be checked to match the hostname in addr. Otherwise, flags 74 | // should be 0. 75 | // 76 | // Dial probably won't work for you unless you set a verify location or add 77 | // some certs to the certificate store of the client context you're using. 78 | // This library is not nice enough to use the system certificate store by 79 | // default for you yet. 80 | func Dial(network, addr string, ctx *Ctx, flags DialFlags) (*Conn, error) { 81 | return DialSession(network, addr, ctx, flags, nil) 82 | } 83 | 84 | // DialTimeout acts like Dial but takes a timeout for network dial. 85 | // 86 | // The timeout includes only network dial. It does not include OpenSSL calls. 87 | // 88 | // See func Dial for a description of the network, addr, ctx and flags 89 | // parameters. 90 | func DialTimeout(network, addr string, timeout time.Duration, ctx *Ctx, 91 | flags DialFlags) (*Conn, error) { 92 | d := net.Dialer{Timeout: timeout} 93 | return dialSession(d, network, addr, ctx, flags, nil) 94 | } 95 | 96 | // DialSession will connect to network/address and then wrap the corresponding 97 | // underlying connection with an OpenSSL client connection using context ctx. 98 | // If flags includes InsecureSkipHostVerification, the server certificate's 99 | // hostname will not be checked to match the hostname in addr. Otherwise, flags 100 | // should be 0. 101 | // 102 | // Dial probably won't work for you unless you set a verify location or add 103 | // some certs to the certificate store of the client context you're using. 104 | // This library is not nice enough to use the system certificate store by 105 | // default for you yet. 106 | // 107 | // If session is not nil it will be used to resume the tls state. The session 108 | // can be retrieved from the GetSession method on the Conn. 109 | func DialSession(network, addr string, ctx *Ctx, flags DialFlags, 110 | session []byte) (*Conn, error) { 111 | var d net.Dialer 112 | return dialSession(d, network, addr, ctx, flags, session) 113 | } 114 | 115 | func dialSession(d net.Dialer, network, addr string, ctx *Ctx, flags DialFlags, 116 | session []byte) (*Conn, error) { 117 | host, _, err := net.SplitHostPort(addr) 118 | if err != nil { 119 | return nil, err 120 | } 121 | if ctx == nil { 122 | var err error 123 | ctx, err = NewCtx() 124 | if err != nil { 125 | return nil, err 126 | } 127 | // TODO: use operating system default certificate chain? 128 | } 129 | 130 | c, err := d.Dial(network, addr) 131 | if err != nil { 132 | return nil, err 133 | } 134 | conn, err := Client(c, ctx) 135 | if err != nil { 136 | c.Close() 137 | return nil, err 138 | } 139 | if session != nil { 140 | err := conn.setSession(session) 141 | if err != nil { 142 | c.Close() 143 | return nil, err 144 | } 145 | } 146 | if flags&DisableSNI == 0 { 147 | err = conn.SetTlsExtHostName(host) 148 | if err != nil { 149 | conn.Close() 150 | return nil, err 151 | } 152 | } 153 | err = conn.Handshake() 154 | if err != nil { 155 | conn.Close() 156 | return nil, err 157 | } 158 | if flags&InsecureSkipHostVerification == 0 { 159 | err = conn.VerifyHostname(host) 160 | if err != nil { 161 | conn.Close() 162 | return nil, err 163 | } 164 | } 165 | return conn, nil 166 | } 167 | -------------------------------------------------------------------------------- /nid.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | type NID int 18 | 19 | const ( 20 | NID_undef NID = 0 21 | NID_rsadsi NID = 1 22 | NID_pkcs NID = 2 23 | NID_md2 NID = 3 24 | NID_md5 NID = 4 25 | NID_rc4 NID = 5 26 | NID_rsaEncryption NID = 6 27 | NID_md2WithRSAEncryption NID = 7 28 | NID_md5WithRSAEncryption NID = 8 29 | NID_pbeWithMD2AndDES_CBC NID = 9 30 | NID_pbeWithMD5AndDES_CBC NID = 10 31 | NID_X500 NID = 11 32 | NID_X509 NID = 12 33 | NID_commonName NID = 13 34 | NID_countryName NID = 14 35 | NID_localityName NID = 15 36 | NID_stateOrProvinceName NID = 16 37 | NID_organizationName NID = 17 38 | NID_organizationalUnitName NID = 18 39 | NID_rsa NID = 19 40 | NID_pkcs7 NID = 20 41 | NID_pkcs7_data NID = 21 42 | NID_pkcs7_signed NID = 22 43 | NID_pkcs7_enveloped NID = 23 44 | NID_pkcs7_signedAndEnveloped NID = 24 45 | NID_pkcs7_digest NID = 25 46 | NID_pkcs7_encrypted NID = 26 47 | NID_pkcs3 NID = 27 48 | NID_dhKeyAgreement NID = 28 49 | NID_des_ecb NID = 29 50 | NID_des_cfb64 NID = 30 51 | NID_des_cbc NID = 31 52 | NID_des_ede NID = 32 53 | NID_des_ede3 NID = 33 54 | NID_idea_cbc NID = 34 55 | NID_idea_cfb64 NID = 35 56 | NID_idea_ecb NID = 36 57 | NID_rc2_cbc NID = 37 58 | NID_rc2_ecb NID = 38 59 | NID_rc2_cfb64 NID = 39 60 | NID_rc2_ofb64 NID = 40 61 | NID_sha NID = 41 62 | NID_shaWithRSAEncryption NID = 42 63 | NID_des_ede_cbc NID = 43 64 | NID_des_ede3_cbc NID = 44 65 | NID_des_ofb64 NID = 45 66 | NID_idea_ofb64 NID = 46 67 | NID_pkcs9 NID = 47 68 | NID_pkcs9_emailAddress NID = 48 69 | NID_pkcs9_unstructuredName NID = 49 70 | NID_pkcs9_contentType NID = 50 71 | NID_pkcs9_messageDigest NID = 51 72 | NID_pkcs9_signingTime NID = 52 73 | NID_pkcs9_countersignature NID = 53 74 | NID_pkcs9_challengePassword NID = 54 75 | NID_pkcs9_unstructuredAddress NID = 55 76 | NID_pkcs9_extCertAttributes NID = 56 77 | NID_netscape NID = 57 78 | NID_netscape_cert_extension NID = 58 79 | NID_netscape_data_type NID = 59 80 | NID_des_ede_cfb64 NID = 60 81 | NID_des_ede3_cfb64 NID = 61 82 | NID_des_ede_ofb64 NID = 62 83 | NID_des_ede3_ofb64 NID = 63 84 | NID_sha1 NID = 64 85 | NID_sha1WithRSAEncryption NID = 65 86 | NID_dsaWithSHA NID = 66 87 | NID_dsa_2 NID = 67 88 | NID_pbeWithSHA1AndRC2_CBC NID = 68 89 | NID_id_pbkdf2 NID = 69 90 | NID_dsaWithSHA1_2 NID = 70 91 | NID_netscape_cert_type NID = 71 92 | NID_netscape_base_url NID = 72 93 | NID_netscape_revocation_url NID = 73 94 | NID_netscape_ca_revocation_url NID = 74 95 | NID_netscape_renewal_url NID = 75 96 | NID_netscape_ca_policy_url NID = 76 97 | NID_netscape_ssl_server_name NID = 77 98 | NID_netscape_comment NID = 78 99 | NID_netscape_cert_sequence NID = 79 100 | NID_desx_cbc NID = 80 101 | NID_id_ce NID = 81 102 | NID_subject_key_identifier NID = 82 103 | NID_key_usage NID = 83 104 | NID_private_key_usage_period NID = 84 105 | NID_subject_alt_name NID = 85 106 | NID_issuer_alt_name NID = 86 107 | NID_basic_constraints NID = 87 108 | NID_crl_number NID = 88 109 | NID_certificate_policies NID = 89 110 | NID_authority_key_identifier NID = 90 111 | NID_bf_cbc NID = 91 112 | NID_bf_ecb NID = 92 113 | NID_bf_cfb64 NID = 93 114 | NID_bf_ofb64 NID = 94 115 | NID_mdc2 NID = 95 116 | NID_mdc2WithRSA NID = 96 117 | NID_rc4_40 NID = 97 118 | NID_rc2_40_cbc NID = 98 119 | NID_givenName NID = 99 120 | NID_surname NID = 100 121 | NID_initials NID = 101 122 | NID_uniqueIdentifier NID = 102 123 | NID_crl_distribution_points NID = 103 124 | NID_md5WithRSA NID = 104 125 | NID_serialNumber NID = 105 126 | NID_title NID = 106 127 | NID_description NID = 107 128 | NID_cast5_cbc NID = 108 129 | NID_cast5_ecb NID = 109 130 | NID_cast5_cfb64 NID = 110 131 | NID_cast5_ofb64 NID = 111 132 | NID_pbeWithMD5AndCast5_CBC NID = 112 133 | NID_dsaWithSHA1 NID = 113 134 | NID_md5_sha1 NID = 114 135 | NID_sha1WithRSA NID = 115 136 | NID_dsa NID = 116 137 | NID_ripemd160 NID = 117 138 | NID_ripemd160WithRSA NID = 119 139 | NID_rc5_cbc NID = 120 140 | NID_rc5_ecb NID = 121 141 | NID_rc5_cfb64 NID = 122 142 | NID_rc5_ofb64 NID = 123 143 | NID_rle_compression NID = 124 144 | NID_zlib_compression NID = 125 145 | NID_ext_key_usage NID = 126 146 | NID_id_pkix NID = 127 147 | NID_id_kp NID = 128 148 | NID_server_auth NID = 129 149 | NID_client_auth NID = 130 150 | NID_code_sign NID = 131 151 | NID_email_protect NID = 132 152 | NID_time_stamp NID = 133 153 | NID_ms_code_ind NID = 134 154 | NID_ms_code_com NID = 135 155 | NID_ms_ctl_sign NID = 136 156 | NID_ms_sgc NID = 137 157 | NID_ms_efs NID = 138 158 | NID_ns_sgc NID = 139 159 | NID_delta_crl NID = 140 160 | NID_crl_reason NID = 141 161 | NID_invalidity_date NID = 142 162 | NID_sxnet NID = 143 163 | NID_pbe_WithSHA1And128BitRC4 NID = 144 164 | NID_pbe_WithSHA1And40BitRC4 NID = 145 165 | NID_pbe_WithSHA1And3_Key_TripleDES_CBC NID = 146 166 | NID_pbe_WithSHA1And2_Key_TripleDES_CBC NID = 147 167 | NID_pbe_WithSHA1And128BitRC2_CBC NID = 148 168 | NID_pbe_WithSHA1And40BitRC2_CBC NID = 149 169 | NID_keyBag NID = 150 170 | NID_pkcs8ShroudedKeyBag NID = 151 171 | NID_certBag NID = 152 172 | NID_crlBag NID = 153 173 | NID_secretBag NID = 154 174 | NID_safeContentsBag NID = 155 175 | NID_friendlyName NID = 156 176 | NID_localKeyID NID = 157 177 | NID_x509Certificate NID = 158 178 | NID_sdsiCertificate NID = 159 179 | NID_x509Crl NID = 160 180 | NID_pbes2 NID = 161 181 | NID_pbmac1 NID = 162 182 | NID_hmacWithSHA1 NID = 163 183 | NID_id_qt_cps NID = 164 184 | NID_id_qt_unotice NID = 165 185 | NID_rc2_64_cbc NID = 166 186 | NID_SMIMECapabilities NID = 167 187 | NID_pbeWithMD2AndRC2_CBC NID = 168 188 | NID_pbeWithMD5AndRC2_CBC NID = 169 189 | NID_pbeWithSHA1AndDES_CBC NID = 170 190 | NID_ms_ext_req NID = 171 191 | NID_ext_req NID = 172 192 | NID_name NID = 173 193 | NID_dnQualifier NID = 174 194 | NID_id_pe NID = 175 195 | NID_id_ad NID = 176 196 | NID_info_access NID = 177 197 | NID_ad_OCSP NID = 178 198 | NID_ad_ca_issuers NID = 179 199 | NID_OCSP_sign NID = 180 200 | NID_X9_62_id_ecPublicKey NID = 408 201 | NID_hmac NID = 855 202 | NID_cmac NID = 894 203 | NID_dhpublicnumber NID = 920 204 | NID_tls1_prf NID = 1021 205 | NID_hkdf NID = 1036 206 | NID_X25519 NID = 1034 207 | NID_X448 NID = 1035 208 | NID_ED25519 NID = 1087 209 | NID_ED448 NID = 1088 210 | ) 211 | -------------------------------------------------------------------------------- /object.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | // #include "shim.h" 18 | import "C" 19 | 20 | // CreateObjectIdentifier creates ObjectIdentifier and returns NID for the created 21 | // ObjectIdentifier 22 | func CreateObjectIdentifier(oid string, shortName string, longName string) NID { 23 | return NID(C.OBJ_create(C.CString(oid), C.CString(shortName), C.CString(longName))) 24 | } 25 | -------------------------------------------------------------------------------- /pem.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | import ( 18 | "regexp" 19 | ) 20 | 21 | var pemSplit *regexp.Regexp = regexp.MustCompile(`(?sm)` + 22 | `(^-----[\s-]*?BEGIN.*?-----[\s-]*?$` + 23 | `.*?` + 24 | `^-----[\s-]*?END.*?-----[\s-]*?$)`) 25 | 26 | func SplitPEM(data []byte) [][]byte { 27 | return pemSplit.FindAll(data, -1) 28 | } 29 | -------------------------------------------------------------------------------- /sha1.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | // #include "shim.h" 18 | import "C" 19 | 20 | import ( 21 | "errors" 22 | "runtime" 23 | "unsafe" 24 | ) 25 | 26 | type SHA1Hash struct { 27 | ctx *C.EVP_MD_CTX 28 | engine *Engine 29 | } 30 | 31 | func NewSHA1Hash() (*SHA1Hash, error) { return NewSHA1HashWithEngine(nil) } 32 | 33 | func NewSHA1HashWithEngine(e *Engine) (*SHA1Hash, error) { 34 | hash := &SHA1Hash{engine: e} 35 | hash.ctx = C.X_EVP_MD_CTX_new() 36 | if hash.ctx == nil { 37 | return nil, errors.New("openssl: sha1: unable to allocate ctx") 38 | } 39 | runtime.SetFinalizer(hash, func(hash *SHA1Hash) { hash.Close() }) 40 | if err := hash.Reset(); err != nil { 41 | return nil, err 42 | } 43 | return hash, nil 44 | } 45 | 46 | func (s *SHA1Hash) Close() { 47 | if s.ctx != nil { 48 | C.X_EVP_MD_CTX_free(s.ctx) 49 | s.ctx = nil 50 | } 51 | } 52 | 53 | func engineRef(e *Engine) *C.ENGINE { 54 | if e == nil { 55 | return nil 56 | } 57 | return e.e 58 | } 59 | 60 | func (s *SHA1Hash) Reset() error { 61 | if C.X_EVP_DigestInit_ex(s.ctx, C.X_EVP_sha1(), engineRef(s.engine)) != 1 { 62 | return errors.New("openssl: sha1: cannot init digest ctx") 63 | } 64 | return nil 65 | } 66 | 67 | func (s *SHA1Hash) Write(p []byte) (n int, err error) { 68 | if len(p) == 0 { 69 | return 0, nil 70 | } 71 | if C.X_EVP_DigestUpdate(s.ctx, unsafe.Pointer(&p[0]), 72 | C.size_t(len(p))) != 1 { 73 | return 0, errors.New("openssl: sha1: cannot update digest") 74 | } 75 | return len(p), nil 76 | } 77 | 78 | func (s *SHA1Hash) Sum() (result [20]byte, err error) { 79 | if C.X_EVP_DigestFinal_ex(s.ctx, 80 | (*C.uchar)(unsafe.Pointer(&result[0])), nil) != 1 { 81 | return result, errors.New("openssl: sha1: cannot finalize ctx") 82 | } 83 | return result, s.Reset() 84 | } 85 | 86 | func SHA1(data []byte) (result [20]byte, err error) { 87 | hash, err := NewSHA1Hash() 88 | if err != nil { 89 | return result, err 90 | } 91 | defer hash.Close() 92 | if _, err := hash.Write(data); err != nil { 93 | return result, err 94 | } 95 | return hash.Sum() 96 | } 97 | -------------------------------------------------------------------------------- /sha1_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | import ( 18 | "crypto/rand" 19 | "crypto/sha1" 20 | "io" 21 | "testing" 22 | ) 23 | 24 | func TestSHA1(t *testing.T) { 25 | for i := 0; i < 100; i++ { 26 | buf := make([]byte, 10*1024-i) 27 | if _, err := io.ReadFull(rand.Reader, buf); err != nil { 28 | t.Fatal(err) 29 | } 30 | 31 | expected := sha1.Sum(buf) 32 | got, err := SHA1(buf) 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | 37 | if expected != got { 38 | t.Fatalf("exp:%x got:%x", expected, got) 39 | } 40 | } 41 | } 42 | 43 | func TestSHA1Writer(t *testing.T) { 44 | ohash, err := NewSHA1Hash() 45 | if err != nil { 46 | t.Fatal(err) 47 | } 48 | hash := sha1.New() 49 | 50 | for i := 0; i < 100; i++ { 51 | if err := ohash.Reset(); err != nil { 52 | t.Fatal(err) 53 | } 54 | hash.Reset() 55 | buf := make([]byte, 10*1024-i) 56 | if _, err := io.ReadFull(rand.Reader, buf); err != nil { 57 | t.Fatal(err) 58 | } 59 | 60 | if _, err := ohash.Write(buf); err != nil { 61 | t.Fatal(err) 62 | } 63 | if _, err := hash.Write(buf); err != nil { 64 | t.Fatal(err) 65 | } 66 | 67 | var got, exp [20]byte 68 | 69 | hash.Sum(exp[:0]) 70 | got, err := ohash.Sum() 71 | if err != nil { 72 | t.Fatal(err) 73 | } 74 | 75 | if got != exp { 76 | t.Fatalf("exp:%x got:%x", exp, got) 77 | } 78 | } 79 | } 80 | 81 | type shafunc func([]byte) 82 | 83 | func benchmarkSHA1(b *testing.B, length int64, fn shafunc) { 84 | buf := make([]byte, length) 85 | if _, err := io.ReadFull(rand.Reader, buf); err != nil { 86 | b.Fatal(err) 87 | } 88 | b.SetBytes(length) 89 | b.ResetTimer() 90 | for i := 0; i < b.N; i++ { 91 | fn(buf) 92 | } 93 | } 94 | 95 | func BenchmarkSHA1Large_openssl(b *testing.B) { 96 | benchmarkSHA1(b, 1024*1024, func(buf []byte) { SHA1(buf) }) 97 | } 98 | 99 | func BenchmarkSHA1Large_stdlib(b *testing.B) { 100 | benchmarkSHA1(b, 1024*1024, func(buf []byte) { sha1.Sum(buf) }) 101 | } 102 | 103 | func BenchmarkSHA1Small_openssl(b *testing.B) { 104 | benchmarkSHA1(b, 1, func(buf []byte) { SHA1(buf) }) 105 | } 106 | 107 | func BenchmarkSHA1Small_stdlib(b *testing.B) { 108 | benchmarkSHA1(b, 1, func(buf []byte) { sha1.Sum(buf) }) 109 | } 110 | -------------------------------------------------------------------------------- /sha256.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | // #include "shim.h" 18 | import "C" 19 | 20 | import ( 21 | "errors" 22 | "runtime" 23 | "unsafe" 24 | ) 25 | 26 | type SHA256Hash struct { 27 | ctx *C.EVP_MD_CTX 28 | engine *Engine 29 | } 30 | 31 | func NewSHA256Hash() (*SHA256Hash, error) { return NewSHA256HashWithEngine(nil) } 32 | 33 | func NewSHA256HashWithEngine(e *Engine) (*SHA256Hash, error) { 34 | hash := &SHA256Hash{engine: e} 35 | hash.ctx = C.X_EVP_MD_CTX_new() 36 | if hash.ctx == nil { 37 | return nil, errors.New("openssl: sha256: unable to allocate ctx") 38 | } 39 | runtime.SetFinalizer(hash, func(hash *SHA256Hash) { hash.Close() }) 40 | if err := hash.Reset(); err != nil { 41 | return nil, err 42 | } 43 | return hash, nil 44 | } 45 | 46 | func (s *SHA256Hash) Close() { 47 | if s.ctx != nil { 48 | C.X_EVP_MD_CTX_free(s.ctx) 49 | s.ctx = nil 50 | } 51 | } 52 | 53 | func (s *SHA256Hash) Reset() error { 54 | if C.X_EVP_DigestInit_ex(s.ctx, C.X_EVP_sha256(), engineRef(s.engine)) != 1 { 55 | return errors.New("openssl: sha256: cannot init digest ctx") 56 | } 57 | return nil 58 | } 59 | 60 | func (s *SHA256Hash) Write(p []byte) (n int, err error) { 61 | if len(p) == 0 { 62 | return 0, nil 63 | } 64 | if C.X_EVP_DigestUpdate(s.ctx, unsafe.Pointer(&p[0]), 65 | C.size_t(len(p))) != 1 { 66 | return 0, errors.New("openssl: sha256: cannot update digest") 67 | } 68 | return len(p), nil 69 | } 70 | 71 | func (s *SHA256Hash) Sum() (result [32]byte, err error) { 72 | if C.X_EVP_DigestFinal_ex(s.ctx, 73 | (*C.uchar)(unsafe.Pointer(&result[0])), nil) != 1 { 74 | return result, errors.New("openssl: sha256: cannot finalize ctx") 75 | } 76 | return result, s.Reset() 77 | } 78 | 79 | func SHA256(data []byte) (result [32]byte, err error) { 80 | hash, err := NewSHA256Hash() 81 | if err != nil { 82 | return result, err 83 | } 84 | defer hash.Close() 85 | if _, err := hash.Write(data); err != nil { 86 | return result, err 87 | } 88 | return hash.Sum() 89 | } 90 | -------------------------------------------------------------------------------- /sha256_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | import ( 18 | "crypto/rand" 19 | "crypto/sha256" 20 | "io" 21 | "testing" 22 | ) 23 | 24 | func TestSHA256(t *testing.T) { 25 | for i := 0; i < 100; i++ { 26 | buf := make([]byte, 10*1024-i) 27 | if _, err := io.ReadFull(rand.Reader, buf); err != nil { 28 | t.Fatal(err) 29 | } 30 | 31 | expected := sha256.Sum256(buf) 32 | got, err := SHA256(buf) 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | 37 | if expected != got { 38 | t.Fatalf("exp:%x got:%x", expected, got) 39 | } 40 | } 41 | } 42 | 43 | func TestSHA256Writer(t *testing.T) { 44 | ohash, err := NewSHA256Hash() 45 | if err != nil { 46 | t.Fatal(err) 47 | } 48 | hash := sha256.New() 49 | 50 | for i := 0; i < 100; i++ { 51 | if err := ohash.Reset(); err != nil { 52 | t.Fatal(err) 53 | } 54 | hash.Reset() 55 | buf := make([]byte, 10*1024-i) 56 | if _, err := io.ReadFull(rand.Reader, buf); err != nil { 57 | t.Fatal(err) 58 | } 59 | 60 | if _, err := ohash.Write(buf); err != nil { 61 | t.Fatal(err) 62 | } 63 | if _, err := hash.Write(buf); err != nil { 64 | t.Fatal(err) 65 | } 66 | 67 | var got, exp [32]byte 68 | 69 | hash.Sum(exp[:0]) 70 | got, err := ohash.Sum() 71 | if err != nil { 72 | t.Fatal(err) 73 | } 74 | 75 | if got != exp { 76 | t.Fatalf("exp:%x got:%x", exp, got) 77 | } 78 | } 79 | } 80 | 81 | func benchmarkSHA256(b *testing.B, length int64, fn shafunc) { 82 | buf := make([]byte, length) 83 | if _, err := io.ReadFull(rand.Reader, buf); err != nil { 84 | b.Fatal(err) 85 | } 86 | b.SetBytes(length) 87 | b.ResetTimer() 88 | for i := 0; i < b.N; i++ { 89 | fn(buf) 90 | } 91 | } 92 | 93 | func BenchmarkSHA256Large_openssl(b *testing.B) { 94 | benchmarkSHA256(b, 1024*1024, func(buf []byte) { SHA256(buf) }) 95 | } 96 | 97 | func BenchmarkSHA256Large_stdlib(b *testing.B) { 98 | benchmarkSHA256(b, 1024*1024, func(buf []byte) { sha256.Sum256(buf) }) 99 | } 100 | 101 | func BenchmarkSHA256Small_openssl(b *testing.B) { 102 | benchmarkSHA256(b, 1, func(buf []byte) { SHA256(buf) }) 103 | } 104 | 105 | func BenchmarkSHA256Small_stdlib(b *testing.B) { 106 | benchmarkSHA256(b, 1, func(buf []byte) { sha256.Sum256(buf) }) 107 | } 108 | -------------------------------------------------------------------------------- /shim.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Space Monkey, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | */ 17 | 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | 33 | #ifndef SSL_MODE_RELEASE_BUFFERS 34 | #define SSL_MODE_RELEASE_BUFFERS 0 35 | #endif 36 | 37 | #ifndef SSL_OP_NO_COMPRESSION 38 | #define SSL_OP_NO_COMPRESSION 0 39 | #endif 40 | 41 | /* shim methods */ 42 | extern int X_shim_init(); 43 | 44 | /* Library methods */ 45 | extern void X_OPENSSL_free(void *ref); 46 | extern void *X_OPENSSL_malloc(size_t size); 47 | 48 | /* SSL methods */ 49 | extern long X_SSL_set_options(SSL* ssl, long options); 50 | extern long X_SSL_get_options(SSL* ssl); 51 | extern long X_SSL_clear_options(SSL* ssl, long options); 52 | extern long X_SSL_set_tlsext_host_name(SSL *ssl, const char *name); 53 | extern const char * X_SSL_get_cipher_name(const SSL *ssl); 54 | extern int X_SSL_session_reused(SSL *ssl); 55 | extern int X_SSL_new_index(); 56 | 57 | extern const SSL_METHOD *X_SSLv23_method(); 58 | extern const SSL_METHOD *X_SSLv3_method(); 59 | extern const SSL_METHOD *X_TLSv1_method(); 60 | extern const SSL_METHOD *X_TLSv1_1_method(); 61 | extern const SSL_METHOD *X_TLSv1_2_method(); 62 | 63 | #if defined SSL_CTRL_SET_TLSEXT_HOSTNAME 64 | extern int sni_cb(SSL *ssl_conn, int *ad, void *arg); 65 | #endif 66 | extern int X_SSL_verify_cb(int ok, X509_STORE_CTX* store); 67 | 68 | /* SSL_CTX methods */ 69 | extern int X_SSL_CTX_new_index(); 70 | extern int X_SSL_CTX_set_min_proto_version(SSL_CTX *ctx, int version); 71 | extern int X_SSL_CTX_set_max_proto_version(SSL_CTX *ctx, int version); 72 | extern long X_SSL_CTX_set_options(SSL_CTX* ctx, long options); 73 | extern long X_SSL_CTX_clear_options(SSL_CTX* ctx, long options); 74 | extern long X_SSL_CTX_get_options(SSL_CTX* ctx); 75 | extern long X_SSL_CTX_set_mode(SSL_CTX* ctx, long modes); 76 | extern long X_SSL_CTX_get_mode(SSL_CTX* ctx); 77 | extern long X_SSL_CTX_set_session_cache_mode(SSL_CTX* ctx, long modes); 78 | extern long X_SSL_CTX_sess_set_cache_size(SSL_CTX* ctx, long t); 79 | extern long X_SSL_CTX_sess_get_cache_size(SSL_CTX* ctx); 80 | extern long X_SSL_CTX_set_timeout(SSL_CTX* ctx, long t); 81 | extern long X_SSL_CTX_get_timeout(SSL_CTX* ctx); 82 | extern long X_SSL_CTX_add_extra_chain_cert(SSL_CTX* ctx, X509 *cert); 83 | extern long X_SSL_CTX_set_tmp_ecdh(SSL_CTX* ctx, EC_KEY *key); 84 | extern long X_SSL_CTX_set_tlsext_servername_callback(SSL_CTX* ctx, int (*cb)(SSL *con, int *ad, void *args)); 85 | extern int X_SSL_CTX_verify_cb(int ok, X509_STORE_CTX* store); 86 | extern long X_SSL_CTX_set_tmp_dh(SSL_CTX* ctx, DH *dh); 87 | extern long X_PEM_read_DHparams(SSL_CTX* ctx, DH *dh); 88 | extern int X_SSL_CTX_set_tlsext_ticket_key_cb(SSL_CTX *sslctx, 89 | int (*cb)(SSL *s, unsigned char key_name[16], 90 | unsigned char iv[EVP_MAX_IV_LENGTH], 91 | EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int enc)); 92 | extern int X_SSL_CTX_ticket_key_cb(SSL *s, unsigned char key_name[16], 93 | unsigned char iv[EVP_MAX_IV_LENGTH], 94 | EVP_CIPHER_CTX *cctx, HMAC_CTX *hctx, int enc); 95 | extern int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const unsigned char *protos, 96 | unsigned int protos_len); 97 | 98 | /* BIO methods */ 99 | extern int X_BIO_get_flags(BIO *b); 100 | extern void X_BIO_set_flags(BIO *bio, int flags); 101 | extern void X_BIO_clear_flags(BIO *bio, int flags); 102 | extern void X_BIO_set_data(BIO *bio, void* data); 103 | extern void *X_BIO_get_data(BIO *bio); 104 | extern int X_BIO_read(BIO *b, void *buf, int len); 105 | extern int X_BIO_write(BIO *b, const void *buf, int len); 106 | extern BIO *X_BIO_new_write_bio(); 107 | extern BIO *X_BIO_new_read_bio(); 108 | 109 | /* EVP methods */ 110 | extern const int X_ED25519_SUPPORT; 111 | extern int X_EVP_PKEY_ED25519; 112 | extern const EVP_MD *X_EVP_get_digestbyname(const char *name); 113 | extern EVP_MD_CTX *X_EVP_MD_CTX_new(); 114 | extern void X_EVP_MD_CTX_free(EVP_MD_CTX *ctx); 115 | extern const EVP_MD *X_EVP_md_null(); 116 | extern const EVP_MD *X_EVP_md5(); 117 | extern const EVP_MD *X_EVP_md4(); 118 | extern const EVP_MD *X_EVP_sha(); 119 | extern const EVP_MD *X_EVP_sha1(); 120 | extern const EVP_MD *X_EVP_dss(); 121 | extern const EVP_MD *X_EVP_dss1(); 122 | extern const EVP_MD *X_EVP_ripemd160(); 123 | extern const EVP_MD *X_EVP_sha224(); 124 | extern const EVP_MD *X_EVP_sha256(); 125 | extern const EVP_MD *X_EVP_sha384(); 126 | extern const EVP_MD *X_EVP_sha512(); 127 | extern int X_EVP_MD_size(const EVP_MD *md); 128 | extern int X_EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *type, ENGINE *impl); 129 | extern int X_EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d, size_t cnt); 130 | extern int X_EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *s); 131 | extern int X_EVP_SignInit(EVP_MD_CTX *ctx, const EVP_MD *type); 132 | extern int X_EVP_SignUpdate(EVP_MD_CTX *ctx, const void *d, unsigned int cnt); 133 | extern int X_EVP_DigestSignInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey); 134 | extern int X_EVP_DigestSign(EVP_MD_CTX *ctx, unsigned char *sigret, size_t *siglen, const unsigned char *tbs, size_t tbslen); 135 | extern EVP_PKEY *X_EVP_PKEY_new(void); 136 | extern void X_EVP_PKEY_free(EVP_PKEY *pkey); 137 | extern int X_EVP_PKEY_size(EVP_PKEY *pkey); 138 | extern struct rsa_st *X_EVP_PKEY_get1_RSA(EVP_PKEY *pkey); 139 | extern int X_EVP_PKEY_set1_RSA(EVP_PKEY *pkey, struct rsa_st *key); 140 | extern int X_EVP_PKEY_assign_charp(EVP_PKEY *pkey, int type, char *key); 141 | extern int X_EVP_SignFinal(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *s, EVP_PKEY *pkey); 142 | extern int X_EVP_VerifyInit(EVP_MD_CTX *ctx, const EVP_MD *type); 143 | extern int X_EVP_VerifyUpdate(EVP_MD_CTX *ctx, const void *d, unsigned int cnt); 144 | extern int X_EVP_VerifyFinal(EVP_MD_CTX *ctx, const unsigned char *sigbuf, unsigned int siglen, EVP_PKEY *pkey); 145 | extern int X_EVP_DigestVerifyInit(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, const EVP_MD *type, ENGINE *e, EVP_PKEY *pkey); 146 | extern int X_EVP_DigestVerify(EVP_MD_CTX *ctx, const unsigned char *sigret, size_t siglen, const unsigned char *tbs, size_t tbslen); 147 | extern int X_EVP_CIPHER_block_size(EVP_CIPHER *c); 148 | extern int X_EVP_CIPHER_key_length(EVP_CIPHER *c); 149 | extern int X_EVP_CIPHER_iv_length(EVP_CIPHER *c); 150 | extern int X_EVP_CIPHER_nid(EVP_CIPHER *c); 151 | extern int X_EVP_CIPHER_CTX_block_size(EVP_CIPHER_CTX *ctx); 152 | extern int X_EVP_CIPHER_CTX_key_length(EVP_CIPHER_CTX *ctx); 153 | extern int X_EVP_CIPHER_CTX_iv_length(EVP_CIPHER_CTX *ctx); 154 | extern void X_EVP_CIPHER_CTX_set_padding(EVP_CIPHER_CTX *ctx, int padding); 155 | extern const EVP_CIPHER *X_EVP_CIPHER_CTX_cipher(EVP_CIPHER_CTX *ctx); 156 | extern int X_EVP_CIPHER_CTX_encrypting(const EVP_CIPHER_CTX *ctx); 157 | extern int X_EVP_PKEY_CTX_set_ec_paramgen_curve_nid(EVP_PKEY_CTX *ctx, int nid); 158 | 159 | /* HMAC methods */ 160 | extern size_t X_HMAC_size(const HMAC_CTX *e); 161 | extern HMAC_CTX *X_HMAC_CTX_new(void); 162 | extern void X_HMAC_CTX_free(HMAC_CTX *ctx); 163 | extern int X_HMAC_Init_ex(HMAC_CTX *ctx, const void *key, int len, const EVP_MD *md, ENGINE *impl); 164 | extern int X_HMAC_Update(HMAC_CTX *ctx, const unsigned char *data, size_t len); 165 | extern int X_HMAC_Final(HMAC_CTX *ctx, unsigned char *md, unsigned int *len); 166 | 167 | /* X509 methods */ 168 | extern int X_X509_add_ref(X509* x509); 169 | extern const ASN1_TIME *X_X509_get0_notBefore(const X509 *x); 170 | extern const ASN1_TIME *X_X509_get0_notAfter(const X509 *x); 171 | extern int X_sk_X509_num(STACK_OF(X509) *sk); 172 | extern X509 *X_sk_X509_value(STACK_OF(X509)* sk, int i); 173 | extern long X_X509_get_version(const X509 *x); 174 | extern int X_X509_set_version(X509 *x, long version); 175 | 176 | /* PEM methods */ 177 | extern int X_PEM_write_bio_PrivateKey_traditional(BIO *bio, EVP_PKEY *key, const EVP_CIPHER *enc, unsigned char *kstr, int klen, pem_password_cb *cb, void *u); 178 | 179 | /* Object methods */ 180 | extern int OBJ_create(const char *oid,const char *sn,const char *ln); 181 | 182 | /* Extension helper method */ 183 | extern const unsigned char * get_extention(X509 *x, int NID, int *data_len); 184 | extern int add_custom_ext(X509 *cert, int nid, char *value, int len); -------------------------------------------------------------------------------- /sni.c: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #include 16 | #include "_cgo_export.h" 17 | #include 18 | 19 | int sni_cb(SSL *con, int *ad, void *arg) { 20 | SSL_CTX* ssl_ctx = ssl_ctx = SSL_get_SSL_CTX(con); 21 | void* p = SSL_CTX_get_ex_data(ssl_ctx, get_ssl_ctx_idx()); 22 | return sni_cb_thunk(p, con, ad, arg); 23 | } 24 | -------------------------------------------------------------------------------- /ssl.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | // #include "shim.h" 18 | import "C" 19 | 20 | import ( 21 | "os" 22 | "unsafe" 23 | 24 | "github.com/mattn/go-pointer" 25 | ) 26 | 27 | type SSLTLSExtErr int 28 | 29 | const ( 30 | SSLTLSExtErrOK SSLTLSExtErr = C.SSL_TLSEXT_ERR_OK 31 | SSLTLSExtErrAlertWarning SSLTLSExtErr = C.SSL_TLSEXT_ERR_ALERT_WARNING 32 | SSLTLSEXTErrAlertFatal SSLTLSExtErr = C.SSL_TLSEXT_ERR_ALERT_FATAL 33 | SSLTLSEXTErrNoAck SSLTLSExtErr = C.SSL_TLSEXT_ERR_NOACK 34 | ) 35 | 36 | var ( 37 | ssl_idx = C.X_SSL_new_index() 38 | ) 39 | 40 | //export get_ssl_idx 41 | func get_ssl_idx() C.int { 42 | return ssl_idx 43 | } 44 | 45 | type SSL struct { 46 | ssl *C.SSL 47 | verify_cb VerifyCallback 48 | } 49 | 50 | //export go_ssl_verify_cb_thunk 51 | func go_ssl_verify_cb_thunk(p unsafe.Pointer, ok C.int, ctx *C.X509_STORE_CTX) C.int { 52 | defer func() { 53 | if err := recover(); err != nil { 54 | logger.Critf("openssl: verify callback panic'd: %v", err) 55 | os.Exit(1) 56 | } 57 | }() 58 | verify_cb := pointer.Restore(p).(*SSL).verify_cb 59 | // set up defaults just in case verify_cb is nil 60 | if verify_cb != nil { 61 | store := &CertificateStoreCtx{ctx: ctx} 62 | if verify_cb(ok == 1, store) { 63 | ok = 1 64 | } else { 65 | ok = 0 66 | } 67 | } 68 | return ok 69 | } 70 | 71 | // Wrapper around SSL_get_servername. Returns server name according to rfc6066 72 | // http://tools.ietf.org/html/rfc6066. 73 | func (s *SSL) GetServername() string { 74 | return C.GoString(C.SSL_get_servername(s.ssl, C.TLSEXT_NAMETYPE_host_name)) 75 | } 76 | 77 | // GetOptions returns SSL options. See 78 | // https://www.openssl.org/docs/ssl/SSL_CTX_set_options.html 79 | func (s *SSL) GetOptions() Options { 80 | return Options(C.X_SSL_get_options(s.ssl)) 81 | } 82 | 83 | // SetOptions sets SSL options. See 84 | // https://www.openssl.org/docs/ssl/SSL_CTX_set_options.html 85 | func (s *SSL) SetOptions(options Options) Options { 86 | return Options(C.X_SSL_set_options(s.ssl, C.long(options))) 87 | } 88 | 89 | // ClearOptions clear SSL options. See 90 | // https://www.openssl.org/docs/ssl/SSL_CTX_set_options.html 91 | func (s *SSL) ClearOptions(options Options) Options { 92 | return Options(C.X_SSL_clear_options(s.ssl, C.long(options))) 93 | } 94 | 95 | // SetVerify controls peer verification settings. See 96 | // http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html 97 | func (s *SSL) SetVerify(options VerifyOptions, verify_cb VerifyCallback) { 98 | s.verify_cb = verify_cb 99 | if verify_cb != nil { 100 | C.SSL_set_verify(s.ssl, C.int(options), (*[0]byte)(C.X_SSL_verify_cb)) 101 | } else { 102 | C.SSL_set_verify(s.ssl, C.int(options), nil) 103 | } 104 | } 105 | 106 | // SetVerifyMode controls peer verification setting. See 107 | // http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html 108 | func (s *SSL) SetVerifyMode(options VerifyOptions) { 109 | s.SetVerify(options, s.verify_cb) 110 | } 111 | 112 | // SetVerifyCallback controls peer verification setting. See 113 | // http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html 114 | func (s *SSL) SetVerifyCallback(verify_cb VerifyCallback) { 115 | s.SetVerify(s.VerifyMode(), verify_cb) 116 | } 117 | 118 | // GetVerifyCallback returns callback function. See 119 | // http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html 120 | func (s *SSL) GetVerifyCallback() VerifyCallback { 121 | return s.verify_cb 122 | } 123 | 124 | // VerifyMode returns peer verification setting. See 125 | // http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html 126 | func (s *SSL) VerifyMode() VerifyOptions { 127 | return VerifyOptions(C.SSL_get_verify_mode(s.ssl)) 128 | } 129 | 130 | // SetVerifyDepth controls how many certificates deep the certificate 131 | // verification logic is willing to follow a certificate chain. See 132 | // https://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html 133 | func (s *SSL) SetVerifyDepth(depth int) { 134 | C.SSL_set_verify_depth(s.ssl, C.int(depth)) 135 | } 136 | 137 | // GetVerifyDepth controls how many certificates deep the certificate 138 | // verification logic is willing to follow a certificate chain. See 139 | // https://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html 140 | func (s *SSL) GetVerifyDepth() int { 141 | return int(C.SSL_get_verify_depth(s.ssl)) 142 | } 143 | 144 | // SetSSLCtx changes context to new one. Useful for Server Name Indication (SNI) 145 | // rfc6066 http://tools.ietf.org/html/rfc6066. See 146 | // http://stackoverflow.com/questions/22373332/serving-multiple-domains-in-one-box-with-sni 147 | func (s *SSL) SetSSLCtx(ctx *Ctx) { 148 | /* 149 | * SSL_set_SSL_CTX() only changes certs as of 1.0.0d 150 | * adjust other things we care about 151 | */ 152 | C.SSL_set_SSL_CTX(s.ssl, ctx.ctx) 153 | } 154 | 155 | //export sni_cb_thunk 156 | func sni_cb_thunk(p unsafe.Pointer, con *C.SSL, ad unsafe.Pointer, arg unsafe.Pointer) C.int { 157 | defer func() { 158 | if err := recover(); err != nil { 159 | logger.Critf("openssl: verify callback sni panic'd: %v", err) 160 | os.Exit(1) 161 | } 162 | }() 163 | 164 | sni_cb := pointer.Restore(p).(*Ctx).sni_cb 165 | 166 | s := &SSL{ssl: con} 167 | // This attaches a pointer to our SSL struct into the SNI callback. 168 | C.SSL_set_ex_data(s.ssl, get_ssl_idx(), pointer.Save(s)) 169 | 170 | // Note: this is ctx.sni_cb, not C.sni_cb 171 | return C.int(sni_cb(s)) 172 | } 173 | -------------------------------------------------------------------------------- /tickets.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2017. See AUTHORS. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package openssl 16 | 17 | // #include "shim.h" 18 | import "C" 19 | 20 | import ( 21 | "os" 22 | "unsafe" 23 | 24 | "github.com/mattn/go-pointer" 25 | ) 26 | 27 | const ( 28 | KeyNameSize = 16 29 | ) 30 | 31 | // TicketCipherCtx describes the cipher that will be used by the ticket store 32 | // for encrypting the tickets. Engine may be nil if no engine is desired. 33 | type TicketCipherCtx struct { 34 | Cipher *Cipher 35 | Engine *Engine 36 | } 37 | 38 | // TicketDigestCtx describes the digest that will be used by the ticket store 39 | // to authenticate the data. Engine may be nil if no engine is desired. 40 | type TicketDigestCtx struct { 41 | Digest *Digest 42 | Engine *Engine 43 | } 44 | 45 | // TicketName is an identifier for the key material for a ticket. 46 | type TicketName [KeyNameSize]byte 47 | 48 | // TicketKey is the key material for a ticket. If this is lost, forward secrecy 49 | // is lost as it allows decrypting TLS sessions retroactively. 50 | type TicketKey struct { 51 | Name TicketName 52 | CipherKey []byte 53 | HMACKey []byte 54 | IV []byte 55 | } 56 | 57 | // TicketKeyManager is a manager for TicketKeys. It allows one to control the 58 | // lifetime of tickets, causing renewals and expirations for keys that are 59 | // created. Calls to the manager are serialized. 60 | type TicketKeyManager interface { 61 | // New should create a brand new TicketKey with a new name. 62 | New() *TicketKey 63 | 64 | // Current should return a key that is still valid. 65 | Current() *TicketKey 66 | 67 | // Lookup should return a key with the given name, or nil if no name 68 | // exists. 69 | Lookup(name TicketName) *TicketKey 70 | 71 | // Expired should return if the key with the given name is expired and 72 | // should not be used any more. 73 | Expired(name TicketName) bool 74 | 75 | // ShouldRenew should return if the key is still ok to use for the current 76 | // session, but we should send a new key for the client. 77 | ShouldRenew(name TicketName) bool 78 | } 79 | 80 | // TicketStore descibes the encryption and authentication methods the tickets 81 | // will use along with a key manager for generating and keeping track of the 82 | // secrets. 83 | type TicketStore struct { 84 | CipherCtx TicketCipherCtx 85 | DigestCtx TicketDigestCtx 86 | Keys TicketKeyManager 87 | } 88 | 89 | func (t *TicketStore) cipherEngine() *C.ENGINE { 90 | if t.CipherCtx.Engine == nil { 91 | return nil 92 | } 93 | return t.CipherCtx.Engine.e 94 | } 95 | 96 | func (t *TicketStore) digestEngine() *C.ENGINE { 97 | if t.DigestCtx.Engine == nil { 98 | return nil 99 | } 100 | return t.DigestCtx.Engine.e 101 | } 102 | 103 | const ( 104 | // instruct to do a handshake 105 | ticket_resp_requireHandshake = 0 106 | // crypto context is set up correctly 107 | ticket_resp_sessionOk = 1 108 | // crypto context is ok, but the ticket should be reissued 109 | ticket_resp_renewSession = 2 110 | // we had a problem that shouldn't fall back to doing a handshake 111 | ticket_resp_error = -1 112 | 113 | // asked to create session crypto context 114 | ticket_req_newSession = 1 115 | // asked to load crypto context for a previous session 116 | ticket_req_lookupSession = 0 117 | ) 118 | 119 | //export go_ticket_key_cb_thunk 120 | func go_ticket_key_cb_thunk(p unsafe.Pointer, s *C.SSL, key_name *C.uchar, 121 | iv *C.uchar, cctx *C.EVP_CIPHER_CTX, hctx *C.HMAC_CTX, enc C.int) C.int { 122 | 123 | // no panic's allowed. it's super hard to guarantee any state at this point 124 | // so just abort everything. 125 | defer func() { 126 | if err := recover(); err != nil { 127 | logger.Critf("openssl: ticket key callback panic'd: %v", err) 128 | os.Exit(1) 129 | } 130 | }() 131 | 132 | ctx := pointer.Restore(p).(*Ctx) 133 | store := ctx.ticket_store 134 | if store == nil { 135 | // TODO(jeff): should this be an error condition? it doesn't make sense 136 | // to be called if we don't have a store I believe, but that's probably 137 | // not worth aborting the handshake which is what I believe returning 138 | // an error would do. 139 | return ticket_resp_requireHandshake 140 | } 141 | 142 | ctx.ticket_store_mu.Lock() 143 | defer ctx.ticket_store_mu.Unlock() 144 | 145 | switch enc { 146 | case ticket_req_newSession: 147 | key := store.Keys.Current() 148 | if key == nil { 149 | key = store.Keys.New() 150 | if key == nil { 151 | return ticket_resp_requireHandshake 152 | } 153 | } 154 | 155 | C.memcpy( 156 | unsafe.Pointer(key_name), 157 | unsafe.Pointer(&key.Name[0]), 158 | KeyNameSize) 159 | C.EVP_EncryptInit_ex( 160 | cctx, 161 | store.CipherCtx.Cipher.ptr, 162 | store.cipherEngine(), 163 | (*C.uchar)(&key.CipherKey[0]), 164 | (*C.uchar)(&key.IV[0])) 165 | C.HMAC_Init_ex( 166 | hctx, 167 | unsafe.Pointer(&key.HMACKey[0]), 168 | C.int(len(key.HMACKey)), 169 | store.DigestCtx.Digest.ptr, 170 | store.digestEngine()) 171 | 172 | return ticket_resp_sessionOk 173 | 174 | case ticket_req_lookupSession: 175 | var name TicketName 176 | C.memcpy( 177 | unsafe.Pointer(&name[0]), 178 | unsafe.Pointer(key_name), 179 | KeyNameSize) 180 | 181 | key := store.Keys.Lookup(name) 182 | if key == nil { 183 | return ticket_resp_requireHandshake 184 | } 185 | if store.Keys.Expired(name) { 186 | return ticket_resp_requireHandshake 187 | } 188 | 189 | C.EVP_DecryptInit_ex( 190 | cctx, 191 | store.CipherCtx.Cipher.ptr, 192 | store.cipherEngine(), 193 | (*C.uchar)(&key.CipherKey[0]), 194 | (*C.uchar)(&key.IV[0])) 195 | C.HMAC_Init_ex( 196 | hctx, 197 | unsafe.Pointer(&key.HMACKey[0]), 198 | C.int(len(key.HMACKey)), 199 | store.DigestCtx.Digest.ptr, 200 | store.digestEngine()) 201 | 202 | if store.Keys.ShouldRenew(name) { 203 | return ticket_resp_renewSession 204 | } 205 | 206 | return ticket_resp_sessionOk 207 | 208 | default: 209 | return ticket_resp_error 210 | } 211 | } 212 | 213 | // SetTicketStore sets the ticket store for the context so that clients can do 214 | // ticket based session resumption. If the store is nil, the 215 | func (c *Ctx) SetTicketStore(store *TicketStore) { 216 | c.ticket_store = store 217 | 218 | if store == nil { 219 | C.X_SSL_CTX_set_tlsext_ticket_key_cb(c.ctx, nil) 220 | } else { 221 | C.X_SSL_CTX_set_tlsext_ticket_key_cb(c.ctx, 222 | (*[0]byte)(C.X_SSL_CTX_ticket_key_cb)) 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /utils/errors.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2014 Space Monkey, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils 16 | 17 | import ( 18 | "errors" 19 | "strings" 20 | ) 21 | 22 | // ErrorGroup collates errors 23 | type ErrorGroup struct { 24 | Errors []error 25 | } 26 | 27 | // Add adds an error to an existing error group 28 | func (e *ErrorGroup) Add(err error) { 29 | if err != nil { 30 | e.Errors = append(e.Errors, err) 31 | } 32 | } 33 | 34 | // Finalize returns an error corresponding to the ErrorGroup state. If there's 35 | // no errors in the group, finalize returns nil. If there's only one error, 36 | // Finalize returns that error. Otherwise, Finalize will make a new error 37 | // consisting of the messages from the constituent errors. 38 | func (e *ErrorGroup) Finalize() error { 39 | if len(e.Errors) == 0 { 40 | return nil 41 | } 42 | if len(e.Errors) == 1 { 43 | return e.Errors[0] 44 | } 45 | msgs := make([]string, 0, len(e.Errors)) 46 | for _, err := range e.Errors { 47 | msgs = append(msgs, err.Error()) 48 | } 49 | return errors.New(strings.Join(msgs, "\n")) 50 | } 51 | -------------------------------------------------------------------------------- /utils/future.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2014 Space Monkey, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package utils 16 | 17 | import ( 18 | "sync" 19 | ) 20 | 21 | // Future is a type that is essentially the inverse of a channel. With a 22 | // channel, you have multiple senders and one receiver. With a future, you can 23 | // have multiple receivers and one sender. Additionally, a future protects 24 | // against double-sends. Since this is usually used for returning function 25 | // results, we also capture and return error values as well. Use NewFuture 26 | // to initialize. 27 | type Future struct { 28 | mutex *sync.Mutex 29 | cond *sync.Cond 30 | received bool 31 | val interface{} 32 | err error 33 | } 34 | 35 | // NewFuture returns an initialized and ready Future. 36 | func NewFuture() *Future { 37 | mutex := &sync.Mutex{} 38 | return &Future{ 39 | mutex: mutex, 40 | cond: sync.NewCond(mutex), 41 | received: false, 42 | val: nil, 43 | err: nil, 44 | } 45 | } 46 | 47 | // Get blocks until the Future has a value set. 48 | func (f *Future) Get() (interface{}, error) { 49 | f.mutex.Lock() 50 | defer f.mutex.Unlock() 51 | for { 52 | if f.received { 53 | return f.val, f.err 54 | } 55 | f.cond.Wait() 56 | } 57 | } 58 | 59 | // Fired returns whether or not a value has been set. If Fired is true, Get 60 | // won't block. 61 | func (f *Future) Fired() bool { 62 | f.mutex.Lock() 63 | defer f.mutex.Unlock() 64 | return f.received 65 | } 66 | 67 | // Set provides the value to present and future Get calls. If Set has already 68 | // been called, this is a no-op. 69 | func (f *Future) Set(val interface{}, err error) { 70 | f.mutex.Lock() 71 | defer f.mutex.Unlock() 72 | if f.received { 73 | return 74 | } 75 | f.received = true 76 | f.val = val 77 | f.err = err 78 | f.cond.Broadcast() 79 | } 80 | -------------------------------------------------------------------------------- /version.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "v0.1.0" 3 | } 4 | --------------------------------------------------------------------------------