diff options
83 files changed, 5139 insertions, 55 deletions
diff --git a/README.boringcrypto.md b/README.boringcrypto.md new file mode 100644 index 0000000000..b6f5805429 --- /dev/null +++ b/README.boringcrypto.md @@ -0,0 +1,18 @@ +# dev.boringcrypto branch + +We have been working inside Google on a fork of Go that uses +BoringCrypto (the core of [BoringSSL](https://boringssl.googlesource.com/boringssl/)) for various crypto primitives, in +furtherance of some [work related to FIPS 140-2](http://csrc.nist.gov/groups/STM/cmvp/documents/140-1/140sp/140sp2964.pdf). We have heard that +some external users of Go would be interested in this code as well, so +I intend to create a new branch dev.boringcrypto that will hold +patches to make Go use BoringCrypto. + +Unlike typical dev branches, we do not intend any eventual merge of +this code into the master branch. Instead we intend to maintain in +that branch the latest release plus BoringCrypto patches. In this +sense it is a bit like dev.typealias holding go1.8+type alias patches. + +To be clear, we are not making any statements or representations about +the suitability of this code in relation to the FIPS 140-2 standard. +Interested users will have to evaluate for themselves whether the code +is useful for their own purposes. diff --git a/VERSION b/VERSION deleted file mode 100644 index 70023c4bd4..0000000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -go1.13.8
\ No newline at end of file diff --git a/api/go1.9.txt b/api/go1.9.txt index c23a17ea1a..c7a4968aaf 100644 --- a/api/go1.9.txt +++ b/api/go1.9.txt @@ -7,6 +7,7 @@ pkg crypto, const BLAKE2b_512 Hash pkg crypto, const BLAKE2s_256 = 16 pkg crypto, const BLAKE2s_256 Hash pkg crypto/x509, type Certificate struct, ExcludedDNSDomains []string +pkg crypto/x509, type VerifyOptions struct, IsBoring func(*Certificate) bool pkg database/sql, method (*Conn) BeginTx(context.Context, *TxOptions) (*Tx, error) pkg database/sql, method (*Conn) Close() error pkg database/sql, method (*Conn) ExecContext(context.Context, string, ...interface{}) (Result, error) diff --git a/misc/boring/README.md b/misc/boring/README.md new file mode 100644 index 0000000000..bd8deff1cc --- /dev/null +++ b/misc/boring/README.md @@ -0,0 +1,108 @@ +# README.md + +This directory holds build scripts for unofficial, unsupported +distributions of Go+BoringCrypto. + +## Version strings + +The distribution name for a Go+BoringCrypto release has the form `<GoVersion>b<BoringCryptoVersion>`, +where `<GoVersion>` is the Go version the release is based on, and `<BoringCryptoVersion>` is +an integer that increments each time there is a new release with different BoringCrypto bits. +The `<BoringCryptoVersion>` is stored in the `VERSION` file in this directory. + +For example, the first release is based on Go 1.8.3 is `go1.8.3b1`. +If the BoringCrypto bits are updated, the next would be `go1.8.3b2`. +If, after that, Go 1.9 is released and the same BoringCrypto code added to it, +that would result in `go1.9b2`. There would likely not be a `go1.9b1`, +since that would indicate Go 1.9 with the older BoringCrypto code. + +## Releases + +The `build.release` script prepares a binary release and publishes it in Google Cloud Storage +at `gs://go-boringcrypto/`, making it available for download at +`https://go-boringcrypto.storage.googleapis.com/<FILE>`. +The script records each published release in the `RELEASES` file in this directory. + +The `build.docker` script, which must be run after `build.release`, prepares a Docker image +and publishes it on hub.docker.com in the goboring organization. +`go1.8.3b1` is published as `goboring/golang:1.8.3b1`. + +## Release process + +Development is done on the dev.boringcrypto branch, which tracks +master. Releases are cut from dev.boringcrypto.go1.X branches, +which are BoringCrypto backported to the Go 1.X release branches. +To issue new BoringCrypto releases based on Go 1.X: + +1. If the BoringCrypto bits have been updated, increment the + number in `VERSION`, send that change out as a CL for review, + get it committed to dev.boringcrypto, and run `git sync`. + +2. Change to the dev.boringcrypto.go1.X branch and cherry-pick + all BoringCrypto updates, including the update of the + `VERSION` file. If desired, merge release-branch.go1.X into + dev.boringcrypto.go1.X. Mail them out and get them committed. + +3. **Back on the dev.boringcrypto branch**, run `git fetch`, + `make.bash` and then `build.release dev.boringcrypto.go1.X`. + The script will determine the base Go version and the + BoringCrypto version, build a release, and upload it. + +4. Run `build.docker`, which will build and upload a Docker image + from the latest release. + +5. Send out a CL with the updated `RELEASES` file and get it + committed to dev.boringcrypto. + +## Building from Docker + +A Dockerfile that starts with `FROM golang:1.8.3` can switch +to `FROM goboring/golang:1.8.3b2` (see [goboring/golang on Docker Hub](https://hub.docker.com/r/goboring/golang/)) +and should need no other modifications. + +## Building from Bazel + +Starting from [bazelbuild/rules_go](https://github.com/bazelbuild/rules_go) +tag 0.7.1, simply download the BoringCrypto-enabled Go SDK using +`go_download_sdk()` before calling `go_register_toolchains()`. + +For example, to use Go 1.9.3 with BoringCrypto on Linux, use the following lines +in `WORKSPACE`: +```python +load("@io_bazel_rules_go//go:def.bzl", "go_rules_dependencies", "go_download_sdk", "go_register_toolchains") + +go_rules_dependencies() + +go_download_sdk( + name = "go_sdk", + sdks = { + "linux_amd64": ("go1.9.3b4.linux-amd64.tar.gz", "db1997b2454a2f27669b849d2d2cafb247a55128d53da678f06cb409310d6660"), + }, + urls = ["https://storage.googleapis.com/go-boringcrypto/{}"], +) + +go_register_toolchains() +``` + +**Note**: you must *not* enable `pure` mode, since cgo must be enabled. To +ensure that binaries are linked with BoringCrypto, you can set `pure = "off"` on +all relevant `go_binary` rules. + +## Caveat + +BoringCrypto is used for a given build only in limited circumstances: + + - The build must be GOOS=linux, GOARCH=amd64. + - The build must have cgo enabled. + - The android build tag must not be specified. + - The cmd_go_bootstrap build tag must not be specified. + +The version string reported by `runtime.Version` does not indicate that BoringCrypto +was actually used for the build. For example, linux/386 and non-cgo linux/amd64 binaries +will report a version of `go1.8.3b2` but not be using BoringCrypto. + +To check whether a given binary is using BoringCrypto, run `go tool nm` on it and check +that it has symbols named `*_Cfunc__goboringcrypto_*`. + +The program [rsc.io/goversion](https://godoc.org/rsc.io/goversion) will report the +crypto implementation used by a given binary when invoked with the `-crypto` flag. diff --git a/misc/boring/RELEASES b/misc/boring/RELEASES new file mode 100644 index 0000000000..a9f5ebad72 --- /dev/null +++ b/misc/boring/RELEASES @@ -0,0 +1,51 @@ +# This file lists published Go+BoringCrypto releases. +# Each line describes a single release: <version> <git commit> <target> <URL> <sha256sum> +go1.9rc2b2 91753387bdf7 linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.9rc2b2.linux-amd64.tar.gz 59355a45e6970e8013060851ddb3f079afe8db52e90db520a0826a13f1b5ae5b +go1.8.3b3 f6ff81bac156 linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.8.3b3.linux-amd64.tar.gz 6287ad971cd268bb2684fb8b1275dea928ad527823062bc057e73036c419e7af +go1.9rc2b4 c339bc4e07a6 linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.9rc2b4.linux-amd64.tar.gz a8f677d48dc93920065fca4dca1a55bf7110aba132489c47e25d26d55c67eb32 +go1.9b4 e6ad24cde71e linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.9b4.linux-amd64.tar.gz 6592e36a05df8e7c59812328a3a4bfa6c4eed72132fc31245951c3ade3ef2a8a +go1.9b4 e6ad24cde71e src https://go-boringcrypto.storage.googleapis.com/go1.9b4.src.tar.gz c85f31dc743fee0e8ce0c6ffc286e27c1f51b66c9b923afafb43cdc378a41091 +go1.8.3b4 42cb4dcdb59a linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.8.3b4.linux-amd64.tar.gz 4011c86e6175925e1c63dc7c19a51f825be53bbe7b08260918e5107b0fbd4f85 +go1.8.3b4 42cb4dcdb59a src https://go-boringcrypto.storage.googleapis.com/go1.8.3b4.src.tar.gz 2531ca8918aa024aed8f4a6c9e5c3b25bc8777623f1efa66aec7214601d474e4 +go1.9.2b4 cda3c6f91d7c linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.9.2b4.linux-amd64.tar.gz 7c5e9a033ddc3ab36646e3bac7fd16962742710c70c18122e44a9ab56cdd3cf7 +go1.9.2b4 cda3c6f91d7c src https://go-boringcrypto.storage.googleapis.com/go1.9.2b4.src.tar.gz 38a2260b64a6a5ab20f8972d08b4765bad116721356433f39aebd29c7598218c +go1.9.3b4 f4e5ebdf35c8 linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.9.3b4.linux-amd64.tar.gz db1997b2454a2f27669b849d2d2cafb247a55128d53da678f06cb409310d6660 +go1.9.3b4 f4e5ebdf35c8 src https://go-boringcrypto.storage.googleapis.com/go1.9.3b4.src.tar.gz 7485e1fc53a9fab9cf34f71de74d69f4c50f9d11a449647de40ee04b59bf8a5b +go1.9.7b4 0bad1bef406e linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.9.7b4.linux-amd64.tar.gz 9e33a0deb8fed3bd7fa3d122bb5143be9e0a974a422ab4ddac5e765fa1310a6f +go1.9.7b4 0bad1bef406e src https://go-boringcrypto.storage.googleapis.com/go1.9.7b4.src.tar.gz ad9fb6e22a27382c468467ecade4937f725b33818852f1c1da0d09b471e7486c +go1.10.3b4 35ba5284935c linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.10.3b4.linux-amd64.tar.gz 6754729d78a375bd1debd980b1e3e7fd49198a980d0bbd8f39e89569aa001942 +go1.10.3b4 35ba5284935c src https://go-boringcrypto.storage.googleapis.com/go1.10.3b4.src.tar.gz f3e75c60a835c11b97e30429b63917ceb31f799b2ba7e2001d99db908fb8e28f +go1.10.4b4 2e2a04a605b6 linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.10.4b4.linux-amd64.tar.gz 17c275ff448686fe1908ecbea5d11ad6f4f7caa288d1786b756439703b12b8b2 +go1.10.4b4 2e2a04a605b6 src https://go-boringcrypto.storage.googleapis.com/go1.10.4b4.src.tar.gz f9cc38e194edabebf338fb74c22f597dc847560618d5d7d4d6cdc28139efa772 +go1.11b4 685dc1638240 linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.11b4.linux-amd64.tar.gz d53417b2071af0104fbc15a957000bccdcb5bbc094df0401f67d51968f7f2e4e +go1.11b4 685dc1638240 src https://go-boringcrypto.storage.googleapis.com/go1.11b4.src.tar.gz 39896f0decd6721e81324cb2bb19540706ca97152c6800a6c8ad15a4e4162184 +go1.11.2b4 35cf0d9f6bbd linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.11.2b4.linux-amd64.tar.gz a9ceb6d0b4413d81ccc94c6460f60ca0c4f36b5dcbf659e1be582cd40c0edfbd +go1.11.2b4 35cf0d9f6bbd src https://go-boringcrypto.storage.googleapis.com/go1.11.2b4.src.tar.gz 8e12a8df1428f00239dc67dd438a81f72c9925982e90b6899f66270971bddc1c +go1.10.7b4 8b246fe0f595 linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.10.7b4.linux-amd64.tar.gz 31917ab96004b9b482399b46928f5c10cdadefed5fda6f4de262efe2c3c7533e +go1.10.7b4 8b246fe0f595 src https://go-boringcrypto.storage.googleapis.com/go1.10.7b4.src.tar.gz 323a184c77e3a377f5ed993b04946ee7b1a8e3350aba2894c0944f1e313636f1 +go1.11.4b4 572c4bce6792 linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.11.4b4.linux-amd64.tar.gz e708ef7ecaf17a3e8e6deceadfa167cc1162f710f97ea4bc124d3837d6e2eaa1 +go1.11.4b4 572c4bce6792 src https://go-boringcrypto.storage.googleapis.com/go1.11.4b4.src.tar.gz ea963b80e218a34470e14e6e997fe06b8c5bf3f9c9bb0c801f7d8ef63b9bcb73 +go1.10.8b4 4b76b996cb0a linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.10.8b4.linux-amd64.tar.gz 6d7d3323030851b595ba7ed66931c352b63de6dfe1ab3e6d6243987765d09819 +go1.10.8b4 4b76b996cb0a src https://go-boringcrypto.storage.googleapis.com/go1.10.8b4.src.tar.gz c1f5df50a4be3d0cb3aed7b80728f2b23c18deff0383636274742a38c145f939 +go1.11.5b4 3fb9dafacc45 linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.11.5b4.linux-amd64.tar.gz 9b5b2972b452da9ba6bba65bab18fb9e8fbda31b5c489275710e5429d76f568c +go1.11.5b4 3fb9dafacc45 src https://go-boringcrypto.storage.googleapis.com/go1.11.5b4.src.tar.gz 1c5801e2af25c9299d9fd94c64f9ec11fd35777c45d5d0f398c0a9884b1cfbbf +go1.12.1b4 88e20e81a61f linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.12.1b4.linux-amd64.tar.gz b71886e0d65e5efea2e0a3cbd0c3cd0daf84c437078e755ecde25f4ac0bbed2f +go1.12.1b4 88e20e81a61f src https://go-boringcrypto.storage.googleapis.com/go1.12.1b4.src.tar.gz d44be1396eb2854b5d9c4d8e8ed0cf9fea1e9dc5a02d8f53b41ba571951a329f +go1.11.6b4 7be8a5843a9b linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.11.6b4.linux-amd64.tar.gz b704f61b8979e64a46da8884c90cd2b0e2d54e802d55e5f56d7c93752334c197 +go1.11.6b4 7be8a5843a9b src https://go-boringcrypto.storage.googleapis.com/go1.11.6b4.src.tar.gz a56b45e24b61ad7b3c90dfd906cd22426a4de9e2e697b4c9ef07a2af047bcb0d +go1.12.5b4 ad495d31d908 linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.12.5b4.linux-amd64.tar.gz df0e64958cf90f27a65b2175eb80bc34a601136eed8e5559bed2a9e349e33707 +go1.12.5b4 ad495d31d908 src https://go-boringcrypto.storage.googleapis.com/go1.12.5b4.src.tar.gz 054d482896a77ae2d7d24c7adf08da5a4401b938871e61a5cdabc735c54cea9f +go1.11.11b4 346babe6a67f linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.11.11b4.linux-amd64.tar.gz c4dd44fa00f491b3d2ea808af8a6c234f915adb27c014512d725bafc4784d75f +go1.11.11b4 346babe6a67f src https://go-boringcrypto.storage.googleapis.com/go1.11.11b4.src.tar.gz 57a724a72f0ba8620cbb48288f39c86ed513c241509ddf73231f4c8cd2a983ac +go1.12.6b4 6b86b09ad4d3 linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.12.6b4.linux-amd64.tar.gz eebc2e7f37555760adb361985b861d0cd34f9401cf7456d8d2f2f3082a60eee1 +go1.12.6b4 6b86b09ad4d3 src https://go-boringcrypto.storage.googleapis.com/go1.12.6b4.src.tar.gz 0e6e9aaf2c72a7e61280ce1e77b2ea24f01a59f4c1e6f0aa72b753206724fd3a +go1.11.12b4 845e947ae34f linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.11.12b4.linux-amd64.tar.gz 91808261fc357855fba920df01a933d6104e907793014317de00b92802d494d9 +go1.11.12b4 845e947ae34f src https://go-boringcrypto.storage.googleapis.com/go1.11.12b4.src.tar.gz 7b64d9e56ea627138d87c7533df8f9932a79ff900f150a8d8e6a3edc2d0066ec +go1.12.7b4 bd126d0ad256 linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.12.7b4.linux-amd64.tar.gz 7f0c73cd397bccad48ab4df4188d3651c25bf33102275848c6e67b882e11f680 +go1.12.7b4 bd126d0ad256 src https://go-boringcrypto.storage.googleapis.com/go1.12.7b4.src.tar.gz 0c48d7b81ef2b948980011fad1d176d6b10636a4016e3aed7438d86e046d816b +go1.11.13b4 4f8e7223f936 linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.11.13b4.linux-amd64.tar.gz eeb232577065732f5d57a4c77b7d73aa60231ee6fd6496daf7558993e92e403f +go1.11.13b4 4f8e7223f936 src https://go-boringcrypto.storage.googleapis.com/go1.11.13b4.src.tar.gz 107da8846803a0a735766ca0947de6cd15cd23d8c584002f06e7ac5f81ecb114 +go1.12.8b4 55186ba70c1a linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.12.8b4.linux-amd64.tar.gz 63f278abfc1e98546bc0ffc87f000d9aae2b06c0700212cb55ffd17d059fb8e1 +go1.12.8b4 55186ba70c1a src https://go-boringcrypto.storage.googleapis.com/go1.12.8b4.src.tar.gz c12b1d56ba4e0572f85a08681e05c66293ad53f04b11ce74c688d78fcb882061 +go1.12.9b4 ee88e5b118b5 linux-amd64 https://go-boringcrypto.storage.googleapis.com/go1.12.9b4.linux-amd64.tar.gz d90989cba1db647b795400a9520eab2fa30f8dea50f4189b18d53f757a4bac44 +go1.12.9b4 ee88e5b118b5 src https://go-boringcrypto.storage.googleapis.com/go1.12.9b4.src.tar.gz 9d4efed8e13fa5ebdadd4fc22f9e35e67bfb34322570c83a15a0879472412e13 diff --git a/misc/boring/VERSION b/misc/boring/VERSION new file mode 100644 index 0000000000..b8626c4cff --- /dev/null +++ b/misc/boring/VERSION @@ -0,0 +1 @@ +4 diff --git a/misc/boring/build.docker b/misc/boring/build.docker new file mode 100755 index 0000000000..fb73edc88e --- /dev/null +++ b/misc/boring/build.docker @@ -0,0 +1,57 @@ +#!/bin/bash +# Copyright 2017 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +# build.docker builds and publishes a Docker image for +# a given Go+BoringCrypto release. + +set -e + +# With no arguments, use the most recent linux-amd64 release in the RELEASES file. +case "$#" in +0) + version=$(grep linux-amd64 RELEASES | tail -1 | awk '{print $1}');; +1) + version="$1";; +*) + echo 'usage: build.docker [version]' >&2 + exit 2 +esac + +url="$(grep "^$version .* linux-amd64 " RELEASES | awk '{print $4}')" +sha256="$(grep "^$version .* linux-amd64 " RELEASES | awk '{print $5}')" +if [ "$sha256" = "" ]; then + echo "cannot find $version in RELEASES file" >&2 + exit 2 +fi + +# Build a temporary directory with a Dockerfile. +dir=$(mktemp -d) +trap "rm -rf $dir" EXIT + +if echo "$url" | grep '!' >/dev/null; then + # ! is sed delimiter below. Should never happen. + echo "URL contains an exclamation mark!" >&2 + exit 2 +fi + +sed "s!UUU!$url!; s/SSS/$sha256/" dockerfile.in >$dir/Dockerfile +cp go-wrapper $dir/go-wrapper + +dversion=$(echo "$version" | sed 's/^go//') +docker build --pull -t goboring/golang:$dversion $dir +docker run goboring/golang:$dversion go version +docker run goboring/golang:$dversion go tool nm /usr/local/go/bin/go >$dir/nm +if ! grep crypto/internal/boring/sig.BoringCrypto $dir/nm >/dev/null; then + echo 'built docker image but did NOT find sig.BoringCrypto in go command!' >&2 + exit 2 +fi +if egrep 'crypto/sha256\.\(\*digest\)' $dir/nm >/dev/null; then + echo 'built docker image but DID find sha256.(*digest) in go command unexpectedly!' >&2 + exit 2 +fi +docker push goboring/golang:$dversion + +echo +echo published as goboring/golang:$dversion diff --git a/misc/boring/build.release b/misc/boring/build.release new file mode 100755 index 0000000000..2256ff2e8f --- /dev/null +++ b/misc/boring/build.release @@ -0,0 +1,104 @@ +#!/bin/bash +# Copyright 2017 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +# build.release builds and publishes a new Go+BoringCrypto release. +# After running this script, the change to the RELEASES file should be +# sent out for review and committed to the repository (but the release +# is already done, so there's not much to review). + +set -e + +case "$#" in +0) + rev=HEAD;; +1) + rev="$1";; +*) + echo 'usage: build.release [git-rev]' >&2 + exit 2 +esac + +# Determine commit to use. +commit=$(git rev-parse "$rev" | awk '{print substr($1, 1, 12)}') +if [ "$commit" = "" ]; then + echo 'cannot find commit in git history' >&2 + exit 2 +fi + +# Determine base Go release from tags. +base=$(git log --decorate=short --oneline "$rev" | grep 'tag: go' | sed 1q | sed 's/[),].*//; s/.*tag: //') +if [ "$base" = "" ]; then + echo "cannot find go release tag in git history for $rev" >&2 + exit 2 +fi + +# Determine boring crypto version from file. +boring=$(git show "$commit:misc/boring/VERSION") +if [ "$boring" = "" ]; then + echo "missing BORINGVERSION file in $commit" >&2 + exit 2 +fi + +# Make sure we're not redefining a published release. +version="${base}b${boring}" +if grep "^$version " RELEASES >/dev/null; then + echo "found $version in RELEASES - not rereleasing" >&2 + exit 2 +fi + +# Show what's going on, while the release builds. +# Good time for user to type ^C if something is wrong. +echo >&2 +echo "building $version from $commit" >&2 +echo >&2 +git log -n1 "$commit" >&2 +echo >&2 + +# Build the release tool in a temporary directory. +dir=$(mktemp -d) +trap "rm -rf $dir" EXIT +export GO111MODULE=on +export GOBIN="$dir" +(cd "$dir"; go get golang.org/x/build/cmd/release) + +# Build the release. +sha() { + if hash sha256sum 2>/dev/null; then + sha256sum "$@" + else + shasum -a 256 "$@" + fi +} +shortgo=$(echo "$base" | perl -pe 's/(go\d+\.\d+)(\.\d+|rc\d+)/$1/') +$dir/release -target linux-amd64 -rev "$commit" -version "$version" -tools "release-branch.$shortgo" -net "release-branch.$shortgo" +$dir/release -target src -rev "$commit" -version "$version" -tools "release-branch.$shortgo" -net "release-branch.$shortgo" +output="$version.linux-amd64.tar.gz" +ls -l "$output" +sha256=$(sha "$output" | awk '{print $1}') +outputsrc="$version.src.tar.gz" +ls -l "$outputsrc" +sha256src=$(sha "$outputsrc" | awk '{print $1}') + +trap "rm -f /tmp/go.release.$$ /tmp/go.nm.$$" EXIT +tar -xzf "$output" -O go/bin/go >/tmp/go.release.$$ +go tool nm /tmp/go.release.$$ >/tmp/go.nm.$$ +if ! grep crypto/internal/boring/sig.BoringCrypto /tmp/go.nm.$$ >/dev/null; then + echo 'built release but did NOT find sig.BoringCrypto in go command!' >&2 + exit 2 +fi +if egrep 'crypto/sha256\.\(\*digest\)' /tmp/go.nm.$$ >/dev/null; then + echo 'built release but DID find sha256.(*digest) in go command unexpectedly!' >&2 + exit 2 +fi + +# Publish the release. +gsutil cp "$output" gs://go-boringcrypto/ +url="https://go-boringcrypto.storage.googleapis.com/$output" +gsutil cp "$outputsrc" gs://go-boringcrypto/ +urlsrc="https://go-boringcrypto.storage.googleapis.com/$outputsrc" + +# Record that it was published. +echo "$version $commit linux-amd64 $url $sha256" >>RELEASES +echo "$version $commit src $urlsrc $sha256src" >>RELEASES diff --git a/misc/boring/dockerfile.in b/misc/boring/dockerfile.in new file mode 100644 index 0000000000..dc03cd6c2f --- /dev/null +++ b/misc/boring/dockerfile.in @@ -0,0 +1,29 @@ +# Template for Dockerfile, used in build.docker script. +# Based on https://github.com/docker-library/golang/blob/master/1.9-rc/stretch/Dockerfile +FROM buildpack-deps:stretch-scm + +# gcc for cgo +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + gcc \ + libc6-dev \ + make \ + pkg-config \ + && rm -rf /var/lib/apt/lists/* + +ADD UUU /go.tgz + +RUN set -eux; \ + \ + echo "SSS /go.tgz" | sha256sum -c -; \ + tar -C /usr/local -xzf /go.tgz; \ + rm /go.tgz; \ + export PATH="/usr/local/go/bin:$PATH"; \ + go version + +ENV GOPATH /go +ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH +RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH" +WORKDIR $GOPATH + +COPY go-wrapper /usr/local/bin/ diff --git a/misc/boring/go-wrapper b/misc/boring/go-wrapper new file mode 100755 index 0000000000..eacd6cc8f6 --- /dev/null +++ b/misc/boring/go-wrapper @@ -0,0 +1,100 @@ +#!/bin/sh +# Copied from https://raw.githubusercontent.com/docker-library/golang/master/1.9-rc/stretch/go-wrapper +# Copied into Docker images. + +set -e + +usage() { + base="$(basename "$0")" + cat <<EOUSAGE + +usage: $base command [args] + +This script assumes that is is run from the root of your Go package (for +example, "/go/src/app" if your GOPATH is set to "/go"). + +In Go 1.4, a feature was introduced to supply the canonical "import path" for a +given package in a comment attached to a package statement +(https://golang.org/s/go14customimport). + +This script allows us to take a generic directory of Go source files such as +"/go/src/app" and determine that the canonical "import path" of where that code +expects to live and reference itself is "github.com/jsmith/my-cool-app". It +will then ensure that "/go/src/github.com/jsmith/my-cool-app" is a symlink to +"/go/src/app", which allows us to build and run it under the proper package +name. + +For compatibility with versions of Go older than 1.4, the "import path" may also +be placed in a file named ".godir". + +Available Commands: + + $base download + $base download -u + (equivalent to "go get -d [args] [godir]") + + $base install + $base install -race + (equivalent to "go install [args] [godir]") + + $base run + $base run -app -specific -arguments + (assumes "GOPATH/bin" is in "PATH") + +EOUSAGE +} + +# make sure there is a subcommand specified +if [ "$#" -eq 0 ]; then + usage >&2 + exit 1 +fi +# "shift" so that "$@" becomes the remaining arguments and can be passed along to other "go" subcommands easily +cmd="$1" +shift + +goDir="$(go list -e -f '{{.ImportComment}}' 2>/dev/null || true)" + +if [ -z "$goDir" -a -s .godir ]; then + goDir="$(cat .godir)" +fi + +dir="$(pwd -P)" +if [ "$goDir" ]; then + goPath="${GOPATH%%:*}" # this just grabs the first path listed in GOPATH, if there are multiple (which is the detection logic "go get" itself uses, too) + goDirPath="$goPath/src/$goDir" + mkdir -p "$(dirname "$goDirPath")" + if [ ! -e "$goDirPath" ]; then + ln -sfv "$dir" "$goDirPath" + elif [ ! -L "$goDirPath" ]; then + echo >&2 "error: $goDirPath already exists but is unexpectedly not a symlink!" + exit 1 + fi + goBin="$goPath/bin/$(basename "$goDir")" +else + goBin="$(basename "$dir")" # likely "app" +fi + +case "$cmd" in + download) + set -- go get -v -d "$@" + if [ "$goDir" ]; then set -- "$@" "$goDir"; fi + set -x; exec "$@" + ;; + + install) + set -- go install -v "$@" + if [ "$goDir" ]; then set -- "$@" "$goDir"; fi + set -x; exec "$@" + ;; + + run) + set -x; exec "$goBin" "$@" + ;; + + *) + echo >&2 'error: unknown command:' "$cmd" + usage >&2 + exit 1 + ;; +esac diff --git a/misc/boring/merge.sh b/misc/boring/merge.sh new file mode 100755 index 0000000000..6d13ade23c --- /dev/null +++ b/misc/boring/merge.sh @@ -0,0 +1,32 @@ +#! /bin/bash +set -euo pipefail + +if [ "$#" -ne 2 ]; then + echo "usage: merge.sh <target branch> <source revision>" + echo "" + echo "example: merge.sh dev.boringcrypto master" + echo " merge.sh dev.boringcrypto.go1.10 go1.10.7" + exit 1 +fi + +set -x +TARGET="$1" +SOURCE="$2" +WORKTREE="$(mktemp -d)" +BRANCH="boring/merge-$TARGET-$(date +%Y%m%d%H%M%S)" + +git fetch +git worktree add --track -b "$BRANCH" "$WORKTREE" "origin/$TARGET" + +cd "$WORKTREE" +export GIT_GOFMT_HOOK=off +git merge -m "all: merge $SOURCE into $TARGET" "$SOURCE" || \ + (git rm VERSION && git commit -m "all: merge $SOURCE into $TARGET") + +if ! git log --format=%B -n 1 | grep "\[dev.boringcrypto"; then + echo "The commit does not seem to be targeting a BoringCrypto branch." + exit 1 +fi + +git codereview mail -r dmitshur@golang.org,filippo@golang.org -trybot HEAD +cd - && git worktree remove "$WORKTREE" diff --git a/misc/boring/release.sh b/misc/boring/release.sh new file mode 100755 index 0000000000..f3b8876821 --- /dev/null +++ b/misc/boring/release.sh @@ -0,0 +1,32 @@ +#! /bin/bash +set -euo pipefail + +if [ "$#" -eq 0 ]; then + echo "usage: <target branch> [<target branch> ...]" + echo "" + echo "example: release.sh dev.boringcrypto.go1.11 dev.boringcrypto.go1.12" + exit 1 +fi + +set -x +WORKTREE="$(mktemp -d)" +BRANCH="boring/release-$(date +%Y%m%d%H%M%S)" + +git fetch +git worktree add --track -b "$BRANCH" "$WORKTREE" origin/dev.boringcrypto + +cd "$WORKTREE/src" +./make.bash + +cd ../misc/boring +for branch in "$@"; do + ./build.release "origin/$branch" +done +./build.docker + +git add RELEASES +git commit -m "misc/boring: add new releases to RELEASES file" +git codereview mail -r dmitshur@golang.org,filippo@golang.org + +rm *.tar.gz +cd - && git worktree remove "$WORKTREE" diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index c2d9ba2fe0..39293917c7 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -1364,6 +1364,33 @@ func dtypesym(t *types.Type) *obj.LSym { // for security, only the exported fields. case TSTRUCT: fields := t.Fields().Slice() + + // omitFieldForAwfulBoringCryptoKludge reports whether + // the field t should be omitted from the reflect data. + // In the crypto/... packages we omit an unexported field + // named "boring", to keep from breaking client code that + // expects rsa.PublicKey etc to have only public fields. + // As the name suggests, this is an awful kludge, but it is + // limited to the dev.boringcrypto branch and avoids + // much more invasive effects elsewhere. + omitFieldForAwfulBoringCryptoKludge := func(t *types.Field) bool { + if t.Sym == nil || t.Sym.Name != "boring" || t.Sym.Pkg == nil { + return false + } + path := t.Sym.Pkg.Path + if t.Sym.Pkg == localpkg { + path = myimportpath + } + return strings.HasPrefix(path, "crypto/") + } + newFields := fields[:0:0] + for _, t1 := range fields { + if !omitFieldForAwfulBoringCryptoKludge(t1) { + newFields = append(newFields, t1) + } + } + fields = newFields + for _, t1 := range fields { dtypesym(t1.Type) } diff --git a/src/cmd/go/go_boring_test.go b/src/cmd/go/go_boring_test.go new file mode 100644 index 0000000000..0000497f06 --- /dev/null +++ b/src/cmd/go/go_boring_test.go @@ -0,0 +1,20 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main_test + +import "testing" + +func TestBoringInternalLink(t *testing.T) { + tg := testgo(t) + defer tg.cleanup() + tg.parallel() + tg.tempFile("main.go", `package main + import "crypto/sha1" + func main() { + sha1.New() + }`) + tg.run("build", "-ldflags=-w -extld=false", tg.path("main.go")) + tg.run("build", "-ldflags=-extld=false", tg.path("main.go")) +} diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go index f6caa01fd2..5d8a71755a 100644 --- a/src/cmd/go/go_test.go +++ b/src/cmd/go/go_test.go @@ -4259,8 +4259,12 @@ func TestBinaryOnlyPackages(t *testing.T) { tg.grepStdout("p2: false", "p2 listed as BinaryOnly") } -// Issue 16050. -func TestAlwaysLinkSysoFiles(t *testing.T) { +// Issue 16050 and 21884. +func TestLinkSysoFiles(t *testing.T) { + if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { + t.Skip("not linux/amd64") + } + tg := testgo(t) defer tg.cleanup() tg.parallel() @@ -4279,6 +4283,10 @@ func TestAlwaysLinkSysoFiles(t *testing.T) { tg.setenv("CGO_ENABLED", "0") tg.run("list", "-f", "{{.SysoFiles}}", "syso") tg.grepStdout("a.syso", "missing syso file with CGO_ENABLED=0") + + tg.setenv("CGO_ENABLED", "1") + tg.run("list", "-msan", "-f", "{{.SysoFiles}}", "syso") + tg.grepStdoutNot("a.syso", "unexpected syso file with -msan") } // Issue 16120. diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go index f0a899fdc8..07f7115e33 100644 --- a/src/cmd/go/internal/load/pkg.go +++ b/src/cmd/go/internal/load/pkg.go @@ -278,6 +278,12 @@ func (p *Package) copyBuild(pp *build.Package) { p.SwigFiles = pp.SwigFiles p.SwigCXXFiles = pp.SwigCXXFiles p.SysoFiles = pp.SysoFiles + if cfg.BuildMSan { + // There's no way for .syso files to be built both with and without + // support for memory sanitizer. Assume they are built without, + // and drop them. + p.SysoFiles = nil + } p.CgoCFLAGS = pp.CgoCFLAGS p.CgoCPPFLAGS = pp.CgoCPPFLAGS p.CgoCXXFLAGS = pp.CgoCXXFLAGS diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 3fa258d275..9cbd77011f 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -931,6 +931,7 @@ var hostobj []Hostobj // These packages can use internal linking mode. // Others trigger external mode. var internalpkg = []string{ + "crypto/internal/boring", "crypto/x509", "net", "os/user", diff --git a/src/crypto/aes/cipher.go b/src/crypto/aes/cipher.go index bb93fbb36e..29d01796eb 100644 --- a/src/crypto/aes/cipher.go +++ b/src/crypto/aes/cipher.go @@ -10,6 +10,8 @@ import ( "strconv" ) +import "crypto/internal/boring" + // The AES block size in bytes. const BlockSize = 16 @@ -37,6 +39,9 @@ func NewCipher(key []byte) (cipher.Block, error) { case 16, 24, 32: break } + if boring.Enabled { + return boring.NewAESCipher(key) + } return newCipher(key) } diff --git a/src/crypto/aes/cipher_asm.go b/src/crypto/aes/cipher_asm.go index 646bdfa5c0..4936699481 100644 --- a/src/crypto/aes/cipher_asm.go +++ b/src/crypto/aes/cipher_asm.go @@ -12,6 +12,8 @@ import ( "internal/cpu" ) +import "crypto/internal/boring" + // defined in asm_*.s //go:noescape @@ -56,6 +58,7 @@ func newCipher(key []byte) (cipher.Block, error) { func (c *aesCipherAsm) BlockSize() int { return BlockSize } func (c *aesCipherAsm) Encrypt(dst, src []byte) { + boring.Unreachable() if len(src) < BlockSize { panic("crypto/aes: input not full block") } @@ -69,6 +72,7 @@ func (c *aesCipherAsm) Encrypt(dst, src []byte) { } func (c *aesCipherAsm) Decrypt(dst, src []byte) { + boring.Unreachable() if len(src) < BlockSize { panic("crypto/aes: input not full block") } diff --git a/src/crypto/ecdsa/boring.go b/src/crypto/ecdsa/boring.go new file mode 100644 index 0000000000..fa15ecb850 --- /dev/null +++ b/src/crypto/ecdsa/boring.go @@ -0,0 +1,100 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ecdsa + +import ( + "crypto/internal/boring" + "math/big" + "sync/atomic" + "unsafe" +) + +// Cached conversions from Go PublicKey/PrivateKey to BoringCrypto. +// +// A new 'boring atomic.Value' field in both PublicKey and PrivateKey +// serves as a cache for the most recent conversion. The cache is an +// atomic.Value because code might reasonably set up a key and then +// (thinking it immutable) use it from multiple goroutines simultaneously. +// The first operation initializes the cache; if there are multiple simultaneous +// first operations, they will do redundant work but not step on each other. +// +// We could just assume that once used in a Sign or Verify operation, +// a particular key is never again modified, but that has not been a +// stated assumption before. Just in case there is any existing code that +// does modify the key between operations, we save the original values +// alongside the cached BoringCrypto key and check that the real key +// still matches before using the cached key. The theory is that the real +// operations are significantly more expensive than the comparison. + +type boringPub struct { + key *boring.PublicKeyECDSA + orig PublicKey +} + +func boringPublicKey(pub *PublicKey) (*boring.PublicKeyECDSA, error) { + b := (*boringPub)(atomic.LoadPointer(&pub.boring)) + if b != nil && publicKeyEqual(&b.orig, pub) { + return b.key, nil + } + + b = new(boringPub) + b.orig = copyPublicKey(pub) + key, err := boring.NewPublicKeyECDSA(b.orig.Curve.Params().Name, b.orig.X, b.orig.Y) + if err != nil { + return nil, err + } + b.key = key + atomic.StorePointer(&pub.boring, unsafe.Pointer(b)) + return key, nil +} + +type boringPriv struct { + key *boring.PrivateKeyECDSA + orig PrivateKey +} + +func boringPrivateKey(priv *PrivateKey) (*boring.PrivateKeyECDSA, error) { + b := (*boringPriv)(atomic.LoadPointer(&priv.boring)) + if b != nil && privateKeyEqual(&b.orig, priv) { + return b.key, nil + } + + b = new(boringPriv) + b.orig = copyPrivateKey(priv) + key, err := boring.NewPrivateKeyECDSA(b.orig.Curve.Params().Name, b.orig.X, b.orig.Y, b.orig.D) + if err != nil { + return nil, err + } + b.key = key + atomic.StorePointer(&priv.boring, unsafe.Pointer(b)) + return key, nil +} + +func publicKeyEqual(k1, k2 *PublicKey) bool { + return k1.X != nil && + k1.Curve.Params() == k2.Curve.Params() && + k1.X.Cmp(k2.X) == 0 && + k1.Y.Cmp(k2.Y) == 0 +} + +func privateKeyEqual(k1, k2 *PrivateKey) bool { + return publicKeyEqual(&k1.PublicKey, &k2.PublicKey) && + k1.D.Cmp(k2.D) == 0 +} + +func copyPublicKey(k *PublicKey) PublicKey { + return PublicKey{ + Curve: k.Curve, + X: new(big.Int).Set(k.X), + Y: new(big.Int).Set(k.Y), + } +} + +func copyPrivateKey(k *PrivateKey) PrivateKey { + return PrivateKey{ + PublicKey: copyPublicKey(&k.PublicKey), + D: new(big.Int).Set(k.D), + } +} diff --git a/src/crypto/ecdsa/ecdsa.go b/src/crypto/ecdsa/ecdsa.go index cc1134f74f..22de2a5661 100644 --- a/src/crypto/ecdsa/ecdsa.go +++ b/src/crypto/ecdsa/ecdsa.go @@ -29,6 +29,11 @@ import ( "math/big" ) +import ( + "crypto/internal/boring" + "unsafe" +) + // A invertible implements fast inverse mod Curve.Params().N type invertible interface { // Inverse returns the inverse of k in GF(P) @@ -48,12 +53,16 @@ const ( type PublicKey struct { elliptic.Curve X, Y *big.Int + + boring unsafe.Pointer } // PrivateKey represents an ECDSA private key. type PrivateKey struct { PublicKey D *big.Int + + boring unsafe.Pointer } type ecdsaSignature struct { @@ -73,6 +82,15 @@ func (priv *PrivateKey) Public() crypto.PublicKey { // where the private part is kept in, for example, a hardware module. Common // uses should use the Sign function in this package directly. func (priv *PrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) { + if boring.Enabled && rand == boring.RandReader { + b, err := boringPrivateKey(priv) + if err != nil { + return nil, err + } + return boring.SignMarshalECDSA(b, digest) + } + boring.UnreachableExceptTests() + r, s, err := Sign(rand, priv, digest) if err != nil { return nil, err @@ -102,6 +120,15 @@ func randFieldElement(c elliptic.Curve, rand io.Reader) (k *big.Int, err error) // GenerateKey generates a public and private key pair. func GenerateKey(c elliptic.Curve, rand io.Reader) (*PrivateKey, error) { + if boring.Enabled && rand == boring.RandReader { + x, y, d, err := boring.GenerateKeyECDSA(c.Params().Name) + if err != nil { + return nil, err + } + return &PrivateKey{PublicKey: PublicKey{Curve: c, X: x, Y: y}, D: d}, nil + } + boring.UnreachableExceptTests() + k, err := randFieldElement(c, rand) if err != nil { return nil, err @@ -155,6 +182,15 @@ var errZeroParam = errors.New("zero parameter") func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) { randutil.MaybeReadByte(rand) + if boring.Enabled && rand == boring.RandReader { + b, err := boringPrivateKey(priv) + if err != nil { + return nil, nil, err + } + return boring.SignECDSA(b, hash) + } + boring.UnreachableExceptTests() + // Get min(log2(q) / 2, 256) bits of entropy from rand. entropylen := (priv.Curve.Params().BitSize + 7) / 16 if entropylen > 32 { @@ -231,6 +267,15 @@ func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err err // Verify verifies the signature in r, s of hash using the public key, pub. Its // return value records whether the signature is valid. func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool { + if boring.Enabled { + b, err := boringPublicKey(pub) + if err != nil { + return false + } + return boring.VerifyECDSA(b, hash, r, s) + } + boring.UnreachableExceptTests() + // See [NSA] 3.4.2 c := pub.Curve N := c.Params().N diff --git a/src/crypto/hmac/hmac.go b/src/crypto/hmac/hmac.go index 801ece67ae..52885b0760 100644 --- a/src/crypto/hmac/hmac.go +++ b/src/crypto/hmac/hmac.go @@ -26,6 +26,8 @@ import ( "hash" ) +import "crypto/internal/boring" + // FIPS 198-1: // https://csrc.nist.gov/publications/fips/fips198-1/FIPS-198-1_final.pdf @@ -68,6 +70,13 @@ func (h *hmac) Reset() { // the returned Hash does not implement encoding.BinaryMarshaler // or encoding.BinaryUnmarshaler. func New(h func() hash.Hash, key []byte) hash.Hash { + if boring.Enabled { + hm := boring.NewHMAC(h, key) + if hm != nil { + return hm + } + // BoringCrypto did not recognize h, so fall through to standard Go code. + } hm := new(hmac) hm.outer = h() hm.inner = h() diff --git a/src/crypto/hmac/hmac_test.go b/src/crypto/hmac/hmac_test.go index eea345edb6..96e84f74a1 100644 --- a/src/crypto/hmac/hmac_test.go +++ b/src/crypto/hmac/hmac_test.go @@ -5,6 +5,7 @@ package hmac import ( + "bytes" "crypto/md5" "crypto/sha1" "crypto/sha256" @@ -518,6 +519,31 @@ var hmacTests = []hmacTest{ sha512.Size, sha512.BlockSize, }, + // HMAC without key is dumb but should probably not fail. + { + sha1.New, + []byte{}, + []byte("message"), + "d5d1ed05121417247616cfc8378f360a39da7cfa", + sha1.Size, + sha1.BlockSize, + }, + { + sha256.New, + []byte{}, + []byte("message"), + "eb08c1f56d5ddee07f7bdf80468083da06b64cf4fac64fe3a90883df5feacae4", + sha256.Size, + sha256.BlockSize, + }, + { + sha512.New, + []byte{}, + []byte("message"), + "08fce52f6395d59c2a3fb8abb281d74ad6f112b9a9c787bcea290d94dadbc82b2ca3e5e12bf2277c7fedbb0154d5493e41bb7459f63c8e39554ea3651b812492", + sha512.Size, + sha512.BlockSize, + }, } func TestHMAC(t *testing.T) { @@ -569,6 +595,42 @@ func TestEqual(t *testing.T) { } } +func TestWriteAfterSum(t *testing.T) { + h := New(sha1.New, nil) + h.Write([]byte("hello")) + sumHello := h.Sum(nil) + + h = New(sha1.New, nil) + h.Write([]byte("hello world")) + sumHelloWorld := h.Sum(nil) + + // Test that Sum has no effect on future Sum or Write operations. + // This is a bit unusual as far as usage, but it's allowed + // by the definition of Go hash.Hash, and some clients expect it to work. + h = New(sha1.New, nil) + h.Write([]byte("hello")) + if sum := h.Sum(nil); !bytes.Equal(sum, sumHello) { + t.Fatalf("1st Sum after hello = %x, want %x", sum, sumHello) + } + if sum := h.Sum(nil); !bytes.Equal(sum, sumHello) { + t.Fatalf("2nd Sum after hello = %x, want %x", sum, sumHello) + } + + h.Write([]byte(" world")) + if sum := h.Sum(nil); !bytes.Equal(sum, sumHelloWorld) { + t.Fatalf("1st Sum after hello world = %x, want %x", sum, sumHelloWorld) + } + if sum := h.Sum(nil); !bytes.Equal(sum, sumHelloWorld) { + t.Fatalf("2nd Sum after hello world = %x, want %x", sum, sumHelloWorld) + } + + h.Reset() + h.Write([]byte("hello")) + if sum := h.Sum(nil); !bytes.Equal(sum, sumHello) { + t.Fatalf("Sum after Reset + hello = %x, want %x", sum, sumHello) + } +} + func BenchmarkHMACSHA256_1K(b *testing.B) { key := make([]byte, 32) buf := make([]byte, 1024) diff --git a/src/crypto/internal/boring/LICENSE b/src/crypto/internal/boring/LICENSE new file mode 100644 index 0000000000..4a618c3e8a --- /dev/null +++ b/src/crypto/internal/boring/LICENSE @@ -0,0 +1,200 @@ +The Go source code and supporting files in this directory +are covered by the usual Go license (see ../../../../LICENSE). + +The goboringcrypto_linux_amd64.syso object file is built +from BoringSSL source code by build/build.sh and is covered +by the BoringSSL license reproduced below and also at +https://boringssl.googlesource.com/boringssl/+/fips-20170615/LICENSE. + +BoringSSL is a fork of OpenSSL. As such, large parts of it fall under OpenSSL +licensing. Files that are completely new have a Google copyright and an ISC +license. This license is reproduced at the bottom of this file. + +Contributors to BoringSSL are required to follow the CLA rules for Chromium: +https://cla.developers.google.com/clas + +Some files from Intel are under yet another license, which is also included +underneath. + +The OpenSSL toolkit stays under a dual license, i.e. both the conditions of the +OpenSSL License and the original SSLeay license apply to the toolkit. See below +for the actual license texts. Actually both licenses are BSD-style Open Source +licenses. In case of any license issues related to OpenSSL please contact +openssl-core@openssl.org. + +The following are Google-internal bug numbers where explicit permission from +some authors is recorded for use of their work. (This is purely for our own +record keeping.) + 27287199 + 27287880 + 27287883 + + OpenSSL License + --------------- + +/* ==================================================================== + * Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + + Original SSLeay License + ----------------------- + +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + + +ISC license used for completely new code in BoringSSL: + +/* Copyright (c) 2015, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + + +Some files from Intel carry the following license: + +# Copyright (c) 2012, Intel Corporation +# +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the +# distribution. +# +# * Neither the name of the Intel Corporation nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# +# THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION ""AS IS"" AND ANY +# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/crypto/internal/boring/aes.go b/src/crypto/internal/boring/aes.go new file mode 100644 index 0000000000..504a841cd8 --- /dev/null +++ b/src/crypto/internal/boring/aes.go @@ -0,0 +1,391 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux,amd64 +// +build !android +// +build !cmd_go_bootstrap +// +build !msan + +package boring + +/* + +#include "goboringcrypto.h" + +// These wrappers allocate out_len on the C stack, and check that it matches the expected +// value, to avoid having to pass a pointer from Go, which would escape to the heap. + +int EVP_AEAD_CTX_seal_wrapper(const GO_EVP_AEAD_CTX *ctx, uint8_t *out, + size_t exp_out_len, + const uint8_t *nonce, size_t nonce_len, + const uint8_t *in, size_t in_len, + const uint8_t *ad, size_t ad_len) { + size_t out_len; + int ok = _goboringcrypto_EVP_AEAD_CTX_seal(ctx, out, &out_len, exp_out_len, + nonce, nonce_len, in, in_len, ad, ad_len); + if (out_len != exp_out_len) { + return 0; + } + return ok; +}; + +int EVP_AEAD_CTX_open_wrapper(const GO_EVP_AEAD_CTX *ctx, uint8_t *out, + size_t exp_out_len, + const uint8_t *nonce, size_t nonce_len, + const uint8_t *in, size_t in_len, + const uint8_t *ad, size_t ad_len) { + size_t out_len; + int ok = _goboringcrypto_EVP_AEAD_CTX_open(ctx, out, &out_len, exp_out_len, + nonce, nonce_len, in, in_len, ad, ad_len); + if (out_len != exp_out_len) { + return 0; + } + return ok; +}; + +*/ +import "C" +import ( + "crypto/cipher" + "errors" + "runtime" + "strconv" + "unsafe" +) + +type aesKeySizeError int + +func (k aesKeySizeError) Error() string { + return "crypto/aes: invalid key size " + strconv.Itoa(int(k)) +} + +const aesBlockSize = 16 + +type aesCipher struct { + key []byte + enc C.GO_AES_KEY + dec C.GO_AES_KEY +} + +type extraModes interface { + // Copied out of crypto/aes/modes.go. + NewCBCEncrypter(iv []byte) cipher.BlockMode + NewCBCDecrypter(iv []byte) cipher.BlockMode + NewCTR(iv []byte) cipher.Stream + NewGCM(nonceSize, tagSize int) (cipher.AEAD, error) + + // Invented for BoringCrypto. + NewGCMTLS() (cipher.AEAD, error) +} + +var _ extraModes = (*aesCipher)(nil) + +func NewAESCipher(key []byte) (cipher.Block, error) { + c := &aesCipher{key: make([]byte, len(key))} + copy(c.key, key) + // Note: 0 is success, contradicting the usual BoringCrypto convention. + if C._goboringcrypto_AES_set_decrypt_key((*C.uint8_t)(unsafe.Pointer(&c.key[0])), C.uint(8*len(c.key)), &c.dec) != 0 || + C._goboringcrypto_AES_set_encrypt_key((*C.uint8_t)(unsafe.Pointer(&c.key[0])), C.uint(8*len(c.key)), &c.enc) != 0 { + return nil, aesKeySizeError(len(key)) + } + return c, nil +} + +func (c *aesCipher) BlockSize() int { return aesBlockSize } + +func (c *aesCipher) Encrypt(dst, src []byte) { + if inexactOverlap(dst, src) { + panic("crypto/cipher: invalid buffer overlap") + } + if len(src) < aesBlockSize { + panic("crypto/aes: input not full block") + } + if len(dst) < aesBlockSize { + panic("crypto/aes: output not full block") + } + C._goboringcrypto_AES_encrypt( + (*C.uint8_t)(unsafe.Pointer(&src[0])), + (*C.uint8_t)(unsafe.Pointer(&dst[0])), + &c.enc) +} + +func (c *aesCipher) Decrypt(dst, src []byte) { + if inexactOverlap(dst, src) { + panic("crypto/cipher: invalid buffer overlap") + } + if len(src) < aesBlockSize { + panic("crypto/aes: input not full block") + } + if len(dst) < aesBlockSize { + panic("crypto/aes: output not full block") + } + C._goboringcrypto_AES_decrypt( + (*C.uint8_t)(unsafe.Pointer(&src[0])), + (*C.uint8_t)(unsafe.Pointer(&dst[0])), + &c.dec) +} + +type aesCBC struct { + key *C.GO_AES_KEY + mode C.int + iv [aesBlockSize]byte +} + +func (x *aesCBC) BlockSize() int { return aesBlockSize } + +func (x *aesCBC) CryptBlocks(dst, src []byte) { + if inexactOverlap(dst, src) { + panic("crypto/cipher: invalid buffer overlap") + } + if len(src)%aesBlockSize != 0 { + panic("crypto/cipher: input not full blocks") + } + if len(dst) < len(src) { + panic("crypto/cipher: output smaller than input") + } + if len(src) > 0 { + C._goboringcrypto_AES_cbc_encrypt( + (*C.uint8_t)(unsafe.Pointer(&src[0])), + (*C.uint8_t)(unsafe.Pointer(&dst[0])), + C.size_t(len(src)), x.key, + (*C.uint8_t)(unsafe.Pointer(&x.iv[0])), x.mode) + } +} + +func (x *aesCBC) SetIV(iv []byte) { + if len(iv) != aesBlockSize { + panic("cipher: incorrect length IV") + } + copy(x.iv[:], iv) +} + +func (c *aesCipher) NewCBCEncrypter(iv []byte) cipher.BlockMode { + x := &aesCBC{key: &c.enc, mode: C.GO_AES_ENCRYPT} + copy(x.iv[:], iv) + return x +} + +func (c *aesCipher) NewCBCDecrypter(iv []byte) cipher.BlockMode { + x := &aesCBC{key: &c.dec, mode: C.GO_AES_DECRYPT} + copy(x.iv[:], iv) + return x +} + +type aesCTR struct { + key *C.GO_AES_KEY + iv [aesBlockSize]byte + num C.uint + ecount_buf [16]C.uint8_t +} + +func (x *aesCTR) XORKeyStream(dst, src []byte) { + if inexactOverlap(dst, src) { + panic("crypto/cipher: invalid buffer overlap") + } + if len(dst) < len(src) { + panic("crypto/cipher: output smaller than input") + } + if len(src) == 0 { + return + } + C._goboringcrypto_AES_ctr128_encrypt( + (*C.uint8_t)(unsafe.Pointer(&src[0])), + (*C.uint8_t)(unsafe.Pointer(&dst[0])), + C.size_t(len(src)), x.key, (*C.uint8_t)(unsafe.Pointer(&x.iv[0])), + &x.ecount_buf[0], &x.num) +} + +func (c *aesCipher) NewCTR(iv []byte) cipher.Stream { + x := &aesCTR{key: &c.enc} + copy(x.iv[:], iv) + return x +} + +type aesGCM struct { + ctx C.GO_EVP_AEAD_CTX + aead *C.GO_EVP_AEAD +} + +const ( + gcmBlockSize = 16 + gcmTagSize = 16 + gcmStandardNonceSize = 12 +) + +type aesNonceSizeError int + +func (n aesNonceSizeError) Error() string { + return "crypto/aes: invalid GCM nonce size " + strconv.Itoa(int(n)) +} + +type noGCM struct { + cipher.Block +} + +func (c *aesCipher) NewGCM(nonceSize, tagSize int) (cipher.AEAD, error) { + if nonceSize != gcmStandardNonceSize && tagSize != gcmTagSize { + return nil, errors.New("crypto/aes: GCM tag and nonce sizes can't be non-standard at the same time") + } + // Fall back to standard library for GCM with non-standard nonce or tag size. + if nonceSize != gcmStandardNonceSize { + return cipher.NewGCMWithNonceSize(&noGCM{c}, nonceSize) + } + if tagSize != gcmTagSize { + return cipher.NewGCMWithTagSize(&noGCM{c}, tagSize) + } + return c.newGCM(false) +} + +func (c *aesCipher) NewGCMTLS() (cipher.AEAD, error) { + return c.newGCM(true) +} + +func (c *aesCipher) newGCM(tls bool) (cipher.AEAD, error) { + var aead *C.GO_EVP_AEAD + switch len(c.key) * 8 { + case 128: + if tls { + aead = C._goboringcrypto_EVP_aead_aes_128_gcm_tls12() + } else { + aead = C._goboringcrypto_EVP_aead_aes_128_gcm() + } + case 256: + if tls { + aead = C._goboringcrypto_EVP_aead_aes_256_gcm_tls12() + } else { + aead = C._goboringcrypto_EVP_aead_aes_256_gcm() + } + default: + // Fall back to standard library for GCM with non-standard key size. + return cipher.NewGCMWithNonceSize(&noGCM{c}, gcmStandardNonceSize) + } + + g := &aesGCM{aead: aead} + if C._goboringcrypto_EVP_AEAD_CTX_init(&g.ctx, aead, (*C.uint8_t)(unsafe.Pointer(&c.key[0])), C.size_t(len(c.key)), C.GO_EVP_AEAD_DEFAULT_TAG_LENGTH, nil) == 0 { + return nil, fail("EVP_AEAD_CTX_init") + } + // Note: Because of the finalizer, any time g.ctx is passed to cgo, + // that call must be followed by a call to runtime.KeepAlive(g), + // to make sure g is not collected (and finalized) before the cgo + // call returns. + runtime.SetFinalizer(g, (*aesGCM).finalize) + if g.NonceSize() != gcmStandardNonceSize { + panic("boringcrypto: internal confusion about nonce size") + } + if g.Overhead() != gcmTagSize { + panic("boringcrypto: internal confusion about tag size") + } + + return g, nil +} + +func (g *aesGCM) finalize() { + C._goboringcrypto_EVP_AEAD_CTX_cleanup(&g.ctx) +} + +func (g *aesGCM) NonceSize() int { + return int(C._goboringcrypto_EVP_AEAD_nonce_length(g.aead)) +} + +func (g *aesGCM) Overhead() int { + return int(C._goboringcrypto_EVP_AEAD_max_overhead(g.aead)) +} + +// base returns the address of the underlying array in b, +// being careful not to panic when b has zero length. +func base(b []byte) *C.uint8_t { + if len(b) == 0 { + return nil + } + return (*C.uint8_t)(unsafe.Pointer(&b[0])) +} + +func (g *aesGCM) Seal(dst, nonce, plaintext, additionalData []byte) []byte { + if len(nonce) != gcmStandardNonceSize { + panic("cipher: incorrect nonce length given to GCM") + } + if uint64(len(plaintext)) > ((1<<32)-2)*aesBlockSize || len(plaintext)+gcmTagSize < len(plaintext) { + panic("cipher: message too large for GCM") + } + if len(dst)+len(plaintext)+gcmTagSize < len(dst) { + panic("cipher: message too large for buffer") + } + + // Make room in dst to append plaintext+overhead. + n := len(dst) + for cap(dst) < n+len(plaintext)+gcmTagSize { + dst = append(dst[:cap(dst)], 0) + } + dst = dst[:n+len(plaintext)+gcmTagSize] + + // Check delayed until now to make sure len(dst) is accurate. + if inexactOverlap(dst[n:], plaintext) { + panic("cipher: invalid buffer overlap") + } + + outLen := C.size_t(len(plaintext) + gcmTagSize) + ok := C.EVP_AEAD_CTX_seal_wrapper( + &g.ctx, + (*C.uint8_t)(unsafe.Pointer(&dst[n])), outLen, + base(nonce), C.size_t(len(nonce)), + base(plaintext), C.size_t(len(plaintext)), + base(additionalData), C.size_t(len(additionalData))) + runtime.KeepAlive(g) + if ok == 0 { + panic(fail("EVP_AEAD_CTX_seal")) + } + return dst[:n+int(outLen)] +} + +var errOpen = errors.New("cipher: message authentication failed") + +func (g *aesGCM) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) { + if len(nonce) != gcmStandardNonceSize { + panic("cipher: incorrect nonce length given to GCM") + } + if len(ciphertext) < gcmTagSize { + return nil, errOpen + } + if uint64(len(ciphertext)) > ((1<<32)-2)*aesBlockSize+gcmTagSize { + return nil, errOpen + } + + // Make room in dst to append ciphertext without tag. + n := len(dst) + for cap(dst) < n+len(ciphertext)-gcmTagSize { + dst = append(dst[:cap(dst)], 0) + } + dst = dst[:n+len(ciphertext)-gcmTagSize] + + // Check delayed until now to make sure len(dst) is accurate. + if inexactOverlap(dst[n:], ciphertext) { + panic("cipher: invalid buffer overlap") + } + + outLen := C.size_t(len(ciphertext) - gcmTagSize) + ok := C.EVP_AEAD_CTX_open_wrapper( + &g.ctx, + base(dst[n:]), outLen, + base(nonce), C.size_t(len(nonce)), + base(ciphertext), C.size_t(len(ciphertext)), + base(additionalData), C.size_t(len(additionalData))) + runtime.KeepAlive(g) + if ok == 0 { + return nil, errOpen + } + return dst[:n+int(outLen)], nil +} + +func anyOverlap(x, y []byte) bool { + return len(x) > 0 && len(y) > 0 && + uintptr(unsafe.Pointer(&x[0])) <= uintptr(unsafe.Pointer(&y[len(y)-1])) && + uintptr(unsafe.Pointer(&y[0])) <= uintptr(unsafe.Pointer(&x[len(x)-1])) +} + +func inexactOverlap(x, y []byte) bool { + if len(x) == 0 || len(y) == 0 || &x[0] == &y[0] { + return false + } + return anyOverlap(x, y) +} diff --git a/src/crypto/internal/boring/boring.go b/src/crypto/internal/boring/boring.go new file mode 100644 index 0000000000..9ccad7eb5d --- /dev/null +++ b/src/crypto/internal/boring/boring.go @@ -0,0 +1,83 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux,amd64 +// +build !android +// +build !cmd_go_bootstrap +// +build !msan + +package boring + +// #include "goboringcrypto.h" +import "C" +import ( + "crypto/internal/boring/sig" + "math/big" +) + +const available = true + +func init() { + C._goboringcrypto_BORINGSSL_bcm_power_on_self_test() + if C._goboringcrypto_FIPS_mode() != 1 { + panic("boringcrypto: not in FIPS mode") + } + sig.BoringCrypto() +} + +// Unreachable marks code that should be unreachable +// when BoringCrypto is in use. It panics. +func Unreachable() { + panic("boringcrypto: invalid code execution") +} + +// provided by runtime to avoid os import +func runtime_arg0() string + +func hasSuffix(s, t string) bool { + return len(s) > len(t) && s[len(s)-len(t):] == t +} + +// UnreachableExceptTests marks code that should be unreachable +// when BoringCrypto is in use. It panics. +func UnreachableExceptTests() { + name := runtime_arg0() + // If BoringCrypto ran on Windows we'd need to allow _test.exe and .test.exe as well. + if !hasSuffix(name, "_test") && !hasSuffix(name, ".test") { + println("boringcrypto: unexpected code execution in", name) + panic("boringcrypto: invalid code execution") + } +} + +type fail string + +func (e fail) Error() string { return "boringcrypto: " + string(e) + " failed" } + +func bigToBN(x *big.Int) *C.GO_BIGNUM { + raw := x.Bytes() + return C._goboringcrypto_BN_bin2bn(base(raw), C.size_t(len(raw)), nil) +} + +func bnToBig(bn *C.GO_BIGNUM) *big.Int { + raw := make([]byte, C._goboringcrypto_BN_num_bytes(bn)) + n := C._goboringcrypto_BN_bn2bin(bn, base(raw)) + return new(big.Int).SetBytes(raw[:n]) +} + +func bigToBn(bnp **C.GO_BIGNUM, b *big.Int) bool { + if *bnp != nil { + C._goboringcrypto_BN_free(*bnp) + *bnp = nil + } + if b == nil { + return true + } + raw := b.Bytes() + bn := C._goboringcrypto_BN_bin2bn(base(raw), C.size_t(len(raw)), nil) + if bn == nil { + return false + } + *bnp = bn + return true +} diff --git a/src/crypto/internal/boring/boring_test.go b/src/crypto/internal/boring/boring_test.go new file mode 100644 index 0000000000..83bbbd3404 --- /dev/null +++ b/src/crypto/internal/boring/boring_test.go @@ -0,0 +1,34 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Most functionality in this package is tested by replacing existing code +// and inheriting that code's tests. + +package boring + +import "testing" + +// Test that func init does not panic. +func TestInit(t *testing.T) {} + +// Test that Unreachable panics. +func TestUnreachable(t *testing.T) { + defer func() { + if Enabled { + if err := recover(); err == nil { + t.Fatal("expected Unreachable to panic") + } + } else { + if err := recover(); err != nil { + t.Fatalf("expected Unreachable to be a no-op") + } + } + }() + Unreachable() +} + +// Test that UnreachableExceptTests does not panic (this is a test). +func TestUnreachableExceptTests(t *testing.T) { + UnreachableExceptTests() +} diff --git a/src/crypto/internal/boring/build/.gitignore b/src/crypto/internal/boring/build/.gitignore new file mode 100644 index 0000000000..a19e8a9ffe --- /dev/null +++ b/src/crypto/internal/boring/build/.gitignore @@ -0,0 +1 @@ +boringssl-24e5886c0edfc409c8083d10f9f1120111efd6f5.tar.xz diff --git a/src/crypto/internal/boring/build/README b/src/crypto/internal/boring/build/README new file mode 100644 index 0000000000..22ce944a44 --- /dev/null +++ b/src/crypto/internal/boring/build/README @@ -0,0 +1,6 @@ +This is not a Go package. The directory must not contain Go sources, +to prevent it from being considered a Go package. + +This directory holds the script for building ../goboringcrypto_*.syso. +Run build.sh on an Ubuntu system. +See the comment at the top of build.sh for details. diff --git a/src/crypto/internal/boring/build/build.sh b/src/crypto/internal/boring/build/build.sh new file mode 100755 index 0000000000..7c50327c2f --- /dev/null +++ b/src/crypto/internal/boring/build/build.sh @@ -0,0 +1,63 @@ +#!/bin/bash +# Copyright 2017 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +# Run on Ubuntu system set up with: +# sudo apt-get install debootstrap +# sudo apt-get install squid-deb-proxy +# +# The script sets up an Ubuntu chroot and then runs the build +# in that chroot, to make sure we know exactly what software +# is being used for the build. To repeat the script reusing the +# chroot installation, run build.sh -quick. This mode is useful +# if all you've modified is goboringcrypto.c and ../goboringcrypto.h +# (or some of the setup scripts in this directory). + +# Comment this setting out if not using squid-deb-proxy, +# but it will be much slower to repeat the script. +http_proxy=http://127.0.0.1:8000 + +chroot=/var/tmp/boringssl + +sudo umount -f $chroot/proc +sudo umount -f $chroot/sys +sudo umount -f $chroot/dev/pts +sudo umount -f $chroot/dev + +set -e +if [ "$1" != "-quick" ]; then + sudo rm -rf $chroot + sudo http_proxy=$http_proxy debootstrap --variant=minbase zesty $chroot +fi + +sudo chown $USER $chroot +sudo chmod u+w $chroot + +sudo mount -t proc proc $chroot/proc +sudo mount -t sysfs sys $chroot/sys +sudo mount -o bind /dev $chroot/dev +sudo mount -t devpts devpts $chroot/dev/pts + +sudo cp sources.list $chroot/etc/apt/sources.list + +cp *chroot.sh $chroot + +# Following http://csrc.nist.gov/groups/STM/cmvp/documents/140-1/140sp/140sp2964.pdf page 18. +if [ ! -e $chroot/boringssl-24e5886c0edfc409c8083d10f9f1120111efd6f5.tar.xz ]; then + wget -O $chroot/boringssl-24e5886c0edfc409c8083d10f9f1120111efd6f5.tar.xz https://commondatastorage.googleapis.com/chromium-boringssl-docs/fips/boringssl-24e5886c0edfc409c8083d10f9f1120111efd6f5.tar.xz +fi +if [ "$(sha256sum $chroot/boringssl-24e5886c0edfc409c8083d10f9f1120111efd6f5.tar.xz | awk '{print $1}')" != 15a65d676eeae27618e231183a1ce9804fc9c91bcc3abf5f6ca35216c02bf4da ]; then + echo WRONG SHA256SUM + exit 2 +fi + +rm -rf $chroot/godriver +mkdir $chroot/godriver +cp ../goboringcrypto.h $chroot/godriver + +sudo http_proxy=$http_proxy chroot $chroot /root_setup_in_chroot.sh +sudo chroot --userspec=$(id -u):$(id -g) $chroot /build_in_chroot.sh +cp $chroot/godriver/goboringcrypto_linux_amd64.syso .. +sha256sum ../goboringcrypto_linux_amd64.syso +echo DONE diff --git a/src/crypto/internal/boring/build/build_in_chroot.sh b/src/crypto/internal/boring/build/build_in_chroot.sh new file mode 100755 index 0000000000..b5e5a778b6 --- /dev/null +++ b/src/crypto/internal/boring/build/build_in_chroot.sh @@ -0,0 +1,198 @@ +#!/bin/bash +# Copyright 2017 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +set -e +echo running build_in_chroot.sh +id +date +export LANG=C +unset LANGUAGE + +# Build BoringCrypto libcrypto.a. +# Following http://csrc.nist.gov/groups/STM/cmvp/documents/140-1/140sp/140sp2964.pdf page 18. +if ! [ -e ./boringssl/build/tool/bssl ]; then + export PATH=$PATH:/usr/lib/go-1.8/bin:/clangbin + + # Go requires -fPIC for linux/amd64 cgo builds. + # Setting -fPIC only affects the compilation of the non-module code in libcrypto.a, + # because the FIPS module itself is already built with -fPIC. + mkdir /clangbin + echo '#!/bin/bash + exec clang-4.0 -fPIC "$@" + ' >/clangbin/clang + echo '#!/bin/bash + exec clang++-4.0 -fPIC "$@" + ' >/clangbin/clang++ + chmod +x /clangbin/clang /clangbin/clang++ + + rm -rf boringssl + tar xJf ../boringssl-*z + cd boringssl + + # Verbatim instructions from BoringCrypto build docs. + printf "set(CMAKE_C_COMPILER \"clang\")\nset(CMAKE_CXX_COMPILER \"clang++\")\n" >/toolchain + mkdir build && cd build && cmake -GNinja -DCMAKE_TOOLCHAIN_FILE=/toolchain -DFIPS=1 -DCMAKE_BUILD_TYPE=Release .. + ninja -v + ninja run_tests + + cd ../.. +fi +if [ "$(./boringssl/build/tool/bssl isfips)" != 1 ]; then + echo "NOT FIPS" + exit 2 +fi + + +# Build and run test C++ program to make sure goboringcrypto.h matches openssl/*.h. +# Also collect list of checked symbols in syms.txt +set -x +set -e +cd godriver +cat >goboringcrypto.cc <<'EOF' +#include <cassert> +#include "goboringcrypto0.h" +#include "goboringcrypto1.h" +#define check_size(t) if(sizeof(t) != sizeof(GO_ ## t)) {printf("sizeof(" #t ")=%d, but sizeof(GO_" #t ")=%d\n", (int)sizeof(t), (int)sizeof(GO_ ## t)); ret=1;} +#define check_func(f) { auto x = f; x = _goboringcrypto_ ## f ; } +#define check_value(n, v) if(n != v) {printf(#n "=%d, but goboringcrypto.h defines it as %d\n", (int)n, (int)v); ret=1;} +int main() { +int ret = 0; +#include "goboringcrypto.x" +return ret; +} +EOF + +awk ' +BEGIN { + exitcode = 0 +} + +# Ignore comments, #includes, blank lines. +/^\/\// || /^#/ || NF == 0 { next } + +# Ignore unchecked declarations. +/\/\*unchecked/ { next } + +# Check enum values. +!enum && $1 == "enum" && $NF == "{" { + enum = 1 + next +} +enum && $1 == "};" { + enum = 0 + next +} +enum && NF == 3 && $2 == "=" { + name = $1 + sub(/^GO_/, "", name) + val = $3 + sub(/,$/, "", val) + print "check_value(" name ", " val ")" > "goboringcrypto.x" + next +} +enum { + print FILENAME ":" NR ": unexpected line in enum: " $0 > "/dev/stderr" + exitcode = 1 + next +} + +# Check struct sizes. +/^typedef struct / && $NF ~ /^GO_/ { + name = $NF + sub(/^GO_/, "", name) + sub(/;$/, "", name) + print "check_size(" name ")" > "goboringcrypto.x" + next +} + +# Check function prototypes. +/^(const )?[^ ]+ \**_goboringcrypto_.*\(/ { + name = $2 + if($1 == "const") + name = $3 + sub(/^\**_goboringcrypto_/, "", name) + sub(/\(.*/, "", name) + print "check_func(" name ")" > "goboringcrypto.x" + print name > "syms.txt" + next +} + +{ + print FILENAME ":" NR ": unexpected line: " $0 > "/dev/stderr" + exitcode = 1 +} + +END { + exit exitcode +} +' goboringcrypto.h + +cat goboringcrypto.h | awk ' + /^\/\/ #include/ {sub(/\/\//, ""); print > "goboringcrypto0.h"; next} + /typedef struct|enum ([a-z_]+ )?{|^[ \t]/ {print;next} + {gsub(/GO_/, ""); gsub(/enum go_/, "enum "); print} +' >goboringcrypto1.h +clang++-4.0 -std=c++11 -fPIC -I../boringssl/include -O2 -o a.out goboringcrypto.cc +./a.out || exit 2 + +# Prepare copy of libcrypto.a with only the checked functions renamed and exported. +# All other symbols are left alone and hidden. +echo BORINGSSL_bcm_power_on_self_test >>syms.txt +awk '{print "_goboringcrypto_" $0 }' syms.txt >globals.txt +awk '{print $0 " _goboringcrypto_" $0 }' syms.txt >renames.txt +objcopy --globalize-symbol=BORINGSSL_bcm_power_on_self_test ../boringssl/build/crypto/libcrypto.a libcrypto.a + +# clang implements u128 % u128 -> u128 by calling __umodti3, +# which is in libgcc. To make the result self-contained even if linking +# against a different compiler version, link our own __umodti3 into the syso. +# This one is specialized so it only expects divisors below 2^64, +# which is all BoringCrypto uses. (Otherwise it will seg fault.) +cat >umod.s <<'EOF' +# tu_int __umodti3(tu_int x, tu_int y) +# x is rsi:rdi, y is rcx:rdx, return result is rdx:rax. +.globl __umodti3 +__umodti3: + # specialized to u128 % u64, so verify that + test %rcx,%rcx + jne 1f + + # save divisor + movq %rdx, %r8 + + # reduce top 64 bits mod divisor + movq %rsi, %rax + xorl %edx, %edx + divq %r8 + + # reduce full 128-bit mod divisor + # quotient fits in 64 bits because top 64 bits have been reduced < divisor. + # (even though we only care about the remainder, divq also computes + # the quotient, and it will trap if the quotient is too large.) + movq %rdi, %rax + divq %r8 + + # expand remainder to 128 for return + movq %rdx, %rax + xorl %edx, %edx + ret + +1: + # crash - only want 64-bit divisor + xorl %ecx, %ecx + movl %ecx, 0(%ecx) + jmp 1b + +.section .note.GNU-stack,"",@progbits +EOF +clang-4.0 -c -o umod.o umod.s + +ld -r -nostdlib --whole-archive -o goboringcrypto.o libcrypto.a umod.o +echo __umodti3 _goboringcrypto___umodti3 >>renames.txt +objcopy --redefine-syms=renames.txt goboringcrypto.o goboringcrypto2.o +objcopy --keep-global-symbols=globals.txt goboringcrypto2.o goboringcrypto_linux_amd64.syso + +# Done! +ls -l goboringcrypto_linux_amd64.syso +sha256sum goboringcrypto_linux_amd64.syso diff --git a/src/crypto/internal/boring/build/root_setup_in_chroot.sh b/src/crypto/internal/boring/build/root_setup_in_chroot.sh new file mode 100755 index 0000000000..fef91f5680 --- /dev/null +++ b/src/crypto/internal/boring/build/root_setup_in_chroot.sh @@ -0,0 +1,13 @@ +#!/bin/bash +# Copyright 2017 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +set -e +id +date +echo http_proxy=$http_proxy +export LANG=C +unset LANGUAGE +apt-get update +apt-get install --no-install-recommends -y cmake clang-4.0 golang-1.8-go ninja-build xz-utils diff --git a/src/crypto/internal/boring/build/sources.list b/src/crypto/internal/boring/build/sources.list new file mode 100644 index 0000000000..35df0f2a58 --- /dev/null +++ b/src/crypto/internal/boring/build/sources.list @@ -0,0 +1,10 @@ +deb http://archive.ubuntu.com/ubuntu/ zesty main restricted +deb http://archive.ubuntu.com/ubuntu/ zesty-updates main restricted +deb http://archive.ubuntu.com/ubuntu/ zesty universe +deb http://archive.ubuntu.com/ubuntu/ zesty-updates universe +deb http://archive.ubuntu.com/ubuntu/ zesty multiverse +deb http://archive.ubuntu.com/ubuntu/ zesty-updates multiverse +deb http://archive.ubuntu.com/ubuntu/ zesty-backports main restricted universe multiverse +deb http://security.ubuntu.com/ubuntu zesty-security main restricted +deb http://security.ubuntu.com/ubuntu zesty-security universe +deb http://security.ubuntu.com/ubuntu zesty-security multiverse diff --git a/src/crypto/internal/boring/doc.go b/src/crypto/internal/boring/doc.go new file mode 100644 index 0000000000..64f41e3c82 --- /dev/null +++ b/src/crypto/internal/boring/doc.go @@ -0,0 +1,14 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package boring provides access to BoringCrypto implementation functions. +// Check the constant Enabled to find out whether BoringCrypto is available. +// If BoringCrypto is not available, the functions in this package all panic. +package boring + +// Enabled reports whether BoringCrypto is available. +// When enabled is false, all functions in this package panic. +// +// BoringCrypto is only available on linux/amd64 systems. +const Enabled = available diff --git a/src/crypto/internal/boring/ecdsa.go b/src/crypto/internal/boring/ecdsa.go new file mode 100644 index 0000000000..4fcba4be72 --- /dev/null +++ b/src/crypto/internal/boring/ecdsa.go @@ -0,0 +1,201 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux,amd64 +// +build !android +// +build !cmd_go_bootstrap +// +build !msan + +package boring + +// #include "goboringcrypto.h" +import "C" +import ( + "encoding/asn1" + "errors" + "math/big" + "runtime" + "unsafe" +) + +type ecdsaSignature struct { + R, S *big.Int +} + +type PrivateKeyECDSA struct { + key *C.GO_EC_KEY +} + +func (k *PrivateKeyECDSA) finalize() { + C._goboringcrypto_EC_KEY_free(k.key) +} + +type PublicKeyECDSA struct { + key *C.GO_EC_KEY +} + +func (k *PublicKeyECDSA) finalize() { + C._goboringcrypto_EC_KEY_free(k.key) +} + +var errUnknownCurve = errors.New("boringcrypto: unknown elliptic curve") + +func curveNID(curve string) (C.int, error) { + switch curve { + case "P-224": + return C.GO_NID_secp224r1, nil + case "P-256": + return C.GO_NID_X9_62_prime256v1, nil + case "P-384": + return C.GO_NID_secp384r1, nil + case "P-521": + return C.GO_NID_secp521r1, nil + } + return 0, errUnknownCurve +} + +func NewPublicKeyECDSA(curve string, X, Y *big.Int) (*PublicKeyECDSA, error) { + key, err := newECKey(curve, X, Y) + if err != nil { + return nil, err + } + k := &PublicKeyECDSA{key} + // Note: Because of the finalizer, any time k.key is passed to cgo, + // that call must be followed by a call to runtime.KeepAlive(k), + // to make sure k is not collected (and finalized) before the cgo + // call returns. + runtime.SetFinalizer(k, (*PublicKeyECDSA).finalize) + return k, nil +} + +func newECKey(curve string, X, Y *big.Int) (*C.GO_EC_KEY, error) { + nid, err := curveNID(curve) + if err != nil { + return nil, err + } + key := C._goboringcrypto_EC_KEY_new_by_curve_name(nid) + if key == nil { + return nil, fail("EC_KEY_new_by_curve_name") + } + group := C._goboringcrypto_EC_KEY_get0_group(key) + pt := C._goboringcrypto_EC_POINT_new(group) + if pt == nil { + C._goboringcrypto_EC_KEY_free(key) + return nil, fail("EC_POINT_new") + } + bx := bigToBN(X) + by := bigToBN(Y) + ok := bx != nil && by != nil && C._goboringcrypto_EC_POINT_set_affine_coordinates_GFp(group, pt, bx, by, nil) != 0 && + C._goboringcrypto_EC_KEY_set_public_key(key, pt) != 0 + if bx != nil { + C._goboringcrypto_BN_free(bx) + } + if by != nil { + C._goboringcrypto_BN_free(by) + } + C._goboringcrypto_EC_POINT_free(pt) + if !ok { + C._goboringcrypto_EC_KEY_free(key) + return nil, fail("EC_POINT_set_affine_coordinates_GFp") + } + return key, nil +} + +func NewPrivateKeyECDSA(curve string, X, Y *big.Int, D *big.Int) (*PrivateKeyECDSA, error) { + key, err := newECKey(curve, X, Y) + if err != nil { + return nil, err + } + bd := bigToBN(D) + ok := bd != nil && C._goboringcrypto_EC_KEY_set_private_key(key, bd) != 0 + if bd != nil { + C._goboringcrypto_BN_free(bd) + } + if !ok { + C._goboringcrypto_EC_KEY_free(key) + return nil, fail("EC_KEY_set_private_key") + } + k := &PrivateKeyECDSA{key} + // Note: Because of the finalizer, any time k.key is passed to cgo, + // that call must be followed by a call to runtime.KeepAlive(k), + // to make sure k is not collected (and finalized) before the cgo + // call returns. + runtime.SetFinalizer(k, (*PrivateKeyECDSA).finalize) + return k, nil +} + +func SignECDSA(priv *PrivateKeyECDSA, hash []byte) (r, s *big.Int, err error) { + // We could use ECDSA_do_sign instead but would need to convert + // the resulting BIGNUMs to *big.Int form. If we're going to do a + // conversion, converting the ASN.1 form is more convenient and + // likely not much more expensive. + sig, err := SignMarshalECDSA(priv, hash) + if err != nil { + return nil, nil, err + } + var esig ecdsaSignature + if _, err := asn1.Unmarshal(sig, &esig); err != nil { + return nil, nil, err + } + return esig.R, esig.S, nil +} + +func SignMarshalECDSA(priv *PrivateKeyECDSA, hash []byte) ([]byte, error) { + size := C._goboringcrypto_ECDSA_size(priv.key) + sig := make([]byte, size) + var sigLen C.uint + if C._goboringcrypto_ECDSA_sign(0, base(hash), C.size_t(len(hash)), (*C.uint8_t)(unsafe.Pointer(&sig[0])), &sigLen, priv.key) == 0 { + return nil, fail("ECDSA_sign") + } + runtime.KeepAlive(priv) + return sig[:sigLen], nil +} + +func VerifyECDSA(pub *PublicKeyECDSA, hash []byte, r, s *big.Int) bool { + // We could use ECDSA_do_verify instead but would need to convert + // r and s to BIGNUM form. If we're going to do a conversion, marshaling + // to ASN.1 is more convenient and likely not much more expensive. + sig, err := asn1.Marshal(ecdsaSignature{r, s}) + if err != nil { + return false + } + ok := C._goboringcrypto_ECDSA_verify(0, base(hash), C.size_t(len(hash)), (*C.uint8_t)(unsafe.Pointer(&sig[0])), C.size_t(len(sig)), pub.key) != 0 + runtime.KeepAlive(pub) + return ok +} + +func GenerateKeyECDSA(curve string) (X, Y, D *big.Int, err error) { + nid, err := curveNID(curve) + if err != nil { + return nil, nil, nil, err + } + key := C._goboringcrypto_EC_KEY_new_by_curve_name(nid) + if key == nil { + return nil, nil, nil, fail("EC_KEY_new_by_curve_name") + } + defer C._goboringcrypto_EC_KEY_free(key) + if C._goboringcrypto_EC_KEY_generate_key_fips(key) == 0 { + return nil, nil, nil, fail("EC_KEY_generate_key_fips") + } + group := C._goboringcrypto_EC_KEY_get0_group(key) + pt := C._goboringcrypto_EC_KEY_get0_public_key(key) + bd := C._goboringcrypto_EC_KEY_get0_private_key(key) + if pt == nil || bd == nil { + return nil, nil, nil, fail("EC_KEY_get0_private_key") + } + bx := C._goboringcrypto_BN_new() + if bx == nil { + return nil, nil, nil, fail("BN_new") + } + defer C._goboringcrypto_BN_free(bx) + by := C._goboringcrypto_BN_new() + if by == nil { + return nil, nil, nil, fail("BN_new") + } + defer C._goboringcrypto_BN_free(by) + if C._goboringcrypto_EC_POINT_get_affine_coordinates_GFp(group, pt, bx, by, nil) == 0 { + return nil, nil, nil, fail("EC_POINT_get_affine_coordinates_GFp") + } + return bnToBig(bx), bnToBig(by), bnToBig(bd), nil +} diff --git a/src/crypto/internal/boring/fipstls/dummy.s b/src/crypto/internal/boring/fipstls/dummy.s new file mode 100644 index 0000000000..53bb7d9430 --- /dev/null +++ b/src/crypto/internal/boring/fipstls/dummy.s @@ -0,0 +1,10 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// runtime_arg0 is declared in tls.go without a body. +// It's provided by package runtime, +// but the go command doesn't know that. +// Having this assembly file keeps the go command +// from complaining about the missing body +// (because the implementation might be here). diff --git a/src/crypto/internal/boring/fipstls/tls.go b/src/crypto/internal/boring/fipstls/tls.go new file mode 100644 index 0000000000..4127533fbd --- /dev/null +++ b/src/crypto/internal/boring/fipstls/tls.go @@ -0,0 +1,49 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package fipstls allows control over whether crypto/tls requires FIPS-approved settings. +// This package's effects are independent of the use of the BoringCrypto implementation. +package fipstls + +import "sync/atomic" + +var required uint32 + +// Force forces crypto/tls to restrict TLS configurations to FIPS-approved settings. +// By design, this call is impossible to undo (except in tests). +// +// Note that this call has an effect even in programs using +// standard crypto (that is, even when Enabled = false). +func Force() { + atomic.StoreUint32(&required, 1) +} + +// Abandon allows non-FIPS-approved settings. +// If called from a non-test binary, it panics. +func Abandon() { + // Note: Not using boring.UnreachableExceptTests because we want + // this test to happen even when boring.Enabled = false. + name := runtime_arg0() + // Allow _test for Go command, .test for Bazel, + // NaClMain for NaCl (where all binaries run as NaClMain), + // and empty string for Windows (where runtime_arg0 can't easily find the name). + // Since this is an internal package, testing that this isn't used on the + // other operating systems should suffice to catch any mistakes. + if !hasSuffix(name, "_test") && !hasSuffix(name, ".test") && name != "NaClMain" && name != "" { + panic("fipstls: invalid use of Abandon in " + name) + } + atomic.StoreUint32(&required, 0) +} + +// provided by runtime +func runtime_arg0() string + +func hasSuffix(s, t string) bool { + return len(s) > len(t) && s[len(s)-len(t):] == t +} + +// Required reports whether FIPS-approved settings are required. +func Required() bool { + return atomic.LoadUint32(&required) != 0 +} diff --git a/src/crypto/internal/boring/goboringcrypto.h b/src/crypto/internal/boring/goboringcrypto.h new file mode 100644 index 0000000000..f982ce83c2 --- /dev/null +++ b/src/crypto/internal/boring/goboringcrypto.h @@ -0,0 +1,236 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This header file describes the BoringCrypto ABI as built for use in Go. +// The BoringCrypto build for Go (which generates goboringcrypto_*.syso) +// takes the standard libcrypto.a from BoringCrypto and adds the prefix +// _goboringcrypto_ to every symbol, to avoid possible conflicts with +// code wrapping a different BoringCrypto or OpenSSL. +// +// To make this header standalone (so that building Go does not require +// having a full set of BoringCrypto headers), the struct details are not here. +// Instead, while building the syso, we compile and run a C++ program +// that checks that the sizes match. The program also checks (during compilation) +// that all the function prototypes match the BoringCrypto equivalents. +// The generation of the checking program depends on the declaration +// forms used below (one line for most, multiline for enums). + +#include <stdlib.h> // size_t +#include <stdint.h> // uint8_t + +// This symbol is hidden in BoringCrypto and marked as a constructor, +// but cmd/link's internal linking mode doesn't handle constructors. +// Until it does, we've exported the symbol and can call it explicitly. +// (If using external linking mode, it will therefore be called twice, +// once explicitly and once as a constructor, but that's OK.) +/*unchecked*/ void _goboringcrypto_BORINGSSL_bcm_power_on_self_test(void); + +// #include <openssl/crypto.h> +int _goboringcrypto_FIPS_mode(void); + +// #include <openssl/rand.h> +int _goboringcrypto_RAND_bytes(uint8_t*, size_t); + +// #include <openssl/nid.h> +enum { + GO_NID_md5_sha1 = 114, + + GO_NID_secp224r1 = 713, + GO_NID_X9_62_prime256v1 = 415, + GO_NID_secp384r1 = 715, + GO_NID_secp521r1 = 716, + + GO_NID_sha224 = 675, + GO_NID_sha256 = 672, + GO_NID_sha384 = 673, + GO_NID_sha512 = 674, +}; + +// #include <openssl/sha.h> +typedef struct GO_SHA_CTX { char data[96]; } GO_SHA_CTX; +int _goboringcrypto_SHA1_Init(GO_SHA_CTX*); +int _goboringcrypto_SHA1_Update(GO_SHA_CTX*, const void*, size_t); +int _goboringcrypto_SHA1_Final(uint8_t*, GO_SHA_CTX*); + +typedef struct GO_SHA256_CTX { char data[48+64]; } GO_SHA256_CTX; +int _goboringcrypto_SHA224_Init(GO_SHA256_CTX*); +int _goboringcrypto_SHA224_Update(GO_SHA256_CTX*, const void*, size_t); +int _goboringcrypto_SHA224_Final(uint8_t*, GO_SHA256_CTX*); +int _goboringcrypto_SHA256_Init(GO_SHA256_CTX*); +int _goboringcrypto_SHA256_Update(GO_SHA256_CTX*, const void*, size_t); +int _goboringcrypto_SHA256_Final(uint8_t*, GO_SHA256_CTX*); + +typedef struct GO_SHA512_CTX { char data[88+128]; } GO_SHA512_CTX; +int _goboringcrypto_SHA384_Init(GO_SHA512_CTX*); +int _goboringcrypto_SHA384_Update(GO_SHA512_CTX*, const void*, size_t); +int _goboringcrypto_SHA384_Final(uint8_t*, GO_SHA512_CTX*); +int _goboringcrypto_SHA512_Init(GO_SHA512_CTX*); +int _goboringcrypto_SHA512_Update(GO_SHA512_CTX*, const void*, size_t); +int _goboringcrypto_SHA512_Final(uint8_t*, GO_SHA512_CTX*); + +// #include <openssl/digest.h> +/*unchecked (opaque)*/ typedef struct GO_EVP_MD { char data[1]; } GO_EVP_MD; +const GO_EVP_MD* _goboringcrypto_EVP_md4(void); +const GO_EVP_MD* _goboringcrypto_EVP_md5(void); +const GO_EVP_MD* _goboringcrypto_EVP_md5_sha1(void); +const GO_EVP_MD* _goboringcrypto_EVP_sha1(void); +const GO_EVP_MD* _goboringcrypto_EVP_sha224(void); +const GO_EVP_MD* _goboringcrypto_EVP_sha256(void); +const GO_EVP_MD* _goboringcrypto_EVP_sha384(void); +const GO_EVP_MD* _goboringcrypto_EVP_sha512(void); +int _goboringcrypto_EVP_MD_type(const GO_EVP_MD*); +size_t _goboringcrypto_EVP_MD_size(const GO_EVP_MD*); + +// #include <openssl/hmac.h> +typedef struct GO_HMAC_CTX { char data[104]; } GO_HMAC_CTX; +void _goboringcrypto_HMAC_CTX_init(GO_HMAC_CTX*); +void _goboringcrypto_HMAC_CTX_cleanup(GO_HMAC_CTX*); +int _goboringcrypto_HMAC_Init(GO_HMAC_CTX*, const void*, int, const GO_EVP_MD*); +int _goboringcrypto_HMAC_Update(GO_HMAC_CTX*, const uint8_t*, size_t); +int _goboringcrypto_HMAC_Final(GO_HMAC_CTX*, uint8_t*, unsigned int*); +size_t _goboringcrypto_HMAC_size(const GO_HMAC_CTX*); +int _goboringcrypto_HMAC_CTX_copy_ex(GO_HMAC_CTX *dest, const GO_HMAC_CTX *src); + +// #include <openssl/aes.h> +typedef struct GO_AES_KEY { char data[244]; } GO_AES_KEY; +int _goboringcrypto_AES_set_encrypt_key(const uint8_t*, unsigned int, GO_AES_KEY*); +int _goboringcrypto_AES_set_decrypt_key(const uint8_t*, unsigned int, GO_AES_KEY*); +void _goboringcrypto_AES_encrypt(const uint8_t*, uint8_t*, const GO_AES_KEY*); +void _goboringcrypto_AES_decrypt(const uint8_t*, uint8_t*, const GO_AES_KEY*); +void _goboringcrypto_AES_ctr128_encrypt(const uint8_t*, uint8_t*, size_t, const GO_AES_KEY*, uint8_t*, uint8_t*, unsigned int*); +enum { + GO_AES_ENCRYPT = 1, + GO_AES_DECRYPT = 0 +}; +void _goboringcrypto_AES_cbc_encrypt(const uint8_t*, uint8_t*, size_t, const GO_AES_KEY*, uint8_t*, const int); + +// #include <openssl/aead.h> +/*unchecked (opaque)*/ typedef struct GO_EVP_AEAD { char data[1]; } GO_EVP_AEAD; +/*unchecked (opaque)*/ typedef struct GO_ENGINE { char data[1]; } GO_ENGINE; +const GO_EVP_AEAD* _goboringcrypto_EVP_aead_aes_128_gcm(void); +const GO_EVP_AEAD* _goboringcrypto_EVP_aead_aes_256_gcm(void); +enum { + GO_EVP_AEAD_DEFAULT_TAG_LENGTH = 0 +}; +size_t _goboringcrypto_EVP_AEAD_key_length(const GO_EVP_AEAD*); +size_t _goboringcrypto_EVP_AEAD_nonce_length(const GO_EVP_AEAD*); +size_t _goboringcrypto_EVP_AEAD_max_overhead(const GO_EVP_AEAD*); +size_t _goboringcrypto_EVP_AEAD_max_tag_len(const GO_EVP_AEAD*); +typedef struct GO_EVP_AEAD_CTX { char data[24]; } GO_EVP_AEAD_CTX; +void _goboringcrypto_EVP_AEAD_CTX_zero(GO_EVP_AEAD_CTX*); +int _goboringcrypto_EVP_AEAD_CTX_init(GO_EVP_AEAD_CTX*, const GO_EVP_AEAD*, const uint8_t*, size_t, size_t, GO_ENGINE*); +void _goboringcrypto_EVP_AEAD_CTX_cleanup(GO_EVP_AEAD_CTX*); +int _goboringcrypto_EVP_AEAD_CTX_seal(const GO_EVP_AEAD_CTX*, uint8_t*, size_t*, size_t, const uint8_t*, size_t, const uint8_t*, size_t, const uint8_t*, size_t); +int _goboringcrypto_EVP_AEAD_CTX_open(const GO_EVP_AEAD_CTX*, uint8_t*, size_t*, size_t, const uint8_t*, size_t, const uint8_t*, size_t, const uint8_t*, size_t); +const GO_EVP_AEAD* _goboringcrypto_EVP_aead_aes_128_gcm_tls12(void); +const GO_EVP_AEAD* _goboringcrypto_EVP_aead_aes_256_gcm_tls12(void); +enum go_evp_aead_direction_t { + go_evp_aead_open = 0, + go_evp_aead_seal = 1 +}; +int _goboringcrypto_EVP_AEAD_CTX_init_with_direction(GO_EVP_AEAD_CTX*, const GO_EVP_AEAD*, const uint8_t*, size_t, size_t, enum go_evp_aead_direction_t); + +// #include <openssl/bn.h> +/*unchecked (opaque)*/ typedef struct GO_BN_CTX { char data[1]; } GO_BN_CTX; +typedef struct GO_BIGNUM { char data[24]; } GO_BIGNUM; +GO_BIGNUM* _goboringcrypto_BN_new(void); +void _goboringcrypto_BN_free(GO_BIGNUM*); +unsigned _goboringcrypto_BN_num_bits(const GO_BIGNUM*); +unsigned _goboringcrypto_BN_num_bytes(const GO_BIGNUM*); +int _goboringcrypto_BN_is_negative(const GO_BIGNUM*); +GO_BIGNUM* _goboringcrypto_BN_bin2bn(const uint8_t*, size_t, GO_BIGNUM*); +size_t _goboringcrypto_BN_bn2bin(const GO_BIGNUM*, uint8_t*); + +// #include <openssl/ec.h> +/*unchecked (opaque)*/ typedef struct GO_EC_GROUP { char data[1]; } GO_EC_GROUP; +GO_EC_GROUP* _goboringcrypto_EC_GROUP_new_by_curve_name(int); +void _goboringcrypto_EC_GROUP_free(GO_EC_GROUP*); + +/*unchecked (opaque)*/ typedef struct GO_EC_POINT { char data[1]; } GO_EC_POINT; +GO_EC_POINT* _goboringcrypto_EC_POINT_new(const GO_EC_GROUP*); +void _goboringcrypto_EC_POINT_free(GO_EC_POINT*); +int _goboringcrypto_EC_POINT_get_affine_coordinates_GFp(const GO_EC_GROUP*, const GO_EC_POINT*, GO_BIGNUM*, GO_BIGNUM*, GO_BN_CTX*); +int _goboringcrypto_EC_POINT_set_affine_coordinates_GFp(const GO_EC_GROUP*, GO_EC_POINT*, const GO_BIGNUM*, const GO_BIGNUM*, GO_BN_CTX*); + +// #include <openssl/ec_key.h> +/*unchecked (opaque)*/ typedef struct GO_EC_KEY { char data[1]; } GO_EC_KEY; +GO_EC_KEY* _goboringcrypto_EC_KEY_new(void); +GO_EC_KEY* _goboringcrypto_EC_KEY_new_by_curve_name(int); +void _goboringcrypto_EC_KEY_free(GO_EC_KEY*); +const GO_EC_GROUP* _goboringcrypto_EC_KEY_get0_group(const GO_EC_KEY*); +int _goboringcrypto_EC_KEY_generate_key_fips(GO_EC_KEY*); +int _goboringcrypto_EC_KEY_set_private_key(GO_EC_KEY*, const GO_BIGNUM*); +int _goboringcrypto_EC_KEY_set_public_key(GO_EC_KEY*, const GO_EC_POINT*); +int _goboringcrypto_EC_KEY_is_opaque(const GO_EC_KEY*); +const GO_BIGNUM* _goboringcrypto_EC_KEY_get0_private_key(const GO_EC_KEY*); +const GO_EC_POINT* _goboringcrypto_EC_KEY_get0_public_key(const GO_EC_KEY*); +// TODO: EC_KEY_check_fips? + +// #include <openssl/ecdsa.h> +typedef struct GO_ECDSA_SIG { char data[16]; } GO_ECDSA_SIG; +GO_ECDSA_SIG* _goboringcrypto_ECDSA_SIG_new(void); +void _goboringcrypto_ECDSA_SIG_free(GO_ECDSA_SIG*); +GO_ECDSA_SIG* _goboringcrypto_ECDSA_do_sign(const uint8_t*, size_t, const GO_EC_KEY*); +int _goboringcrypto_ECDSA_do_verify(const uint8_t*, size_t, const GO_ECDSA_SIG*, const GO_EC_KEY*); +int _goboringcrypto_ECDSA_sign(int, const uint8_t*, size_t, uint8_t*, unsigned int*, const GO_EC_KEY*); +size_t _goboringcrypto_ECDSA_size(const GO_EC_KEY*); +int _goboringcrypto_ECDSA_verify(int, const uint8_t*, size_t, const uint8_t*, size_t, const GO_EC_KEY*); + +// #include <openssl/rsa.h> + +// Note: order of struct fields here is unchecked. +typedef struct GO_RSA { void *meth; GO_BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp; char data[120]; } GO_RSA; +/*unchecked (opaque)*/ typedef struct GO_BN_GENCB { char data[1]; } GO_BN_GENCB; +GO_RSA* _goboringcrypto_RSA_new(void); +void _goboringcrypto_RSA_free(GO_RSA*); +void _goboringcrypto_RSA_get0_key(const GO_RSA*, const GO_BIGNUM **n, const GO_BIGNUM **e, const GO_BIGNUM **d); +void _goboringcrypto_RSA_get0_factors(const GO_RSA*, const GO_BIGNUM **p, const GO_BIGNUM **q); +void _goboringcrypto_RSA_get0_crt_params(const GO_RSA*, const GO_BIGNUM **dmp1, const GO_BIGNUM **dmp2, const GO_BIGNUM **iqmp); +int _goboringcrypto_RSA_generate_key_ex(GO_RSA*, int, GO_BIGNUM*, GO_BN_GENCB*); +int _goboringcrypto_RSA_generate_key_fips(GO_RSA*, int, GO_BN_GENCB*); +enum { + GO_RSA_PKCS1_PADDING = 1, + GO_RSA_NO_PADDING = 3, + GO_RSA_PKCS1_OAEP_PADDING = 4, + GO_RSA_PKCS1_PSS_PADDING = 6, +}; +int _goboringcrypto_RSA_encrypt(GO_RSA*, size_t *out_len, uint8_t *out, size_t max_out, const uint8_t *in, size_t in_len, int padding); +int _goboringcrypto_RSA_decrypt(GO_RSA*, size_t *out_len, uint8_t *out, size_t max_out, const uint8_t *in, size_t in_len, int padding); +int _goboringcrypto_RSA_sign(int hash_nid, const uint8_t* in, unsigned int in_len, uint8_t *out, unsigned int *out_len, GO_RSA*); +int _goboringcrypto_RSA_sign_pss_mgf1(GO_RSA*, size_t *out_len, uint8_t *out, size_t max_out, const uint8_t *in, size_t in_len, const GO_EVP_MD *md, const GO_EVP_MD *mgf1_md, int salt_len); +int _goboringcrypto_RSA_sign_raw(GO_RSA*, size_t *out_len, uint8_t *out, size_t max_out, const uint8_t *in, size_t in_len, int padding); +int _goboringcrypto_RSA_verify(int hash_nid, const uint8_t *msg, size_t msg_len, const uint8_t *sig, size_t sig_len, GO_RSA*); +int _goboringcrypto_RSA_verify_pss_mgf1(GO_RSA*, const uint8_t *msg, size_t msg_len, const GO_EVP_MD *md, const GO_EVP_MD *mgf1_md, int salt_len, const uint8_t *sig, size_t sig_len); +int _goboringcrypto_RSA_verify_raw(GO_RSA*, size_t *out_len, uint8_t *out, size_t max_out, const uint8_t *in, size_t in_len, int padding); +unsigned _goboringcrypto_RSA_size(const GO_RSA*); +int _goboringcrypto_RSA_is_opaque(const GO_RSA*); +int _goboringcrypto_RSA_check_key(const GO_RSA*); +int _goboringcrypto_RSA_check_fips(GO_RSA*); +GO_RSA* _goboringcrypto_RSA_public_key_from_bytes(const uint8_t*, size_t); +GO_RSA* _goboringcrypto_RSA_private_key_from_bytes(const uint8_t*, size_t); +int _goboringcrypto_RSA_public_key_to_bytes(uint8_t**, size_t*, const GO_RSA*); +int _goboringcrypto_RSA_private_key_to_bytes(uint8_t**, size_t*, const GO_RSA*); + +// #include <openssl/evp.h> +/*unchecked (opaque)*/ typedef struct GO_EVP_PKEY { char data[1]; } GO_EVP_PKEY; +GO_EVP_PKEY* _goboringcrypto_EVP_PKEY_new(void); +void _goboringcrypto_EVP_PKEY_free(GO_EVP_PKEY*); +int _goboringcrypto_EVP_PKEY_set1_RSA(GO_EVP_PKEY*, GO_RSA*); + +/*unchecked (opaque)*/ typedef struct GO_EVP_PKEY_CTX { char data[1]; } GO_EVP_PKEY_CTX; + +GO_EVP_PKEY_CTX* _goboringcrypto_EVP_PKEY_CTX_new(GO_EVP_PKEY*, GO_ENGINE*); +void _goboringcrypto_EVP_PKEY_CTX_free(GO_EVP_PKEY_CTX*); +int _goboringcrypto_EVP_PKEY_CTX_set0_rsa_oaep_label(GO_EVP_PKEY_CTX*, uint8_t*, size_t); +int _goboringcrypto_EVP_PKEY_CTX_set_rsa_oaep_md(GO_EVP_PKEY_CTX*, const GO_EVP_MD*); +int _goboringcrypto_EVP_PKEY_CTX_set_rsa_padding(GO_EVP_PKEY_CTX*, int padding); +int _goboringcrypto_EVP_PKEY_decrypt(GO_EVP_PKEY_CTX*, uint8_t*, size_t*, const uint8_t*, size_t); +int _goboringcrypto_EVP_PKEY_encrypt(GO_EVP_PKEY_CTX*, uint8_t*, size_t*, const uint8_t*, size_t); +int _goboringcrypto_EVP_PKEY_decrypt_init(GO_EVP_PKEY_CTX*); +int _goboringcrypto_EVP_PKEY_encrypt_init(GO_EVP_PKEY_CTX*); +int _goboringcrypto_EVP_PKEY_CTX_set_rsa_mgf1_md(GO_EVP_PKEY_CTX*, const GO_EVP_MD*); +int _goboringcrypto_EVP_PKEY_CTX_set_rsa_pss_saltlen(GO_EVP_PKEY_CTX*, int); +int _goboringcrypto_EVP_PKEY_sign_init(GO_EVP_PKEY_CTX*); +int _goboringcrypto_EVP_PKEY_verify_init(GO_EVP_PKEY_CTX*); +int _goboringcrypto_EVP_PKEY_sign(GO_EVP_PKEY_CTX*, uint8_t*, size_t*, const uint8_t*, size_t); diff --git a/src/crypto/internal/boring/goboringcrypto_linux_amd64.syso b/src/crypto/internal/boring/goboringcrypto_linux_amd64.syso Binary files differnew file mode 100644 index 0000000000..e439dc3cd4 --- /dev/null +++ b/src/crypto/internal/boring/goboringcrypto_linux_amd64.syso diff --git a/src/crypto/internal/boring/hmac.go b/src/crypto/internal/boring/hmac.go new file mode 100644 index 0000000000..01b5844e5f --- /dev/null +++ b/src/crypto/internal/boring/hmac.go @@ -0,0 +1,156 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux,amd64 +// +build !android +// +build !cmd_go_bootstrap +// +build !msan + +package boring + +// #include "goboringcrypto.h" +import "C" +import ( + "crypto" + "hash" + "runtime" + "unsafe" +) + +// hashToMD converts a hash.Hash implementation from this package +// to a BoringCrypto *C.GO_EVP_MD. +func hashToMD(h hash.Hash) *C.GO_EVP_MD { + switch h.(type) { + case *sha1Hash: + return C._goboringcrypto_EVP_sha1() + case *sha224Hash: + return C._goboringcrypto_EVP_sha224() + case *sha256Hash: + return C._goboringcrypto_EVP_sha256() + case *sha384Hash: + return C._goboringcrypto_EVP_sha384() + case *sha512Hash: + return C._goboringcrypto_EVP_sha512() + } + return nil +} + +// cryptoHashToMD converts a crypto.Hash +// to a BoringCrypto *C.GO_EVP_MD. +func cryptoHashToMD(ch crypto.Hash) *C.GO_EVP_MD { + switch ch { + case crypto.MD5: + return C._goboringcrypto_EVP_md5() + case crypto.MD5SHA1: + return C._goboringcrypto_EVP_md5_sha1() + case crypto.SHA1: + return C._goboringcrypto_EVP_sha1() + case crypto.SHA224: + return C._goboringcrypto_EVP_sha224() + case crypto.SHA256: + return C._goboringcrypto_EVP_sha256() + case crypto.SHA384: + return C._goboringcrypto_EVP_sha384() + case crypto.SHA512: + return C._goboringcrypto_EVP_sha512() + } + return nil +} + +// NewHMAC returns a new HMAC using BoringCrypto. +// The function h must return a hash implemented by +// BoringCrypto (for example, h could be boring.NewSHA256). +// If h is not recognized, NewHMAC returns nil. +func NewHMAC(h func() hash.Hash, key []byte) hash.Hash { + ch := h() + md := hashToMD(ch) + if md == nil { + return nil + } + + // Note: Could hash down long keys here using EVP_Digest. + hkey := make([]byte, len(key)) + copy(hkey, key) + hmac := &boringHMAC{ + md: md, + size: ch.Size(), + blockSize: ch.BlockSize(), + key: hkey, + } + hmac.Reset() + return hmac +} + +type boringHMAC struct { + md *C.GO_EVP_MD + ctx C.GO_HMAC_CTX + ctx2 C.GO_HMAC_CTX + size int + blockSize int + key []byte + sum []byte + needCleanup bool +} + +func (h *boringHMAC) Reset() { + if h.needCleanup { + C._goboringcrypto_HMAC_CTX_cleanup(&h.ctx) + } else { + h.needCleanup = true + // Note: Because of the finalizer, any time h.ctx is passed to cgo, + // that call must be followed by a call to runtime.KeepAlive(h), + // to make sure h is not collected (and finalized) before the cgo + // call returns. + runtime.SetFinalizer(h, (*boringHMAC).finalize) + } + C._goboringcrypto_HMAC_CTX_init(&h.ctx) + + if C._goboringcrypto_HMAC_Init(&h.ctx, unsafe.Pointer(base(h.key)), C.int(len(h.key)), h.md) == 0 { + panic("boringcrypto: HMAC_Init failed") + } + if int(C._goboringcrypto_HMAC_size(&h.ctx)) != h.size { + println("boringcrypto: HMAC size:", C._goboringcrypto_HMAC_size(&h.ctx), "!=", h.size) + panic("boringcrypto: HMAC size mismatch") + } + runtime.KeepAlive(h) // Next line will keep h alive too; just making doubly sure. + h.sum = nil +} + +func (h *boringHMAC) finalize() { + C._goboringcrypto_HMAC_CTX_cleanup(&h.ctx) +} + +func (h *boringHMAC) Write(p []byte) (int, error) { + if len(p) > 0 { + C._goboringcrypto_HMAC_Update(&h.ctx, (*C.uint8_t)(unsafe.Pointer(&p[0])), C.size_t(len(p))) + } + runtime.KeepAlive(h) + return len(p), nil +} + +func (h *boringHMAC) Size() int { + return h.size +} + +func (h *boringHMAC) BlockSize() int { + return h.blockSize +} + +func (h *boringHMAC) Sum(in []byte) []byte { + if h.sum == nil { + size := h.Size() + h.sum = make([]byte, size) + } + // Make copy of context because Go hash.Hash mandates + // that Sum has no effect on the underlying stream. + // In particular it is OK to Sum, then Write more, then Sum again, + // and the second Sum acts as if the first didn't happen. + C._goboringcrypto_HMAC_CTX_init(&h.ctx2) + if C._goboringcrypto_HMAC_CTX_copy_ex(&h.ctx2, &h.ctx) == 0 { + panic("boringcrypto: HMAC_CTX_copy_ex failed") + } + C._goboringcrypto_HMAC_Final(&h.ctx2, (*C.uint8_t)(unsafe.Pointer(&h.sum[0])), nil) + C._goboringcrypto_HMAC_CTX_cleanup(&h.ctx2) + return append(in, h.sum...) +} diff --git a/src/crypto/internal/boring/notboring.go b/src/crypto/internal/boring/notboring.go new file mode 100644 index 0000000000..c21cb3cd55 --- /dev/null +++ b/src/crypto/internal/boring/notboring.go @@ -0,0 +1,109 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !linux !amd64 !cgo android cmd_go_bootstrap msan + +package boring + +import ( + "crypto" + "crypto/cipher" + "crypto/internal/boring/sig" + "hash" + "math/big" +) + +const available = false + +// Unreachable marks code that should be unreachable +// when BoringCrypto is in use. It is a no-op without BoringCrypto. +func Unreachable() { + // Code that's unreachable when using BoringCrypto + // is exactly the code we want to detect for reporting + // standard Go crypto. + sig.StandardCrypto() +} + +// UnreachableExceptTests marks code that should be unreachable +// when BoringCrypto is in use. It is a no-op without BoringCrypto. +func UnreachableExceptTests() {} + +type randReader int + +func (randReader) Read(b []byte) (int, error) { panic("boringcrypto: not available") } + +const RandReader = randReader(0) + +func NewSHA1() hash.Hash { panic("boringcrypto: not available") } +func NewSHA224() hash.Hash { panic("boringcrypto: not available") } +func NewSHA256() hash.Hash { panic("boringcrypto: not available") } +func NewSHA384() hash.Hash { panic("boringcrypto: not available") } +func NewSHA512() hash.Hash { panic("boringcrypto: not available") } + +func NewHMAC(h func() hash.Hash, key []byte) hash.Hash { panic("boringcrypto: not available") } + +func NewAESCipher(key []byte) (cipher.Block, error) { panic("boringcrypto: not available") } + +type PublicKeyECDSA struct{ _ int } +type PrivateKeyECDSA struct{ _ int } + +func GenerateKeyECDSA(curve string) (X, Y, D *big.Int, err error) { + panic("boringcrypto: not available") +} +func NewPrivateKeyECDSA(curve string, X, Y, D *big.Int) (*PrivateKeyECDSA, error) { + panic("boringcrypto: not available") +} +func NewPublicKeyECDSA(curve string, X, Y *big.Int) (*PublicKeyECDSA, error) { + panic("boringcrypto: not available") +} +func SignECDSA(priv *PrivateKeyECDSA, hash []byte) (r, s *big.Int, err error) { + panic("boringcrypto: not available") +} +func SignMarshalECDSA(priv *PrivateKeyECDSA, hash []byte) ([]byte, error) { + panic("boringcrypto: not available") +} +func VerifyECDSA(pub *PublicKeyECDSA, hash []byte, r, s *big.Int) bool { + panic("boringcrypto: not available") +} + +type PublicKeyRSA struct{ _ int } +type PrivateKeyRSA struct{ _ int } + +func DecryptRSAOAEP(h hash.Hash, priv *PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) { + panic("boringcrypto: not available") +} +func DecryptRSAPKCS1(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) { + panic("boringcrypto: not available") +} +func DecryptRSANoPadding(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) { + panic("boringcrypto: not available") +} +func EncryptRSAOAEP(h hash.Hash, pub *PublicKeyRSA, msg, label []byte) ([]byte, error) { + panic("boringcrypto: not available") +} +func EncryptRSAPKCS1(pub *PublicKeyRSA, msg []byte) ([]byte, error) { + panic("boringcrypto: not available") +} +func EncryptRSANoPadding(pub *PublicKeyRSA, msg []byte) ([]byte, error) { + panic("boringcrypto: not available") +} +func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv *big.Int, err error) { + panic("boringcrypto: not available") +} +func NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv *big.Int) (*PrivateKeyRSA, error) { + panic("boringcrypto: not available") +} +func NewPublicKeyRSA(N, E *big.Int) (*PublicKeyRSA, error) { panic("boringcrypto: not available") } +func SignRSAPKCS1v15(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte) ([]byte, error) { + panic("boringcrypto: not available") +} +func SignRSAPSS(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte, saltLen int) ([]byte, error) { + panic("boringcrypto: not available") +} +func VerifyRSAPKCS1v15(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte) error { + panic("boringcrypto: not available") +} +func VerifyRSAPSS(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte, saltLen int) error { + panic("boringcrypto: not available") +} diff --git a/src/crypto/internal/boring/rand.go b/src/crypto/internal/boring/rand.go new file mode 100644 index 0000000000..c3fc27cc08 --- /dev/null +++ b/src/crypto/internal/boring/rand.go @@ -0,0 +1,27 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux,amd64 +// +build !android +// +build !cmd_go_bootstrap +// +build !msan + +package boring + +// #include "goboringcrypto.h" +import "C" +import "unsafe" + +type randReader int + +func (randReader) Read(b []byte) (int, error) { + // Note: RAND_bytes should never fail; the return value exists only for historical reasons. + // We check it even so. + if len(b) > 0 && C._goboringcrypto_RAND_bytes((*C.uint8_t)(unsafe.Pointer(&b[0])), C.size_t(len(b))) == 0 { + return 0, fail("RAND_bytes") + } + return len(b), nil +} + +const RandReader = randReader(0) diff --git a/src/crypto/internal/boring/rsa.go b/src/crypto/internal/boring/rsa.go new file mode 100644 index 0000000000..8cb55266e4 --- /dev/null +++ b/src/crypto/internal/boring/rsa.go @@ -0,0 +1,329 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux,amd64 +// +build !android +// +build !cmd_go_bootstrap +// +build !msan + +package boring + +// #include "goboringcrypto.h" +import "C" +import ( + "crypto" + "crypto/subtle" + "errors" + "hash" + "math/big" + "runtime" + "strconv" + "unsafe" +) + +func GenerateKeyRSA(bits int) (N, E, D, P, Q, Dp, Dq, Qinv *big.Int, err error) { + bad := func(e error) (N, E, D, P, Q, Dp, Dq, Qinv *big.Int, err error) { + return nil, nil, nil, nil, nil, nil, nil, nil, e + } + + key := C._goboringcrypto_RSA_new() + if key == nil { + return bad(fail("RSA_new")) + } + defer C._goboringcrypto_RSA_free(key) + + if C._goboringcrypto_RSA_generate_key_fips(key, C.int(bits), nil) == 0 { + return bad(fail("RSA_generate_key_fips")) + } + + var n, e, d, p, q, dp, dq, qinv *C.GO_BIGNUM + C._goboringcrypto_RSA_get0_key(key, &n, &e, &d) + C._goboringcrypto_RSA_get0_factors(key, &p, &q) + C._goboringcrypto_RSA_get0_crt_params(key, &dp, &dq, &qinv) + return bnToBig(n), bnToBig(e), bnToBig(d), bnToBig(p), bnToBig(q), bnToBig(dp), bnToBig(dq), bnToBig(qinv), nil +} + +type PublicKeyRSA struct { + key *C.GO_RSA +} + +func NewPublicKeyRSA(N, E *big.Int) (*PublicKeyRSA, error) { + key := C._goboringcrypto_RSA_new() + if key == nil { + return nil, fail("RSA_new") + } + if !bigToBn(&key.n, N) || + !bigToBn(&key.e, E) { + return nil, fail("BN_bin2bn") + } + k := &PublicKeyRSA{key: key} + // Note: Because of the finalizer, any time k.key is passed to cgo, + // that call must be followed by a call to runtime.KeepAlive(k), + // to make sure k is not collected (and finalized) before the cgo + // call returns. + runtime.SetFinalizer(k, (*PublicKeyRSA).finalize) + return k, nil +} + +func (k *PublicKeyRSA) finalize() { + C._goboringcrypto_RSA_free(k.key) +} + +type PrivateKeyRSA struct { + key *C.GO_RSA +} + +func NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv *big.Int) (*PrivateKeyRSA, error) { + key := C._goboringcrypto_RSA_new() + if key == nil { + return nil, fail("RSA_new") + } + if !bigToBn(&key.n, N) || + !bigToBn(&key.e, E) || + !bigToBn(&key.d, D) || + !bigToBn(&key.p, P) || + !bigToBn(&key.q, Q) || + !bigToBn(&key.dmp1, Dp) || + !bigToBn(&key.dmq1, Dq) || + !bigToBn(&key.iqmp, Qinv) { + return nil, fail("BN_bin2bn") + } + k := &PrivateKeyRSA{key: key} + // Note: Because of the finalizer, any time k.key is passed to cgo, + // that call must be followed by a call to runtime.KeepAlive(k), + // to make sure k is not collected (and finalized) before the cgo + // call returns. + runtime.SetFinalizer(k, (*PrivateKeyRSA).finalize) + return k, nil +} + +func (k *PrivateKeyRSA) finalize() { + C._goboringcrypto_RSA_free(k.key) +} + +func setupRSA(key *C.GO_RSA, + padding C.int, h hash.Hash, label []byte, saltLen int, ch crypto.Hash, + init func(*C.GO_EVP_PKEY_CTX) C.int) (pkey *C.GO_EVP_PKEY, ctx *C.GO_EVP_PKEY_CTX, err error) { + defer func() { + if err != nil { + if pkey != nil { + C._goboringcrypto_EVP_PKEY_free(pkey) + pkey = nil + } + if ctx != nil { + C._goboringcrypto_EVP_PKEY_CTX_free(ctx) + ctx = nil + } + } + }() + + pkey = C._goboringcrypto_EVP_PKEY_new() + if pkey == nil { + return nil, nil, fail("EVP_PKEY_new") + } + if C._goboringcrypto_EVP_PKEY_set1_RSA(pkey, key) == 0 { + return nil, nil, fail("EVP_PKEY_set1_RSA") + } + ctx = C._goboringcrypto_EVP_PKEY_CTX_new(pkey, nil) + if ctx == nil { + return nil, nil, fail("EVP_PKEY_CTX_new") + } + if init(ctx) == 0 { + return nil, nil, fail("EVP_PKEY_operation_init") + } + if C._goboringcrypto_EVP_PKEY_CTX_set_rsa_padding(ctx, padding) == 0 { + return nil, nil, fail("EVP_PKEY_CTX_set_rsa_padding") + } + if padding == C.GO_RSA_PKCS1_OAEP_PADDING { + md := hashToMD(h) + if md == nil { + return nil, nil, errors.New("crypto/rsa: unsupported hash function") + } + if C._goboringcrypto_EVP_PKEY_CTX_set_rsa_oaep_md(ctx, md) == 0 { + return nil, nil, fail("EVP_PKEY_set_rsa_oaep_md") + } + // ctx takes ownership of label, so malloc a copy for BoringCrypto to free. + clabel := (*C.uint8_t)(C.malloc(C.size_t(len(label)))) + if clabel == nil { + return nil, nil, fail("malloc") + } + copy((*[1 << 30]byte)(unsafe.Pointer(clabel))[:len(label)], label) + if C._goboringcrypto_EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, clabel, C.size_t(len(label))) == 0 { + return nil, nil, fail("EVP_PKEY_CTX_set0_rsa_oaep_label") + } + } + if padding == C.GO_RSA_PKCS1_PSS_PADDING { + if saltLen != 0 { + if C._goboringcrypto_EVP_PKEY_CTX_set_rsa_pss_saltlen(ctx, C.int(saltLen)) == 0 { + return nil, nil, fail("EVP_PKEY_set_rsa_pss_saltlen") + } + } + md := cryptoHashToMD(ch) + if md == nil { + return nil, nil, errors.New("crypto/rsa: unsupported hash function") + } + if C._goboringcrypto_EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, md) == 0 { + return nil, nil, fail("EVP_PKEY_set_rsa_mgf1_md") + } + } + + return pkey, ctx, nil +} + +func cryptRSA(gokey interface{}, key *C.GO_RSA, + padding C.int, h hash.Hash, label []byte, saltLen int, ch crypto.Hash, + init func(*C.GO_EVP_PKEY_CTX) C.int, + crypt func(*C.GO_EVP_PKEY_CTX, *C.uint8_t, *C.size_t, *C.uint8_t, C.size_t) C.int, + in []byte) ([]byte, error) { + + pkey, ctx, err := setupRSA(key, padding, h, label, saltLen, ch, init) + if err != nil { + return nil, err + } + defer C._goboringcrypto_EVP_PKEY_free(pkey) + defer C._goboringcrypto_EVP_PKEY_CTX_free(ctx) + + var outLen C.size_t + if crypt(ctx, nil, &outLen, base(in), C.size_t(len(in))) == 0 { + return nil, fail("EVP_PKEY_decrypt/encrypt") + } + out := make([]byte, outLen) + if crypt(ctx, base(out), &outLen, base(in), C.size_t(len(in))) == 0 { + return nil, fail("EVP_PKEY_decrypt/encrypt") + } + runtime.KeepAlive(gokey) // keep key from being freed before now + return out[:outLen], nil +} + +func DecryptRSAOAEP(h hash.Hash, priv *PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) { + return cryptRSA(priv, priv.key, C.GO_RSA_PKCS1_OAEP_PADDING, h, label, 0, 0, decryptInit, decrypt, ciphertext) +} + +func EncryptRSAOAEP(h hash.Hash, pub *PublicKeyRSA, msg, label []byte) ([]byte, error) { + return cryptRSA(pub, pub.key, C.GO_RSA_PKCS1_OAEP_PADDING, h, label, 0, 0, encryptInit, encrypt, msg) +} + +func DecryptRSAPKCS1(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) { + return cryptRSA(priv, priv.key, C.GO_RSA_PKCS1_PADDING, nil, nil, 0, 0, decryptInit, decrypt, ciphertext) +} + +func EncryptRSAPKCS1(pub *PublicKeyRSA, msg []byte) ([]byte, error) { + return cryptRSA(pub, pub.key, C.GO_RSA_PKCS1_PADDING, nil, nil, 0, 0, encryptInit, encrypt, msg) +} + +func DecryptRSANoPadding(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) { + return cryptRSA(priv, priv.key, C.GO_RSA_NO_PADDING, nil, nil, 0, 0, decryptInit, decrypt, ciphertext) +} + +func EncryptRSANoPadding(pub *PublicKeyRSA, msg []byte) ([]byte, error) { + return cryptRSA(pub, pub.key, C.GO_RSA_NO_PADDING, nil, nil, 0, 0, encryptInit, encrypt, msg) +} + +// These dumb wrappers work around the fact that cgo functions cannot be used as values directly. + +func decryptInit(ctx *C.GO_EVP_PKEY_CTX) C.int { + return C._goboringcrypto_EVP_PKEY_decrypt_init(ctx) +} + +func decrypt(ctx *C.GO_EVP_PKEY_CTX, out *C.uint8_t, outLen *C.size_t, in *C.uint8_t, inLen C.size_t) C.int { + return C._goboringcrypto_EVP_PKEY_decrypt(ctx, out, outLen, in, inLen) +} + +func encryptInit(ctx *C.GO_EVP_PKEY_CTX) C.int { + return C._goboringcrypto_EVP_PKEY_encrypt_init(ctx) +} + +func encrypt(ctx *C.GO_EVP_PKEY_CTX, out *C.uint8_t, outLen *C.size_t, in *C.uint8_t, inLen C.size_t) C.int { + return C._goboringcrypto_EVP_PKEY_encrypt(ctx, out, outLen, in, inLen) +} + +func SignRSAPSS(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte, saltLen int) ([]byte, error) { + md := cryptoHashToMD(h) + if md == nil { + return nil, errors.New("crypto/rsa: unsupported hash function") + } + if saltLen == 0 { + saltLen = -1 + } + out := make([]byte, C._goboringcrypto_RSA_size(priv.key)) + var outLen C.size_t + if C._goboringcrypto_RSA_sign_pss_mgf1(priv.key, &outLen, base(out), C.size_t(len(out)), base(hashed), C.size_t(len(hashed)), md, nil, C.int(saltLen)) == 0 { + return nil, fail("RSA_sign_pss_mgf1") + } + runtime.KeepAlive(priv) + + return out[:outLen], nil +} + +func VerifyRSAPSS(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte, saltLen int) error { + md := cryptoHashToMD(h) + if md == nil { + return errors.New("crypto/rsa: unsupported hash function") + } + if saltLen == 0 { + saltLen = -2 // auto-recover + } + if C._goboringcrypto_RSA_verify_pss_mgf1(pub.key, base(hashed), C.size_t(len(hashed)), md, nil, C.int(saltLen), base(sig), C.size_t(len(sig))) == 0 { + return fail("RSA_verify_pss_mgf1") + } + runtime.KeepAlive(pub) + return nil +} + +func SignRSAPKCS1v15(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte) ([]byte, error) { + out := make([]byte, C._goboringcrypto_RSA_size(priv.key)) + if h == 0 { + // No hashing. + var outLen C.size_t + if C._goboringcrypto_RSA_sign_raw(priv.key, &outLen, base(out), C.size_t(len(out)), base(hashed), C.size_t(len(hashed)), C.GO_RSA_PKCS1_PADDING) == 0 { + return nil, fail("RSA_sign_raw") + } + runtime.KeepAlive(priv) + return out[:outLen], nil + } + + md := cryptoHashToMD(h) + if md == nil { + return nil, errors.New("crypto/rsa: unsupported hash function: " + strconv.Itoa(int(h))) + } + nid := C._goboringcrypto_EVP_MD_type(md) + var outLen C.uint + if C._goboringcrypto_RSA_sign(nid, base(hashed), C.uint(len(hashed)), base(out), &outLen, priv.key) == 0 { + return nil, fail("RSA_sign") + } + runtime.KeepAlive(priv) + return out[:outLen], nil +} + +func VerifyRSAPKCS1v15(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte) error { + size := int(C._goboringcrypto_RSA_size(pub.key)) + if len(sig) < size { + // BoringCrypto requires sig to be same size as RSA key, so pad with leading zeros. + zsig := make([]byte, size) + copy(zsig[len(zsig)-len(sig):], sig) + sig = zsig + } + if h == 0 { + var outLen C.size_t + out := make([]byte, size) + if C._goboringcrypto_RSA_verify_raw(pub.key, &outLen, base(out), C.size_t(len(out)), base(sig), C.size_t(len(sig)), C.GO_RSA_PKCS1_PADDING) == 0 { + return fail("RSA_verify") + } + if subtle.ConstantTimeCompare(hashed, out[:outLen]) != 1 { + return fail("RSA_verify") + } + runtime.KeepAlive(pub) + return nil + } + md := cryptoHashToMD(h) + if md == nil { + return errors.New("crypto/rsa: unsupported hash function") + } + nid := C._goboringcrypto_EVP_MD_type(md) + if C._goboringcrypto_RSA_verify(nid, base(hashed), C.size_t(len(hashed)), base(sig), C.size_t(len(sig)), pub.key) == 0 { + return fail("RSA_verify") + } + runtime.KeepAlive(pub) + return nil +} diff --git a/src/crypto/internal/boring/sha.go b/src/crypto/internal/boring/sha.go new file mode 100644 index 0000000000..5ca158ca32 --- /dev/null +++ b/src/crypto/internal/boring/sha.go @@ -0,0 +1,480 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build linux,amd64 +// +build !android +// +build !cmd_go_bootstrap +// +build !msan + +package boring + +// #include "goboringcrypto.h" +import "C" +import ( + "errors" + "hash" + "unsafe" +) + +// NewSHA1 returns a new SHA1 hash. +func NewSHA1() hash.Hash { + h := new(sha1Hash) + h.Reset() + return h +} + +type sha1Hash struct { + ctx C.GO_SHA_CTX + out [20]byte +} + +type sha1Ctx struct { + h [5]uint32 + nl, nh uint32 + x [64]byte + nx uint32 +} + +func (h *sha1Hash) Reset() { C._goboringcrypto_SHA1_Init(&h.ctx) } +func (h *sha1Hash) Size() int { return 20 } +func (h *sha1Hash) BlockSize() int { return 64 } +func (h *sha1Hash) Sum(in []byte) []byte { return append(in, h.sum()...) } + +func (h *sha1Hash) Write(p []byte) (int, error) { + if len(p) > 0 && C._goboringcrypto_SHA1_Update(&h.ctx, unsafe.Pointer(&p[0]), C.size_t(len(p))) == 0 { + panic("boringcrypto: SHA1_Update failed") + } + return len(p), nil +} + +func (h0 *sha1Hash) sum() []byte { + h := *h0 // make copy so future Write+Sum is valid + if C._goboringcrypto_SHA1_Final((*C.uint8_t)(unsafe.Pointer(&h.out[0])), &h.ctx) == 0 { + panic("boringcrypto: SHA1_Final failed") + } + return h.out[:] +} + +const ( + sha1Magic = "sha\x01" + sha1MarshaledSize = len(sha1Magic) + 5*4 + 64 + 8 +) + +func (h *sha1Hash) MarshalBinary() ([]byte, error) { + d := (*sha1Ctx)(unsafe.Pointer(&h.ctx)) + b := make([]byte, 0, sha1MarshaledSize) + b = append(b, sha1Magic...) + b = appendUint32(b, d.h[0]) + b = appendUint32(b, d.h[1]) + b = appendUint32(b, d.h[2]) + b = appendUint32(b, d.h[3]) + b = appendUint32(b, d.h[4]) + b = append(b, d.x[:d.nx]...) + b = b[:len(b)+len(d.x)-int(d.nx)] // already zero + b = appendUint64(b, uint64(d.nl)>>3|uint64(d.nh)<<29) + return b, nil +} + +func (h *sha1Hash) UnmarshalBinary(b []byte) error { + if len(b) < len(sha1Magic) || string(b[:len(sha1Magic)]) != sha1Magic { + return errors.New("crypto/sha1: invalid hash state identifier") + } + if len(b) != sha1MarshaledSize { + return errors.New("crypto/sha1: invalid hash state size") + } + d := (*sha1Ctx)(unsafe.Pointer(&h.ctx)) + b = b[len(sha1Magic):] + b, d.h[0] = consumeUint32(b) + b, d.h[1] = consumeUint32(b) + b, d.h[2] = consumeUint32(b) + b, d.h[3] = consumeUint32(b) + b, d.h[4] = consumeUint32(b) + b = b[copy(d.x[:], b):] + b, n := consumeUint64(b) + d.nl = uint32(n << 3) + d.nh = uint32(n >> 29) + d.nx = uint32(n) % 64 + return nil +} + +// NewSHA224 returns a new SHA224 hash. +func NewSHA224() hash.Hash { + h := new(sha224Hash) + h.Reset() + return h +} + +type sha224Hash struct { + ctx C.GO_SHA256_CTX + out [224 / 8]byte +} + +func (h *sha224Hash) Reset() { C._goboringcrypto_SHA224_Init(&h.ctx) } +func (h *sha224Hash) Size() int { return 224 / 8 } +func (h *sha224Hash) BlockSize() int { return 64 } +func (h *sha224Hash) Sum(in []byte) []byte { return append(in, h.sum()...) } + +func (h *sha224Hash) Write(p []byte) (int, error) { + if len(p) > 0 && C._goboringcrypto_SHA224_Update(&h.ctx, unsafe.Pointer(&p[0]), C.size_t(len(p))) == 0 { + panic("boringcrypto: SHA224_Update failed") + } + return len(p), nil +} + +func (h0 *sha224Hash) sum() []byte { + h := *h0 // make copy so future Write+Sum is valid + if C._goboringcrypto_SHA224_Final((*C.uint8_t)(unsafe.Pointer(&h.out[0])), &h.ctx) == 0 { + panic("boringcrypto: SHA224_Final failed") + } + return h.out[:] +} + +// NewSHA256 returns a new SHA256 hash. +func NewSHA256() hash.Hash { + h := new(sha256Hash) + h.Reset() + return h +} + +type sha256Hash struct { + ctx C.GO_SHA256_CTX + out [256 / 8]byte +} + +func (h *sha256Hash) Reset() { C._goboringcrypto_SHA256_Init(&h.ctx) } +func (h *sha256Hash) Size() int { return 256 / 8 } +func (h *sha256Hash) BlockSize() int { return 64 } +func (h *sha256Hash) Sum(in []byte) []byte { return append(in, h.sum()...) } + +func (h *sha256Hash) Write(p []byte) (int, error) { + if len(p) > 0 && C._goboringcrypto_SHA256_Update(&h.ctx, unsafe.Pointer(&p[0]), C.size_t(len(p))) == 0 { + panic("boringcrypto: SHA256_Update failed") + } + return len(p), nil +} + +func (h0 *sha256Hash) sum() []byte { + h := *h0 // make copy so future Write+Sum is valid + if C._goboringcrypto_SHA256_Final((*C.uint8_t)(unsafe.Pointer(&h.out[0])), &h.ctx) == 0 { + panic("boringcrypto: SHA256_Final failed") + } + return h.out[:] +} + +const ( + magic224 = "sha\x02" + magic256 = "sha\x03" + marshaledSize256 = len(magic256) + 8*4 + 64 + 8 +) + +type sha256Ctx struct { + h [8]uint32 + nl, nh uint32 + x [64]byte + nx uint32 +} + +func (h *sha224Hash) MarshalBinary() ([]byte, error) { + d := (*sha256Ctx)(unsafe.Pointer(&h.ctx)) + b := make([]byte, 0, marshaledSize256) + b = append(b, magic224...) + b = appendUint32(b, d.h[0]) + b = appendUint32(b, d.h[1]) + b = appendUint32(b, d.h[2]) + b = appendUint32(b, d.h[3]) + b = appendUint32(b, d.h[4]) + b = appendUint32(b, d.h[5]) + b = appendUint32(b, d.h[6]) + b = appendUint32(b, d.h[7]) + b = append(b, d.x[:d.nx]...) + b = b[:len(b)+len(d.x)-int(d.nx)] // already zero + b = appendUint64(b, uint64(d.nl)>>3|uint64(d.nh)<<29) + return b, nil +} + +func (h *sha256Hash) MarshalBinary() ([]byte, error) { + d := (*sha256Ctx)(unsafe.Pointer(&h.ctx)) + b := make([]byte, 0, marshaledSize256) + b = append(b, magic256...) + b = appendUint32(b, d.h[0]) + b = appendUint32(b, d.h[1]) + b = appendUint32(b, d.h[2]) + b = appendUint32(b, d.h[3]) + b = appendUint32(b, d.h[4]) + b = appendUint32(b, d.h[5]) + b = appendUint32(b, d.h[6]) + b = appendUint32(b, d.h[7]) + b = append(b, d.x[:d.nx]...) + b = b[:len(b)+len(d.x)-int(d.nx)] // already zero + b = appendUint64(b, uint64(d.nl)>>3|uint64(d.nh)<<29) + return b, nil +} + +func (h *sha224Hash) UnmarshalBinary(b []byte) error { + if len(b) < len(magic224) || string(b[:len(magic224)]) != magic224 { + return errors.New("crypto/sha256: invalid hash state identifier") + } + if len(b) != marshaledSize256 { + return errors.New("crypto/sha256: invalid hash state size") + } + d := (*sha256Ctx)(unsafe.Pointer(&h.ctx)) + b = b[len(magic224):] + b, d.h[0] = consumeUint32(b) + b, d.h[1] = consumeUint32(b) + b, d.h[2] = consumeUint32(b) + b, d.h[3] = consumeUint32(b) + b, d.h[4] = consumeUint32(b) + b, d.h[5] = consumeUint32(b) + b, d.h[6] = consumeUint32(b) + b, d.h[7] = consumeUint32(b) + b = b[copy(d.x[:], b):] + b, n := consumeUint64(b) + d.nl = uint32(n << 3) + d.nh = uint32(n >> 29) + d.nx = uint32(n) % 64 + return nil +} + +func (h *sha256Hash) UnmarshalBinary(b []byte) error { + if len(b) < len(magic256) || string(b[:len(magic256)]) != magic256 { + return errors.New("crypto/sha256: invalid hash state identifier") + } + if len(b) != marshaledSize256 { + return errors.New("crypto/sha256: invalid hash state size") + } + d := (*sha256Ctx)(unsafe.Pointer(&h.ctx)) + b = b[len(magic256):] + b, d.h[0] = consumeUint32(b) + b, d.h[1] = consumeUint32(b) + b, d.h[2] = consumeUint32(b) + b, d.h[3] = consumeUint32(b) + b, d.h[4] = consumeUint32(b) + b, d.h[5] = consumeUint32(b) + b, d.h[6] = consumeUint32(b) + b, d.h[7] = consumeUint32(b) + b = b[copy(d.x[:], b):] + b, n := consumeUint64(b) + d.nl = uint32(n << 3) + d.nh = uint32(n >> 29) + d.nx = uint32(n) % 64 + return nil +} + +// NewSHA384 returns a new SHA384 hash. +func NewSHA384() hash.Hash { + h := new(sha384Hash) + h.Reset() + return h +} + +type sha384Hash struct { + ctx C.GO_SHA512_CTX + out [384 / 8]byte +} + +func (h *sha384Hash) Reset() { C._goboringcrypto_SHA384_Init(&h.ctx) } +func (h *sha384Hash) Size() int { return 384 / 8 } +func (h *sha384Hash) BlockSize() int { return 128 } +func (h *sha384Hash) Sum(in []byte) []byte { return append(in, h.sum()...) } + +func (h *sha384Hash) Write(p []byte) (int, error) { + if len(p) > 0 && C._goboringcrypto_SHA384_Update(&h.ctx, unsafe.Pointer(&p[0]), C.size_t(len(p))) == 0 { + panic("boringcrypto: SHA384_Update failed") + } + return len(p), nil +} + +func (h0 *sha384Hash) sum() []byte { + h := *h0 // make copy so future Write+Sum is valid + if C._goboringcrypto_SHA384_Final((*C.uint8_t)(unsafe.Pointer(&h.out[0])), &h.ctx) == 0 { + panic("boringcrypto: SHA384_Final failed") + } + return h.out[:] +} + +// NewSHA512 returns a new SHA512 hash. +func NewSHA512() hash.Hash { + h := new(sha512Hash) + h.Reset() + return h +} + +type sha512Hash struct { + ctx C.GO_SHA512_CTX + out [512 / 8]byte +} + +func (h *sha512Hash) Reset() { C._goboringcrypto_SHA512_Init(&h.ctx) } +func (h *sha512Hash) Size() int { return 512 / 8 } +func (h *sha512Hash) BlockSize() int { return 128 } +func (h *sha512Hash) Sum(in []byte) []byte { return append(in, h.sum()...) } + +func (h *sha512Hash) Write(p []byte) (int, error) { + if len(p) > 0 && C._goboringcrypto_SHA512_Update(&h.ctx, unsafe.Pointer(&p[0]), C.size_t(len(p))) == 0 { + panic("boringcrypto: SHA512_Update failed") + } + return len(p), nil +} + +func (h0 *sha512Hash) sum() []byte { + h := *h0 // make copy so future Write+Sum is valid + if C._goboringcrypto_SHA512_Final((*C.uint8_t)(unsafe.Pointer(&h.out[0])), &h.ctx) == 0 { + panic("boringcrypto: SHA512_Final failed") + } + return h.out[:] +} + +type sha512Ctx struct { + h [8]uint64 + nl, nh uint64 + x [128]byte + nx uint32 +} + +const ( + magic384 = "sha\x04" + magic512_224 = "sha\x05" + magic512_256 = "sha\x06" + magic512 = "sha\x07" + marshaledSize512 = len(magic512) + 8*8 + 128 + 8 +) + +var zero [128]byte + +func (h *sha384Hash) MarshalBinary() ([]byte, error) { + d := (*sha512Ctx)(unsafe.Pointer(&h.ctx)) + b := make([]byte, 0, marshaledSize512) + b = append(b, magic384...) + b = appendUint64(b, d.h[0]) + b = appendUint64(b, d.h[1]) + b = appendUint64(b, d.h[2]) + b = appendUint64(b, d.h[3]) + b = appendUint64(b, d.h[4]) + b = appendUint64(b, d.h[5]) + b = appendUint64(b, d.h[6]) + b = appendUint64(b, d.h[7]) + b = append(b, d.x[:d.nx]...) + b = b[:len(b)+len(d.x)-int(d.nx)] // already zero + b = appendUint64(b, d.nl>>3|d.nh<<61) + return b, nil +} + +func (h *sha512Hash) MarshalBinary() ([]byte, error) { + d := (*sha512Ctx)(unsafe.Pointer(&h.ctx)) + b := make([]byte, 0, marshaledSize512) + b = append(b, magic512...) + b = appendUint64(b, d.h[0]) + b = appendUint64(b, d.h[1]) + b = appendUint64(b, d.h[2]) + b = appendUint64(b, d.h[3]) + b = appendUint64(b, d.h[4]) + b = appendUint64(b, d.h[5]) + b = appendUint64(b, d.h[6]) + b = appendUint64(b, d.h[7]) + b = append(b, d.x[:d.nx]...) + b = b[:len(b)+len(d.x)-int(d.nx)] // already zero + b = appendUint64(b, d.nl>>3|d.nh<<61) + return b, nil +} + +func (h *sha384Hash) UnmarshalBinary(b []byte) error { + if len(b) < len(magic512) { + return errors.New("crypto/sha512: invalid hash state identifier") + } + if string(b[:len(magic384)]) != magic384 { + return errors.New("crypto/sha512: invalid hash state identifier") + } + if len(b) != marshaledSize512 { + return errors.New("crypto/sha512: invalid hash state size") + } + d := (*sha512Ctx)(unsafe.Pointer(&h.ctx)) + b = b[len(magic512):] + b, d.h[0] = consumeUint64(b) + b, d.h[1] = consumeUint64(b) + b, d.h[2] = consumeUint64(b) + b, d.h[3] = consumeUint64(b) + b, d.h[4] = consumeUint64(b) + b, d.h[5] = consumeUint64(b) + b, d.h[6] = consumeUint64(b) + b, d.h[7] = consumeUint64(b) + b = b[copy(d.x[:], b):] + b, n := consumeUint64(b) + d.nl = n << 3 + d.nh = n >> 61 + d.nx = uint32(n) % 128 + return nil +} + +func (h *sha512Hash) UnmarshalBinary(b []byte) error { + if len(b) < len(magic512) { + return errors.New("crypto/sha512: invalid hash state identifier") + } + if string(b[:len(magic512)]) != magic512 { + return errors.New("crypto/sha512: invalid hash state identifier") + } + if len(b) != marshaledSize512 { + return errors.New("crypto/sha512: invalid hash state size") + } + d := (*sha512Ctx)(unsafe.Pointer(&h.ctx)) + b = b[len(magic512):] + b, d.h[0] = consumeUint64(b) + b, d.h[1] = consumeUint64(b) + b, d.h[2] = consumeUint64(b) + b, d.h[3] = consumeUint64(b) + b, d.h[4] = consumeUint64(b) + b, d.h[5] = consumeUint64(b) + b, d.h[6] = consumeUint64(b) + b, d.h[7] = consumeUint64(b) + b = b[copy(d.x[:], b):] + b, n := consumeUint64(b) + d.nl = n << 3 + d.nh = n >> 61 + d.nx = uint32(n) % 128 + return nil +} + +func appendUint64(b []byte, x uint64) []byte { + var a [8]byte + putUint64(a[:], x) + return append(b, a[:]...) +} + +func appendUint32(b []byte, x uint32) []byte { + var a [4]byte + putUint32(a[:], x) + return append(b, a[:]...) +} + +func consumeUint64(b []byte) ([]byte, uint64) { + _ = b[7] + x := uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 | + uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56 + return b[8:], x +} + +func consumeUint32(b []byte) ([]byte, uint32) { + _ = b[3] + x := uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24 + return b[4:], x +} + +func putUint64(x []byte, s uint64) { + _ = x[7] + x[0] = byte(s >> 56) + x[1] = byte(s >> 48) + x[2] = byte(s >> 40) + x[3] = byte(s >> 32) + x[4] = byte(s >> 24) + x[5] = byte(s >> 16) + x[6] = byte(s >> 8) + x[7] = byte(s) +} + +func putUint32(x []byte, s uint32) { + _ = x[3] + x[0] = byte(s >> 24) + x[1] = byte(s >> 16) + x[2] = byte(s >> 8) + x[3] = byte(s) +} diff --git a/src/crypto/internal/boring/sig/sig.go b/src/crypto/internal/boring/sig/sig.go new file mode 100644 index 0000000000..716c03c5e9 --- /dev/null +++ b/src/crypto/internal/boring/sig/sig.go @@ -0,0 +1,17 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package sig holds “code signatures” that can be called +// and will result in certain code sequences being linked into +// the final binary. The functions themselves are no-ops. +package sig + +// BoringCrypto indicates that the BoringCrypto module is present. +func BoringCrypto() + +// FIPSOnly indicates that package crypto/tls/fipsonly is present. +func FIPSOnly() + +// StandardCrypto indicates that standard Go crypto is present. +func StandardCrypto() diff --git a/src/crypto/internal/boring/sig/sig_amd64.s b/src/crypto/internal/boring/sig/sig_amd64.s new file mode 100644 index 0000000000..64e3462e4e --- /dev/null +++ b/src/crypto/internal/boring/sig/sig_amd64.s @@ -0,0 +1,54 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +// These functions are no-ops, but you can search for their implementations +// to find out whether they are linked into a particular binary. +// +// Each function consists of a two-byte jump over the next 29-bytes, +// then a 5-byte indicator sequence unlikely to occur in real x86 instructions, +// then a randomly-chosen 24-byte sequence, and finally a return instruction +// (the target of the jump). +// +// These sequences are known to rsc.io/goversion. + +#define START \ + BYTE $0xEB; BYTE $0x1D; BYTE $0xF4; BYTE $0x48; BYTE $0xF4; BYTE $0x4B; BYTE $0xF4 + +#define END \ + BYTE $0xC3 + +// BoringCrypto indicates that BoringCrypto (in particular, its func init) is present. +TEXT ·BoringCrypto(SB),NOSPLIT,$0 + START + BYTE $0xB3; BYTE $0x32; BYTE $0xF5; BYTE $0x28; + BYTE $0x13; BYTE $0xA3; BYTE $0xB4; BYTE $0x50; + BYTE $0xD4; BYTE $0x41; BYTE $0xCC; BYTE $0x24; + BYTE $0x85; BYTE $0xF0; BYTE $0x01; BYTE $0x45; + BYTE $0x4E; BYTE $0x92; BYTE $0x10; BYTE $0x1B; + BYTE $0x1D; BYTE $0x2F; BYTE $0x19; BYTE $0x50; + END + +// StandardCrypto indicates that standard Go crypto is present. +TEXT ·StandardCrypto(SB),NOSPLIT,$0 + START + BYTE $0xba; BYTE $0xee; BYTE $0x4d; BYTE $0xfa; + BYTE $0x98; BYTE $0x51; BYTE $0xca; BYTE $0x56; + BYTE $0xa9; BYTE $0x11; BYTE $0x45; BYTE $0xe8; + BYTE $0x3e; BYTE $0x99; BYTE $0xc5; BYTE $0x9c; + BYTE $0xf9; BYTE $0x11; BYTE $0xcb; BYTE $0x8e; + BYTE $0x80; BYTE $0xda; BYTE $0xf1; BYTE $0x2f; + END + +// FIPSOnly indicates that crypto/tls/fipsonly is present. +TEXT ·FIPSOnly(SB),NOSPLIT,$0 + START + BYTE $0x36; BYTE $0x3C; BYTE $0xB9; BYTE $0xCE; + BYTE $0x9D; BYTE $0x68; BYTE $0x04; BYTE $0x7D; + BYTE $0x31; BYTE $0xF2; BYTE $0x8D; BYTE $0x32; + BYTE $0x5D; BYTE $0x5C; BYTE $0xA5; BYTE $0x87; + BYTE $0x3F; BYTE $0x5D; BYTE $0x80; BYTE $0xCA; + BYTE $0xF6; BYTE $0xD6; BYTE $0x15; BYTE $0x1B; + END diff --git a/src/crypto/internal/boring/sig/sig_other.s b/src/crypto/internal/boring/sig/sig_other.s new file mode 100644 index 0000000000..2eb31734ef --- /dev/null +++ b/src/crypto/internal/boring/sig/sig_other.s @@ -0,0 +1,19 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// These functions are no-ops. +// On amd64 they have recognizable implementations, so that you can +// search a particular binary to see if they are present. +// On other platforms (those using this source file), they don't. + +// +build !amd64 + +TEXT ·BoringCrypto(SB),$0 + RET + +TEXT ·FIPSOnly(SB),$0 + RET + +TEXT ·StandardCrypto(SB),$0 + RET diff --git a/src/crypto/issue21104_test.go b/src/crypto/issue21104_test.go index b4276df4e1..4662088799 100644 --- a/src/crypto/issue21104_test.go +++ b/src/crypto/issue21104_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package crypto +package crypto_test import ( "crypto/aes" diff --git a/src/crypto/rand/rand_unix.go b/src/crypto/rand/rand_unix.go index f3091f51c5..246639702e 100644 --- a/src/crypto/rand/rand_unix.go +++ b/src/crypto/rand/rand_unix.go @@ -22,12 +22,18 @@ import ( "time" ) +import "crypto/internal/boring" + const urandomDevice = "/dev/urandom" // Easy implementation: read from /dev/urandom. // This is sufficient on Linux, OS X, and FreeBSD. func init() { + if boring.Enabled { + Reader = boring.RandReader + return + } if runtime.GOOS == "plan9" { Reader = newReader(nil) } else { @@ -48,6 +54,7 @@ type devReader struct { var altGetRandom func([]byte) (ok bool) func (r *devReader) Read(b []byte) (n int, err error) { + boring.Unreachable() if atomic.CompareAndSwapInt32(&r.used, 0, 1) { // First use of randomness. Start timer to warn about // being blocked on entropy not being available. @@ -117,6 +124,7 @@ type reader struct { } func (r *reader) Read(b []byte) (n int, err error) { + boring.Unreachable() r.mu.Lock() defer r.mu.Unlock() n = len(b) diff --git a/src/crypto/rsa/boring.go b/src/crypto/rsa/boring.go new file mode 100644 index 0000000000..0f362a2f16 --- /dev/null +++ b/src/crypto/rsa/boring.go @@ -0,0 +1,124 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package rsa + +import ( + "crypto/internal/boring" + "math/big" + "sync/atomic" + "unsafe" +) + +// Cached conversions from Go PublicKey/PrivateKey to BoringCrypto. +// +// A new 'boring atomic.Value' field in both PublicKey and PrivateKey +// serves as a cache for the most recent conversion. The cache is an +// atomic.Value because code might reasonably set up a key and then +// (thinking it immutable) use it from multiple goroutines simultaneously. +// The first operation initializes the cache; if there are multiple simultaneous +// first operations, they will do redundant work but not step on each other. +// +// We could just assume that once used in a sign/verify/encrypt/decrypt operation, +// a particular key is never again modified, but that has not been a +// stated assumption before. Just in case there is any existing code that +// does modify the key between operations, we save the original values +// alongside the cached BoringCrypto key and check that the real key +// still matches before using the cached key. The theory is that the real +// operations are significantly more expensive than the comparison. + +type boringPub struct { + key *boring.PublicKeyRSA + orig PublicKey +} + +func boringPublicKey(pub *PublicKey) (*boring.PublicKeyRSA, error) { + b := (*boringPub)(atomic.LoadPointer(&pub.boring)) + if b != nil && publicKeyEqual(&b.orig, pub) { + return b.key, nil + } + + b = new(boringPub) + b.orig = copyPublicKey(pub) + key, err := boring.NewPublicKeyRSA(b.orig.N, big.NewInt(int64(b.orig.E))) + if err != nil { + return nil, err + } + b.key = key + atomic.StorePointer(&pub.boring, unsafe.Pointer(b)) + return key, nil +} + +type boringPriv struct { + key *boring.PrivateKeyRSA + orig PrivateKey +} + +func boringPrivateKey(priv *PrivateKey) (*boring.PrivateKeyRSA, error) { + b := (*boringPriv)(atomic.LoadPointer(&priv.boring)) + if b != nil && privateKeyEqual(&b.orig, priv) { + return b.key, nil + } + + b = new(boringPriv) + b.orig = copyPrivateKey(priv) + + var N, E, D, P, Q, Dp, Dq, Qinv *big.Int + N = b.orig.N + E = big.NewInt(int64(b.orig.E)) + D = b.orig.D + if len(b.orig.Primes) == 2 { + P = b.orig.Primes[0] + Q = b.orig.Primes[1] + Dp = b.orig.Precomputed.Dp + Dq = b.orig.Precomputed.Dq + Qinv = b.orig.Precomputed.Qinv + } + key, err := boring.NewPrivateKeyRSA(N, E, D, P, Q, Dp, Dq, Qinv) + if err != nil { + return nil, err + } + b.key = key + atomic.StorePointer(&priv.boring, unsafe.Pointer(b)) + return key, nil +} + +func publicKeyEqual(k1, k2 *PublicKey) bool { + return k1.N != nil && + k1.N.Cmp(k2.N) == 0 && + k1.E == k2.E +} + +func copyPublicKey(k *PublicKey) PublicKey { + return PublicKey{ + N: new(big.Int).Set(k.N), + E: k.E, + } +} + +func privateKeyEqual(k1, k2 *PrivateKey) bool { + return publicKeyEqual(&k1.PublicKey, &k2.PublicKey) && + k1.D.Cmp(k2.D) == 0 +} + +func copyPrivateKey(k *PrivateKey) PrivateKey { + dst := PrivateKey{ + PublicKey: copyPublicKey(&k.PublicKey), + D: new(big.Int).Set(k.D), + } + dst.Primes = make([]*big.Int, len(k.Primes)) + for i, p := range k.Primes { + dst.Primes[i] = new(big.Int).Set(p) + } + if x := k.Precomputed.Dp; x != nil { + dst.Precomputed.Dp = new(big.Int).Set(x) + } + if x := k.Precomputed.Dq; x != nil { + dst.Precomputed.Dq = new(big.Int).Set(x) + } + if x := k.Precomputed.Qinv; x != nil { + dst.Precomputed.Qinv = new(big.Int).Set(x) + } + return dst +} diff --git a/src/crypto/rsa/boring_test.go b/src/crypto/rsa/boring_test.go new file mode 100644 index 0000000000..26fdabb197 --- /dev/null +++ b/src/crypto/rsa/boring_test.go @@ -0,0 +1,127 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Note: Can run these tests against the non-BoringCrypto +// version of the code by using "CGO_ENABLED=0 go test". + +package rsa + +import ( + "crypto" + "crypto/rand" + "encoding/asn1" + "reflect" + "runtime" + "runtime/debug" + "sync" + "sync/atomic" + "testing" + "unsafe" +) + +func TestBoringASN1Marshal(t *testing.T) { + k, err := GenerateKey(rand.Reader, 128) + if err != nil { + t.Fatal(err) + } + // This used to fail, because of the unexported 'boring' field. + // Now the compiler hides it [sic]. + _, err = asn1.Marshal(k.PublicKey) + if err != nil { + t.Fatal(err) + } +} + +func TestBoringDeepEqual(t *testing.T) { + k, err := GenerateKey(rand.Reader, 128) + if err != nil { + t.Fatal(err) + } + k.boring = nil // probably nil already but just in case + k2 := *k + k2.boring = unsafe.Pointer(k) // anything not nil, for this test + if !reflect.DeepEqual(k, &k2) { + // compiler should be hiding the boring field from reflection + t.Fatalf("DeepEqual compared boring fields") + } +} + +func TestBoringVerify(t *testing.T) { + // This changed behavior and broke golang.org/x/crypto/openpgp. + // Go accepts signatures without leading 0 padding, while BoringCrypto does not. + // So the Go wrappers must adapt. + key := &PublicKey{ + N: bigFromHex("c4fdf7b40a5477f206e6ee278eaef888ca73bf9128a9eef9f2f1ddb8b7b71a4c07cfa241f028a04edb405e4d916c61d6beabc333813dc7b484d2b3c52ee233c6a79b1eea4e9cc51596ba9cd5ac5aeb9df62d86ea051055b79d03f8a4fa9f38386f5bd17529138f3325d46801514ea9047977e0829ed728e68636802796801be1"), + E: 65537, + } + + hash := fromHex("019c5571724fb5d0e47a4260c940e9803ba05a44") + paddedHash := fromHex("3021300906052b0e03021a05000414019c5571724fb5d0e47a4260c940e9803ba05a44") + + // signature is one byte shorter than key.N. + sig := fromHex("5edfbeb6a73e7225ad3cc52724e2872e04260d7daf0d693c170d8c4b243b8767bc7785763533febc62ec2600c30603c433c095453ede59ff2fcabeb84ce32e0ed9d5cf15ffcbc816202b64370d4d77c1e9077d74e94a16fb4fa2e5bec23a56d7a73cf275f91691ae1801a976fcde09e981a2f6327ac27ea1fecf3185df0d56") + + err := VerifyPKCS1v15(key, 0, paddedHash, sig) + if err != nil { + t.Errorf("raw: %v", err) + } + + err = VerifyPKCS1v15(key, crypto.SHA1, hash, sig) + if err != nil { + t.Errorf("sha1: %v", err) + } +} + +func TestBoringGenerateKey(t *testing.T) { + k, err := GenerateKey(rand.Reader, 2048) // 2048 is smallest size BoringCrypto might kick in for + if err != nil { + t.Fatal(err) + } + + // Non-Boring GenerateKey always sets CRTValues to a non-nil (possibly empty) slice. + if k.Precomputed.CRTValues == nil { + t.Fatalf("GenerateKey: Precomputed.CRTValues = nil") + } +} + +func TestBoringFinalizers(t *testing.T) { + if runtime.GOOS == "nacl" || runtime.GOOS == "js" { + // Times out on nacl and js/wasm (without BoringCrypto) + // but not clear why - probably consuming rand.Reader too quickly + // and being throttled. Also doesn't really matter. + t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH) + } + + k, err := GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatal(err) + } + + // Run test with GOGC=10, to make bug more likely. + // Without the KeepAlives, the loop usually dies after + // about 30 iterations. + defer debug.SetGCPercent(debug.SetGCPercent(10)) + for n := 0; n < 200; n++ { + // Clear the underlying BoringCrypto object. + atomic.StorePointer(&k.boring, nil) + + // Race to create the underlying BoringCrypto object. + // The ones that lose the race are prime candidates for + // being GC'ed too early if the finalizers are not being + // used correctly. + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + defer wg.Done() + sum := make([]byte, 32) + _, err := SignPKCS1v15(rand.Reader, k, crypto.SHA256, sum) + if err != nil { + panic(err) // usually caused by memory corruption, so hard stop + } + }() + } + wg.Wait() + } +} diff --git a/src/crypto/rsa/pkcs1v15.go b/src/crypto/rsa/pkcs1v15.go index 37790acb98..6bd16f529d 100644 --- a/src/crypto/rsa/pkcs1v15.go +++ b/src/crypto/rsa/pkcs1v15.go @@ -14,6 +14,8 @@ import ( "crypto/internal/randutil" ) +import "crypto/internal/boring" + // This file implements encryption and decryption using PKCS#1 v1.5 padding. // PKCS1v15DecrypterOpts is for passing options to PKCS#1 v1.5 decryption using @@ -36,8 +38,8 @@ type PKCS1v15DecryptOptions struct { // // WARNING: use of this function to encrypt plaintexts other than // session keys is dangerous. Use RSA OAEP in new protocols. -func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) ([]byte, error) { - randutil.MaybeReadByte(rand) +func EncryptPKCS1v15(random io.Reader, pub *PublicKey, msg []byte) ([]byte, error) { + randutil.MaybeReadByte(random) if err := checkPub(pub); err != nil { return nil, err @@ -47,20 +49,37 @@ func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) ([]byte, error) return nil, ErrMessageTooLong } + if boring.Enabled && random == boring.RandReader { + bkey, err := boringPublicKey(pub) + if err != nil { + return nil, err + } + return boring.EncryptRSAPKCS1(bkey, msg) + } + boring.UnreachableExceptTests() + // EM = 0x00 || 0x02 || PS || 0x00 || M em := make([]byte, k) em[1] = 2 ps, mm := em[2:len(em)-len(msg)-1], em[len(em)-len(msg):] - err := nonZeroRandomBytes(ps, rand) + err := nonZeroRandomBytes(ps, random) if err != nil { return nil, err } em[len(em)-len(msg)-1] = 0 copy(mm, msg) + if boring.Enabled { + var bkey *boring.PublicKeyRSA + bkey, err = boringPublicKey(pub) + if err != nil { + return nil, err + } + return boring.EncryptRSANoPadding(bkey, em) + } + m := new(big.Int).SetBytes(em) c := encrypt(new(big.Int), pub, m) - copyWithLeftPad(em, c.Bytes()) return em, nil } @@ -77,6 +96,19 @@ func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) ([]byt if err := checkPub(&priv.PublicKey); err != nil { return nil, err } + + if boring.Enabled { + bkey, err := boringPrivateKey(priv) + if err != nil { + return nil, err + } + out, err := boring.DecryptRSAPKCS1(bkey, ciphertext) + if err != nil { + return nil, ErrDecryption + } + return out, nil + } + valid, out, index, err := decryptPKCS1v15(rand, priv, ciphertext) if err != nil { return nil, err @@ -144,13 +176,26 @@ func decryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (valid return } - c := new(big.Int).SetBytes(ciphertext) - m, err := decrypt(rand, priv, c) - if err != nil { - return + if boring.Enabled { + var bkey *boring.PrivateKeyRSA + bkey, err = boringPrivateKey(priv) + if err != nil { + return + } + em, err = boring.DecryptRSANoPadding(bkey, ciphertext) + if err != nil { + return + } + } else { + c := new(big.Int).SetBytes(ciphertext) + var m *big.Int + m, err = decrypt(rand, priv, c) + if err != nil { + return + } + em = leftPad(m.Bytes(), k) } - em = leftPad(m.Bytes(), k) firstByteIsZero := subtle.ConstantTimeByteEq(em[0], 0) secondByteIsTwo := subtle.ConstantTimeByteEq(em[1], 2) @@ -229,7 +274,7 @@ var hashPrefixes = map[crypto.Hash][]byte{ // messages is small, an attacker may be able to build a map from // messages to signatures and identify the signed messages. As ever, // signatures provide authenticity, not confidentiality. -func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) { +func SignPKCS1v15(random io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) { hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed)) if err != nil { return nil, err @@ -241,6 +286,14 @@ func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []b return nil, ErrMessageTooLong } + if boring.Enabled { + bkey, err := boringPrivateKey(priv) + if err != nil { + return nil, err + } + return boring.SignRSAPKCS1v15(bkey, hash, hashed) + } + // EM = 0x00 || 0x01 || PS || 0x00 || T em := make([]byte, k) em[1] = 1 @@ -251,7 +304,7 @@ func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []b copy(em[k-hashLen:k], hashed) m := new(big.Int).SetBytes(em) - c, err := decryptAndCheck(rand, priv, m) + c, err := decryptAndCheck(random, priv, m) if err != nil { return nil, err } @@ -266,6 +319,17 @@ func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []b // returning a nil error. If hash is zero then hashed is used directly. This // isn't advisable except for interoperability. func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) error { + if boring.Enabled { + bkey, err := boringPublicKey(pub) + if err != nil { + return err + } + if err := boring.VerifyRSAPKCS1v15(bkey, hash, hashed, sig); err != nil { + return ErrVerification + } + return nil + } + hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed)) if err != nil { return err diff --git a/src/crypto/rsa/pkcs1v15_test.go b/src/crypto/rsa/pkcs1v15_test.go index 7e62560a04..d412474034 100644 --- a/src/crypto/rsa/pkcs1v15_test.go +++ b/src/crypto/rsa/pkcs1v15_test.go @@ -64,7 +64,7 @@ func TestDecryptPKCS1v15(t *testing.T) { for i, test := range decryptPKCS1v15Tests { out, err := decryptFunc(decodeBase64(test.in)) if err != nil { - t.Errorf("#%d error decrypting", i) + t.Errorf("#%d error decrypting: %v", i, err) } want := []byte(test.out) if !bytes.Equal(out, want) { diff --git a/src/crypto/rsa/pss.go b/src/crypto/rsa/pss.go index 3ff0c2f4d0..e32cb7e0a7 100644 --- a/src/crypto/rsa/pss.go +++ b/src/crypto/rsa/pss.go @@ -17,6 +17,8 @@ import ( "math/big" ) +import "crypto/internal/boring" + func emsaPSSEncode(mHash []byte, emBits int, salt []byte, hash hash.Hash) ([]byte, error) { // See [1], section 9.1.1 hLen := hash.Size() @@ -197,6 +199,21 @@ func signPSSWithSalt(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed, if err != nil { return } + + if boring.Enabled { + bkey, err := boringPrivateKey(priv) + if err != nil { + return nil, err + } + // Note: BoringCrypto takes care of the "AndCheck" part of "decryptAndCheck". + // (It's not just decrypt.) + s, err := boring.DecryptRSANoPadding(bkey, em) + if err != nil { + return nil, err + } + return s, nil + } + m := new(big.Int).SetBytes(em) c, err := decryptAndCheck(rand, priv, m) if err != nil { @@ -259,6 +276,14 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte, hash = opts.Hash } + if boring.Enabled && rand == boring.RandReader { + bkey, err := boringPrivateKey(priv) + if err != nil { + return nil, err + } + return boring.SignRSAPSS(bkey, hash, hashed, saltLength) + } + salt := make([]byte, saltLength) if _, err := io.ReadFull(rand, salt); err != nil { return nil, err @@ -277,6 +302,16 @@ func VerifyPSS(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte, opts // verifyPSS verifies a PSS signature with the given salt length. func verifyPSS(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte, saltLen int) error { + if boring.Enabled { + bkey, err := boringPublicKey(pub) + if err != nil { + return err + } + if err := boring.VerifyRSAPSS(bkey, hash, hashed, sig, saltLen); err != nil { + return ErrVerification + } + return nil + } nBits := pub.N.BitLen() if len(sig) != (nBits+7)/8 { return ErrVerification diff --git a/src/crypto/rsa/pss_test.go b/src/crypto/rsa/pss_test.go index dfa8d8bb5a..32f6f0c3aa 100644 --- a/src/crypto/rsa/pss_test.go +++ b/src/crypto/rsa/pss_test.go @@ -9,7 +9,6 @@ import ( "bytes" "compress/bzip2" "crypto" - _ "crypto/md5" "crypto/rand" "crypto/sha1" _ "crypto/sha256" @@ -211,7 +210,7 @@ func TestPSSSigning(t *testing.T) { {8, 8, true}, } - hash := crypto.MD5 + hash := crypto.SHA1 h := hash.New() h.Write([]byte("testing")) hashed := h.Sum(nil) diff --git a/src/crypto/rsa/rsa.go b/src/crypto/rsa/rsa.go index d058949242..65fbcd664a 100644 --- a/src/crypto/rsa/rsa.go +++ b/src/crypto/rsa/rsa.go @@ -35,6 +35,11 @@ import ( "crypto/internal/randutil" ) +import ( + "crypto/internal/boring" + "unsafe" +) + var bigZero = big.NewInt(0) var bigOne = big.NewInt(1) @@ -42,6 +47,8 @@ var bigOne = big.NewInt(1) type PublicKey struct { N *big.Int // modulus E int // public exponent + + boring unsafe.Pointer } // Size returns the modulus size in bytes. Raw signatures and ciphertexts @@ -93,6 +100,8 @@ type PrivateKey struct { // Precomputed contains precomputed values that speed up private // operations, if available. Precomputed PrecomputedValues + + boring unsafe.Pointer } // Public returns the public key corresponding to priv. @@ -222,6 +231,32 @@ func GenerateKey(random io.Reader, bits int) (*PrivateKey, error) { func GenerateMultiPrimeKey(random io.Reader, nprimes int, bits int) (*PrivateKey, error) { randutil.MaybeReadByte(random) + if boring.Enabled && random == boring.RandReader && nprimes == 2 && (bits == 2048 || bits == 3072) { + N, E, D, P, Q, Dp, Dq, Qinv, err := boring.GenerateKeyRSA(bits) + if err != nil { + return nil, err + } + e64 := E.Int64() + if !E.IsInt64() || int64(int(e64)) != e64 { + return nil, errors.New("crypto/rsa: generated key exponent too large") + } + key := &PrivateKey{ + PublicKey: PublicKey{ + N: N, + E: int(e64), + }, + D: D, + Primes: []*big.Int{P, Q}, + Precomputed: PrecomputedValues{ + Dp: Dp, + Dq: Dq, + Qinv: Qinv, + CRTValues: make([]CRTValue, 0), // non-nil, to match Precompute + }, + } + return key, nil + } + priv := new(PrivateKey) priv.E = 65537 @@ -351,6 +386,7 @@ func mgf1XOR(out []byte, hash hash.Hash, seed []byte) { var ErrMessageTooLong = errors.New("crypto/rsa: message too long for RSA public key size") func encrypt(c *big.Int, pub *PublicKey, m *big.Int) *big.Int { + boring.Unreachable() e := big.NewInt(int64(pub.E)) c.Exp(m, e, pub.N) return c @@ -383,6 +419,15 @@ func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, l return nil, ErrMessageTooLong } + if boring.Enabled && random == boring.RandReader { + bkey, err := boringPublicKey(pub) + if err != nil { + return nil, err + } + return boring.EncryptRSAOAEP(hash, bkey, msg, label) + } + boring.UnreachableExceptTests() + hash.Write(label) lHash := hash.Sum(nil) hash.Reset() @@ -403,10 +448,24 @@ func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, l mgf1XOR(db, hash, seed) mgf1XOR(seed, hash, db) - m := new(big.Int) - m.SetBytes(em) - c := encrypt(new(big.Int), pub, m) - out := c.Bytes() + var out []byte + if boring.Enabled { + var bkey *boring.PublicKeyRSA + bkey, err = boringPublicKey(pub) + if err != nil { + return nil, err + } + c, err := boring.EncryptRSANoPadding(bkey, em) + if err != nil { + return nil, err + } + out = c + } else { + m := new(big.Int) + m.SetBytes(em) + c := encrypt(new(big.Int), pub, m) + out = c.Bytes() + } if len(out) < k { // If the output is too small, we need to left-pad with zeros. @@ -460,6 +519,9 @@ func (priv *PrivateKey) Precompute() { // decrypt performs an RSA decryption, resulting in a plaintext integer. If a // random source is given, RSA blinding is used. func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err error) { + if len(priv.Primes) <= 2 { + boring.Unreachable() + } // TODO(agl): can we get away with reusing blinds? if c.Cmp(priv.N) > 0 { err = ErrDecryption @@ -576,6 +638,17 @@ func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext return nil, ErrDecryption } + if boring.Enabled { + bkey, err := boringPrivateKey(priv) + if err != nil { + return nil, err + } + out, err := boring.DecryptRSAOAEP(hash, bkey, ciphertext, label) + if err != nil { + return nil, ErrDecryption + } + return out, nil + } c := new(big.Int).SetBytes(ciphertext) m, err := decrypt(random, priv, c) diff --git a/src/crypto/rsa/rsa_test.go b/src/crypto/rsa/rsa_test.go index 84b167455f..766d9a954f 100644 --- a/src/crypto/rsa/rsa_test.go +++ b/src/crypto/rsa/rsa_test.go @@ -10,23 +10,27 @@ import ( "crypto/rand" "crypto/sha1" "crypto/sha256" + "fmt" "math/big" "testing" ) +import "crypto/internal/boring" + func TestKeyGeneration(t *testing.T) { - size := 1024 - if testing.Short() { - size = 128 - } - priv, err := GenerateKey(rand.Reader, size) - if err != nil { - t.Errorf("failed to generate key") - } - if bits := priv.N.BitLen(); bits != size { - t.Errorf("key too short (%d vs %d)", bits, size) + for _, size := range []int{128, 1024, 2048, 3072} { + priv, err := GenerateKey(rand.Reader, size) + if err != nil { + t.Errorf("GenerateKey(%d): %v", size, err) + } + if bits := priv.N.BitLen(); bits != size { + t.Errorf("key too short (%d vs %d)", bits, size) + } + testKeyBasics(t, priv) + if testing.Short() { + break + } } - testKeyBasics(t, priv) } func Test3PrimeKeyGeneration(t *testing.T) { @@ -110,6 +114,25 @@ func testKeyBasics(t *testing.T, priv *PrivateKey) { t.Errorf("private exponent too large") } + if boring.Enabled { + // Cannot call encrypt/decrypt directly. Test via PKCS1v15. + msg := []byte("hi!") + enc, err := EncryptPKCS1v15(rand.Reader, &priv.PublicKey, msg) + if err != nil { + t.Errorf("EncryptPKCS1v15: %v", err) + return + } + dec, err := DecryptPKCS1v15(rand.Reader, priv, enc) + if err != nil { + t.Errorf("DecryptPKCS1v15: %v", err) + return + } + if !bytes.Equal(dec, msg) { + t.Errorf("got:%x want:%x (%+v)", dec, msg, priv) + } + return + } + pub := &priv.PublicKey m := big.NewInt(42) c := encrypt(new(big.Int), pub, m) @@ -158,6 +181,10 @@ func init() { } func BenchmarkRSA2048Decrypt(b *testing.B) { + if boring.Enabled { + b.Skip("no raw decrypt in BoringCrypto") + } + b.StopTimer() c := fromBase10("8472002792838218989464636159316973636630013835787202418124758118372358261975764365740026024610403138425986214991379012696600761514742817632790916315594342398720903716529235119816755589383377471752116975374952783629225022962092351886861518911824745188989071172097120352727368980275252089141512321893536744324822590480751098257559766328893767334861211872318961900897793874075248286439689249972315699410830094164386544311554704755110361048571142336148077772023880664786019636334369759624917224888206329520528064315309519262325023881707530002540634660750469137117568199824615333883758410040459705787022909848740188613313") @@ -180,6 +207,10 @@ func BenchmarkRSA2048Sign(b *testing.B) { } func Benchmark3PrimeRSA2048Decrypt(b *testing.B) { + if boring.Enabled { + b.Skip("no raw decrypt in BoringCrypto") + } + b.StopTimer() priv := &PrivateKey{ PublicKey: PublicKey{ @@ -222,7 +253,7 @@ func TestEncryptOAEP(t *testing.T) { n := new(big.Int) for i, test := range testEncryptOAEPData { n.SetString(test.modulus, 16) - public := PublicKey{n, test.e} + public := PublicKey{N: n, E: test.e} for j, message := range test.msgs { randomSource := bytes.NewReader(message.seed) @@ -247,7 +278,7 @@ func TestDecryptOAEP(t *testing.T) { n.SetString(test.modulus, 16) d.SetString(test.d, 16) private := new(PrivateKey) - private.PublicKey = PublicKey{n, test.e} + private.PublicKey = PublicKey{N: n, E: test.e} private.D = d for j, message := range test.msgs { @@ -272,6 +303,36 @@ func TestDecryptOAEP(t *testing.T) { } } +func TestEncryptDecryptOAEP(t *testing.T) { + sha256 := sha256.New() + n := new(big.Int) + d := new(big.Int) + for i, test := range testEncryptOAEPData { + n.SetString(test.modulus, 16) + d.SetString(test.d, 16) + priv := new(PrivateKey) + priv.PublicKey = PublicKey{N: n, E: test.e} + priv.D = d + + for j, message := range test.msgs { + label := []byte(fmt.Sprintf("hi#%d", j)) + enc, err := EncryptOAEP(sha256, rand.Reader, &priv.PublicKey, message.in, label) + if err != nil { + t.Errorf("#%d,%d: EncryptOAEP: %v", i, j, err) + continue + } + dec, err := DecryptOAEP(sha256, rand.Reader, priv, enc, label) + if err != nil { + t.Errorf("#%d,%d: DecryptOAEP: %v", i, j, err) + continue + } + if !bytes.Equal(dec, message.in) { + t.Errorf("#%d,%d: round trip %q -> %q", i, j, message.in, dec) + } + } + } +} + // testEncryptOAEPData contains a subset of the vectors from RSA's "Test vectors for RSA-OAEP". var testEncryptOAEPData = []testEncryptOAEPStruct{ // Key 1 diff --git a/src/crypto/sha1/boring.go b/src/crypto/sha1/boring.go new file mode 100644 index 0000000000..44c26092ee --- /dev/null +++ b/src/crypto/sha1/boring.go @@ -0,0 +1,22 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Extra indirection here so that when building go_bootstrap +// cmd/internal/boring is not even imported, so that we don't +// have to maintain changes to cmd/dist's deps graph. + +// +build !cmd_go_bootstrap + +package sha1 + +import ( + "crypto/internal/boring" + "hash" +) + +const boringEnabled = boring.Enabled + +func boringNewSHA1() hash.Hash { return boring.NewSHA1() } + +func boringUnreachable() { boring.Unreachable() } diff --git a/src/crypto/sha1/notboring.go b/src/crypto/sha1/notboring.go new file mode 100644 index 0000000000..9726fcd268 --- /dev/null +++ b/src/crypto/sha1/notboring.go @@ -0,0 +1,17 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build cmd_go_bootstrap + +package sha1 + +import ( + "hash" +) + +const boringEnabled = false + +func boringNewSHA1() hash.Hash { panic("boringcrypto: not available") } + +func boringUnreachable() {} diff --git a/src/crypto/sha1/sha1.go b/src/crypto/sha1/sha1.go index 286a59d33d..329435f282 100644 --- a/src/crypto/sha1/sha1.go +++ b/src/crypto/sha1/sha1.go @@ -119,6 +119,9 @@ func (d *digest) Reset() { // implements encoding.BinaryMarshaler and encoding.BinaryUnmarshaler to // marshal and unmarshal the internal state of the hash. func New() hash.Hash { + if boringEnabled { + return boringNewSHA1() + } d := new(digest) d.Reset() return d @@ -129,6 +132,7 @@ func (d *digest) Size() int { return Size } func (d *digest) BlockSize() int { return BlockSize } func (d *digest) Write(p []byte) (nn int, err error) { + boringUnreachable() nn = len(p) d.len += uint64(nn) if d.nx > 0 { @@ -152,6 +156,7 @@ func (d *digest) Write(p []byte) (nn int, err error) { } func (d *digest) Sum(in []byte) []byte { + boringUnreachable() // Make a copy of d so that caller can keep writing and summing. d0 := *d hash := d0.checkSum() @@ -259,6 +264,13 @@ func (d *digest) constSum() [Size]byte { // Sum returns the SHA-1 checksum of the data. func Sum(data []byte) [Size]byte { + if boringEnabled { + h := New() + h.Write(data) + var ret [Size]byte + h.Sum(ret[:0]) + return ret + } var d digest d.Reset() d.Write(data) diff --git a/src/crypto/sha1/sha1_test.go b/src/crypto/sha1/sha1_test.go index 681e928de2..8e7a3d339c 100644 --- a/src/crypto/sha1/sha1_test.go +++ b/src/crypto/sha1/sha1_test.go @@ -16,6 +16,8 @@ import ( "testing" ) +import "crypto/internal/boring" + type sha1Test struct { out string in string @@ -77,6 +79,9 @@ func TestGolden(t *testing.T) { io.WriteString(c, g.in[len(g.in)/2:]) sum = c.Sum(nil) case 3: + if boring.Enabled { + continue + } io.WriteString(c, g.in[0:len(g.in)/2]) c.(*digest).ConstantTimeSum(nil) io.WriteString(c, g.in[len(g.in)/2:]) @@ -141,6 +146,9 @@ func TestBlockSize(t *testing.T) { // Tests that blockGeneric (pure Go) and block (in assembly for some architectures) match. func TestBlockGeneric(t *testing.T) { + if boring.Enabled { + t.Skip("BoringCrypto doesn't expose digest") + } for i := 1; i < 30; i++ { // arbitrary factor gen, asm := New().(*digest), New().(*digest) buf := make([]byte, BlockSize*i) diff --git a/src/crypto/sha256/sha256.go b/src/crypto/sha256/sha256.go index e1cccf65a6..8b54a427d7 100644 --- a/src/crypto/sha256/sha256.go +++ b/src/crypto/sha256/sha256.go @@ -13,6 +13,8 @@ import ( "hash" ) +import "crypto/internal/boring" + func init() { crypto.RegisterHash(crypto.SHA224, New224) crypto.RegisterHash(crypto.SHA256, New) @@ -159,6 +161,9 @@ func (d *digest) Reset() { // encoding.BinaryUnmarshaler to marshal and unmarshal the internal // state of the hash. func New() hash.Hash { + if boring.Enabled { + return boring.NewSHA256() + } d := new(digest) d.Reset() return d @@ -166,6 +171,9 @@ func New() hash.Hash { // New224 returns a new hash.Hash computing the SHA224 checksum. func New224() hash.Hash { + if boring.Enabled { + return boring.NewSHA224() + } d := new(digest) d.is224 = true d.Reset() @@ -182,6 +190,7 @@ func (d *digest) Size() int { func (d *digest) BlockSize() int { return BlockSize } func (d *digest) Write(p []byte) (nn int, err error) { + boring.Unreachable() nn = len(p) d.len += uint64(nn) if d.nx > 0 { @@ -205,6 +214,7 @@ func (d *digest) Write(p []byte) (nn int, err error) { } func (d *digest) Sum(in []byte) []byte { + boring.Unreachable() // Make a copy of d so that caller can keep writing and summing. d0 := *d hash := d0.checkSum() @@ -252,6 +262,13 @@ func (d *digest) checkSum() [Size]byte { // Sum256 returns the SHA256 checksum of the data. func Sum256(data []byte) [Size]byte { + if boring.Enabled { + h := New() + h.Write(data) + var ret [Size]byte + h.Sum(ret[:0]) + return ret + } var d digest d.Reset() d.Write(data) @@ -260,6 +277,13 @@ func Sum256(data []byte) [Size]byte { // Sum224 returns the SHA224 checksum of the data. func Sum224(data []byte) (sum224 [Size224]byte) { + if boring.Enabled { + h := New224() + h.Write(data) + var ret [Size224]byte + h.Sum(ret[:0]) + return ret + } var d digest d.is224 = true d.Reset() diff --git a/src/crypto/sha256/sha256_test.go b/src/crypto/sha256/sha256_test.go index 433c5a4c5e..13178fb21f 100644 --- a/src/crypto/sha256/sha256_test.go +++ b/src/crypto/sha256/sha256_test.go @@ -16,6 +16,8 @@ import ( "testing" ) +import "crypto/internal/boring" + type sha256Test struct { out string in string @@ -216,6 +218,9 @@ func TestBlockSize(t *testing.T) { // Tests that blockGeneric (pure Go) and block (in assembly for some architectures) match. func TestBlockGeneric(t *testing.T) { + if boring.Enabled { + t.Skip("BoringCrypto doesn't expose digest") + } gen, asm := New().(*digest), New().(*digest) buf := make([]byte, BlockSize*20) // arbitrary factor rand.Read(buf) diff --git a/src/crypto/sha512/sha512.go b/src/crypto/sha512/sha512.go index 9c143a2a28..1a2cef317c 100644 --- a/src/crypto/sha512/sha512.go +++ b/src/crypto/sha512/sha512.go @@ -17,6 +17,8 @@ import ( "hash" ) +import "crypto/internal/boring" + func init() { crypto.RegisterHash(crypto.SHA384, New384) crypto.RegisterHash(crypto.SHA512, New) @@ -211,6 +213,9 @@ func consumeUint64(b []byte) ([]byte, uint64) { // New returns a new hash.Hash computing the SHA-512 checksum. func New() hash.Hash { + if boring.Enabled { + return boring.NewSHA512() + } d := &digest{function: crypto.SHA512} d.Reset() return d @@ -232,6 +237,9 @@ func New512_256() hash.Hash { // New384 returns a new hash.Hash computing the SHA-384 checksum. func New384() hash.Hash { + if boring.Enabled { + return boring.NewSHA384() + } d := &digest{function: crypto.SHA384} d.Reset() return d @@ -253,6 +261,9 @@ func (d *digest) Size() int { func (d *digest) BlockSize() int { return BlockSize } func (d *digest) Write(p []byte) (nn int, err error) { + if d.function != crypto.SHA512_224 && d.function != crypto.SHA512_256 { + boring.Unreachable() + } nn = len(p) d.len += uint64(nn) if d.nx > 0 { @@ -276,6 +287,9 @@ func (d *digest) Write(p []byte) (nn int, err error) { } func (d *digest) Sum(in []byte) []byte { + if d.function != crypto.SHA512_224 && d.function != crypto.SHA512_256 { + boring.Unreachable() + } // Make a copy of d so that caller can keep writing and summing. d0 := new(digest) *d0 = *d @@ -330,6 +344,13 @@ func (d *digest) checkSum() [Size]byte { // Sum512 returns the SHA512 checksum of the data. func Sum512(data []byte) [Size]byte { + if boring.Enabled { + h := New() + h.Write(data) + var ret [Size]byte + h.Sum(ret[:0]) + return ret + } d := digest{function: crypto.SHA512} d.Reset() d.Write(data) @@ -338,6 +359,13 @@ func Sum512(data []byte) [Size]byte { // Sum384 returns the SHA384 checksum of the data. func Sum384(data []byte) (sum384 [Size384]byte) { + if boring.Enabled { + h := New384() + h.Write(data) + var ret [Size384]byte + h.Sum(ret[:0]) + return ret + } d := digest{function: crypto.SHA384} d.Reset() d.Write(data) diff --git a/src/crypto/sha512/sha512_test.go b/src/crypto/sha512/sha512_test.go index 59998b1d38..6964bef8f7 100644 --- a/src/crypto/sha512/sha512_test.go +++ b/src/crypto/sha512/sha512_test.go @@ -17,6 +17,8 @@ import ( "testing" ) +import "crypto/internal/boring" + type sha512Test struct { out string in string @@ -822,6 +824,9 @@ func TestBlockSize(t *testing.T) { // Tests that blockGeneric (pure Go) and block (in assembly for some architectures) match. func TestBlockGeneric(t *testing.T) { + if boring.Enabled { + t.Skip("BoringCrypto doesn't expose digest") + } gen, asm := New().(*digest), New().(*digest) buf := make([]byte, BlockSize*20) // arbitrary factor rand.Read(buf) diff --git a/src/crypto/tls/boring.go b/src/crypto/tls/boring.go new file mode 100644 index 0000000000..5b5c20ce1f --- /dev/null +++ b/src/crypto/tls/boring.go @@ -0,0 +1,137 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tls + +import ( + "crypto/ecdsa" + "crypto/internal/boring/fipstls" + "crypto/rsa" + "crypto/x509" +) + +// needFIPS returns fipstls.Required(); it avoids a new import in common.go. +func needFIPS() bool { + return fipstls.Required() +} + +// fipsMinVersion replaces c.minVersion in FIPS-only mode. +func fipsMinVersion(c *Config) uint16 { + // FIPS requires TLS 1.2. + return VersionTLS12 +} + +// fipsMaxVersion replaces c.maxVersion in FIPS-only mode. +func fipsMaxVersion(c *Config) uint16 { + // FIPS requires TLS 1.2. + return VersionTLS12 +} + +// default defaultFIPSCurvePreferences is the FIPS-allowed curves, +// in preference order (most preferable first). +var defaultFIPSCurvePreferences = []CurveID{CurveP256, CurveP384, CurveP521} + +// fipsCurvePreferences replaces c.curvePreferences in FIPS-only mode. +func fipsCurvePreferences(c *Config) []CurveID { + if c == nil || len(c.CurvePreferences) == 0 { + return defaultFIPSCurvePreferences + } + var list []CurveID + for _, id := range c.CurvePreferences { + for _, allowed := range defaultFIPSCurvePreferences { + if id == allowed { + list = append(list, id) + break + } + } + } + return list +} + +// default FIPSCipherSuites is the FIPS-allowed cipher suites, +// in preference order (most preferable first). +var defaultFIPSCipherSuites = []uint16{ + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384, +} + +// fipsCipherSuites replaces c.cipherSuites in FIPS-only mode. +func fipsCipherSuites(c *Config) []uint16 { + if c == nil || c.CipherSuites == nil { + return defaultFIPSCipherSuites + } + var list []uint16 + for _, id := range c.CipherSuites { + for _, allowed := range defaultFIPSCipherSuites { + if id == allowed { + list = append(list, id) + break + } + } + } + return list +} + +// isBoringCertificate reports whether a certificate may be used +// when constructing a verified chain. +// It is called for each leaf, intermediate, and root certificate. +func isBoringCertificate(c *x509.Certificate) bool { + if !needFIPS() { + // Everything is OK if we haven't forced FIPS-only mode. + return true + } + + // Otherwise the key must be RSA 2048, RSA 3072, or ECDSA P-256. + switch k := c.PublicKey.(type) { + default: + return false + case *rsa.PublicKey: + if size := k.N.BitLen(); size != 2048 && size != 3072 { + return false + } + case *ecdsa.PublicKey: + if name := k.Curve.Params().Name; name != "P-256" && name != "P-384" { + return false + } + } + + return true +} + +// fipsSupportedSignatureAlgorithms currently are a subset of +// defaultSupportedSignatureAlgorithms without Ed25519 and SHA-1. +var fipsSupportedSignatureAlgorithms = []SignatureScheme{ + PSSWithSHA256, + PSSWithSHA384, + PSSWithSHA512, + PKCS1WithSHA256, + ECDSAWithP256AndSHA256, + PKCS1WithSHA384, + ECDSAWithP384AndSHA384, + PKCS1WithSHA512, + ECDSAWithP521AndSHA512, +} + +// supportedSignatureAlgorithms returns the supported signature algorithms. +func supportedSignatureAlgorithms() []SignatureScheme { + if !needFIPS() { + return defaultSupportedSignatureAlgorithms + } + return fipsSupportedSignatureAlgorithms +} + +// supportedSignatureAlgorithmsTLS12 returns the supported signature algorithms +// for TLS 1.2. Issue 32425. +func supportedSignatureAlgorithmsTLS12() []SignatureScheme { + if !needFIPS() { + return defaultSupportedSignatureAlgorithmsTLS12 + } + return fipsSupportedSignatureAlgorithms[3:] +} + +var testingOnlyForceClientHelloSignatureAlgorithms []SignatureScheme diff --git a/src/crypto/tls/boring_test.go b/src/crypto/tls/boring_test.go new file mode 100644 index 0000000000..74c2636a87 --- /dev/null +++ b/src/crypto/tls/boring_test.go @@ -0,0 +1,634 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package tls + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/internal/boring/fipstls" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "math/big" + "net" + "runtime" + "strings" + "testing" + "time" +) + +func TestBoringServerProtocolVersion(t *testing.T) { + test := func(name string, v uint16, msg string) { + t.Run(name, func(t *testing.T) { + serverConfig := testConfig.Clone() + serverConfig.MinVersion = VersionSSL30 + clientHello := &clientHelloMsg{ + vers: v, + random: make([]byte, 32), + cipherSuites: allCipherSuites(), + compressionMethods: []uint8{compressionNone}, + supportedVersions: []uint16{v}, + } + testClientHelloFailure(t, serverConfig, clientHello, msg) + }) + } + + test("VersionSSL30", VersionSSL30, "") + test("VersionTLS10", VersionTLS10, "") + test("VersionTLS11", VersionTLS11, "") + test("VersionTLS12", VersionTLS12, "") + test("VersionTLS13", VersionTLS13, "") + + fipstls.Force() + defer fipstls.Abandon() + test("VersionSSL30", VersionSSL30, "client offered only unsupported versions") + test("VersionTLS10", VersionTLS10, "client offered only unsupported versions") + test("VersionTLS11", VersionTLS11, "client offered only unsupported versions") + test("VersionTLS12", VersionTLS12, "") + test("VersionTLS13", VersionTLS13, "client offered only unsupported versions") +} + +func isBoringVersion(v uint16) bool { + return v == VersionTLS12 +} + +func isBoringCipherSuite(id uint16) bool { + switch id { + case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384: + return true + } + return false +} + +func isBoringCurve(id CurveID) bool { + switch id { + case CurveP256, CurveP384, CurveP521: + return true + } + return false +} + +func isECDSA(id uint16) bool { + for _, suite := range cipherSuites { + if suite.id == id { + return suite.flags&suiteECSign == suiteECSign + } + } + panic(fmt.Sprintf("unknown cipher suite %#x", id)) +} + +func isBoringSignatureScheme(alg SignatureScheme) bool { + switch alg { + default: + return false + case PKCS1WithSHA256, + ECDSAWithP256AndSHA256, + PKCS1WithSHA384, + ECDSAWithP384AndSHA384, + PKCS1WithSHA512, + ECDSAWithP521AndSHA512, + PSSWithSHA256, + PSSWithSHA384, + PSSWithSHA512: + // ok + } + return true +} + +func TestBoringServerCipherSuites(t *testing.T) { + serverConfig := testConfig.Clone() + serverConfig.CipherSuites = allCipherSuites() + serverConfig.Certificates = make([]Certificate, 1) + + for _, id := range allCipherSuites() { + if isECDSA(id) { + serverConfig.Certificates[0].Certificate = [][]byte{testECDSACertificate} + serverConfig.Certificates[0].PrivateKey = testECDSAPrivateKey + } else { + serverConfig.Certificates[0].Certificate = [][]byte{testRSACertificate} + serverConfig.Certificates[0].PrivateKey = testRSAPrivateKey + } + serverConfig.BuildNameToCertificate() + t.Run(fmt.Sprintf("suite=%#x", id), func(t *testing.T) { + clientHello := &clientHelloMsg{ + vers: VersionTLS12, + random: make([]byte, 32), + cipherSuites: []uint16{id}, + compressionMethods: []uint8{compressionNone}, + supportedCurves: defaultCurvePreferences, + supportedPoints: []uint8{pointFormatUncompressed}, + } + + testClientHello(t, serverConfig, clientHello) + t.Run("fipstls", func(t *testing.T) { + fipstls.Force() + defer fipstls.Abandon() + msg := "" + if !isBoringCipherSuite(id) { + msg = "no cipher suite supported by both client and server" + } + testClientHelloFailure(t, serverConfig, clientHello, msg) + }) + }) + } +} + +func TestBoringServerCurves(t *testing.T) { + serverConfig := testConfig.Clone() + serverConfig.Certificates = make([]Certificate, 1) + serverConfig.Certificates[0].Certificate = [][]byte{testECDSACertificate} + serverConfig.Certificates[0].PrivateKey = testECDSAPrivateKey + serverConfig.BuildNameToCertificate() + + for _, curveid := range defaultCurvePreferences { + t.Run(fmt.Sprintf("curve=%d", curveid), func(t *testing.T) { + clientHello := &clientHelloMsg{ + vers: VersionTLS12, + random: make([]byte, 32), + cipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, + compressionMethods: []uint8{compressionNone}, + supportedCurves: []CurveID{curveid}, + supportedPoints: []uint8{pointFormatUncompressed}, + } + + testClientHello(t, serverConfig, clientHello) + + // With fipstls forced, bad curves should be rejected. + t.Run("fipstls", func(t *testing.T) { + fipstls.Force() + defer fipstls.Abandon() + msg := "" + if !isBoringCurve(curveid) { + msg = "no cipher suite supported by both client and server" + } + testClientHelloFailure(t, serverConfig, clientHello, msg) + }) + }) + } +} + +func boringHandshake(t *testing.T, clientConfig, serverConfig *Config) (clientErr, serverErr error) { + c, s := localPipe(t) + client := Client(c, clientConfig) + server := Server(s, serverConfig) + done := make(chan error, 1) + go func() { + done <- client.Handshake() + c.Close() + }() + serverErr = server.Handshake() + s.Close() + clientErr = <-done + return +} + +func TestBoringServerSignatureAndHash(t *testing.T) { + defer func() { + testingOnlyForceClientHelloSignatureAlgorithms = nil + }() + + for _, sigHash := range defaultSupportedSignatureAlgorithms { + t.Run(fmt.Sprintf("%#x", sigHash), func(t *testing.T) { + serverConfig := testConfig.Clone() + serverConfig.Certificates = make([]Certificate, 1) + + testingOnlyForceClientHelloSignatureAlgorithms = []SignatureScheme{sigHash} + + switch signatureFromSignatureScheme(sigHash) { + case signaturePKCS1v15, signatureRSAPSS: + serverConfig.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256} + serverConfig.Certificates[0].Certificate = [][]byte{testRSA2048Certificate} + serverConfig.Certificates[0].PrivateKey = testRSA2048PrivateKey + case signatureEd25519: + serverConfig.CipherSuites = []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256} + serverConfig.Certificates[0].Certificate = [][]byte{testEd25519Certificate} + serverConfig.Certificates[0].PrivateKey = testEd25519PrivateKey + case signatureECDSA: + serverConfig.CipherSuites = []uint16{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256} + serverConfig.Certificates[0].Certificate = [][]byte{testECDSACertificate} + serverConfig.Certificates[0].PrivateKey = testECDSAPrivateKey + } + serverConfig.BuildNameToCertificate() + // PKCS#1 v1.5 signature algorithms can't be used standalone in TLS + // 1.3, and the ECDSA ones bind to the curve used. + // RSA-PSS signatures are not supported in TLS 1.2. Issue 32425. + if signatureFromSignatureScheme(sigHash) != signatureRSAPSS { + serverConfig.MaxVersion = VersionTLS12 + } + + clientErr, serverErr := boringHandshake(t, testConfig, serverConfig) + if clientErr != nil { + t.Fatalf("expected handshake with %#x to succeed; client error: %v; server error: %v", sigHash, clientErr, serverErr) + } + + // With fipstls forced, bad curves should be rejected. + t.Run("fipstls", func(t *testing.T) { + fipstls.Force() + defer fipstls.Abandon() + clientErr, _ := boringHandshake(t, testConfig, serverConfig) + // RSA-PSS is only supported in TLS 1.3, prohibited by forcing fipstls. Issue 32425. + if isBoringSignatureScheme(sigHash) && signatureFromSignatureScheme(sigHash) != signatureRSAPSS { + if clientErr != nil { + t.Fatalf("expected handshake with %#x to succeed; err=%v", sigHash, clientErr) + } + } else { + if clientErr == nil { + t.Fatalf("expected handshake with %#x to fail, but it succeeded", sigHash) + } + } + }) + }) + } +} + +func TestBoringClientHello(t *testing.T) { + // Test that no matter what we put in the client config, + // the client does not offer non-FIPS configurations. + fipstls.Force() + defer fipstls.Abandon() + + c, s := net.Pipe() + defer c.Close() + defer s.Close() + + clientConfig := testConfig.Clone() + // All sorts of traps for the client to avoid. + clientConfig.MinVersion = VersionSSL30 + clientConfig.MaxVersion = VersionTLS13 + clientConfig.CipherSuites = allCipherSuites() + clientConfig.CurvePreferences = defaultCurvePreferences + + go Client(c, testConfig).Handshake() + srv := Server(s, testConfig) + msg, err := srv.readHandshake() + if err != nil { + t.Fatal(err) + } + hello, ok := msg.(*clientHelloMsg) + if !ok { + t.Fatalf("unexpected message type %T", msg) + } + + if !isBoringVersion(hello.vers) { + t.Errorf("client vers=%#x, want %#x (TLS 1.2)", hello.vers, VersionTLS12) + } + for _, v := range hello.supportedVersions { + if !isBoringVersion(v) { + t.Errorf("client offered disallowed version %#x", v) + } + } + for _, id := range hello.cipherSuites { + if !isBoringCipherSuite(id) { + t.Errorf("client offered disallowed suite %#x", id) + } + } + for _, id := range hello.supportedCurves { + if !isBoringCurve(id) { + t.Errorf("client offered disallowed curve %d", id) + } + } + for _, sigHash := range hello.supportedSignatureAlgorithms { + if !isBoringSignatureScheme(sigHash) { + t.Errorf("client offered disallowed signature-and-hash %v", sigHash) + } + } +} + +func TestBoringCertAlgs(t *testing.T) { + // NaCl, arm and wasm time out generating keys. Nothing in this test is architecture-specific, so just don't bother on those. + if runtime.GOOS == "nacl" || runtime.GOARCH == "arm" || runtime.GOOS == "js" { + t.Skipf("skipping on %s/%s because key generation takes too long", runtime.GOOS, runtime.GOARCH) + } + + // Set up some roots, intermediate CAs, and leaf certs with various algorithms. + // X_Y is X signed by Y. + R1 := boringCert(t, "R1", boringRSAKey(t, 2048), nil, boringCertCA|boringCertFIPSOK) + R2 := boringCert(t, "R2", boringRSAKey(t, 4096), nil, boringCertCA) + + M1_R1 := boringCert(t, "M1_R1", boringECDSAKey(t, elliptic.P256()), R1, boringCertCA|boringCertFIPSOK) + M2_R1 := boringCert(t, "M2_R1", boringECDSAKey(t, elliptic.P224()), R1, boringCertCA) + + I_R1 := boringCert(t, "I_R1", boringRSAKey(t, 3072), R1, boringCertCA|boringCertFIPSOK) + I_R2 := boringCert(t, "I_R2", I_R1.key, R2, boringCertCA|boringCertFIPSOK) + I_M1 := boringCert(t, "I_M1", I_R1.key, M1_R1, boringCertCA|boringCertFIPSOK) + I_M2 := boringCert(t, "I_M2", I_R1.key, M2_R1, boringCertCA|boringCertFIPSOK) + + L1_I := boringCert(t, "L1_I", boringECDSAKey(t, elliptic.P384()), I_R1, boringCertLeaf|boringCertFIPSOK) + L2_I := boringCert(t, "L2_I", boringRSAKey(t, 1024), I_R1, boringCertLeaf) + + // boringCert checked that isBoringCertificate matches the caller's boringCertFIPSOK bit. + // If not, no point in building bigger end-to-end tests. + if t.Failed() { + t.Fatalf("isBoringCertificate failures; not continuing") + } + + // client verifying server cert + testServerCert := func(t *testing.T, desc string, pool *x509.CertPool, key interface{}, list [][]byte, ok bool) { + clientConfig := testConfig.Clone() + clientConfig.RootCAs = pool + clientConfig.InsecureSkipVerify = false + clientConfig.ServerName = "example.com" + + serverConfig := testConfig.Clone() + serverConfig.Certificates = []Certificate{{Certificate: list, PrivateKey: key}} + serverConfig.BuildNameToCertificate() + + clientErr, _ := boringHandshake(t, clientConfig, serverConfig) + + if (clientErr == nil) == ok { + if ok { + t.Logf("%s: accept", desc) + } else { + t.Logf("%s: reject", desc) + } + } else { + if ok { + t.Errorf("%s: BAD reject (%v)", desc, clientErr) + } else { + t.Errorf("%s: BAD accept", desc) + } + } + } + + // server verifying client cert + testClientCert := func(t *testing.T, desc string, pool *x509.CertPool, key interface{}, list [][]byte, ok bool) { + clientConfig := testConfig.Clone() + clientConfig.ServerName = "example.com" + clientConfig.Certificates = []Certificate{{Certificate: list, PrivateKey: key}} + + serverConfig := testConfig.Clone() + serverConfig.ClientCAs = pool + serverConfig.ClientAuth = RequireAndVerifyClientCert + + _, serverErr := boringHandshake(t, clientConfig, serverConfig) + + if (serverErr == nil) == ok { + if ok { + t.Logf("%s: accept", desc) + } else { + t.Logf("%s: reject", desc) + } + } else { + if ok { + t.Errorf("%s: BAD reject (%v)", desc, serverErr) + } else { + t.Errorf("%s: BAD accept", desc) + } + } + } + + // Run simple basic test with known answers before proceeding to + // exhaustive test with computed answers. + r1pool := x509.NewCertPool() + r1pool.AddCert(R1.cert) + testServerCert(t, "basic", r1pool, L2_I.key, [][]byte{L2_I.der, I_R1.der}, true) + testClientCert(t, "basic (client cert)", r1pool, L2_I.key, [][]byte{L2_I.der, I_R1.der}, true) + fipstls.Force() + testServerCert(t, "basic (fips)", r1pool, L2_I.key, [][]byte{L2_I.der, I_R1.der}, false) + testClientCert(t, "basic (fips, client cert)", r1pool, L2_I.key, [][]byte{L2_I.der, I_R1.der}, false) + fipstls.Abandon() + + if t.Failed() { + t.Fatal("basic test failed, skipping exhaustive test") + } + + if testing.Short() { + t.Logf("basic test passed; skipping exhaustive test in -short mode") + return + } + + for l := 1; l <= 2; l++ { + leaf := L1_I + if l == 2 { + leaf = L2_I + } + for i := 0; i < 64; i++ { + reachable := map[string]bool{leaf.parentOrg: true} + reachableFIPS := map[string]bool{leaf.parentOrg: leaf.fipsOK} + list := [][]byte{leaf.der} + listName := leaf.name + addList := func(cond int, c *boringCertificate) { + if cond != 0 { + list = append(list, c.der) + listName += "," + c.name + if reachable[c.org] { + reachable[c.parentOrg] = true + } + if reachableFIPS[c.org] && c.fipsOK { + reachableFIPS[c.parentOrg] = true + } + } + } + addList(i&1, I_R1) + addList(i&2, I_R2) + addList(i&4, I_M1) + addList(i&8, I_M2) + addList(i&16, M1_R1) + addList(i&32, M2_R1) + + for r := 1; r <= 3; r++ { + pool := x509.NewCertPool() + rootName := "," + shouldVerify := false + shouldVerifyFIPS := false + addRoot := func(cond int, c *boringCertificate) { + if cond != 0 { + rootName += "," + c.name + pool.AddCert(c.cert) + if reachable[c.org] { + shouldVerify = true + } + if reachableFIPS[c.org] && c.fipsOK { + shouldVerifyFIPS = true + } + } + } + addRoot(r&1, R1) + addRoot(r&2, R2) + rootName = rootName[1:] // strip leading comma + testServerCert(t, listName+"->"+rootName[1:], pool, leaf.key, list, shouldVerify) + testClientCert(t, listName+"->"+rootName[1:]+"(client cert)", pool, leaf.key, list, shouldVerify) + fipstls.Force() + testServerCert(t, listName+"->"+rootName[1:]+" (fips)", pool, leaf.key, list, shouldVerifyFIPS) + testClientCert(t, listName+"->"+rootName[1:]+" (fips, client cert)", pool, leaf.key, list, shouldVerifyFIPS) + fipstls.Abandon() + } + } + } +} + +const ( + boringCertCA = iota + boringCertLeaf + boringCertFIPSOK = 0x80 +) + +func boringRSAKey(t *testing.T, size int) *rsa.PrivateKey { + k, err := rsa.GenerateKey(rand.Reader, size) + if err != nil { + t.Fatal(err) + } + return k +} + +func boringECDSAKey(t *testing.T, curve elliptic.Curve) *ecdsa.PrivateKey { + k, err := ecdsa.GenerateKey(curve, rand.Reader) + if err != nil { + t.Fatal(err) + } + return k +} + +type boringCertificate struct { + name string + org string + parentOrg string + der []byte + cert *x509.Certificate + key interface{} + fipsOK bool +} + +func boringCert(t *testing.T, name string, key interface{}, parent *boringCertificate, mode int) *boringCertificate { + org := name + parentOrg := "" + if i := strings.Index(org, "_"); i >= 0 { + org = org[:i] + parentOrg = name[i+1:] + } + tmpl := &x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + Organization: []string{org}, + }, + NotBefore: time.Unix(0, 0), + NotAfter: time.Unix(0, 0), + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, + BasicConstraintsValid: true, + } + if mode&^boringCertFIPSOK == boringCertLeaf { + tmpl.DNSNames = []string{"example.com"} + } else { + tmpl.IsCA = true + tmpl.KeyUsage |= x509.KeyUsageCertSign + } + + var pcert *x509.Certificate + var pkey interface{} + if parent != nil { + pcert = parent.cert + pkey = parent.key + } else { + pcert = tmpl + pkey = key + } + + var pub interface{} + var desc string + switch k := key.(type) { + case *rsa.PrivateKey: + pub = &k.PublicKey + desc = fmt.Sprintf("RSA-%d", k.N.BitLen()) + case *ecdsa.PrivateKey: + pub = &k.PublicKey + desc = "ECDSA-" + k.Curve.Params().Name + default: + t.Fatalf("invalid key %T", key) + } + + der, err := x509.CreateCertificate(rand.Reader, tmpl, pcert, pub, pkey) + if err != nil { + t.Fatal(err) + } + cert, err := x509.ParseCertificate(der) + if err != nil { + t.Fatal(err) + } + + // Tell isBoringCertificate to enforce FIPS restrictions for this check. + fipstls.Force() + defer fipstls.Abandon() + + fipsOK := mode&boringCertFIPSOK != 0 + if isBoringCertificate(cert) != fipsOK { + t.Errorf("isBoringCertificate(cert with %s key) = %v, want %v", desc, !fipsOK, fipsOK) + } + return &boringCertificate{name, org, parentOrg, der, cert, key, fipsOK} +} + +// A self-signed test certificate with an RSA key of size 2048, for testing +// RSA-PSS with SHA512. SAN of example.golang. +var ( + testRSA2048Certificate []byte + testRSA2048PrivateKey *rsa.PrivateKey +) + +func init() { + block, _ := pem.Decode([]byte(` +-----BEGIN CERTIFICATE----- +MIIC/zCCAeegAwIBAgIRALHHX/kh4+4zMU9DarzBEcQwDQYJKoZIhvcNAQELBQAw +EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xMTAxMDExNTA0MDVaFw0yMDEyMjkxNTA0 +MDVaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCf8fk0N6ieCBX4IOVIfKitt4kGcOQLeimCfsjqqHcysMIVGEtFSM6E +4Ay141f/7IqdW0UtIqNb4PXhROID7yDxR284xL6XbCuv/t5hP3UcehYc3hmLiyVd +MkZQiZWtfUUJf/1qOtM+ohNg59LRWp4d+6iX0la1JL3EwCIckkNjJ9hQbF7Pb2CS ++ES9Yo55KAap8KOblpcR8MBSN38bqnwjfQdCXvOEOjam2HUxKzEFX5MA+fA0me4C +ioCcCRLWKl+GoN9F8fABfoZ+T+2eal4DLuO95rXR8SrOIVBh3XFOr/RVhjtXcNVF +ZKcvDt6d68V6jAKAYKm5nlj9GPpd4v+rAgMBAAGjUDBOMA4GA1UdDwEB/wQEAwIF +oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBkGA1UdEQQSMBCC +DmV4YW1wbGUuZ29sYW5nMA0GCSqGSIb3DQEBCwUAA4IBAQCOoYsVcFCBhboqe3WH +dC6V7XXXECmnjh01r8h80yv0NR379nSD3cw2M+HKvaXysWqrl5hjGVKw0vtwD81r +V4JzDu7IfIog5m8+QNC+7LqDZsz88vDKOrsoySVOmUCgmCKFXew+LA+eO/iQEJTr +7ensddOeXJEp27Ed5vW+kmWW3Qmglc2Gwy8wFrMDIqnrnOzBA4oCnDEgtXJt0zog +nRwbfEMAWi1aQRy5dT9KA3SP9mo5SeTFSzGGHiE4s4gHUe7jvsAFF2qgtD6+wH6s +z9b6shxnC7g5IlBKhI7SVB/Uqt2ydJ+kH1YbjMcIq6NAM5eNMKgZuJr3+zwsSgwh +GNaE +-----END CERTIFICATE-----`)) + testRSA2048Certificate = block.Bytes + + block, _ = pem.Decode([]byte(` +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAn/H5NDeonggV+CDlSHyorbeJBnDkC3opgn7I6qh3MrDCFRhL +RUjOhOAMteNX/+yKnVtFLSKjW+D14UTiA+8g8UdvOMS+l2wrr/7eYT91HHoWHN4Z +i4slXTJGUImVrX1FCX/9ajrTPqITYOfS0VqeHfuol9JWtSS9xMAiHJJDYyfYUGxe +z29gkvhEvWKOeSgGqfCjm5aXEfDAUjd/G6p8I30HQl7zhDo2pth1MSsxBV+TAPnw +NJnuAoqAnAkS1ipfhqDfRfHwAX6Gfk/tnmpeAy7jvea10fEqziFQYd1xTq/0VYY7 +V3DVRWSnLw7enevFeowCgGCpuZ5Y/Rj6XeL/qwIDAQABAoIBAQCNpMZifd/vg42h +HdCvLuZaYS0R7SunFlpoXEsltGdLFsnp0IfoJZ/ugFQBSAIIfLwMumU6oXA1z7Uv +98aIYV61DePrTCDVDFBsHbNmP8JAo8WtbusEbwd5zyoB7LYG2+clkJklWE73KqUq +rmI+UJeyScl2Gin7ZTxBXz1WPBk9VwcnwkeaXpgASIBW23fhECM9gnYEEwaBez5T +6Me8d1tHtYQv7vsKe7ro9w9/HKrRXejqYKK1LxkhfFriyV+m8LZJZn2nXOa6G3gF +Nb8Qk1Uk5PUBENBmyMFJhT4M/uuSq4YtMrrO2gi8Q+fPhuGzc5SshYKRBp0W4P5r +mtVCtEFRAoGBAMENBIFLrV2+HsGj0xYFasKov/QPe6HSTR1Hh2IZONp+oK4oszWE +jBT4VcnITmpl6tC1Wy4GcrxjNgKIFZAj+1x1LUULdorXkuG8yr0tAhG9zNyfWsSy +PrSovC0UVbzr8Jxxla+kQVxEQQqWQxPlEVuL8kXaIDA6Lyt1Hpua2LvPAoGBANQZ +c6Lq2T7+BxLxNdi2m8kZzej5kgzBp/XdVsbFWRlebIX2KrFHsrHzT9PUk3DE1vZK +M6pzTt94nQhWSkDgCaw1SohElJ3HFIFwcusF1SJAc3pQepd8ug6IYdlpDMLtBj/P +/5P6BVUtgo05E4+I/T3iYatmglQxTtlZ0RkSV2llAoGBALOXkKFX7ahPvf0WksDh +uTfuFOTPoowgQG0EpgW0wRdCxeg/JLic3lSD0gsttQV2WsRecryWcxaelRg10RmO +38BbogmhaF4xvgsSvujOfiZTE8oK1T43M+6NKsIlML3YILbpU/9aJxPWy0s2DqDr +cQJhZrlk+pzjBA7Bnf/URdwxAoGAKR/CNw14D+mrL3YLbbiCXiydqxVwxv5pdZdz +8thi3TNcsWC4iGURdcVqbfUinVPdJiXe/Kac3WGCeRJaFVgbKAOxLti1RB5MkIhg +D8eyupBqk4W1L1gkrxqsdj4TFlxkwMywjl2E2S4YyQ8PBt6V04DoVRZsIKzqz+PF +UionPq0CgYBCYXvqioJhPewkOq/Y5wrDBeZW1FQK5QD9W5M8/5zxd4rdvJtjhbJp +oOrtvMdrl6upy9Hz4BJD3FXwVFiPFE7jqeNqi0F21viLxBPMMD3UODF6LL5EyLiR +9V4xVMS8KXxvg7rxsuqzMPscViaWUL6WNVBhsD2+92dHxSXzz5EJKQ== +-----END RSA PRIVATE KEY-----`)) + var err error + testRSA2048PrivateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + panic(err) + } +} diff --git a/src/crypto/tls/cipher_suites.go b/src/crypto/tls/cipher_suites.go index 61bbc9f909..1febee40ee 100644 --- a/src/crypto/tls/cipher_suites.go +++ b/src/crypto/tls/cipher_suites.go @@ -18,6 +18,8 @@ import ( "hash" ) +import "crypto/internal/boring" + // a keyAgreement implements the client and server side of a TLS key agreement // protocol by generating and processing key exchange messages. type keyAgreement interface { @@ -149,7 +151,13 @@ func macSHA1(version uint16, key []byte) macFunction { copy(mac.key, key) return mac } - return tls10MAC{h: hmac.New(newConstantTimeHash(sha1.New), key)} + h := sha1.New + // The BoringCrypto SHA1 does not have a constant-time + // checksum function, so don't try to use it. + if !boring.Enabled { + h = newConstantTimeHash(h) + } + return tls10MAC{h: hmac.New(h, key)} } // macSHA256 returns a SHA-256 based MAC. These are only supported in TLS 1.2 @@ -238,6 +246,10 @@ func (f *xorNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]by return result, err } +type gcmtls interface { + NewGCMTLS() (cipher.AEAD, error) +} + func aeadAESGCM(key, noncePrefix []byte) aead { if len(noncePrefix) != noncePrefixLength { panic("tls: internal error: wrong nonce length") @@ -246,7 +258,13 @@ func aeadAESGCM(key, noncePrefix []byte) aead { if err != nil { panic(err) } - aead, err := cipher.NewGCM(aes) + var aead cipher.AEAD + if aesTLS, ok := aes.(gcmtls); ok { + aead, err = aesTLS.NewGCMTLS() + } else { + boring.Unreachable() + aead, err = cipher.NewGCM(aes) + } if err != nil { panic(err) } @@ -346,6 +364,7 @@ func (c *cthWrapper) Write(p []byte) (int, error) { return c.h.Write(p) } func (c *cthWrapper) Sum(b []byte) []byte { return c.h.ConstantTimeSum(b) } func newConstantTimeHash(h func() hash.Hash) func() hash.Hash { + boring.Unreachable() return func() hash.Hash { return &cthWrapper{h().(constantTimeHash)} } diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go index ef0b385848..121c1d9d23 100644 --- a/src/crypto/tls/common.go +++ b/src/crypto/tls/common.go @@ -22,6 +22,8 @@ import ( "time" ) +import "crypto/internal/boring" + const ( VersionTLS10 = 0x0301 VersionTLS11 = 0x0302 @@ -169,11 +171,11 @@ const ( // hash function associated with the Ed25519 signature scheme. var directSigning crypto.Hash = 0 -// supportedSignatureAlgorithms contains the signature and hash algorithms that +// defaultSupportedSignatureAlgorithms contains the signature and hash algorithms that // the code advertises as supported in a TLS 1.2+ ClientHello and in a TLS 1.2+ // CertificateRequest. The two fields are merged to match with TLS 1.3. // Note that in TLS 1.2, the ECDSA algorithms are not constrained to P-256, etc. -var supportedSignatureAlgorithms = []SignatureScheme{ +var defaultSupportedSignatureAlgorithms = []SignatureScheme{ PSSWithSHA256, ECDSAWithP256AndSHA256, Ed25519, @@ -188,10 +190,10 @@ var supportedSignatureAlgorithms = []SignatureScheme{ ECDSAWithSHA1, } -// supportedSignatureAlgorithmsTLS12 contains the signature and hash algorithms +// defaultSupportedSignatureAlgorithmsTLS12 contains the signature and hash algorithms // that are supported in TLS 1.2, where it is possible to distinguish the // protocol version. This is temporary, see Issue 32425. -var supportedSignatureAlgorithmsTLS12 = []SignatureScheme{ +var defaultSupportedSignatureAlgorithmsTLS12 = []SignatureScheme{ PKCS1WithSHA256, ECDSAWithP256AndSHA256, Ed25519, @@ -776,6 +778,9 @@ func (c *Config) time() time.Time { } func (c *Config) cipherSuites() []uint16 { + if needFIPS() { + return fipsCipherSuites(c) + } s := c.CipherSuites if s == nil { s = defaultCipherSuites() @@ -794,6 +799,9 @@ var supportedVersions = []uint16{ func (c *Config) supportedVersions(isClient bool) []uint16 { versions := make([]uint16, 0, len(supportedVersions)) for _, v := range supportedVersions { + if needFIPS() && (v < fipsMinVersion(c) || v > fipsMaxVersion(c)) { + continue + } // TLS 1.0 is the default minimum version. if (c == nil || c.MinVersion == 0) && v < VersionTLS10 { continue @@ -880,6 +888,9 @@ func supportedVersionsFromMax(maxVersion uint16) []uint16 { var defaultCurvePreferences = []CurveID{X25519, CurveP256, CurveP384, CurveP521} func (c *Config) curvePreferences() []CurveID { + if needFIPS() { + return fipsCurvePreferences(c) + } if c == nil || len(c.CurvePreferences) == 0 { return defaultCurvePreferences } @@ -1140,7 +1151,8 @@ func initDefaultCipherSuites() { hasGCMAsm = hasGCMAsmAMD64 || hasGCMAsmARM64 || hasGCMAsmS390X ) - if hasGCMAsm { + if hasGCMAsm || boring.Enabled { + // If BoringCrypto is enabled, always prioritize AES-GCM. // If AES-GCM hardware is provided then prioritise AES-GCM // cipher suites. topCipherSuites = []uint16{ diff --git a/src/crypto/tls/fipsonly/fipsonly.go b/src/crypto/tls/fipsonly/fipsonly.go new file mode 100644 index 0000000000..85b3532d26 --- /dev/null +++ b/src/crypto/tls/fipsonly/fipsonly.go @@ -0,0 +1,27 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package fipsonly restricts all TLS configuration to FIPS-approved settings. +// +// The effect is triggered by importing the package anywhere in a program, as in: +// +// import _ "crypto/tls/fipsonly" +// +// This package only exists in the dev.boringcrypto branch of Go. +package fipsonly + +// This functionality is provided as a side effect of an import to make +// it trivial to add to an existing program. It requires only a single line +// added to an existing source file, or it can be done by adding a whole +// new source file and not modifying any existing source files. + +import ( + "crypto/internal/boring/fipstls" + "crypto/internal/boring/sig" +) + +func init() { + fipstls.Force() + sig.FIPSOnly() +} diff --git a/src/crypto/tls/fipsonly/fipsonly_test.go b/src/crypto/tls/fipsonly/fipsonly_test.go new file mode 100644 index 0000000000..facd24807d --- /dev/null +++ b/src/crypto/tls/fipsonly/fipsonly_test.go @@ -0,0 +1,16 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package fipsonly + +import ( + "crypto/internal/boring/fipstls" + "testing" +) + +func Test(t *testing.T) { + if !fipstls.Required() { + t.Fatal("fipstls.Required() = false, must be true") + } +} diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go index ed000eb619..1ff3422148 100644 --- a/src/crypto/tls/handshake_client.go +++ b/src/crypto/tls/handshake_client.go @@ -114,7 +114,10 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, ecdheParameters, error) { } if hello.vers >= VersionTLS12 { - hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms + hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms() + } + if testingOnlyForceClientHelloSignatureAlgorithms != nil { + hello.supportedSignatureAlgorithms = testingOnlyForceClientHelloSignatureAlgorithms } var params ecdheParameters @@ -573,7 +576,7 @@ func (hs *clientHandshakeState) doFullHandshake() error { return fmt.Errorf("tls: client certificate private key of type %T does not implement crypto.Signer", chainToSend.PrivateKey) } - signatureAlgorithm, sigType, hashFunc, err := pickSignatureAlgorithm(key.Public(), certReq.supportedSignatureAlgorithms, supportedSignatureAlgorithmsTLS12, c.vers) + signatureAlgorithm, sigType, hashFunc, err := pickSignatureAlgorithm(key.Public(), certReq.supportedSignatureAlgorithms, supportedSignatureAlgorithmsTLS12(), c.vers) if err != nil { c.sendAlert(alertInternalError) return err @@ -822,6 +825,8 @@ func (c *Conn) verifyServerCertificate(certificates [][]byte) error { if !c.config.InsecureSkipVerify { opts := x509.VerifyOptions{ + IsBoring: isBoringCertificate, + Roots: c.config.RootCAs, CurrentTime: c.config.time(), DNSName: c.config.ServerName, diff --git a/src/crypto/tls/handshake_client_tls13.go b/src/crypto/tls/handshake_client_tls13.go index 82207eb646..067231b199 100644 --- a/src/crypto/tls/handshake_client_tls13.go +++ b/src/crypto/tls/handshake_client_tls13.go @@ -39,6 +39,10 @@ type clientHandshakeStateTLS13 struct { func (hs *clientHandshakeStateTLS13) handshake() error { c := hs.c + if needFIPS() { + return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode") + } + // The server must not select TLS 1.3 in a renegotiation. See RFC 8446, // sections 4.1.2 and 4.1.3. if c.handshakes > 0 { @@ -448,7 +452,7 @@ func (hs *clientHandshakeStateTLS13) readServerCertificate() error { } // See RFC 8446, Section 4.4.3. - if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms) { + if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms()) { c.sendAlert(alertIllegalParameter) return errors.New("tls: invalid certificate signature algorithm") } diff --git a/src/crypto/tls/handshake_messages_test.go b/src/crypto/tls/handshake_messages_test.go index 21beb8ef2d..1f08f6ac0e 100644 --- a/src/crypto/tls/handshake_messages_test.go +++ b/src/crypto/tls/handshake_messages_test.go @@ -151,10 +151,10 @@ func (*clientHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value { } } if rand.Intn(10) > 5 { - m.supportedSignatureAlgorithms = supportedSignatureAlgorithms + m.supportedSignatureAlgorithms = supportedSignatureAlgorithms() } if rand.Intn(10) > 5 { - m.supportedSignatureAlgorithmsCert = supportedSignatureAlgorithms + m.supportedSignatureAlgorithmsCert = supportedSignatureAlgorithms() } for i := 0; i < rand.Intn(5); i++ { m.alpnProtocols = append(m.alpnProtocols, randomString(rand.Intn(20)+1, rand)) @@ -386,10 +386,10 @@ func (*certificateRequestMsgTLS13) Generate(rand *rand.Rand, size int) reflect.V m.scts = true } if rand.Intn(10) > 5 { - m.supportedSignatureAlgorithms = supportedSignatureAlgorithms + m.supportedSignatureAlgorithms = supportedSignatureAlgorithms() } if rand.Intn(10) > 5 { - m.supportedSignatureAlgorithmsCert = supportedSignatureAlgorithms + m.supportedSignatureAlgorithmsCert = supportedSignatureAlgorithms() } if rand.Intn(10) > 5 { m.certificateAuthorities = make([][]byte, 3) diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go index a3d8848caf..366c19a0f5 100644 --- a/src/crypto/tls/handshake_server.go +++ b/src/crypto/tls/handshake_server.go @@ -467,7 +467,7 @@ func (hs *serverHandshakeState) doFullHandshake() error { } if c.vers >= VersionTLS12 { certReq.hasSignatureAlgorithm = true - certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithmsTLS12 + certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithmsTLS12() } // An empty list of certificateAuthorities signals to @@ -727,6 +727,8 @@ func (c *Conn) processCertsFromClient(certificate Certificate) error { if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 { opts := x509.VerifyOptions{ + IsBoring: isBoringCertificate, + Roots: c.config.ClientCAs, CurrentTime: c.config.time(), Intermediates: x509.NewCertPool(), diff --git a/src/crypto/tls/handshake_server_tls13.go b/src/crypto/tls/handshake_server_tls13.go index 74f430cf8a..65001e4ecf 100644 --- a/src/crypto/tls/handshake_server_tls13.go +++ b/src/crypto/tls/handshake_server_tls13.go @@ -42,6 +42,10 @@ type serverHandshakeStateTLS13 struct { func (hs *serverHandshakeStateTLS13) handshake() error { c := hs.c + if needFIPS() { + return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode") + } + // For an overview of the TLS 1.3 handshake, see RFC 8446, Section 2. if err := hs.processClientHello(); err != nil { return err @@ -595,7 +599,7 @@ func (hs *serverHandshakeStateTLS13) sendServerCertificate() error { certReq := new(certificateRequestMsgTLS13) certReq.ocspStapling = true certReq.scts = true - certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms + certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms() if c.config.ClientCAs != nil { certReq.certificateAuthorities = c.config.ClientCAs.Subjects() } @@ -800,7 +804,7 @@ func (hs *serverHandshakeStateTLS13) readClientCertificate() error { } // See RFC 8446, Section 4.4.3. - if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms) { + if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms()) { c.sendAlert(alertIllegalParameter) return errors.New("tls: invalid certificate signature algorithm") } diff --git a/src/crypto/tls/key_agreement.go b/src/crypto/tls/key_agreement.go index 2922017cc4..c92dd2e5b5 100644 --- a/src/crypto/tls/key_agreement.go +++ b/src/crypto/tls/key_agreement.go @@ -185,7 +185,7 @@ NextCandidate: return nil, errors.New("tls: certificate private key does not implement crypto.Signer") } - signatureAlgorithm, sigType, hashFunc, err := pickSignatureAlgorithm(priv.Public(), clientHello.supportedSignatureAlgorithms, supportedSignatureAlgorithmsTLS12, ka.version) + signatureAlgorithm, sigType, hashFunc, err := pickSignatureAlgorithm(priv.Public(), clientHello.supportedSignatureAlgorithms, supportedSignatureAlgorithmsTLS12(), ka.version) if err != nil { return nil, err } diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go index 3b5b3576bd..a30f64456d 100644 --- a/src/crypto/x509/verify.go +++ b/src/crypto/x509/verify.go @@ -188,6 +188,11 @@ var errNotParsed = errors.New("x509: missing ASN.1 contents; use ParseCertificat // VerifyOptions contains parameters for Certificate.Verify. It's a structure // because other PKIX verification APIs have ended up needing many options. type VerifyOptions struct { + // IsBoring is a validity check for BoringCrypto. + // If not nil, it will be called to check whether a given certificate + // can be used for constructing verification chains. + IsBoring func(*Certificate) bool + DNSName string Intermediates *CertPool Roots *CertPool // if nil, the system roots are used @@ -699,6 +704,13 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V } } + if opts.IsBoring != nil && !opts.IsBoring(c) { + // IncompatibleUsage is not quite right here, + // but it's also the "no chains found" error + // and is close enough. + return CertificateInvalidError{c, IncompatibleUsage, ""} + } + return nil } diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 179b0bfef5..b8696ec6f9 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -121,10 +121,17 @@ var pkgDeps = map[string][]string{ "reflect": {"L2"}, "sort": {"internal/reflectlite"}, + "crypto/internal/boring": {"L2", "C", "crypto", "crypto/cipher", "crypto/internal/boring/sig", "crypto/subtle", "encoding/asn1", "hash", "math/big"}, + "crypto/internal/boring/fipstls": {"sync/atomic"}, + "crypto/internal/cipherhw": {"crypto/internal/boring"}, + "crypto/tls/fipsonly": {"crypto/internal/boring/fipstls", "crypto/internal/boring/sig"}, + "L3": { "L2", "crypto", "crypto/cipher", + "crypto/internal/boring", + "crypto/internal/boring/fipstls", "crypto/internal/subtle", "crypto/subtle", "encoding/base32", diff --git a/src/internal/boringtest/boring.go b/src/internal/boringtest/boring.go new file mode 100644 index 0000000000..bea1276e69 --- /dev/null +++ b/src/internal/boringtest/boring.go @@ -0,0 +1,8 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Nothing to see here but the tests. +// This file keeps 'go install internal/...' working. + +package boring diff --git a/src/internal/boringtest/boring_test.go b/src/internal/boringtest/boring_test.go new file mode 100644 index 0000000000..a6b07eda70 --- /dev/null +++ b/src/internal/boringtest/boring_test.go @@ -0,0 +1,47 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Like crypto/rsa/boring_test.go but outside the crypto/ tree. +// Tests what happens if a package outside the crypto/ tree +// "adopts" a struct definition. This happens in golang.org/x/crypto/ssh. + +package boring + +import ( + "crypto/rand" + "crypto/rsa" + "encoding/asn1" + "reflect" + "testing" +) + +type publicKey rsa.PublicKey + +func TestBoringASN1Marshal(t *testing.T) { + k, err := rsa.GenerateKey(rand.Reader, 128) + if err != nil { + t.Fatal(err) + } + pk := (*publicKey)(&k.PublicKey) + // This used to fail, because of the unexported 'boring' field. + // Now the compiler hides it [sic]. + _, err = asn1.Marshal(*pk) + if err != nil { + t.Fatal(err) + } +} + +func TestBoringDeepEqual(t *testing.T) { + k0, err := rsa.GenerateKey(rand.Reader, 128) + if err != nil { + t.Fatal(err) + } + k := (*publicKey)(&k0.PublicKey) + k2 := *k + rsa.EncryptPKCS1v15(rand.Reader, (*rsa.PublicKey)(&k2), []byte("hello")) // initialize hidden boring field + if !reflect.DeepEqual(k, &k2) { + // compiler should be hiding the boring field from reflection + t.Fatalf("DeepEqual compared boring fields") + } +} diff --git a/src/runtime/race/testdata/mop_test.go b/src/runtime/race/testdata/mop_test.go index 5d25ed4bb6..b60cabfe86 100644 --- a/src/runtime/race/testdata/mop_test.go +++ b/src/runtime/race/testdata/mop_test.go @@ -6,9 +6,9 @@ package race_test import ( "bytes" - "crypto/sha1" "errors" "fmt" + "hash/crc32" "io" "os" "runtime" @@ -1924,7 +1924,7 @@ func TestRaceIssue5567(t *testing.T) { err = nil } }() - h := sha1.New() + h := crc32.New(crc32.MakeTable(0x12345678)) for b := range in { h.Write(b) } diff --git a/src/runtime/runtime_boring.go b/src/runtime/runtime_boring.go new file mode 100644 index 0000000000..5a98b20253 --- /dev/null +++ b/src/runtime/runtime_boring.go @@ -0,0 +1,19 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package runtime + +import _ "unsafe" // for go:linkname + +//go:linkname boring_runtime_arg0 crypto/internal/boring.runtime_arg0 +func boring_runtime_arg0() string { + // On Windows, argslice is not set, and it's too much work to find argv0. + if len(argslice) == 0 { + return "" + } + return argslice[0] +} + +//go:linkname fipstls_runtime_arg0 crypto/internal/boring/fipstls.runtime_arg0 +func fipstls_runtime_arg0() string { return boring_runtime_arg0() } |