vendor
This commit is contained in:
24
vendor/github.com/google/certificate-transparency-go/.gitignore
generated
vendored
Normal file
24
vendor/github.com/google/certificate-transparency-go/.gitignore
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
*.iml
|
||||
*.swo
|
||||
*.swp
|
||||
*.tfstate
|
||||
*.tfstate.backup
|
||||
*~
|
||||
/.idea
|
||||
/certcheck
|
||||
/chainfix
|
||||
/coverage.txt
|
||||
/createtree
|
||||
/crlcheck
|
||||
/ctclient
|
||||
/ct_server
|
||||
/ct_hammer
|
||||
/data
|
||||
/dumpscts
|
||||
/etcdiscover
|
||||
/gossip_server
|
||||
/preloader
|
||||
/scanlog
|
||||
/trillian_log_server
|
||||
/trillian_log_signer
|
||||
/trillian.json
|
66
vendor/github.com/google/certificate-transparency-go/.travis.yml
generated
vendored
Normal file
66
vendor/github.com/google/certificate-transparency-go/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
sudo: false
|
||||
language: go
|
||||
os: linux
|
||||
go: 1.9
|
||||
|
||||
env:
|
||||
- GOFLAGS=
|
||||
- GOFLAGS=-race
|
||||
- GOFLAGS= WITH_ETCD=true
|
||||
- GOFLAGS=-race WITH_ETCD=true
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
install:
|
||||
- |
|
||||
if [ ! -d $HOME/gopath/src/github.com/google ]; then
|
||||
mkdir -p $HOME/gopath/src/github.com/google
|
||||
ln -s $TRAVIS_BUILD_DIR $HOME/gopath/src/github.com/google/certificate-transparency-go
|
||||
fi
|
||||
- mkdir ../protoc
|
||||
- |
|
||||
(
|
||||
cd ../protoc
|
||||
wget https://github.com/google/protobuf/releases/download/v3.2.0/protoc-3.2.0-${TRAVIS_OS_NAME}-x86_64.zip
|
||||
unzip protoc-3.2.0-${TRAVIS_OS_NAME}-x86_64.zip
|
||||
)
|
||||
- export PATH=$(pwd)/../protoc/bin:$PATH
|
||||
- go get -d -t ./...
|
||||
- go get github.com/alecthomas/gometalinter
|
||||
- gometalinter --install
|
||||
- go get -u github.com/golang/protobuf/proto
|
||||
- go get -u github.com/golang/protobuf/protoc-gen-go
|
||||
- go install github.com/golang/mock/mockgen
|
||||
# install vendored etcd binary
|
||||
- go install ./vendor/github.com/coreos/etcd/cmd/etcd
|
||||
- go install ./vendor/github.com/coreos/etcd/cmd/etcdctl
|
||||
- pushd ${GOPATH}/src/github.com/google/trillian
|
||||
- go get -d -t ./...
|
||||
- popd
|
||||
|
||||
script:
|
||||
- set -e
|
||||
- export TRILLIAN_SQL_DRIVER=mysql
|
||||
- cd $HOME/gopath/src/github.com/google/certificate-transparency-go
|
||||
- ./scripts/presubmit.sh ${PRESUBMIT_OPTS}
|
||||
- |
|
||||
# Check re-generation didn't change anything
|
||||
status=$(git status --porcelain | grep -v coverage) || :
|
||||
if [[ -n ${status} ]]; then
|
||||
echo "Regenerated files differ from checked-in versions: ${status}"
|
||||
git status
|
||||
git diff
|
||||
exit 1
|
||||
fi
|
||||
- |
|
||||
if [[ "${WITH_ETCD}" == "true" ]]; then
|
||||
export ETCD_DIR="${GOPATH}/bin"
|
||||
fi
|
||||
- ./trillian/integration/integration_test.sh
|
||||
- HAMMER_OPTS="--operations=1500" ./trillian/integration/ct_hammer_test.sh
|
||||
- set +e
|
||||
|
||||
after_success:
|
||||
- cp /tmp/coverage.txt .
|
||||
- bash <(curl -s https://codecov.io/bash)
|
27
vendor/github.com/google/certificate-transparency-go/AUTHORS
generated
vendored
Normal file
27
vendor/github.com/google/certificate-transparency-go/AUTHORS
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
# This is the official list of benchmark authors for copyright purposes.
|
||||
# This file is distinct from the CONTRIBUTORS files.
|
||||
# See the latter for an explanation.
|
||||
#
|
||||
# Names should be added to this file as:
|
||||
# Name or Organization <email address>
|
||||
# The email address is not required for organizations.
|
||||
#
|
||||
# Please keep the list sorted.
|
||||
|
||||
Comodo CA Limited
|
||||
Ed Maste <emaste@freebsd.org>
|
||||
Fiaz Hossain <fiaz.hossain@salesforce.com>
|
||||
Google Inc.
|
||||
Internet Security Research Group
|
||||
Jeff Trawick <trawick@gmail.com>
|
||||
Katriel Cohn-Gordon <katriel.cohn-gordon@cybersecurity.ox.ac.uk>
|
||||
Laël Cellier <lael.cellier@gmail.com>
|
||||
Mark Schloesser <ms@mwcollect.org>
|
||||
NORDUnet A/S
|
||||
Nicholas Galbreath <nickg@client9.com>
|
||||
Oliver Weidner <Oliver.Weidner@gmail.com>
|
||||
PrimeKey Solutions AB
|
||||
Ruslan Kovalov <ruslan.kovalyov@gmail.com>
|
||||
Venafi, Inc.
|
||||
Vladimir Rutsky <vladimir@rutsky.org>
|
||||
Ximin Luo <infinity0@gmx.com>
|
58
vendor/github.com/google/certificate-transparency-go/CONTRIBUTING.md
generated
vendored
Normal file
58
vendor/github.com/google/certificate-transparency-go/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
# How to contribute #
|
||||
|
||||
We'd love to accept your patches and contributions to this project. There are
|
||||
a just a few small guidelines you need to follow.
|
||||
|
||||
|
||||
## Contributor License Agreement ##
|
||||
|
||||
Contributions to any Google project must be accompanied by a Contributor
|
||||
License Agreement. This is not a copyright **assignment**, it simply gives
|
||||
Google permission to use and redistribute your contributions as part of the
|
||||
project.
|
||||
|
||||
* If you are an individual writing original source code and you're sure you
|
||||
own the intellectual property, then you'll need to sign an [individual
|
||||
CLA][].
|
||||
|
||||
* If you work for a company that wants to allow you to contribute your work,
|
||||
then you'll need to sign a [corporate CLA][].
|
||||
|
||||
You generally only need to submit a CLA once, so if you've already submitted
|
||||
one (even if it was for a different project), you probably don't need to do it
|
||||
again.
|
||||
|
||||
[individual CLA]: https://developers.google.com/open-source/cla/individual
|
||||
[corporate CLA]: https://developers.google.com/open-source/cla/corporate
|
||||
|
||||
Once your CLA is submitted (or if you already submitted one for
|
||||
another Google project), make a commit adding yourself to the
|
||||
[AUTHORS][] and [CONTRIBUTORS][] files. This commit can be part
|
||||
of your first [pull request][].
|
||||
|
||||
[AUTHORS]: AUTHORS
|
||||
[CONTRIBUTORS]: CONTRIBUTORS
|
||||
|
||||
|
||||
## Submitting a patch ##
|
||||
|
||||
1. It's generally best to start by opening a new issue describing the bug or
|
||||
feature you're intending to fix. Even if you think it's relatively minor,
|
||||
it's helpful to know what people are working on. Mention in the initial
|
||||
issue that you are planning to work on that bug or feature so that it can
|
||||
be assigned to you.
|
||||
|
||||
1. Follow the normal process of [forking][] the project, and setup a new
|
||||
branch to work in. It's important that each group of changes be done in
|
||||
separate branches in order to ensure that a pull request only includes the
|
||||
commits related to that bug or feature.
|
||||
|
||||
1. Do your best to have [well-formed commit messages][] for each change.
|
||||
This provides consistency throughout the project, and ensures that commit
|
||||
messages are able to be formatted properly by various git tools.
|
||||
|
||||
1. Finally, push the commits to your fork and submit a [pull request][].
|
||||
|
||||
[forking]: https://help.github.com/articles/fork-a-repo
|
||||
[well-formed commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
|
||||
[pull request]: https://help.github.com/articles/creating-a-pull-request
|
57
vendor/github.com/google/certificate-transparency-go/CONTRIBUTORS
generated
vendored
Normal file
57
vendor/github.com/google/certificate-transparency-go/CONTRIBUTORS
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
# People who have agreed to one of the CLAs and can contribute patches.
|
||||
# The AUTHORS file lists the copyright holders; this file
|
||||
# lists people. For example, Google employees are listed here
|
||||
# but not in AUTHORS, because Google holds the copyright.
|
||||
#
|
||||
# Names should be added to this file only after verifying that
|
||||
# the individual or the individual's organization has agreed to
|
||||
# the appropriate Contributor License Agreement, found here:
|
||||
#
|
||||
# https://developers.google.com/open-source/cla/individual
|
||||
# https://developers.google.com/open-source/cla/corporate
|
||||
#
|
||||
# The agreement for individuals can be filled out on the web.
|
||||
#
|
||||
# When adding J Random Contributor's name to this file,
|
||||
# either J's name or J's organization's name should be
|
||||
# added to the AUTHORS file, depending on whether the
|
||||
# individual or corporate CLA was used.
|
||||
#
|
||||
# Names should be added to this file as:
|
||||
# Name <email address>
|
||||
#
|
||||
# Please keep the list sorted.
|
||||
|
||||
Adam Eijdenberg <eijdenberg@google.com> <adam.eijdenberg@gmail.com>
|
||||
Al Cutter <al@google.com>
|
||||
Ben Laurie <benl@google.com> <ben@links.org>
|
||||
Chris Kennelly <ckennelly@google.com> <ckennelly@ckennelly.com>
|
||||
David Drysdale <drysdale@google.com>
|
||||
Deyan Bektchiev <deyan.bektchiev@venafi.com> <deyan@bektchiev.net>
|
||||
Ed Maste <emaste@freebsd.org>
|
||||
Emilia Kasper <ekasper@google.com>
|
||||
Eran Messeri <eranm@google.com> <eran.mes@gmail.com>
|
||||
Fiaz Hossain <fiaz.hossain@salesforce.com>
|
||||
Gary Belvin <gbelvin@google.com> <gdbelvin@gmail.com>
|
||||
Jeff Trawick <trawick@gmail.com>
|
||||
Joe Tsai <joetsai@digital-static.net>
|
||||
Kat Joyce <katjoyce@google.com>
|
||||
Katriel Cohn-Gordon <katriel.cohn-gordon@cybersecurity.ox.ac.uk>
|
||||
Kiril Nikolov <kiril.nikolov@venafi.com>
|
||||
Konrad Kraszewski <kraszewski@google.com> <laiquendir@gmail.com>
|
||||
Laël Cellier <lael.cellier@gmail.com>
|
||||
Linus Nordberg <linus@nordu.net>
|
||||
Mark Schloesser <ms@mwcollect.org>
|
||||
Nicholas Galbreath <nickg@client9.com>
|
||||
Oliver Weidner <Oliver.Weidner@gmail.com>
|
||||
Pascal Leroy <phl@google.com>
|
||||
Paul Hadfield <hadfieldp@google.com> <paul@phad.org.uk>
|
||||
Paul Lietar <lietar@google.com>
|
||||
Pierre Phaneuf <pphaneuf@google.com>
|
||||
Rob Percival <robpercival@google.com>
|
||||
Rob Stradling <rob@comodo.com>
|
||||
Roland Shoemaker <roland@letsencrypt.org>
|
||||
Ruslan Kovalov <ruslan.kovalyov@gmail.com>
|
||||
Samuel Lidén Borell <samuel@kodafritt.se>
|
||||
Vladimir Rutsky <vladimir@rutsky.org>
|
||||
Ximin Luo <infinity0@gmx.com>
|
202
vendor/github.com/google/certificate-transparency-go/LICENSE
generated
vendored
Normal file
202
vendor/github.com/google/certificate-transparency-go/LICENSE
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
144
vendor/github.com/google/certificate-transparency-go/README.md
generated
vendored
Normal file
144
vendor/github.com/google/certificate-transparency-go/README.md
generated
vendored
Normal file
@ -0,0 +1,144 @@
|
||||
# Certificate Transparency: Go Code
|
||||
|
||||
[](https://travis-ci.org/google/certificate-transparency-go)
|
||||
[](https://goreportcard.com/report/github.com/google/certificate-transparency-go)
|
||||
[](https://godoc.org/github.com/google/certificate-transparency-go)
|
||||
|
||||
This repository holds Go code related to
|
||||
[Certificate Transparency](https://www.certificate-transparency.org/) (CT). The
|
||||
repository requires Go version 1.9.
|
||||
|
||||
- [Repository Structure](#repository-structure)
|
||||
- [Trillian CT Personality](#trillian-ct-personality)
|
||||
- [Working on the Code](#working-on-the-code)
|
||||
- [Rebuilding Generated Code](#rebuilding-generated-code)
|
||||
- [Updating Vendor Code](#updating-vendor-code)
|
||||
- [Running Codebase Checks](#running-codebase-checks)
|
||||
|
||||
## Repository Structure
|
||||
|
||||
The main parts of the repository are:
|
||||
|
||||
- Encoding libraries:
|
||||
- `asn1/` and `x509/` are forks of the upstream Go `encoding/asn1` and
|
||||
`crypto/x509` libraries. We maintain separate forks of these packages
|
||||
because CT is intended to act as an observatory of certificates across the
|
||||
ecosystem; as such, we need to be able to process somewhat-malformed
|
||||
certificates that the stricter upstream code would (correctly) reject.
|
||||
Our `x509` fork also includes code for working with the
|
||||
[pre-certificates defined in RFC 6962](https://tools.ietf.org/html/rfc6962#section-3.1).
|
||||
- `tls` holds a library for processing TLS-encoded data as described in
|
||||
[RFC 5246](https://tools.ietf.org/html/rfc5246).
|
||||
- `x509util` provides additional utilities for dealing with
|
||||
`x509.Certificate`s.
|
||||
- CT client libraries:
|
||||
- The top-level `ct` package (in `.`) holds types and utilities for working
|
||||
with CT data structures defined in
|
||||
[RFC 6962](https://tools.ietf.org/html/rfc6962).
|
||||
- `client/` and `jsonclient/` hold libraries that allow access to CT Logs
|
||||
via entrypoints described in
|
||||
[section 4 of RFC 6962](https://tools.ietf.org/html/rfc6962#section-4).
|
||||
- `scanner/` holds a library for scanning the entire contents of an existing
|
||||
CT Log.
|
||||
- Command line tools:
|
||||
- `./client/ctclient` allows interaction with a CT Log
|
||||
- `./scanner/scanlog` allows an existing CT Log to be scanned for certificates
|
||||
of interest; please be polite when running this tool against a Log.
|
||||
- `./x509util/certcheck` allows display and verification of certificates
|
||||
- `./x509util/crlcheck` allows display and verification of certificate
|
||||
revocation lists (CRLs).
|
||||
- CT Personality for [Trillian](https://github.com/google/trillian):
|
||||
- `trillian/` holds code that allows a Certificate Transparency Log to be
|
||||
run using a Trillian Log as its back-end -- see
|
||||
[below](#trillian-ct-personality).
|
||||
|
||||
|
||||
## Trillian CT Personality
|
||||
|
||||
The `trillian/` subdirectory holds code and scripts for running a CT Log based
|
||||
on the [Trillian](https://github.com/google/trillian) general transparency Log.
|
||||
|
||||
The main code for the CT personality is held in `trillian/ctfe`; this code
|
||||
responds to HTTP requests on the
|
||||
[CT API paths](https://tools.ietf.org/html/rfc6962#section-4) and translates
|
||||
them to the equivalent gRPC API requests to the Trillian Log.
|
||||
|
||||
This obviously relies on the gRPC API definitions at
|
||||
`github.com/google/trillian`; the code also uses common libraries from the
|
||||
Trillian project for:
|
||||
- exposing monitoring and statistics via an `interface` and corresponding
|
||||
Prometheus implementation (`github.com/google/trillian/monitoring/...`)
|
||||
- dealing with cryptographic keys (`github.com/google/trillian/crypto/...`).
|
||||
|
||||
The `trillian/integration/` directory holds scripts and tests for running the whole
|
||||
system locally. In particular:
|
||||
- `trillian/integration/ct_integration_test.sh` brings up local processes
|
||||
running a Trillian Log server, signer and a CT personality, and exercises the
|
||||
complete set of RFC 6962 API entrypoints.
|
||||
- `trillian/integration/ct_hammer_test.sh` brings up a complete system and runs
|
||||
a continuous randomized test of the CT entrypoints.
|
||||
|
||||
These scripts require a local database instance to be configured as described
|
||||
in the [Trillian instructions](https://github.com/google/trillian#mysql-setup).
|
||||
|
||||
|
||||
## Working on the Code
|
||||
|
||||
Developers who want to make changes to the codebase need some additional
|
||||
dependencies and tools, described in the following sections. The
|
||||
[Travis configuration](.travis.yml) for the codebase is also useful reference
|
||||
for the required tools and scripts, as it may be more up-to-date than this
|
||||
document.
|
||||
|
||||
### Rebuilding Generated Code
|
||||
|
||||
Some of the CT Go code is autogenerated from other files:
|
||||
|
||||
- [Protocol buffer](https://developers.google.com/protocol-buffers/) message
|
||||
definitions are converted to `.pb.go` implementations.
|
||||
- A mock implementation of the Trillian gRPC API (in `trillian/mockclient`) is
|
||||
created with [GoMock](https://github.com/golang/mock).
|
||||
|
||||
Re-generating mock or protobuffer files is only needed if you're changing
|
||||
the original files; if you do, you'll need to install the prerequisites:
|
||||
|
||||
- `mockgen` tool from https://github.com/golang/mock
|
||||
- `protoc`, [Go support for protoc](https://github.com/golang/protobuf) (see
|
||||
documentation linked from the
|
||||
[protobuf site](https://github.com/google/protobuf))
|
||||
|
||||
and run the following:
|
||||
|
||||
```bash
|
||||
go generate -x ./... # hunts for //go:generate comments and runs them
|
||||
```
|
||||
|
||||
### Updating Vendor Code
|
||||
|
||||
The codebase includes a couple of external projects under the `vendor/`
|
||||
subdirectory, to ensure that builds use a fixed version (typically because the
|
||||
upstream repository does not guarantee back-compatibility between the tip
|
||||
`master` branch and the current stable release). See
|
||||
[instructions in the Trillian repo](https://github.com/google/trillian#updating-vendor-code)
|
||||
for how to update vendored subtrees.
|
||||
|
||||
|
||||
### Running Codebase Checks
|
||||
|
||||
The [`scripts/presubmit.sh`](scripts/presubmit.sh) script runs various tools
|
||||
and tests over the codebase.
|
||||
|
||||
```bash
|
||||
# Install gometalinter and all linters
|
||||
go get -u github.com/alecthomas/gometalinter
|
||||
gometalinter --install
|
||||
|
||||
# Run code generation, build, test and linters
|
||||
./scripts/presubmit.sh
|
||||
|
||||
# Run build, test and linters but skip code generation
|
||||
./scripts/presubmit.sh --no-generate
|
||||
|
||||
# Or just run the linters alone:
|
||||
gometalinter --config=gometalinter.json ./...
|
||||
```
|
1141
vendor/github.com/google/certificate-transparency-go/asn1/asn1.go
generated
vendored
Normal file
1141
vendor/github.com/google/certificate-transparency-go/asn1/asn1.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1173
vendor/github.com/google/certificate-transparency-go/asn1/asn1_test.go
generated
vendored
Normal file
1173
vendor/github.com/google/certificate-transparency-go/asn1/asn1_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
177
vendor/github.com/google/certificate-transparency-go/asn1/common.go
generated
vendored
Normal file
177
vendor/github.com/google/certificate-transparency-go/asn1/common.go
generated
vendored
Normal file
@ -0,0 +1,177 @@
|
||||
// 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.
|
||||
|
||||
package asn1
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ASN.1 objects have metadata preceding them:
|
||||
// the tag: the type of the object
|
||||
// a flag denoting if this object is compound or not
|
||||
// the class type: the namespace of the tag
|
||||
// the length of the object, in bytes
|
||||
|
||||
// Here are some standard tags and classes
|
||||
|
||||
// ASN.1 tags represent the type of the following object.
|
||||
const (
|
||||
TagBoolean = 1
|
||||
TagInteger = 2
|
||||
TagBitString = 3
|
||||
TagOctetString = 4
|
||||
TagNull = 5
|
||||
TagOID = 6
|
||||
TagEnum = 10
|
||||
TagUTF8String = 12
|
||||
TagSequence = 16
|
||||
TagSet = 17
|
||||
TagNumericString = 18
|
||||
TagPrintableString = 19
|
||||
TagT61String = 20
|
||||
TagIA5String = 22
|
||||
TagUTCTime = 23
|
||||
TagGeneralizedTime = 24
|
||||
TagGeneralString = 27
|
||||
)
|
||||
|
||||
// ASN.1 class types represent the namespace of the tag.
|
||||
const (
|
||||
ClassUniversal = 0
|
||||
ClassApplication = 1
|
||||
ClassContextSpecific = 2
|
||||
ClassPrivate = 3
|
||||
)
|
||||
|
||||
type tagAndLength struct {
|
||||
class, tag, length int
|
||||
isCompound bool
|
||||
}
|
||||
|
||||
// ASN.1 has IMPLICIT and EXPLICIT tags, which can be translated as "instead
|
||||
// of" and "in addition to". When not specified, every primitive type has a
|
||||
// default tag in the UNIVERSAL class.
|
||||
//
|
||||
// For example: a BIT STRING is tagged [UNIVERSAL 3] by default (although ASN.1
|
||||
// doesn't actually have a UNIVERSAL keyword). However, by saying [IMPLICIT
|
||||
// CONTEXT-SPECIFIC 42], that means that the tag is replaced by another.
|
||||
//
|
||||
// On the other hand, if it said [EXPLICIT CONTEXT-SPECIFIC 10], then an
|
||||
// /additional/ tag would wrap the default tag. This explicit tag will have the
|
||||
// compound flag set.
|
||||
//
|
||||
// (This is used in order to remove ambiguity with optional elements.)
|
||||
//
|
||||
// You can layer EXPLICIT and IMPLICIT tags to an arbitrary depth, however we
|
||||
// don't support that here. We support a single layer of EXPLICIT or IMPLICIT
|
||||
// tagging with tag strings on the fields of a structure.
|
||||
|
||||
// fieldParameters is the parsed representation of tag string from a structure field.
|
||||
type fieldParameters struct {
|
||||
optional bool // true iff the field is OPTIONAL
|
||||
explicit bool // true iff an EXPLICIT tag is in use.
|
||||
application bool // true iff an APPLICATION tag is in use.
|
||||
defaultValue *int64 // a default value for INTEGER typed fields (maybe nil).
|
||||
tag *int // the EXPLICIT or IMPLICIT tag (maybe nil).
|
||||
stringType int // the string tag to use when marshaling.
|
||||
timeType int // the time tag to use when marshaling.
|
||||
set bool // true iff this should be encoded as a SET
|
||||
omitEmpty bool // true iff this should be omitted if empty when marshaling.
|
||||
name string // name of field for better diagnostics
|
||||
|
||||
// Invariants:
|
||||
// if explicit is set, tag is non-nil.
|
||||
}
|
||||
|
||||
// Given a tag string with the format specified in the package comment,
|
||||
// parseFieldParameters will parse it into a fieldParameters structure,
|
||||
// ignoring unknown parts of the string.
|
||||
func parseFieldParameters(str string) (ret fieldParameters) {
|
||||
for _, part := range strings.Split(str, ",") {
|
||||
switch {
|
||||
case part == "optional":
|
||||
ret.optional = true
|
||||
case part == "explicit":
|
||||
ret.explicit = true
|
||||
if ret.tag == nil {
|
||||
ret.tag = new(int)
|
||||
}
|
||||
case part == "generalized":
|
||||
ret.timeType = TagGeneralizedTime
|
||||
case part == "utc":
|
||||
ret.timeType = TagUTCTime
|
||||
case part == "ia5":
|
||||
ret.stringType = TagIA5String
|
||||
case part == "printable":
|
||||
ret.stringType = TagPrintableString
|
||||
case part == "numeric":
|
||||
ret.stringType = TagNumericString
|
||||
case part == "utf8":
|
||||
ret.stringType = TagUTF8String
|
||||
case strings.HasPrefix(part, "default:"):
|
||||
i, err := strconv.ParseInt(part[8:], 10, 64)
|
||||
if err == nil {
|
||||
ret.defaultValue = new(int64)
|
||||
*ret.defaultValue = i
|
||||
}
|
||||
case strings.HasPrefix(part, "tag:"):
|
||||
i, err := strconv.Atoi(part[4:])
|
||||
if err == nil {
|
||||
ret.tag = new(int)
|
||||
*ret.tag = i
|
||||
}
|
||||
case part == "set":
|
||||
ret.set = true
|
||||
case part == "application":
|
||||
ret.application = true
|
||||
if ret.tag == nil {
|
||||
ret.tag = new(int)
|
||||
}
|
||||
case part == "omitempty":
|
||||
ret.omitEmpty = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Given a reflected Go type, getUniversalType returns the default tag number
|
||||
// and expected compound flag.
|
||||
func getUniversalType(t reflect.Type) (matchAny bool, tagNumber int, isCompound, ok bool) {
|
||||
switch t {
|
||||
case rawValueType:
|
||||
return true, -1, false, true
|
||||
case objectIdentifierType:
|
||||
return false, TagOID, false, true
|
||||
case bitStringType:
|
||||
return false, TagBitString, false, true
|
||||
case timeType:
|
||||
return false, TagUTCTime, false, true
|
||||
case enumeratedType:
|
||||
return false, TagEnum, false, true
|
||||
case bigIntType:
|
||||
return false, TagInteger, false, true
|
||||
}
|
||||
switch t.Kind() {
|
||||
case reflect.Bool:
|
||||
return false, TagBoolean, false, true
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return false, TagInteger, false, true
|
||||
case reflect.Struct:
|
||||
return false, TagSequence, true, true
|
||||
case reflect.Slice:
|
||||
if t.Elem().Kind() == reflect.Uint8 {
|
||||
return false, TagOctetString, false, true
|
||||
}
|
||||
if strings.HasSuffix(t.Name(), "SET") {
|
||||
return false, TagSet, true, true
|
||||
}
|
||||
return false, TagSequence, true, true
|
||||
case reflect.String:
|
||||
return false, TagPrintableString, false, true
|
||||
}
|
||||
return false, 0, false, false
|
||||
}
|
689
vendor/github.com/google/certificate-transparency-go/asn1/marshal.go
generated
vendored
Normal file
689
vendor/github.com/google/certificate-transparency-go/asn1/marshal.go
generated
vendored
Normal file
@ -0,0 +1,689 @@
|
||||
// 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.
|
||||
|
||||
package asn1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var (
|
||||
byte00Encoder encoder = byteEncoder(0x00)
|
||||
byteFFEncoder encoder = byteEncoder(0xff)
|
||||
)
|
||||
|
||||
// encoder represents an ASN.1 element that is waiting to be marshaled.
|
||||
type encoder interface {
|
||||
// Len returns the number of bytes needed to marshal this element.
|
||||
Len() int
|
||||
// Encode encodes this element by writing Len() bytes to dst.
|
||||
Encode(dst []byte)
|
||||
}
|
||||
|
||||
type byteEncoder byte
|
||||
|
||||
func (c byteEncoder) Len() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func (c byteEncoder) Encode(dst []byte) {
|
||||
dst[0] = byte(c)
|
||||
}
|
||||
|
||||
type bytesEncoder []byte
|
||||
|
||||
func (b bytesEncoder) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func (b bytesEncoder) Encode(dst []byte) {
|
||||
if copy(dst, b) != len(b) {
|
||||
panic("internal error")
|
||||
}
|
||||
}
|
||||
|
||||
type stringEncoder string
|
||||
|
||||
func (s stringEncoder) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
func (s stringEncoder) Encode(dst []byte) {
|
||||
if copy(dst, s) != len(s) {
|
||||
panic("internal error")
|
||||
}
|
||||
}
|
||||
|
||||
type multiEncoder []encoder
|
||||
|
||||
func (m multiEncoder) Len() int {
|
||||
var size int
|
||||
for _, e := range m {
|
||||
size += e.Len()
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
func (m multiEncoder) Encode(dst []byte) {
|
||||
var off int
|
||||
for _, e := range m {
|
||||
e.Encode(dst[off:])
|
||||
off += e.Len()
|
||||
}
|
||||
}
|
||||
|
||||
type taggedEncoder struct {
|
||||
// scratch contains temporary space for encoding the tag and length of
|
||||
// an element in order to avoid extra allocations.
|
||||
scratch [8]byte
|
||||
tag encoder
|
||||
body encoder
|
||||
}
|
||||
|
||||
func (t *taggedEncoder) Len() int {
|
||||
return t.tag.Len() + t.body.Len()
|
||||
}
|
||||
|
||||
func (t *taggedEncoder) Encode(dst []byte) {
|
||||
t.tag.Encode(dst)
|
||||
t.body.Encode(dst[t.tag.Len():])
|
||||
}
|
||||
|
||||
type int64Encoder int64
|
||||
|
||||
func (i int64Encoder) Len() int {
|
||||
n := 1
|
||||
|
||||
for i > 127 {
|
||||
n++
|
||||
i >>= 8
|
||||
}
|
||||
|
||||
for i < -128 {
|
||||
n++
|
||||
i >>= 8
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func (i int64Encoder) Encode(dst []byte) {
|
||||
n := i.Len()
|
||||
|
||||
for j := 0; j < n; j++ {
|
||||
dst[j] = byte(i >> uint((n-1-j)*8))
|
||||
}
|
||||
}
|
||||
|
||||
func base128IntLength(n int64) int {
|
||||
if n == 0 {
|
||||
return 1
|
||||
}
|
||||
|
||||
l := 0
|
||||
for i := n; i > 0; i >>= 7 {
|
||||
l++
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func appendBase128Int(dst []byte, n int64) []byte {
|
||||
l := base128IntLength(n)
|
||||
|
||||
for i := l - 1; i >= 0; i-- {
|
||||
o := byte(n >> uint(i*7))
|
||||
o &= 0x7f
|
||||
if i != 0 {
|
||||
o |= 0x80
|
||||
}
|
||||
|
||||
dst = append(dst, o)
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
func makeBigInt(n *big.Int, fieldName string) (encoder, error) {
|
||||
if n == nil {
|
||||
return nil, StructuralError{"empty integer", fieldName}
|
||||
}
|
||||
|
||||
if n.Sign() < 0 {
|
||||
// A negative number has to be converted to two's-complement
|
||||
// form. So we'll invert and subtract 1. If the
|
||||
// most-significant-bit isn't set then we'll need to pad the
|
||||
// beginning with 0xff in order to keep the number negative.
|
||||
nMinus1 := new(big.Int).Neg(n)
|
||||
nMinus1.Sub(nMinus1, bigOne)
|
||||
bytes := nMinus1.Bytes()
|
||||
for i := range bytes {
|
||||
bytes[i] ^= 0xff
|
||||
}
|
||||
if len(bytes) == 0 || bytes[0]&0x80 == 0 {
|
||||
return multiEncoder([]encoder{byteFFEncoder, bytesEncoder(bytes)}), nil
|
||||
}
|
||||
return bytesEncoder(bytes), nil
|
||||
} else if n.Sign() == 0 {
|
||||
// Zero is written as a single 0 zero rather than no bytes.
|
||||
return byte00Encoder, nil
|
||||
} else {
|
||||
bytes := n.Bytes()
|
||||
if len(bytes) > 0 && bytes[0]&0x80 != 0 {
|
||||
// We'll have to pad this with 0x00 in order to stop it
|
||||
// looking like a negative number.
|
||||
return multiEncoder([]encoder{byte00Encoder, bytesEncoder(bytes)}), nil
|
||||
}
|
||||
return bytesEncoder(bytes), nil
|
||||
}
|
||||
}
|
||||
|
||||
func appendLength(dst []byte, i int) []byte {
|
||||
n := lengthLength(i)
|
||||
|
||||
for ; n > 0; n-- {
|
||||
dst = append(dst, byte(i>>uint((n-1)*8)))
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
func lengthLength(i int) (numBytes int) {
|
||||
numBytes = 1
|
||||
for i > 255 {
|
||||
numBytes++
|
||||
i >>= 8
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func appendTagAndLength(dst []byte, t tagAndLength) []byte {
|
||||
b := uint8(t.class) << 6
|
||||
if t.isCompound {
|
||||
b |= 0x20
|
||||
}
|
||||
if t.tag >= 31 {
|
||||
b |= 0x1f
|
||||
dst = append(dst, b)
|
||||
dst = appendBase128Int(dst, int64(t.tag))
|
||||
} else {
|
||||
b |= uint8(t.tag)
|
||||
dst = append(dst, b)
|
||||
}
|
||||
|
||||
if t.length >= 128 {
|
||||
l := lengthLength(t.length)
|
||||
dst = append(dst, 0x80|byte(l))
|
||||
dst = appendLength(dst, t.length)
|
||||
} else {
|
||||
dst = append(dst, byte(t.length))
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
type bitStringEncoder BitString
|
||||
|
||||
func (b bitStringEncoder) Len() int {
|
||||
return len(b.Bytes) + 1
|
||||
}
|
||||
|
||||
func (b bitStringEncoder) Encode(dst []byte) {
|
||||
dst[0] = byte((8 - b.BitLength%8) % 8)
|
||||
if copy(dst[1:], b.Bytes) != len(b.Bytes) {
|
||||
panic("internal error")
|
||||
}
|
||||
}
|
||||
|
||||
type oidEncoder []int
|
||||
|
||||
func (oid oidEncoder) Len() int {
|
||||
l := base128IntLength(int64(oid[0]*40 + oid[1]))
|
||||
for i := 2; i < len(oid); i++ {
|
||||
l += base128IntLength(int64(oid[i]))
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (oid oidEncoder) Encode(dst []byte) {
|
||||
dst = appendBase128Int(dst[:0], int64(oid[0]*40+oid[1]))
|
||||
for i := 2; i < len(oid); i++ {
|
||||
dst = appendBase128Int(dst, int64(oid[i]))
|
||||
}
|
||||
}
|
||||
|
||||
func makeObjectIdentifier(oid []int, fieldName string) (e encoder, err error) {
|
||||
if len(oid) < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) {
|
||||
return nil, StructuralError{"invalid object identifier", fieldName}
|
||||
}
|
||||
|
||||
return oidEncoder(oid), nil
|
||||
}
|
||||
|
||||
func makePrintableString(s, fieldName string) (e encoder, err error) {
|
||||
for i := 0; i < len(s); i++ {
|
||||
// The asterisk is often used in PrintableString, even though
|
||||
// it is invalid. If a PrintableString was specifically
|
||||
// requested then the asterisk is permitted by this code.
|
||||
// Ampersand is allowed in parsing due a handful of CA
|
||||
// certificates, however when making new certificates
|
||||
// it is rejected.
|
||||
if !isPrintable(s[i], allowAsterisk, rejectAmpersand) {
|
||||
return nil, StructuralError{"PrintableString contains invalid character", fieldName}
|
||||
}
|
||||
}
|
||||
|
||||
return stringEncoder(s), nil
|
||||
}
|
||||
|
||||
func makeIA5String(s, fieldName string) (e encoder, err error) {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if s[i] > 127 {
|
||||
return nil, StructuralError{"IA5String contains invalid character", fieldName}
|
||||
}
|
||||
}
|
||||
|
||||
return stringEncoder(s), nil
|
||||
}
|
||||
|
||||
func makeNumericString(s string, fieldName string) (e encoder, err error) {
|
||||
for i := 0; i < len(s); i++ {
|
||||
if !isNumeric(s[i]) {
|
||||
return nil, StructuralError{"NumericString contains invalid character", fieldName}
|
||||
}
|
||||
}
|
||||
|
||||
return stringEncoder(s), nil
|
||||
}
|
||||
|
||||
func makeUTF8String(s string) encoder {
|
||||
return stringEncoder(s)
|
||||
}
|
||||
|
||||
func appendTwoDigits(dst []byte, v int) []byte {
|
||||
return append(dst, byte('0'+(v/10)%10), byte('0'+v%10))
|
||||
}
|
||||
|
||||
func appendFourDigits(dst []byte, v int) []byte {
|
||||
var bytes [4]byte
|
||||
for i := range bytes {
|
||||
bytes[3-i] = '0' + byte(v%10)
|
||||
v /= 10
|
||||
}
|
||||
return append(dst, bytes[:]...)
|
||||
}
|
||||
|
||||
func outsideUTCRange(t time.Time) bool {
|
||||
year := t.Year()
|
||||
return year < 1950 || year >= 2050
|
||||
}
|
||||
|
||||
func makeUTCTime(t time.Time, fieldName string) (e encoder, err error) {
|
||||
dst := make([]byte, 0, 18)
|
||||
|
||||
dst, err = appendUTCTime(dst, t, fieldName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bytesEncoder(dst), nil
|
||||
}
|
||||
|
||||
func makeGeneralizedTime(t time.Time, fieldName string) (e encoder, err error) {
|
||||
dst := make([]byte, 0, 20)
|
||||
|
||||
dst, err = appendGeneralizedTime(dst, t, fieldName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bytesEncoder(dst), nil
|
||||
}
|
||||
|
||||
func appendUTCTime(dst []byte, t time.Time, fieldName string) (ret []byte, err error) {
|
||||
year := t.Year()
|
||||
|
||||
switch {
|
||||
case 1950 <= year && year < 2000:
|
||||
dst = appendTwoDigits(dst, year-1900)
|
||||
case 2000 <= year && year < 2050:
|
||||
dst = appendTwoDigits(dst, year-2000)
|
||||
default:
|
||||
return nil, StructuralError{"cannot represent time as UTCTime", fieldName}
|
||||
}
|
||||
|
||||
return appendTimeCommon(dst, t), nil
|
||||
}
|
||||
|
||||
func appendGeneralizedTime(dst []byte, t time.Time, fieldName string) (ret []byte, err error) {
|
||||
year := t.Year()
|
||||
if year < 0 || year > 9999 {
|
||||
return nil, StructuralError{"cannot represent time as GeneralizedTime", fieldName}
|
||||
}
|
||||
|
||||
dst = appendFourDigits(dst, year)
|
||||
|
||||
return appendTimeCommon(dst, t), nil
|
||||
}
|
||||
|
||||
func appendTimeCommon(dst []byte, t time.Time) []byte {
|
||||
_, month, day := t.Date()
|
||||
|
||||
dst = appendTwoDigits(dst, int(month))
|
||||
dst = appendTwoDigits(dst, day)
|
||||
|
||||
hour, min, sec := t.Clock()
|
||||
|
||||
dst = appendTwoDigits(dst, hour)
|
||||
dst = appendTwoDigits(dst, min)
|
||||
dst = appendTwoDigits(dst, sec)
|
||||
|
||||
_, offset := t.Zone()
|
||||
|
||||
switch {
|
||||
case offset/60 == 0:
|
||||
return append(dst, 'Z')
|
||||
case offset > 0:
|
||||
dst = append(dst, '+')
|
||||
case offset < 0:
|
||||
dst = append(dst, '-')
|
||||
}
|
||||
|
||||
offsetMinutes := offset / 60
|
||||
if offsetMinutes < 0 {
|
||||
offsetMinutes = -offsetMinutes
|
||||
}
|
||||
|
||||
dst = appendTwoDigits(dst, offsetMinutes/60)
|
||||
dst = appendTwoDigits(dst, offsetMinutes%60)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
func stripTagAndLength(in []byte) []byte {
|
||||
_, offset, err := parseTagAndLength(in, 0, "")
|
||||
if err != nil {
|
||||
return in
|
||||
}
|
||||
return in[offset:]
|
||||
}
|
||||
|
||||
func makeBody(value reflect.Value, params fieldParameters) (e encoder, err error) {
|
||||
switch value.Type() {
|
||||
case flagType:
|
||||
return bytesEncoder(nil), nil
|
||||
case timeType:
|
||||
t := value.Interface().(time.Time)
|
||||
if params.timeType == TagGeneralizedTime || outsideUTCRange(t) {
|
||||
return makeGeneralizedTime(t, params.name)
|
||||
}
|
||||
return makeUTCTime(t, params.name)
|
||||
case bitStringType:
|
||||
return bitStringEncoder(value.Interface().(BitString)), nil
|
||||
case objectIdentifierType:
|
||||
return makeObjectIdentifier(value.Interface().(ObjectIdentifier), params.name)
|
||||
case bigIntType:
|
||||
return makeBigInt(value.Interface().(*big.Int), params.name)
|
||||
}
|
||||
|
||||
switch v := value; v.Kind() {
|
||||
case reflect.Bool:
|
||||
if v.Bool() {
|
||||
return byteFFEncoder, nil
|
||||
}
|
||||
return byte00Encoder, nil
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return int64Encoder(v.Int()), nil
|
||||
case reflect.Struct:
|
||||
t := v.Type()
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
if t.Field(i).PkgPath != "" {
|
||||
return nil, StructuralError{"struct contains unexported fields", t.Field(i).Name}
|
||||
}
|
||||
}
|
||||
|
||||
startingField := 0
|
||||
|
||||
n := t.NumField()
|
||||
if n == 0 {
|
||||
return bytesEncoder(nil), nil
|
||||
}
|
||||
|
||||
// If the first element of the structure is a non-empty
|
||||
// RawContents, then we don't bother serializing the rest.
|
||||
if t.Field(0).Type == rawContentsType {
|
||||
s := v.Field(0)
|
||||
if s.Len() > 0 {
|
||||
bytes := s.Bytes()
|
||||
/* The RawContents will contain the tag and
|
||||
* length fields but we'll also be writing
|
||||
* those ourselves, so we strip them out of
|
||||
* bytes */
|
||||
return bytesEncoder(stripTagAndLength(bytes)), nil
|
||||
}
|
||||
|
||||
startingField = 1
|
||||
}
|
||||
|
||||
switch n1 := n - startingField; n1 {
|
||||
case 0:
|
||||
return bytesEncoder(nil), nil
|
||||
case 1:
|
||||
return makeField(v.Field(startingField), parseFieldParameters(t.Field(startingField).Tag.Get("asn1")))
|
||||
default:
|
||||
m := make([]encoder, n1)
|
||||
for i := 0; i < n1; i++ {
|
||||
m[i], err = makeField(v.Field(i+startingField), parseFieldParameters(t.Field(i+startingField).Tag.Get("asn1")))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return multiEncoder(m), nil
|
||||
}
|
||||
case reflect.Slice:
|
||||
sliceType := v.Type()
|
||||
if sliceType.Elem().Kind() == reflect.Uint8 {
|
||||
return bytesEncoder(v.Bytes()), nil
|
||||
}
|
||||
|
||||
var fp fieldParameters
|
||||
|
||||
switch l := v.Len(); l {
|
||||
case 0:
|
||||
return bytesEncoder(nil), nil
|
||||
case 1:
|
||||
return makeField(v.Index(0), fp)
|
||||
default:
|
||||
m := make([]encoder, l)
|
||||
|
||||
for i := 0; i < l; i++ {
|
||||
m[i], err = makeField(v.Index(i), fp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return multiEncoder(m), nil
|
||||
}
|
||||
case reflect.String:
|
||||
switch params.stringType {
|
||||
case TagIA5String:
|
||||
return makeIA5String(v.String(), params.name)
|
||||
case TagPrintableString:
|
||||
return makePrintableString(v.String(), params.name)
|
||||
case TagNumericString:
|
||||
return makeNumericString(v.String(), params.name)
|
||||
default:
|
||||
return makeUTF8String(v.String()), nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, StructuralError{"unknown Go type", params.name}
|
||||
}
|
||||
|
||||
func makeField(v reflect.Value, params fieldParameters) (e encoder, err error) {
|
||||
if !v.IsValid() {
|
||||
return nil, fmt.Errorf("asn1: cannot marshal nil value")
|
||||
}
|
||||
// If the field is an interface{} then recurse into it.
|
||||
if v.Kind() == reflect.Interface && v.Type().NumMethod() == 0 {
|
||||
return makeField(v.Elem(), params)
|
||||
}
|
||||
|
||||
if v.Kind() == reflect.Slice && v.Len() == 0 && params.omitEmpty {
|
||||
return bytesEncoder(nil), nil
|
||||
}
|
||||
|
||||
if params.optional && params.defaultValue != nil && canHaveDefaultValue(v.Kind()) {
|
||||
defaultValue := reflect.New(v.Type()).Elem()
|
||||
defaultValue.SetInt(*params.defaultValue)
|
||||
|
||||
if reflect.DeepEqual(v.Interface(), defaultValue.Interface()) {
|
||||
return bytesEncoder(nil), nil
|
||||
}
|
||||
}
|
||||
|
||||
// If no default value is given then the zero value for the type is
|
||||
// assumed to be the default value. This isn't obviously the correct
|
||||
// behavior, but it's what Go has traditionally done.
|
||||
if params.optional && params.defaultValue == nil {
|
||||
if reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) {
|
||||
return bytesEncoder(nil), nil
|
||||
}
|
||||
}
|
||||
|
||||
if v.Type() == rawValueType {
|
||||
rv := v.Interface().(RawValue)
|
||||
if len(rv.FullBytes) != 0 {
|
||||
return bytesEncoder(rv.FullBytes), nil
|
||||
}
|
||||
|
||||
t := new(taggedEncoder)
|
||||
|
||||
t.tag = bytesEncoder(appendTagAndLength(t.scratch[:0], tagAndLength{rv.Class, rv.Tag, len(rv.Bytes), rv.IsCompound}))
|
||||
t.body = bytesEncoder(rv.Bytes)
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
matchAny, tag, isCompound, ok := getUniversalType(v.Type())
|
||||
if !ok || matchAny {
|
||||
return nil, StructuralError{fmt.Sprintf("unknown Go type: %v", v.Type()), params.name}
|
||||
}
|
||||
|
||||
if params.timeType != 0 && tag != TagUTCTime {
|
||||
return nil, StructuralError{"explicit time type given to non-time member", params.name}
|
||||
}
|
||||
|
||||
if params.stringType != 0 && tag != TagPrintableString {
|
||||
return nil, StructuralError{"explicit string type given to non-string member", params.name}
|
||||
}
|
||||
|
||||
switch tag {
|
||||
case TagPrintableString:
|
||||
if params.stringType == 0 {
|
||||
// This is a string without an explicit string type. We'll use
|
||||
// a PrintableString if the character set in the string is
|
||||
// sufficiently limited, otherwise we'll use a UTF8String.
|
||||
for _, r := range v.String() {
|
||||
if r >= utf8.RuneSelf || !isPrintable(byte(r), rejectAsterisk, rejectAmpersand) {
|
||||
if !utf8.ValidString(v.String()) {
|
||||
return nil, errors.New("asn1: string not valid UTF-8")
|
||||
}
|
||||
tag = TagUTF8String
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tag = params.stringType
|
||||
}
|
||||
case TagUTCTime:
|
||||
if params.timeType == TagGeneralizedTime || outsideUTCRange(v.Interface().(time.Time)) {
|
||||
tag = TagGeneralizedTime
|
||||
}
|
||||
}
|
||||
|
||||
if params.set {
|
||||
if tag != TagSequence {
|
||||
return nil, StructuralError{"non sequence tagged as set", params.name}
|
||||
}
|
||||
tag = TagSet
|
||||
}
|
||||
|
||||
t := new(taggedEncoder)
|
||||
|
||||
t.body, err = makeBody(v, params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bodyLen := t.body.Len()
|
||||
|
||||
class := ClassUniversal
|
||||
if params.tag != nil {
|
||||
if params.application {
|
||||
class = ClassApplication
|
||||
} else {
|
||||
class = ClassContextSpecific
|
||||
}
|
||||
|
||||
if params.explicit {
|
||||
t.tag = bytesEncoder(appendTagAndLength(t.scratch[:0], tagAndLength{ClassUniversal, tag, bodyLen, isCompound}))
|
||||
|
||||
tt := new(taggedEncoder)
|
||||
|
||||
tt.body = t
|
||||
|
||||
tt.tag = bytesEncoder(appendTagAndLength(tt.scratch[:0], tagAndLength{
|
||||
class: class,
|
||||
tag: *params.tag,
|
||||
length: bodyLen + t.tag.Len(),
|
||||
isCompound: true,
|
||||
}))
|
||||
|
||||
return tt, nil
|
||||
}
|
||||
|
||||
// implicit tag.
|
||||
tag = *params.tag
|
||||
}
|
||||
|
||||
t.tag = bytesEncoder(appendTagAndLength(t.scratch[:0], tagAndLength{class, tag, bodyLen, isCompound}))
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// Marshal returns the ASN.1 encoding of val.
|
||||
//
|
||||
// In addition to the struct tags recognised by Unmarshal, the following can be
|
||||
// used:
|
||||
//
|
||||
// ia5: causes strings to be marshaled as ASN.1, IA5String values
|
||||
// omitempty: causes empty slices to be skipped
|
||||
// printable: causes strings to be marshaled as ASN.1, PrintableString values
|
||||
// utf8: causes strings to be marshaled as ASN.1, UTF8String values
|
||||
// utc: causes time.Time to be marshaled as ASN.1, UTCTime values
|
||||
// generalized: causes time.Time to be marshaled as ASN.1, GeneralizedTime values
|
||||
func Marshal(val interface{}) ([]byte, error) {
|
||||
return MarshalWithParams(val, "")
|
||||
}
|
||||
|
||||
// MarshalWithParams allows field parameters to be specified for the
|
||||
// top-level element. The form of the params is the same as the field tags.
|
||||
func MarshalWithParams(val interface{}, params string) ([]byte, error) {
|
||||
e, err := makeField(reflect.ValueOf(val), parseFieldParameters(params))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b := make([]byte, e.Len())
|
||||
e.Encode(b)
|
||||
return b, nil
|
||||
}
|
263
vendor/github.com/google/certificate-transparency-go/asn1/marshal_test.go
generated
vendored
Normal file
263
vendor/github.com/google/certificate-transparency-go/asn1/marshal_test.go
generated
vendored
Normal file
@ -0,0 +1,263 @@
|
||||
// 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.
|
||||
|
||||
package asn1
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type intStruct struct {
|
||||
A int
|
||||
}
|
||||
|
||||
type twoIntStruct struct {
|
||||
A int
|
||||
B int
|
||||
}
|
||||
|
||||
type bigIntStruct struct {
|
||||
A *big.Int
|
||||
}
|
||||
|
||||
type nestedStruct struct {
|
||||
A intStruct
|
||||
}
|
||||
|
||||
type rawContentsStruct struct {
|
||||
Raw RawContent
|
||||
A int
|
||||
}
|
||||
|
||||
type implicitTagTest struct {
|
||||
A int `asn1:"implicit,tag:5"`
|
||||
}
|
||||
|
||||
type explicitTagTest struct {
|
||||
A int `asn1:"explicit,tag:5"`
|
||||
}
|
||||
|
||||
type flagTest struct {
|
||||
A Flag `asn1:"tag:0,optional"`
|
||||
}
|
||||
|
||||
type generalizedTimeTest struct {
|
||||
A time.Time `asn1:"generalized"`
|
||||
}
|
||||
|
||||
type ia5StringTest struct {
|
||||
A string `asn1:"ia5"`
|
||||
}
|
||||
|
||||
type printableStringTest struct {
|
||||
A string `asn1:"printable"`
|
||||
}
|
||||
|
||||
type genericStringTest struct {
|
||||
A string
|
||||
}
|
||||
|
||||
type optionalRawValueTest struct {
|
||||
A RawValue `asn1:"optional"`
|
||||
}
|
||||
|
||||
type omitEmptyTest struct {
|
||||
A []string `asn1:"omitempty"`
|
||||
}
|
||||
|
||||
type defaultTest struct {
|
||||
A int `asn1:"optional,default:1"`
|
||||
}
|
||||
|
||||
type applicationTest struct {
|
||||
A int `asn1:"application,tag:0"`
|
||||
B int `asn1:"application,tag:1,explicit"`
|
||||
}
|
||||
|
||||
type numericStringTest struct {
|
||||
A string `asn1:"numeric"`
|
||||
}
|
||||
|
||||
type testAuthKeyID struct {
|
||||
ID []byte `asn1:"optional,tag:0"`
|
||||
Issuer RawValue `asn1:"optional,tag:1"`
|
||||
SerialNumber *big.Int `asn1:"optional,tag:2"`
|
||||
}
|
||||
|
||||
type testSET []int
|
||||
|
||||
var PST = time.FixedZone("PST", -8*60*60)
|
||||
|
||||
type marshalTest struct {
|
||||
in interface{}
|
||||
out string // hex encoded
|
||||
}
|
||||
|
||||
func farFuture() time.Time {
|
||||
t, err := time.Parse(time.RFC3339, "2100-04-05T12:01:01Z")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
var marshalTests = []marshalTest{
|
||||
{10, "02010a"},
|
||||
{127, "02017f"},
|
||||
{128, "02020080"},
|
||||
{-128, "020180"},
|
||||
{-129, "0202ff7f"},
|
||||
{intStruct{64}, "3003020140"},
|
||||
{bigIntStruct{big.NewInt(0x123456)}, "30050203123456"},
|
||||
{twoIntStruct{64, 65}, "3006020140020141"},
|
||||
{nestedStruct{intStruct{127}}, "3005300302017f"},
|
||||
{[]byte{1, 2, 3}, "0403010203"},
|
||||
{implicitTagTest{64}, "3003850140"},
|
||||
{explicitTagTest{64}, "3005a503020140"},
|
||||
{flagTest{true}, "30028000"},
|
||||
{flagTest{false}, "3000"},
|
||||
{time.Unix(0, 0).UTC(), "170d3730303130313030303030305a"},
|
||||
{time.Unix(1258325776, 0).UTC(), "170d3039313131353232353631365a"},
|
||||
{time.Unix(1258325776, 0).In(PST), "17113039313131353134353631362d30383030"},
|
||||
{farFuture(), "180f32313030303430353132303130315a"},
|
||||
{generalizedTimeTest{time.Unix(1258325776, 0).UTC()}, "3011180f32303039313131353232353631365a"},
|
||||
{BitString{[]byte{0x80}, 1}, "03020780"},
|
||||
{BitString{[]byte{0x81, 0xf0}, 12}, "03030481f0"},
|
||||
{ObjectIdentifier([]int{1, 2, 3, 4}), "06032a0304"},
|
||||
{ObjectIdentifier([]int{1, 2, 840, 133549, 1, 1, 5}), "06092a864888932d010105"},
|
||||
{ObjectIdentifier([]int{2, 100, 3}), "0603813403"},
|
||||
{"test", "130474657374"},
|
||||
{
|
||||
"" +
|
||||
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
|
||||
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
|
||||
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
|
||||
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", // This is 127 times 'x'
|
||||
"137f" +
|
||||
"7878787878787878787878787878787878787878787878787878787878787878" +
|
||||
"7878787878787878787878787878787878787878787878787878787878787878" +
|
||||
"7878787878787878787878787878787878787878787878787878787878787878" +
|
||||
"78787878787878787878787878787878787878787878787878787878787878",
|
||||
},
|
||||
{
|
||||
"" +
|
||||
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
|
||||
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
|
||||
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
|
||||
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", // This is 128 times 'x'
|
||||
"138180" +
|
||||
"7878787878787878787878787878787878787878787878787878787878787878" +
|
||||
"7878787878787878787878787878787878787878787878787878787878787878" +
|
||||
"7878787878787878787878787878787878787878787878787878787878787878" +
|
||||
"7878787878787878787878787878787878787878787878787878787878787878",
|
||||
},
|
||||
{ia5StringTest{"test"}, "3006160474657374"},
|
||||
{optionalRawValueTest{}, "3000"},
|
||||
{printableStringTest{"test"}, "3006130474657374"},
|
||||
{printableStringTest{"test*"}, "30071305746573742a"},
|
||||
{genericStringTest{"test"}, "3006130474657374"},
|
||||
{genericStringTest{"test*"}, "30070c05746573742a"},
|
||||
{genericStringTest{"test&"}, "30070c057465737426"},
|
||||
{rawContentsStruct{nil, 64}, "3003020140"},
|
||||
{rawContentsStruct{[]byte{0x30, 3, 1, 2, 3}, 64}, "3003010203"},
|
||||
{RawValue{Tag: 1, Class: 2, IsCompound: false, Bytes: []byte{1, 2, 3}}, "8103010203"},
|
||||
{testSET([]int{10}), "310302010a"},
|
||||
{omitEmptyTest{[]string{}}, "3000"},
|
||||
{omitEmptyTest{[]string{"1"}}, "30053003130131"},
|
||||
{"Σ", "0c02cea3"},
|
||||
{defaultTest{0}, "3003020100"},
|
||||
{defaultTest{1}, "3000"},
|
||||
{defaultTest{2}, "3003020102"},
|
||||
{applicationTest{1, 2}, "30084001016103020102"},
|
||||
{numericStringTest{"1 9"}, "30051203312039"},
|
||||
{testAuthKeyID{ID: []byte{0x01, 0x02, 0x03, 0x04}, SerialNumber: big.NewInt(0x12233445566)}, "300e8004010203048206012233445566"},
|
||||
{testAuthKeyID{ID: []byte{0x01, 0x02, 0x03, 0x04}}, "3006800401020304"},
|
||||
}
|
||||
|
||||
func TestMarshal(t *testing.T) {
|
||||
for i, test := range marshalTests {
|
||||
data, err := Marshal(test.in)
|
||||
if err != nil {
|
||||
t.Errorf("#%d failed: %s", i, err)
|
||||
}
|
||||
out, _ := hex.DecodeString(test.out)
|
||||
if !bytes.Equal(out, data) {
|
||||
t.Errorf("#%d got: %x want %x\n\t%q\n\t%q", i, data, out, data, out)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type marshalWithParamsTest struct {
|
||||
in interface{}
|
||||
params string
|
||||
out string // hex encoded
|
||||
}
|
||||
|
||||
var marshalWithParamsTests = []marshalWithParamsTest{
|
||||
{intStruct{10}, "set", "310302010a"},
|
||||
{intStruct{10}, "application", "600302010a"},
|
||||
}
|
||||
|
||||
func TestMarshalWithParams(t *testing.T) {
|
||||
for i, test := range marshalWithParamsTests {
|
||||
data, err := MarshalWithParams(test.in, test.params)
|
||||
if err != nil {
|
||||
t.Errorf("#%d failed: %s", i, err)
|
||||
}
|
||||
out, _ := hex.DecodeString(test.out)
|
||||
if !bytes.Equal(out, data) {
|
||||
t.Errorf("#%d got: %x want %x\n\t%q\n\t%q", i, data, out, data, out)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type marshalErrTest struct {
|
||||
in interface{}
|
||||
err string
|
||||
}
|
||||
|
||||
var marshalErrTests = []marshalErrTest{
|
||||
{bigIntStruct{nil}, "empty integer"},
|
||||
{numericStringTest{"a"}, "invalid character"},
|
||||
{ia5StringTest{"\xb0"}, "invalid character"},
|
||||
{printableStringTest{"!"}, "invalid character"},
|
||||
}
|
||||
|
||||
func TestMarshalError(t *testing.T) {
|
||||
for i, test := range marshalErrTests {
|
||||
_, err := Marshal(test.in)
|
||||
if err == nil {
|
||||
t.Errorf("#%d should fail, but success", i)
|
||||
continue
|
||||
}
|
||||
|
||||
if !strings.Contains(err.Error(), test.err) {
|
||||
t.Errorf("#%d got: %v want %v", i, err, test.err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidUTF8(t *testing.T) {
|
||||
_, err := Marshal(string([]byte{0xff, 0xff}))
|
||||
if err == nil {
|
||||
t.Errorf("invalid UTF8 string was accepted")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMarshal(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, test := range marshalTests {
|
||||
Marshal(test.in)
|
||||
}
|
||||
}
|
||||
}
|
17
vendor/github.com/google/certificate-transparency-go/client/configpb/gen.go
generated
vendored
Normal file
17
vendor/github.com/google/certificate-transparency-go/client/configpb/gen.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package configpb
|
||||
|
||||
//go:generate protoc -I=. -I=$GOPATH/src --go_out=:. multilog.proto
|
124
vendor/github.com/google/certificate-transparency-go/client/configpb/multilog.pb.go
generated
vendored
Normal file
124
vendor/github.com/google/certificate-transparency-go/client/configpb/multilog.pb.go
generated
vendored
Normal file
@ -0,0 +1,124 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: multilog.proto
|
||||
|
||||
/*
|
||||
Package configpb is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
multilog.proto
|
||||
|
||||
It has these top-level messages:
|
||||
TemporalLogConfig
|
||||
LogShardConfig
|
||||
*/
|
||||
package configpb
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
import google_protobuf "github.com/golang/protobuf/ptypes/timestamp"
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
// TemporalLogConfig is a set of LogShardConfig messages, whose
|
||||
// time limits should be contiguous.
|
||||
type TemporalLogConfig struct {
|
||||
Shard []*LogShardConfig `protobuf:"bytes,1,rep,name=shard" json:"shard,omitempty"`
|
||||
}
|
||||
|
||||
func (m *TemporalLogConfig) Reset() { *m = TemporalLogConfig{} }
|
||||
func (m *TemporalLogConfig) String() string { return proto.CompactTextString(m) }
|
||||
func (*TemporalLogConfig) ProtoMessage() {}
|
||||
func (*TemporalLogConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||
|
||||
func (m *TemporalLogConfig) GetShard() []*LogShardConfig {
|
||||
if m != nil {
|
||||
return m.Shard
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LogShardConfig describes the acceptable date range for a single shard of a temporal
|
||||
// log.
|
||||
type LogShardConfig struct {
|
||||
Uri string `protobuf:"bytes,1,opt,name=uri" json:"uri,omitempty"`
|
||||
// The log's public key in DER-encoded PKIX form.
|
||||
PublicKeyDer []byte `protobuf:"bytes,2,opt,name=public_key_der,json=publicKeyDer,proto3" json:"public_key_der,omitempty"`
|
||||
// not_after_start defines the start of the range of acceptable NotAfter
|
||||
// values, inclusive.
|
||||
// Leaving this unset implies no lower bound to the range.
|
||||
NotAfterStart *google_protobuf.Timestamp `protobuf:"bytes,3,opt,name=not_after_start,json=notAfterStart" json:"not_after_start,omitempty"`
|
||||
// not_after_limit defines the end of the range of acceptable NotAfter values,
|
||||
// exclusive.
|
||||
// Leaving this unset implies no upper bound to the range.
|
||||
NotAfterLimit *google_protobuf.Timestamp `protobuf:"bytes,4,opt,name=not_after_limit,json=notAfterLimit" json:"not_after_limit,omitempty"`
|
||||
}
|
||||
|
||||
func (m *LogShardConfig) Reset() { *m = LogShardConfig{} }
|
||||
func (m *LogShardConfig) String() string { return proto.CompactTextString(m) }
|
||||
func (*LogShardConfig) ProtoMessage() {}
|
||||
func (*LogShardConfig) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
|
||||
|
||||
func (m *LogShardConfig) GetUri() string {
|
||||
if m != nil {
|
||||
return m.Uri
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *LogShardConfig) GetPublicKeyDer() []byte {
|
||||
if m != nil {
|
||||
return m.PublicKeyDer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *LogShardConfig) GetNotAfterStart() *google_protobuf.Timestamp {
|
||||
if m != nil {
|
||||
return m.NotAfterStart
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *LogShardConfig) GetNotAfterLimit() *google_protobuf.Timestamp {
|
||||
if m != nil {
|
||||
return m.NotAfterLimit
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*TemporalLogConfig)(nil), "configpb.TemporalLogConfig")
|
||||
proto.RegisterType((*LogShardConfig)(nil), "configpb.LogShardConfig")
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("multilog.proto", fileDescriptor0) }
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 241 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x8f, 0xb1, 0x4e, 0xc3, 0x30,
|
||||
0x14, 0x45, 0x65, 0x02, 0x08, 0xdc, 0x12, 0xc0, 0x93, 0xd5, 0x85, 0xa8, 0x62, 0xc8, 0xe4, 0x4a,
|
||||
0xe5, 0x0b, 0xa0, 0x6c, 0x64, 0x4a, 0xbb, 0x47, 0x4e, 0xeb, 0x18, 0x0b, 0x3b, 0xcf, 0x72, 0x5e,
|
||||
0x86, 0xfe, 0x25, 0x9f, 0x84, 0x1c, 0x2b, 0x43, 0x37, 0xb6, 0xa7, 0x77, 0xcf, 0xb9, 0xd2, 0xa5,
|
||||
0xb9, 0x1b, 0x2d, 0x1a, 0x0b, 0x5a, 0xf8, 0x00, 0x08, 0xec, 0xee, 0x08, 0x7d, 0x67, 0xb4, 0x6f,
|
||||
0x57, 0x2f, 0x1a, 0x40, 0x5b, 0xb5, 0x99, 0xfe, 0xed, 0xd8, 0x6d, 0xd0, 0x38, 0x35, 0xa0, 0x74,
|
||||
0x3e, 0xa1, 0xeb, 0x1d, 0x7d, 0x3e, 0x28, 0xe7, 0x21, 0x48, 0x5b, 0x81, 0xde, 0x4d, 0x1e, 0x13,
|
||||
0xf4, 0x66, 0xf8, 0x96, 0xe1, 0xc4, 0x49, 0x91, 0x95, 0x8b, 0x2d, 0x17, 0x73, 0x9f, 0xa8, 0x40,
|
||||
0xef, 0x63, 0x92, 0xc0, 0x3a, 0x61, 0xeb, 0x5f, 0x42, 0xf3, 0xcb, 0x84, 0x3d, 0xd1, 0x6c, 0x0c,
|
||||
0x86, 0x93, 0x82, 0x94, 0xf7, 0x75, 0x3c, 0xd9, 0x2b, 0xcd, 0xfd, 0xd8, 0x5a, 0x73, 0x6c, 0x7e,
|
||||
0xd4, 0xb9, 0x39, 0xa9, 0xc0, 0xaf, 0x0a, 0x52, 0x2e, 0xeb, 0x65, 0xfa, 0x7e, 0xa9, 0xf3, 0xa7,
|
||||
0x0a, 0xec, 0x83, 0x3e, 0xf6, 0x80, 0x8d, 0xec, 0x50, 0x85, 0x66, 0x40, 0x19, 0x90, 0x67, 0x05,
|
||||
0x29, 0x17, 0xdb, 0x95, 0x48, 0x53, 0xc4, 0x3c, 0x45, 0x1c, 0xe6, 0x29, 0xf5, 0x43, 0x0f, 0xf8,
|
||||
0x1e, 0x8d, 0x7d, 0x14, 0x2e, 0x3b, 0xac, 0x71, 0x06, 0xf9, 0xf5, 0xff, 0x3b, 0xaa, 0x28, 0xb4,
|
||||
0xb7, 0x13, 0xf2, 0xf6, 0x17, 0x00, 0x00, 0xff, 0xff, 0xf8, 0xd9, 0x50, 0x5b, 0x5b, 0x01, 0x00,
|
||||
0x00,
|
||||
}
|
43
vendor/github.com/google/certificate-transparency-go/client/configpb/multilog.proto
generated
vendored
Normal file
43
vendor/github.com/google/certificate-transparency-go/client/configpb/multilog.proto
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package configpb;
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
|
||||
// TemporalLogConfig is a set of LogShardConfig messages, whose
|
||||
// time limits should be contiguous.
|
||||
message TemporalLogConfig {
|
||||
repeated LogShardConfig shard = 1;
|
||||
}
|
||||
|
||||
// LogShardConfig describes the acceptable date range for a single shard of a temporal
|
||||
// log.
|
||||
message LogShardConfig {
|
||||
string uri = 1;
|
||||
|
||||
// The log's public key in DER-encoded PKIX form.
|
||||
bytes public_key_der = 2;
|
||||
|
||||
// not_after_start defines the start of the range of acceptable NotAfter
|
||||
// values, inclusive.
|
||||
// Leaving this unset implies no lower bound to the range.
|
||||
google.protobuf.Timestamp not_after_start = 3;
|
||||
// not_after_limit defines the end of the range of acceptable NotAfter values,
|
||||
// exclusive.
|
||||
// Leaving this unset implies no upper bound to the range.
|
||||
google.protobuf.Timestamp not_after_limit = 4;
|
||||
}
|
75
vendor/github.com/google/certificate-transparency-go/client/getentries.go
generated
vendored
Normal file
75
vendor/github.com/google/certificate-transparency-go/client/getentries.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
ct "github.com/google/certificate-transparency-go"
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
)
|
||||
|
||||
// GetRawEntries exposes the /ct/v1/get-entries result with only the JSON parsing done.
|
||||
func (c *LogClient) GetRawEntries(ctx context.Context, start, end int64) (*ct.GetEntriesResponse, error) {
|
||||
if end < 0 {
|
||||
return nil, errors.New("end should be >= 0")
|
||||
}
|
||||
if end < start {
|
||||
return nil, errors.New("start should be <= end")
|
||||
}
|
||||
|
||||
params := map[string]string{
|
||||
"start": strconv.FormatInt(start, 10),
|
||||
"end": strconv.FormatInt(end, 10),
|
||||
}
|
||||
if ctx == nil {
|
||||
ctx = context.TODO()
|
||||
}
|
||||
|
||||
var resp ct.GetEntriesResponse
|
||||
httpRsp, body, err := c.GetAndParse(ctx, ct.GetEntriesPath, params, &resp)
|
||||
if err != nil {
|
||||
if httpRsp != nil {
|
||||
return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
// GetEntries attempts to retrieve the entries in the sequence [start, end] from the CT log server
|
||||
// (RFC6962 s4.6) as parsed [pre-]certificates for convenience, held in a slice of ct.LogEntry structures.
|
||||
// However, this does mean that any certificate parsing failures will cause a failure of the whole
|
||||
// retrieval operation; for more robust retrieval of parsed certificates, use GetRawEntries() and invoke
|
||||
// ct.LogEntryFromLeaf() on each individual entry.
|
||||
func (c *LogClient) GetEntries(ctx context.Context, start, end int64) ([]ct.LogEntry, error) {
|
||||
resp, err := c.GetRawEntries(ctx, start, end)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entries := make([]ct.LogEntry, len(resp.Entries))
|
||||
for i, entry := range resp.Entries {
|
||||
index := start + int64(i)
|
||||
logEntry, err := ct.LogEntryFromLeaf(index, &entry)
|
||||
if _, ok := err.(x509.NonFatalErrors); !ok && err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entries[i] = *logEntry
|
||||
}
|
||||
return entries, nil
|
||||
}
|
283
vendor/github.com/google/certificate-transparency-go/client/logclient.go
generated
vendored
Normal file
283
vendor/github.com/google/certificate-transparency-go/client/logclient.go
generated
vendored
Normal file
@ -0,0 +1,283 @@
|
||||
// Copyright 2014 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package client is a CT log client implementation and contains types and code
|
||||
// for interacting with RFC6962-compliant CT Log instances.
|
||||
// See http://tools.ietf.org/html/rfc6962 for details
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
ct "github.com/google/certificate-transparency-go"
|
||||
"github.com/google/certificate-transparency-go/jsonclient"
|
||||
"github.com/google/certificate-transparency-go/tls"
|
||||
)
|
||||
|
||||
// LogClient represents a client for a given CT Log instance
|
||||
type LogClient struct {
|
||||
jsonclient.JSONClient
|
||||
}
|
||||
|
||||
// New constructs a new LogClient instance.
|
||||
// |uri| is the base URI of the CT log instance to interact with, e.g.
|
||||
// http://ct.googleapis.com/pilot
|
||||
// |hc| is the underlying client to be used for HTTP requests to the CT log.
|
||||
// |opts| can be used to provide a customer logger interface and a public key
|
||||
// for signature verification.
|
||||
func New(uri string, hc *http.Client, opts jsonclient.Options) (*LogClient, error) {
|
||||
logClient, err := jsonclient.New(uri, hc, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &LogClient{*logClient}, err
|
||||
}
|
||||
|
||||
// RspError represents an error that occurred when processing a response from a server,
|
||||
// and also includes key details from the http.Response that triggered the error.
|
||||
type RspError struct {
|
||||
Err error
|
||||
StatusCode int
|
||||
Body []byte
|
||||
}
|
||||
|
||||
// Error formats the RspError instance, focusing on the error.
|
||||
func (e RspError) Error() string {
|
||||
return e.Err.Error()
|
||||
}
|
||||
|
||||
// Attempts to add |chain| to the log, using the api end-point specified by
|
||||
// |path|. If provided context expires before submission is complete an
|
||||
// error will be returned.
|
||||
func (c *LogClient) addChainWithRetry(ctx context.Context, ctype ct.LogEntryType, path string, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) {
|
||||
var resp ct.AddChainResponse
|
||||
var req ct.AddChainRequest
|
||||
for _, link := range chain {
|
||||
req.Chain = append(req.Chain, link.Data)
|
||||
}
|
||||
|
||||
httpRsp, body, err := c.PostAndParseWithRetry(ctx, path, &req, &resp)
|
||||
if err != nil {
|
||||
if httpRsp != nil {
|
||||
return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ds ct.DigitallySigned
|
||||
if rest, err := tls.Unmarshal(resp.Signature, &ds); err != nil {
|
||||
return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body}
|
||||
} else if len(rest) > 0 {
|
||||
return nil, RspError{
|
||||
Err: fmt.Errorf("trailing data (%d bytes) after DigitallySigned", len(rest)),
|
||||
StatusCode: httpRsp.StatusCode,
|
||||
Body: body,
|
||||
}
|
||||
}
|
||||
|
||||
exts, err := base64.StdEncoding.DecodeString(resp.Extensions)
|
||||
if err != nil {
|
||||
return nil, RspError{
|
||||
Err: fmt.Errorf("invalid base64 data in Extensions (%q): %v", resp.Extensions, err),
|
||||
StatusCode: httpRsp.StatusCode,
|
||||
Body: body,
|
||||
}
|
||||
}
|
||||
|
||||
var logID ct.LogID
|
||||
copy(logID.KeyID[:], resp.ID)
|
||||
sct := &ct.SignedCertificateTimestamp{
|
||||
SCTVersion: resp.SCTVersion,
|
||||
LogID: logID,
|
||||
Timestamp: resp.Timestamp,
|
||||
Extensions: ct.CTExtensions(exts),
|
||||
Signature: ds,
|
||||
}
|
||||
if err := c.VerifySCTSignature(*sct, ctype, chain); err != nil {
|
||||
return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body}
|
||||
}
|
||||
return sct, nil
|
||||
}
|
||||
|
||||
// AddChain adds the (DER represented) X509 |chain| to the log.
|
||||
func (c *LogClient) AddChain(ctx context.Context, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) {
|
||||
return c.addChainWithRetry(ctx, ct.X509LogEntryType, ct.AddChainPath, chain)
|
||||
}
|
||||
|
||||
// AddPreChain adds the (DER represented) Precertificate |chain| to the log.
|
||||
func (c *LogClient) AddPreChain(ctx context.Context, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) {
|
||||
return c.addChainWithRetry(ctx, ct.PrecertLogEntryType, ct.AddPreChainPath, chain)
|
||||
}
|
||||
|
||||
// AddJSON submits arbitrary data to to XJSON server.
|
||||
func (c *LogClient) AddJSON(ctx context.Context, data interface{}) (*ct.SignedCertificateTimestamp, error) {
|
||||
req := ct.AddJSONRequest{Data: data}
|
||||
var resp ct.AddChainResponse
|
||||
httpRsp, body, err := c.PostAndParse(ctx, ct.AddJSONPath, &req, &resp)
|
||||
if err != nil {
|
||||
if httpRsp != nil {
|
||||
return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
var ds ct.DigitallySigned
|
||||
if rest, err := tls.Unmarshal(resp.Signature, &ds); err != nil {
|
||||
return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body}
|
||||
} else if len(rest) > 0 {
|
||||
return nil, RspError{
|
||||
Err: fmt.Errorf("trailing data (%d bytes) after DigitallySigned", len(rest)),
|
||||
StatusCode: httpRsp.StatusCode,
|
||||
Body: body,
|
||||
}
|
||||
}
|
||||
var logID ct.LogID
|
||||
copy(logID.KeyID[:], resp.ID)
|
||||
return &ct.SignedCertificateTimestamp{
|
||||
SCTVersion: resp.SCTVersion,
|
||||
LogID: logID,
|
||||
Timestamp: resp.Timestamp,
|
||||
Extensions: ct.CTExtensions(resp.Extensions),
|
||||
Signature: ds,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetSTH retrieves the current STH from the log.
|
||||
// Returns a populated SignedTreeHead, or a non-nil error (which may be of type
|
||||
// RspError if a raw http.Response is available).
|
||||
func (c *LogClient) GetSTH(ctx context.Context) (*ct.SignedTreeHead, error) {
|
||||
var resp ct.GetSTHResponse
|
||||
httpRsp, body, err := c.GetAndParse(ctx, ct.GetSTHPath, nil, &resp)
|
||||
if err != nil {
|
||||
if httpRsp != nil {
|
||||
return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
sth := ct.SignedTreeHead{
|
||||
TreeSize: resp.TreeSize,
|
||||
Timestamp: resp.Timestamp,
|
||||
}
|
||||
|
||||
if len(resp.SHA256RootHash) != sha256.Size {
|
||||
return nil, RspError{
|
||||
Err: fmt.Errorf("sha256_root_hash is invalid length, expected %d got %d", sha256.Size, len(resp.SHA256RootHash)),
|
||||
StatusCode: httpRsp.StatusCode,
|
||||
Body: body,
|
||||
}
|
||||
}
|
||||
copy(sth.SHA256RootHash[:], resp.SHA256RootHash)
|
||||
|
||||
var ds ct.DigitallySigned
|
||||
if rest, err := tls.Unmarshal(resp.TreeHeadSignature, &ds); err != nil {
|
||||
return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body}
|
||||
} else if len(rest) > 0 {
|
||||
return nil, RspError{
|
||||
Err: fmt.Errorf("trailing data (%d bytes) after DigitallySigned", len(rest)),
|
||||
StatusCode: httpRsp.StatusCode,
|
||||
Body: body,
|
||||
}
|
||||
}
|
||||
sth.TreeHeadSignature = ds
|
||||
if err := c.VerifySTHSignature(sth); err != nil {
|
||||
return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body}
|
||||
}
|
||||
return &sth, nil
|
||||
}
|
||||
|
||||
// VerifySTHSignature checks the signature in sth, returning any error encountered or nil if verification is
|
||||
// successful.
|
||||
func (c *LogClient) VerifySTHSignature(sth ct.SignedTreeHead) error {
|
||||
if c.Verifier == nil {
|
||||
// Can't verify signatures without a verifier
|
||||
return nil
|
||||
}
|
||||
return c.Verifier.VerifySTHSignature(sth)
|
||||
}
|
||||
|
||||
// VerifySCTSignature checks the signature in sct for the given LogEntryType, with associated certificate chain.
|
||||
func (c *LogClient) VerifySCTSignature(sct ct.SignedCertificateTimestamp, ctype ct.LogEntryType, certData []ct.ASN1Cert) error {
|
||||
if c.Verifier == nil {
|
||||
// Can't verify signatures without a verifier
|
||||
return nil
|
||||
}
|
||||
leaf, err := ct.MerkleTreeLeafFromRawChain(certData, ctype, sct.Timestamp)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to build MerkleTreeLeaf: %v", err)
|
||||
}
|
||||
entry := ct.LogEntry{Leaf: *leaf}
|
||||
return c.Verifier.VerifySCTSignature(sct, entry)
|
||||
}
|
||||
|
||||
// GetSTHConsistency retrieves the consistency proof between two snapshots.
|
||||
func (c *LogClient) GetSTHConsistency(ctx context.Context, first, second uint64) ([][]byte, error) {
|
||||
base10 := 10
|
||||
params := map[string]string{
|
||||
"first": strconv.FormatUint(first, base10),
|
||||
"second": strconv.FormatUint(second, base10),
|
||||
}
|
||||
var resp ct.GetSTHConsistencyResponse
|
||||
httpRsp, body, err := c.GetAndParse(ctx, ct.GetSTHConsistencyPath, params, &resp)
|
||||
if err != nil {
|
||||
if httpRsp != nil {
|
||||
return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return resp.Consistency, nil
|
||||
}
|
||||
|
||||
// GetProofByHash returns an audit path for the hash of an SCT.
|
||||
func (c *LogClient) GetProofByHash(ctx context.Context, hash []byte, treeSize uint64) (*ct.GetProofByHashResponse, error) {
|
||||
b64Hash := base64.StdEncoding.EncodeToString(hash)
|
||||
base10 := 10
|
||||
params := map[string]string{
|
||||
"tree_size": strconv.FormatUint(treeSize, base10),
|
||||
"hash": b64Hash,
|
||||
}
|
||||
var resp ct.GetProofByHashResponse
|
||||
httpRsp, body, err := c.GetAndParse(ctx, ct.GetProofByHashPath, params, &resp)
|
||||
if err != nil {
|
||||
if httpRsp != nil {
|
||||
return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
// GetAcceptedRoots retrieves the set of acceptable root certificates for a log.
|
||||
func (c *LogClient) GetAcceptedRoots(ctx context.Context) ([]ct.ASN1Cert, error) {
|
||||
var resp ct.GetRootsResponse
|
||||
httpRsp, body, err := c.GetAndParse(ctx, ct.GetRootsPath, nil, &resp)
|
||||
if err != nil {
|
||||
if httpRsp != nil {
|
||||
return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
var roots []ct.ASN1Cert
|
||||
for _, cert64 := range resp.Certificates {
|
||||
cert, err := base64.StdEncoding.DecodeString(cert64)
|
||||
if err != nil {
|
||||
return nil, RspError{Err: err, StatusCode: httpRsp.StatusCode, Body: body}
|
||||
}
|
||||
roots = append(roots, ct.ASN1Cert{Data: cert})
|
||||
}
|
||||
return roots, nil
|
||||
}
|
713
vendor/github.com/google/certificate-transparency-go/client/logclient_test.go
generated
vendored
Normal file
713
vendor/github.com/google/certificate-transparency-go/client/logclient_test.go
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
221
vendor/github.com/google/certificate-transparency-go/client/multilog.go
generated
vendored
Normal file
221
vendor/github.com/google/certificate-transparency-go/client/multilog.go
generated
vendored
Normal file
@ -0,0 +1,221 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
ct "github.com/google/certificate-transparency-go"
|
||||
"github.com/google/certificate-transparency-go/client/configpb"
|
||||
"github.com/google/certificate-transparency-go/jsonclient"
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
)
|
||||
|
||||
type interval struct {
|
||||
lower *time.Time // nil => no lower bound
|
||||
upper *time.Time // nil => no upper bound
|
||||
}
|
||||
|
||||
// TemporalLogConfigFromFile creates a TemporalLogConfig object from the given
|
||||
// filename, which should contain text-protobuf encoded configuration data.
|
||||
func TemporalLogConfigFromFile(filename string) (*configpb.TemporalLogConfig, error) {
|
||||
if len(filename) == 0 {
|
||||
return nil, errors.New("log config filename empty")
|
||||
}
|
||||
|
||||
cfgText, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read log config: %v", err)
|
||||
}
|
||||
|
||||
var cfg configpb.TemporalLogConfig
|
||||
if err := proto.UnmarshalText(string(cfgText), &cfg); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse log config: %v", err)
|
||||
}
|
||||
|
||||
if len(cfg.Shard) == 0 {
|
||||
return nil, errors.New("empty log config found")
|
||||
}
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
// AddLogClient is an interface that allows adding certificates and pre-certificates to a log.
|
||||
// Both LogClient and TemporalLogClient implement this interface, which allows users to
|
||||
// commonize code for adding certs to normal/temporal logs.
|
||||
type AddLogClient interface {
|
||||
AddChain(ctx context.Context, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error)
|
||||
AddPreChain(ctx context.Context, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error)
|
||||
GetAcceptedRoots(ctx context.Context) ([]ct.ASN1Cert, error)
|
||||
}
|
||||
|
||||
// TemporalLogClient allows [pre-]certificates to be uploaded to a temporal log.
|
||||
type TemporalLogClient struct {
|
||||
Clients []*LogClient
|
||||
intervals []interval
|
||||
}
|
||||
|
||||
// NewTemporalLogClient builds a new client for interacting with a temporal log.
|
||||
// The provided config should be contiguous and chronological.
|
||||
func NewTemporalLogClient(cfg configpb.TemporalLogConfig, hc *http.Client) (*TemporalLogClient, error) {
|
||||
if len(cfg.Shard) == 0 {
|
||||
return nil, errors.New("empty config")
|
||||
}
|
||||
|
||||
overall, err := shardInterval(cfg.Shard[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cfg.Shard[0] invalid: %v", err)
|
||||
}
|
||||
intervals := make([]interval, 0, len(cfg.Shard))
|
||||
intervals = append(intervals, overall)
|
||||
for i := 1; i < len(cfg.Shard); i++ {
|
||||
interval, err := shardInterval(cfg.Shard[i])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cfg.Shard[%d] invalid: %v", i, err)
|
||||
}
|
||||
if overall.upper == nil {
|
||||
return nil, fmt.Errorf("cfg.Shard[%d] extends an interval with no upper bound", i)
|
||||
}
|
||||
if interval.lower == nil {
|
||||
return nil, fmt.Errorf("cfg.Shard[%d] has no lower bound but extends an interval", i)
|
||||
}
|
||||
if !interval.lower.Equal(*overall.upper) {
|
||||
return nil, fmt.Errorf("cfg.Shard[%d] starts at %v but previous interval ended at %v", i, interval.lower, overall.upper)
|
||||
}
|
||||
overall.upper = interval.upper
|
||||
intervals = append(intervals, interval)
|
||||
}
|
||||
clients := make([]*LogClient, 0, len(cfg.Shard))
|
||||
for i, shard := range cfg.Shard {
|
||||
opts := jsonclient.Options{}
|
||||
opts.PublicKeyDER = shard.GetPublicKeyDer()
|
||||
c, err := New(shard.Uri, hc, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create client for cfg.Shard[%d]: %v", i, err)
|
||||
}
|
||||
clients = append(clients, c)
|
||||
}
|
||||
tlc := TemporalLogClient{
|
||||
Clients: clients,
|
||||
intervals: intervals,
|
||||
}
|
||||
return &tlc, nil
|
||||
}
|
||||
|
||||
// GetAcceptedRoots retrieves the set of acceptable root certificates for all
|
||||
// of the shards of a temporal log (i.e. the union).
|
||||
func (tlc *TemporalLogClient) GetAcceptedRoots(ctx context.Context) ([]ct.ASN1Cert, error) {
|
||||
type result struct {
|
||||
roots []ct.ASN1Cert
|
||||
err error
|
||||
}
|
||||
results := make(chan result, len(tlc.Clients))
|
||||
for _, c := range tlc.Clients {
|
||||
go func(c *LogClient) {
|
||||
var r result
|
||||
r.roots, r.err = c.GetAcceptedRoots(ctx)
|
||||
results <- r
|
||||
}(c)
|
||||
}
|
||||
|
||||
var allRoots []ct.ASN1Cert
|
||||
seen := make(map[[sha256.Size]byte]bool)
|
||||
for range tlc.Clients {
|
||||
r := <-results
|
||||
if r.err != nil {
|
||||
return nil, r.err
|
||||
}
|
||||
for _, root := range r.roots {
|
||||
h := sha256.Sum256(root.Data)
|
||||
if seen[h] {
|
||||
continue
|
||||
}
|
||||
seen[h] = true
|
||||
allRoots = append(allRoots, root)
|
||||
}
|
||||
}
|
||||
return allRoots, nil
|
||||
}
|
||||
|
||||
// AddChain adds the (DER represented) X509 chain to the appropriate log.
|
||||
func (tlc *TemporalLogClient) AddChain(ctx context.Context, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) {
|
||||
return tlc.addChain(ctx, ct.X509LogEntryType, ct.AddChainPath, chain)
|
||||
}
|
||||
|
||||
// AddPreChain adds the (DER represented) Precertificate chain to the appropriate log.
|
||||
func (tlc *TemporalLogClient) AddPreChain(ctx context.Context, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) {
|
||||
return tlc.addChain(ctx, ct.PrecertLogEntryType, ct.AddPreChainPath, chain)
|
||||
}
|
||||
|
||||
func (tlc *TemporalLogClient) addChain(ctx context.Context, ctype ct.LogEntryType, path string, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) {
|
||||
// Parse the first entry in the chain
|
||||
if len(chain) == 0 {
|
||||
return nil, errors.New("missing chain")
|
||||
}
|
||||
cert, err := x509.ParseCertificate(chain[0].Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse initial chain entry: %v", err)
|
||||
}
|
||||
cidx, err := tlc.IndexByDate(cert.NotAfter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find log to process cert: %v", err)
|
||||
}
|
||||
return tlc.Clients[cidx].addChainWithRetry(ctx, ctype, path, chain)
|
||||
}
|
||||
|
||||
// IndexByDate returns the index of the Clients entry that is appropriate for the given
|
||||
// date.
|
||||
func (tlc *TemporalLogClient) IndexByDate(when time.Time) (int, error) {
|
||||
for i, interval := range tlc.intervals {
|
||||
if (interval.lower != nil) && when.Before(*interval.lower) {
|
||||
continue
|
||||
}
|
||||
if (interval.upper != nil) && !when.Before(*interval.upper) {
|
||||
continue
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
return -1, fmt.Errorf("no log found encompassing date %v", when)
|
||||
}
|
||||
|
||||
func shardInterval(cfg *configpb.LogShardConfig) (interval, error) {
|
||||
var interval interval
|
||||
if cfg.NotAfterStart != nil {
|
||||
t, err := ptypes.Timestamp(cfg.NotAfterStart)
|
||||
if err != nil {
|
||||
return interval, fmt.Errorf("failed to parse NotAfterStart: %v", err)
|
||||
}
|
||||
interval.lower = &t
|
||||
}
|
||||
if cfg.NotAfterLimit != nil {
|
||||
t, err := ptypes.Timestamp(cfg.NotAfterLimit)
|
||||
if err != nil {
|
||||
return interval, fmt.Errorf("failed to parse NotAfterLimit: %v", err)
|
||||
}
|
||||
interval.upper = &t
|
||||
}
|
||||
|
||||
if interval.lower != nil && interval.upper != nil && !(*interval.lower).Before(*interval.upper) {
|
||||
return interval, errors.New("inverted interval")
|
||||
}
|
||||
return interval, nil
|
||||
}
|
481
vendor/github.com/google/certificate-transparency-go/client/multilog_test.go
generated
vendored
Normal file
481
vendor/github.com/google/certificate-transparency-go/client/multilog_test.go
generated
vendored
Normal file
@ -0,0 +1,481 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/pem"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
tspb "github.com/golang/protobuf/ptypes/timestamp"
|
||||
ct "github.com/google/certificate-transparency-go"
|
||||
"github.com/google/certificate-transparency-go/client/configpb"
|
||||
"github.com/google/certificate-transparency-go/testdata"
|
||||
"github.com/google/certificate-transparency-go/x509util"
|
||||
)
|
||||
|
||||
func TestNewTemporalLogClient(t *testing.T) {
|
||||
ts0, _ := ptypes.TimestampProto(time.Date(2010, 9, 19, 11, 00, 00, 00, time.UTC))
|
||||
ts1, _ := ptypes.TimestampProto(time.Date(2011, 9, 19, 11, 00, 00, 00, time.UTC))
|
||||
ts2, _ := ptypes.TimestampProto(time.Date(2012, 9, 19, 11, 00, 00, 00, time.UTC))
|
||||
ts2_5, _ := ptypes.TimestampProto(time.Date(2013, 3, 19, 11, 00, 00, 00, time.UTC))
|
||||
ts3, _ := ptypes.TimestampProto(time.Date(2013, 9, 19, 11, 00, 00, 00, time.UTC))
|
||||
ts4, _ := ptypes.TimestampProto(time.Date(2014, 9, 19, 11, 00, 00, 00, time.UTC))
|
||||
|
||||
tests := []struct {
|
||||
cfg configpb.TemporalLogConfig
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
cfg: configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: "one", NotAfterStart: nil, NotAfterLimit: nil},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
cfg: configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: "one", NotAfterStart: ts0, NotAfterLimit: ts1},
|
||||
{Uri: "two", NotAfterStart: ts1, NotAfterLimit: ts2},
|
||||
{Uri: "three", NotAfterStart: ts2, NotAfterLimit: ts3},
|
||||
{Uri: "four", NotAfterStart: ts3, NotAfterLimit: ts4},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
cfg: configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: "one", NotAfterStart: nil, NotAfterLimit: ts1},
|
||||
{Uri: "two", NotAfterStart: ts1, NotAfterLimit: ts2},
|
||||
{Uri: "three", NotAfterStart: ts2, NotAfterLimit: ts3},
|
||||
{Uri: "four", NotAfterStart: ts3, NotAfterLimit: ts4},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
cfg: configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: "one", NotAfterStart: nil, NotAfterLimit: ts1},
|
||||
{Uri: "two", NotAfterStart: ts1, NotAfterLimit: ts2},
|
||||
{Uri: "three", NotAfterStart: ts2, NotAfterLimit: ts3},
|
||||
{Uri: "four", NotAfterStart: ts3, NotAfterLimit: nil},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
cfg: configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: "one", NotAfterStart: ts0, NotAfterLimit: ts1},
|
||||
{Uri: "two", NotAfterStart: ts1, NotAfterLimit: ts2},
|
||||
{Uri: "three", NotAfterStart: ts2, NotAfterLimit: ts3},
|
||||
{Uri: "four", NotAfterStart: ts3, NotAfterLimit: nil},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
cfg: configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: "one", NotAfterStart: ts0, NotAfterLimit: ts1},
|
||||
{Uri: "two", NotAfterStart: ts1, NotAfterLimit: ts2},
|
||||
{Uri: "three", NotAfterStart: ts2, NotAfterLimit: ts3},
|
||||
{Uri: "threeA", NotAfterStart: ts2_5, NotAfterLimit: ts3},
|
||||
{Uri: "four", NotAfterStart: ts3, NotAfterLimit: ts4},
|
||||
},
|
||||
},
|
||||
wantErr: "previous interval ended at",
|
||||
},
|
||||
{
|
||||
cfg: configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: "one", NotAfterStart: ts0, NotAfterLimit: ts1},
|
||||
{Uri: "three", NotAfterStart: ts2, NotAfterLimit: ts3},
|
||||
{Uri: "two", NotAfterStart: ts1, NotAfterLimit: ts2},
|
||||
{Uri: "four", NotAfterStart: ts3, NotAfterLimit: nil},
|
||||
},
|
||||
},
|
||||
wantErr: "previous interval ended at",
|
||||
},
|
||||
{
|
||||
cfg: configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: "one", NotAfterStart: nil, NotAfterLimit: ts1},
|
||||
{Uri: "two", NotAfterStart: ts1, NotAfterLimit: ts1},
|
||||
{Uri: "three", NotAfterStart: ts2, NotAfterLimit: ts3},
|
||||
{Uri: "four", NotAfterStart: ts3, NotAfterLimit: ts4},
|
||||
},
|
||||
},
|
||||
wantErr: "inverted",
|
||||
},
|
||||
{
|
||||
cfg: configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: "one", NotAfterStart: ts0, NotAfterLimit: ts1},
|
||||
{Uri: "two", NotAfterStart: ts1, NotAfterLimit: nil},
|
||||
{Uri: "three", NotAfterStart: ts2, NotAfterLimit: ts3},
|
||||
{Uri: "four", NotAfterStart: ts3, NotAfterLimit: nil},
|
||||
},
|
||||
},
|
||||
wantErr: "no upper bound",
|
||||
},
|
||||
{
|
||||
cfg: configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: "one", NotAfterStart: ts0, NotAfterLimit: ts1},
|
||||
{Uri: "two", NotAfterStart: ts1, NotAfterLimit: ts2},
|
||||
{Uri: "three", NotAfterStart: nil, NotAfterLimit: ts3},
|
||||
{Uri: "four", NotAfterStart: ts3, NotAfterLimit: nil},
|
||||
},
|
||||
},
|
||||
wantErr: "has no lower bound",
|
||||
},
|
||||
{
|
||||
wantErr: "empty",
|
||||
},
|
||||
{
|
||||
cfg: configpb.TemporalLogConfig{Shard: []*configpb.LogShardConfig{}},
|
||||
wantErr: "empty",
|
||||
},
|
||||
{
|
||||
cfg: configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: "one", NotAfterStart: ts1, NotAfterLimit: ts1},
|
||||
},
|
||||
},
|
||||
wantErr: "inverted",
|
||||
},
|
||||
{
|
||||
cfg: configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: "one", NotAfterStart: ts2, NotAfterLimit: ts1},
|
||||
},
|
||||
},
|
||||
wantErr: "inverted",
|
||||
},
|
||||
{
|
||||
cfg: configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: "one", NotAfterStart: &tspb.Timestamp{Seconds: -1, Nanos: -1}, NotAfterLimit: ts2},
|
||||
},
|
||||
},
|
||||
wantErr: "failed to parse",
|
||||
},
|
||||
{
|
||||
cfg: configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: "one", NotAfterStart: ts1, NotAfterLimit: &tspb.Timestamp{Seconds: -1, Nanos: -1}},
|
||||
},
|
||||
},
|
||||
wantErr: "failed to parse",
|
||||
},
|
||||
{
|
||||
cfg: configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{
|
||||
Uri: "one",
|
||||
NotAfterStart: nil,
|
||||
NotAfterLimit: nil,
|
||||
PublicKeyDer: []byte{0x01, 0x02},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: "invalid public key",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
_, err := NewTemporalLogClient(test.cfg, nil)
|
||||
if err != nil {
|
||||
if test.wantErr == "" {
|
||||
t.Errorf("NewTemporalLogClient(%+v)=nil,%v; want _,nil", test.cfg, err)
|
||||
} else if !strings.Contains(err.Error(), test.wantErr) {
|
||||
t.Errorf("NewTemporalLogClient(%+v)=nil,%v; want _,%q", test.cfg, err, test.wantErr)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if test.wantErr != "" {
|
||||
t.Errorf("NewTemporalLogClient(%+v)=_, nil; want _,%q", test.cfg, test.wantErr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIndexByDate(t *testing.T) {
|
||||
time0 := time.Date(2010, 9, 19, 11, 00, 00, 00, time.UTC)
|
||||
time1 := time.Date(2011, 9, 19, 11, 00, 00, 00, time.UTC)
|
||||
time1_9 := time.Date(2012, 9, 19, 10, 59, 59, 00, time.UTC)
|
||||
time2 := time.Date(2012, 9, 19, 11, 00, 00, 00, time.UTC)
|
||||
time2_5 := time.Date(2013, 3, 19, 11, 00, 00, 00, time.UTC)
|
||||
time3 := time.Date(2013, 9, 19, 11, 00, 00, 00, time.UTC)
|
||||
time4 := time.Date(2014, 9, 19, 11, 00, 00, 00, time.UTC)
|
||||
|
||||
ts0, _ := ptypes.TimestampProto(time0)
|
||||
ts1, _ := ptypes.TimestampProto(time1)
|
||||
ts2, _ := ptypes.TimestampProto(time2)
|
||||
ts3, _ := ptypes.TimestampProto(time3)
|
||||
ts4, _ := ptypes.TimestampProto(time4)
|
||||
|
||||
allCfg := configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: "zero", NotAfterStart: nil, NotAfterLimit: ts0},
|
||||
{Uri: "one", NotAfterStart: ts0, NotAfterLimit: ts1},
|
||||
{Uri: "two", NotAfterStart: ts1, NotAfterLimit: ts2},
|
||||
{Uri: "three", NotAfterStart: ts2, NotAfterLimit: ts3},
|
||||
{Uri: "four", NotAfterStart: ts3, NotAfterLimit: ts4},
|
||||
{Uri: "five", NotAfterStart: ts4, NotAfterLimit: nil},
|
||||
},
|
||||
}
|
||||
uptoCfg := configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: "zero", NotAfterStart: nil, NotAfterLimit: ts0},
|
||||
{Uri: "one", NotAfterStart: ts0, NotAfterLimit: ts1},
|
||||
{Uri: "two", NotAfterStart: ts1, NotAfterLimit: ts2},
|
||||
{Uri: "three", NotAfterStart: ts2, NotAfterLimit: ts3},
|
||||
{Uri: "four", NotAfterStart: ts3, NotAfterLimit: ts4},
|
||||
},
|
||||
}
|
||||
fromCfg :=
|
||||
configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: "zero", NotAfterStart: ts0, NotAfterLimit: ts1},
|
||||
{Uri: "one", NotAfterStart: ts1, NotAfterLimit: ts2},
|
||||
{Uri: "two", NotAfterStart: ts2, NotAfterLimit: ts3},
|
||||
{Uri: "three", NotAfterStart: ts3, NotAfterLimit: ts4},
|
||||
{Uri: "four", NotAfterStart: ts4, NotAfterLimit: nil},
|
||||
},
|
||||
}
|
||||
boundedCfg :=
|
||||
configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: "zero", NotAfterStart: ts0, NotAfterLimit: ts1},
|
||||
{Uri: "one", NotAfterStart: ts1, NotAfterLimit: ts2},
|
||||
{Uri: "two", NotAfterStart: ts2, NotAfterLimit: ts3},
|
||||
{Uri: "three", NotAfterStart: ts3, NotAfterLimit: ts4},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
cfg configpb.TemporalLogConfig
|
||||
when time.Time
|
||||
want int
|
||||
wantErr bool
|
||||
}{
|
||||
{cfg: allCfg, when: time.Date(2000, 9, 19, 11, 00, 00, 00, time.UTC), want: 0},
|
||||
{cfg: allCfg, when: time0, want: 1},
|
||||
{cfg: allCfg, when: time1, want: 2},
|
||||
{cfg: allCfg, when: time1_9, want: 2},
|
||||
{cfg: allCfg, when: time2, want: 3},
|
||||
{cfg: allCfg, when: time2_5, want: 3},
|
||||
{cfg: allCfg, when: time3, want: 4},
|
||||
{cfg: allCfg, when: time4, want: 5},
|
||||
{cfg: allCfg, when: time.Date(2015, 9, 19, 11, 00, 00, 00, time.UTC), want: 5},
|
||||
|
||||
{cfg: uptoCfg, when: time.Date(2000, 9, 19, 11, 00, 00, 00, time.UTC), want: 0},
|
||||
{cfg: uptoCfg, when: time0, want: 1},
|
||||
{cfg: uptoCfg, when: time1, want: 2},
|
||||
{cfg: uptoCfg, when: time2, want: 3},
|
||||
{cfg: uptoCfg, when: time2_5, want: 3},
|
||||
{cfg: uptoCfg, when: time3, want: 4},
|
||||
{cfg: uptoCfg, when: time4, wantErr: true},
|
||||
{cfg: uptoCfg, when: time.Date(2015, 9, 19, 11, 00, 00, 00, time.UTC), wantErr: true},
|
||||
|
||||
{cfg: fromCfg, when: time.Date(2000, 9, 19, 11, 00, 00, 00, time.UTC), wantErr: true},
|
||||
{cfg: fromCfg, when: time0, want: 0},
|
||||
{cfg: fromCfg, when: time1, want: 1},
|
||||
{cfg: fromCfg, when: time2, want: 2},
|
||||
{cfg: fromCfg, when: time2_5, want: 2},
|
||||
{cfg: fromCfg, when: time3, want: 3},
|
||||
{cfg: fromCfg, when: time4, want: 4},
|
||||
{cfg: fromCfg, when: time.Date(2015, 9, 19, 11, 00, 00, 00, time.UTC), want: 4},
|
||||
|
||||
{cfg: boundedCfg, when: time.Date(2000, 9, 19, 11, 00, 00, 00, time.UTC), wantErr: true},
|
||||
{cfg: boundedCfg, when: time0, want: 0},
|
||||
{cfg: boundedCfg, when: time1, want: 1},
|
||||
{cfg: boundedCfg, when: time2, want: 2},
|
||||
{cfg: boundedCfg, when: time2_5, want: 2},
|
||||
{cfg: boundedCfg, when: time3, want: 3},
|
||||
{cfg: boundedCfg, when: time4, wantErr: true},
|
||||
{cfg: boundedCfg, when: time.Date(2015, 9, 19, 11, 00, 00, 00, time.UTC), wantErr: true},
|
||||
}
|
||||
for _, test := range tests {
|
||||
tlc, err := NewTemporalLogClient(test.cfg, nil)
|
||||
if err != nil {
|
||||
t.Errorf("NewTemporalLogClient(%+v)=nil, %v; want _,nil", test.cfg, err)
|
||||
continue
|
||||
}
|
||||
got, err := tlc.IndexByDate(test.when)
|
||||
if err != nil {
|
||||
if !test.wantErr {
|
||||
t.Errorf("NewTemporalLogClient(%+v).idxByDate()=%d,%v; want %d,nil", test.cfg, got, err, test.want)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if test.wantErr {
|
||||
t.Errorf("NewTemporalLogClient(%+v).idxByDate(%v)=%d, nil; want _, 'no log found'", test.cfg, test.when, got)
|
||||
}
|
||||
if got != test.want {
|
||||
t.Errorf("NewTemporalLogClient(%+v).idxByDate(%v)=%d, nil; want %d, nil", test.cfg, test.when, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemporalAddChain(t *testing.T) {
|
||||
hs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/ct/v1/add-chain":
|
||||
data, _ := sctToJSON(testdata.TestCertProof)
|
||||
w.Write(data)
|
||||
case "/ct/v1/add-pre-chain":
|
||||
data, _ := sctToJSON(testdata.TestPreCertProof)
|
||||
w.Write(data)
|
||||
default:
|
||||
t.Fatalf("Incorrect URL path: %s", r.URL.Path)
|
||||
}
|
||||
}))
|
||||
defer hs.Close()
|
||||
|
||||
cert, err := x509util.CertificateFromPEM(testdata.TestCertPEM)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse certificate from PEM: %v", err)
|
||||
}
|
||||
certChain := []ct.ASN1Cert{{Data: cert.Raw}}
|
||||
precert, err := x509util.CertificateFromPEM(testdata.TestPreCertPEM)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse pre-certificate from PEM: %v", err)
|
||||
}
|
||||
issuer, err := x509util.CertificateFromPEM(testdata.CACertPEM)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse issuer certificate from PEM: %v", err)
|
||||
}
|
||||
precertChain := []ct.ASN1Cert{{Data: precert.Raw}, {Data: issuer.Raw}}
|
||||
// Both have Not After = Jun 1 00:00:00 2022 GMT
|
||||
ts1, _ := ptypes.TimestampProto(time.Date(2022, 5, 19, 11, 00, 00, 00, time.UTC))
|
||||
ts2, _ := ptypes.TimestampProto(time.Date(2022, 6, 19, 11, 00, 00, 00, time.UTC))
|
||||
p, _ := pem.Decode([]byte(testdata.LogPublicKeyPEM))
|
||||
if p == nil {
|
||||
t.Fatalf("Failed to parse public key from PEM: %v", err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
cfg configpb.TemporalLogConfig
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
cfg: configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: hs.URL, NotAfterStart: nil, NotAfterLimit: nil, PublicKeyDer: p.Bytes},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
cfg: configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: hs.URL, NotAfterStart: nil, NotAfterLimit: ts2, PublicKeyDer: p.Bytes},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
cfg: configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: hs.URL, NotAfterStart: ts1, NotAfterLimit: nil, PublicKeyDer: p.Bytes},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
cfg: configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: hs.URL, NotAfterStart: ts1, NotAfterLimit: ts2, PublicKeyDer: p.Bytes},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
cfg: configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: hs.URL, NotAfterStart: nil, NotAfterLimit: ts1, PublicKeyDer: p.Bytes},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
cfg: configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{Uri: hs.URL, NotAfterStart: ts2, NotAfterLimit: nil, PublicKeyDer: p.Bytes},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
for _, test := range tests {
|
||||
tlc, err := NewTemporalLogClient(test.cfg, nil)
|
||||
if err != nil {
|
||||
t.Errorf("NewTemporalLogClient(%+v)=nil, %v; want _,nil", test.cfg, err)
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = tlc.AddChain(ctx, certChain)
|
||||
if err != nil {
|
||||
if !test.wantErr {
|
||||
t.Errorf("AddChain()=nil,%v; want sct,nil", err)
|
||||
}
|
||||
} else if test.wantErr {
|
||||
t.Errorf("AddChain()=sct,nil; want nil,_")
|
||||
}
|
||||
|
||||
_, err = tlc.AddPreChain(ctx, precertChain)
|
||||
if err != nil {
|
||||
if !test.wantErr {
|
||||
t.Errorf("AddPreChain()=nil,%v; want sct,nil", err)
|
||||
}
|
||||
} else if test.wantErr {
|
||||
t.Errorf("AddPreChain()=sct,nil; want nil,_")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemporalAddChainErrors(t *testing.T) {
|
||||
hs := serveSCTAt(t, "/ct/v1/add-chain", testdata.TestCertProof)
|
||||
defer hs.Close()
|
||||
|
||||
cfg := configpb.TemporalLogConfig{
|
||||
Shard: []*configpb.LogShardConfig{
|
||||
{
|
||||
Uri: hs.URL,
|
||||
NotAfterStart: nil,
|
||||
NotAfterLimit: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
tlc, err := NewTemporalLogClient(cfg, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("NewTemporalLogClient(%+v)=nil, %v; want _,nil", cfg, err)
|
||||
}
|
||||
|
||||
_, err = tlc.AddChain(ctx, nil)
|
||||
if err == nil {
|
||||
t.Errorf("AddChain(nil)=sct,nil; want nil, 'missing chain'")
|
||||
}
|
||||
_, err = tlc.AddChain(ctx, []ct.ASN1Cert{{Data: []byte{0x01, 0x02}}})
|
||||
if err == nil {
|
||||
t.Errorf("AddChain(nil)=sct,nil; want nil, 'failed to parse'")
|
||||
}
|
||||
|
||||
}
|
25
vendor/github.com/google/certificate-transparency-go/gometalinter.json
generated
vendored
Normal file
25
vendor/github.com/google/certificate-transparency-go/gometalinter.json
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"Linters": {
|
||||
"license": "./scripts/check_license.sh:PATH:LINE:MESSAGE",
|
||||
"forked": "./scripts/check_forked.sh:PATH:LINE:MESSAGE"
|
||||
},
|
||||
"Enable": [
|
||||
"forked",
|
||||
"gocyclo",
|
||||
"gofmt",
|
||||
"goimports",
|
||||
"golint",
|
||||
"license",
|
||||
"misspell",
|
||||
"vet"
|
||||
],
|
||||
"Exclude": [
|
||||
"x509/",
|
||||
"asn1/",
|
||||
".+\\.pb\\.go",
|
||||
".+\\.pb\\.gw\\.go",
|
||||
"mock_.+\\.go"
|
||||
],
|
||||
"Cyclo": 40,
|
||||
"Vendor": true
|
||||
}
|
72
vendor/github.com/google/certificate-transparency-go/jsonclient/backoff.go
generated
vendored
Normal file
72
vendor/github.com/google/certificate-transparency-go/jsonclient/backoff.go
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package jsonclient
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type backoff struct {
|
||||
mu sync.RWMutex
|
||||
multiplier uint
|
||||
notBefore time.Time
|
||||
}
|
||||
|
||||
const (
|
||||
// maximum backoff is 2^(maxMultiplier-1) = 128 seconds
|
||||
maxMultiplier = 8
|
||||
)
|
||||
|
||||
func (b *backoff) set(override *time.Duration) time.Duration {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
if b.notBefore.After(time.Now()) {
|
||||
if override != nil {
|
||||
// If existing backoff is set but override would be longer than
|
||||
// it then set it to that.
|
||||
notBefore := time.Now().Add(*override)
|
||||
if notBefore.After(b.notBefore) {
|
||||
b.notBefore = notBefore
|
||||
}
|
||||
}
|
||||
return time.Until(b.notBefore)
|
||||
}
|
||||
var wait time.Duration
|
||||
if override != nil {
|
||||
wait = *override
|
||||
} else {
|
||||
if b.multiplier < maxMultiplier {
|
||||
b.multiplier++
|
||||
}
|
||||
wait = time.Second * time.Duration(1<<(b.multiplier-1))
|
||||
}
|
||||
b.notBefore = time.Now().Add(wait)
|
||||
return wait
|
||||
}
|
||||
|
||||
func (b *backoff) decreaseMultiplier() {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
if b.multiplier > 0 {
|
||||
b.multiplier--
|
||||
}
|
||||
}
|
||||
|
||||
func (b *backoff) until() time.Time {
|
||||
b.mu.RLock()
|
||||
defer b.mu.RUnlock()
|
||||
return b.notBefore
|
||||
}
|
117
vendor/github.com/google/certificate-transparency-go/jsonclient/backoff_test.go
generated
vendored
Normal file
117
vendor/github.com/google/certificate-transparency-go/jsonclient/backoff_test.go
generated
vendored
Normal file
@ -0,0 +1,117 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package jsonclient
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
const testLeeway = 25 * time.Microsecond
|
||||
|
||||
func fuzzyTimeEquals(a, b time.Time, leeway time.Duration) bool {
|
||||
diff := math.Abs(float64(a.Sub(b).Nanoseconds()))
|
||||
if diff < float64(leeway.Nanoseconds()) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func fuzzyDurationEquals(a, b time.Duration, leeway time.Duration) bool {
|
||||
diff := math.Abs(float64(a.Nanoseconds() - b.Nanoseconds()))
|
||||
if diff < float64(leeway.Nanoseconds()) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func TestBackoff(t *testing.T) {
|
||||
b := backoff{}
|
||||
|
||||
// Test that the interval increases as expected
|
||||
for i := uint(0); i < maxMultiplier; i++ {
|
||||
n := time.Now()
|
||||
interval := b.set(nil)
|
||||
if interval != time.Second*(1<<i) {
|
||||
t.Fatalf("backoff.set(nil)=%v; want %v", interval, time.Second*(1<<i))
|
||||
}
|
||||
expected := n.Add(interval)
|
||||
until := b.until()
|
||||
if !fuzzyTimeEquals(expected, until, time.Millisecond) {
|
||||
t.Fatalf("backoff.until()=%v; want %v (+ 0-250ms)", expected, until)
|
||||
}
|
||||
|
||||
// reset notBefore
|
||||
b.notBefore = time.Time{}
|
||||
}
|
||||
|
||||
// Test that multiplier doesn't go above maxMultiplier
|
||||
b.multiplier = maxMultiplier
|
||||
b.notBefore = time.Time{}
|
||||
interval := b.set(nil)
|
||||
if b.multiplier > maxMultiplier {
|
||||
t.Fatalf("backoff.multiplier=%v; want %v", b.multiplier, maxMultiplier)
|
||||
}
|
||||
if interval > time.Second*(1<<(maxMultiplier-1)) {
|
||||
t.Fatalf("backoff.set(nil)=%v; want %v", interval, 1<<(maxMultiplier-1)*time.Second)
|
||||
}
|
||||
|
||||
// Test decreaseMultiplier properly decreases the multiplier
|
||||
b.multiplier = 1
|
||||
b.notBefore = time.Time{}
|
||||
b.decreaseMultiplier()
|
||||
if b.multiplier != 0 {
|
||||
t.Fatalf("backoff.multiplier=%v; want %v", b.multiplier, 0)
|
||||
}
|
||||
|
||||
// Test decreaseMultiplier doesn't reduce multiplier below 0
|
||||
b.decreaseMultiplier()
|
||||
if b.multiplier != 0 {
|
||||
t.Fatalf("backoff.multiplier=%v; want %v", b.multiplier, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackoffOverride(t *testing.T) {
|
||||
b := backoff{}
|
||||
for _, tc := range []struct {
|
||||
notBefore time.Time
|
||||
override time.Duration
|
||||
expectedInterval time.Duration
|
||||
}{
|
||||
{
|
||||
notBefore: time.Now().Add(time.Hour),
|
||||
override: time.Second * 1800,
|
||||
expectedInterval: time.Hour,
|
||||
},
|
||||
{
|
||||
notBefore: time.Now().Add(time.Hour),
|
||||
override: time.Second * 7200,
|
||||
expectedInterval: 2 * time.Hour,
|
||||
},
|
||||
{
|
||||
notBefore: time.Time{},
|
||||
override: time.Second * 7200,
|
||||
expectedInterval: 2 * time.Hour,
|
||||
},
|
||||
} {
|
||||
b.multiplier = 0
|
||||
b.notBefore = tc.notBefore
|
||||
interval := b.set(&tc.override)
|
||||
if !fuzzyDurationEquals(tc.expectedInterval, interval, testLeeway) {
|
||||
t.Fatalf("backoff.set(%v)=%v; want %v", tc.override, interval, tc.expectedInterval)
|
||||
}
|
||||
}
|
||||
}
|
289
vendor/github.com/google/certificate-transparency-go/jsonclient/client.go
generated
vendored
Normal file
289
vendor/github.com/google/certificate-transparency-go/jsonclient/client.go
generated
vendored
Normal file
@ -0,0 +1,289 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package jsonclient
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
ct "github.com/google/certificate-transparency-go"
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
"golang.org/x/net/context/ctxhttp"
|
||||
)
|
||||
|
||||
const maxJitter = 250 * time.Millisecond
|
||||
|
||||
type backoffer interface {
|
||||
// set adjusts/increases the current backoff interval (typically on retryable failure);
|
||||
// if the optional parameter is provided, this will be used as the interval if it is greater
|
||||
// than the currently set interval. Returns the current wait period so that it can be
|
||||
// logged along with any error message.
|
||||
set(*time.Duration) time.Duration
|
||||
// decreaseMultiplier reduces the current backoff multiplier, typically on success.
|
||||
decreaseMultiplier()
|
||||
// until returns the time until which the client should wait before making a request,
|
||||
// it may be in the past in which case it should be ignored.
|
||||
until() time.Time
|
||||
}
|
||||
|
||||
// JSONClient provides common functionality for interacting with a JSON server
|
||||
// that uses cryptographic signatures.
|
||||
type JSONClient struct {
|
||||
uri string // the base URI of the server. e.g. http://ct.googleapis/pilot
|
||||
httpClient *http.Client // used to interact with the server via HTTP
|
||||
Verifier *ct.SignatureVerifier // nil for no verification (e.g. no public key available)
|
||||
logger Logger // interface to use for logging warnings and errors
|
||||
backoff backoffer // object used to store and calculate backoff information
|
||||
}
|
||||
|
||||
// Logger is a simple logging interface used to log internal errors and warnings
|
||||
type Logger interface {
|
||||
// Printf formats and logs a message
|
||||
Printf(string, ...interface{})
|
||||
}
|
||||
|
||||
// Options are the options for creating a new JSONClient.
|
||||
type Options struct {
|
||||
// Interface to use for logging warnings and errors, if nil the
|
||||
// standard library log package will be used.
|
||||
Logger Logger
|
||||
// PEM format public key to use for signature verification.
|
||||
PublicKey string
|
||||
// DER format public key to use for signature verification.
|
||||
PublicKeyDER []byte
|
||||
}
|
||||
|
||||
// ParsePublicKey parses and returns the public key contained in opts.
|
||||
// If both opts.PublicKey and opts.PublicKeyDER are set, PublicKeyDER is used.
|
||||
// If neither is set, nil will be returned.
|
||||
func (opts *Options) ParsePublicKey() (crypto.PublicKey, error) {
|
||||
if len(opts.PublicKeyDER) > 0 {
|
||||
return x509.ParsePKIXPublicKey(opts.PublicKeyDER)
|
||||
}
|
||||
|
||||
if opts.PublicKey != "" {
|
||||
pubkey, _ /* keyhash */, rest, err := ct.PublicKeyFromPEM([]byte(opts.PublicKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(rest) > 0 {
|
||||
return nil, errors.New("extra data found after PEM key decoded")
|
||||
}
|
||||
return pubkey, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type basicLogger struct{}
|
||||
|
||||
func (bl *basicLogger) Printf(msg string, args ...interface{}) {
|
||||
log.Printf(msg, args...)
|
||||
}
|
||||
|
||||
// New constructs a new JSONClient instance, for the given base URI, using the
|
||||
// given http.Client object (if provided) and the Options object.
|
||||
// If opts does not specify a public key, signatures will not be verified.
|
||||
func New(uri string, hc *http.Client, opts Options) (*JSONClient, error) {
|
||||
pubkey, err := opts.ParsePublicKey()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid public key: %v", err)
|
||||
}
|
||||
|
||||
var verifier *ct.SignatureVerifier
|
||||
if pubkey != nil {
|
||||
var err error
|
||||
verifier, err = ct.NewSignatureVerifier(pubkey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if hc == nil {
|
||||
hc = new(http.Client)
|
||||
}
|
||||
logger := opts.Logger
|
||||
if logger == nil {
|
||||
logger = &basicLogger{}
|
||||
}
|
||||
return &JSONClient{
|
||||
uri: strings.TrimRight(uri, "/"),
|
||||
httpClient: hc,
|
||||
Verifier: verifier,
|
||||
logger: logger,
|
||||
backoff: &backoff{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetAndParse makes a HTTP GET call to the given path, and attempta to parse
|
||||
// the response as a JSON representation of the rsp structure. Returns the
|
||||
// http.Response, the body of the response, and an error. Note that the
|
||||
// returned http.Response can be non-nil even when an error is returned,
|
||||
// in particular when the HTTP status is not OK or when the JSON parsing fails.
|
||||
func (c *JSONClient) GetAndParse(ctx context.Context, path string, params map[string]string, rsp interface{}) (*http.Response, []byte, error) {
|
||||
if ctx == nil {
|
||||
return nil, nil, errors.New("context.Context required")
|
||||
}
|
||||
// Build a GET request with URL-encoded parameters.
|
||||
vals := url.Values{}
|
||||
for k, v := range params {
|
||||
vals.Add(k, v)
|
||||
}
|
||||
fullURI := fmt.Sprintf("%s%s?%s", c.uri, path, vals.Encode())
|
||||
httpReq, err := http.NewRequest(http.MethodGet, fullURI, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
httpRsp, err := ctxhttp.Do(ctx, c.httpClient, httpReq)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Read everything now so http.Client can reuse the connection.
|
||||
body, err := ioutil.ReadAll(httpRsp.Body)
|
||||
httpRsp.Body.Close()
|
||||
if err != nil {
|
||||
return httpRsp, body, fmt.Errorf("failed to read response body: %v", err)
|
||||
}
|
||||
|
||||
if httpRsp.StatusCode != http.StatusOK {
|
||||
return httpRsp, body, fmt.Errorf("got HTTP Status %q", httpRsp.Status)
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(bytes.NewReader(body)).Decode(rsp); err != nil {
|
||||
return httpRsp, body, err
|
||||
}
|
||||
|
||||
return httpRsp, body, nil
|
||||
}
|
||||
|
||||
// PostAndParse makes a HTTP POST call to the given path, including the request
|
||||
// parameters, and attempts to parse the response as a JSON representation of
|
||||
// the rsp structure. Returns the http.Response, the body of the response, and
|
||||
// an error. Note that the returned http.Response can be non-nil even when an
|
||||
// error is returned, in particular when the HTTP status is not OK or when the
|
||||
// JSON parsing fails.
|
||||
func (c *JSONClient) PostAndParse(ctx context.Context, path string, req, rsp interface{}) (*http.Response, []byte, error) {
|
||||
if ctx == nil {
|
||||
return nil, nil, errors.New("context.Context required")
|
||||
}
|
||||
// Build a POST request with JSON body.
|
||||
postBody, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
fullURI := fmt.Sprintf("%s%s", c.uri, path)
|
||||
httpReq, err := http.NewRequest(http.MethodPost, fullURI, bytes.NewReader(postBody))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
httpReq.Header.Set("Content-Type", "application/json")
|
||||
|
||||
httpRsp, err := ctxhttp.Do(ctx, c.httpClient, httpReq)
|
||||
|
||||
// Read all of the body, if there is one, so that the http.Client can do Keep-Alive.
|
||||
var body []byte
|
||||
if httpRsp != nil {
|
||||
body, err = ioutil.ReadAll(httpRsp.Body)
|
||||
httpRsp.Body.Close()
|
||||
}
|
||||
if err != nil {
|
||||
return httpRsp, body, err
|
||||
}
|
||||
|
||||
if httpRsp.StatusCode == http.StatusOK {
|
||||
if err = json.Unmarshal(body, &rsp); err != nil {
|
||||
return httpRsp, body, err
|
||||
}
|
||||
}
|
||||
return httpRsp, body, nil
|
||||
}
|
||||
|
||||
// waitForBackoff blocks until the defined backoff interval or context has expired, if the returned
|
||||
// not before time is in the past it returns immediately.
|
||||
func (c *JSONClient) waitForBackoff(ctx context.Context) error {
|
||||
dur := time.Until(c.backoff.until().Add(time.Millisecond * time.Duration(rand.Intn(int(maxJitter.Seconds()*1000)))))
|
||||
if dur < 0 {
|
||||
dur = 0
|
||||
}
|
||||
backoffTimer := time.NewTimer(dur)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-backoffTimer.C:
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PostAndParseWithRetry makes a HTTP POST call, but retries (with backoff) on
|
||||
// retriable errors; the caller should set a deadline on the provided context
|
||||
// to prevent infinite retries. Return values are as for PostAndParse.
|
||||
func (c *JSONClient) PostAndParseWithRetry(ctx context.Context, path string, req, rsp interface{}) (*http.Response, []byte, error) {
|
||||
if ctx == nil {
|
||||
return nil, nil, errors.New("context.Context required")
|
||||
}
|
||||
for {
|
||||
httpRsp, body, err := c.PostAndParse(ctx, path, req, rsp)
|
||||
if err != nil {
|
||||
// Don't retry context errors.
|
||||
if err == context.Canceled || err == context.DeadlineExceeded {
|
||||
return nil, nil, err
|
||||
}
|
||||
wait := c.backoff.set(nil)
|
||||
c.logger.Printf("Request failed, backing-off for %s: %s", wait, err)
|
||||
} else {
|
||||
switch {
|
||||
case httpRsp.StatusCode == http.StatusOK:
|
||||
return httpRsp, body, nil
|
||||
case httpRsp.StatusCode == http.StatusRequestTimeout:
|
||||
// Request timeout, retry immediately
|
||||
c.logger.Printf("Request timed out, retrying immediately")
|
||||
case httpRsp.StatusCode == http.StatusServiceUnavailable:
|
||||
var backoff *time.Duration
|
||||
// Retry-After may be either a number of seconds as a int or a RFC 1123
|
||||
// date string (RFC 7231 Section 7.1.3)
|
||||
if retryAfter := httpRsp.Header.Get("Retry-After"); retryAfter != "" {
|
||||
if seconds, err := strconv.Atoi(retryAfter); err == nil {
|
||||
b := time.Duration(seconds) * time.Second
|
||||
backoff = &b
|
||||
} else if date, err := time.Parse(time.RFC1123, retryAfter); err == nil {
|
||||
b := date.Sub(time.Now())
|
||||
backoff = &b
|
||||
}
|
||||
}
|
||||
wait := c.backoff.set(backoff)
|
||||
c.logger.Printf("Request failed, backing-off for %s: got HTTP status %s", wait, httpRsp.Status)
|
||||
default:
|
||||
return httpRsp, body, fmt.Errorf("got HTTP Status %q", httpRsp.Status)
|
||||
}
|
||||
}
|
||||
if err := c.waitForBackoff(ctx); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
446
vendor/github.com/google/certificate-transparency-go/jsonclient/client_test.go
generated
vendored
Normal file
446
vendor/github.com/google/certificate-transparency-go/jsonclient/client_test.go
generated
vendored
Normal file
@ -0,0 +1,446 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package jsonclient
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/certificate-transparency-go/testdata"
|
||||
)
|
||||
|
||||
func publicKeyPEMToDER(key string) []byte {
|
||||
block, _ := pem.Decode([]byte(key))
|
||||
if block == nil {
|
||||
panic("failed to decode public key PEM")
|
||||
}
|
||||
if block.Type != "PUBLIC KEY" {
|
||||
panic("PEM does not have type 'PUBLIC KEY'")
|
||||
}
|
||||
return block.Bytes
|
||||
}
|
||||
|
||||
func TestNewJSONClient(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
opts Options
|
||||
errstr string
|
||||
}{
|
||||
{
|
||||
name: "invalid PublicKey",
|
||||
opts: Options{PublicKey: "bogus"},
|
||||
errstr: "no PEM block",
|
||||
},
|
||||
{
|
||||
name: "invalid PublicKeyDER",
|
||||
opts: Options{PublicKeyDER: []byte("bogus")},
|
||||
errstr: "asn1: structure error",
|
||||
},
|
||||
{
|
||||
name: "RSA PublicKey",
|
||||
opts: Options{PublicKey: testdata.RsaPublicKeyPEM},
|
||||
},
|
||||
{
|
||||
name: "RSA PublicKeyDER",
|
||||
opts: Options{PublicKeyDER: publicKeyPEMToDER(testdata.RsaPublicKeyPEM)},
|
||||
},
|
||||
{
|
||||
name: "ECDSA PublicKey",
|
||||
opts: Options{PublicKey: testdata.EcdsaPublicKeyPEM},
|
||||
},
|
||||
{
|
||||
name: "ECDSA PublicKeyDER",
|
||||
opts: Options{PublicKeyDER: publicKeyPEMToDER(testdata.EcdsaPublicKeyPEM)},
|
||||
},
|
||||
{
|
||||
name: "DSA PublicKey",
|
||||
opts: Options{PublicKey: testdata.DsaPublicKeyPEM},
|
||||
errstr: "Unsupported public key type",
|
||||
},
|
||||
{
|
||||
name: "DSA PublicKeyDER",
|
||||
opts: Options{PublicKeyDER: publicKeyPEMToDER(testdata.DsaPublicKeyPEM)},
|
||||
errstr: "Unsupported public key type",
|
||||
},
|
||||
{
|
||||
name: "PublicKey contains trailing garbage",
|
||||
opts: Options{PublicKey: testdata.RsaPublicKeyPEM + "bogus"},
|
||||
errstr: "extra data found",
|
||||
},
|
||||
{
|
||||
name: "PublicKeyDER contains trailing garbage",
|
||||
opts: Options{PublicKeyDER: append(publicKeyPEMToDER(testdata.RsaPublicKeyPEM), []byte("deadbeef")...)},
|
||||
errstr: "trailing data",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
client, err := New("http://127.0.0.1", nil, test.opts)
|
||||
if test.errstr != "" {
|
||||
if err == nil {
|
||||
t.Errorf("%v: New()=%p,nil; want error %q", test.name, client, test.errstr)
|
||||
} else if !strings.Contains(err.Error(), test.errstr) {
|
||||
t.Errorf("%v: New()=nil,%q; want error %q", test.name, err, test.errstr)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("%v: New()=nil,%q; want no error", test.name, err)
|
||||
} else if client == nil {
|
||||
t.Errorf("%v: New()=nil,nil; want client", test.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type TestStruct struct {
|
||||
TreeSize int `json:"tree_size"`
|
||||
Timestamp int `json:"timestamp"`
|
||||
Data string `json:"data"`
|
||||
}
|
||||
|
||||
type TestParams struct {
|
||||
RespCode int `json:"rc"`
|
||||
}
|
||||
|
||||
func MockServer(t *testing.T, failCount int, retryAfter int) *httptest.Server {
|
||||
t.Helper()
|
||||
mu := sync.Mutex{}
|
||||
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
switch r.URL.Path {
|
||||
case "/struct/path":
|
||||
fmt.Fprintf(w, `{"tree_size": 11, "timestamp": 99}`)
|
||||
case "/struct/params":
|
||||
var s TestStruct
|
||||
if r.Method == http.MethodGet {
|
||||
s.TreeSize, _ = strconv.Atoi(r.FormValue("tree_size"))
|
||||
s.Timestamp, _ = strconv.Atoi(r.FormValue("timestamp"))
|
||||
s.Data = r.FormValue("data")
|
||||
} else {
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&s)
|
||||
if err != nil {
|
||||
panic("Failed to decode: " + err.Error())
|
||||
}
|
||||
defer r.Body.Close()
|
||||
}
|
||||
fmt.Fprintf(w, `{"tree_size": %d, "timestamp": %d, "data": "%s"}`, s.TreeSize, s.Timestamp, s.Data)
|
||||
case "/error":
|
||||
var params TestParams
|
||||
if r.Method == http.MethodGet {
|
||||
params.RespCode, _ = strconv.Atoi(r.FormValue("rc"))
|
||||
} else {
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(¶ms)
|
||||
if err != nil {
|
||||
panic("Failed to decode: " + err.Error())
|
||||
}
|
||||
defer r.Body.Close()
|
||||
}
|
||||
http.Error(w, "error page", params.RespCode)
|
||||
case "/malformed":
|
||||
fmt.Fprintf(w, `{"tree_size": 11, "timestamp": 99`) // no closing }
|
||||
case "/retry":
|
||||
if failCount > 0 {
|
||||
failCount--
|
||||
if retryAfter != 0 {
|
||||
if retryAfter > 0 {
|
||||
w.Header().Add("Retry-After", strconv.Itoa(retryAfter))
|
||||
}
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
} else {
|
||||
w.WriteHeader(http.StatusRequestTimeout)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(w, `{"tree_size": 11, "timestamp": 99}`)
|
||||
}
|
||||
case "/retry-rfc1123":
|
||||
if failCount > 0 {
|
||||
failCount--
|
||||
w.Header().Add("Retry-After", time.Now().Add(time.Duration(retryAfter)*time.Second).Format(time.RFC1123))
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
} else {
|
||||
fmt.Fprintf(w, `{"tree_size": 11, "timestamp": 99}`)
|
||||
}
|
||||
default:
|
||||
t.Fatalf("Unhandled URL path: %s", r.URL.Path)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
func TestGetAndParse(t *testing.T) {
|
||||
rc := regexp.MustCompile
|
||||
tests := []struct {
|
||||
uri string
|
||||
params map[string]string
|
||||
status int
|
||||
result TestStruct
|
||||
errstr *regexp.Regexp
|
||||
wantBody bool
|
||||
}{
|
||||
{uri: "/short%", errstr: rc("invalid URL escape")},
|
||||
{uri: "/malformed", status: http.StatusOK, errstr: rc("unexpected EOF"), wantBody: true},
|
||||
{uri: "/error", params: map[string]string{"rc": "404"}, status: http.StatusNotFound, wantBody: true},
|
||||
{uri: "/error", params: map[string]string{"rc": "403"}, status: http.StatusForbidden, wantBody: true},
|
||||
{uri: "/struct/path", status: http.StatusOK, result: TestStruct{11, 99, ""}, wantBody: true},
|
||||
{
|
||||
uri: "/struct/params",
|
||||
status: http.StatusOK,
|
||||
params: map[string]string{"tree_size": "42", "timestamp": "88", "data": "abcd"},
|
||||
result: TestStruct{42, 88, "abcd"},
|
||||
wantBody: true,
|
||||
},
|
||||
}
|
||||
|
||||
ts := MockServer(t, -1, 0)
|
||||
defer ts.Close()
|
||||
|
||||
logClient, err := New(ts.URL, &http.Client{}, Options{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
for _, test := range tests {
|
||||
var result TestStruct
|
||||
httpRsp, body, err := logClient.GetAndParse(ctx, test.uri, test.params, &result)
|
||||
if gotBody := (body != nil); gotBody != test.wantBody {
|
||||
t.Errorf("GetAndParse(%q) got body? %v, want? %v", test.uri, gotBody, test.wantBody)
|
||||
}
|
||||
if test.errstr != nil {
|
||||
if err == nil {
|
||||
t.Errorf("GetAndParse(%q)=%+v,_,nil; want error matching %q", test.uri, result, test.errstr)
|
||||
} else if !test.errstr.MatchString(err.Error()) {
|
||||
t.Errorf("GetAndParse(%q)=nil,_,%q; want error matching %q", test.uri, err.Error(), test.errstr)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if httpRsp.StatusCode != test.status {
|
||||
t.Errorf("GetAndParse('%s') got status %d; want %d", test.uri, httpRsp.StatusCode, test.status)
|
||||
}
|
||||
if test.status == http.StatusOK {
|
||||
if err != nil {
|
||||
t.Errorf("GetAndParse(%q)=nil,_,%q; want %+v", test.uri, err.Error(), result)
|
||||
}
|
||||
if !reflect.DeepEqual(result, test.result) {
|
||||
t.Errorf("GetAndParse(%q)=%+v,_,nil; want %+v", test.uri, result, test.result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostAndParse(t *testing.T) {
|
||||
rc := regexp.MustCompile
|
||||
tests := []struct {
|
||||
uri string
|
||||
request interface{}
|
||||
status int
|
||||
result TestStruct
|
||||
errstr *regexp.Regexp
|
||||
wantBody bool
|
||||
}{
|
||||
{uri: "/short%", errstr: rc("invalid URL escape")},
|
||||
{uri: "/struct/params", request: json.Number(`invalid`), errstr: rc("invalid number literal")},
|
||||
{uri: "/malformed", status: http.StatusOK, errstr: rc("unexpected end of JSON"), wantBody: true},
|
||||
{uri: "/error", request: TestParams{RespCode: 404}, status: http.StatusNotFound, wantBody: true},
|
||||
{uri: "/error", request: TestParams{RespCode: 403}, status: http.StatusForbidden, wantBody: true},
|
||||
{uri: "/struct/path", status: http.StatusOK, result: TestStruct{11, 99, ""}, wantBody: true},
|
||||
{
|
||||
uri: "/struct/params",
|
||||
status: http.StatusOK,
|
||||
request: TestStruct{42, 88, "abcd"},
|
||||
result: TestStruct{42, 88, "abcd"},
|
||||
wantBody: true,
|
||||
},
|
||||
}
|
||||
|
||||
ts := MockServer(t, -1, 0)
|
||||
defer ts.Close()
|
||||
|
||||
logClient, err := New(ts.URL, &http.Client{}, Options{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
for _, test := range tests {
|
||||
var result TestStruct
|
||||
httpRsp, body, err := logClient.PostAndParse(ctx, test.uri, test.request, &result)
|
||||
if gotBody := (body != nil); gotBody != test.wantBody {
|
||||
t.Errorf("GetAndParse(%q) returned body %v, wanted %v", test.uri, gotBody, test.wantBody)
|
||||
}
|
||||
if test.errstr != nil {
|
||||
if err == nil {
|
||||
t.Errorf("PostAndParse(%q)=%+v,nil; want error matching %q", test.uri, result, test.errstr)
|
||||
} else if !test.errstr.MatchString(err.Error()) {
|
||||
t.Errorf("PostAndParse(%q)=nil,%q; want error matching %q", test.uri, err.Error(), test.errstr)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if httpRsp.StatusCode != test.status {
|
||||
t.Errorf("PostAndParse(%q) got status %d; want %d", test.uri, httpRsp.StatusCode, test.status)
|
||||
}
|
||||
if test.status == http.StatusOK {
|
||||
if err != nil {
|
||||
t.Errorf("PostAndParse(%q)=nil,%q; want %+v", test.uri, err.Error(), test.result)
|
||||
}
|
||||
if !reflect.DeepEqual(result, test.result) {
|
||||
t.Errorf("PostAndParse(%q)=%+v,nil; want %+v", test.uri, result, test.result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mockBackoff is not safe for concurrent usage
|
||||
type mockBackoff struct {
|
||||
override time.Duration
|
||||
}
|
||||
|
||||
func (mb *mockBackoff) set(o *time.Duration) time.Duration {
|
||||
if o != nil {
|
||||
mb.override = *o
|
||||
}
|
||||
return 0
|
||||
}
|
||||
func (mb *mockBackoff) decreaseMultiplier() {}
|
||||
func (mb *mockBackoff) until() time.Time { return time.Time{} }
|
||||
|
||||
func TestPostAndParseWithRetry(t *testing.T) {
|
||||
tests := []struct {
|
||||
uri string
|
||||
request interface{}
|
||||
deadlineSecs int // -1 indicates no deadline
|
||||
retryAfter int // -1 indicates generate 503 with no Retry-After
|
||||
failCount int
|
||||
errstr string
|
||||
expectedBackoff time.Duration // 0 indicates no expected backoff override set
|
||||
}{
|
||||
{
|
||||
uri: "/error",
|
||||
request: TestParams{RespCode: 418},
|
||||
deadlineSecs: -1,
|
||||
retryAfter: 0,
|
||||
failCount: 0,
|
||||
errstr: "teapot",
|
||||
expectedBackoff: 0,
|
||||
},
|
||||
{
|
||||
uri: "/short%",
|
||||
request: nil,
|
||||
deadlineSecs: 0,
|
||||
retryAfter: 0,
|
||||
failCount: 0,
|
||||
errstr: "deadline exceeded",
|
||||
expectedBackoff: 0,
|
||||
},
|
||||
{
|
||||
uri: "/retry",
|
||||
request: nil,
|
||||
deadlineSecs: -1,
|
||||
retryAfter: 0,
|
||||
failCount: 1,
|
||||
errstr: "",
|
||||
expectedBackoff: 0,
|
||||
},
|
||||
{
|
||||
uri: "/retry",
|
||||
request: nil,
|
||||
deadlineSecs: -1,
|
||||
retryAfter: 5,
|
||||
failCount: 1,
|
||||
errstr: "",
|
||||
expectedBackoff: 5 * time.Second,
|
||||
},
|
||||
{
|
||||
uri: "/retry-rfc1123",
|
||||
request: nil,
|
||||
deadlineSecs: -1,
|
||||
retryAfter: 5,
|
||||
failCount: 1,
|
||||
errstr: "",
|
||||
expectedBackoff: 5 * time.Second,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
ts := MockServer(t, test.failCount, test.retryAfter)
|
||||
defer ts.Close()
|
||||
|
||||
logClient, err := New(ts.URL, &http.Client{}, Options{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mb := mockBackoff{}
|
||||
logClient.backoff = &mb
|
||||
ctx := context.Background()
|
||||
if test.deadlineSecs >= 0 {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithDeadline(context.Background(), time.Now().Add(time.Duration(test.deadlineSecs)*time.Second))
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
var result TestStruct
|
||||
httpRsp, _, err := logClient.PostAndParseWithRetry(ctx, test.uri, test.request, &result)
|
||||
if test.errstr != "" {
|
||||
if err == nil {
|
||||
t.Errorf("PostAndParseWithRetry()=%+v,nil; want error %q", result, test.errstr)
|
||||
} else if !strings.Contains(err.Error(), test.errstr) {
|
||||
t.Errorf("PostAndParseWithRetry()=nil,%q; want error %q", err.Error(), test.errstr)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("PostAndParseWithRetry()=nil,%q; want no error", err.Error())
|
||||
} else if httpRsp.StatusCode != http.StatusOK {
|
||||
t.Errorf("PostAndParseWithRetry() got status %d; want OK(404)", httpRsp.StatusCode)
|
||||
}
|
||||
if test.expectedBackoff > 0 && !fuzzyDurationEquals(test.expectedBackoff, mb.override, time.Second) {
|
||||
t.Errorf("Unexpected backoff override set: got: %s, wanted: %s", mb.override, test.expectedBackoff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextRequired(t *testing.T) {
|
||||
ts := MockServer(t, -1, 0)
|
||||
defer ts.Close()
|
||||
|
||||
logClient, err := New(ts.URL, &http.Client{}, Options{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var result TestStruct
|
||||
_, _, err = logClient.GetAndParse(nil, "/struct/path", nil, &result)
|
||||
if err == nil {
|
||||
t.Errorf("GetAndParse() succeeded with empty Context")
|
||||
}
|
||||
_, _, err = logClient.PostAndParse(nil, "/struct/path", nil, &result)
|
||||
if err == nil {
|
||||
t.Errorf("PostAndParse() succeeded with empty Context")
|
||||
}
|
||||
_, _, err = logClient.PostAndParseWithRetry(nil, "/struct/path", nil, &result)
|
||||
if err == nil {
|
||||
t.Errorf("PostAndParseWithRetry() succeeded with empty Context")
|
||||
}
|
||||
}
|
255
vendor/github.com/google/certificate-transparency-go/serialization.go
generated
vendored
Normal file
255
vendor/github.com/google/certificate-transparency-go/serialization.go
generated
vendored
Normal file
@ -0,0 +1,255 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ct
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/google/certificate-transparency-go/tls"
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
)
|
||||
|
||||
// SerializeSCTSignatureInput serializes the passed in sct and log entry into
|
||||
// the correct format for signing.
|
||||
func SerializeSCTSignatureInput(sct SignedCertificateTimestamp, entry LogEntry) ([]byte, error) {
|
||||
switch sct.SCTVersion {
|
||||
case V1:
|
||||
input := CertificateTimestamp{
|
||||
SCTVersion: sct.SCTVersion,
|
||||
SignatureType: CertificateTimestampSignatureType,
|
||||
Timestamp: sct.Timestamp,
|
||||
EntryType: entry.Leaf.TimestampedEntry.EntryType,
|
||||
Extensions: sct.Extensions,
|
||||
}
|
||||
switch entry.Leaf.TimestampedEntry.EntryType {
|
||||
case X509LogEntryType:
|
||||
input.X509Entry = entry.Leaf.TimestampedEntry.X509Entry
|
||||
case PrecertLogEntryType:
|
||||
input.PrecertEntry = &PreCert{
|
||||
IssuerKeyHash: entry.Leaf.TimestampedEntry.PrecertEntry.IssuerKeyHash,
|
||||
TBSCertificate: entry.Leaf.TimestampedEntry.PrecertEntry.TBSCertificate,
|
||||
}
|
||||
case XJSONLogEntryType:
|
||||
input.JSONEntry = entry.Leaf.TimestampedEntry.JSONEntry
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported entry type %s", entry.Leaf.TimestampedEntry.EntryType)
|
||||
}
|
||||
return tls.Marshal(input)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown SCT version %d", sct.SCTVersion)
|
||||
}
|
||||
}
|
||||
|
||||
// SerializeSTHSignatureInput serializes the passed in STH into the correct
|
||||
// format for signing.
|
||||
func SerializeSTHSignatureInput(sth SignedTreeHead) ([]byte, error) {
|
||||
switch sth.Version {
|
||||
case V1:
|
||||
if len(sth.SHA256RootHash) != crypto.SHA256.Size() {
|
||||
return nil, fmt.Errorf("invalid TreeHash length, got %d expected %d", len(sth.SHA256RootHash), crypto.SHA256.Size())
|
||||
}
|
||||
|
||||
input := TreeHeadSignature{
|
||||
Version: sth.Version,
|
||||
SignatureType: TreeHashSignatureType,
|
||||
Timestamp: sth.Timestamp,
|
||||
TreeSize: sth.TreeSize,
|
||||
SHA256RootHash: sth.SHA256RootHash,
|
||||
}
|
||||
return tls.Marshal(input)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported STH version %d", sth.Version)
|
||||
}
|
||||
}
|
||||
|
||||
// CreateX509MerkleTreeLeaf generates a MerkleTreeLeaf for an X509 cert
|
||||
func CreateX509MerkleTreeLeaf(cert ASN1Cert, timestamp uint64) *MerkleTreeLeaf {
|
||||
return &MerkleTreeLeaf{
|
||||
Version: V1,
|
||||
LeafType: TimestampedEntryLeafType,
|
||||
TimestampedEntry: &TimestampedEntry{
|
||||
Timestamp: timestamp,
|
||||
EntryType: X509LogEntryType,
|
||||
X509Entry: &cert,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// CreateJSONMerkleTreeLeaf creates the merkle tree leaf for json data.
|
||||
func CreateJSONMerkleTreeLeaf(data interface{}, timestamp uint64) *MerkleTreeLeaf {
|
||||
jsonData, err := json.Marshal(AddJSONRequest{Data: data})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
// Match the JSON serialization implemented by json-c
|
||||
jsonStr := strings.Replace(string(jsonData), ":", ": ", -1)
|
||||
jsonStr = strings.Replace(jsonStr, ",", ", ", -1)
|
||||
jsonStr = strings.Replace(jsonStr, "{", "{ ", -1)
|
||||
jsonStr = strings.Replace(jsonStr, "}", " }", -1)
|
||||
jsonStr = strings.Replace(jsonStr, "/", `\/`, -1)
|
||||
// TODO: Pending google/certificate-transparency#1243, replace with
|
||||
// ObjectHash once supported by CT server.
|
||||
|
||||
return &MerkleTreeLeaf{
|
||||
Version: V1,
|
||||
LeafType: TimestampedEntryLeafType,
|
||||
TimestampedEntry: &TimestampedEntry{
|
||||
Timestamp: timestamp,
|
||||
EntryType: XJSONLogEntryType,
|
||||
JSONEntry: &JSONDataEntry{Data: []byte(jsonStr)},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// MerkleTreeLeafFromRawChain generates a MerkleTreeLeaf from a chain (in DER-encoded form) and timestamp.
|
||||
func MerkleTreeLeafFromRawChain(rawChain []ASN1Cert, etype LogEntryType, timestamp uint64) (*MerkleTreeLeaf, error) {
|
||||
// Need at most 3 of the chain
|
||||
count := 3
|
||||
if count > len(rawChain) {
|
||||
count = len(rawChain)
|
||||
}
|
||||
chain := make([]*x509.Certificate, count)
|
||||
for i := range chain {
|
||||
cert, err := x509.ParseCertificate(rawChain[i].Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse chain[%d] cert: %v", i, err)
|
||||
}
|
||||
chain[i] = cert
|
||||
}
|
||||
return MerkleTreeLeafFromChain(chain, etype, timestamp)
|
||||
}
|
||||
|
||||
// MerkleTreeLeafFromChain generates a MerkleTreeLeaf from a chain and timestamp.
|
||||
func MerkleTreeLeafFromChain(chain []*x509.Certificate, etype LogEntryType, timestamp uint64) (*MerkleTreeLeaf, error) {
|
||||
leaf := MerkleTreeLeaf{
|
||||
Version: V1,
|
||||
LeafType: TimestampedEntryLeafType,
|
||||
TimestampedEntry: &TimestampedEntry{
|
||||
EntryType: etype,
|
||||
Timestamp: timestamp,
|
||||
},
|
||||
}
|
||||
if etype == X509LogEntryType {
|
||||
leaf.TimestampedEntry.X509Entry = &ASN1Cert{Data: chain[0].Raw}
|
||||
return &leaf, nil
|
||||
}
|
||||
if etype != PrecertLogEntryType {
|
||||
return nil, fmt.Errorf("unknown LogEntryType %d", etype)
|
||||
}
|
||||
|
||||
// Pre-certs are more complicated. First, parse the leaf pre-cert and its
|
||||
// putative issuer.
|
||||
if len(chain) < 2 {
|
||||
return nil, fmt.Errorf("no issuer cert available for precert leaf building")
|
||||
}
|
||||
issuer := chain[1]
|
||||
cert := chain[0]
|
||||
|
||||
var preIssuer *x509.Certificate
|
||||
if IsPreIssuer(issuer) {
|
||||
// Replace the cert's issuance information with details from the pre-issuer.
|
||||
preIssuer = issuer
|
||||
|
||||
// The issuer of the pre-cert is not going to be the issuer of the final
|
||||
// cert. Change to use the final issuer's key hash.
|
||||
if len(chain) < 3 {
|
||||
return nil, fmt.Errorf("no issuer cert available for pre-issuer")
|
||||
}
|
||||
issuer = chain[2]
|
||||
}
|
||||
|
||||
// Next, post-process the DER-encoded TBSCertificate, to remove the CT poison
|
||||
// extension and possibly update the issuer field.
|
||||
defangedTBS, err := x509.BuildPrecertTBS(cert.RawTBSCertificate, preIssuer)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to remove poison extension: %v", err)
|
||||
}
|
||||
|
||||
leaf.TimestampedEntry.EntryType = PrecertLogEntryType
|
||||
leaf.TimestampedEntry.PrecertEntry = &PreCert{
|
||||
IssuerKeyHash: sha256.Sum256(issuer.RawSubjectPublicKeyInfo),
|
||||
TBSCertificate: defangedTBS,
|
||||
}
|
||||
return &leaf, nil
|
||||
}
|
||||
|
||||
// IsPreIssuer indicates whether a certificate is a pre-cert issuer with the specific
|
||||
// certificate transparency extended key usage.
|
||||
func IsPreIssuer(issuer *x509.Certificate) bool {
|
||||
for _, eku := range issuer.ExtKeyUsage {
|
||||
if eku == x509.ExtKeyUsageCertificateTransparency {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// LogEntryFromLeaf converts a LeafEntry object (which has the raw leaf data after JSON parsing)
|
||||
// into a LogEntry object (which includes x509.Certificate objects, after TLS and ASN.1 parsing).
|
||||
// Note that this function may return a valid LogEntry object and a non-nil error value, when
|
||||
// the error indicates a non-fatal parsing error (of type x509.NonFatalErrors).
|
||||
func LogEntryFromLeaf(index int64, leafEntry *LeafEntry) (*LogEntry, error) {
|
||||
var leaf MerkleTreeLeaf
|
||||
if rest, err := tls.Unmarshal(leafEntry.LeafInput, &leaf); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal MerkleTreeLeaf for index %d: %v", index, err)
|
||||
} else if len(rest) > 0 {
|
||||
return nil, fmt.Errorf("trailing data (%d bytes) after MerkleTreeLeaf for index %d", len(rest), index)
|
||||
}
|
||||
|
||||
var err error
|
||||
entry := LogEntry{Index: index, Leaf: leaf}
|
||||
switch leaf.TimestampedEntry.EntryType {
|
||||
case X509LogEntryType:
|
||||
var certChain CertificateChain
|
||||
if rest, err := tls.Unmarshal(leafEntry.ExtraData, &certChain); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal ExtraData for index %d: %v", index, err)
|
||||
} else if len(rest) > 0 {
|
||||
return nil, fmt.Errorf("trailing data (%d bytes) after CertificateChain for index %d", len(rest), index)
|
||||
}
|
||||
entry.Chain = certChain.Entries
|
||||
entry.X509Cert, err = leaf.X509Certificate()
|
||||
if _, ok := err.(x509.NonFatalErrors); !ok && err != nil {
|
||||
return nil, fmt.Errorf("failed to parse certificate in MerkleTreeLeaf for index %d: %v", index, err)
|
||||
}
|
||||
|
||||
case PrecertLogEntryType:
|
||||
var precertChain PrecertChainEntry
|
||||
if rest, err := tls.Unmarshal(leafEntry.ExtraData, &precertChain); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal PrecertChainEntry for index %d: %v", index, err)
|
||||
} else if len(rest) > 0 {
|
||||
return nil, fmt.Errorf("trailing data (%d bytes) after PrecertChainEntry for index %d", len(rest), index)
|
||||
}
|
||||
entry.Chain = precertChain.CertificateChain
|
||||
var tbsCert *x509.Certificate
|
||||
tbsCert, err = leaf.Precertificate()
|
||||
if _, ok := err.(x509.NonFatalErrors); !ok && err != nil {
|
||||
return nil, fmt.Errorf("failed to parse precertificate in MerkleTreeLeaf for index %d: %v", index, err)
|
||||
}
|
||||
entry.Precert = &Precertificate{
|
||||
Submitted: precertChain.PreCertificate,
|
||||
IssuerKeyHash: leaf.TimestampedEntry.PrecertEntry.IssuerKeyHash,
|
||||
TBSCertificate: tbsCert,
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("saw unknown entry type at index %d: %v", index, leaf.TimestampedEntry.EntryType)
|
||||
}
|
||||
// err may hold a x509.NonFatalErrors object.
|
||||
return &entry, err
|
||||
}
|
513
vendor/github.com/google/certificate-transparency-go/serialization_test.go
generated
vendored
Normal file
513
vendor/github.com/google/certificate-transparency-go/serialization_test.go
generated
vendored
Normal file
@ -0,0 +1,513 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ct
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/certificate-transparency-go/tls"
|
||||
)
|
||||
|
||||
func dh(h string) []byte {
|
||||
r, err := hex.DecodeString(h)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
const (
|
||||
defaultSCTLogIDString string = "iamapublickeyshatwofivesixdigest"
|
||||
defaultSCTTimestamp uint64 = 1234
|
||||
defaultSCTSignatureString string = "\x04\x03\x00\x09signature"
|
||||
defaultCertifictateString string = "certificate"
|
||||
defaultPrecertString string = "precert"
|
||||
defaultPrecertIssuerHashString string = "iamapublickeyshatwofivesixdigest"
|
||||
defaultPrecertTBSString string = "tbs"
|
||||
|
||||
defaultCertificateSCTSignatureInputHexString string =
|
||||
// version, 1 byte
|
||||
"00" +
|
||||
// signature type, 1 byte
|
||||
"00" +
|
||||
// timestamp, 8 bytes
|
||||
"00000000000004d2" +
|
||||
// entry type, 2 bytes
|
||||
"0000" +
|
||||
// leaf certificate length, 3 bytes
|
||||
"00000b" +
|
||||
// leaf certificate, 11 bytes
|
||||
"6365727469666963617465" +
|
||||
// extensions length, 2 bytes
|
||||
"0000" +
|
||||
// extensions, 0 bytes
|
||||
""
|
||||
|
||||
defaultPrecertSCTSignatureInputHexString string =
|
||||
// version, 1 byte
|
||||
"00" +
|
||||
// signature type, 1 byte
|
||||
"00" +
|
||||
// timestamp, 8 bytes
|
||||
"00000000000004d2" +
|
||||
// entry type, 2 bytes
|
||||
"0001" +
|
||||
// issuer key hash, 32 bytes
|
||||
"69616d617075626c69636b657973686174776f66697665736978646967657374" +
|
||||
// tbs certificate length, 3 bytes
|
||||
"000003" +
|
||||
// tbs certificate, 3 bytes
|
||||
"746273" +
|
||||
// extensions length, 2 bytes
|
||||
"0000" +
|
||||
// extensions, 0 bytes
|
||||
""
|
||||
|
||||
defaultSTHSignedHexString string =
|
||||
// version, 1 byte
|
||||
"00" +
|
||||
// signature type, 1 byte
|
||||
"01" +
|
||||
// timestamp, 8 bytes
|
||||
"0000000000000929" +
|
||||
// tree size, 8 bytes
|
||||
"0000000000000006" +
|
||||
// root hash, 32 bytes
|
||||
"696d757374626565786163746c7974686972747974776f62797465736c6f6e67"
|
||||
|
||||
defaultSCTHexString string =
|
||||
// version, 1 byte
|
||||
"00" +
|
||||
// keyid, 32 bytes
|
||||
"69616d617075626c69636b657973686174776f66697665736978646967657374" +
|
||||
// timestamp, 8 bytes
|
||||
"00000000000004d2" +
|
||||
// extensions length, 2 bytes
|
||||
"0000" +
|
||||
// extensions, 0 bytes
|
||||
// hash algo, sig algo, 2 bytes
|
||||
"0403" +
|
||||
// signature length, 2 bytes
|
||||
"0009" +
|
||||
// signature, 9 bytes
|
||||
"7369676e6174757265"
|
||||
|
||||
defaultSCTListHexString string = "0476007400380069616d617075626c69636b657973686174776f6669766573697864696765737400000000000004d20000040300097369676e617475726500380069616d617075626c69636b657973686174776f6669766573697864696765737400000000000004d20000040300097369676e6174757265"
|
||||
)
|
||||
|
||||
func defaultSCTLogID() LogID {
|
||||
var id LogID
|
||||
copy(id.KeyID[:], defaultSCTLogIDString)
|
||||
return id
|
||||
}
|
||||
|
||||
func defaultSCTSignature() DigitallySigned {
|
||||
var ds DigitallySigned
|
||||
if _, err := tls.Unmarshal([]byte(defaultSCTSignatureString), &ds); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return ds
|
||||
}
|
||||
|
||||
func defaultSCT() SignedCertificateTimestamp {
|
||||
return SignedCertificateTimestamp{
|
||||
SCTVersion: V1,
|
||||
LogID: defaultSCTLogID(),
|
||||
Timestamp: defaultSCTTimestamp,
|
||||
Extensions: []byte{},
|
||||
Signature: defaultSCTSignature()}
|
||||
}
|
||||
|
||||
func defaultCertificate() []byte {
|
||||
return []byte(defaultCertifictateString)
|
||||
}
|
||||
|
||||
func defaultExtensions() []byte {
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
func defaultCertificateSCTSignatureInput(t *testing.T) []byte {
|
||||
t.Helper()
|
||||
r, err := hex.DecodeString(defaultCertificateSCTSignatureInputHexString)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to decode defaultCertificateSCTSignatureInputHexString: %v", err)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func defaultCertificateLogEntry() LogEntry {
|
||||
return LogEntry{
|
||||
Index: 1,
|
||||
Leaf: MerkleTreeLeaf{
|
||||
Version: V1,
|
||||
LeafType: TimestampedEntryLeafType,
|
||||
TimestampedEntry: &TimestampedEntry{
|
||||
Timestamp: defaultSCTTimestamp,
|
||||
EntryType: X509LogEntryType,
|
||||
X509Entry: &ASN1Cert{Data: defaultCertificate()},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func defaultPrecertSCTSignatureInput(t *testing.T) []byte {
|
||||
t.Helper()
|
||||
r, err := hex.DecodeString(defaultPrecertSCTSignatureInputHexString)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to decode defaultPrecertSCTSignatureInputHexString: %v", err)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func defaultPrecertTBS() []byte {
|
||||
return []byte(defaultPrecertTBSString)
|
||||
}
|
||||
|
||||
func defaultPrecertIssuerHash() [32]byte {
|
||||
var b [32]byte
|
||||
copy(b[:], []byte(defaultPrecertIssuerHashString))
|
||||
return b
|
||||
}
|
||||
|
||||
func defaultPrecertLogEntry() LogEntry {
|
||||
return LogEntry{
|
||||
Index: 1,
|
||||
Leaf: MerkleTreeLeaf{
|
||||
Version: V1,
|
||||
LeafType: TimestampedEntryLeafType,
|
||||
TimestampedEntry: &TimestampedEntry{
|
||||
Timestamp: defaultSCTTimestamp,
|
||||
EntryType: PrecertLogEntryType,
|
||||
PrecertEntry: &PreCert{
|
||||
IssuerKeyHash: defaultPrecertIssuerHash(),
|
||||
TBSCertificate: defaultPrecertTBS(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func defaultSTH() SignedTreeHead {
|
||||
var root SHA256Hash
|
||||
copy(root[:], "imustbeexactlythirtytwobyteslong")
|
||||
return SignedTreeHead{
|
||||
TreeSize: 6,
|
||||
Timestamp: 2345,
|
||||
SHA256RootHash: root,
|
||||
TreeHeadSignature: DigitallySigned{
|
||||
Algorithm: tls.SignatureAndHashAlgorithm{
|
||||
Hash: tls.SHA256,
|
||||
Signature: tls.ECDSA},
|
||||
Signature: []byte("tree_signature"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Tests start here:
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func TestSerializeV1SCTSignatureInputForCertificateKAT(t *testing.T) {
|
||||
serialized, err := SerializeSCTSignatureInput(defaultSCT(), defaultCertificateLogEntry())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to serialize SCT for signing: %v", err)
|
||||
}
|
||||
if bytes.Compare(serialized, defaultCertificateSCTSignatureInput(t)) != 0 {
|
||||
t.Fatalf("Serialized certificate signature input doesn't match expected answer:\n%v\n%v", serialized, defaultCertificateSCTSignatureInput(t))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSerializeV1SCTSignatureInputForPrecertKAT(t *testing.T) {
|
||||
serialized, err := SerializeSCTSignatureInput(defaultSCT(), defaultPrecertLogEntry())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to serialize SCT for signing: %v", err)
|
||||
}
|
||||
if bytes.Compare(serialized, defaultPrecertSCTSignatureInput(t)) != 0 {
|
||||
t.Fatalf("Serialized precertificate signature input doesn't match expected answer:\n%v\n%v", serialized, defaultPrecertSCTSignatureInput(t))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSerializeV1SCTJSONSignature(t *testing.T) {
|
||||
entry := LogEntry{Leaf: *CreateJSONMerkleTreeLeaf("data", defaultSCT().Timestamp)}
|
||||
expected := dh(
|
||||
// version, 1 byte
|
||||
"00" +
|
||||
// signature type, 1 byte
|
||||
"00" +
|
||||
// timestamp, 8 bytes
|
||||
"00000000000004d2" +
|
||||
// entry type, 2 bytes
|
||||
"8000" +
|
||||
// tbs certificate length, 18 bytes
|
||||
"000012" +
|
||||
// { "data": "data" }, 3 bytes
|
||||
"7b202264617461223a20226461746122207d" +
|
||||
// extensions length, 2 bytes
|
||||
"0000" +
|
||||
// extensions, 0 bytes
|
||||
"")
|
||||
serialized, err := SerializeSCTSignatureInput(defaultSCT(), entry)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to serialize SCT for signing: %v", err)
|
||||
}
|
||||
if !bytes.Equal(serialized, expected) {
|
||||
t.Fatalf("Serialized JSON signature :\n%x, want\n%x", serialized, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSerializeV1STHSignatureKAT(t *testing.T) {
|
||||
b, err := SerializeSTHSignatureInput(defaultSTH())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to serialize defaultSTH: %v", err)
|
||||
}
|
||||
if bytes.Compare(b, mustDehex(t, defaultSTHSignedHexString)) != 0 {
|
||||
t.Fatalf("defaultSTH incorrectly serialized, expected:\n%v\ngot:\n%v", mustDehex(t, defaultSTHSignedHexString), b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalDigitallySigned(t *testing.T) {
|
||||
b, err := tls.Marshal(
|
||||
DigitallySigned{
|
||||
Algorithm: tls.SignatureAndHashAlgorithm{
|
||||
Hash: tls.SHA512,
|
||||
Signature: tls.ECDSA},
|
||||
Signature: []byte("signature")})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal DigitallySigned struct: %v", err)
|
||||
}
|
||||
if b[0] != byte(tls.SHA512) {
|
||||
t.Fatalf("Expected b[0] == SHA512, but found %v", tls.HashAlgorithm(b[0]))
|
||||
}
|
||||
if b[1] != byte(tls.ECDSA) {
|
||||
t.Fatalf("Expected b[1] == ECDSA, but found %v", tls.SignatureAlgorithm(b[1]))
|
||||
}
|
||||
if b[2] != 0x00 || b[3] != 0x09 {
|
||||
t.Fatalf("Found incorrect length bytes, expected (0x00, 0x09) found %v", b[2:3])
|
||||
}
|
||||
if string(b[4:]) != "signature" {
|
||||
t.Fatalf("Found incorrect signature bytes, expected %v, found %v", []byte("signature"), b[4:])
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalDigitallySigned(t *testing.T) {
|
||||
var ds DigitallySigned
|
||||
if _, err := tls.Unmarshal([]byte("\x01\x02\x00\x0aSiGnAtUrE!"), &ds); err != nil {
|
||||
t.Fatalf("Failed to unmarshal DigitallySigned: %v", err)
|
||||
}
|
||||
if ds.Algorithm.Hash != tls.MD5 {
|
||||
t.Fatalf("Expected HashAlgorithm %v, but got %v", tls.MD5, ds.Algorithm.Hash)
|
||||
}
|
||||
if ds.Algorithm.Signature != tls.DSA {
|
||||
t.Fatalf("Expected SignatureAlgorithm %v, but got %v", tls.DSA, ds.Algorithm.Signature)
|
||||
}
|
||||
if string(ds.Signature) != "SiGnAtUrE!" {
|
||||
t.Fatalf("Expected Signature %v, but got %v", []byte("SiGnAtUrE!"), ds.Signature)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshalSCTRoundTrip(t *testing.T) {
|
||||
sctIn := defaultSCT()
|
||||
b, err := tls.Marshal(sctIn)
|
||||
if err != nil {
|
||||
t.Fatalf("tls.Marshal(SCT)=nil,%v; want no error", err)
|
||||
}
|
||||
var sctOut SignedCertificateTimestamp
|
||||
if _, err := tls.Unmarshal(b, &sctOut); err != nil {
|
||||
t.Errorf("tls.Unmarshal(%s)=nil,%v; want %+v,nil", hex.EncodeToString(b), err, sctIn)
|
||||
} else if !reflect.DeepEqual(sctIn, sctOut) {
|
||||
t.Errorf("tls.Unmarshal(%s)=%v,nil; want %+v,nil", hex.EncodeToString(b), sctOut, sctIn)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalSCT(t *testing.T) {
|
||||
b, err := tls.Marshal(defaultSCT())
|
||||
if err != nil {
|
||||
t.Errorf("tls.Marshal(defaultSCT)=nil,%v; want %s", err, defaultSCTHexString)
|
||||
} else if !bytes.Equal(dh(defaultSCTHexString), b) {
|
||||
t.Errorf("tls.Marshal(defaultSCT)=%s,nil; want %s", hex.EncodeToString(b), defaultSCTHexString)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalSCT(t *testing.T) {
|
||||
want := defaultSCT()
|
||||
var got SignedCertificateTimestamp
|
||||
if _, err := tls.Unmarshal(dh(defaultSCTHexString), &got); err != nil {
|
||||
t.Errorf("tls.Unmarshal(%s)=nil,%v; want %+v,nil", defaultSCTHexString, err, want)
|
||||
} else if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("tls.Unmarshal(%s)=%+v,nil; want %+v,nil", defaultSCTHexString, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestX509MerkleTreeLeafHash(t *testing.T) {
|
||||
certFile := "./testdata/test-cert.pem"
|
||||
sctFile := "./testdata/test-cert.proof"
|
||||
certB, err := ioutil.ReadFile(certFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read file %s: %v", certFile, err)
|
||||
}
|
||||
certDER, _ := pem.Decode(certB)
|
||||
|
||||
sctB, err := ioutil.ReadFile(sctFile)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read file %s: %v", sctFile, err)
|
||||
}
|
||||
var sct SignedCertificateTimestamp
|
||||
if _, err := tls.Unmarshal(sctB, &sct); err != nil {
|
||||
t.Fatalf("Failed to deserialize SCT: %v", err)
|
||||
}
|
||||
|
||||
leaf := CreateX509MerkleTreeLeaf(ASN1Cert{Data: certDER.Bytes}, sct.Timestamp)
|
||||
b, err := tls.Marshal(*leaf)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to Serialize x509 leaf: %v", err)
|
||||
}
|
||||
|
||||
leafBytes := dh("00000000013ddb27ded900000002ce308202ca30820233a003020102020106300d06092a864886f70d01010505003055310b300906035504061302474231243022060355040a131b4365727469666963617465205472616e73706172656e6379204341310e300c0603550408130557616c65733110300e060355040713074572772057656e301e170d3132303630313030303030305a170d3232303630313030303030305a3052310b30090603550406130247423121301f060355040a13184365727469666963617465205472616e73706172656e6379310e300c0603550408130557616c65733110300e060355040713074572772057656e30819f300d06092a864886f70d010101050003818d0030818902818100b1fa37936111f8792da2081c3fe41925008531dc7f2c657bd9e1de4704160b4c9f19d54ada4470404c1c51341b8f1f7538dddd28d9aca48369fc5646ddcc7617f8168aae5b41d43331fca2dadfc804d57208949061f9eef902ca47ce88c644e000f06eeeccabdc9dd2f68a22ccb09dc76e0dbc73527765b1a37a8c676253dcc10203010001a381ac3081a9301d0603551d0e041604146a0d982a3b62c44b6d2ef4e9bb7a01aa9cb798e2307d0603551d230476307480145f9d880dc873e654d4f80dd8e6b0c124b447c355a159a4573055310b300906035504061302474231243022060355040a131b4365727469666963617465205472616e73706172656e6379204341310e300c0603550408130557616c65733110300e060355040713074572772057656e82010030090603551d1304023000300d06092a864886f70d010105050003818100171cd84aac414a9a030f22aac8f688b081b2709b848b4e5511406cd707fed028597a9faefc2eee2978d633aaac14ed3235197da87e0f71b8875f1ac9e78b281749ddedd007e3ecf50645f8cbf667256cd6a1647b5e13203bb8582de7d6696f656d1c60b95f456b7fcf338571908f1c69727d24c4fccd249295795814d1dac0e60000")
|
||||
if !bytes.Equal(b, leafBytes) {
|
||||
t.Errorf("CreateX509MerkleTreeLeaf(): got\n %x, want\n%x", b, sctB)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestJSONMerkleTreeLeaf(t *testing.T) {
|
||||
data := `CioaINV25GV8X4a6M6Q10avSLP9PYd5N8MwWxQvWU7E2CzZ8IgYI0KnavAUSWAoIZDc1NjMzMzMSTAgEEAMaRjBEAiBQlnp6Q3di86g8M3l5gz+9qls/Cz1+KJ+tK/jpaBtUCgIgXaJ94uLsnChA1NY7ocGwKrQwPU688hwaZ5L/DboV4mQ=2`
|
||||
timestamp := uint64(1469664866615)
|
||||
leaf := CreateJSONMerkleTreeLeaf(data, timestamp)
|
||||
b, err := tls.Marshal(*leaf)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to Serialize x509 leaf: %v", err)
|
||||
}
|
||||
leafBytes := dh("0000000001562eda313780000000c67b202264617461223a202243696f61494e563235475638583461364d365131306176534c5039505964354e384d77577851765755374532437a5a3849675949304b6e617641555357416f495a4463314e6a4d7a4d7a4d535441674545414d61526a4245416942516c6e703651336469383667384d336c35677a2b39716c735c2f437a312b4b4a2b744b5c2f6a70614274554367496758614a3934754c736e436841314e59376f6347774b72517750553638386877615a354c5c2f44626f56346d513d3222207d0000")
|
||||
|
||||
if !bytes.Equal(b, leafBytes) {
|
||||
t.Errorf("CreateJSONMerkleTreeLeaf(): got\n%x, want\n%x", b, leafBytes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogEntryFromLeaf(t *testing.T) {
|
||||
const (
|
||||
// Cert example taken from entry #1 in argon2018 log
|
||||
leafDER = "308204ef308202d7a00302010202070556658a503cca300d06092a864886f70d01010b0500307f310b3009060355040613024742310f300d06035504080c064c6f6e646f6e31173015060355040a0c0e476f6f676c6520554b204c74642e3121301f060355040b0c184365727469666963617465205472616e73706172656e63793123302106035504030c1a4d657267652044656c617920496e7465726d6564696174652031301e170d3137303831303132343331355a170d3138303333313038333231375a3063310b3009060355040613024742310f300d06035504070c064c6f6e646f6e31283026060355040a0c1f476f6f676c65204365727469666963617465205472616e73706172656e637931193017060355040513103135303233363839393537353331363230820122300d06092a864886f70d01010105000382010f003082010a0282010100a2fb53365dfbcefea77e1d65bc40f34f7919fcae85d82d3003428199f0c893fca95ba139156fd5e9a3bd84dc6dab8e74151fde6dd25b31526c85719bbf8990f3d6b21bb7f321306f6ddc50b96e8917fa103b388a00e1e954ee0232a9f9fb2efa8c9f9196a7fe84dad1f66b5d36127c71c9dcf25a04acd7bfda7866dfb77498c63a7ae9e7d0772fe9ba938a9ff6c0209196988158e6ea055fe967dd7599ef4bd7f306ded231cca10d89b4d6de40916e615d1d4cc6032585822a650743e34735d464fc0d544d1fad8c293df22f4a55ce3fbfb55d90cdc5ab84695a5a13d46f3176f143d9d28f60dca841eac603d30cec830a62feec091c927e6c781df330f14ca10203010001a3818b30818830130603551d25040c300a06082b0601050507030130230603551d11041c301a8218666c6f776572732d746f2d7468652d776f726c642e636f6d300c0603551d130101ff04023000301f0603551d23041830168014e93c04e1802fc284132d26709ef2fd1acfaafec6301d0603551d0e041604142f3948061fe546939f5e8dbc3fe4c0a1fbaab6b7300d06092a864886f70d01010b0500038202010052a2480c754c51cfdc9f99a82a8eb7c34e5e2bdcdfdd7543fadadb578083416d34ebb87fea3c90baf97f06be5baf5c41101ad1bdd2a2f554de6a3e8cd5ff3d78354badad01032a007d2eaf03590ee5397e223b2936f0c8b59c0407079c8975ffb34eb1cfe784cf3bc45e198a601473537f1ef382e0b5311d2ddf430ade7cfd28900ec9d91c1db49a6b2fb1b9e13b94135fed978d646e048b2fa9dc36ef5821cea8ebbed38d4c2d7811e9660f23d7636b295caccdc945a010a4c364fd7e7480aa5282d28fc46ce7f4f636ef2cc57c8bb1aee5da79bc6107205d4abcd3fb09a1db023ba4e8e9f34ae36ff5b2672fc2a14af8d23d67a437b3eb507ca90f73121841ab1498ab712d18063244dc3514bfffbaf6d45acdfc5316a248589a04b79b2abbca454e2e21f9b487e21eea21565c99ebc1013b87253c91f43ac6d2d2dea7090877c2a7404bce2545662ce005dc12eb57b1efe7145af8070b5dfa86736664a644a9c0f7e7c38d715cf874b818d519927eddc69b55c781b6e0a6eef8f3e46b9e059105b7932a978704e924904dbaa9583f3dd606467f4cc41589b702f1a02d517d3cd93b55d67d0b379e2527fded951be9dfb86d473613e6d9b8399ef5174d3e03185bd3cb4ea859b465c6c49010d4119d613a60878c0e453f17cfa3ce925e10f6e0a5adb745cebe218c3c82627a120e2907eeb9ec5307664474093cbc92d65fc7"
|
||||
leafCA = "308205c8308203b0a00302010202021001300d06092a864886f70d0101050500307d310b3009060355040613024742310f300d06035504080c064c6f6e646f6e31173015060355040a0c0e476f6f676c6520554b204c74642e3121301f060355040b0c184365727469666963617465205472616e73706172656e63793121301f06035504030c184d657267652044656c6179204d6f6e69746f7220526f6f74301e170d3134303731373132323633305a170d3139303731363132323633305a307f310b3009060355040613024742310f300d06035504080c064c6f6e646f6e31173015060355040a0c0e476f6f676c6520554b204c74642e3121301f060355040b0c184365727469666963617465205472616e73706172656e63793123302106035504030c1a4d657267652044656c617920496e7465726d656469617465203130820222300d06092a864886f70d01010105000382020f003082020a0282020100c1e874feff9aeef303bbfa63453881faaf8dc1c22c09641daf430381f33bc157bf6c4c8a8d57b1abc792859d20f2191509c597c437b14673dea5af4bea14396dd436dc620555d7953e0ede01f7ffb44f3ff7cde64ed245634e0df0aafce9c3ac5eb63d8de1d969cac8854195403a9f9d1d4c3dceedf1351edd945743bc54ab745b204fb5259fe3edf695c2cf90b886c48ff680b744fec2691b4345242b31c36f3118b727c5de5c25ec1aa30a4c2461c5119ef6bb90d816e6e44c5b9955bfa2ed3416bf6e4a53c92fafac0d1f0b8bf3d35be0d4f61ca05d28fc662dfa588fba3ee0380470c012ded9e51bbf1e7a25efa745784c49d05eaf8dcee0527361ec913126005e972cf4b863914f8361582ed24563ff9e03c52e8a9ca3264c56c186b4ec52b7e695ce42ae17ec7ae0257131e1dbf48f2dde242e6e91ea304988135a15482b05fc091355328b39e586e8dd3a4a3a14cb97eef68f9f69728c291f2195d2cce73d4ae90845b1bfc5fae040b94fc359a29511981b9966aeb56d3a7c5e48f8eca815e5be86b3d36e6a27e0e2c4dee6e30f12a7c936b8c98cad5928aca238dfc39cf9f2c5246cbbbb280cb6f99eb49bfd1d78089539072c164c7083371746dedbc4dec1cb9439073af3f2e60f8c505f067961a8c539454fc5341158eccc78532f3e39c3187c9439fc0ff88ee957131d478df063dd50b2ad3fe7a070e905e3868b0203010001a350304e301d0603551d0e04160414e93c04e1802fc284132d26709ef2fd1acfaafec6301f0603551d23041830168014f35f7b7549e37841396a20b67c6b4c5cc93d5841300c0603551d13040530030101ff300d06092a864886f70d010105050003820201000858cbd545f2e92e09906ac39f3d55c13607a651436386c6e90f128773a0eb3f4725d8af35fb7f880436b481f2cf47801825de54f13f8f5920bf3d916e753141de5e59d2debbfc3fc226721f15a16d3b7a4618ea0551639f1d2cb7b9faad1b7e070f23a6e8197c3d7549bba6553fd5db419ce399477f6a0481b90f51c9d307d82cb05cf967828a1ace65207cf86b6d16792245dcf24b4c179f91184736e7e2fcb863a4b5c89b0ac2f368390a10594b95c856e259c77564316898cf87a6817d18585fc976d681d9d510ef2ad37e8ad0e49f5bd499c9ec7fe8f43b17dffb9b7d0dfd8300c1c5389c9ea0be4370dcbf78bd3efc2308d250b866bbca031c0c49ff77a7a5420daa1f1b6a444d366653974c2d179c3009871ee6c89140fca9efdf23bd4b88c6ebaeb9286f58f3cfc21e4874f182d1ecd6058919b03b18db7b795be9cb25fc5166a945ef8e1133cd60312a3234f4649df6166407cbb5ecc838e9e118c05dee7c896a9987655ae7e349cd8166e68d34dea3b4ae892a9f2385053271e860b542be3650503974f3bb6f2688375ab28487da6751d0f2c3a35e78efe30b19d57808ebea2c4453990ad81eb96289c0f99c5080f82092bc6123a340c63a617f3bc4adc1298d88a278a693ef93688611d3b4eded0b6d023ed9f6c2ea8836483197525b1b1bce70a90c3403b094d5f412aa1141b9965ab8314c52f772deffc1008c"
|
||||
rootCA = "308205cd308203b5a0030201020209009ed3ccb1d12ca272300d06092a864886f70d0101050500307d310b3009060355040613024742310f300d06035504080c064c6f6e646f6e31173015060355040a0c0e476f6f676c6520554b204c74642e3121301f060355040b0c184365727469666963617465205472616e73706172656e63793121301f06035504030c184d657267652044656c6179204d6f6e69746f7220526f6f74301e170d3134303731373132303534335a170d3431313230323132303534335a307d310b3009060355040613024742310f300d06035504080c064c6f6e646f6e31173015060355040a0c0e476f6f676c6520554b204c74642e3121301f060355040b0c184365727469666963617465205472616e73706172656e63793121301f06035504030c184d657267652044656c6179204d6f6e69746f7220526f6f7430820222300d06092a864886f70d01010105000382020f003082020a0282020100aa161cf2205ed81ac565483cda426a3db2e588fdb758b17b93ea8d68495d534a01ba4f6cd1c0fc0a128af79c066dc54c3f437e05ba275ee61dbf9cbdb2928183738139397b6189ae738fef2b9b609a6dd8e0b0d0e20b243db936c029cdc2220af2c0e1a5e4aa41a006af458957e2b1178d27156ef0cb717e16d54025d97f43e9916fb240fb85f7d579462fa0ac76c76256843750bf1ccdfeb76c8c47886477644d5ec3235628adf6a09c8488bfa5036de717908151a6b585f273dd9fb5332b9af76e8fbfa91eaf4311816dde27c5c44f2fd06cc2204d7147f77ba6b16a2a5fca470023614729538bee6b3cb07264713832aec161550eb501906802215223acc2564ad1f98bb5934924eb56d383fc7598be45c89d995281c0efb0d206d29a6d25a10a48fe235332379c5ca69e83599faa677dd20823f5c84a961255eca5d4871d54ca1df0774aa117b0f42cd6e9fda7e8a48a53923c5f94043353544e644b5a6562e5cef9fc2bd2fcfcce3323335cf7fe7c4d83c1b7f839c4790192d3ba9aa9f32093aa8ee7cbe708059d538dc663cca1b825331aa836754a0d13de63bf65b6e2044dcdf041f1a0c5a9c3c38fe74cf576d451c23eaa519db32ef9e039bd848a194c3b5e41a55642dc283ddbd73d1dd97ae6951de18ad89d005007fae7e88bc7a3cce8b7ccc49603a0db67c76d58a28d4b77aa7460801e34377d0c5e4606c2e25b0203010001a350304e301d0603551d0e04160414f35f7b7549e37841396a20b67c6b4c5cc93d5841301f0603551d23041830168014f35f7b7549e37841396a20b67c6b4c5cc93d5841300c0603551d13040530030101ff300d06092a864886f70d01010505000382020100771cfea34579a97520d8c2423d68ecd07891f8c7f1c38bf3cd30ea9d3637bfc5d373532ec7656558bef4950646750c6fe085c52d9ffc09e66ebfa2b067de7727cb381d2b25db58b9c2197fd5eb53e020f429b86a3b1f37a07a761a66a5b3ecd797c46695a37ff2d47c54126be6bd28a2a103357227c6b73f7f689b09b48927e6e9a52267a728a115d4bcbb477533dc28f3fc57da735a3ec54fbc36990b17febb7e46b324208c1fa7425a0cba48bdc0381ea8285215261c3c483f2fa6d1da0dba4949107189f32d728a7ff395d43430af3b8ce4be5075bcf67d66661941dc8be37340f8f9282b2d2aadd69065932ad49769f8bc7fc9e5f6e79ff392420ba78de3172778e2b67e4df18440dd561e5a7844c8efa06c0e5f5b8695fa069114a00518fb4c19f9d7855831b5eeebced14b8598daffa49f2dcf505bff6417d84b28e83599d4e0371ef64b2d82ffa068a31044f7322fee2f654ec357c9c121f3458a509728c37f5673412ad0d5e76aa6b4eb1582181a8be404d3dc35e71edd83ee388087d6147c4d86f1cacacface0104df1f4b100c2ceb1be4d1851c4f31e7c4409262185878f23cceb30790143f0d5bd80d2c0ed4260aaa312597d950aaf3c8bcfc812d9a56e8d160dd772a494743710a7e478a046d8a5d505ee6b8cc37fec09dfd4cb57c6c4d8e8ef2a22e1d9e50957852633138d722253e51ba77b0069d38812207a"
|
||||
noExts = "0000"
|
||||
// Precert example taken from entry #2 in argon2018 log
|
||||
issuerKeyHash = "e37689003073a0c649cc656de946c03174d25c566fe3c3805b846f5236943798"
|
||||
precertTBS = "0002db308202d7a0030201020207055667377e8bcc300d06092a864886f70d01010b0500307f310b3009060355040613024742310f300d06035504080c064c6f6e646f6e31173015060355040a0c0e476f6f676c6520554b204c74642e3121301f060355040b0c184365727469666963617465205472616e73706172656e63793123302106035504030c1a4d657267652044656c617920496e7465726d6564696174652031301e170d3137303831303134343331365a170d3138303432363134323430315a3063310b3009060355040613024742310f300d06035504070c064c6f6e646f6e31283026060355040a0c1f476f6f676c65204365727469666963617465205472616e73706172656e637931193017060355040513103135303233373631393632313337303830820122300d06092a864886f70d01010105000382010f003082010a0282010100dea100ff02f31ae6f76f9c26525afcdd0ef6eef780d72b4b1c0ff14fc7ac021852d6f34af20713d05fea2c2e1a4b488b3849c2511cf30fcd2e61d9e7392557498a4ff600c8fdc912f05c8a583a5f2b6a3d3320c6cc10b7eed502de392d11b3c4d57fa6e3ddc69d3f73305bb6441a0359bd526272784523ae5319cffd2993abba54d26c4c1b760c8660b65161a349e415207a6fbb20d02ce13054e6ffa7776bccfd26c3e4220e0e504f102f352260aaa9864411f7eeae8f6071c9ba54b83d11af43ab58dcb6a7d9053654b98b165fb84a27c78361d957c70a064f45bf4501ef744302e497839a34222e94b3018e587c70c130208976d9f80cf6741a95abfa6b810203010001a3818b30818830130603551d25040c300a06082b0601050507030130230603551d11041c301a8218666c6f776572732d746f2d7468652d776f726c642e636f6d300c0603551d130101ff04023000301f0603551d23041830168014e93c04e1802fc284132d26709ef2fd1acfaafec6301d0603551d0e04160414df25c220250d548e08341c26cadc5effc177841c"
|
||||
precertDER = "30820504308202eca0030201020207055667377e8bcc300d06092a864886f70d01010b0500307f310b3009060355040613024742310f300d06035504080c064c6f6e646f6e31173015060355040a0c0e476f6f676c6520554b204c74642e3121301f060355040b0c184365727469666963617465205472616e73706172656e63793123302106035504030c1a4d657267652044656c617920496e7465726d6564696174652031301e170d3137303831303134343331365a170d3138303432363134323430315a3063310b3009060355040613024742310f300d06035504070c064c6f6e646f6e31283026060355040a0c1f476f6f676c65204365727469666963617465205472616e73706172656e637931193017060355040513103135303233373631393632313337303830820122300d06092a864886f70d01010105000382010f003082010a0282010100dea100ff02f31ae6f76f9c26525afcdd0ef6eef780d72b4b1c0ff14fc7ac021852d6f34af20713d05fea2c2e1a4b488b3849c2511cf30fcd2e61d9e7392557498a4ff600c8fdc912f05c8a583a5f2b6a3d3320c6cc10b7eed502de392d11b3c4d57fa6e3ddc69d3f73305bb6441a0359bd526272784523ae5319cffd2993abba54d26c4c1b760c8660b65161a349e415207a6fbb20d02ce13054e6ffa7776bccfd26c3e4220e0e504f102f352260aaa9864411f7eeae8f6071c9ba54b83d11af43ab58dcb6a7d9053654b98b165fb84a27c78361d957c70a064f45bf4501ef744302e497839a34222e94b3018e587c70c130208976d9f80cf6741a95abfa6b810203010001a381a030819d30130603551d25040c300a06082b0601050507030130230603551d11041c301a8218666c6f776572732d746f2d7468652d776f726c642e636f6d300c0603551d130101ff04023000301f0603551d23041830168014e93c04e1802fc284132d26709ef2fd1acfaafec6301d0603551d0e04160414df25c220250d548e08341c26cadc5effc177841c3013060a2b06010401d6790204030101ff04020500300d06092a864886f70d01010b05000382020100ae9ca16ec19bb469d08628b1296f50e3e15b362e2b18c691b11eef3af9ce655bf74e0c21c84f6091132851ba78465c3ae97a1409ee7505395d4e7e0318189029a12bf1c3ba2b6f3231c7aac13dbbfaade8d56f0fbe91d32440ad0ab816184c72392154275ead8418cc62e4e2b08de1b14acb6b27c0f36fa586feb875666f46d232a32ef022440d52cdd8bd31a42de55bfa77de8742816f086830b07eedbde545af5a2b9dd17bd49ded508589a0673f6e0d55f210818422093fd10939f0c81521ca654958e6e01b76ef8c7380bdb331e67d44ccb18a83ed04d97d463c37c7cbc592768e2373e198a1d64be3bd22d1833994706797461d05a85e779cd6e2b4b2b14e81d1eca454f29780c47a7366041ace1a48319eff3f1f04bbd471d5125774ef050e47bf664a98101b7be3337bb786b760a92be46488c6a15f72972a4b7c932c736311f0ac1d40920580329657f00e26cfb6d3b1db1eb7a95952fbcbfcbaf9d17587f03aeb9c3b403d1dccad895316658d35fe385fc5a62b60db36e3f07c4798314936aeb3f40094aee9ec1350ea8f68d1aeb41b211ecd0c9e29c5fa2d6576bcb2ad5ec8cc936e1f5a127afa0de3ae490b914adaa733f18ed9348d497e10ba4aa3008f84deec6976292dc0d3c2aa523602188916dd468b47f3d571e71fe51cd293c805d1280a53ab9f519a616d889303be461354edfc29dc3cc85d8570264cf4"
|
||||
precertCA = "308205c8308203b0a00302010202021001300d06092a864886f70d0101050500307d310b3009060355040613024742310f300d06035504080c064c6f6e646f6e31173015060355040a0c0e476f6f676c6520554b204c74642e3121301f060355040b0c184365727469666963617465205472616e73706172656e63793121301f06035504030c184d657267652044656c6179204d6f6e69746f7220526f6f74301e170d3134303731373132323633305a170d3139303731363132323633305a307f310b3009060355040613024742310f300d06035504080c064c6f6e646f6e31173015060355040a0c0e476f6f676c6520554b204c74642e3121301f060355040b0c184365727469666963617465205472616e73706172656e63793123302106035504030c1a4d657267652044656c617920496e7465726d656469617465203130820222300d06092a864886f70d01010105000382020f003082020a0282020100c1e874feff9aeef303bbfa63453881faaf8dc1c22c09641daf430381f33bc157bf6c4c8a8d57b1abc792859d20f2191509c597c437b14673dea5af4bea14396dd436dc620555d7953e0ede01f7ffb44f3ff7cde64ed245634e0df0aafce9c3ac5eb63d8de1d969cac8854195403a9f9d1d4c3dceedf1351edd945743bc54ab745b204fb5259fe3edf695c2cf90b886c48ff680b744fec2691b4345242b31c36f3118b727c5de5c25ec1aa30a4c2461c5119ef6bb90d816e6e44c5b9955bfa2ed3416bf6e4a53c92fafac0d1f0b8bf3d35be0d4f61ca05d28fc662dfa588fba3ee0380470c012ded9e51bbf1e7a25efa745784c49d05eaf8dcee0527361ec913126005e972cf4b863914f8361582ed24563ff9e03c52e8a9ca3264c56c186b4ec52b7e695ce42ae17ec7ae0257131e1dbf48f2dde242e6e91ea304988135a15482b05fc091355328b39e586e8dd3a4a3a14cb97eef68f9f69728c291f2195d2cce73d4ae90845b1bfc5fae040b94fc359a29511981b9966aeb56d3a7c5e48f8eca815e5be86b3d36e6a27e0e2c4dee6e30f12a7c936b8c98cad5928aca238dfc39cf9f2c5246cbbbb280cb6f99eb49bfd1d78089539072c164c7083371746dedbc4dec1cb9439073af3f2e60f8c505f067961a8c539454fc5341158eccc78532f3e39c3187c9439fc0ff88ee957131d478df063dd50b2ad3fe7a070e905e3868b0203010001a350304e301d0603551d0e04160414e93c04e1802fc284132d26709ef2fd1acfaafec6301f0603551d23041830168014f35f7b7549e37841396a20b67c6b4c5cc93d5841300c0603551d13040530030101ff300d06092a864886f70d010105050003820201000858cbd545f2e92e09906ac39f3d55c13607a651436386c6e90f128773a0eb3f4725d8af35fb7f880436b481f2cf47801825de54f13f8f5920bf3d916e753141de5e59d2debbfc3fc226721f15a16d3b7a4618ea0551639f1d2cb7b9faad1b7e070f23a6e8197c3d7549bba6553fd5db419ce399477f6a0481b90f51c9d307d82cb05cf967828a1ace65207cf86b6d16792245dcf24b4c179f91184736e7e2fcb863a4b5c89b0ac2f368390a10594b95c856e259c77564316898cf87a6817d18585fc976d681d9d510ef2ad37e8ad0e49f5bd499c9ec7fe8f43b17dffb9b7d0dfd8300c1c5389c9ea0be4370dcbf78bd3efc2308d250b866bbca031c0c49ff77a7a5420daa1f1b6a444d366653974c2d179c3009871ee6c89140fca9efdf23bd4b88c6ebaeb9286f58f3cfc21e4874f182d1ecd6058919b03b18db7b795be9cb25fc5166a945ef8e1133cd60312a3234f4649df6166407cbb5ecc838e9e118c05dee7c896a9987655ae7e349cd8166e68d34dea3b4ae892a9f2385053271e860b542be3650503974f3bb6f2688375ab28487da6751d0f2c3a35e78efe30b19d57808ebea2c4453990ad81eb96289c0f99c5080f82092bc6123a340c63a617f3bc4adc1298d88a278a693ef93688611d3b4eded0b6d023ed9f6c2ea8836483197525b1b1bce70a90c3403b094d5f412aa1141b9965ab8314c52f772deffc1008c"
|
||||
precertRoot = "308205cd308203b5a0030201020209009ed3ccb1d12ca272300d06092a864886f70d0101050500307d310b3009060355040613024742310f300d06035504080c064c6f6e646f6e31173015060355040a0c0e476f6f676c6520554b204c74642e3121301f060355040b0c184365727469666963617465205472616e73706172656e63793121301f06035504030c184d657267652044656c6179204d6f6e69746f7220526f6f74301e170d3134303731373132303534335a170d3431313230323132303534335a307d310b3009060355040613024742310f300d06035504080c064c6f6e646f6e31173015060355040a0c0e476f6f676c6520554b204c74642e3121301f060355040b0c184365727469666963617465205472616e73706172656e63793121301f06035504030c184d657267652044656c6179204d6f6e69746f7220526f6f7430820222300d06092a864886f70d01010105000382020f003082020a0282020100aa161cf2205ed81ac565483cda426a3db2e588fdb758b17b93ea8d68495d534a01ba4f6cd1c0fc0a128af79c066dc54c3f437e05ba275ee61dbf9cbdb2928183738139397b6189ae738fef2b9b609a6dd8e0b0d0e20b243db936c029cdc2220af2c0e1a5e4aa41a006af458957e2b1178d27156ef0cb717e16d54025d97f43e9916fb240fb85f7d579462fa0ac76c76256843750bf1ccdfeb76c8c47886477644d5ec3235628adf6a09c8488bfa5036de717908151a6b585f273dd9fb5332b9af76e8fbfa91eaf4311816dde27c5c44f2fd06cc2204d7147f77ba6b16a2a5fca470023614729538bee6b3cb07264713832aec161550eb501906802215223acc2564ad1f98bb5934924eb56d383fc7598be45c89d995281c0efb0d206d29a6d25a10a48fe235332379c5ca69e83599faa677dd20823f5c84a961255eca5d4871d54ca1df0774aa117b0f42cd6e9fda7e8a48a53923c5f94043353544e644b5a6562e5cef9fc2bd2fcfcce3323335cf7fe7c4d83c1b7f839c4790192d3ba9aa9f32093aa8ee7cbe708059d538dc663cca1b825331aa836754a0d13de63bf65b6e2044dcdf041f1a0c5a9c3c38fe74cf576d451c23eaa519db32ef9e039bd848a194c3b5e41a55642dc283ddbd73d1dd97ae6951de18ad89d005007fae7e88bc7a3cce8b7ccc49603a0db67c76d58a28d4b77aa7460801e34377d0c5e4606c2e25b0203010001a350304e301d0603551d0e04160414f35f7b7549e37841396a20b67c6b4c5cc93d5841301f0603551d23041830168014f35f7b7549e37841396a20b67c6b4c5cc93d5841300c0603551d13040530030101ff300d06092a864886f70d01010505000382020100771cfea34579a97520d8c2423d68ecd07891f8c7f1c38bf3cd30ea9d3637bfc5d373532ec7656558bef4950646750c6fe085c52d9ffc09e66ebfa2b067de7727cb381d2b25db58b9c2197fd5eb53e020f429b86a3b1f37a07a761a66a5b3ecd797c46695a37ff2d47c54126be6bd28a2a103357227c6b73f7f689b09b48927e6e9a52267a728a115d4bcbb477533dc28f3fc57da735a3ec54fbc36990b17febb7e46b324208c1fa7425a0cba48bdc0381ea8285215261c3c483f2fa6d1da0dba4949107189f32d728a7ff395d43430af3b8ce4be5075bcf67d66661941dc8be37340f8f9282b2d2aadd69065932ad49769f8bc7fc9e5f6e79ff392420ba78de3172778e2b67e4df18440dd561e5a7844c8efa06c0e5f5b8695fa069114a00518fb4c19f9d7855831b5eeebced14b8598daffa49f2dcf505bff6417d84b28e83599d4e0371ef64b2d82ffa068a31044f7322fee2f654ec357c9c121f3458a509728c37f5673412ad0d5e76aa6b4eb1582181a8be404d3dc35e71edd83ee388087d6147c4d86f1cacacface0104df1f4b100c2ceb1be4d1851c4f31e7c4409262185878f23cceb30790143f0d5bd80d2c0ed4260aaa312597d950aaf3c8bcfc812d9a56e8d160dd772a494743710a7e478a046d8a5d505ee6b8cc37fec09dfd4cb57c6c4d8e8ef2a22e1d9e50957852633138d722253e51ba77b0069d38812207a"
|
||||
)
|
||||
var tests = []struct {
|
||||
leaf LeafEntry
|
||||
wantCert bool
|
||||
wantPrecert bool
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
leaf: LeafEntry{},
|
||||
wantErr: "failed to unmarshal",
|
||||
},
|
||||
{
|
||||
leaf: LeafEntry{
|
||||
// {version + leaf_type + timestamp + entry_type + len + cert + exts}
|
||||
LeafInput: dh("00" + "00" + "0000015dcc2b99c8" + "0000" + "0004f3" + leafDER + noExts),
|
||||
ExtraData: dh("000ba3" + "0005cc" + leafCA + "0005d1" + rootCA),
|
||||
},
|
||||
wantCert: true,
|
||||
},
|
||||
{
|
||||
leaf: LeafEntry{
|
||||
LeafInput: dh("00" + "00" + "0000015dcc2b99c8" + "0000" + "0004f3" + leafDER + noExts + "ff"),
|
||||
ExtraData: dh("000ba3" + "0005cc" + leafCA + "0005d1" + rootCA),
|
||||
},
|
||||
wantErr: "trailing data",
|
||||
},
|
||||
{
|
||||
leaf: LeafEntry{
|
||||
LeafInput: dh("00" + "00" + "0000015dcc2b99c8" + "0000" + "0004f3" + leafDER + noExts),
|
||||
ExtraData: dh("000ba3" + "0005cc" + leafCA + "0005d1" + rootCA + "00"),
|
||||
},
|
||||
wantErr: "trailing data",
|
||||
},
|
||||
{
|
||||
leaf: LeafEntry{
|
||||
LeafInput: dh("00" + "00" + "0000015dcc2b99c8" + "0000" + "0004f3" + leafDER + noExts),
|
||||
},
|
||||
wantErr: "failed to unmarshal",
|
||||
},
|
||||
{
|
||||
leaf: LeafEntry{
|
||||
LeafInput: dh("00" + "00" + "0000015dcc2b99c8" + "8000" + "0004f3" + leafDER + noExts),
|
||||
},
|
||||
wantErr: "unknown entry type",
|
||||
},
|
||||
{
|
||||
leaf: LeafEntry{
|
||||
// version + leaf_type + timestamp + entry_type + key_hash + tbs + exts
|
||||
LeafInput: dh("00" + "00" + "0000015dcc997890" + "0001" + issuerKeyHash + precertTBS + noExts),
|
||||
ExtraData: dh("000508" + precertDER +
|
||||
("000ba3" + "0005cc" + precertCA + "0005d1" + precertRoot)),
|
||||
},
|
||||
wantPrecert: true,
|
||||
},
|
||||
{
|
||||
leaf: LeafEntry{
|
||||
LeafInput: dh("00" + "00" + "0000015dcc997890" + "0001" + issuerKeyHash + precertTBS + noExts),
|
||||
ExtraData: dh("000508" + precertDER +
|
||||
("000ba3" + "0005cc" + precertCA + "0005d1" + precertRoot) + "ff"),
|
||||
},
|
||||
wantErr: "trailing data",
|
||||
},
|
||||
{
|
||||
leaf: LeafEntry{
|
||||
LeafInput: dh("00" + "00" + "0000015dcc997890" + "0001" + issuerKeyHash + precertTBS + noExts + "ff"),
|
||||
ExtraData: dh("000508" + precertDER +
|
||||
("000ba3" + "0005cc" + precertCA + "0005d1" + precertRoot)),
|
||||
},
|
||||
wantErr: "trailing data",
|
||||
},
|
||||
{
|
||||
leaf: LeafEntry{
|
||||
LeafInput: dh("00" + "00" + "0000015dcc997890" + "0001" + issuerKeyHash + precertTBS + noExts),
|
||||
},
|
||||
wantErr: "failed to unmarshal",
|
||||
},
|
||||
}
|
||||
for i, test := range tests {
|
||||
got, err := LogEntryFromLeaf(int64(i), &test.leaf)
|
||||
if err != nil {
|
||||
if test.wantErr == "" {
|
||||
t.Errorf("LogEntryFromLeaf(%d) = _, %v; want _, nil", i, err)
|
||||
} else if !strings.Contains(err.Error(), test.wantErr) {
|
||||
t.Errorf("LogEntryFromLeaf(%d) = _, %v; want _, err containing %q", i, err, test.wantErr)
|
||||
}
|
||||
} else if test.wantErr != "" {
|
||||
t.Errorf("LogEntryFromLeaf(%d) = _, nil; want _, err containing %q", i, test.wantErr)
|
||||
}
|
||||
if gotCert := (got != nil && got.X509Cert != nil); gotCert != test.wantCert {
|
||||
t.Errorf("LogEntryFromLeaf(%d).X509Cert = %v; want %v", i, gotCert, test.wantCert)
|
||||
}
|
||||
if gotPrecert := (got != nil && got.Precert != nil); gotPrecert != test.wantPrecert {
|
||||
t.Errorf("LogEntryFromLeaf(%d).Precert = %v; want %v", i, gotPrecert, test.wantPrecert)
|
||||
}
|
||||
}
|
||||
}
|
101
vendor/github.com/google/certificate-transparency-go/signatures.go
generated
vendored
Normal file
101
vendor/github.com/google/certificate-transparency-go/signatures.go
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ct
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"encoding/pem"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/google/certificate-transparency-go/tls"
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
)
|
||||
|
||||
var allowVerificationWithNonCompliantKeys = flag.Bool("allow_verification_with_non_compliant_keys", false,
|
||||
"Allow a SignatureVerifier to use keys which are technically non-compliant with RFC6962.")
|
||||
|
||||
// PublicKeyFromPEM parses a PEM formatted block and returns the public key contained within and any remaining unread bytes, or an error.
|
||||
func PublicKeyFromPEM(b []byte) (crypto.PublicKey, SHA256Hash, []byte, error) {
|
||||
p, rest := pem.Decode(b)
|
||||
if p == nil {
|
||||
return nil, [sha256.Size]byte{}, rest, fmt.Errorf("no PEM block found in %s", string(b))
|
||||
}
|
||||
k, err := x509.ParsePKIXPublicKey(p.Bytes)
|
||||
return k, sha256.Sum256(p.Bytes), rest, err
|
||||
}
|
||||
|
||||
// SignatureVerifier can verify signatures on SCTs and STHs
|
||||
type SignatureVerifier struct {
|
||||
pubKey crypto.PublicKey
|
||||
}
|
||||
|
||||
// NewSignatureVerifier creates a new SignatureVerifier using the passed in PublicKey.
|
||||
func NewSignatureVerifier(pk crypto.PublicKey) (*SignatureVerifier, error) {
|
||||
switch pkType := pk.(type) {
|
||||
case *rsa.PublicKey:
|
||||
if pkType.N.BitLen() < 2048 {
|
||||
e := fmt.Errorf("public key is RSA with < 2048 bits (size:%d)", pkType.N.BitLen())
|
||||
if !(*allowVerificationWithNonCompliantKeys) {
|
||||
return nil, e
|
||||
}
|
||||
log.Printf("WARNING: %v", e)
|
||||
}
|
||||
case *ecdsa.PublicKey:
|
||||
params := *(pkType.Params())
|
||||
if params != *elliptic.P256().Params() {
|
||||
e := fmt.Errorf("public is ECDSA, but not on the P256 curve")
|
||||
if !(*allowVerificationWithNonCompliantKeys) {
|
||||
return nil, e
|
||||
}
|
||||
log.Printf("WARNING: %v", e)
|
||||
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("Unsupported public key type %v", pkType)
|
||||
}
|
||||
|
||||
return &SignatureVerifier{
|
||||
pubKey: pk,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// VerifySignature verifies the given signature sig matches the data.
|
||||
func (s SignatureVerifier) VerifySignature(data []byte, sig tls.DigitallySigned) error {
|
||||
return tls.VerifySignature(s.pubKey, data, sig)
|
||||
}
|
||||
|
||||
// VerifySCTSignature verifies that the SCT's signature is valid for the given LogEntry.
|
||||
func (s SignatureVerifier) VerifySCTSignature(sct SignedCertificateTimestamp, entry LogEntry) error {
|
||||
sctData, err := SerializeSCTSignatureInput(sct, entry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.VerifySignature(sctData, tls.DigitallySigned(sct.Signature))
|
||||
}
|
||||
|
||||
// VerifySTHSignature verifies that the STH's signature is valid.
|
||||
func (s SignatureVerifier) VerifySTHSignature(sth SignedTreeHead) error {
|
||||
sthData, err := SerializeSTHSignatureInput(sth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.VerifySignature(sthData, tls.DigitallySigned(sth.TreeHeadSignature))
|
||||
}
|
493
vendor/github.com/google/certificate-transparency-go/signatures_test.go
generated
vendored
Normal file
493
vendor/github.com/google/certificate-transparency-go/signatures_test.go
generated
vendored
Normal file
@ -0,0 +1,493 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ct
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"encoding/hex"
|
||||
mrand "math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/google/certificate-transparency-go/tls"
|
||||
)
|
||||
|
||||
const (
|
||||
sigTestDERCertString = "308202ca30820233a003020102020102300d06092a864886f70d01010505003055310b300" +
|
||||
"906035504061302474231243022060355040a131b4365727469666963617465205472616e" +
|
||||
"73706172656e6379204341310e300c0603550408130557616c65733110300e06035504071" +
|
||||
"3074572772057656e301e170d3132303630313030303030305a170d323230363031303030" +
|
||||
"3030305a3052310b30090603550406130247423121301f060355040a13184365727469666" +
|
||||
"963617465205472616e73706172656e6379310e300c0603550408130557616c6573311030" +
|
||||
"0e060355040713074572772057656e30819f300d06092a864886f70d010101050003818d0" +
|
||||
"030818902818100b8742267898b99ba6bfd6e6f7ada8e54337f58feb7227c46248437ba5f" +
|
||||
"89b007cbe1ecb4545b38ed23fddbf6b9742cafb638157f68184776a1b38ab39318ddd7344" +
|
||||
"89b4d750117cd83a220a7b52f295d1e18571469a581c23c68c57d973761d9787a091fb586" +
|
||||
"4936b166535e21b427e3c6d690b2e91a87f36b7ec26f59ce53b50203010001a381ac3081a" +
|
||||
"9301d0603551d0e041604141184e1187c87956dffc31dd0521ff564efbeae8d307d060355" +
|
||||
"1d23047630748014a3b8d89ba2690dfb48bbbf87c1039ddce56256c6a159a4573055310b3" +
|
||||
"00906035504061302474231243022060355040a131b436572746966696361746520547261" +
|
||||
"6e73706172656e6379204341310e300c0603550408130557616c65733110300e060355040" +
|
||||
"713074572772057656e82010030090603551d1304023000300d06092a864886f70d010105" +
|
||||
"050003818100292ecf6e46c7a0bcd69051739277710385363341c0a9049637279707ae23c" +
|
||||
"c5128a4bdea0d480ed0206b39e3a77a2b0c49b0271f4140ab75c1de57aba498e09459b479" +
|
||||
"cf92a4d5d5dd5cbe3f0a11e25f04078df88fc388b61b867a8de46216c0e17c31fc7d8003e" +
|
||||
"cc37be22292f84242ab87fb08bd4dfa3c1b9ce4d3ee6667da"
|
||||
|
||||
sigTestSCTTimestamp = 1348589665525
|
||||
|
||||
sigTestCertSCTSignatureEC = "0403" + "0048" +
|
||||
"3046022100d3f7690e7ee80d9988a54a3821056393e9eb0c686ad67fbae3686c888fb1a3c" +
|
||||
"e022100f9a51c6065bbba7ad7116a31bea1c31dbed6a921e1df02e4b403757fae3254ae"
|
||||
|
||||
sigTestEC256PrivateKeyPEM = "-----BEGIN EC PRIVATE KEY-----\n" +
|
||||
"MHcCAQEEIG8QAquNnarN6Ik2cMIZtPBugh9wNRe0e309MCmDfBGuoAoGCCqGSM49\n" +
|
||||
"AwEHoUQDQgAES0AfBkjr7b8b19p5Gk8plSAN16wWXZyhYsH6FMCEUK60t7pem/ck\n" +
|
||||
"oPX8hupuaiJzJS0ZQ0SEoJGlFxkUFwft5g==\n" +
|
||||
"-----END EC PRIVATE KEY-----\n"
|
||||
|
||||
sigTestEC256PublicKeyPEM = "-----BEGIN PUBLIC KEY-----\n" +
|
||||
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAES0AfBkjr7b8b19p5Gk8plSAN16wW\n" +
|
||||
"XZyhYsH6FMCEUK60t7pem/ckoPX8hupuaiJzJS0ZQ0SEoJGlFxkUFwft5g==\n" +
|
||||
"-----END PUBLIC KEY-----\n"
|
||||
|
||||
sigTestEC256PublicKey2PEM = "-----BEGIN PUBLIC KEY-----\n" +
|
||||
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfahLEimAoz2t01p3uMziiLOl/fHT\n" +
|
||||
"DM0YDOhBRuiBARsV4UvxG2LdNgoIGLrtCzWE0J5APC2em4JlvR8EEEFMoA==\n" +
|
||||
"-----END PUBLIC KEY-----\n"
|
||||
|
||||
sigTestRSAPrivateKeyPEM = "-----BEGIN RSA PRIVATE KEY-----\n" +
|
||||
"MIIEpAIBAAKCAQEAxy7llbig9kL0wo5AyV1FhmJLvWTWxzAMwGdhG1h1CqQpaWut\n" +
|
||||
"XGI9WKRDJSZ/9dr9vgvqdRX2QsnUdJbJ3cz5Z1ie/RdT/mSVO7ZEqvJS93PIHnqu\n" +
|
||||
"FZXxNnIerGnQ7guC+Zm9BlQ2DIhYpnvVRRVyD/D8KT92R7qOu3JACduoMrF1synk\n" +
|
||||
"nL8rb8lZvCej8tbhJ38yibMWTmkxsFS+a29Xqk8pkhgwIwvUZqcMaqZo+4/iCuKL\n" +
|
||||
"bVc85V98SvbcnmsX3gqeQnyRtxlctlclcbvHmJt5U+3yF1UtcuiyZf1gjcAqnOgv\n" +
|
||||
"ZZYzsodXi0KGV7NRQhTPvwH0C8In2qL+v4qWAQIDAQABAoIBAQCdyqsaNw9cx6I6\n" +
|
||||
"1pLAcuF3GjvCKDZ1ybzwV3V4QlVGPtKHr0PBIhpTNJ30ulE4pWnKuoncg695LYbf\n" +
|
||||
"be0xhwY1NuGMwoRJzcjjavtvKVVMry5j5vAuLYDPjwx5rcJUMk5qCb7TWrcOqp0A\n" +
|
||||
"Fq3XcqvPsSsyShIbtNEJ8fKFXLwcm07bGDgOacrXieP/nL2Hh6joeAJLgnKAOtU5\n" +
|
||||
"qw6fdweYGThfhdCwaBq0WSaxj6nMG3Q40bHurvdOAtU1GF2a27BGsnfKFyKlvk8+\n" +
|
||||
"K7tCc4oXo4WWEUuOwu6SmB1kYIZLf258B0PFQJwN8OA3Mbek+F4Bm3DzWe1aLS5L\n" +
|
||||
"wpOYxrq5AoGBAO0sGq+ic+9K81FvBBUYXiFtt9rv3nU9jZhjqpfvdqRs2KXt8ldz\n" +
|
||||
"2M+JCRFHt8rLDEutK/NZuZcq3wAXS3EeMIp33QZ7Yuj5LeG9eD0asX8yq51Toua3\n" +
|
||||
"gRDbiR00Vz/3BINM8JufN/sPLoUiuAV5mlOTktZ8+z7ixO4ravMB1Z9HAoGBANb+\n" +
|
||||
"w+1Hre8+4JEnl3yRh1UNDmbhCc2tRxCD4QJyb9qaOl2IK1QXuDcwD8owdenwOrAi\n" +
|
||||
"I5yKx7y4oKNfdSrP2wlAGS/GAEL5f+JhLtv2cNoKNxMXNRgYfJAQeMKBjINdECia\n" +
|
||||
"G89lbPVCm+F3guzrO70giA4617GFSEA31rRC1BR3AoGBAKcQLiwRrsCcdxChtqp1\n" +
|
||||
"Y7kAZEXgOT80gI0bh4tGrrfbxC/9kHtxqwNlb/GwJxK+PIcCELd2OHj3ReX2grnH\n" +
|
||||
"nkGrdRGf0GhzPZKJuCyypN0IgEJuK42BLXUGb2sW926jPZaPl9zHJtO+OfKmJiIV\n" +
|
||||
"KlQ8224i04fUjQuHoepTHHr5AoGAS8AZ4lmWFCywTRSJEG/qIfJmt6LkpF5AIraE\n" +
|
||||
"qisN9BTRKbFXqtpsoq1BcvjeIt3sn7B3oalYNMtMdiOlEb+Iqlq2RRnbb72e7HFX\n" +
|
||||
"ZFMRchGVVBmiMGo4QT48fjPNAV/h2Jxr3ggbetLMP4WvULCVLM7wgSsEYlzWlyHV\n" +
|
||||
"eU/uj4MCgYADGpY3Q3ueB23eTTnAMtESwmAQvjTBOPKVpaV/ohHb8BdzAjwcgEUA\n" +
|
||||
"wB1be/bHCrLbW09Pi3HVp0I0x0mBAYoUP2NRYKCYlhs28cu+ygB4nsy+YZPg00+E\n" +
|
||||
"ByqqrQ0zuN82ytXzRFHmh2Hb2O+HOj6aJjgVuj/rR7aifIt8scSAhg==\n" +
|
||||
"-----END RSA PRIVATE KEY-----\n"
|
||||
|
||||
sigTestRSAPublicKeyPEM = "-----BEGIN PUBLIC KEY-----\n" +
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxy7llbig9kL0wo5AyV1F\n" +
|
||||
"hmJLvWTWxzAMwGdhG1h1CqQpaWutXGI9WKRDJSZ/9dr9vgvqdRX2QsnUdJbJ3cz5\n" +
|
||||
"Z1ie/RdT/mSVO7ZEqvJS93PIHnquFZXxNnIerGnQ7guC+Zm9BlQ2DIhYpnvVRRVy\n" +
|
||||
"D/D8KT92R7qOu3JACduoMrF1synknL8rb8lZvCej8tbhJ38yibMWTmkxsFS+a29X\n" +
|
||||
"qk8pkhgwIwvUZqcMaqZo+4/iCuKLbVc85V98SvbcnmsX3gqeQnyRtxlctlclcbvH\n" +
|
||||
"mJt5U+3yF1UtcuiyZf1gjcAqnOgvZZYzsodXi0KGV7NRQhTPvwH0C8In2qL+v4qW\n" +
|
||||
"AQIDAQAB\n" +
|
||||
"-----END PUBLIC KEY-----\n"
|
||||
|
||||
sigTestCertSCTSignatureRSA = "0401" + "0100" +
|
||||
"6bc1fecfe9052036e31278cd7eded90d000b127f2b657831baf5ecb31ee3" +
|
||||
"c17497abd9562df6319928a36df0ab1a1a917b3f4530e1ca0000ae6c4a0c" +
|
||||
"0efada7df83beb95da8eea98f1a27c70afa1ccaa7a0245e1db785b1c0d9f" +
|
||||
"ee307e926e14bed1eac0d01c34939e659360432a9552c02b89c3ef3c44aa" +
|
||||
"22fc31f2444522975ee83989dd7af1ab05b91bbf0985ca4d04245b68a683" +
|
||||
"01d300f0c976ce13d58618dad1b49c0ec5cdc4352016823fc88c479ef214" +
|
||||
"76c5f19923af207dbb1b2cff72d4e1e5ee77dd420b85d0f9dcc30a0f617c" +
|
||||
"2d3c916eb77f167323500d1b53dc4253321a106e441af343cf2f68630873" +
|
||||
"abd43ca52629c586107eb7eb85f2c3ee"
|
||||
|
||||
sigTestCertSCTSignatureUnsupportedSignatureAlgorithm = "0402" + "0000"
|
||||
|
||||
sigTestCertSCTSignatureUnsupportedHashAlgorithm = "0303" + "0000"
|
||||
|
||||
// Some time in September 2012.
|
||||
sigTestDefaultSTHTimestamp = 1348589667204
|
||||
|
||||
sigTestDefaultTreeSize = 42
|
||||
|
||||
// *Some* hash that we pretend is a valid root hash.
|
||||
sigTestDefaultRootHash = "18041bd4665083001fba8c5411d2d748e8abbfdcdfd9218cb02b68a78e7d4c23"
|
||||
|
||||
sigTestDefaultSTHSerialized = "000100000139fe354384000000000000002a18041bd4665083001fba8c5411d2d748e8abb" +
|
||||
"fdcdfd9218cb02b68a78e7d4c23"
|
||||
|
||||
sigTestDefaultSTHSignature = "0403" + "0048" +
|
||||
"3046022100befd8060563763a5e49ba53e6443c13f7624fd6403178113736e16012aca983" +
|
||||
"e022100f572568dbfe9a86490eb915c4ee16ad5ecd708fed35ed4e5cd1b2c3f087b4130"
|
||||
|
||||
sigTestKeyIDEC = "b69d879e3f2c4402556dcda2f6b2e02ff6b6df4789c53000e14f4b125ae847aa"
|
||||
|
||||
sigTestKeyIDRSA = "b853f84c71a7aa5f23905ba5340f183af927c330c7ce590ba1524981c4ec4358"
|
||||
)
|
||||
|
||||
func mustDehex(t *testing.T, h string) []byte {
|
||||
t.Helper()
|
||||
r, err := hex.DecodeString(h)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to decode hex string (%s): %v", h, err)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func sigTestSCTWithSignature(t *testing.T, sig, keyID string) SignedCertificateTimestamp {
|
||||
t.Helper()
|
||||
var ds DigitallySigned
|
||||
if _, err := tls.Unmarshal(mustDehex(t, sig), &ds); err != nil {
|
||||
t.Fatalf("Failed to unmarshal sigTestCertSCTSignatureEC: %v", err)
|
||||
}
|
||||
var id LogID
|
||||
copy(id.KeyID[:], mustDehex(t, keyID))
|
||||
return SignedCertificateTimestamp{
|
||||
SCTVersion: V1,
|
||||
LogID: id,
|
||||
Timestamp: sigTestSCTTimestamp,
|
||||
Signature: ds,
|
||||
}
|
||||
}
|
||||
|
||||
func sigTestSCTEC(t *testing.T) SignedCertificateTimestamp {
|
||||
t.Helper()
|
||||
return sigTestSCTWithSignature(t, sigTestCertSCTSignatureEC, sigTestKeyIDEC)
|
||||
}
|
||||
|
||||
func sigTestSCTRSA(t *testing.T) SignedCertificateTimestamp {
|
||||
t.Helper()
|
||||
return sigTestSCTWithSignature(t, sigTestCertSCTSignatureRSA, sigTestKeyIDEC)
|
||||
}
|
||||
|
||||
func sigTestECPublicKey(t *testing.T) crypto.PublicKey {
|
||||
t.Helper()
|
||||
pk, _, _, err := PublicKeyFromPEM([]byte(sigTestEC256PublicKeyPEM))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse sigTestEC256PublicKey: %v", err)
|
||||
}
|
||||
return pk
|
||||
}
|
||||
|
||||
func sigTestECPublicKey2(t *testing.T) crypto.PublicKey {
|
||||
t.Helper()
|
||||
pk, _, _, err := PublicKeyFromPEM([]byte(sigTestEC256PublicKey2PEM))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse sigTestEC256PublicKey2: %v", err)
|
||||
}
|
||||
return pk
|
||||
}
|
||||
|
||||
func sigTestRSAPublicKey(t *testing.T) crypto.PublicKey {
|
||||
t.Helper()
|
||||
pk, _, _, err := PublicKeyFromPEM([]byte(sigTestRSAPublicKeyPEM))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse sigTestRSAPublicKey: %v", err)
|
||||
}
|
||||
return pk
|
||||
}
|
||||
|
||||
func sigTestCertLogEntry(t *testing.T) LogEntry {
|
||||
t.Helper()
|
||||
return LogEntry{
|
||||
Index: 0,
|
||||
Leaf: MerkleTreeLeaf{
|
||||
Version: V1,
|
||||
LeafType: TimestampedEntryLeafType,
|
||||
TimestampedEntry: &TimestampedEntry{
|
||||
Timestamp: sigTestSCTTimestamp,
|
||||
EntryType: X509LogEntryType,
|
||||
X509Entry: &ASN1Cert{Data: mustDehex(t, sigTestDERCertString)},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func sigTestDefaultSTH(t *testing.T) SignedTreeHead {
|
||||
t.Helper()
|
||||
var ds DigitallySigned
|
||||
if _, err := tls.Unmarshal(mustDehex(t, sigTestDefaultSTHSignature), &ds); err != nil {
|
||||
t.Fatalf("Failed to unmarshal sigTestCertSCTSignatureEC: %v", err)
|
||||
}
|
||||
var rootHash SHA256Hash
|
||||
copy(rootHash[:], mustDehex(t, sigTestDefaultRootHash))
|
||||
return SignedTreeHead{
|
||||
Version: V1,
|
||||
Timestamp: sigTestDefaultSTHTimestamp,
|
||||
TreeSize: sigTestDefaultTreeSize,
|
||||
SHA256RootHash: rootHash,
|
||||
TreeHeadSignature: ds,
|
||||
}
|
||||
}
|
||||
|
||||
func mustCreateSignatureVerifier(t *testing.T, pk crypto.PublicKey) SignatureVerifier {
|
||||
t.Helper()
|
||||
sv, err := NewSignatureVerifier(pk)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create SignatureVerifier: %v", err)
|
||||
}
|
||||
return *sv
|
||||
}
|
||||
|
||||
func corruptByteAt(b []byte, pos int) {
|
||||
b[pos] ^= byte(mrand.Intn(255) + 1)
|
||||
}
|
||||
|
||||
func corruptBytes(b []byte) {
|
||||
corruptByteAt(b, mrand.Intn(len(b)))
|
||||
}
|
||||
|
||||
func expectVerifySCTToFail(t *testing.T, sv SignatureVerifier, sct SignedCertificateTimestamp, msg string) {
|
||||
t.Helper()
|
||||
if err := sv.VerifySCTSignature(sct, sigTestCertLogEntry(t)); err == nil {
|
||||
t.Fatal(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifySCTSignatureEC(t *testing.T) {
|
||||
v := mustCreateSignatureVerifier(t, sigTestECPublicKey(t))
|
||||
if err := v.VerifySCTSignature(sigTestSCTEC(t), sigTestCertLogEntry(t)); err != nil {
|
||||
t.Fatalf("Failed to verify signature on SCT: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestVerifySCTSignatureRSA(t *testing.T) {
|
||||
v := mustCreateSignatureVerifier(t, sigTestRSAPublicKey(t))
|
||||
if err := v.VerifySCTSignature(sigTestSCTRSA(t), sigTestCertLogEntry(t)); err != nil {
|
||||
t.Fatalf("Failed to verify signature on SCT: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestVerifySCTSignatureFailsForMismatchedSignatureAlgorithm(t *testing.T) {
|
||||
v := mustCreateSignatureVerifier(t, sigTestECPublicKey(t))
|
||||
expectVerifySCTToFail(t, v, sigTestSCTRSA(t), "Successfully verified with mismatched signature algorithm")
|
||||
}
|
||||
|
||||
func TestVerifySCTSignatureFailsForUnknownSignatureAlgorithm(t *testing.T) {
|
||||
v := mustCreateSignatureVerifier(t, sigTestECPublicKey(t))
|
||||
expectVerifySCTToFail(t, v, sigTestSCTWithSignature(t, sigTestCertSCTSignatureUnsupportedSignatureAlgorithm, sigTestKeyIDEC),
|
||||
"Successfully verified signature with unsupported signature algorithm")
|
||||
}
|
||||
|
||||
func TestVerifySCTSignatureFailsForUnknownHashAlgorithm(t *testing.T) {
|
||||
v := mustCreateSignatureVerifier(t, sigTestECPublicKey(t))
|
||||
expectVerifySCTToFail(t, v, sigTestSCTWithSignature(t, sigTestCertSCTSignatureUnsupportedHashAlgorithm, sigTestKeyIDEC),
|
||||
"Successfully verified signature with unsupported hash algorithm")
|
||||
}
|
||||
|
||||
func testVerifySCTSignatureFailsForIncorrectLeafBytes(t *testing.T, sct SignedCertificateTimestamp, sv SignatureVerifier) {
|
||||
t.Helper()
|
||||
entry := sigTestCertLogEntry(t)
|
||||
for i := range entry.Leaf.TimestampedEntry.X509Entry.Data {
|
||||
old := entry.Leaf.TimestampedEntry.X509Entry.Data[i]
|
||||
corruptByteAt(entry.Leaf.TimestampedEntry.X509Entry.Data, i)
|
||||
if err := sv.VerifySCTSignature(sct, entry); err == nil {
|
||||
t.Fatalf("Incorrectly verfied signature over corrupted leaf data, uncovered byte at %d?", i)
|
||||
}
|
||||
entry.Leaf.TimestampedEntry.X509Entry.Data[i] = old
|
||||
}
|
||||
// Ensure we were only corrupting one byte at a time, should be correct again now.
|
||||
if err := sv.VerifySCTSignature(sct, entry); err != nil {
|
||||
t.Fatalf("Input data appears to still be corrupt, bug? %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func testVerifySCTSignatureFailsForIncorrectSignature(t *testing.T, sct SignedCertificateTimestamp, sv SignatureVerifier) {
|
||||
t.Helper()
|
||||
corruptBytes(sct.Signature.Signature)
|
||||
expectVerifySCTToFail(t, sv, sct, "Incorrectly verified corrupt signature")
|
||||
}
|
||||
|
||||
func TestVerifySCTSignatureECFailsForIncorrectLeafBytes(t *testing.T) {
|
||||
v := mustCreateSignatureVerifier(t, sigTestECPublicKey(t))
|
||||
testVerifySCTSignatureFailsForIncorrectLeafBytes(t, sigTestSCTEC(t), v)
|
||||
}
|
||||
|
||||
func TestVerifySCTSignatureECFailsForIncorrectTimestamp(t *testing.T) {
|
||||
v := mustCreateSignatureVerifier(t, sigTestECPublicKey(t))
|
||||
sct := sigTestSCTEC(t)
|
||||
sct.Timestamp++
|
||||
expectVerifySCTToFail(t, v, sct, "Incorrectly verified signature with incorrect SCT timestamp.")
|
||||
}
|
||||
|
||||
func TestVerifySCTSignatureECFailsForIncorrectVersion(t *testing.T) {
|
||||
v := mustCreateSignatureVerifier(t, sigTestECPublicKey(t))
|
||||
sct := sigTestSCTEC(t)
|
||||
sct.SCTVersion++
|
||||
expectVerifySCTToFail(t, v, sct, "Incorrectly verified signature with incorrect SCT Version.")
|
||||
}
|
||||
|
||||
func TestVerifySCTSignatureECFailsForIncorrectSignature(t *testing.T) {
|
||||
v := mustCreateSignatureVerifier(t, sigTestECPublicKey(t))
|
||||
testVerifySCTSignatureFailsForIncorrectSignature(t, sigTestSCTEC(t), v)
|
||||
}
|
||||
|
||||
func TestVerifySCTSignatureRSAFailsForIncorrectLeafBytes(t *testing.T) {
|
||||
v := mustCreateSignatureVerifier(t, sigTestRSAPublicKey(t))
|
||||
testVerifySCTSignatureFailsForIncorrectLeafBytes(t, sigTestSCTRSA(t), v)
|
||||
}
|
||||
|
||||
func TestVerifySCTSignatureRSAFailsForIncorrectSignature(t *testing.T) {
|
||||
v := mustCreateSignatureVerifier(t, sigTestRSAPublicKey(t))
|
||||
testVerifySCTSignatureFailsForIncorrectSignature(t, sigTestSCTRSA(t), v)
|
||||
}
|
||||
|
||||
func TestVerifySCTSignatureFailsForSignatureCreatedWithDifferentAlgorithm(t *testing.T) {
|
||||
v := mustCreateSignatureVerifier(t, sigTestRSAPublicKey(t))
|
||||
testVerifySCTSignatureFailsForIncorrectSignature(t, sigTestSCTEC(t), v)
|
||||
}
|
||||
|
||||
func TestVerifySCTSignatureFailsForSignatureCreatedWithDifferentKey(t *testing.T) {
|
||||
v := mustCreateSignatureVerifier(t, sigTestECPublicKey2(t))
|
||||
testVerifySCTSignatureFailsForIncorrectSignature(t, sigTestSCTEC(t), v)
|
||||
}
|
||||
|
||||
func expectVerifySTHToPass(t *testing.T, v SignatureVerifier, sth SignedTreeHead) {
|
||||
t.Helper()
|
||||
if err := v.VerifySTHSignature(sth); err != nil {
|
||||
t.Fatalf("Incorrectly failed to verify STH signature: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func expectVerifySTHToFail(t *testing.T, v SignatureVerifier, sth SignedTreeHead) {
|
||||
t.Helper()
|
||||
if err := v.VerifySTHSignature(sth); err == nil {
|
||||
t.Fatal("Incorrectly verified STH signature")
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyValidSTH(t *testing.T) {
|
||||
v := mustCreateSignatureVerifier(t, sigTestECPublicKey(t))
|
||||
sth := sigTestDefaultSTH(t)
|
||||
expectVerifySTHToPass(t, v, sth)
|
||||
}
|
||||
|
||||
func TestVerifySTHCatchesCorruptSignature(t *testing.T) {
|
||||
v := mustCreateSignatureVerifier(t, sigTestECPublicKey(t))
|
||||
sth := sigTestDefaultSTH(t)
|
||||
corruptBytes(sth.TreeHeadSignature.Signature)
|
||||
expectVerifySTHToFail(t, v, sth)
|
||||
}
|
||||
|
||||
func TestVerifySTHCatchesCorruptRootHash(t *testing.T) {
|
||||
v := mustCreateSignatureVerifier(t, sigTestECPublicKey(t))
|
||||
sth := sigTestDefaultSTH(t)
|
||||
for i := range sth.SHA256RootHash {
|
||||
old := sth.SHA256RootHash[i]
|
||||
corruptByteAt(sth.SHA256RootHash[:], i)
|
||||
expectVerifySTHToFail(t, v, sth)
|
||||
sth.SHA256RootHash[i] = old
|
||||
}
|
||||
// ensure we were only testing one corrupt byte at a time - should be correct again now.
|
||||
expectVerifySTHToPass(t, v, sth)
|
||||
}
|
||||
|
||||
func TestVerifySTHCatchesCorruptTimestamp(t *testing.T) {
|
||||
v := mustCreateSignatureVerifier(t, sigTestECPublicKey(t))
|
||||
sth := sigTestDefaultSTH(t)
|
||||
sth.Timestamp++
|
||||
expectVerifySTHToFail(t, v, sth)
|
||||
}
|
||||
|
||||
func TestVerifySTHCatchesCorruptVersion(t *testing.T) {
|
||||
v := mustCreateSignatureVerifier(t, sigTestECPublicKey(t))
|
||||
sth := sigTestDefaultSTH(t)
|
||||
sth.Version++
|
||||
expectVerifySTHToFail(t, v, sth)
|
||||
}
|
||||
|
||||
func TestVerifySTHCatchesCorruptTreeSize(t *testing.T) {
|
||||
v := mustCreateSignatureVerifier(t, sigTestECPublicKey(t))
|
||||
sth := sigTestDefaultSTH(t)
|
||||
sth.TreeSize++
|
||||
expectVerifySTHToFail(t, v, sth)
|
||||
}
|
||||
|
||||
func TestVerifySTHFailsToVerifyForKeyWithDifferentAlgorithm(t *testing.T) {
|
||||
v := mustCreateSignatureVerifier(t, sigTestRSAPublicKey(t))
|
||||
sth := sigTestDefaultSTH(t)
|
||||
expectVerifySTHToFail(t, v, sth)
|
||||
}
|
||||
|
||||
func TestVerifySTHFailsToVerifyForDifferentKey(t *testing.T) {
|
||||
v := mustCreateSignatureVerifier(t, sigTestECPublicKey2(t))
|
||||
sth := sigTestDefaultSTH(t)
|
||||
expectVerifySTHToFail(t, v, sth)
|
||||
}
|
||||
|
||||
func TestNewSignatureVerifierFailsWithUnsupportedKeyType(t *testing.T) {
|
||||
var k dsa.PrivateKey
|
||||
if err := dsa.GenerateParameters(&k.Parameters, rand.Reader, dsa.L1024N160); err != nil {
|
||||
t.Fatalf("Failed to generate DSA key parameters: %v", err)
|
||||
}
|
||||
if err := dsa.GenerateKey(&k, rand.Reader); err != nil {
|
||||
t.Fatalf("Failed to generate DSA key: %v", err)
|
||||
}
|
||||
if _, err := NewSignatureVerifier(k); err == nil {
|
||||
t.Fatal("Creating a SignatureVerifier with a DSA key unexpectedly succeeded")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewSignatureVerifierFailsWithBadKeyParametersForEC(t *testing.T) {
|
||||
k, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to generate ECDSA key on P224: %v", err)
|
||||
}
|
||||
if _, err := NewSignatureVerifier(k); err == nil {
|
||||
t.Fatal("Incorrectly created new SignatureVerifier with EC P224 key.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewSignatureVerifierFailsWithBadKeyParametersForRSA(t *testing.T) {
|
||||
k, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to generate 1024 bit RSA key: %v", err)
|
||||
}
|
||||
if _, err := NewSignatureVerifier(k); err == nil {
|
||||
t.Fatal("Incorrectly created new SignatureVerifier with 1024 bit RSA key.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWillAllowNonCompliantECKeyWithOverride(t *testing.T) {
|
||||
*allowVerificationWithNonCompliantKeys = true
|
||||
k, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to generate EC key on P224: %v", err)
|
||||
}
|
||||
if _, err := NewSignatureVerifier(k.Public()); err != nil {
|
||||
t.Fatalf("Incorrectly disallowed P224 EC key with override set: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWillAllowNonCompliantRSAKeyWithOverride(t *testing.T) {
|
||||
*allowVerificationWithNonCompliantKeys = true
|
||||
k, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to generate 1024 bit RSA key: %v", err)
|
||||
}
|
||||
if _, err := NewSignatureVerifier(k.Public()); err != nil {
|
||||
t.Fatalf("Incorrectly disallowed 1024 bit RSA key with override set: %v", err)
|
||||
}
|
||||
}
|
30
vendor/github.com/google/certificate-transparency-go/testdata/argon.cfg
generated
vendored
Normal file
30
vendor/github.com/google/certificate-transparency-go/testdata/argon.cfg
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
shard {
|
||||
uri: "https://ct.googleapis.com/logs/argon2017"
|
||||
public_key_der:"0Y0\023\006\007*\206H\316=\002\001\006\010*\206H\316=\003\001\007\003B\000\004Tm|\211\335\352\235\360\272_\364m`z7O\002%\277\034\366o\205\256\257\025\337in\355\333\251\232)\227\362\231v\036\3463F\036'\364\276p\335Y\327\272\317\376\320r\216\260W\017\2357\211b\243"
|
||||
not_after_start:<seconds:1483228800>
|
||||
not_after_limit:<seconds:1514764800>
|
||||
}
|
||||
shard {
|
||||
uri: "https://ct.googleapis.com/logs/argon2018"
|
||||
public_key_der:"0Y0\023\006\007*\206H\316=\002\001\006\010*\206H\316=\003\001\007\003B\000\004\322\000U\005\255\325G\264\031\273\315\225\373)\327X=x$\315\316F\235\3732\324qN`\002%^Y>\327\324\003\270mChh~\350\240e\013>nqY\2227\276\251\350\361\243+\344\331\rUh"
|
||||
not_after_start:<seconds:1514764800>
|
||||
not_after_limit:<seconds:1546300800>
|
||||
}
|
||||
shard {
|
||||
uri: "https://ct.googleapis.com/logs/argon2019"
|
||||
public_key_der:"0Y0\023\006\007*\206H\316=\002\001\006\010*\206H\316=\003\001\007\003B\000\004#s\020\233\341\363^\366\230ki\225\226\020x\316I\333\264\004\374q,Z\222`h%\300J\032\241\260a-\033\207\024\251\272\360\0013Y\035\0050\351B\025\347U\327*\370\264\242\272E\311F\221\207V"
|
||||
not_after_start:<seconds:1546300800>
|
||||
not_after_limit:<seconds:1577836800>
|
||||
}
|
||||
shard {
|
||||
uri: "https://ct.googleapis.com/logs/argon2020"
|
||||
public_key_der:"0Y0\023\006\007*\206H\316=\002\001\006\010*\206H\316=\003\001\007\003B\000\004\351<v\247\\\212c\2155\344\334\210b\367k\223~\236\263K\200s\\\300\340\364>LdX\373vcQ2\030c\325\262\273\355\352\377^;$n/5R\213\2645\232\255\234\025\250i \352P\030\314"
|
||||
not_after_start:<seconds:1577836800>
|
||||
not_after_limit:<seconds:1609459200>
|
||||
}
|
||||
shard {
|
||||
uri: "https://ct.googleapis.com/logs/argon2021"
|
||||
public_key_der:"0Y0\023\006\007*\206H\316=\002\001\006\010*\206H\316=\003\001\007\003B\000\004M\340fd\352\363d\2528\305\211-\307\330\010\331\310Dq\355\334\303\373[\257\234d\241\tf\204\035|h\247\354\304?\214\234\202\340\030\331t\024\351\264y\201\242\224Ub\363\234\013D\203\241+\311q+"
|
||||
not_after_start:<seconds:1609459200>
|
||||
not_after_limit:<seconds:1640995200>
|
||||
}
|
235
vendor/github.com/google/certificate-transparency-go/testdata/certs.go
generated
vendored
Normal file
235
vendor/github.com/google/certificate-transparency-go/testdata/certs.go
generated
vendored
Normal file
@ -0,0 +1,235 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package testdata
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
const (
|
||||
// CACertPEM is a CA cert:
|
||||
// Certificate:
|
||||
// Data:
|
||||
// Version: 3 (0x2)
|
||||
// Serial Number: 0 (0x0)
|
||||
// Signature Algorithm: sha1WithRSAEncryption
|
||||
// Issuer: C=GB, O=Certificate Transparency CA, ST=Wales, L=Erw Wen
|
||||
// Validity
|
||||
// Not Before: Jun 1 00:00:00 2012 GMT
|
||||
// Not After : Jun 1 00:00:00 2022 GMT
|
||||
// Subject: C=GB, O=Certificate Transparency CA, ST=Wales, L=Erw Wen
|
||||
// Subject Public Key Info:
|
||||
// Public Key Algorithm: rsaEncryption
|
||||
// Public-Key: (1024 bit)
|
||||
// Modulus:
|
||||
// 00:d5:8a:68:53:62:10:a2:71:19:93:6e:77:83:21:
|
||||
// 18:1c:2a:40:13:c6:d0:7b:8c:76:eb:91:57:d3:d0:
|
||||
// fb:4b:3b:51:6e:ce:cb:d1:c9:8d:91:c5:2f:74:3f:
|
||||
// ab:63:5d:55:09:9c:d1:3a:ba:f3:1a:e5:41:44:24:
|
||||
// 51:a7:4c:78:16:f2:24:3c:f8:48:cf:28:31:cc:e6:
|
||||
// 7b:a0:4a:5a:23:81:9f:3c:ba:37:e6:24:d9:c3:bd:
|
||||
// b2:99:b8:39:dd:fe:26:31:d2:cb:3a:84:fc:7b:b2:
|
||||
// b5:c5:2f:cf:c1:4f:ff:40:6f:5c:d4:46:69:cb:b2:
|
||||
// f7:cf:df:86:fb:6a:b9:d1:b1
|
||||
// Exponent: 65537 (0x10001)
|
||||
// X509v3 extensions:
|
||||
// X509v3 Subject Key Identifier:
|
||||
// 5F:9D:88:0D:C8:73:E6:54:D4:F8:0D:D8:E6:B0:C1:24:B4:47:C3:55
|
||||
// X509v3 Authority Key Identifier:
|
||||
// keyid:5F:9D:88:0D:C8:73:E6:54:D4:F8:0D:D8:E6:B0:C1:24:B4:47:C3:55
|
||||
// DirName:/C=GB/O=Certificate Transparency CA/ST=Wales/L=Erw Wen
|
||||
// serial:00
|
||||
//
|
||||
// X509v3 Basic Constraints:
|
||||
// CA:TRUE
|
||||
// Signature Algorithm: sha1WithRSAEncryption
|
||||
// 06:08:cc:4a:6d:64:f2:20:5e:14:6c:04:b2:76:f9:2b:0e:fa:
|
||||
// 94:a5:da:f2:3a:fc:38:06:60:6d:39:90:d0:a1:ea:23:3d:40:
|
||||
// 29:57:69:46:3b:04:66:61:e7:fa:1d:17:99:15:20:9a:ea:2e:
|
||||
// 0a:77:51:76:41:12:27:d7:c0:03:07:c7:47:0e:61:58:4f:d7:
|
||||
// 33:42:24:72:7f:51:d6:90:bc:47:a9:df:35:4d:b0:f6:eb:25:
|
||||
// 95:5d:e1:89:3c:4d:d5:20:2b:24:a2:f3:e4:40:d2:74:b5:4e:
|
||||
// 1b:d3:76:26:9c:a9:62:89:b7:6e:ca:a4:10:90:e1:4f:3b:0a:
|
||||
// 94:2e
|
||||
CACertPEM = "-----BEGIN CERTIFICATE-----\n" +
|
||||
"MIIC0DCCAjmgAwIBAgIBADANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJHQjEk\n" +
|
||||
"MCIGA1UEChMbQ2VydGlmaWNhdGUgVHJhbnNwYXJlbmN5IENBMQ4wDAYDVQQIEwVX\n" +
|
||||
"YWxlczEQMA4GA1UEBxMHRXJ3IFdlbjAeFw0xMjA2MDEwMDAwMDBaFw0yMjA2MDEw\n" +
|
||||
"MDAwMDBaMFUxCzAJBgNVBAYTAkdCMSQwIgYDVQQKExtDZXJ0aWZpY2F0ZSBUcmFu\n" +
|
||||
"c3BhcmVuY3kgQ0ExDjAMBgNVBAgTBVdhbGVzMRAwDgYDVQQHEwdFcncgV2VuMIGf\n" +
|
||||
"MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVimhTYhCicRmTbneDIRgcKkATxtB7\n" +
|
||||
"jHbrkVfT0PtLO1FuzsvRyY2RxS90P6tjXVUJnNE6uvMa5UFEJFGnTHgW8iQ8+EjP\n" +
|
||||
"KDHM5nugSlojgZ88ujfmJNnDvbKZuDnd/iYx0ss6hPx7srXFL8/BT/9Ab1zURmnL\n" +
|
||||
"svfP34b7arnRsQIDAQABo4GvMIGsMB0GA1UdDgQWBBRfnYgNyHPmVNT4DdjmsMEk\n" +
|
||||
"tEfDVTB9BgNVHSMEdjB0gBRfnYgNyHPmVNT4DdjmsMEktEfDVaFZpFcwVTELMAkG\n" +
|
||||
"A1UEBhMCR0IxJDAiBgNVBAoTG0NlcnRpZmljYXRlIFRyYW5zcGFyZW5jeSBDQTEO\n" +
|
||||
"MAwGA1UECBMFV2FsZXMxEDAOBgNVBAcTB0VydyBXZW6CAQAwDAYDVR0TBAUwAwEB\n" +
|
||||
"/zANBgkqhkiG9w0BAQUFAAOBgQAGCMxKbWTyIF4UbASydvkrDvqUpdryOvw4BmBt\n" +
|
||||
"OZDQoeojPUApV2lGOwRmYef6HReZFSCa6i4Kd1F2QRIn18ADB8dHDmFYT9czQiRy\n" +
|
||||
"f1HWkLxHqd81TbD26yWVXeGJPE3VICskovPkQNJ0tU4b03YmnKliibduyqQQkOFP\n" +
|
||||
"OwqULg==\n" +
|
||||
"-----END CERTIFICATE-----"
|
||||
|
||||
// TestCertPEM is a leaf certificate signed by CACertPEM.
|
||||
// Certificate:
|
||||
// Data:
|
||||
// Version: 3 (0x2)
|
||||
// Serial Number: 6 (0x6)
|
||||
// Signature Algorithm: sha1WithRSAEncryption
|
||||
// Issuer: C=GB, O=Certificate Transparency CA, ST=Wales, L=Erw Wen
|
||||
// Validity
|
||||
// Not Before: Jun 1 00:00:00 2012 GMT
|
||||
// Not After : Jun 1 00:00:00 2022 GMT
|
||||
// Subject: C=GB, O=Certificate Transparency, ST=Wales, L=Erw Wen
|
||||
// Subject Public Key Info:
|
||||
// Public Key Algorithm: rsaEncryption
|
||||
// Public-Key: (1024 bit)
|
||||
// Modulus:
|
||||
// 00:b1:fa:37:93:61:11:f8:79:2d:a2:08:1c:3f:e4:
|
||||
// 19:25:00:85:31:dc:7f:2c:65:7b:d9:e1:de:47:04:
|
||||
// 16:0b:4c:9f:19:d5:4a:da:44:70:40:4c:1c:51:34:
|
||||
// 1b:8f:1f:75:38:dd:dd:28:d9:ac:a4:83:69:fc:56:
|
||||
// 46:dd:cc:76:17:f8:16:8a:ae:5b:41:d4:33:31:fc:
|
||||
// a2:da:df:c8:04:d5:72:08:94:90:61:f9:ee:f9:02:
|
||||
// ca:47:ce:88:c6:44:e0:00:f0:6e:ee:cc:ab:dc:9d:
|
||||
// d2:f6:8a:22:cc:b0:9d:c7:6e:0d:bc:73:52:77:65:
|
||||
// b1:a3:7a:8c:67:62:53:dc:c1
|
||||
// Exponent: 65537 (0x10001)
|
||||
// X509v3 extensions:
|
||||
// X509v3 Subject Key Identifier:
|
||||
// 6A:0D:98:2A:3B:62:C4:4B:6D:2E:F4:E9:BB:7A:01:AA:9C:B7:98:E2
|
||||
// X509v3 Authority Key Identifier:
|
||||
// keyid:5F:9D:88:0D:C8:73:E6:54:D4:F8:0D:D8:E6:B0:C1:24:B4:47:C3:55
|
||||
// DirName:/C=GB/O=Certificate Transparency CA/ST=Wales/L=Erw Wen
|
||||
// serial:00
|
||||
//
|
||||
// X509v3 Basic Constraints:
|
||||
// CA:FALSE
|
||||
// Signature Algorithm: sha1WithRSAEncryption
|
||||
// 17:1c:d8:4a:ac:41:4a:9a:03:0f:22:aa:c8:f6:88:b0:81:b2:
|
||||
// 70:9b:84:8b:4e:55:11:40:6c:d7:07:fe:d0:28:59:7a:9f:ae:
|
||||
// fc:2e:ee:29:78:d6:33:aa:ac:14:ed:32:35:19:7d:a8:7e:0f:
|
||||
// 71:b8:87:5f:1a:c9:e7:8b:28:17:49:dd:ed:d0:07:e3:ec:f5:
|
||||
// 06:45:f8:cb:f6:67:25:6c:d6:a1:64:7b:5e:13:20:3b:b8:58:
|
||||
// 2d:e7:d6:69:6f:65:6d:1c:60:b9:5f:45:6b:7f:cf:33:85:71:
|
||||
// 90:8f:1c:69:72:7d:24:c4:fc:cd:24:92:95:79:58:14:d1:da:
|
||||
// c0:e6
|
||||
TestCertPEM = "-----BEGIN CERTIFICATE-----\n" +
|
||||
"MIICyjCCAjOgAwIBAgIBBjANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJHQjEk\n" +
|
||||
"MCIGA1UEChMbQ2VydGlmaWNhdGUgVHJhbnNwYXJlbmN5IENBMQ4wDAYDVQQIEwVX\n" +
|
||||
"YWxlczEQMA4GA1UEBxMHRXJ3IFdlbjAeFw0xMjA2MDEwMDAwMDBaFw0yMjA2MDEw\n" +
|
||||
"MDAwMDBaMFIxCzAJBgNVBAYTAkdCMSEwHwYDVQQKExhDZXJ0aWZpY2F0ZSBUcmFu\n" +
|
||||
"c3BhcmVuY3kxDjAMBgNVBAgTBVdhbGVzMRAwDgYDVQQHEwdFcncgV2VuMIGfMA0G\n" +
|
||||
"CSqGSIb3DQEBAQUAA4GNADCBiQKBgQCx+jeTYRH4eS2iCBw/5BklAIUx3H8sZXvZ\n" +
|
||||
"4d5HBBYLTJ8Z1UraRHBATBxRNBuPH3U43d0o2aykg2n8VkbdzHYX+BaKrltB1DMx\n" +
|
||||
"/KLa38gE1XIIlJBh+e75AspHzojGROAA8G7uzKvcndL2iiLMsJ3Hbg28c1J3ZbGj\n" +
|
||||
"eoxnYlPcwQIDAQABo4GsMIGpMB0GA1UdDgQWBBRqDZgqO2LES20u9Om7egGqnLeY\n" +
|
||||
"4jB9BgNVHSMEdjB0gBRfnYgNyHPmVNT4DdjmsMEktEfDVaFZpFcwVTELMAkGA1UE\n" +
|
||||
"BhMCR0IxJDAiBgNVBAoTG0NlcnRpZmljYXRlIFRyYW5zcGFyZW5jeSBDQTEOMAwG\n" +
|
||||
"A1UECBMFV2FsZXMxEDAOBgNVBAcTB0VydyBXZW6CAQAwCQYDVR0TBAIwADANBgkq\n" +
|
||||
"hkiG9w0BAQUFAAOBgQAXHNhKrEFKmgMPIqrI9oiwgbJwm4SLTlURQGzXB/7QKFl6\n" +
|
||||
"n678Lu4peNYzqqwU7TI1GX2ofg9xuIdfGsnniygXSd3t0Afj7PUGRfjL9mclbNah\n" +
|
||||
"ZHteEyA7uFgt59Zpb2VtHGC5X0Vrf88zhXGQjxxpcn0kxPzNJJKVeVgU0drA5g==\n" +
|
||||
"-----END CERTIFICATE-----\n"
|
||||
|
||||
// TestPreCertPEM is a pre-certificate signed by CACertPEM.
|
||||
// Certificate:
|
||||
// Data:
|
||||
// Version: 3 (0x2)
|
||||
// Serial Number: 7 (0x7)
|
||||
// Signature Algorithm: sha1WithRSAEncryption
|
||||
// Issuer: C=GB, O=Certificate Transparency CA, ST=Wales, L=Erw Wen
|
||||
// Validity
|
||||
// Not Before: Jun 1 00:00:00 2012 GMT
|
||||
// Not After : Jun 1 00:00:00 2022 GMT
|
||||
// Subject: C=GB, O=Certificate Transparency, ST=Wales, L=Erw Wen
|
||||
// Subject Public Key Info:
|
||||
// Public Key Algorithm: rsaEncryption
|
||||
// Public-Key: (1024 bit)
|
||||
// Modulus:
|
||||
// 00:be:ef:98:e7:c2:68:77:ae:38:5f:75:32:5a:0c:
|
||||
// 1d:32:9b:ed:f1:8f:aa:f4:d7:96:bf:04:7e:b7:e1:
|
||||
// ce:15:c9:5b:a2:f8:0e:e4:58:bd:7d:b8:6f:8a:4b:
|
||||
// 25:21:91:a7:9b:d7:00:c3:8e:9c:03:89:b4:5c:d4:
|
||||
// dc:9a:12:0a:b2:1e:0c:b4:1c:d0:e7:28:05:a4:10:
|
||||
// cd:9c:5b:db:5d:49:27:72:6d:af:17:10:f6:01:87:
|
||||
// 37:7e:a2:5b:1a:1e:39:ee:d0:b8:81:19:dc:15:4d:
|
||||
// c6:8f:7d:a8:e3:0c:af:15:8a:33:e6:c9:50:9f:4a:
|
||||
// 05:b0:14:09:ff:5d:d8:7e:b5
|
||||
// Exponent: 65537 (0x10001)
|
||||
// X509v3 extensions:
|
||||
// X509v3 Subject Key Identifier:
|
||||
// 20:31:54:1A:F2:5C:05:FF:D8:65:8B:68:43:79:4F:5E:90:36:F7:B4
|
||||
// X509v3 Authority Key Identifier:
|
||||
// keyid:5F:9D:88:0D:C8:73:E6:54:D4:F8:0D:D8:E6:B0:C1:24:B4:47:C3:55
|
||||
// DirName:/C=GB/O=Certificate Transparency CA/ST=Wales/L=Erw Wen
|
||||
// serial:00
|
||||
//
|
||||
// X509v3 Basic Constraints:
|
||||
// CA:FALSE
|
||||
// 1.3.6.1.4.1.11129.2.4.3: critical
|
||||
// ..
|
||||
// Signature Algorithm: sha1WithRSAEncryption
|
||||
// 02:a1:c3:9e:01:5a:f5:4d:ff:02:3c:33:60:87:5f:ff:34:37:
|
||||
// 55:2f:1f:09:01:bd:c2:54:31:5f:33:72:b7:23:fb:15:fb:ce:
|
||||
// cc:4d:f4:71:a0:ce:4d:8c:54:65:5d:84:87:97:fb:28:1e:3d:
|
||||
// fa:bb:46:2d:2c:68:4b:05:6f:ea:7b:63:b4:70:ff:16:6e:32:
|
||||
// d4:46:06:35:b3:d2:bc:6d:a8:24:9b:26:30:e7:1f:c3:4f:08:
|
||||
// f2:3d:d4:ee:22:8f:8f:74:f6:3d:78:63:11:dd:0a:58:11:40:
|
||||
// 5f:90:6c:ca:2c:2d:3e:eb:fc:81:99:64:eb:d8:cf:7c:08:86:
|
||||
// 3f:be
|
||||
TestPreCertPEM = "-----BEGIN CERTIFICATE-----\n" +
|
||||
"MIIC3zCCAkigAwIBAgIBBzANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJHQjEk\n" +
|
||||
"MCIGA1UEChMbQ2VydGlmaWNhdGUgVHJhbnNwYXJlbmN5IENBMQ4wDAYDVQQIEwVX\n" +
|
||||
"YWxlczEQMA4GA1UEBxMHRXJ3IFdlbjAeFw0xMjA2MDEwMDAwMDBaFw0yMjA2MDEw\n" +
|
||||
"MDAwMDBaMFIxCzAJBgNVBAYTAkdCMSEwHwYDVQQKExhDZXJ0aWZpY2F0ZSBUcmFu\n" +
|
||||
"c3BhcmVuY3kxDjAMBgNVBAgTBVdhbGVzMRAwDgYDVQQHEwdFcncgV2VuMIGfMA0G\n" +
|
||||
"CSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+75jnwmh3rjhfdTJaDB0ym+3xj6r015a/\n" +
|
||||
"BH634c4VyVui+A7kWL19uG+KSyUhkaeb1wDDjpwDibRc1NyaEgqyHgy0HNDnKAWk\n" +
|
||||
"EM2cW9tdSSdyba8XEPYBhzd+olsaHjnu0LiBGdwVTcaPfajjDK8VijPmyVCfSgWw\n" +
|
||||
"FAn/Xdh+tQIDAQABo4HBMIG+MB0GA1UdDgQWBBQgMVQa8lwF/9hli2hDeU9ekDb3\n" +
|
||||
"tDB9BgNVHSMEdjB0gBRfnYgNyHPmVNT4DdjmsMEktEfDVaFZpFcwVTELMAkGA1UE\n" +
|
||||
"BhMCR0IxJDAiBgNVBAoTG0NlcnRpZmljYXRlIFRyYW5zcGFyZW5jeSBDQTEOMAwG\n" +
|
||||
"A1UECBMFV2FsZXMxEDAOBgNVBAcTB0VydyBXZW6CAQAwCQYDVR0TBAIwADATBgor\n" +
|
||||
"BgEEAdZ5AgQDAQH/BAIFADANBgkqhkiG9w0BAQUFAAOBgQACocOeAVr1Tf8CPDNg\n" +
|
||||
"h1//NDdVLx8JAb3CVDFfM3K3I/sV+87MTfRxoM5NjFRlXYSHl/soHj36u0YtLGhL\n" +
|
||||
"BW/qe2O0cP8WbjLURgY1s9K8bagkmyYw5x/DTwjyPdTuIo+PdPY9eGMR3QpYEUBf\n" +
|
||||
"kGzKLC0+6/yBmWTr2M98CIY/vg==\n" +
|
||||
"-----END CERTIFICATE-----\n"
|
||||
)
|
||||
|
||||
var (
|
||||
// TestCertProof is a TLS-encoded ct.SignedCertificateTimestamp corresponding
|
||||
// to TestCertPEM.
|
||||
TestCertProof = dh("00df1c2ec11500945247a96168325ddc5c7959e8f7c6d388fc002e0bbd3f74d7" +
|
||||
"640000013ddb27ded900000403004730450220606e10ae5c2d5a1b0aed49dc49" +
|
||||
"37f48de71a4e9784e9c208dfbfe9ef536cf7f2022100beb29c72d7d06d61d06b" +
|
||||
"db38a069469aa86fe12e18bb7cc45689a2c0187ef5a5")
|
||||
|
||||
// TestPreCertProof is a TLS-encoded ct.SignedCertificateTimestamp
|
||||
// corresponding to TestPreCertPEM
|
||||
TestPreCertProof = dh("00df1c2ec11500945247a96168325ddc5c7959e8f7c6d388fc002e0bbd3f74d7" +
|
||||
"640000013ddb27df9300000403004730450220482f6751af35dba65436be1fd6" +
|
||||
"640f3dbf9a41429495924530288fa3e5e23e06022100e4edc0db3ac572b1e2f5" +
|
||||
"e8ab6a680653987dcf41027dfeffa105519d89edbf08")
|
||||
)
|
||||
|
||||
func dh(h string) []byte {
|
||||
r, err := hex.DecodeString(h)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return r
|
||||
}
|
218
vendor/github.com/google/certificate-transparency-go/testdata/keys.go
generated
vendored
Normal file
218
vendor/github.com/google/certificate-transparency-go/testdata/keys.go
generated
vendored
Normal file
@ -0,0 +1,218 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package testdata holds data and utility functions needed for various Go tests.
|
||||
package testdata
|
||||
|
||||
import "encoding/hex"
|
||||
|
||||
// Hashes of text string 'abcd' with various algorithms; check with either "<alg>sum input" or "openssl dgst -<alg> input"
|
||||
const (
|
||||
// AbcdMD5 is 'abcd' hashed with MD5
|
||||
AbcdMD5 = "e2fc714c4727ee9395f324cd2e7f331f"
|
||||
// AbcdSHA1 is 'abcd' hashed with SHA1
|
||||
AbcdSHA1 = "81fe8bfe87576c3ecb22426f8e57847382917acf"
|
||||
// AbcdSHA224 is 'abcd' hashed with SHA224
|
||||
AbcdSHA224 = "a76654d8e3550e9a2d67a0eeb6c67b220e5885eddd3fde135806e601"
|
||||
// AbcdSHA256 is 'abcd' hashed with SHA256
|
||||
AbcdSHA256 = "88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589"
|
||||
// AbcdSHA384 is 'abcd' hashed with SHA384
|
||||
AbcdSHA384 = "1165b3406ff0b52a3d24721f785462ca2276c9f454a116c2b2ba20171a7905ea5a026682eb659c4d5f115c363aa3c79b"
|
||||
// AbcdSHA512 is 'abcd' hashed with SHA512
|
||||
AbcdSHA512 = "d8022f2060ad6efd297ab73dcc5355c9b214054b0d1776a136a669d26a7d3b14f73aa0d0ebff19ee333368f0164b6419a96da49e3e481753e7e96b716bdccb6f"
|
||||
)
|
||||
|
||||
const (
|
||||
// LogPublicKeyPEM is an ECDSA key copied from test/testdata/ct-server-key-public.pem
|
||||
LogPublicKeyPEM = "-----BEGIN PUBLIC KEY-----\n" +
|
||||
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmXg8sUUzwBYaWrRb+V0IopzQ6o3U\n" +
|
||||
"yEJ04r5ZrRXGdpYM8K+hB0pXrGRLI0eeWz+3skXrS0IO83AhA3GpRL6s6w==\n" +
|
||||
"-----END PUBLIC KEY-----\n"
|
||||
|
||||
// RsaPrivateKeyPEM was generated with:
|
||||
// openssl genpkey -algorithm RSA -out rsa.privkey.pem -pkeyopt rsa_keygen_bits:2048
|
||||
RsaPrivateKeyPEM = "-----BEGIN PRIVATE KEY-----\n" +
|
||||
"MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDP8SJM5SBIbFpF\n" +
|
||||
"dRAUEERo35E4iFXfT5SVAzBIqATkCpf3LfdFZgssPeliBdTtgdvT/CWC2m6aVXYZ\n" +
|
||||
"eScAJOauTpGDyJ7OXj7Yhx1q6u28NuQleU3UQK5QYb7VOjeIse7oTh+THf5IK5T5\n" +
|
||||
"esyh+ioybEYBXXMQetHc4YbXAq06crNT2BP+AxQ4z27xu4X5CKLwwVMa2xwMCFDC\n" +
|
||||
"Y7mxQzEhFv8+HbOtM0IQMSSyh6+o0w4Yzt6i/TUOytZb1D7WHOEMpVTd1CMbXjN0\n" +
|
||||
"tFZvoO0V+n0OV3oT1E5AvwujVguUJblQiapbL890/nZbBtP3xMoFLLhc7sX7N2vR\n" +
|
||||
"1wLWsjOVAgMBAAECggEBAL+frUZDV86l20JqsFhs7T3f2MnKCahyg7AWciZif69O\n" +
|
||||
"e+BTQa14bg9lNm8YhLIim1vs3vyJIqei3eR3mxMs7k/vI3XYKVBv1WZgjSF8QXzS\n" +
|
||||
"8Mf/01MoD/sPOHby4T5dCpaVd89xMmV7lBubqHwUN1KkKJcVcPXc2Qy94C6/zrcu\n" +
|
||||
"Vb7Q91ugTvvhyalWEN02M3tzHEn4cXrnMLWPmTgxb7YtTMRIbJFx2um7/W9G5D+1\n" +
|
||||
"ZtKMjwt+P/yXynx3Sa555MjjJ1/LUyMSbQZoLE0SGOigz1VQnP8nFlpvXUHzktiA\n" +
|
||||
"IBPBBQSsEoS8H3z6WThwY72O795/i2l5mf7EUUAa2/kCgYEA+W7tJPg69KWHKn6E\n" +
|
||||
"gGZKIDTxvp3vRxjLedp7D7cu6bVPCBVIHrPQdmp/mneY6x7Air0a9gCVbh32iCrm\n" +
|
||||
"UrSBCio4i6jtqtna2T1Gc0z63K3UrVQPR0Z6Swq1XDOI6/I23ibxPr12/XXWWYlA\n" +
|
||||
"wkPwvewJVrjtMoP1dkAKVbyzImcCgYEA1WqS0xg7BL4iNGFPrrSme2blZOgga83x\n" +
|
||||
"CtY9dnWAs0IvdxsuItCDaFVJm3oU18ohirEMzqZvbRlwMlUawpd75hCNWyVF4cRo\n" +
|
||||
"+l1RvzOlC/7QXX/E+Kv7sTEUYH7hJyS46QhOtC1/ndAyObk9c2VNJmERfMwIHNm9\n" +
|
||||
"BfSN8yrq1KMCgYEA94fCZPbGIvSFj4EgYv+fvhhscvrucsLDYniTuUPTlXAtLttX\n" +
|
||||
"x8gwLuN/ID5hjarl7oi90bVAlZe8iOLx0M96YykFFmuc9/jcOsuZN2EEbq0/KocJ\n" +
|
||||
"5nSldgT5d7dYwLWNB6bjr5x8Egm3nwEbN+4OYZt0pRA9q+zSUfg5iV4K8y8CgYA7\n" +
|
||||
"S9YposTbJ3zXcuYx022iQc+gvsIrUdgUO7xuCm3M4KnRfRLPh4HLXk8KTNw3rKiv\n" +
|
||||
"IUw+qo2xEW1T/sNlp7M8FANCfNOyy+CjF4ScDFxiPdVk9RgkQ5y1+b4ApaAnQRPD\n" +
|
||||
"Y5SCiVW44lziHu7M/it2a2fxdbsXUQQtAGrkUltW4wKBgAPZyEGi4V2wryOswmkC\n" +
|
||||
"I29TbC6ChnUOvqJfSfz536eIi41D1Ua2Y/VlAK1ud7K8ilr/M4VoRjHqbPivR1Uk\n" +
|
||||
"RU7nO3cWRBuDBjgZWeAq5WFIMPbm9gRlaTECI6EFtAOI1GcHOsOBecd3PBiQNXvj\n" +
|
||||
"YyGD1wSrHQwDcnxtP1rqEQDV\n" +
|
||||
"-----END PRIVATE KEY-----\n"
|
||||
|
||||
// RsaPublicKeyPEM was generated from above with:
|
||||
// openssl rsa -pubout -in rsa.privkey.pem -out rsa.pubkey.pem
|
||||
RsaPublicKeyPEM = "-----BEGIN PUBLIC KEY-----\n" +
|
||||
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz/EiTOUgSGxaRXUQFBBE\n" +
|
||||
"aN+ROIhV30+UlQMwSKgE5AqX9y33RWYLLD3pYgXU7YHb0/wlgtpumlV2GXknACTm\n" +
|
||||
"rk6Rg8iezl4+2IcdaurtvDbkJXlN1ECuUGG+1To3iLHu6E4fkx3+SCuU+XrMofoq\n" +
|
||||
"MmxGAV1zEHrR3OGG1wKtOnKzU9gT/gMUOM9u8buF+Qii8MFTGtscDAhQwmO5sUMx\n" +
|
||||
"IRb/Ph2zrTNCEDEksoevqNMOGM7eov01DsrWW9Q+1hzhDKVU3dQjG14zdLRWb6Dt\n" +
|
||||
"Ffp9Dld6E9ROQL8Lo1YLlCW5UImqWy/PdP52WwbT98TKBSy4XO7F+zdr0dcC1rIz\n" +
|
||||
"lQIDAQAB\n" +
|
||||
"-----END PUBLIC KEY-----\n"
|
||||
|
||||
// RsaSignedAbcdHex was generated with:
|
||||
// echo -n "abcd" > sign.input
|
||||
// openssl sha256 -sign rsa.privkey.pem -out sign.rsa sign.input
|
||||
// Verify with:
|
||||
// openssl sha256 -verify rsa.pubkey.pem -signature sign.rsa sign.input
|
||||
RsaSignedAbcdHex = "4692f17adad62324a38a786d9b05eb7facf95c277729bc23e8d4f4fe25c0ac0e" +
|
||||
"9115f14ffd4dc6c00e6b2717677e96ed4da9e2226745db5c2d14ec8b4f96959e" +
|
||||
"a43ca73e3ab801d4e76b0c0599c741b9701dd0c22b20cc7a294fccce97d2eda2" +
|
||||
"72448830a19b0b1075f962d3bcf65922f81f4747dde1a2883ffce12941927af6" +
|
||||
"25549ea7b4f745a95f5558822af434cc7471ea5a21cce656a7667d29bff3e6f2" +
|
||||
"fb3d48bbee1a60116f0dd429d5691b008482e9ec42538bcf2692e1a685e836c8" +
|
||||
"49316fb3c49b58bd5a0c33f0256b09d59af08a018fe64cd31051cb9e9a1d3fde" +
|
||||
"e186bd80bfe304abbbec2ac18d3bee09465cb69f2638728aba4b9886a252768a"
|
||||
|
||||
// DsaPrivateKeyPEM was generated with:
|
||||
// openssl dsaparam -out dsa.param.pem 2048; openssl gendsa dsa.param.pem -out dsa.privkey.pem
|
||||
DsaPrivateKeyPEM = "-----BEGIN DSA PRIVATE KEY-----\n" +
|
||||
"MIIDVwIBAAKCAQEA3Q72qQc0ZM2eUjvZ/XOROQTbhmjzuZdzY7kd+82n54NPeqVz\n" +
|
||||
"+FWmk26INd7pUNbn/TjniN5+pC2pK+p9alFLE19bOvsY4x7Ugwrpd9sQIea3W4Q7\n" +
|
||||
"H9PaEW+BooFzT/feC9TmnU0ynwoNhZB33Cr3k3PFt/69lmyrkd91wTpXrstrQurU\n" +
|
||||
"h4baL5l6t2/JT3TxpKx6xe19hWqqFsBLu/j+kE2o2Aw1TVLHg4/74TJLfqAkWnCI\n" +
|
||||
"u9QL0uGQnW80kRfJnT+tUJXrXMpg1HxK7CsDEW5xsAWF370Is0UPaP7jRru6FiSN\n" +
|
||||
"ObcVhSqCIs+QJIWie9cRtwniwhoi4z6AfGeEYQIhAOkSzAsWja1fwB486/LCW+zv\n" +
|
||||
"DJKiCGLTalimfawzHQHFAoIBAQDJvCujEZ/WeQFLlP/0zS+2DjqdaxnKj1xuXfoL\n" +
|
||||
"LOocvMw06dnrQNmDu/nBQiYSfzf7zjwpnx+AVjBQssK0jXs0l7gw52FyCIztDejT\n" +
|
||||
"/ybMVudwKUGudxAEjOclOSQ7XVpPd4p3UMy+E6pkJ8VRiuNFDibYTD+O3XhFTj2l\n" +
|
||||
"6LqV/KcB2fWV1fUqfxax2vpcwe4clTJbJXUgJC3mYmUXv1U6z0hliDz2WMhbOyea\n" +
|
||||
"1fs31zYvIMyk2wJFJl7+EbXb8BgYcQt1FE4c14fuFnincAW0So+xBZ5DtiTja4lm\n" +
|
||||
"DE0OAI2hhLslSQ7rGwWt2zIKSFLtsWf78CP1wigJvWcUVhazAoIBAFAhxgtOaV8G\n" +
|
||||
"FJxs6A3TtXEmmrq3i5/w3/8l7skWamcHvcwrvw37g5KQY+N+85JZszHPjmUzEq/o\n" +
|
||||
"NOLjF6zq7+oRtWbOR3Mvc2hHsHg86tE/HAkBIu2G73c9UuskzGSMec4F6TyWKHvC\n" +
|
||||
"26F2cvu9aA42v/8q1d/QQs3+LrtARGhyFCqnBVNSZmZj5ngR0kLGUfs/LQklpGFe\n" +
|
||||
"VciXK57RxHSPo15lnKiHW9kCte7tGEg9Eber8paMz67dQDuj1yRkS5H7JXqRZjIP\n" +
|
||||
"UrICWGGk1vluQsuoyI/Nu8/tL7Im24xZH6XHBm4F+aijl9XlAJiKXfkCLIaxYSdc\n" +
|
||||
"4PXquTwtRLICIQCijrpD+UhidGFLPZCCLzBC239PrP7PabSVurWXHh5iYw==\n" +
|
||||
"-----END DSA PRIVATE KEY-----\n"
|
||||
|
||||
// DsaPrivateKeyPKCS8PEM is a copy of the private key above, encoded in PKCS#8 format.
|
||||
// Generated from above with:
|
||||
// openssl pkcs8 -topk8 -nocrypt -in dsa.privkey.pem -out dsa.privkey.pkcs8.pem
|
||||
DsaPrivateKeyPKCS8PEM = "-----BEGIN PRIVATE KEY-----\n" +
|
||||
"MIICZgIBADCCAjoGByqGSM44BAEwggItAoIBAQDdDvapBzRkzZ5SO9n9c5E5BNuG\n" +
|
||||
"aPO5l3NjuR37zafng096pXP4VaaTbog13ulQ1uf9OOeI3n6kLakr6n1qUUsTX1s6\n" +
|
||||
"+xjjHtSDCul32xAh5rdbhDsf09oRb4GigXNP994L1OadTTKfCg2FkHfcKveTc8W3\n" +
|
||||
"/r2WbKuR33XBOleuy2tC6tSHhtovmXq3b8lPdPGkrHrF7X2FaqoWwEu7+P6QTajY\n" +
|
||||
"DDVNUseDj/vhMkt+oCRacIi71AvS4ZCdbzSRF8mdP61QletcymDUfErsKwMRbnGw\n" +
|
||||
"BYXfvQizRQ9o/uNGu7oWJI05txWFKoIiz5AkhaJ71xG3CeLCGiLjPoB8Z4RhAiEA\n" +
|
||||
"6RLMCxaNrV/AHjzr8sJb7O8MkqIIYtNqWKZ9rDMdAcUCggEBAMm8K6MRn9Z5AUuU\n" +
|
||||
"//TNL7YOOp1rGcqPXG5d+gss6hy8zDTp2etA2YO7+cFCJhJ/N/vOPCmfH4BWMFCy\n" +
|
||||
"wrSNezSXuDDnYXIIjO0N6NP/JsxW53ApQa53EASM5yU5JDtdWk93indQzL4TqmQn\n" +
|
||||
"xVGK40UOJthMP47deEVOPaXoupX8pwHZ9ZXV9Sp/FrHa+lzB7hyVMlsldSAkLeZi\n" +
|
||||
"ZRe/VTrPSGWIPPZYyFs7J5rV+zfXNi8gzKTbAkUmXv4RtdvwGBhxC3UUThzXh+4W\n" +
|
||||
"eKdwBbRKj7EFnkO2JONriWYMTQ4AjaGEuyVJDusbBa3bMgpIUu2xZ/vwI/XCKAm9\n" +
|
||||
"ZxRWFrMEIwIhAKKOukP5SGJ0YUs9kIIvMELbf0+s/s9ptJW6tZceHmJj\n" +
|
||||
"-----END PRIVATE KEY-----\n"
|
||||
|
||||
// DsaPublicKeyPEM was generated from above with:
|
||||
// openssl dsa -in dsa.privkey.pem -pubout -out dsa.pubkey.pem
|
||||
DsaPublicKeyPEM = "-----BEGIN PUBLIC KEY-----\n" +
|
||||
"MIIDRzCCAjoGByqGSM44BAEwggItAoIBAQDdDvapBzRkzZ5SO9n9c5E5BNuGaPO5\n" +
|
||||
"l3NjuR37zafng096pXP4VaaTbog13ulQ1uf9OOeI3n6kLakr6n1qUUsTX1s6+xjj\n" +
|
||||
"HtSDCul32xAh5rdbhDsf09oRb4GigXNP994L1OadTTKfCg2FkHfcKveTc8W3/r2W\n" +
|
||||
"bKuR33XBOleuy2tC6tSHhtovmXq3b8lPdPGkrHrF7X2FaqoWwEu7+P6QTajYDDVN\n" +
|
||||
"UseDj/vhMkt+oCRacIi71AvS4ZCdbzSRF8mdP61QletcymDUfErsKwMRbnGwBYXf\n" +
|
||||
"vQizRQ9o/uNGu7oWJI05txWFKoIiz5AkhaJ71xG3CeLCGiLjPoB8Z4RhAiEA6RLM\n" +
|
||||
"CxaNrV/AHjzr8sJb7O8MkqIIYtNqWKZ9rDMdAcUCggEBAMm8K6MRn9Z5AUuU//TN\n" +
|
||||
"L7YOOp1rGcqPXG5d+gss6hy8zDTp2etA2YO7+cFCJhJ/N/vOPCmfH4BWMFCywrSN\n" +
|
||||
"ezSXuDDnYXIIjO0N6NP/JsxW53ApQa53EASM5yU5JDtdWk93indQzL4TqmQnxVGK\n" +
|
||||
"40UOJthMP47deEVOPaXoupX8pwHZ9ZXV9Sp/FrHa+lzB7hyVMlsldSAkLeZiZRe/\n" +
|
||||
"VTrPSGWIPPZYyFs7J5rV+zfXNi8gzKTbAkUmXv4RtdvwGBhxC3UUThzXh+4WeKdw\n" +
|
||||
"BbRKj7EFnkO2JONriWYMTQ4AjaGEuyVJDusbBa3bMgpIUu2xZ/vwI/XCKAm9ZxRW\n" +
|
||||
"FrMDggEFAAKCAQBQIcYLTmlfBhScbOgN07VxJpq6t4uf8N//Je7JFmpnB73MK78N\n" +
|
||||
"+4OSkGPjfvOSWbMxz45lMxKv6DTi4xes6u/qEbVmzkdzL3NoR7B4POrRPxwJASLt\n" +
|
||||
"hu93PVLrJMxkjHnOBek8lih7wtuhdnL7vWgONr//KtXf0ELN/i67QERochQqpwVT\n" +
|
||||
"UmZmY+Z4EdJCxlH7Py0JJaRhXlXIlyue0cR0j6NeZZyoh1vZArXu7RhIPRG3q/KW\n" +
|
||||
"jM+u3UA7o9ckZEuR+yV6kWYyD1KyAlhhpNb5bkLLqMiPzbvP7S+yJtuMWR+lxwZu\n" +
|
||||
"Bfmoo5fV5QCYil35AiyGsWEnXOD16rk8LUSy\n" +
|
||||
"-----END PUBLIC KEY-----\n"
|
||||
|
||||
// DsaSignedAbcdHex was generated with:
|
||||
// echo -n "abcd" > sign.input
|
||||
// openssl dgst -dss1 -sign dsa.privkey.pem -out sign.dsa sign.input
|
||||
// Note that this includes randomization so will give different results each time.
|
||||
// Verify with:
|
||||
// openssl dgst -dss1 -verify dsa.pubkey.pem -signature sign.dsa sign.input
|
||||
DsaSignedAbcdHex = "3045022100c287211dec54eab597ed5264f6fdf57faf651909914c533d42f0e3" +
|
||||
"2809e9141402205f34cc4b8ca12ebdf025bf36bbe1ff7d807d4cfe951ac1329a" +
|
||||
"35a39f5e971f10"
|
||||
|
||||
// EcdsaPrivateKeyPEM was generated with:
|
||||
// openssl ecparam -genkey -name prime256v1 -noout -out ecdsa.privkey.pem
|
||||
EcdsaPrivateKeyPEM = "-----BEGIN EC PRIVATE KEY-----\n" +
|
||||
"MHcCAQEEIHg0hg3vLqLVI7wv2y0cCk+kmwuwoKsMZqzqbjqP1AtjoAoGCCqGSM49\n" +
|
||||
"AwEHoUQDQgAEjHs6Rw7KFt8Wd2ZcioZi7eZY5nodUXMnCWUhZzsGVsPaexqUyPSr\n" +
|
||||
"9cQgrCe7MPRLJ524AO6rREqfs7FKt85++A==\n" +
|
||||
"-----END EC PRIVATE KEY-----\n"
|
||||
|
||||
// EcdsaPrivateKeyPKCS8PEM is a copy of the private key above, encoded in PKCS#8 format.
|
||||
// Generated from above with:
|
||||
// openssl pkcs8 -topk8 -nocrypt -in ecdsa.privkey.pem -out ecdsa.privkey.pkcs8.pem
|
||||
EcdsaPrivateKeyPKCS8PEM = "-----BEGIN PRIVATE KEY-----\n" +
|
||||
"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgeDSGDe8uotUjvC/b\n" +
|
||||
"LRwKT6SbC7CgqwxmrOpuOo/UC2OhRANCAASMezpHDsoW3xZ3ZlyKhmLt5ljmeh1R\n" +
|
||||
"cycJZSFnOwZWw9p7GpTI9Kv1xCCsJ7sw9EsnnbgA7qtESp+zsUq3zn74\n" +
|
||||
"-----END PRIVATE KEY-----\n"
|
||||
|
||||
// EcdsaPublicKeyPEM was generated from above with:
|
||||
// openssl ec -in ecdsa.privkey.pem -pubout -out ecdsa.pubkey.pem
|
||||
EcdsaPublicKeyPEM = "-----BEGIN PUBLIC KEY-----\n" +
|
||||
"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEjHs6Rw7KFt8Wd2ZcioZi7eZY5nod\n" +
|
||||
"UXMnCWUhZzsGVsPaexqUyPSr9cQgrCe7MPRLJ524AO6rREqfs7FKt85++A==\n" +
|
||||
"-----END PUBLIC KEY-----\n"
|
||||
|
||||
// EcdsaSignedAbcdHex was generated with:
|
||||
// echo -n "abcd" > sign.input
|
||||
// openssl dgst -sha256 -sign ecdsa.privkey.pem -out sign.ecdsa sign.input
|
||||
// Note that this includes randomization so will give different results each time.
|
||||
// Verify with:
|
||||
// openssl dgst -sha256 -verify ecdsa.pubkey.pem -signature sign.ecdsa sign.input
|
||||
EcdsaSignedAbcdHex = "304502202e0204dadf2baa35e426b66ab8cd32a9ba33421187645a9110512e3e" +
|
||||
"d1b8007b022100ae83f5f993ab3af6f46bb09b4f15d07cc582b03e1353879ada" +
|
||||
"ce68796fe537e5"
|
||||
)
|
||||
|
||||
// FromHex decodes a hex string to a byte array, and panics on error; only suitable for
|
||||
// use in test code.
|
||||
func FromHex(hexdata string) []byte {
|
||||
data, err := hex.DecodeString(hexdata)
|
||||
if err != nil {
|
||||
panic("non hex data: " + err.Error())
|
||||
}
|
||||
return data
|
||||
}
|
60
vendor/github.com/google/certificate-transparency-go/testdata/test-cert.pem
generated
vendored
Normal file
60
vendor/github.com/google/certificate-transparency-go/testdata/test-cert.pem
generated
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 6 (0x6)
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
Issuer: C=GB, O=Certificate Transparency CA, ST=Wales, L=Erw Wen
|
||||
Validity
|
||||
Not Before: Jun 1 00:00:00 2012 GMT
|
||||
Not After : Jun 1 00:00:00 2022 GMT
|
||||
Subject: C=GB, O=Certificate Transparency, ST=Wales, L=Erw Wen
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (1024 bit)
|
||||
Modulus:
|
||||
00:b1:fa:37:93:61:11:f8:79:2d:a2:08:1c:3f:e4:
|
||||
19:25:00:85:31:dc:7f:2c:65:7b:d9:e1:de:47:04:
|
||||
16:0b:4c:9f:19:d5:4a:da:44:70:40:4c:1c:51:34:
|
||||
1b:8f:1f:75:38:dd:dd:28:d9:ac:a4:83:69:fc:56:
|
||||
46:dd:cc:76:17:f8:16:8a:ae:5b:41:d4:33:31:fc:
|
||||
a2:da:df:c8:04:d5:72:08:94:90:61:f9:ee:f9:02:
|
||||
ca:47:ce:88:c6:44:e0:00:f0:6e:ee:cc:ab:dc:9d:
|
||||
d2:f6:8a:22:cc:b0:9d:c7:6e:0d:bc:73:52:77:65:
|
||||
b1:a3:7a:8c:67:62:53:dc:c1
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Subject Key Identifier:
|
||||
6A:0D:98:2A:3B:62:C4:4B:6D:2E:F4:E9:BB:7A:01:AA:9C:B7:98:E2
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:5F:9D:88:0D:C8:73:E6:54:D4:F8:0D:D8:E6:B0:C1:24:B4:47:C3:55
|
||||
DirName:/C=GB/O=Certificate Transparency CA/ST=Wales/L=Erw Wen
|
||||
serial:00
|
||||
|
||||
X509v3 Basic Constraints:
|
||||
CA:FALSE
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
17:1c:d8:4a:ac:41:4a:9a:03:0f:22:aa:c8:f6:88:b0:81:b2:
|
||||
70:9b:84:8b:4e:55:11:40:6c:d7:07:fe:d0:28:59:7a:9f:ae:
|
||||
fc:2e:ee:29:78:d6:33:aa:ac:14:ed:32:35:19:7d:a8:7e:0f:
|
||||
71:b8:87:5f:1a:c9:e7:8b:28:17:49:dd:ed:d0:07:e3:ec:f5:
|
||||
06:45:f8:cb:f6:67:25:6c:d6:a1:64:7b:5e:13:20:3b:b8:58:
|
||||
2d:e7:d6:69:6f:65:6d:1c:60:b9:5f:45:6b:7f:cf:33:85:71:
|
||||
90:8f:1c:69:72:7d:24:c4:fc:cd:24:92:95:79:58:14:d1:da:
|
||||
c0:e6
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICyjCCAjOgAwIBAgIBBjANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJHQjEk
|
||||
MCIGA1UEChMbQ2VydGlmaWNhdGUgVHJhbnNwYXJlbmN5IENBMQ4wDAYDVQQIEwVX
|
||||
YWxlczEQMA4GA1UEBxMHRXJ3IFdlbjAeFw0xMjA2MDEwMDAwMDBaFw0yMjA2MDEw
|
||||
MDAwMDBaMFIxCzAJBgNVBAYTAkdCMSEwHwYDVQQKExhDZXJ0aWZpY2F0ZSBUcmFu
|
||||
c3BhcmVuY3kxDjAMBgNVBAgTBVdhbGVzMRAwDgYDVQQHEwdFcncgV2VuMIGfMA0G
|
||||
CSqGSIb3DQEBAQUAA4GNADCBiQKBgQCx+jeTYRH4eS2iCBw/5BklAIUx3H8sZXvZ
|
||||
4d5HBBYLTJ8Z1UraRHBATBxRNBuPH3U43d0o2aykg2n8VkbdzHYX+BaKrltB1DMx
|
||||
/KLa38gE1XIIlJBh+e75AspHzojGROAA8G7uzKvcndL2iiLMsJ3Hbg28c1J3ZbGj
|
||||
eoxnYlPcwQIDAQABo4GsMIGpMB0GA1UdDgQWBBRqDZgqO2LES20u9Om7egGqnLeY
|
||||
4jB9BgNVHSMEdjB0gBRfnYgNyHPmVNT4DdjmsMEktEfDVaFZpFcwVTELMAkGA1UE
|
||||
BhMCR0IxJDAiBgNVBAoTG0NlcnRpZmljYXRlIFRyYW5zcGFyZW5jeSBDQTEOMAwG
|
||||
A1UECBMFV2FsZXMxEDAOBgNVBAcTB0VydyBXZW6CAQAwCQYDVR0TBAIwADANBgkq
|
||||
hkiG9w0BAQUFAAOBgQAXHNhKrEFKmgMPIqrI9oiwgbJwm4SLTlURQGzXB/7QKFl6
|
||||
n678Lu4peNYzqqwU7TI1GX2ofg9xuIdfGsnniygXSd3t0Afj7PUGRfjL9mclbNah
|
||||
ZHteEyA7uFgt59Zpb2VtHGC5X0Vrf88zhXGQjxxpcn0kxPzNJJKVeVgU0drA5g==
|
||||
-----END CERTIFICATE-----
|
BIN
vendor/github.com/google/certificate-transparency-go/testdata/test-cert.proof
generated
vendored
Normal file
BIN
vendor/github.com/google/certificate-transparency-go/testdata/test-cert.proof
generated
vendored
Normal file
Binary file not shown.
65
vendor/github.com/google/certificate-transparency-go/tls/hash_test.go
generated
vendored
Normal file
65
vendor/github.com/google/certificate-transparency-go/tls/hash_test.go
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/certificate-transparency-go/testdata"
|
||||
)
|
||||
|
||||
func TestGenerateHash(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in string // hex encoded
|
||||
algo HashAlgorithm
|
||||
want string // hex encoded
|
||||
errstr string
|
||||
}{
|
||||
// Empty hash values
|
||||
{"", MD5, "d41d8cd98f00b204e9800998ecf8427e", ""},
|
||||
{"", SHA1, "da39a3ee5e6b4b0d3255bfef95601890afd80709", ""},
|
||||
{"", SHA224, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", ""},
|
||||
{"", SHA256, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", ""},
|
||||
{"", SHA384, "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", ""},
|
||||
{"", SHA512, "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", ""},
|
||||
{"", 999, "", "unsupported"},
|
||||
|
||||
// Hashes of "abcd".
|
||||
{"61626364", MD5, testdata.AbcdMD5, ""},
|
||||
{"61626364", SHA1, testdata.AbcdSHA1, ""},
|
||||
{"61626364", SHA224, testdata.AbcdSHA224, ""},
|
||||
{"61626364", SHA256, testdata.AbcdSHA256, ""},
|
||||
{"61626364", SHA384, testdata.AbcdSHA384, ""},
|
||||
{"61626364", SHA512, testdata.AbcdSHA512, ""},
|
||||
}
|
||||
for _, test := range tests {
|
||||
got, _, err := generateHash(test.algo, testdata.FromHex(test.in))
|
||||
if test.errstr != "" {
|
||||
if err == nil {
|
||||
t.Errorf("generateHash(%s)=%s,nil; want error %q", test.in, hex.EncodeToString(got), test.errstr)
|
||||
} else if !strings.Contains(err.Error(), test.errstr) {
|
||||
t.Errorf("generateHash(%s)=nil,%q; want error %q", test.in, test.errstr, err.Error())
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("generateHash(%s)=nil,%q; want %s", test.in, err, test.want)
|
||||
} else if hex.EncodeToString(got) != test.want {
|
||||
t.Errorf("generateHash(%s)=%s,nil; want %s", test.in, hex.EncodeToString(got), test.want)
|
||||
}
|
||||
}
|
||||
}
|
152
vendor/github.com/google/certificate-transparency-go/tls/signature.go
generated
vendored
Normal file
152
vendor/github.com/google/certificate-transparency-go/tls/signature.go
generated
vendored
Normal file
@ -0,0 +1,152 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
_ "crypto/md5" // For registration side-effect
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
_ "crypto/sha1" // For registration side-effect
|
||||
_ "crypto/sha256" // For registration side-effect
|
||||
_ "crypto/sha512" // For registration side-effect
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/big"
|
||||
|
||||
"github.com/google/certificate-transparency-go/asn1"
|
||||
)
|
||||
|
||||
type dsaSig struct {
|
||||
R, S *big.Int
|
||||
}
|
||||
|
||||
func generateHash(algo HashAlgorithm, data []byte) ([]byte, crypto.Hash, error) {
|
||||
var hashType crypto.Hash
|
||||
switch algo {
|
||||
case MD5:
|
||||
hashType = crypto.MD5
|
||||
case SHA1:
|
||||
hashType = crypto.SHA1
|
||||
case SHA224:
|
||||
hashType = crypto.SHA224
|
||||
case SHA256:
|
||||
hashType = crypto.SHA256
|
||||
case SHA384:
|
||||
hashType = crypto.SHA384
|
||||
case SHA512:
|
||||
hashType = crypto.SHA512
|
||||
default:
|
||||
return nil, hashType, fmt.Errorf("unsupported Algorithm.Hash in signature: %v", algo)
|
||||
}
|
||||
|
||||
hasher := hashType.New()
|
||||
if _, err := hasher.Write(data); err != nil {
|
||||
return nil, hashType, fmt.Errorf("failed to write to hasher: %v", err)
|
||||
}
|
||||
return hasher.Sum([]byte{}), hashType, nil
|
||||
}
|
||||
|
||||
// VerifySignature verifies that the passed in signature over data was created by the given PublicKey.
|
||||
func VerifySignature(pubKey crypto.PublicKey, data []byte, sig DigitallySigned) error {
|
||||
hash, hashType, err := generateHash(sig.Algorithm.Hash, data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch sig.Algorithm.Signature {
|
||||
case RSA:
|
||||
rsaKey, ok := pubKey.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot verify RSA signature with %T key", pubKey)
|
||||
}
|
||||
if err := rsa.VerifyPKCS1v15(rsaKey, hashType, hash, sig.Signature); err != nil {
|
||||
return fmt.Errorf("failed to verify rsa signature: %v", err)
|
||||
}
|
||||
case DSA:
|
||||
dsaKey, ok := pubKey.(*dsa.PublicKey)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot verify DSA signature with %T key", pubKey)
|
||||
}
|
||||
var dsaSig dsaSig
|
||||
rest, err := asn1.Unmarshal(sig.Signature, &dsaSig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unmarshal DSA signature: %v", err)
|
||||
}
|
||||
if len(rest) != 0 {
|
||||
log.Printf("Garbage following signature %v", rest)
|
||||
}
|
||||
if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 {
|
||||
return errors.New("DSA signature contained zero or negative values")
|
||||
}
|
||||
if !dsa.Verify(dsaKey, hash, dsaSig.R, dsaSig.S) {
|
||||
return errors.New("failed to verify DSA signature")
|
||||
}
|
||||
case ECDSA:
|
||||
ecdsaKey, ok := pubKey.(*ecdsa.PublicKey)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot verify ECDSA signature with %T key", pubKey)
|
||||
}
|
||||
var ecdsaSig dsaSig
|
||||
rest, err := asn1.Unmarshal(sig.Signature, &ecdsaSig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unmarshal ECDSA signature: %v", err)
|
||||
}
|
||||
if len(rest) != 0 {
|
||||
log.Printf("Garbage following signature %v", rest)
|
||||
}
|
||||
if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
|
||||
return errors.New("ECDSA signature contained zero or negative values")
|
||||
}
|
||||
|
||||
if !ecdsa.Verify(ecdsaKey, hash, ecdsaSig.R, ecdsaSig.S) {
|
||||
return errors.New("failed to verify ECDSA signature")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unsupported Algorithm.Signature in signature: %v", sig.Algorithm.Hash)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateSignature builds a signature over the given data using the specified hash algorithm and private key.
|
||||
func CreateSignature(privKey crypto.PrivateKey, hashAlgo HashAlgorithm, data []byte) (DigitallySigned, error) {
|
||||
var sig DigitallySigned
|
||||
sig.Algorithm.Hash = hashAlgo
|
||||
hash, hashType, err := generateHash(sig.Algorithm.Hash, data)
|
||||
if err != nil {
|
||||
return sig, err
|
||||
}
|
||||
|
||||
switch privKey := privKey.(type) {
|
||||
case rsa.PrivateKey:
|
||||
sig.Algorithm.Signature = RSA
|
||||
sig.Signature, err = rsa.SignPKCS1v15(rand.Reader, &privKey, hashType, hash)
|
||||
return sig, err
|
||||
case ecdsa.PrivateKey:
|
||||
sig.Algorithm.Signature = ECDSA
|
||||
var ecdsaSig dsaSig
|
||||
ecdsaSig.R, ecdsaSig.S, err = ecdsa.Sign(rand.Reader, &privKey, hash)
|
||||
if err != nil {
|
||||
return sig, err
|
||||
}
|
||||
sig.Signature, err = asn1.Marshal(ecdsaSig)
|
||||
return sig, err
|
||||
default:
|
||||
return sig, fmt.Errorf("unsupported private key type %T", privKey)
|
||||
}
|
||||
}
|
160
vendor/github.com/google/certificate-transparency-go/tls/signature_test.go
generated
vendored
Normal file
160
vendor/github.com/google/certificate-transparency-go/tls/signature_test.go
generated
vendored
Normal file
@ -0,0 +1,160 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tls_test
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"encoding/pem"
|
||||
mathrand "math/rand"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/certificate-transparency-go/testdata"
|
||||
"github.com/google/certificate-transparency-go/tls"
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
)
|
||||
|
||||
func TestVerifySignature(t *testing.T) {
|
||||
var tests = []struct {
|
||||
pubKey crypto.PublicKey
|
||||
in string // hex encoded
|
||||
hashAlgo tls.HashAlgorithm
|
||||
sigAlgo tls.SignatureAlgorithm
|
||||
errstr string
|
||||
sig string // hex encoded
|
||||
}{
|
||||
{PEM2PK(testdata.RsaPublicKeyPEM), "61626364", 99, tls.ECDSA, "unsupported Algorithm.Hash", "1234"},
|
||||
{PEM2PK(testdata.RsaPublicKeyPEM), "61626364", tls.SHA256, 99, "unsupported Algorithm.Signature", "1234"},
|
||||
|
||||
{PEM2PK(testdata.RsaPublicKeyPEM), "61626364", tls.SHA256, tls.DSA, "cannot verify DSA", "1234"},
|
||||
{PEM2PK(testdata.RsaPublicKeyPEM), "61626364", tls.SHA256, tls.ECDSA, "cannot verify ECDSA", "1234"},
|
||||
{PEM2PK(testdata.RsaPublicKeyPEM), "61626364", tls.SHA256, tls.RSA, "verification error", "1234"},
|
||||
{PEM2PK(testdata.RsaPublicKeyPEM), "61626364", tls.SHA256, tls.ECDSA, "cannot verify ECDSA", "1234"},
|
||||
|
||||
{PEM2PK(testdata.DsaPublicKeyPEM), "61626364", tls.SHA1, tls.RSA, "cannot verify RSA", "1234"},
|
||||
{PEM2PK(testdata.DsaPublicKeyPEM), "61626364", tls.SHA1, tls.ECDSA, "cannot verify ECDSA", "1234"},
|
||||
{PEM2PK(testdata.DsaPublicKeyPEM), "61626364", tls.SHA1, tls.DSA, "failed to unmarshal DSA signature", "1234"},
|
||||
{PEM2PK(testdata.DsaPublicKeyPEM), "61626364", tls.SHA1, tls.DSA, "failed to verify DSA signature", "3006020101020101eeff"},
|
||||
{PEM2PK(testdata.DsaPublicKeyPEM), "61626364", tls.SHA1, tls.DSA, "zero or negative values", "3006020100020181"},
|
||||
|
||||
{PEM2PK(testdata.EcdsaPublicKeyPEM), "61626364", tls.SHA256, tls.RSA, "cannot verify RSA", "1234"},
|
||||
{PEM2PK(testdata.EcdsaPublicKeyPEM), "61626364", tls.SHA256, tls.DSA, "cannot verify DSA", "1234"},
|
||||
{PEM2PK(testdata.EcdsaPublicKeyPEM), "61626364", tls.SHA256, tls.ECDSA, "failed to unmarshal ECDSA signature", "1234"},
|
||||
{PEM2PK(testdata.EcdsaPublicKeyPEM), "61626364", tls.SHA256, tls.ECDSA, "failed to verify ECDSA signature", "3006020101020101eeff"},
|
||||
{PEM2PK(testdata.EcdsaPublicKeyPEM), "61626364", tls.SHA256, tls.ECDSA, "zero or negative values", "3006020100020181"},
|
||||
|
||||
{PEM2PK(testdata.RsaPublicKeyPEM), "61626364", tls.SHA256, tls.RSA, "", testdata.RsaSignedAbcdHex},
|
||||
{PEM2PK(testdata.DsaPublicKeyPEM), "61626364", tls.SHA1, tls.DSA, "", testdata.DsaSignedAbcdHex},
|
||||
{PEM2PK(testdata.EcdsaPublicKeyPEM), "61626364", tls.SHA256, tls.ECDSA, "", testdata.EcdsaSignedAbcdHex},
|
||||
}
|
||||
for _, test := range tests {
|
||||
algo := tls.SignatureAndHashAlgorithm{Hash: test.hashAlgo, Signature: test.sigAlgo}
|
||||
signed := tls.DigitallySigned{Algorithm: algo, Signature: testdata.FromHex(test.sig)}
|
||||
|
||||
err := tls.VerifySignature(test.pubKey, testdata.FromHex(test.in), signed)
|
||||
if test.errstr != "" {
|
||||
if err == nil {
|
||||
t.Errorf("VerifySignature(%s)=nil; want %q", test.in, test.errstr)
|
||||
} else if !strings.Contains(err.Error(), test.errstr) {
|
||||
t.Errorf("VerifySignature(%s)=%q; want %q", test.in, err.Error(), test.errstr)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("VerifySignature(%s)=%q; want nil", test.in, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateSignatureVerifySignatureRoundTrip(t *testing.T) {
|
||||
var tests = []struct {
|
||||
privKey crypto.PrivateKey
|
||||
pubKey crypto.PublicKey
|
||||
hashAlgo tls.HashAlgorithm
|
||||
}{
|
||||
{PEM2PrivKey(testdata.RsaPrivateKeyPEM), PEM2PK(testdata.RsaPublicKeyPEM), tls.SHA256},
|
||||
{PEM2PrivKey(testdata.EcdsaPrivateKeyPKCS8PEM), PEM2PK(testdata.EcdsaPublicKeyPEM), tls.SHA256},
|
||||
}
|
||||
seed := time.Now().UnixNano()
|
||||
r := mathrand.New(mathrand.NewSource(seed))
|
||||
for _, test := range tests {
|
||||
for j := 0; j < 1; j++ {
|
||||
dataLen := 10 + r.Intn(100)
|
||||
data := make([]byte, dataLen)
|
||||
_, _ = r.Read(data)
|
||||
sig, err := tls.CreateSignature(test.privKey, test.hashAlgo, data)
|
||||
if err != nil {
|
||||
t.Errorf("CreateSignature(%T, %v) failed with: %q", test.privKey, test.hashAlgo, err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
if err := tls.VerifySignature(test.pubKey, data, sig); err != nil {
|
||||
t.Errorf("VerifySignature(%T, %v) failed with: %q", test.pubKey, test.hashAlgo, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateSignatureFailures(t *testing.T) {
|
||||
var tests = []struct {
|
||||
privKey crypto.PrivateKey
|
||||
hashAlgo tls.HashAlgorithm
|
||||
in string // hex encoded
|
||||
errstr string
|
||||
}{
|
||||
{PEM2PrivKey(testdata.EcdsaPrivateKeyPKCS8PEM), 99, "abcd", "unsupported Algorithm.Hash"},
|
||||
{nil, tls.SHA256, "abcd", "unsupported private key type"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
if sig, err := tls.CreateSignature(test.privKey, test.hashAlgo, testdata.FromHex(test.in)); err == nil {
|
||||
t.Errorf("CreateSignature(%T, %v)=%v,nil; want error %q", test.privKey, test.hashAlgo, sig, test.errstr)
|
||||
} else if !strings.Contains(err.Error(), test.errstr) {
|
||||
t.Errorf("CreateSignature(%T, %v)=nil,%q; want error %q", test.privKey, test.hashAlgo, err.Error(), test.errstr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func PEM2PK(s string) crypto.PublicKey {
|
||||
p, _ := pem.Decode([]byte(s))
|
||||
if p == nil {
|
||||
panic("no PEM block found in " + s)
|
||||
}
|
||||
pubKey, _ := x509.ParsePKIXPublicKey(p.Bytes)
|
||||
if pubKey == nil {
|
||||
panic("public key not parsed from " + s)
|
||||
}
|
||||
return pubKey
|
||||
}
|
||||
func PEM2PrivKey(s string) crypto.PrivateKey {
|
||||
p, _ := pem.Decode([]byte(s))
|
||||
if p == nil {
|
||||
panic("no PEM block found in " + s)
|
||||
}
|
||||
|
||||
// Try various different private key formats one after another.
|
||||
if rsaPrivKey, err := x509.ParsePKCS1PrivateKey(p.Bytes); err == nil {
|
||||
return *rsaPrivKey
|
||||
}
|
||||
if pkcs8Key, err := x509.ParsePKCS8PrivateKey(p.Bytes); err == nil {
|
||||
if reflect.TypeOf(pkcs8Key).Kind() == reflect.Ptr {
|
||||
pkcs8Key = reflect.ValueOf(pkcs8Key).Elem().Interface()
|
||||
}
|
||||
return pkcs8Key
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
711
vendor/github.com/google/certificate-transparency-go/tls/tls.go
generated
vendored
Normal file
711
vendor/github.com/google/certificate-transparency-go/tls/tls.go
generated
vendored
Normal file
@ -0,0 +1,711 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package tls implements functionality for dealing with TLS-encoded data,
|
||||
// as defined in RFC 5246. This includes parsing and generation of TLS-encoded
|
||||
// data, together with utility functions for dealing with the DigitallySigned
|
||||
// TLS type.
|
||||
package tls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// This file holds utility functions for TLS encoding/decoding data
|
||||
// as per RFC 5246 section 4.
|
||||
|
||||
// A structuralError suggests that the TLS data is valid, but the Go type
|
||||
// which is receiving it doesn't match.
|
||||
type structuralError struct {
|
||||
field string
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e structuralError) Error() string {
|
||||
var prefix string
|
||||
if e.field != "" {
|
||||
prefix = e.field + ": "
|
||||
}
|
||||
return "tls: structure error: " + prefix + e.msg
|
||||
}
|
||||
|
||||
// A syntaxError suggests that the TLS data is invalid.
|
||||
type syntaxError struct {
|
||||
field string
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e syntaxError) Error() string {
|
||||
var prefix string
|
||||
if e.field != "" {
|
||||
prefix = e.field + ": "
|
||||
}
|
||||
return "tls: syntax error: " + prefix + e.msg
|
||||
}
|
||||
|
||||
// Uint24 is an unsigned 3-byte integer.
|
||||
type Uint24 uint32
|
||||
|
||||
// Enum is an unsigned integer.
|
||||
type Enum uint64
|
||||
|
||||
var (
|
||||
uint8Type = reflect.TypeOf(uint8(0))
|
||||
uint16Type = reflect.TypeOf(uint16(0))
|
||||
uint24Type = reflect.TypeOf(Uint24(0))
|
||||
uint32Type = reflect.TypeOf(uint32(0))
|
||||
uint64Type = reflect.TypeOf(uint64(0))
|
||||
enumType = reflect.TypeOf(Enum(0))
|
||||
)
|
||||
|
||||
// Unmarshal parses the TLS-encoded data in b and uses the reflect package to
|
||||
// fill in an arbitrary value pointed at by val. Because Unmarshal uses the
|
||||
// reflect package, the structs being written to must use exported fields
|
||||
// (upper case names).
|
||||
//
|
||||
// The mappings between TLS types and Go types is as follows; some fields
|
||||
// must have tags (to indicate their encoded size).
|
||||
//
|
||||
// TLS Go Required Tags
|
||||
// opaque byte / uint8
|
||||
// uint8 byte / uint8
|
||||
// uint16 uint16
|
||||
// uint24 tls.Uint24
|
||||
// uint32 uint32
|
||||
// uint64 uint64
|
||||
// enum tls.Enum size:S or maxval:N
|
||||
// Type<N,M> []Type minlen:N,maxlen:M
|
||||
// opaque[N] [N]byte / [N]uint8
|
||||
// uint8[N] [N]byte / [N]uint8
|
||||
// struct { } struct { }
|
||||
// select(T) {
|
||||
// case e1: Type *T selector:Field,val:e1
|
||||
// }
|
||||
//
|
||||
// TLS variants (RFC 5246 s4.6.1) are only supported when the value of the
|
||||
// associated enumeration type is available earlier in the same enclosing
|
||||
// struct, and each possible variant is marked with a selector tag (to
|
||||
// indicate which field selects the variants) and a val tag (to indicate
|
||||
// what value of the selector picks this particular field).
|
||||
//
|
||||
// For example, a TLS structure:
|
||||
//
|
||||
// enum { e1(1), e2(2) } EnumType;
|
||||
// struct {
|
||||
// EnumType sel;
|
||||
// select(sel) {
|
||||
// case e1: uint16
|
||||
// case e2: uint32
|
||||
// } data;
|
||||
// } VariantItem;
|
||||
//
|
||||
// would have a corresponding Go type:
|
||||
//
|
||||
// type VariantItem struct {
|
||||
// Sel tls.Enum `tls:"maxval:2"`
|
||||
// Data16 *uint16 `tls:"selector:Sel,val:1"`
|
||||
// Data32 *uint32 `tls:"selector:Sel,val:2"`
|
||||
// }
|
||||
//
|
||||
// TLS fixed-length vectors of types other than opaque or uint8 are not supported.
|
||||
//
|
||||
// For TLS variable-length vectors that are themselves used in other vectors,
|
||||
// create a single-field structure to represent the inner type. For example, for:
|
||||
//
|
||||
// opaque InnerType<1..65535>;
|
||||
// struct {
|
||||
// InnerType inners<1,65535>;
|
||||
// } Something;
|
||||
//
|
||||
// convert to:
|
||||
//
|
||||
// type InnerType struct {
|
||||
// Val []byte `tls:"minlen:1,maxlen:65535"`
|
||||
// }
|
||||
// type Something struct {
|
||||
// Inners []InnerType `tls:"minlen:1,maxlen:65535"`
|
||||
// }
|
||||
//
|
||||
// If the encoded value does not fit in the Go type, Unmarshal returns a parse error.
|
||||
func Unmarshal(b []byte, val interface{}) ([]byte, error) {
|
||||
return UnmarshalWithParams(b, val, "")
|
||||
}
|
||||
|
||||
// UnmarshalWithParams allows field parameters to be specified for the
|
||||
// top-level element. The form of the params is the same as the field tags.
|
||||
func UnmarshalWithParams(b []byte, val interface{}, params string) ([]byte, error) {
|
||||
info, err := fieldTagToFieldInfo(params, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// The passed in interface{} is a pointer (to allow the value to be written
|
||||
// to); extract the pointed-to object as a reflect.Value, so parseField
|
||||
// can do various introspection things.
|
||||
v := reflect.ValueOf(val).Elem()
|
||||
offset, err := parseField(v, b, 0, info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[offset:], nil
|
||||
}
|
||||
|
||||
// Return the number of bytes needed to encode values up to (and including) x.
|
||||
func byteCount(x uint64) uint {
|
||||
switch {
|
||||
case x < 0x100:
|
||||
return 1
|
||||
case x < 0x10000:
|
||||
return 2
|
||||
case x < 0x1000000:
|
||||
return 3
|
||||
case x < 0x100000000:
|
||||
return 4
|
||||
case x < 0x10000000000:
|
||||
return 5
|
||||
case x < 0x1000000000000:
|
||||
return 6
|
||||
case x < 0x100000000000000:
|
||||
return 7
|
||||
default:
|
||||
return 8
|
||||
}
|
||||
}
|
||||
|
||||
type fieldInfo struct {
|
||||
count uint // Number of bytes
|
||||
countSet bool
|
||||
minlen uint64 // Only relevant for slices
|
||||
maxlen uint64 // Only relevant for slices
|
||||
selector string // Only relevant for select sub-values
|
||||
val uint64 // Only relevant for select sub-values
|
||||
name string // Used for better error messages
|
||||
}
|
||||
|
||||
func (i *fieldInfo) fieldName() string {
|
||||
if i == nil {
|
||||
return ""
|
||||
}
|
||||
return i.name
|
||||
}
|
||||
|
||||
// Given a tag string, return a fieldInfo describing the field.
|
||||
func fieldTagToFieldInfo(str string, name string) (*fieldInfo, error) {
|
||||
var info *fieldInfo
|
||||
// Iterate over clauses in the tag, ignoring any that don't parse properly.
|
||||
for _, part := range strings.Split(str, ",") {
|
||||
switch {
|
||||
case strings.HasPrefix(part, "maxval:"):
|
||||
if v, err := strconv.ParseUint(part[7:], 10, 64); err == nil {
|
||||
info = &fieldInfo{count: byteCount(v), countSet: true}
|
||||
}
|
||||
case strings.HasPrefix(part, "size:"):
|
||||
if sz, err := strconv.ParseUint(part[5:], 10, 32); err == nil {
|
||||
info = &fieldInfo{count: uint(sz), countSet: true}
|
||||
}
|
||||
case strings.HasPrefix(part, "maxlen:"):
|
||||
v, err := strconv.ParseUint(part[7:], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if info == nil {
|
||||
info = &fieldInfo{}
|
||||
}
|
||||
info.count = byteCount(v)
|
||||
info.countSet = true
|
||||
info.maxlen = v
|
||||
case strings.HasPrefix(part, "minlen:"):
|
||||
v, err := strconv.ParseUint(part[7:], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if info == nil {
|
||||
info = &fieldInfo{}
|
||||
}
|
||||
info.minlen = v
|
||||
case strings.HasPrefix(part, "selector:"):
|
||||
if info == nil {
|
||||
info = &fieldInfo{}
|
||||
}
|
||||
info.selector = part[9:]
|
||||
case strings.HasPrefix(part, "val:"):
|
||||
v, err := strconv.ParseUint(part[4:], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if info == nil {
|
||||
info = &fieldInfo{}
|
||||
}
|
||||
info.val = v
|
||||
}
|
||||
}
|
||||
if info != nil {
|
||||
info.name = name
|
||||
if info.selector == "" {
|
||||
if info.count < 1 {
|
||||
return nil, structuralError{name, "field of unknown size in " + str}
|
||||
} else if info.count > 8 {
|
||||
return nil, structuralError{name, "specified size too large in " + str}
|
||||
} else if info.minlen > info.maxlen {
|
||||
return nil, structuralError{name, "specified length range inverted in " + str}
|
||||
} else if info.val > 0 {
|
||||
return nil, structuralError{name, "specified selector value but not field in " + str}
|
||||
}
|
||||
}
|
||||
} else if name != "" {
|
||||
info = &fieldInfo{name: name}
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// Check that a value fits into a field described by a fieldInfo structure.
|
||||
func (i fieldInfo) check(val uint64, fldName string) error {
|
||||
if val >= (1 << (8 * i.count)) {
|
||||
return structuralError{fldName, fmt.Sprintf("value %d too large for size", val)}
|
||||
}
|
||||
if i.maxlen != 0 {
|
||||
if val < i.minlen {
|
||||
return structuralError{fldName, fmt.Sprintf("value %d too small for minimum %d", val, i.minlen)}
|
||||
}
|
||||
if val > i.maxlen {
|
||||
return structuralError{fldName, fmt.Sprintf("value %d too large for maximum %d", val, i.maxlen)}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// readVarUint reads an big-endian unsigned integer of the given size in
|
||||
// bytes.
|
||||
func readVarUint(data []byte, info *fieldInfo) (uint64, error) {
|
||||
if info == nil || !info.countSet {
|
||||
return 0, structuralError{info.fieldName(), "no field size information available"}
|
||||
}
|
||||
if len(data) < int(info.count) {
|
||||
return 0, syntaxError{info.fieldName(), "truncated variable-length integer"}
|
||||
}
|
||||
var result uint64
|
||||
for i := uint(0); i < info.count; i++ {
|
||||
result = (result << 8) | uint64(data[i])
|
||||
}
|
||||
if err := info.check(result, info.name); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// parseField is the main parsing function. Given a byte slice and an offset
|
||||
// (in bytes) into the data, it will try to parse a suitable ASN.1 value out
|
||||
// and store it in the given Value.
|
||||
func parseField(v reflect.Value, data []byte, initOffset int, info *fieldInfo) (int, error) {
|
||||
offset := initOffset
|
||||
rest := data[offset:]
|
||||
|
||||
fieldType := v.Type()
|
||||
// First look for known fixed types.
|
||||
switch fieldType {
|
||||
case uint8Type:
|
||||
if len(rest) < 1 {
|
||||
return offset, syntaxError{info.fieldName(), "truncated uint8"}
|
||||
}
|
||||
v.SetUint(uint64(rest[0]))
|
||||
offset++
|
||||
return offset, nil
|
||||
case uint16Type:
|
||||
if len(rest) < 2 {
|
||||
return offset, syntaxError{info.fieldName(), "truncated uint16"}
|
||||
}
|
||||
v.SetUint(uint64(binary.BigEndian.Uint16(rest)))
|
||||
offset += 2
|
||||
return offset, nil
|
||||
case uint24Type:
|
||||
if len(rest) < 3 {
|
||||
return offset, syntaxError{info.fieldName(), "truncated uint24"}
|
||||
}
|
||||
v.SetUint(uint64(data[0])<<16 | uint64(data[1])<<8 | uint64(data[2]))
|
||||
offset += 3
|
||||
return offset, nil
|
||||
case uint32Type:
|
||||
if len(rest) < 4 {
|
||||
return offset, syntaxError{info.fieldName(), "truncated uint32"}
|
||||
}
|
||||
v.SetUint(uint64(binary.BigEndian.Uint32(rest)))
|
||||
offset += 4
|
||||
return offset, nil
|
||||
case uint64Type:
|
||||
if len(rest) < 8 {
|
||||
return offset, syntaxError{info.fieldName(), "truncated uint64"}
|
||||
}
|
||||
v.SetUint(uint64(binary.BigEndian.Uint64(rest)))
|
||||
offset += 8
|
||||
return offset, nil
|
||||
}
|
||||
|
||||
// Now deal with user-defined types.
|
||||
switch v.Kind() {
|
||||
case enumType.Kind():
|
||||
// Assume that anything of the same kind as Enum is an Enum, so that
|
||||
// users can alias types of their own to Enum.
|
||||
val, err := readVarUint(rest, info)
|
||||
if err != nil {
|
||||
return offset, err
|
||||
}
|
||||
v.SetUint(val)
|
||||
offset += int(info.count)
|
||||
return offset, nil
|
||||
case reflect.Struct:
|
||||
structType := fieldType
|
||||
// TLS includes a select(Enum) {..} construct, where the value of an enum
|
||||
// indicates which variant field is present (like a C union). We require
|
||||
// that the enum value be an earlier field in the same structure (the selector),
|
||||
// and that each of the possible variant destination fields be pointers.
|
||||
// So the Go mapping looks like:
|
||||
// type variantType struct {
|
||||
// Which tls.Enum `tls:"size:1"` // this is the selector
|
||||
// Val1 *type1 `tls:"selector:Which,val:1"` // this is a destination
|
||||
// Val2 *type2 `tls:"selector:Which,val:1"` // this is a destination
|
||||
// }
|
||||
|
||||
// To deal with this, we track any enum-like fields and their values...
|
||||
enums := make(map[string]uint64)
|
||||
// .. and we track which selector names we've seen (in the destination field tags),
|
||||
// and whether a destination for that selector has been chosen.
|
||||
selectorSeen := make(map[string]bool)
|
||||
for i := 0; i < structType.NumField(); i++ {
|
||||
// Find information about this field.
|
||||
tag := structType.Field(i).Tag.Get("tls")
|
||||
fieldInfo, err := fieldTagToFieldInfo(tag, structType.Field(i).Name)
|
||||
if err != nil {
|
||||
return offset, err
|
||||
}
|
||||
|
||||
destination := v.Field(i)
|
||||
if fieldInfo.selector != "" {
|
||||
// This is a possible select(Enum) destination, so first check that the referenced
|
||||
// selector field has already been seen earlier in the struct.
|
||||
choice, ok := enums[fieldInfo.selector]
|
||||
if !ok {
|
||||
return offset, structuralError{fieldInfo.name, "selector not seen: " + fieldInfo.selector}
|
||||
}
|
||||
if structType.Field(i).Type.Kind() != reflect.Ptr {
|
||||
return offset, structuralError{fieldInfo.name, "choice field not a pointer type"}
|
||||
}
|
||||
// Is this the first mention of the selector field name? If so, remember it.
|
||||
seen, ok := selectorSeen[fieldInfo.selector]
|
||||
if !ok {
|
||||
selectorSeen[fieldInfo.selector] = false
|
||||
}
|
||||
if choice != fieldInfo.val {
|
||||
// This destination field was not the chosen one, so make it nil (we checked
|
||||
// it was a pointer above).
|
||||
v.Field(i).Set(reflect.Zero(structType.Field(i).Type))
|
||||
continue
|
||||
}
|
||||
if seen {
|
||||
// We already saw a different destination field receive the value for this
|
||||
// selector value, which indicates a badly annotated structure.
|
||||
return offset, structuralError{fieldInfo.name, "duplicate selector value for " + fieldInfo.selector}
|
||||
}
|
||||
selectorSeen[fieldInfo.selector] = true
|
||||
// Make an object of the pointed-to type and parse into that.
|
||||
v.Field(i).Set(reflect.New(structType.Field(i).Type.Elem()))
|
||||
destination = v.Field(i).Elem()
|
||||
}
|
||||
offset, err = parseField(destination, data, offset, fieldInfo)
|
||||
if err != nil {
|
||||
return offset, err
|
||||
}
|
||||
|
||||
// Remember any possible tls.Enum values encountered in case they are selectors.
|
||||
if structType.Field(i).Type.Kind() == enumType.Kind() {
|
||||
enums[structType.Field(i).Name] = v.Field(i).Uint()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Now we have seen all fields in the structure, check that all select(Enum) {..} selector
|
||||
// fields found a destination to put their data in.
|
||||
for selector, seen := range selectorSeen {
|
||||
if !seen {
|
||||
return offset, syntaxError{info.fieldName(), selector + ": unhandled value for selector"}
|
||||
}
|
||||
}
|
||||
return offset, nil
|
||||
case reflect.Array:
|
||||
datalen := v.Len()
|
||||
|
||||
if datalen > len(rest) {
|
||||
return offset, syntaxError{info.fieldName(), "truncated array"}
|
||||
}
|
||||
inner := rest[:datalen]
|
||||
offset += datalen
|
||||
if fieldType.Elem().Kind() != reflect.Uint8 {
|
||||
// Only byte/uint8 arrays are supported
|
||||
return offset, structuralError{info.fieldName(), "unsupported array type: " + v.Type().String()}
|
||||
}
|
||||
reflect.Copy(v, reflect.ValueOf(inner))
|
||||
return offset, nil
|
||||
|
||||
case reflect.Slice:
|
||||
sliceType := fieldType
|
||||
// Slices represent variable-length vectors, which are prefixed by a length field.
|
||||
// The fieldInfo indicates the size of that length field.
|
||||
varlen, err := readVarUint(rest, info)
|
||||
if err != nil {
|
||||
return offset, err
|
||||
}
|
||||
datalen := int(varlen)
|
||||
offset += int(info.count)
|
||||
rest = rest[info.count:]
|
||||
|
||||
if datalen > len(rest) {
|
||||
return offset, syntaxError{info.fieldName(), "truncated slice"}
|
||||
}
|
||||
inner := rest[:datalen]
|
||||
offset += datalen
|
||||
if fieldType.Elem().Kind() == reflect.Uint8 {
|
||||
// Fast version for []byte
|
||||
v.Set(reflect.MakeSlice(sliceType, datalen, datalen))
|
||||
reflect.Copy(v, reflect.ValueOf(inner))
|
||||
return offset, nil
|
||||
}
|
||||
|
||||
v.Set(reflect.MakeSlice(sliceType, 0, datalen))
|
||||
single := reflect.New(sliceType.Elem())
|
||||
for innerOffset := 0; innerOffset < len(inner); {
|
||||
var err error
|
||||
innerOffset, err = parseField(single.Elem(), inner, innerOffset, nil)
|
||||
if err != nil {
|
||||
return offset, err
|
||||
}
|
||||
v.Set(reflect.Append(v, single.Elem()))
|
||||
}
|
||||
return offset, nil
|
||||
|
||||
default:
|
||||
return offset, structuralError{info.fieldName(), fmt.Sprintf("unsupported type: %s of kind %s", fieldType, v.Kind())}
|
||||
}
|
||||
}
|
||||
|
||||
// Marshal returns the TLS encoding of val.
|
||||
func Marshal(val interface{}) ([]byte, error) {
|
||||
return MarshalWithParams(val, "")
|
||||
}
|
||||
|
||||
// MarshalWithParams returns the TLS encoding of val, and allows field
|
||||
// parameters to be specified for the top-level element. The form
|
||||
// of the params is the same as the field tags.
|
||||
func MarshalWithParams(val interface{}, params string) ([]byte, error) {
|
||||
info, err := fieldTagToFieldInfo(params, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out bytes.Buffer
|
||||
v := reflect.ValueOf(val)
|
||||
if err := marshalField(&out, v, info); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out.Bytes(), err
|
||||
}
|
||||
|
||||
func marshalField(out *bytes.Buffer, v reflect.Value, info *fieldInfo) error {
|
||||
var prefix string
|
||||
if info != nil && len(info.name) > 0 {
|
||||
prefix = info.name + ": "
|
||||
}
|
||||
fieldType := v.Type()
|
||||
// First look for known fixed types.
|
||||
switch fieldType {
|
||||
case uint8Type:
|
||||
out.WriteByte(byte(v.Uint()))
|
||||
return nil
|
||||
case uint16Type:
|
||||
scratch := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(scratch, uint16(v.Uint()))
|
||||
out.Write(scratch)
|
||||
return nil
|
||||
case uint24Type:
|
||||
i := v.Uint()
|
||||
if i > 0xffffff {
|
||||
return structuralError{info.fieldName(), fmt.Sprintf("uint24 overflow %d", i)}
|
||||
}
|
||||
scratch := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(scratch, uint32(i))
|
||||
out.Write(scratch[1:])
|
||||
return nil
|
||||
case uint32Type:
|
||||
scratch := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(scratch, uint32(v.Uint()))
|
||||
out.Write(scratch)
|
||||
return nil
|
||||
case uint64Type:
|
||||
scratch := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(scratch, uint64(v.Uint()))
|
||||
out.Write(scratch)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Now deal with user-defined types.
|
||||
switch v.Kind() {
|
||||
case enumType.Kind():
|
||||
i := v.Uint()
|
||||
if info == nil {
|
||||
return structuralError{info.fieldName(), "enum field tag missing"}
|
||||
}
|
||||
if err := info.check(i, prefix); err != nil {
|
||||
return err
|
||||
}
|
||||
scratch := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(scratch, uint64(i))
|
||||
out.Write(scratch[(8 - info.count):])
|
||||
return nil
|
||||
case reflect.Struct:
|
||||
structType := fieldType
|
||||
enums := make(map[string]uint64) // Values of any Enum fields
|
||||
// The comment parseField() describes the mapping of the TLS select(Enum) {..} construct;
|
||||
// here we have selector and source (rather than destination) fields.
|
||||
|
||||
// Track which selector names we've seen (in the source field tags), and whether a source
|
||||
// value for that selector has been processed.
|
||||
selectorSeen := make(map[string]bool)
|
||||
for i := 0; i < structType.NumField(); i++ {
|
||||
// Find information about this field.
|
||||
tag := structType.Field(i).Tag.Get("tls")
|
||||
fieldInfo, err := fieldTagToFieldInfo(tag, structType.Field(i).Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
source := v.Field(i)
|
||||
if fieldInfo.selector != "" {
|
||||
// This field is a possible source for a select(Enum) {..}. First check
|
||||
// the selector field name has been seen.
|
||||
choice, ok := enums[fieldInfo.selector]
|
||||
if !ok {
|
||||
return structuralError{fieldInfo.name, "selector not seen: " + fieldInfo.selector}
|
||||
}
|
||||
if structType.Field(i).Type.Kind() != reflect.Ptr {
|
||||
return structuralError{fieldInfo.name, "choice field not a pointer type"}
|
||||
}
|
||||
// Is this the first mention of the selector field name? If so, remember it.
|
||||
seen, ok := selectorSeen[fieldInfo.selector]
|
||||
if !ok {
|
||||
selectorSeen[fieldInfo.selector] = false
|
||||
}
|
||||
if choice != fieldInfo.val {
|
||||
// This source was not chosen; police that it should be nil.
|
||||
if v.Field(i).Pointer() != uintptr(0) {
|
||||
return structuralError{fieldInfo.name, "unchosen field is non-nil"}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if seen {
|
||||
// We already saw a different source field generate the value for this
|
||||
// selector value, which indicates a badly annotated structure.
|
||||
return structuralError{fieldInfo.name, "duplicate selector value for " + fieldInfo.selector}
|
||||
}
|
||||
selectorSeen[fieldInfo.selector] = true
|
||||
if v.Field(i).Pointer() == uintptr(0) {
|
||||
return structuralError{fieldInfo.name, "chosen field is nil"}
|
||||
}
|
||||
// Marshal from the pointed-to source object.
|
||||
source = v.Field(i).Elem()
|
||||
}
|
||||
|
||||
var fieldData bytes.Buffer
|
||||
if err := marshalField(&fieldData, source, fieldInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
out.Write(fieldData.Bytes())
|
||||
|
||||
// Remember any tls.Enum values encountered in case they are selectors.
|
||||
if structType.Field(i).Type.Kind() == enumType.Kind() {
|
||||
enums[structType.Field(i).Name] = v.Field(i).Uint()
|
||||
}
|
||||
}
|
||||
// Now we have seen all fields in the structure, check that all select(Enum) {..} selector
|
||||
// fields found a source field get get their data from.
|
||||
for selector, seen := range selectorSeen {
|
||||
if !seen {
|
||||
return syntaxError{info.fieldName(), selector + ": unhandled value for selector"}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
case reflect.Array:
|
||||
datalen := v.Len()
|
||||
arrayType := fieldType
|
||||
if arrayType.Elem().Kind() != reflect.Uint8 {
|
||||
// Only byte/uint8 arrays are supported
|
||||
return structuralError{info.fieldName(), "unsupported array type"}
|
||||
}
|
||||
bytes := make([]byte, datalen)
|
||||
for i := 0; i < datalen; i++ {
|
||||
bytes[i] = uint8(v.Index(i).Uint())
|
||||
}
|
||||
_, err := out.Write(bytes)
|
||||
return err
|
||||
|
||||
case reflect.Slice:
|
||||
if info == nil {
|
||||
return structuralError{info.fieldName(), "slice field tag missing"}
|
||||
}
|
||||
|
||||
sliceType := fieldType
|
||||
if sliceType.Elem().Kind() == reflect.Uint8 {
|
||||
// Fast version for []byte: first write the length as info.count bytes.
|
||||
datalen := v.Len()
|
||||
scratch := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(scratch, uint64(datalen))
|
||||
out.Write(scratch[(8 - info.count):])
|
||||
|
||||
if err := info.check(uint64(datalen), prefix); err != nil {
|
||||
return err
|
||||
}
|
||||
// Then just write the data.
|
||||
bytes := make([]byte, datalen)
|
||||
for i := 0; i < datalen; i++ {
|
||||
bytes[i] = uint8(v.Index(i).Uint())
|
||||
}
|
||||
_, err := out.Write(bytes)
|
||||
return err
|
||||
}
|
||||
// General version: use a separate Buffer to write the slice entries into.
|
||||
var innerBuf bytes.Buffer
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if err := marshalField(&innerBuf, v.Index(i), nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Now insert (and check) the size.
|
||||
size := uint64(innerBuf.Len())
|
||||
if err := info.check(size, prefix); err != nil {
|
||||
return err
|
||||
}
|
||||
scratch := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(scratch, size)
|
||||
out.Write(scratch[(8 - info.count):])
|
||||
|
||||
// Then copy the data.
|
||||
_, err := out.Write(innerBuf.Bytes())
|
||||
return err
|
||||
|
||||
default:
|
||||
return structuralError{info.fieldName(), fmt.Sprintf("unsupported type: %s of kind %s", fieldType, v.Kind())}
|
||||
}
|
||||
}
|
355
vendor/github.com/google/certificate-transparency-go/tls/tls_test.go
generated
vendored
Normal file
355
vendor/github.com/google/certificate-transparency-go/tls/tls_test.go
generated
vendored
Normal file
@ -0,0 +1,355 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testStruct struct {
|
||||
Data []byte `tls:"minlen:2,maxlen:4"`
|
||||
IntVal uint16
|
||||
Other [4]byte
|
||||
Enum Enum `tls:"size:2"`
|
||||
}
|
||||
|
||||
type testVariant struct {
|
||||
Which Enum `tls:"size:1"`
|
||||
Val16 *uint16 `tls:"selector:Which,val:0"`
|
||||
Val32 *uint32 `tls:"selector:Which,val:1"`
|
||||
}
|
||||
|
||||
type testTwoVariants struct {
|
||||
Which Enum `tls:"size:1"`
|
||||
Val16 *uint16 `tls:"selector:Which,val:0"`
|
||||
Val32 *uint32 `tls:"selector:Which,val:1"`
|
||||
Second Enum `tls:"size:1"`
|
||||
Second16 *uint16 `tls:"selector:Second,val:0"`
|
||||
Second32 *uint32 `tls:"selector:Second,val:1"`
|
||||
}
|
||||
|
||||
// Check that library users can define their own Enum types.
|
||||
type aliasEnum Enum
|
||||
type testAliasEnum struct {
|
||||
Val aliasEnum `tls:"size:1"`
|
||||
Val16 *uint16 `tls:"selector:Val,val:1"`
|
||||
Val32 *uint32 `tls:"selector:Val,val:2"`
|
||||
}
|
||||
|
||||
type testNonByteSlice struct {
|
||||
Vals []uint16 `tls:"minlen:2,maxlen:6"`
|
||||
}
|
||||
|
||||
type testSliceOfStructs struct {
|
||||
Vals []testVariant `tls:"minlen:0,maxlen:100"`
|
||||
}
|
||||
|
||||
type testInnerType struct {
|
||||
Val []byte `tls:"minlen:0,maxlen:65535"`
|
||||
}
|
||||
|
||||
type testSliceOfSlices struct {
|
||||
Inners []testInnerType `tls:"minlen:0,maxlen:65535"`
|
||||
}
|
||||
|
||||
func TestMarshalUnmarshalRoundTrip(t *testing.T) {
|
||||
thing := testStruct{Data: []byte{0x01, 0x02, 0x03}, IntVal: 42, Other: [4]byte{1, 2, 3, 4}, Enum: 17}
|
||||
data, err := Marshal(thing)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to Marshal(%+v): %s", thing, err.Error())
|
||||
}
|
||||
var other testStruct
|
||||
rest, err := Unmarshal(data, &other)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to Unmarshal(%s)", hex.EncodeToString(data))
|
||||
}
|
||||
if len(rest) > 0 {
|
||||
t.Errorf("Data left over after Unmarshal(%s): %s", hex.EncodeToString(data), hex.EncodeToString(rest))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldTagToFieldInfo(t *testing.T) {
|
||||
var tests = []struct {
|
||||
tag string
|
||||
want *fieldInfo
|
||||
errstr string
|
||||
}{
|
||||
{"", nil, ""},
|
||||
{"bogus", nil, ""},
|
||||
{"also,bogus", nil, ""},
|
||||
{"also,bogus:99", nil, ""},
|
||||
{"maxval:1xyz", nil, ""},
|
||||
{"maxval:1", &fieldInfo{count: 1, countSet: true}, ""},
|
||||
{"maxval:255", &fieldInfo{count: 1, countSet: true}, ""},
|
||||
{"maxval:256", &fieldInfo{count: 2, countSet: true}, ""},
|
||||
{"maxval:65535", &fieldInfo{count: 2, countSet: true}, ""},
|
||||
{"maxval:65536", &fieldInfo{count: 3, countSet: true}, ""},
|
||||
{"maxval:16777215", &fieldInfo{count: 3, countSet: true}, ""},
|
||||
{"maxval:16777216", &fieldInfo{count: 4, countSet: true}, ""},
|
||||
{"maxval:16777216", &fieldInfo{count: 4, countSet: true}, ""},
|
||||
{"maxval:4294967295", &fieldInfo{count: 4, countSet: true}, ""},
|
||||
{"maxval:4294967296", &fieldInfo{count: 5, countSet: true}, ""},
|
||||
{"maxval:1099511627775", &fieldInfo{count: 5, countSet: true}, ""},
|
||||
{"maxval:1099511627776", &fieldInfo{count: 6, countSet: true}, ""},
|
||||
{"maxval:281474976710655", &fieldInfo{count: 6, countSet: true}, ""},
|
||||
{"maxval:281474976710656", &fieldInfo{count: 7, countSet: true}, ""},
|
||||
{"maxval:72057594037927935", &fieldInfo{count: 7, countSet: true}, ""},
|
||||
{"maxval:72057594037927936", &fieldInfo{count: 8, countSet: true}, ""},
|
||||
{"minlen:1x", nil, ""},
|
||||
{"maxlen:1x", nil, ""},
|
||||
{"maxlen:1", &fieldInfo{count: 1, countSet: true, maxlen: 1}, ""},
|
||||
{"maxlen:255", &fieldInfo{count: 1, countSet: true, maxlen: 255}, ""},
|
||||
{"maxlen:65535", &fieldInfo{count: 2, countSet: true, maxlen: 65535}, ""},
|
||||
{"minlen:65530,maxlen:65535", &fieldInfo{count: 2, countSet: true, minlen: 65530, maxlen: 65535}, ""},
|
||||
{"maxlen:65535,minlen:65530", &fieldInfo{count: 2, countSet: true, minlen: 65530, maxlen: 65535}, ""},
|
||||
{"minlen:65536,maxlen:65535", nil, "inverted"},
|
||||
{"maxlen:16777215", &fieldInfo{count: 3, countSet: true, maxlen: 16777215}, ""},
|
||||
{"maxlen:281474976710655", &fieldInfo{count: 6, countSet: true, maxlen: 281474976710655}, ""},
|
||||
{"maxlen:72057594037927936", &fieldInfo{count: 8, countSet: true, maxlen: 72057594037927936}, ""},
|
||||
{"size:0", nil, "unknown size"},
|
||||
{"size:1", &fieldInfo{count: 1, countSet: true}, ""},
|
||||
{"size:2", &fieldInfo{count: 2, countSet: true}, ""},
|
||||
{"size:3", &fieldInfo{count: 3, countSet: true}, ""},
|
||||
{"size:4", &fieldInfo{count: 4, countSet: true}, ""},
|
||||
{"size:5", &fieldInfo{count: 5, countSet: true}, ""},
|
||||
{"size:6", &fieldInfo{count: 6, countSet: true}, ""},
|
||||
{"size:7", &fieldInfo{count: 7, countSet: true}, ""},
|
||||
{"size:8", &fieldInfo{count: 8, countSet: true}, ""},
|
||||
{"size:9", nil, "too large"},
|
||||
{"size:1x", nil, ""},
|
||||
{"size:1,val:9", nil, "selector value"},
|
||||
{"selector:Bob,val:x9", &fieldInfo{selector: "Bob"}, ""},
|
||||
{"selector:Fred,val:1", &fieldInfo{selector: "Fred", val: 1}, ""},
|
||||
{"val:9,selector:Fred,val:1", &fieldInfo{selector: "Fred", val: 1}, ""},
|
||||
}
|
||||
for _, test := range tests {
|
||||
got, err := fieldTagToFieldInfo(test.tag, "")
|
||||
if test.errstr != "" {
|
||||
if err == nil {
|
||||
t.Errorf("fieldTagToFieldInfo('%v')=%+v,nil; want error %q", test.tag, got, test.errstr)
|
||||
} else if !strings.Contains(err.Error(), test.errstr) {
|
||||
t.Errorf("fieldTagToFieldInfo('%v')=nil,%q; want error %q", test.tag, err.Error(), test.errstr)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("fieldTagToFieldInfo('%v')=nil,%q; want %+v", test.tag, err.Error(), test.want)
|
||||
} else if !reflect.DeepEqual(got, test.want) {
|
||||
t.Errorf("fieldTagToFieldInfo('%v')=%+v,nil; want %+v", test.tag, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Can't take the address of a numeric constant so use helper functions
|
||||
func newByte(n byte) *byte { return &n }
|
||||
func newUint8(n uint8) *uint8 { return &n }
|
||||
func newUint16(n uint16) *uint16 { return &n }
|
||||
func newUint24(n Uint24) *Uint24 { return &n }
|
||||
func newUint32(n uint32) *uint32 { return &n }
|
||||
func newUint64(n uint64) *uint64 { return &n }
|
||||
func newInt16(n int16) *int16 { return &n }
|
||||
func newEnum(n Enum) *Enum { return &n }
|
||||
|
||||
func TestUnmarshalMarshalWithParamsRoundTrip(t *testing.T) {
|
||||
var tests = []struct {
|
||||
data string // hex encoded
|
||||
params string
|
||||
item interface{}
|
||||
}{
|
||||
{"00", "", newUint8(0)},
|
||||
{"03", "", newByte(3)},
|
||||
{"0101", "", newUint16(0x0101)},
|
||||
{"010203", "", newUint24(0x010203)},
|
||||
{"000000", "", newUint24(0x00)},
|
||||
{"00000009", "", newUint32(0x09)},
|
||||
{"0000000901020304", "", newUint64(0x0901020304)},
|
||||
{"030405", "", &[3]byte{3, 4, 5}},
|
||||
{"03", "", &[1]byte{3}},
|
||||
{"0001", "size:2", newEnum(1)},
|
||||
{"0100000001", "size:5", newEnum(0x100000001)},
|
||||
{"12", "maxval:18", newEnum(18)},
|
||||
// Note that maxval is just used to give enum size; it's not policed
|
||||
{"20", "maxval:18", newEnum(32)},
|
||||
{"020a0b", "minlen:1,maxlen:5", &[]byte{0xa, 0xb}},
|
||||
{"020a0b0101010203040011", "", &testStruct{Data: []byte{0xa, 0xb}, IntVal: 0x101, Other: [4]byte{1, 2, 3, 4}, Enum: 17}},
|
||||
{"000102", "", &testVariant{Which: 0, Val16: newUint16(0x0102)}},
|
||||
{"0101020304", "", &testVariant{Which: 1, Val32: newUint32(0x01020304)}},
|
||||
{"0001020104030201", "", &testTwoVariants{Which: 0, Val16: newUint16(0x0102), Second: 1, Second32: newUint32(0x04030201)}},
|
||||
{"06010102020303", "", &testNonByteSlice{Vals: []uint16{0x101, 0x202, 0x303}}},
|
||||
{"00", "", &testSliceOfStructs{Vals: []testVariant{}}},
|
||||
{"080001020101020304", "",
|
||||
&testSliceOfStructs{
|
||||
Vals: []testVariant{
|
||||
{Which: 0, Val16: newUint16(0x0102)},
|
||||
{Which: 1, Val32: newUint32(0x01020304)},
|
||||
},
|
||||
},
|
||||
},
|
||||
{"000a00030102030003040506", "",
|
||||
&testSliceOfSlices{
|
||||
Inners: []testInnerType{
|
||||
{Val: []byte{1, 2, 3}},
|
||||
{Val: []byte{4, 5, 6}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{"011011", "", &testAliasEnum{Val: 1, Val16: newUint16(0x1011)}},
|
||||
{"0403", "", &SignatureAndHashAlgorithm{Hash: SHA256, Signature: ECDSA}},
|
||||
{"04030003010203", "",
|
||||
&DigitallySigned{
|
||||
Algorithm: SignatureAndHashAlgorithm{Hash: SHA256, Signature: ECDSA},
|
||||
Signature: []byte{1, 2, 3},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
inVal := reflect.ValueOf(test.item).Elem()
|
||||
pv := reflect.New(reflect.TypeOf(test.item).Elem())
|
||||
val := pv.Interface()
|
||||
inData, _ := hex.DecodeString(test.data)
|
||||
if _, err := UnmarshalWithParams(inData, val, test.params); err != nil {
|
||||
t.Errorf("Unmarshal(%s)=nil,%q; want %+v", test.data, err.Error(), inVal)
|
||||
} else if !reflect.DeepEqual(val, test.item) {
|
||||
t.Errorf("Unmarshal(%s)=%+v,nil; want %+v", test.data, reflect.ValueOf(val).Elem(), inVal)
|
||||
}
|
||||
|
||||
if data, err := MarshalWithParams(inVal.Interface(), test.params); err != nil {
|
||||
t.Errorf("Marshal(%+v)=nil,%q; want %s", inVal, err.Error(), test.data)
|
||||
} else if !bytes.Equal(data, inData) {
|
||||
t.Errorf("Marshal(%+v)=%s,nil; want %s", inVal, hex.EncodeToString(data), test.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type testInvalidFieldTag struct {
|
||||
Data []byte `tls:"minlen:3,maxlen:2"`
|
||||
}
|
||||
|
||||
type testDuplicateSelectorVal struct {
|
||||
Which Enum `tls:"size:1"`
|
||||
Val *uint16 `tls:"selector:Which,val:0"`
|
||||
DupVal *uint32 `tls:"selector:Which"` // implicit val:0
|
||||
}
|
||||
|
||||
type testMissingSelector struct {
|
||||
Val *uint16 `tls:"selector:Missing,val:0"`
|
||||
}
|
||||
|
||||
type testChoiceNotPointer struct {
|
||||
Which Enum `tls:"size:1"`
|
||||
Val uint16 `tls:"selector:Which,val:0"`
|
||||
}
|
||||
|
||||
type nonEnumAlias uint16
|
||||
|
||||
func newNonEnumAlias(n nonEnumAlias) *nonEnumAlias { return &n }
|
||||
|
||||
func TestUnmarshalWithParamsFailures(t *testing.T) {
|
||||
var tests = []struct {
|
||||
data string // hex encoded
|
||||
params string
|
||||
item interface{}
|
||||
errstr string
|
||||
}{
|
||||
{"", "", newUint8(0), "truncated"},
|
||||
{"0x01", "", newUint16(0x0101), "truncated"},
|
||||
{"0103", "", newUint24(0x010203), "truncated"},
|
||||
{"00", "", newUint24(0x00), "truncated"},
|
||||
{"000009", "", newUint32(0x09), "truncated"},
|
||||
{"00000901020304", "", newUint64(0x0901020304), "truncated"},
|
||||
{"0102", "", newInt16(0x0102), "unsupported type"}, // TLS encoding only supports unsigned integers
|
||||
{"0607", "", &[3]byte{6, 7, 8}, "truncated array"},
|
||||
{"01010202", "", &[3]uint16{0x101, 0x202}, "unsupported array"},
|
||||
{"01", "", newEnum(1), "no field size"},
|
||||
{"00", "size:2", newEnum(0), "truncated"},
|
||||
{"00", "size:9", newEnum(0), "too large"},
|
||||
{"020a0b", "minlen:4,maxlen:8", &[]byte{0x0a, 0x0b}, "too small"},
|
||||
{"040a0b0c0d", "minlen:1,maxlen:3", &[]byte{0x0a, 0x0b, 0x0c, 0x0d}, "too large"},
|
||||
{"020a0b", "minlen:8,maxlen:6", &[]byte{0x0a, 0x0b}, "inverted"},
|
||||
{"020a", "minlen:0,maxlen:6", &[]byte{0x0a, 0x0b}, "truncated"},
|
||||
{"02", "minlen:0,maxlen:6", &[]byte{0x0a, 0x0b}, "truncated"},
|
||||
{"0001", "minlen:0,maxlen:256", &[]byte{0x0a, 0x0b}, "truncated"},
|
||||
{"020a", "minlen:0", &[]byte{0x0a, 0x0b}, "unknown size"},
|
||||
{"020a", "", &[]byte{0x0a, 0x0b}, "no field size information"},
|
||||
{"020a0b", "", &testInvalidFieldTag{}, "range inverted"},
|
||||
{"020a0b01010102030400", "",
|
||||
&testStruct{Data: []byte{0xa, 0xb}, IntVal: 0x101, Other: [4]byte{1, 2, 3, 4}, Enum: 17}, "truncated"},
|
||||
{"010102", "", &testVariant{Which: 1, Val32: newUint32(0x01020304)}, "truncated"},
|
||||
{"092122", "", &testVariant{Which: 0, Val16: newUint16(0x2122)}, "unhandled value for selector"},
|
||||
{"0001020304", "", &testDuplicateSelectorVal{Which: 0, Val: newUint16(0x0102)}, "duplicate selector value"},
|
||||
{"0102", "", &testMissingSelector{Val: newUint16(1)}, "selector not seen"},
|
||||
{"000007", "", &testChoiceNotPointer{Which: 0, Val: 7}, "choice field not a pointer type"},
|
||||
{"05010102020303", "", &testNonByteSlice{Vals: []uint16{0x101, 0x202, 0x303}}, "truncated"},
|
||||
{"0101", "size:2", newNonEnumAlias(0x0102), "unsupported type"},
|
||||
{"0403010203", "",
|
||||
&DigitallySigned{
|
||||
Algorithm: SignatureAndHashAlgorithm{Hash: SHA256, Signature: ECDSA},
|
||||
Signature: []byte{1, 2, 3}}, "truncated"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
pv := reflect.New(reflect.TypeOf(test.item).Elem())
|
||||
val := pv.Interface()
|
||||
in, _ := hex.DecodeString(test.data)
|
||||
if _, err := UnmarshalWithParams(in, val, test.params); err == nil {
|
||||
t.Errorf("Unmarshal(%s)=%+v,nil; want error %q", test.data, reflect.ValueOf(val).Elem(), test.errstr)
|
||||
} else if !strings.Contains(err.Error(), test.errstr) {
|
||||
t.Errorf("Unmarshal(%s)=nil,%q; want error %q", test.data, err.Error(), test.errstr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalWithParamsFailures(t *testing.T) {
|
||||
var tests = []struct {
|
||||
item interface{}
|
||||
params string
|
||||
errstr string
|
||||
}{
|
||||
{Uint24(0x1000000), "", "overflow"},
|
||||
{int16(0x0102), "", "unsupported type"}, // All TLS ints are unsigned
|
||||
{Enum(1), "", "field tag missing"},
|
||||
{Enum(256), "size:1", "too large"},
|
||||
{Enum(256), "maxval:255", "too large"},
|
||||
{Enum(2), "", "field tag missing"},
|
||||
{Enum(256), "size:9", "too large"},
|
||||
{[]byte{0xa, 0xb, 0xc, 0xd}, "minlen:1,maxlen:3", "too large"},
|
||||
{[]byte{0xa, 0xb, 0xc, 0xd}, "minlen:6,maxlen:13", "too small"},
|
||||
{[]byte{0xa, 0xb, 0xc, 0xd}, "minlen:6,maxlen:3", "inverted"},
|
||||
{[]byte{0xa, 0xb, 0xc, 0xd}, "minlen:6", "unknown size"},
|
||||
{[]byte{0xa, 0xb, 0xc, 0xd}, "", "field tag missing"},
|
||||
{[3]uint16{0x101, 0x202}, "", "unsupported array"},
|
||||
{testInvalidFieldTag{}, "", "inverted"},
|
||||
{testStruct{Data: []byte{0xa}, IntVal: 0x101, Other: [4]byte{1, 2, 3, 4}, Enum: 17}, "", "too small"},
|
||||
{testVariant{Which: 0, Val32: newUint32(0x01020304)}, "", "chosen field is nil"},
|
||||
{testVariant{Which: 0, Val16: newUint16(11), Val32: newUint32(0x01020304)}, "", "unchosen field is non-nil"},
|
||||
{testVariant{Which: 3}, "", "unhandled value for selector"},
|
||||
{testMissingSelector{Val: newUint16(1)}, "", "selector not seen"},
|
||||
{testChoiceNotPointer{Which: 0, Val: 7}, "", "choice field not a pointer"},
|
||||
{testDuplicateSelectorVal{Which: 0, Val: newUint16(1)}, "", "duplicate selector value"},
|
||||
{testNonByteSlice{Vals: []uint16{1, 2, 3, 4}}, "", "too large"},
|
||||
{testSliceOfStructs{[]testVariant{{Which: 3}}}, "", "unhandled value for selector"},
|
||||
{nonEnumAlias(0x0102), "", "unsupported type"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
if data, err := MarshalWithParams(test.item, test.params); err == nil {
|
||||
t.Errorf("Marshal(%+v)=%x,nil; want error %q", test.item, data, test.errstr)
|
||||
} else if !strings.Contains(err.Error(), test.errstr) {
|
||||
t.Errorf("Marshal(%+v)=nil,%q; want error %q", test.item, err.Error(), test.errstr)
|
||||
}
|
||||
}
|
||||
}
|
96
vendor/github.com/google/certificate-transparency-go/tls/types.go
generated
vendored
Normal file
96
vendor/github.com/google/certificate-transparency-go/tls/types.go
generated
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tls
|
||||
|
||||
import "fmt"
|
||||
|
||||
// DigitallySigned gives information about a signature, including the algorithm used
|
||||
// and the signature value. Defined in RFC 5246 s4.7.
|
||||
type DigitallySigned struct {
|
||||
Algorithm SignatureAndHashAlgorithm
|
||||
Signature []byte `tls:"minlen:0,maxlen:65535"`
|
||||
}
|
||||
|
||||
func (d DigitallySigned) String() string {
|
||||
return fmt.Sprintf("Signature: HashAlgo=%v SignAlgo=%v Value=%x", d.Algorithm.Hash, d.Algorithm.Signature, d.Signature)
|
||||
}
|
||||
|
||||
// SignatureAndHashAlgorithm gives information about the algorithms used for a
|
||||
// signature. Defined in RFC 5246 s7.4.1.4.1.
|
||||
type SignatureAndHashAlgorithm struct {
|
||||
Hash HashAlgorithm `tls:"maxval:255"`
|
||||
Signature SignatureAlgorithm `tls:"maxval:255"`
|
||||
}
|
||||
|
||||
// HashAlgorithm enum from RFC 5246 s7.4.1.4.1.
|
||||
type HashAlgorithm Enum
|
||||
|
||||
// HashAlgorithm constants from RFC 5246 s7.4.1.4.1.
|
||||
const (
|
||||
None HashAlgorithm = 0
|
||||
MD5 HashAlgorithm = 1
|
||||
SHA1 HashAlgorithm = 2
|
||||
SHA224 HashAlgorithm = 3
|
||||
SHA256 HashAlgorithm = 4
|
||||
SHA384 HashAlgorithm = 5
|
||||
SHA512 HashAlgorithm = 6
|
||||
)
|
||||
|
||||
func (h HashAlgorithm) String() string {
|
||||
switch h {
|
||||
case None:
|
||||
return "None"
|
||||
case MD5:
|
||||
return "MD5"
|
||||
case SHA1:
|
||||
return "SHA1"
|
||||
case SHA224:
|
||||
return "SHA224"
|
||||
case SHA256:
|
||||
return "SHA256"
|
||||
case SHA384:
|
||||
return "SHA384"
|
||||
case SHA512:
|
||||
return "SHA512"
|
||||
default:
|
||||
return fmt.Sprintf("UNKNOWN(%d)", h)
|
||||
}
|
||||
}
|
||||
|
||||
// SignatureAlgorithm enum from RFC 5246 s7.4.1.4.1.
|
||||
type SignatureAlgorithm Enum
|
||||
|
||||
// SignatureAlgorithm constants from RFC 5246 s7.4.1.4.1.
|
||||
const (
|
||||
Anonymous SignatureAlgorithm = 0
|
||||
RSA SignatureAlgorithm = 1
|
||||
DSA SignatureAlgorithm = 2
|
||||
ECDSA SignatureAlgorithm = 3
|
||||
)
|
||||
|
||||
func (s SignatureAlgorithm) String() string {
|
||||
switch s {
|
||||
case Anonymous:
|
||||
return "Anonymous"
|
||||
case RSA:
|
||||
return "RSA"
|
||||
case DSA:
|
||||
return "DSA"
|
||||
case ECDSA:
|
||||
return "ECDSA"
|
||||
default:
|
||||
return fmt.Sprintf("UNKNOWN(%d)", s)
|
||||
}
|
||||
}
|
77
vendor/github.com/google/certificate-transparency-go/tls/types_test.go
generated
vendored
Normal file
77
vendor/github.com/google/certificate-transparency-go/tls/types_test.go
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package tls
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestHashAlgorithmString(t *testing.T) {
|
||||
var tests = []struct {
|
||||
algo HashAlgorithm
|
||||
want string
|
||||
}{
|
||||
{None, "None"},
|
||||
{MD5, "MD5"},
|
||||
{SHA1, "SHA1"},
|
||||
{SHA224, "SHA224"},
|
||||
{SHA256, "SHA256"},
|
||||
{SHA384, "SHA384"},
|
||||
{SHA512, "SHA512"},
|
||||
{99, "UNKNOWN(99)"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
if got := test.algo.String(); got != test.want {
|
||||
t.Errorf("%v.String()=%q; want %q", test.algo, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignatureAlgorithmString(t *testing.T) {
|
||||
var tests = []struct {
|
||||
algo SignatureAlgorithm
|
||||
want string
|
||||
}{
|
||||
{Anonymous, "Anonymous"},
|
||||
{RSA, "RSA"},
|
||||
{DSA, "DSA"},
|
||||
{ECDSA, "ECDSA"},
|
||||
{99, "UNKNOWN(99)"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
if got := test.algo.String(); got != test.want {
|
||||
t.Errorf("%v.String()=%q; want %q", test.algo, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDigitallySignedString(t *testing.T) {
|
||||
var tests = []struct {
|
||||
ds DigitallySigned
|
||||
want string
|
||||
}{
|
||||
{
|
||||
ds: DigitallySigned{Algorithm: SignatureAndHashAlgorithm{Hash: SHA1, Signature: RSA}, Signature: []byte{0x01, 0x02}},
|
||||
want: "Signature: HashAlgo=SHA1 SignAlgo=RSA Value=0102",
|
||||
},
|
||||
{
|
||||
ds: DigitallySigned{Algorithm: SignatureAndHashAlgorithm{Hash: 99, Signature: 99}, Signature: []byte{0x03, 0x04}},
|
||||
want: "Signature: HashAlgo=UNKNOWN(99) SignAlgo=UNKNOWN(99) Value=0304",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
if got := test.ds.String(); got != test.want {
|
||||
t.Errorf("%v.String()=%q; want %q", test.ds, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
460
vendor/github.com/google/certificate-transparency-go/types.go
generated
vendored
Normal file
460
vendor/github.com/google/certificate-transparency-go/types.go
generated
vendored
Normal file
@ -0,0 +1,460 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package ct holds core types and utilities for Certificate Transparency.
|
||||
package ct
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/certificate-transparency-go/tls"
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// The following structures represent those outlined in RFC6962; any section
|
||||
// numbers mentioned refer to that RFC.
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// LogEntryType represents the LogEntryType enum from section 3.1:
|
||||
// enum { x509_entry(0), precert_entry(1), (65535) } LogEntryType;
|
||||
type LogEntryType tls.Enum // tls:"maxval:65535"
|
||||
|
||||
// LogEntryType constants from section 3.1.
|
||||
const (
|
||||
X509LogEntryType LogEntryType = 0
|
||||
PrecertLogEntryType LogEntryType = 1
|
||||
XJSONLogEntryType LogEntryType = 0x8000 // Experimental. Don't rely on this!
|
||||
)
|
||||
|
||||
func (e LogEntryType) String() string {
|
||||
switch e {
|
||||
case X509LogEntryType:
|
||||
return "X509LogEntryType"
|
||||
case PrecertLogEntryType:
|
||||
return "PrecertLogEntryType"
|
||||
case XJSONLogEntryType:
|
||||
return "XJSONLogEntryType"
|
||||
default:
|
||||
return fmt.Sprintf("UnknownEntryType(%d)", e)
|
||||
}
|
||||
}
|
||||
|
||||
// MerkleLeafType represents the MerkleLeafType enum from section 3.4:
|
||||
// enum { timestamped_entry(0), (255) } MerkleLeafType;
|
||||
type MerkleLeafType tls.Enum // tls:"maxval:255"
|
||||
|
||||
// TimestampedEntryLeafType is the only defined MerkleLeafType constant from section 3.4.
|
||||
const TimestampedEntryLeafType MerkleLeafType = 0 // Entry type for an SCT
|
||||
|
||||
func (m MerkleLeafType) String() string {
|
||||
switch m {
|
||||
case TimestampedEntryLeafType:
|
||||
return "TimestampedEntryLeafType"
|
||||
default:
|
||||
return fmt.Sprintf("UnknownLeafType(%d)", m)
|
||||
}
|
||||
}
|
||||
|
||||
// Version represents the Version enum from section 3.2:
|
||||
// enum { v1(0), (255) } Version;
|
||||
type Version tls.Enum // tls:"maxval:255"
|
||||
|
||||
// CT Version constants from section 3.2.
|
||||
const (
|
||||
V1 Version = 0
|
||||
)
|
||||
|
||||
func (v Version) String() string {
|
||||
switch v {
|
||||
case V1:
|
||||
return "V1"
|
||||
default:
|
||||
return fmt.Sprintf("UnknownVersion(%d)", v)
|
||||
}
|
||||
}
|
||||
|
||||
// SignatureType differentiates STH signatures from SCT signatures, see section 3.2.
|
||||
// enum { certificate_timestamp(0), tree_hash(1), (255) } SignatureType;
|
||||
type SignatureType tls.Enum // tls:"maxval:255"
|
||||
|
||||
// SignatureType constants from section 3.2.
|
||||
const (
|
||||
CertificateTimestampSignatureType SignatureType = 0
|
||||
TreeHashSignatureType SignatureType = 1
|
||||
)
|
||||
|
||||
func (st SignatureType) String() string {
|
||||
switch st {
|
||||
case CertificateTimestampSignatureType:
|
||||
return "CertificateTimestamp"
|
||||
case TreeHashSignatureType:
|
||||
return "TreeHash"
|
||||
default:
|
||||
return fmt.Sprintf("UnknownSignatureType(%d)", st)
|
||||
}
|
||||
}
|
||||
|
||||
// ASN1Cert type for holding the raw DER bytes of an ASN.1 Certificate
|
||||
// (section 3.1).
|
||||
type ASN1Cert struct {
|
||||
Data []byte `tls:"minlen:1,maxlen:16777215"`
|
||||
}
|
||||
|
||||
// LogID holds the hash of the Log's public key (section 3.2).
|
||||
// TODO(pphaneuf): Users should be migrated to the one in the logid package.
|
||||
type LogID struct {
|
||||
KeyID [sha256.Size]byte
|
||||
}
|
||||
|
||||
// PreCert represents a Precertificate (section 3.2).
|
||||
type PreCert struct {
|
||||
IssuerKeyHash [sha256.Size]byte
|
||||
TBSCertificate []byte `tls:"minlen:1,maxlen:16777215"` // DER-encoded TBSCertificate
|
||||
}
|
||||
|
||||
// CTExtensions is a representation of the raw bytes of any CtExtension
|
||||
// structure (see section 3.2).
|
||||
// nolint: golint
|
||||
type CTExtensions []byte // tls:"minlen:0,maxlen:65535"`
|
||||
|
||||
// MerkleTreeNode represents an internal node in the CT tree.
|
||||
type MerkleTreeNode []byte
|
||||
|
||||
// ConsistencyProof represents a CT consistency proof (see sections 2.1.2 and
|
||||
// 4.4).
|
||||
type ConsistencyProof []MerkleTreeNode
|
||||
|
||||
// AuditPath represents a CT inclusion proof (see sections 2.1.1 and 4.5).
|
||||
type AuditPath []MerkleTreeNode
|
||||
|
||||
// LeafInput represents a serialized MerkleTreeLeaf structure.
|
||||
type LeafInput []byte
|
||||
|
||||
// DigitallySigned is a local alias for tls.DigitallySigned so that we can
|
||||
// attach a MarshalJSON method.
|
||||
type DigitallySigned tls.DigitallySigned
|
||||
|
||||
// FromBase64String populates the DigitallySigned structure from the base64 data passed in.
|
||||
// Returns an error if the base64 data is invalid.
|
||||
func (d *DigitallySigned) FromBase64String(b64 string) error {
|
||||
raw, err := base64.StdEncoding.DecodeString(b64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unbase64 DigitallySigned: %v", err)
|
||||
}
|
||||
var ds tls.DigitallySigned
|
||||
if rest, err := tls.Unmarshal(raw, &ds); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal DigitallySigned: %v", err)
|
||||
} else if len(rest) > 0 {
|
||||
return fmt.Errorf("trailing data (%d bytes) after DigitallySigned", len(rest))
|
||||
}
|
||||
*d = DigitallySigned(ds)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Base64String returns the base64 representation of the DigitallySigned struct.
|
||||
func (d DigitallySigned) Base64String() (string, error) {
|
||||
b, err := tls.Marshal(d)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(b), nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaller interface.
|
||||
func (d DigitallySigned) MarshalJSON() ([]byte, error) {
|
||||
b64, err := d.Base64String()
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
return []byte(`"` + b64 + `"`), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (d *DigitallySigned) UnmarshalJSON(b []byte) error {
|
||||
var content string
|
||||
if err := json.Unmarshal(b, &content); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal DigitallySigned: %v", err)
|
||||
}
|
||||
return d.FromBase64String(content)
|
||||
}
|
||||
|
||||
// LogEntry represents the (parsed) contents of an entry in a CT log. This is described
|
||||
// in section 3.1, but note that this structure does *not* match the TLS structure
|
||||
// defined there (the TLS structure is never used directly in RFC6962).
|
||||
type LogEntry struct {
|
||||
Index int64
|
||||
Leaf MerkleTreeLeaf
|
||||
// Exactly one of the following three fields should be non-empty.
|
||||
X509Cert *x509.Certificate // Parsed X.509 certificate
|
||||
Precert *Precertificate // Extracted precertificate
|
||||
JSONData []byte
|
||||
|
||||
// Chain holds the issuing certificate chain, starting with the
|
||||
// issuer of the leaf certificate / pre-certificate.
|
||||
Chain []ASN1Cert
|
||||
}
|
||||
|
||||
// PrecertChainEntry holds an precertificate together with a validation chain
|
||||
// for it; see section 3.1.
|
||||
type PrecertChainEntry struct {
|
||||
PreCertificate ASN1Cert `tls:"minlen:1,maxlen:16777215"`
|
||||
CertificateChain []ASN1Cert `tls:"minlen:0,maxlen:16777215"`
|
||||
}
|
||||
|
||||
// CertificateChain holds a chain of certificates, as returned as extra data
|
||||
// for get-entries (section 4.6).
|
||||
type CertificateChain struct {
|
||||
Entries []ASN1Cert `tls:"minlen:0,maxlen:16777215"`
|
||||
}
|
||||
|
||||
// JSONDataEntry holds arbitrary data.
|
||||
type JSONDataEntry struct {
|
||||
Data []byte `tls:"minlen:0,maxlen:1677215"`
|
||||
}
|
||||
|
||||
// SHA256Hash represents the output from the SHA256 hash function.
|
||||
type SHA256Hash [sha256.Size]byte
|
||||
|
||||
// FromBase64String populates the SHA256 struct with the contents of the base64 data passed in.
|
||||
func (s *SHA256Hash) FromBase64String(b64 string) error {
|
||||
bs, err := base64.StdEncoding.DecodeString(b64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unbase64 LogID: %v", err)
|
||||
}
|
||||
if len(bs) != sha256.Size {
|
||||
return fmt.Errorf("invalid SHA256 length, expected 32 but got %d", len(bs))
|
||||
}
|
||||
copy(s[:], bs)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Base64String returns the base64 representation of this SHA256Hash.
|
||||
func (s SHA256Hash) Base64String() string {
|
||||
return base64.StdEncoding.EncodeToString(s[:])
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaller interface for SHA256Hash.
|
||||
func (s SHA256Hash) MarshalJSON() ([]byte, error) {
|
||||
return []byte(`"` + s.Base64String() + `"`), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaller interface.
|
||||
func (s *SHA256Hash) UnmarshalJSON(b []byte) error {
|
||||
var content string
|
||||
if err := json.Unmarshal(b, &content); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal SHA256Hash: %v", err)
|
||||
}
|
||||
return s.FromBase64String(content)
|
||||
}
|
||||
|
||||
// SignedTreeHead represents the structure returned by the get-sth CT method
|
||||
// after base64 decoding; see sections 3.5 and 4.3.
|
||||
type SignedTreeHead struct {
|
||||
Version Version `json:"sth_version"` // The version of the protocol to which the STH conforms
|
||||
TreeSize uint64 `json:"tree_size"` // The number of entries in the new tree
|
||||
Timestamp uint64 `json:"timestamp"` // The time at which the STH was created
|
||||
SHA256RootHash SHA256Hash `json:"sha256_root_hash"` // The root hash of the log's Merkle tree
|
||||
TreeHeadSignature DigitallySigned `json:"tree_head_signature"` // Log's signature over a TLS-encoded TreeHeadSignature
|
||||
LogID SHA256Hash `json:"log_id"` // The SHA256 hash of the log's public key
|
||||
}
|
||||
|
||||
// TreeHeadSignature holds the data over which the signature in an STH is
|
||||
// generated; see section 3.5
|
||||
type TreeHeadSignature struct {
|
||||
Version Version `tls:"maxval:255"`
|
||||
SignatureType SignatureType `tls:"maxval:255"` // == TreeHashSignatureType
|
||||
Timestamp uint64
|
||||
TreeSize uint64
|
||||
SHA256RootHash SHA256Hash
|
||||
}
|
||||
|
||||
// SignedCertificateTimestamp represents the structure returned by the
|
||||
// add-chain and add-pre-chain methods after base64 decoding; see sections
|
||||
// 3.2, 4.1 and 4.2.
|
||||
type SignedCertificateTimestamp struct {
|
||||
SCTVersion Version `tls:"maxval:255"`
|
||||
LogID LogID
|
||||
Timestamp uint64
|
||||
Extensions CTExtensions `tls:"minlen:0,maxlen:65535"`
|
||||
Signature DigitallySigned // Signature over TLS-encoded CertificateTimestamp
|
||||
}
|
||||
|
||||
// CertificateTimestamp is the collection of data that the signature in an
|
||||
// SCT is over; see section 3.2.
|
||||
type CertificateTimestamp struct {
|
||||
SCTVersion Version `tls:"maxval:255"`
|
||||
SignatureType SignatureType `tls:"maxval:255"`
|
||||
Timestamp uint64
|
||||
EntryType LogEntryType `tls:"maxval:65535"`
|
||||
X509Entry *ASN1Cert `tls:"selector:EntryType,val:0"`
|
||||
PrecertEntry *PreCert `tls:"selector:EntryType,val:1"`
|
||||
JSONEntry *JSONDataEntry `tls:"selector:EntryType,val:32768"`
|
||||
Extensions CTExtensions `tls:"minlen:0,maxlen:65535"`
|
||||
}
|
||||
|
||||
func (s SignedCertificateTimestamp) String() string {
|
||||
return fmt.Sprintf("{Version:%d LogId:%s Timestamp:%d Extensions:'%s' Signature:%v}", s.SCTVersion,
|
||||
base64.StdEncoding.EncodeToString(s.LogID.KeyID[:]),
|
||||
s.Timestamp,
|
||||
s.Extensions,
|
||||
s.Signature)
|
||||
}
|
||||
|
||||
// TimestampedEntry is part of the MerkleTreeLeaf structure; see section 3.4.
|
||||
type TimestampedEntry struct {
|
||||
Timestamp uint64
|
||||
EntryType LogEntryType `tls:"maxval:65535"`
|
||||
X509Entry *ASN1Cert `tls:"selector:EntryType,val:0"`
|
||||
PrecertEntry *PreCert `tls:"selector:EntryType,val:1"`
|
||||
JSONEntry *JSONDataEntry `tls:"selector:EntryType,val:32768"`
|
||||
Extensions CTExtensions `tls:"minlen:0,maxlen:65535"`
|
||||
}
|
||||
|
||||
// MerkleTreeLeaf represents the deserialized structure of the hash input for the
|
||||
// leaves of a log's Merkle tree; see section 3.4.
|
||||
type MerkleTreeLeaf struct {
|
||||
Version Version `tls:"maxval:255"`
|
||||
LeafType MerkleLeafType `tls:"maxval:255"`
|
||||
TimestampedEntry *TimestampedEntry `tls:"selector:LeafType,val:0"`
|
||||
}
|
||||
|
||||
// Precertificate represents the parsed CT Precertificate structure.
|
||||
type Precertificate struct {
|
||||
// DER-encoded pre-certificate as originally added, which includes a
|
||||
// poison extension and a signature generated over the pre-cert by
|
||||
// the pre-cert issuer (which might differ from the issuer of the final
|
||||
// cert, see RFC6962 s3.1).
|
||||
Submitted ASN1Cert
|
||||
// SHA256 hash of the issuing key
|
||||
IssuerKeyHash [sha256.Size]byte
|
||||
// Parsed TBSCertificate structure, held in an x509.Certificate for convenience.
|
||||
TBSCertificate *x509.Certificate
|
||||
}
|
||||
|
||||
// X509Certificate returns the X.509 Certificate contained within the
|
||||
// MerkleTreeLeaf.
|
||||
func (m *MerkleTreeLeaf) X509Certificate() (*x509.Certificate, error) {
|
||||
if m.TimestampedEntry.EntryType != X509LogEntryType {
|
||||
return nil, fmt.Errorf("cannot call X509Certificate on a MerkleTreeLeaf that is not an X509 entry")
|
||||
}
|
||||
return x509.ParseCertificate(m.TimestampedEntry.X509Entry.Data)
|
||||
}
|
||||
|
||||
// Precertificate returns the X.509 Precertificate contained within the MerkleTreeLeaf.
|
||||
//
|
||||
// The returned precertificate is embedded in an x509.Certificate, but is in the
|
||||
// form stored internally in the log rather than the original submitted form
|
||||
// (i.e. it does not include the poison extension and any changes to reflect the
|
||||
// final certificate's issuer have been made; see x509.BuildPrecertTBS).
|
||||
func (m *MerkleTreeLeaf) Precertificate() (*x509.Certificate, error) {
|
||||
if m.TimestampedEntry.EntryType != PrecertLogEntryType {
|
||||
return nil, fmt.Errorf("cannot call Precertificate on a MerkleTreeLeaf that is not a precert entry")
|
||||
}
|
||||
return x509.ParseTBSCertificate(m.TimestampedEntry.PrecertEntry.TBSCertificate)
|
||||
}
|
||||
|
||||
// URI paths for Log requests; see section 4.
|
||||
const (
|
||||
AddChainPath = "/ct/v1/add-chain"
|
||||
AddPreChainPath = "/ct/v1/add-pre-chain"
|
||||
GetSTHPath = "/ct/v1/get-sth"
|
||||
GetEntriesPath = "/ct/v1/get-entries"
|
||||
GetProofByHashPath = "/ct/v1/get-proof-by-hash"
|
||||
GetSTHConsistencyPath = "/ct/v1/get-sth-consistency"
|
||||
GetRootsPath = "/ct/v1/get-roots"
|
||||
GetEntryAndProofPath = "/ct/v1/get-entry-and-proof"
|
||||
|
||||
AddJSONPath = "/ct/v1/add-json" // Experimental addition
|
||||
)
|
||||
|
||||
// AddChainRequest represents the JSON request body sent to the add-chain and
|
||||
// add-pre-chain POST methods from sections 4.1 and 4.2.
|
||||
type AddChainRequest struct {
|
||||
Chain [][]byte `json:"chain"`
|
||||
}
|
||||
|
||||
// AddChainResponse represents the JSON response to the add-chain and
|
||||
// add-pre-chain POST methods.
|
||||
// An SCT represents a Log's promise to integrate a [pre-]certificate into the
|
||||
// log within a defined period of time.
|
||||
type AddChainResponse struct {
|
||||
SCTVersion Version `json:"sct_version"` // SCT structure version
|
||||
ID []byte `json:"id"` // Log ID
|
||||
Timestamp uint64 `json:"timestamp"` // Timestamp of issuance
|
||||
Extensions string `json:"extensions"` // Holder for any CT extensions
|
||||
Signature []byte `json:"signature"` // Log signature for this SCT
|
||||
}
|
||||
|
||||
// AddJSONRequest represents the JSON request body sent to the add-json POST method.
|
||||
// The corresponding response re-uses AddChainResponse.
|
||||
// This is an experimental addition not covered by RFC6962.
|
||||
type AddJSONRequest struct {
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
// GetSTHResponse respresents the JSON response to the get-sth GET method from section 4.3.
|
||||
type GetSTHResponse struct {
|
||||
TreeSize uint64 `json:"tree_size"` // Number of certs in the current tree
|
||||
Timestamp uint64 `json:"timestamp"` // Time that the tree was created
|
||||
SHA256RootHash []byte `json:"sha256_root_hash"` // Root hash of the tree
|
||||
TreeHeadSignature []byte `json:"tree_head_signature"` // Log signature for this STH
|
||||
}
|
||||
|
||||
// GetSTHConsistencyResponse represents the JSON response to the get-sth-consistency
|
||||
// GET method from section 4.4. (The corresponding GET request has parameters 'first' and
|
||||
// 'second'.)
|
||||
type GetSTHConsistencyResponse struct {
|
||||
Consistency [][]byte `json:"consistency"`
|
||||
}
|
||||
|
||||
// GetProofByHashResponse represents the JSON response to the get-proof-by-hash GET
|
||||
// method from section 4.5. (The corresponding GET request has parameters 'hash'
|
||||
// and 'tree_size'.)
|
||||
type GetProofByHashResponse struct {
|
||||
LeafIndex int64 `json:"leaf_index"` // The 0-based index of the end entity corresponding to the "hash" parameter.
|
||||
AuditPath [][]byte `json:"audit_path"` // An array of base64-encoded Merkle Tree nodes proving the inclusion of the chosen certificate.
|
||||
}
|
||||
|
||||
// LeafEntry represents a leaf in the Log's Merkle tree, as returned by the get-entries
|
||||
// GET method from section 4.6.
|
||||
type LeafEntry struct {
|
||||
// LeafInput is a TLS-encoded MerkleTreeLeaf
|
||||
LeafInput []byte `json:"leaf_input"`
|
||||
// ExtraData holds (unsigned) extra data, normally the cert validation chain.
|
||||
ExtraData []byte `json:"extra_data"`
|
||||
}
|
||||
|
||||
// GetEntriesResponse respresents the JSON response to the get-entries GET method
|
||||
// from section 4.6.
|
||||
type GetEntriesResponse struct {
|
||||
Entries []LeafEntry `json:"entries"` // the list of returned entries
|
||||
}
|
||||
|
||||
// GetRootsResponse represents the JSON response to the get-roots GET method from section 4.7.
|
||||
type GetRootsResponse struct {
|
||||
Certificates []string `json:"certificates"`
|
||||
}
|
||||
|
||||
// GetEntryAndProofResponse represents the JSON response to the get-entry-and-proof
|
||||
// GET method from section 4.8. (The corresponding GET request has parameters 'leaf_index'
|
||||
// and 'tree_size'.)
|
||||
type GetEntryAndProofResponse struct {
|
||||
LeafInput []byte `json:"leaf_input"` // the entry itself
|
||||
ExtraData []byte `json:"extra_data"` // any chain provided when the entry was added to the log
|
||||
AuditPath [][]byte `json:"audit_path"` // the corresponding proof
|
||||
}
|
66
vendor/github.com/google/certificate-transparency-go/types_test.go
generated
vendored
Normal file
66
vendor/github.com/google/certificate-transparency-go/types_test.go
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ct
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/certificate-transparency-go/tls"
|
||||
)
|
||||
|
||||
const (
|
||||
CertEntry = "000000000149a6e03abe00000006513082064d30820535a003020102020c6a5d4161f5c9b68043270b0c300d06092a864886f70d0101050500305e310b300906035504061302424531193017060355040a1310476c6f62616c5369676e206e762d7361313430320603550403132b476c6f62616c5369676e20457874656e6465642056616c69646174696f6e204341202d2047322054455354301e170d3134313131333031353830315a170d3136313131333031353830315a3082011331183016060355040f0c0f427573696e65737320456e74697479311230100603550405130936363636363636363631133011060b2b0601040182373c0201031302444531293027060b2b0601040182373c02010113186576206a7572697364696374696f6e206c6f63616c69747931263024060b2b0601040182373c02010213156576206a7572697364696374696f6e207374617465310b3009060355040613024a50310a300806035504080c0153310a300806035504070c014c311530130603550409130c657620616464726573732033310c300a060355040b0c034f5531310c300a060355040b0c034f5532310a3008060355040a0c014f3117301506035504030c0e637372636e2e73736c32342e6a7030820122300d06092a864886f70d01010105000382010f003082010a02820101008db9f0d6b359466dffe95ba43dc1a5680eedc8f3cabbc573a236a109bf6e58df816c7bb8156147ab526eceaffd0576e6e1c09ea33433e114d7e5038c697298c7957f01a7e1142320847cf234995bbe42798340cb99e6a7e2cfa950277aef6e02f4d96ddceb0af9541171b0f8f1aa4f0d02453e6e654b25a13f2aff4357cae8177d3bd21855686591a2309d9ff5dead8240304e22eafcc5508587e6b6ad1d00b53c28e5b936269afbf214b73edbdc8a48a86c1c23f3dce55fcce60502c0908bca9bdb22c16c0b34d11b4fd27e9d7bcb56c5ec0fc4d52500fb06b0af5c4112e421022b78b31030cb73e9fd92ffc65919fd8f35e604fcaf025b9c77e3e5dff749a70203010001a38202523082024e300e0603551d0f0101ff0404030205a0304c0603551d2004453043304106092b06010401a03201013034303206082b06010505070201162668747470733a2f2f7777772e676c6f62616c7369676e2e636f6d2f7265706f7369746f72792f30480603551d1f0441303f303da03ba0398637687474703a2f2f63726c2e676c6f62616c7369676e2e636f6d2f67732f67736f7267616e697a6174696f6e76616c63617467322e63726c30819c06082b0601050507010104818f30818c304a06082b06010505073002863e687474703a2f2f7365637572652e676c6f62616c7369676e2e636f6d2f6361636572742f67736f7267616e697a6174696f6e76616c63617467322e637274303e06082b060105050730018632687474703a2f2f6f637370322e676c6f62616c7369676e2e636f6d2f67736f7267616e697a6174696f6e76616c6361746732301d0603551d250416301406082b0601050507030106082b0601050507030230190603551d1104123010820e637372636e2e73736c32342e6a70301d0603551d0e041604147f834b2903e35efff651619083a2efd69a6d70f4301f0603551d23041830168014ab30a406d972d0029ab2c7d3f4241be2fca5320230818a060a2b06010401d679020402047c047a0078007600b0cc83e5a5f97d6baf7c09cc284904872ac7e88b132c6350b7c6fd26e16c6c7700000149a6dc346b00000403004730450220469f4dc0553b7832bd56633c3b9d53faaec84df414b7a05ab1b2d544d146ac3e022100ee899419fd4f95544798f7883fe093692feb4c90e84d651600f7019166a43701300d06092a864886f70d010105050003820101007dcd3e228d68cdc0734c7629fd7d40cd742d0ed1d0d9f49a643af12dcdbc61394638b7c519bb7cae530ccdc3a5037d5cdd8a4d2c01abdc834daf1993f7a22ee2c223377a94da4e68ac69a0b50d2d473ec77651e001c5f71a23cc2defe7616fd6c6491aa7f9a2bb16b930ce3f8cc37cf6a47bfb04fd4eff7db8433cc6fdb05146a4a31fe65211875f2c51129bf0729ce2dc7ce1a5afc6eaa1eb3a36296cb9e091375edfc408c727f6d54bba408da60b46c496a364c504adf47ee0496a9260fe223c8b23c14832635c3dff0dba8a0c8cdd957a77f18443b7782a9b6c7636b7d66df426350b959537e911888e45b2c0b218e50d03fdcfa7f758e8e60dd1a1996bc00000"
|
||||
PrecertEntry = "00000000014b4981f0c800013760e2790f33a498f9b6c149fecfca3993954b536fbf36ad45d0a8415b79337d00047a30820476a00302010202100532298c396a3e25fcaa1977e827b5f3300d06092a864886f70d01010b0500306d310b300906035504061302555331163014060355040a130d47656f547275737420496e632e311f301d060355040b1316464f52205445535420505552504f534553204f4e4c59312530230603550403131c47656f54727573742045562053534c2054455354204341202d204734301e170d3135303230323030303030305a170d3136303232373233353935395a3081c331133011060b2b0601040182373c02010313024742311b3019060b2b0601040182373c020102140a43616c69666f726e6961311e301c060b2b0601040182373c0201010c0d4d6f756e7461696e2056696577310b30090603550406130247423113301106035504080c0a43616c69666f726e69613116301406035504070c0d4d6f756e7461696e2056696577311d301b060355040a0c1453796d616e74656320436f72706f726174696f6e3116301406035504030c0d736466656473662e747275737430820122300d06092a864886f70d01010105000382010f003082010a0282010100b19d97def39ff829c65ea099a3257298b33ff675451fdc5641a222347aee4a56201f4c1a406f2f19815d86dec1a611768e7d556c8e33a7f1b4c78db19cceae540e97ae1f0660b2ee4f8cff2045b84a9da228349744406eceaed0b08d46fdab3543b3d86ea708627a61a529b793a76adc6b776bc8d5b3d4fe21e2c4aa92cfd33b45e7412068e0683a2beffad1df2fc320b8ddbf02ffb603d2cf74798277fd9656b5acd45659b0e5d761e02dcf95c53095555a931ad5bfa9b4967c045d5f12de2d6b537cd93af2ad8b45e5540bd43279876d13e376fb649778e10dfa56165b901bd37e9dee4e46027b4c0732ca7ed64491862abaf6a24a4aaed8f49a0922ca4fb50203010001a38201d1308201cd30470603551d110440303e820d6b6a61736468662e7472757374820b73736466732e7472757374820d736466656473662e747275737482117777772e736466656473662e747275737430090603551d1304023000300e0603551d0f0101ff0404030205a0302b0603551d1f042430223020a01ea01c861a687474703a2f2f676d2e73796d63622e636f6d2f676d2e63726c3081a00603551d2004819830819530819206092b06010401f0220106308184303f06082b06010505070201163368747470733a2f2f7777772e67656f74727573742e636f6d2f7265736f75726365732f7265706f7369746f72792f6c6567616c304106082b0601050507020230350c3368747470733a2f2f7777772e67656f74727573742e636f6d2f7265736f75726365732f7265706f7369746f72792f6c6567616c301d0603551d250416301406082b0601050507030106082b06010505070302301f0603551d23041830168014b1699461abe6cb0c4ce759af5a498b1833c1e147305706082b06010505070101044b3049301f06082b060105050730018613687474703a2f2f676d2e73796d63642e636f6d302606082b06010505073002861a687474703a2f2f676d2e73796d63622e636f6d2f676d2e6372740000"
|
||||
)
|
||||
|
||||
func TestUnmarshalMerkleTreeLeaf(t *testing.T) {
|
||||
var tests = []struct {
|
||||
in string // hex string
|
||||
want LogEntryType
|
||||
errstr string
|
||||
}{
|
||||
{CertEntry, X509LogEntryType, ""},
|
||||
{PrecertEntry, PrecertLogEntryType, ""},
|
||||
{"001234", 0, "LeafType: unhandled value"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
inData, _ := hex.DecodeString(test.in)
|
||||
var got MerkleTreeLeaf
|
||||
_, err := tls.Unmarshal(inData, &got)
|
||||
if test.errstr != "" {
|
||||
if err == nil {
|
||||
t.Errorf("tls.Unmarshal(%s, &MerkleTreeLeaf)=%+v,nil; want error %q", test.in, got, test.errstr)
|
||||
} else if !strings.Contains(err.Error(), test.errstr) {
|
||||
t.Errorf("tls.Unmarshal(%s, &MerkleTreeLeaf)=nil,%q; want error %q", test.in, err.Error(), test.errstr)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("tls.Unmarshal(%s, &MerkleTreeLeaf)=nil,%q; want type %v", test.in, err.Error(), test.want)
|
||||
continue
|
||||
}
|
||||
if got.Version != V1 {
|
||||
t.Errorf("tls.Unmarshal(%s, &MerkleTreeLeaf)=version=%v,nil; want version 1", test.in, got.Version)
|
||||
}
|
||||
if got.LeafType != TimestampedEntryLeafType {
|
||||
t.Errorf("tls.Unmarshal(%s, &MerkleTreeLeaf)=LeafType=%v,nil; want LeafType=%v", test.in, got.LeafType, TimestampedEntryLeafType)
|
||||
}
|
||||
if got.TimestampedEntry.EntryType != test.want {
|
||||
t.Errorf("tls.Unmarshal(%s, &MerkleTreeLeaf)=EntryType=%v,nil; want LeafType=%v", test.in, got.TimestampedEntry.EntryType, test.want)
|
||||
}
|
||||
}
|
||||
}
|
143
vendor/github.com/google/certificate-transparency-go/x509/cert_pool.go
generated
vendored
Normal file
143
vendor/github.com/google/certificate-transparency-go/x509/cert_pool.go
generated
vendored
Normal file
@ -0,0 +1,143 @@
|
||||
// Copyright 2011 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 x509
|
||||
|
||||
import (
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// CertPool is a set of certificates.
|
||||
type CertPool struct {
|
||||
bySubjectKeyId map[string][]int
|
||||
byName map[string][]int
|
||||
certs []*Certificate
|
||||
}
|
||||
|
||||
// NewCertPool returns a new, empty CertPool.
|
||||
func NewCertPool() *CertPool {
|
||||
return &CertPool{
|
||||
bySubjectKeyId: make(map[string][]int),
|
||||
byName: make(map[string][]int),
|
||||
}
|
||||
}
|
||||
|
||||
// SystemCertPool returns a copy of the system cert pool.
|
||||
//
|
||||
// Any mutations to the returned pool are not written to disk and do
|
||||
// not affect any other pool.
|
||||
func SystemCertPool() (*CertPool, error) {
|
||||
if runtime.GOOS == "windows" {
|
||||
// Issue 16736, 18609:
|
||||
return nil, errors.New("crypto/x509: system root pool is not available on Windows")
|
||||
}
|
||||
|
||||
return loadSystemRoots()
|
||||
}
|
||||
|
||||
// findVerifiedParents attempts to find certificates in s which have signed the
|
||||
// given certificate. If any candidates were rejected then errCert will be set
|
||||
// to one of them, arbitrarily, and err will contain the reason that it was
|
||||
// rejected.
|
||||
func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int, errCert *Certificate, err error) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
var candidates []int
|
||||
|
||||
if len(cert.AuthorityKeyId) > 0 {
|
||||
candidates = s.bySubjectKeyId[string(cert.AuthorityKeyId)]
|
||||
}
|
||||
if len(candidates) == 0 {
|
||||
candidates = s.byName[string(cert.RawIssuer)]
|
||||
}
|
||||
|
||||
for _, c := range candidates {
|
||||
if err = cert.CheckSignatureFrom(s.certs[c]); err == nil {
|
||||
parents = append(parents, c)
|
||||
} else {
|
||||
errCert = s.certs[c]
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s *CertPool) contains(cert *Certificate) bool {
|
||||
if s == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
candidates := s.byName[string(cert.RawSubject)]
|
||||
for _, c := range candidates {
|
||||
if s.certs[c].Equal(cert) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// AddCert adds a certificate to a pool.
|
||||
func (s *CertPool) AddCert(cert *Certificate) {
|
||||
if cert == nil {
|
||||
panic("adding nil Certificate to CertPool")
|
||||
}
|
||||
|
||||
// Check that the certificate isn't being added twice.
|
||||
if s.contains(cert) {
|
||||
return
|
||||
}
|
||||
|
||||
n := len(s.certs)
|
||||
s.certs = append(s.certs, cert)
|
||||
|
||||
if len(cert.SubjectKeyId) > 0 {
|
||||
keyId := string(cert.SubjectKeyId)
|
||||
s.bySubjectKeyId[keyId] = append(s.bySubjectKeyId[keyId], n)
|
||||
}
|
||||
name := string(cert.RawSubject)
|
||||
s.byName[name] = append(s.byName[name], n)
|
||||
}
|
||||
|
||||
// AppendCertsFromPEM attempts to parse a series of PEM encoded certificates.
|
||||
// It appends any certificates found to s and reports whether any certificates
|
||||
// were successfully parsed.
|
||||
//
|
||||
// On many Linux systems, /etc/ssl/cert.pem will contain the system wide set
|
||||
// of root CAs in a format suitable for this function.
|
||||
func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) {
|
||||
for len(pemCerts) > 0 {
|
||||
var block *pem.Block
|
||||
block, pemCerts = pem.Decode(pemCerts)
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
cert, err := ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
s.AddCert(cert)
|
||||
ok = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Subjects returns a list of the DER-encoded subjects of
|
||||
// all of the certificates in the pool.
|
||||
func (s *CertPool) Subjects() [][]byte {
|
||||
res := make([][]byte, len(s.certs))
|
||||
for i, c := range s.certs {
|
||||
res[i] = c.RawSubject
|
||||
}
|
||||
return res
|
||||
}
|
230
vendor/github.com/google/certificate-transparency-go/x509/error.go
generated
vendored
Normal file
230
vendor/github.com/google/certificate-transparency-go/x509/error.go
generated
vendored
Normal file
@ -0,0 +1,230 @@
|
||||
package x509
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Error implements the error interface and describes a single error in an X.509 certificate or CRL.
|
||||
type Error struct {
|
||||
ID ErrorID
|
||||
Category ErrCategory
|
||||
Summary string
|
||||
Field string
|
||||
SpecRef string
|
||||
SpecText string
|
||||
// Fatal indicates that parsing has been aborted.
|
||||
Fatal bool
|
||||
}
|
||||
|
||||
func (err Error) Error() string {
|
||||
var msg bytes.Buffer
|
||||
if err.ID != ErrInvalidID {
|
||||
if err.Fatal {
|
||||
msg.WriteRune('E')
|
||||
} else {
|
||||
msg.WriteRune('W')
|
||||
}
|
||||
msg.WriteString(fmt.Sprintf("%03d: ", err.ID))
|
||||
}
|
||||
msg.WriteString(err.Summary)
|
||||
return msg.String()
|
||||
}
|
||||
|
||||
// VerboseError creates a more verbose error string, including spec details.
|
||||
func (err Error) VerboseError() string {
|
||||
var msg bytes.Buffer
|
||||
msg.WriteString(err.Error())
|
||||
if len(err.Field) > 0 || err.Category != UnknownCategory || len(err.SpecRef) > 0 || len(err.SpecText) > 0 {
|
||||
msg.WriteString(" (")
|
||||
needSep := false
|
||||
if len(err.Field) > 0 {
|
||||
msg.WriteString(err.Field)
|
||||
needSep = true
|
||||
}
|
||||
if err.Category != UnknownCategory {
|
||||
if needSep {
|
||||
msg.WriteString(": ")
|
||||
}
|
||||
msg.WriteString(err.Category.String())
|
||||
needSep = true
|
||||
}
|
||||
if len(err.SpecRef) > 0 {
|
||||
if needSep {
|
||||
msg.WriteString(": ")
|
||||
}
|
||||
msg.WriteString(err.SpecRef)
|
||||
needSep = true
|
||||
}
|
||||
if len(err.SpecText) > 0 {
|
||||
if needSep {
|
||||
if len(err.SpecRef) > 0 {
|
||||
msg.WriteString(", ")
|
||||
} else {
|
||||
msg.WriteString(": ")
|
||||
}
|
||||
}
|
||||
msg.WriteString("'")
|
||||
msg.WriteString(err.SpecText)
|
||||
msg.WriteString("'")
|
||||
}
|
||||
msg.WriteString(")")
|
||||
}
|
||||
|
||||
return msg.String()
|
||||
}
|
||||
|
||||
// ErrCategory indicates the category of an x509.Error.
|
||||
type ErrCategory int
|
||||
|
||||
// ErrCategory values.
|
||||
const (
|
||||
UnknownCategory ErrCategory = iota
|
||||
// Errors in ASN.1 encoding
|
||||
InvalidASN1Encoding
|
||||
InvalidASN1Content
|
||||
InvalidASN1DER
|
||||
// Errors in ASN.1 relative to schema
|
||||
InvalidValueRange
|
||||
InvalidASN1Type
|
||||
UnexpectedAdditionalData
|
||||
// Errors in X.509
|
||||
PoorlyFormedCertificate // Fails a SHOULD clause
|
||||
MalformedCertificate // Fails a MUST clause
|
||||
PoorlyFormedCRL // Fails a SHOULD clause
|
||||
MalformedCRL // Fails a MUST clause
|
||||
// Errors relative to CA/Browser Forum guidelines
|
||||
BaselineRequirementsFailure
|
||||
EVRequirementsFailure
|
||||
// Other errors
|
||||
InsecureAlgorithm
|
||||
UnrecognizedValue
|
||||
)
|
||||
|
||||
func (category ErrCategory) String() string {
|
||||
switch category {
|
||||
case InvalidASN1Encoding:
|
||||
return "Invalid ASN.1 encoding"
|
||||
case InvalidASN1Content:
|
||||
return "Invalid ASN.1 content"
|
||||
case InvalidASN1DER:
|
||||
return "Invalid ASN.1 distinguished encoding"
|
||||
case InvalidValueRange:
|
||||
return "Invalid value for range given in schema"
|
||||
case InvalidASN1Type:
|
||||
return "Invalid ASN.1 type for schema"
|
||||
case UnexpectedAdditionalData:
|
||||
return "Unexpected additional data present"
|
||||
case PoorlyFormedCertificate:
|
||||
return "Certificate does not comply with SHOULD clause in spec"
|
||||
case MalformedCertificate:
|
||||
return "Certificate does not comply with MUST clause in spec"
|
||||
case PoorlyFormedCRL:
|
||||
return "Certificate Revocation List does not comply with SHOULD clause in spec"
|
||||
case MalformedCRL:
|
||||
return "Certificate Revocation List does not comply with MUST clause in spec"
|
||||
case BaselineRequirementsFailure:
|
||||
return "Certificate does not comply with CA/BF baseline requirements"
|
||||
case EVRequirementsFailure:
|
||||
return "Certificate does not comply with CA/BF EV requirements"
|
||||
case InsecureAlgorithm:
|
||||
return "Certificate uses an insecure algorithm"
|
||||
case UnrecognizedValue:
|
||||
return "Certificate uses an unrecognized value"
|
||||
default:
|
||||
return fmt.Sprintf("Unknown (%d)", category)
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorID is an identifier for an x509.Error, to allow filtering.
|
||||
type ErrorID int
|
||||
|
||||
// Errors implements the error interface and holds a collection of errors found in a certificate or CRL.
|
||||
type Errors struct {
|
||||
Errs []Error
|
||||
}
|
||||
|
||||
// Error converts to a string.
|
||||
func (e *Errors) Error() string {
|
||||
return e.combineErrors(Error.Error)
|
||||
}
|
||||
|
||||
// VerboseError creates a more verbose error string, including spec details.
|
||||
func (e *Errors) VerboseError() string {
|
||||
return e.combineErrors(Error.VerboseError)
|
||||
}
|
||||
|
||||
// Fatal indicates whether e includes a fatal error
|
||||
func (e *Errors) Fatal() bool {
|
||||
return (e.FirstFatal() != nil)
|
||||
}
|
||||
|
||||
// Empty indicates whether e has no errors.
|
||||
func (e *Errors) Empty() bool {
|
||||
return len(e.Errs) == 0
|
||||
}
|
||||
|
||||
// FirstFatal returns the first fatal error in e, or nil
|
||||
// if there is no fatal error.
|
||||
func (e *Errors) FirstFatal() error {
|
||||
for _, err := range e.Errs {
|
||||
if err.Fatal {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// AddID adds the Error identified by the given id to an x509.Errors.
|
||||
func (e *Errors) AddID(id ErrorID, args ...interface{}) {
|
||||
e.Errs = append(e.Errs, NewError(id, args...))
|
||||
}
|
||||
|
||||
func (e Errors) combineErrors(errfn func(Error) string) string {
|
||||
if len(e.Errs) == 0 {
|
||||
return ""
|
||||
}
|
||||
if len(e.Errs) == 1 {
|
||||
return errfn((e.Errs)[0])
|
||||
}
|
||||
var msg bytes.Buffer
|
||||
msg.WriteString("Errors:")
|
||||
for _, err := range e.Errs {
|
||||
msg.WriteString("\n ")
|
||||
msg.WriteString(errfn(err))
|
||||
}
|
||||
return msg.String()
|
||||
}
|
||||
|
||||
// Filter creates a new Errors object with any entries from the filtered
|
||||
// list of IDs removed.
|
||||
func (e Errors) Filter(filtered []ErrorID) Errors {
|
||||
var results Errors
|
||||
eloop:
|
||||
for _, v := range e.Errs {
|
||||
for _, f := range filtered {
|
||||
if v.ID == f {
|
||||
break eloop
|
||||
}
|
||||
}
|
||||
results.Errs = append(results.Errs, v)
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
// ErrorFilter builds a list of error IDs (suitable for use with Errors.Filter) from a comma-separated string.
|
||||
func ErrorFilter(ignore string) []ErrorID {
|
||||
var ids []ErrorID
|
||||
filters := strings.Split(ignore, ",")
|
||||
for _, f := range filters {
|
||||
v, err := strconv.Atoi(f)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ids = append(ids, ErrorID(v))
|
||||
}
|
||||
return ids
|
||||
}
|
192
vendor/github.com/google/certificate-transparency-go/x509/error_test.go
generated
vendored
Normal file
192
vendor/github.com/google/certificate-transparency-go/x509/error_test.go
generated
vendored
Normal file
@ -0,0 +1,192 @@
|
||||
package x509_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
)
|
||||
|
||||
func TestErrors(t *testing.T) {
|
||||
var tests = []struct {
|
||||
errs []x509.Error
|
||||
want string
|
||||
wantVerbose string
|
||||
wantFatal bool
|
||||
}{
|
||||
{
|
||||
errs: []x509.Error{
|
||||
{Summary: "Error", Field: "a.b.c"},
|
||||
},
|
||||
want: "Error",
|
||||
wantVerbose: "Error (a.b.c)",
|
||||
},
|
||||
{
|
||||
errs: []x509.Error{
|
||||
{
|
||||
Summary: "Error",
|
||||
Field: "a.b.c",
|
||||
SpecRef: "RFC5280 s4.1.2.2",
|
||||
SpecText: "The serial number MUST be a positive integer",
|
||||
Category: x509.MalformedCertificate,
|
||||
},
|
||||
},
|
||||
want: "Error",
|
||||
wantVerbose: "Error (a.b.c: Certificate does not comply with MUST clause in spec: RFC5280 s4.1.2.2, 'The serial number MUST be a positive integer')",
|
||||
},
|
||||
{
|
||||
errs: []x509.Error{
|
||||
{
|
||||
Summary: "Error",
|
||||
Field: "a.b.c",
|
||||
SpecRef: "RFC5280 s4.1.2.2",
|
||||
SpecText: "The serial number MUST be a positive integer",
|
||||
},
|
||||
},
|
||||
want: "Error",
|
||||
wantVerbose: "Error (a.b.c: RFC5280 s4.1.2.2, 'The serial number MUST be a positive integer')",
|
||||
},
|
||||
{
|
||||
errs: []x509.Error{
|
||||
{
|
||||
Summary: "Error",
|
||||
Field: "a.b.c",
|
||||
SpecRef: "RFC5280 s4.1.2.2",
|
||||
Category: x509.MalformedCertificate,
|
||||
},
|
||||
},
|
||||
want: "Error",
|
||||
wantVerbose: "Error (a.b.c: Certificate does not comply with MUST clause in spec: RFC5280 s4.1.2.2)",
|
||||
},
|
||||
{
|
||||
errs: []x509.Error{
|
||||
{
|
||||
Summary: "Error",
|
||||
Field: "a.b.c",
|
||||
SpecText: "The serial number MUST be a positive integer",
|
||||
Category: x509.MalformedCertificate,
|
||||
},
|
||||
},
|
||||
want: "Error",
|
||||
wantVerbose: "Error (a.b.c: Certificate does not comply with MUST clause in spec: 'The serial number MUST be a positive integer')",
|
||||
},
|
||||
{
|
||||
errs: []x509.Error{
|
||||
{
|
||||
Summary: "Error",
|
||||
Field: "a.b.c",
|
||||
SpecRef: "RFC5280 s4.1.2.2",
|
||||
},
|
||||
},
|
||||
want: "Error",
|
||||
wantVerbose: "Error (a.b.c: RFC5280 s4.1.2.2)",
|
||||
},
|
||||
{
|
||||
errs: []x509.Error{
|
||||
{
|
||||
Summary: "Error",
|
||||
Field: "a.b.c",
|
||||
SpecText: "The serial number MUST be a positive integer",
|
||||
},
|
||||
},
|
||||
want: "Error",
|
||||
wantVerbose: "Error (a.b.c: 'The serial number MUST be a positive integer')",
|
||||
},
|
||||
{
|
||||
errs: []x509.Error{
|
||||
{
|
||||
Summary: "Error",
|
||||
Field: "a.b.c",
|
||||
Category: x509.MalformedCertificate,
|
||||
},
|
||||
},
|
||||
want: "Error",
|
||||
wantVerbose: "Error (a.b.c: Certificate does not comply with MUST clause in spec)",
|
||||
},
|
||||
{
|
||||
errs: []x509.Error{
|
||||
{Summary: "Error"},
|
||||
},
|
||||
want: "Error",
|
||||
wantVerbose: "Error",
|
||||
},
|
||||
{
|
||||
errs: []x509.Error{
|
||||
{Summary: "Error\nwith newline", Field: "x", Category: x509.InvalidASN1DER},
|
||||
},
|
||||
want: "Error\nwith newline",
|
||||
wantVerbose: "Error\nwith newline (x: Invalid ASN.1 distinguished encoding)",
|
||||
},
|
||||
{
|
||||
errs: []x509.Error{
|
||||
{Summary: "Error1", Field: "a.b.c"},
|
||||
{Summary: "Error2", Field: "a.b.c.d"},
|
||||
{Summary: "Error3", Field: "x.y.z"},
|
||||
},
|
||||
want: "Errors:\n Error1\n Error2\n Error3",
|
||||
wantVerbose: "Errors:\n Error1 (a.b.c)\n Error2 (a.b.c.d)\n Error3 (x.y.z)",
|
||||
},
|
||||
{
|
||||
errs: []x509.Error{
|
||||
{Summary: "Error1", Field: "a.b.c"},
|
||||
{Summary: "Error2", Field: "a.b.c.d", Fatal: true},
|
||||
{Summary: "Error3", Field: "x.y.z"},
|
||||
},
|
||||
want: "Errors:\n Error1\n Error2\n Error3",
|
||||
wantVerbose: "Errors:\n Error1 (a.b.c)\n Error2 (a.b.c.d)\n Error3 (x.y.z)",
|
||||
wantFatal: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
errs := x509.Errors{Errs: test.errs}
|
||||
if got := errs.Error(); got != test.want {
|
||||
t.Errorf("Errors(%+v).Error()=%q; want %q", test.errs, got, test.want)
|
||||
}
|
||||
if got := errs.VerboseError(); got != test.wantVerbose {
|
||||
t.Errorf("Errors(%+v).VerboseError()=%q; want %q", test.errs, got, test.wantVerbose)
|
||||
}
|
||||
if got := errs.Fatal(); got != test.wantFatal {
|
||||
t.Errorf("Errors(%+v).Fatal()=%v; want %v", test.errs, got, test.wantFatal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorsAppend(t *testing.T) {
|
||||
var errs x509.Errors
|
||||
if got, want := errs.Error(), ""; got != want {
|
||||
t.Errorf("Errors().Error()=%q; want %q", got, want)
|
||||
}
|
||||
if got, want := errs.Empty(), true; got != want {
|
||||
t.Errorf("Errors().Empty()=%t; want %t", got, want)
|
||||
}
|
||||
errs.Errs = append(errs.Errs, x509.Error{
|
||||
Summary: "Error",
|
||||
Field: "a.b.c",
|
||||
SpecRef: "RFC5280 s4.1.2.2"})
|
||||
if got, want := errs.VerboseError(), "Error (a.b.c: RFC5280 s4.1.2.2)"; got != want {
|
||||
t.Errorf("Errors(%+v).Error=%q; want %q", errs, got, want)
|
||||
}
|
||||
if got, want := errs.Empty(), false; got != want {
|
||||
t.Errorf("Errors().Empty()=%t; want %t", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorsFilter(t *testing.T) {
|
||||
var errs x509.Errors
|
||||
id := x509.ErrMaxID + 2
|
||||
errs.AddID(id, "arg1", 2, "arg3")
|
||||
baseErr := errs.Error()
|
||||
|
||||
errs.AddID(x509.ErrMaxID + 1)
|
||||
if got, want := errs.Error(), fmt.Sprintf("Errors:\n %s\n E%03d: Unknown error ID %v: args []", baseErr, x509.ErrMaxID+1, x509.ErrMaxID+1); got != want {
|
||||
t.Errorf("Errors(%+v).Error=%q; want %q", errs, got, want)
|
||||
}
|
||||
|
||||
errList := fmt.Sprintf("%d, %d", x509.ErrMaxID+1, x509.ErrMaxID+1)
|
||||
filter := x509.ErrorFilter(errList)
|
||||
errs2 := errs.Filter(filter)
|
||||
if got, want := errs2.Error(), baseErr; got != want {
|
||||
t.Errorf("Errors(%+v).Error=%q; want %q", errs, got, want)
|
||||
}
|
||||
}
|
302
vendor/github.com/google/certificate-transparency-go/x509/errors.go
generated
vendored
Normal file
302
vendor/github.com/google/certificate-transparency-go/x509/errors.go
generated
vendored
Normal file
@ -0,0 +1,302 @@
|
||||
package x509
|
||||
|
||||
import "fmt"
|
||||
|
||||
// To preserve error IDs, only append to this list, never insert.
|
||||
const (
|
||||
ErrInvalidID ErrorID = iota
|
||||
ErrInvalidCertList
|
||||
ErrTrailingCertList
|
||||
ErrUnexpectedlyCriticalCertListExtension
|
||||
ErrUnexpectedlyNonCriticalCertListExtension
|
||||
ErrInvalidCertListAuthKeyID
|
||||
ErrTrailingCertListAuthKeyID
|
||||
ErrInvalidCertListIssuerAltName
|
||||
ErrInvalidCertListCRLNumber
|
||||
ErrTrailingCertListCRLNumber
|
||||
ErrNegativeCertListCRLNumber
|
||||
ErrInvalidCertListDeltaCRL
|
||||
ErrTrailingCertListDeltaCRL
|
||||
ErrNegativeCertListDeltaCRL
|
||||
ErrInvalidCertListIssuingDP
|
||||
ErrTrailingCertListIssuingDP
|
||||
ErrCertListIssuingDPMultipleTypes
|
||||
ErrCertListIssuingDPInvalidFullName
|
||||
ErrInvalidCertListFreshestCRL
|
||||
ErrInvalidCertListAuthInfoAccess
|
||||
ErrTrailingCertListAuthInfoAccess
|
||||
ErrUnhandledCriticalCertListExtension
|
||||
ErrUnexpectedlyCriticalRevokedCertExtension
|
||||
ErrUnexpectedlyNonCriticalRevokedCertExtension
|
||||
ErrInvalidRevocationReason
|
||||
ErrTrailingRevocationReason
|
||||
ErrInvalidRevocationInvalidityDate
|
||||
ErrTrailingRevocationInvalidityDate
|
||||
ErrInvalidRevocationIssuer
|
||||
ErrUnhandledCriticalRevokedCertExtension
|
||||
|
||||
ErrMaxID
|
||||
)
|
||||
|
||||
// idToError gives a template x509.Error for each defined ErrorID; where the Summary
|
||||
// field may hold format specifiers that take field parameters.
|
||||
var idToError map[ErrorID]Error
|
||||
|
||||
var errorInfo = []Error{
|
||||
{
|
||||
ID: ErrInvalidCertList,
|
||||
Summary: "x509: failed to parse CertificateList: %v",
|
||||
Field: "CertificateList",
|
||||
SpecRef: "RFC 5280 s5.1",
|
||||
Category: InvalidASN1Content,
|
||||
Fatal: true,
|
||||
},
|
||||
{
|
||||
ID: ErrTrailingCertList,
|
||||
Summary: "x509: trailing data after CertificateList",
|
||||
Field: "CertificateList",
|
||||
SpecRef: "RFC 5280 s5.1",
|
||||
Category: InvalidASN1Content,
|
||||
Fatal: true,
|
||||
},
|
||||
|
||||
{
|
||||
ID: ErrUnexpectedlyCriticalCertListExtension,
|
||||
Summary: "x509: certificate list extension %v marked critical but expected to be non-critical",
|
||||
Field: "tbsCertList.crlExtensions.*.critical",
|
||||
SpecRef: "RFC 5280 s5.2",
|
||||
Category: MalformedCRL,
|
||||
},
|
||||
{
|
||||
ID: ErrUnexpectedlyNonCriticalCertListExtension,
|
||||
Summary: "x509: certificate list extension %v marked non-critical but expected to be critical",
|
||||
Field: "tbsCertList.crlExtensions.*.critical",
|
||||
SpecRef: "RFC 5280 s5.2",
|
||||
Category: MalformedCRL,
|
||||
},
|
||||
|
||||
{
|
||||
ID: ErrInvalidCertListAuthKeyID,
|
||||
Summary: "x509: failed to unmarshal certificate-list authority key-id: %v",
|
||||
Field: "tbsCertList.crlExtensions.*.AuthorityKeyIdentifier",
|
||||
SpecRef: "RFC 5280 s5.2.1",
|
||||
Category: InvalidASN1Content,
|
||||
Fatal: true,
|
||||
},
|
||||
{
|
||||
ID: ErrTrailingCertListAuthKeyID,
|
||||
Summary: "x509: trailing data after certificate list auth key ID",
|
||||
Field: "tbsCertList.crlExtensions.*.AuthorityKeyIdentifier",
|
||||
SpecRef: "RFC 5280 s5.2.1",
|
||||
Category: InvalidASN1Content,
|
||||
Fatal: true,
|
||||
},
|
||||
{
|
||||
ID: ErrInvalidCertListIssuerAltName,
|
||||
Summary: "x509: failed to parse CRL issuer alt name: %v",
|
||||
Field: "tbsCertList.crlExtensions.*.IssuerAltName",
|
||||
SpecRef: "RFC 5280 s5.2.2",
|
||||
Category: InvalidASN1Content,
|
||||
Fatal: true,
|
||||
},
|
||||
{
|
||||
ID: ErrInvalidCertListCRLNumber,
|
||||
Summary: "x509: failed to unmarshal certificate-list crl-number: %v",
|
||||
Field: "tbsCertList.crlExtensions.*.CRLNumber",
|
||||
SpecRef: "RFC 5280 s5.2.3",
|
||||
Category: InvalidASN1Content,
|
||||
Fatal: true,
|
||||
},
|
||||
{
|
||||
ID: ErrTrailingCertListCRLNumber,
|
||||
Summary: "x509: trailing data after certificate list crl-number",
|
||||
Field: "tbsCertList.crlExtensions.*.CRLNumber",
|
||||
SpecRef: "RFC 5280 s5.2.3",
|
||||
Category: InvalidASN1Content,
|
||||
Fatal: true,
|
||||
},
|
||||
{
|
||||
ID: ErrNegativeCertListCRLNumber,
|
||||
Summary: "x509: negative certificate list crl-number: %d",
|
||||
Field: "tbsCertList.crlExtensions.*.CRLNumber",
|
||||
SpecRef: "RFC 5280 s5.2.3",
|
||||
Category: MalformedCRL,
|
||||
Fatal: true,
|
||||
},
|
||||
{
|
||||
ID: ErrInvalidCertListDeltaCRL,
|
||||
Summary: "x509: failed to unmarshal certificate-list delta-crl: %v",
|
||||
Field: "tbsCertList.crlExtensions.*.BaseCRLNumber",
|
||||
SpecRef: "RFC 5280 s5.2.4",
|
||||
Category: InvalidASN1Content,
|
||||
Fatal: true,
|
||||
},
|
||||
{
|
||||
ID: ErrTrailingCertListDeltaCRL,
|
||||
Summary: "x509: trailing data after certificate list delta-crl",
|
||||
Field: "tbsCertList.crlExtensions.*.BaseCRLNumber",
|
||||
SpecRef: "RFC 5280 s5.2.4",
|
||||
Category: InvalidASN1Content,
|
||||
Fatal: true,
|
||||
},
|
||||
{
|
||||
ID: ErrNegativeCertListDeltaCRL,
|
||||
Summary: "x509: negative certificate list base-crl-number: %d",
|
||||
Field: "tbsCertList.crlExtensions.*.BaseCRLNumber",
|
||||
SpecRef: "RFC 5280 s5.2.4",
|
||||
Category: MalformedCRL,
|
||||
Fatal: true,
|
||||
},
|
||||
{
|
||||
ID: ErrInvalidCertListIssuingDP,
|
||||
Summary: "x509: failed to unmarshal certificate list issuing distribution point: %v",
|
||||
Field: "tbsCertList.crlExtensions.*.IssuingDistributionPoint",
|
||||
SpecRef: "RFC 5280 s5.2.5",
|
||||
Category: InvalidASN1Content,
|
||||
Fatal: true,
|
||||
},
|
||||
{
|
||||
ID: ErrTrailingCertListIssuingDP,
|
||||
Summary: "x509: trailing data after certificate list issuing distribution point",
|
||||
Field: "tbsCertList.crlExtensions.*.IssuingDistributionPoint",
|
||||
SpecRef: "RFC 5280 s5.2.5",
|
||||
Category: InvalidASN1Content,
|
||||
Fatal: true,
|
||||
},
|
||||
{
|
||||
ID: ErrCertListIssuingDPMultipleTypes,
|
||||
Summary: "x509: multiple cert types set in issuing-distribution-point: user:%v CA:%v attr:%v",
|
||||
Field: "tbsCertList.crlExtensions.*.IssuingDistributionPoint",
|
||||
SpecRef: "RFC 5280 s5.2.5",
|
||||
SpecText: "at most one of onlyContainsUserCerts, onlyContainsCACerts, and onlyContainsAttributeCerts may be set to TRUE.",
|
||||
Category: MalformedCRL,
|
||||
Fatal: true,
|
||||
},
|
||||
{
|
||||
ID: ErrCertListIssuingDPInvalidFullName,
|
||||
Summary: "x509: failed to parse CRL issuing-distribution-point fullName: %v",
|
||||
Field: "tbsCertList.crlExtensions.*.IssuingDistributionPoint.distributionPoint",
|
||||
SpecRef: "RFC 5280 s5.2.5",
|
||||
Category: InvalidASN1Content,
|
||||
Fatal: true,
|
||||
},
|
||||
{
|
||||
ID: ErrInvalidCertListFreshestCRL,
|
||||
Summary: "x509: failed to unmarshal certificate list freshestCRL: %v",
|
||||
Field: "tbsCertList.crlExtensions.*.FreshestCRL",
|
||||
SpecRef: "RFC 5280 s5.2.6",
|
||||
Category: InvalidASN1Content,
|
||||
Fatal: true,
|
||||
},
|
||||
{
|
||||
ID: ErrInvalidCertListAuthInfoAccess,
|
||||
Summary: "x509: failed to unmarshal certificate list authority info access: %v",
|
||||
Field: "tbsCertList.crlExtensions.*.AuthorityInfoAccess",
|
||||
SpecRef: "RFC 5280 s5.2.7",
|
||||
Category: InvalidASN1Content,
|
||||
Fatal: true,
|
||||
},
|
||||
{
|
||||
ID: ErrTrailingCertListAuthInfoAccess,
|
||||
Summary: "x509: trailing data after certificate list authority info access",
|
||||
Field: "tbsCertList.crlExtensions.*.AuthorityInfoAccess",
|
||||
SpecRef: "RFC 5280 s5.2.7",
|
||||
Category: InvalidASN1Content,
|
||||
Fatal: true,
|
||||
},
|
||||
{
|
||||
ID: ErrUnhandledCriticalCertListExtension,
|
||||
Summary: "x509: unhandled critical extension in certificate list: %v",
|
||||
Field: "tbsCertList.revokedCertificates.crlExtensions.*",
|
||||
SpecRef: "RFC 5280 s5.2",
|
||||
SpecText: "If a CRL contains a critical extension that the application cannot process, then the application MUST NOT use that CRL to determine the status of certificates.",
|
||||
Category: MalformedCRL,
|
||||
Fatal: true,
|
||||
},
|
||||
|
||||
{
|
||||
ID: ErrUnexpectedlyCriticalRevokedCertExtension,
|
||||
Summary: "x509: revoked certificate extension %v marked critical but expected to be non-critical",
|
||||
Field: "tbsCertList.revokedCertificates.crlEntryExtensions.*.critical",
|
||||
SpecRef: "RFC 5280 s5.3",
|
||||
Category: MalformedCRL,
|
||||
},
|
||||
{
|
||||
ID: ErrUnexpectedlyNonCriticalRevokedCertExtension,
|
||||
Summary: "x509: revoked certificate extension %v marked non-critical but expected to be critical",
|
||||
Field: "tbsCertList.revokedCertificates.crlEntryExtensions.*.critical",
|
||||
SpecRef: "RFC 5280 s5.3",
|
||||
Category: MalformedCRL,
|
||||
},
|
||||
|
||||
{
|
||||
ID: ErrInvalidRevocationReason,
|
||||
Summary: "x509: failed to parse revocation reason: %v",
|
||||
Field: "tbsCertList.revokedCertificates.crlEntryExtensions.*.CRLReason",
|
||||
SpecRef: "RFC 5280 s5.3.1",
|
||||
Category: InvalidASN1Content,
|
||||
Fatal: true,
|
||||
},
|
||||
{
|
||||
ID: ErrTrailingRevocationReason,
|
||||
Summary: "x509: trailing data after revoked certificate reason",
|
||||
Field: "tbsCertList.revokedCertificates.crlEntryExtensions.*.CRLReason",
|
||||
SpecRef: "RFC 5280 s5.3.1",
|
||||
Category: InvalidASN1Content,
|
||||
Fatal: true,
|
||||
},
|
||||
{
|
||||
ID: ErrInvalidRevocationInvalidityDate,
|
||||
Summary: "x509: failed to parse revoked certificate invalidity date: %v",
|
||||
Field: "tbsCertList.revokedCertificates.crlEntryExtensions.*.InvalidityDate",
|
||||
SpecRef: "RFC 5280 s5.3.2",
|
||||
Category: InvalidASN1Content,
|
||||
Fatal: true,
|
||||
},
|
||||
{
|
||||
ID: ErrTrailingRevocationInvalidityDate,
|
||||
Summary: "x509: trailing data after revoked certificate invalidity date",
|
||||
Field: "tbsCertList.revokedCertificates.crlEntryExtensions.*.InvalidityDate",
|
||||
SpecRef: "RFC 5280 s5.3.2",
|
||||
Category: InvalidASN1Content,
|
||||
Fatal: true,
|
||||
},
|
||||
{
|
||||
ID: ErrInvalidRevocationIssuer,
|
||||
Summary: "x509: failed to parse revocation issuer %v",
|
||||
Field: "tbsCertList.revokedCertificates.crlEntryExtensions.*.CertificateIssuer",
|
||||
SpecRef: "RFC 5280 s5.3.3",
|
||||
Category: InvalidASN1Content,
|
||||
Fatal: true,
|
||||
},
|
||||
{
|
||||
ID: ErrUnhandledCriticalRevokedCertExtension,
|
||||
Summary: "x509: unhandled critical extension in revoked certificate: %v",
|
||||
Field: "tbsCertList.revokedCertificates.crlEntryExtensions.*",
|
||||
SpecRef: "RFC 5280 s5.3",
|
||||
SpecText: "If a CRL contains a critical CRL entry extension that the application cannot process, then the application MUST NOT use that CRL to determine the status of any certificates.",
|
||||
Category: MalformedCRL,
|
||||
Fatal: true,
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
idToError = make(map[ErrorID]Error, len(errorInfo))
|
||||
for _, info := range errorInfo {
|
||||
idToError[info.ID] = info
|
||||
}
|
||||
}
|
||||
|
||||
// NewError builds a new x509.Error based on the template for the given id.
|
||||
func NewError(id ErrorID, args ...interface{}) Error {
|
||||
var err Error
|
||||
if id >= ErrMaxID {
|
||||
err.ID = id
|
||||
err.Summary = fmt.Sprintf("Unknown error ID %v: args %+v", id, args)
|
||||
err.Fatal = true
|
||||
} else {
|
||||
err = idToError[id]
|
||||
err.Summary = fmt.Sprintf(err.Summary, args...)
|
||||
}
|
||||
return err
|
||||
}
|
13
vendor/github.com/google/certificate-transparency-go/x509/errors_test.go
generated
vendored
Normal file
13
vendor/github.com/google/certificate-transparency-go/x509/errors_test.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
package x509
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTemplateIDs(t *testing.T) {
|
||||
for id, template := range idToError {
|
||||
if template.ID != id {
|
||||
t.Errorf("idToError[%v].id=%v; want %v", id, template.ID, id)
|
||||
}
|
||||
}
|
||||
}
|
135
vendor/github.com/google/certificate-transparency-go/x509/example_test.go
generated
vendored
Normal file
135
vendor/github.com/google/certificate-transparency-go/x509/example_test.go
generated
vendored
Normal file
@ -0,0 +1,135 @@
|
||||
// Copyright 2014 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 x509_test
|
||||
|
||||
import (
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
)
|
||||
|
||||
func ExampleCertificate_Verify() {
|
||||
// Verifying with a custom list of root certificates.
|
||||
|
||||
const rootPEM = `
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEBDCCAuygAwIBAgIDAjppMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT
|
||||
MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i
|
||||
YWwgQ0EwHhcNMTMwNDA1MTUxNTU1WhcNMTUwNDA0MTUxNTU1WjBJMQswCQYDVQQG
|
||||
EwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzElMCMGA1UEAxMcR29vZ2xlIEludGVy
|
||||
bmV0IEF1dGhvcml0eSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
|
||||
AJwqBHdc2FCROgajguDYUEi8iT/xGXAaiEZ+4I/F8YnOIe5a/mENtzJEiaB0C1NP
|
||||
VaTOgmKV7utZX8bhBYASxF6UP7xbSDj0U/ck5vuR6RXEz/RTDfRK/J9U3n2+oGtv
|
||||
h8DQUB8oMANA2ghzUWx//zo8pzcGjr1LEQTrfSTe5vn8MXH7lNVg8y5Kr0LSy+rE
|
||||
ahqyzFPdFUuLH8gZYR/Nnag+YyuENWllhMgZxUYi+FOVvuOAShDGKuy6lyARxzmZ
|
||||
EASg8GF6lSWMTlJ14rbtCMoU/M4iarNOz0YDl5cDfsCx3nuvRTPPuj5xt970JSXC
|
||||
DTWJnZ37DhF5iR43xa+OcmkCAwEAAaOB+zCB+DAfBgNVHSMEGDAWgBTAephojYn7
|
||||
qwVkDBF9qn1luMrMTjAdBgNVHQ4EFgQUSt0GFhu89mi1dvWBtrtiGrpagS8wEgYD
|
||||
VR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYwOgYDVR0fBDMwMTAvoC2g
|
||||
K4YpaHR0cDovL2NybC5nZW90cnVzdC5jb20vY3Jscy9ndGdsb2JhbC5jcmwwPQYI
|
||||
KwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwOi8vZ3RnbG9iYWwtb2NzcC5n
|
||||
ZW90cnVzdC5jb20wFwYDVR0gBBAwDjAMBgorBgEEAdZ5AgUBMA0GCSqGSIb3DQEB
|
||||
BQUAA4IBAQA21waAESetKhSbOHezI6B1WLuxfoNCunLaHtiONgaX4PCVOzf9G0JY
|
||||
/iLIa704XtE7JW4S615ndkZAkNoUyHgN7ZVm2o6Gb4ChulYylYbc3GrKBIxbf/a/
|
||||
zG+FA1jDaFETzf3I93k9mTXwVqO94FntT0QJo544evZG0R0SnU++0ED8Vf4GXjza
|
||||
HFa9llF7b1cq26KqltyMdMKVvvBulRP/F/A8rLIQjcxz++iPAsbw+zOzlTvjwsto
|
||||
WHPbqCRiOwY1nQ2pM714A5AuTHhdUDqB1O6gyHA43LL5Z/qHQF1hwFGPa4NrzQU6
|
||||
yuGnBXj8ytqU0CwIPX4WecigUCAkVDNx
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
const certPEM = `
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDujCCAqKgAwIBAgIIE31FZVaPXTUwDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE
|
||||
BhMCVVMxEzARBgNVBAoTCkdvb2dsZSBJbmMxJTAjBgNVBAMTHEdvb2dsZSBJbnRl
|
||||
cm5ldCBBdXRob3JpdHkgRzIwHhcNMTQwMTI5MTMyNzQzWhcNMTQwNTI5MDAwMDAw
|
||||
WjBpMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwN
|
||||
TW91bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzEYMBYGA1UEAwwPbWFp
|
||||
bC5nb29nbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfRrObuSW5T7q
|
||||
5CnSEqefEmtH4CCv6+5EckuriNr1CjfVvqzwfAhopXkLrq45EQm8vkmf7W96XJhC
|
||||
7ZM0dYi1/qOCAU8wggFLMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAa
|
||||
BgNVHREEEzARgg9tYWlsLmdvb2dsZS5jb20wCwYDVR0PBAQDAgeAMGgGCCsGAQUF
|
||||
BwEBBFwwWjArBggrBgEFBQcwAoYfaHR0cDovL3BraS5nb29nbGUuY29tL0dJQUcy
|
||||
LmNydDArBggrBgEFBQcwAYYfaHR0cDovL2NsaWVudHMxLmdvb2dsZS5jb20vb2Nz
|
||||
cDAdBgNVHQ4EFgQUiJxtimAuTfwb+aUtBn5UYKreKvMwDAYDVR0TAQH/BAIwADAf
|
||||
BgNVHSMEGDAWgBRK3QYWG7z2aLV29YG2u2IaulqBLzAXBgNVHSAEEDAOMAwGCisG
|
||||
AQQB1nkCBQEwMAYDVR0fBCkwJzAloCOgIYYfaHR0cDovL3BraS5nb29nbGUuY29t
|
||||
L0dJQUcyLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAH6RYHxHdcGpMpFE3oxDoFnP+
|
||||
gtuBCHan2yE2GRbJ2Cw8Lw0MmuKqHlf9RSeYfd3BXeKkj1qO6TVKwCh+0HdZk283
|
||||
TZZyzmEOyclm3UGFYe82P/iDFt+CeQ3NpmBg+GoaVCuWAARJN/KfglbLyyYygcQq
|
||||
0SgeDh8dRKUiaW3HQSoYvTvdTuqzwK4CXsr3b5/dAOY8uMuG/IAR3FgwTbZ1dtoW
|
||||
RvOTa8hYiU6A475WuZKyEHcwnGYe57u2I2KbMgcKjPniocj4QzgYsVAVKW3IwaOh
|
||||
yE+vPxsiUkvQHdO2fojCkY8jg70jxM+gu59tPDNbw3Uh/2Ij310FgTHsnGQMyA==
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
// First, create the set of root certificates. For this example we only
|
||||
// have one. It's also possible to omit this in order to use the
|
||||
// default root set of the current operating system.
|
||||
roots := x509.NewCertPool()
|
||||
ok := roots.AppendCertsFromPEM([]byte(rootPEM))
|
||||
if !ok {
|
||||
panic("failed to parse root certificate")
|
||||
}
|
||||
|
||||
block, _ := pem.Decode([]byte(certPEM))
|
||||
if block == nil {
|
||||
panic("failed to parse certificate PEM")
|
||||
}
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
panic("failed to parse certificate: " + err.Error())
|
||||
}
|
||||
|
||||
opts := x509.VerifyOptions{
|
||||
DNSName: "mail.google.com",
|
||||
Roots: roots,
|
||||
}
|
||||
|
||||
if _, err := cert.Verify(opts); err != nil {
|
||||
panic("failed to verify certificate: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleParsePKIXPublicKey() {
|
||||
const pubPEM = `
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlRuRnThUjU8/prwYxbty
|
||||
WPT9pURI3lbsKMiB6Fn/VHOKE13p4D8xgOCADpdRagdT6n4etr9atzDKUSvpMtR3
|
||||
CP5noNc97WiNCggBjVWhs7szEe8ugyqF23XwpHQ6uV1LKH50m92MbOWfCtjU9p/x
|
||||
qhNpQQ1AZhqNy5Gevap5k8XzRmjSldNAFZMY7Yv3Gi+nyCwGwpVtBUwhuLzgNFK/
|
||||
yDtw2WcWmUU7NuC8Q6MWvPebxVtCfVp/iQU6q60yyt6aGOBkhAX0LpKAEhKidixY
|
||||
nP9PNVBvxgu3XZ4P36gZV6+ummKdBVnc3NqwBLu5+CcdRdusmHPHd5pHf4/38Z3/
|
||||
6qU2a/fPvWzceVTEgZ47QjFMTCTmCwNt29cvi7zZeQzjtwQgn4ipN9NibRH/Ax/q
|
||||
TbIzHfrJ1xa2RteWSdFjwtxi9C20HUkjXSeI4YlzQMH0fPX6KCE7aVePTOnB69I/
|
||||
a9/q96DiXZajwlpq3wFctrs1oXqBp5DVrCIj8hU2wNgB7LtQ1mCtsYz//heai0K9
|
||||
PhE4X6hiE0YmeAZjR0uHl8M/5aW9xCoJ72+12kKpWAa0SFRWLy6FejNYCYpkupVJ
|
||||
yecLk/4L1W0l6jQQZnWErXZYe0PNFcmwGXy1Rep83kfBRNKRy5tvocalLlwXLdUk
|
||||
AIU+2GKjyT3iMuzZxxFxPFMCAwEAAQ==
|
||||
-----END PUBLIC KEY-----`
|
||||
|
||||
block, _ := pem.Decode([]byte(pubPEM))
|
||||
if block == nil {
|
||||
panic("failed to parse PEM block containing the public key")
|
||||
}
|
||||
|
||||
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||
if err != nil {
|
||||
panic("failed to parse DER encoded public key: " + err.Error())
|
||||
}
|
||||
|
||||
switch pub := pub.(type) {
|
||||
case *rsa.PublicKey:
|
||||
fmt.Println("pub is of type RSA:", pub)
|
||||
case *dsa.PublicKey:
|
||||
fmt.Println("pub is of type DSA:", pub)
|
||||
case *ecdsa.PublicKey:
|
||||
fmt.Println("pub is of type ECDSA:", pub)
|
||||
default:
|
||||
panic("unknown type of public key")
|
||||
}
|
||||
}
|
2003
vendor/github.com/google/certificate-transparency-go/x509/name_constraints_test.go
generated
vendored
Normal file
2003
vendor/github.com/google/certificate-transparency-go/x509/name_constraints_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
164
vendor/github.com/google/certificate-transparency-go/x509/names.go
generated
vendored
Normal file
164
vendor/github.com/google/certificate-transparency-go/x509/names.go
generated
vendored
Normal file
@ -0,0 +1,164 @@
|
||||
// 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.
|
||||
|
||||
package x509
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/google/certificate-transparency-go/asn1"
|
||||
"github.com/google/certificate-transparency-go/x509/pkix"
|
||||
)
|
||||
|
||||
const (
|
||||
// GeneralName tag values from RFC 5280, 4.2.1.6
|
||||
tagOtherName = 0
|
||||
tagRFC822Name = 1
|
||||
tagDNSName = 2
|
||||
tagX400Address = 3
|
||||
tagDirectoryName = 4
|
||||
tagEDIPartyName = 5
|
||||
tagURI = 6
|
||||
tagIPAddress = 7
|
||||
tagRegisteredID = 8
|
||||
)
|
||||
|
||||
// OtherName describes a name related to a certificate which is not in one
|
||||
// of the standard name formats. RFC 5280, 4.2.1.6:
|
||||
// OtherName ::= SEQUENCE {
|
||||
// type-id OBJECT IDENTIFIER,
|
||||
// value [0] EXPLICIT ANY DEFINED BY type-id }
|
||||
type OtherName struct {
|
||||
TypeID asn1.ObjectIdentifier
|
||||
Value asn1.RawValue
|
||||
}
|
||||
|
||||
// GeneralNames holds a collection of names related to a certificate.
|
||||
type GeneralNames struct {
|
||||
DNSNames []string
|
||||
EmailAddresses []string
|
||||
DirectoryNames []pkix.Name
|
||||
URIs []string
|
||||
IPNets []net.IPNet
|
||||
RegisteredIDs []asn1.ObjectIdentifier
|
||||
OtherNames []OtherName
|
||||
}
|
||||
|
||||
// Len returns the total number of names in a GeneralNames object.
|
||||
func (gn GeneralNames) Len() int {
|
||||
return (len(gn.DNSNames) + len(gn.EmailAddresses) + len(gn.DirectoryNames) +
|
||||
len(gn.URIs) + len(gn.IPNets) + len(gn.RegisteredIDs) + len(gn.OtherNames))
|
||||
}
|
||||
|
||||
// Empty indicates whether a GeneralNames object is empty.
|
||||
func (gn GeneralNames) Empty() bool {
|
||||
return gn.Len() == 0
|
||||
}
|
||||
|
||||
func parseGeneralNames(value []byte, gname *GeneralNames) error {
|
||||
// RFC 5280, 4.2.1.6
|
||||
// GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
|
||||
//
|
||||
// GeneralName ::= CHOICE {
|
||||
// otherName [0] OtherName,
|
||||
// rfc822Name [1] IA5String,
|
||||
// dNSName [2] IA5String,
|
||||
// x400Address [3] ORAddress,
|
||||
// directoryName [4] Name,
|
||||
// ediPartyName [5] EDIPartyName,
|
||||
// uniformResourceIdentifier [6] IA5String,
|
||||
// iPAddress [7] OCTET STRING,
|
||||
// registeredID [8] OBJECT IDENTIFIER }
|
||||
var seq asn1.RawValue
|
||||
var rest []byte
|
||||
if rest, err := asn1.Unmarshal(value, &seq); err != nil {
|
||||
return fmt.Errorf("x509: failed to parse GeneralNames: %v", err)
|
||||
} else if len(rest) != 0 {
|
||||
return fmt.Errorf("x509: trailing data after GeneralNames")
|
||||
}
|
||||
if !seq.IsCompound || seq.Tag != asn1.TagSequence || seq.Class != asn1.ClassUniversal {
|
||||
return fmt.Errorf("x509: failed to parse GeneralNames sequence, tag %+v", seq)
|
||||
}
|
||||
|
||||
rest = seq.Bytes
|
||||
for len(rest) > 0 {
|
||||
var err error
|
||||
rest, err = parseGeneralName(rest, gname, false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("x509: failed to parse GeneralName: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseGeneralName(data []byte, gname *GeneralNames, withMask bool) ([]byte, error) {
|
||||
var v asn1.RawValue
|
||||
var rest []byte
|
||||
var err error
|
||||
rest, err = asn1.Unmarshal(data, &v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("x509: failed to unmarshal GeneralNames: %v", err)
|
||||
}
|
||||
switch v.Tag {
|
||||
case tagOtherName:
|
||||
if !v.IsCompound {
|
||||
return nil, fmt.Errorf("x509: failed to unmarshal GeneralNames.otherName: not compound")
|
||||
}
|
||||
var other OtherName
|
||||
v.FullBytes = append([]byte{}, v.FullBytes...)
|
||||
v.FullBytes[0] = asn1.TagSequence | 0x20
|
||||
_, err = asn1.Unmarshal(v.FullBytes, &other)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("x509: failed to unmarshal GeneralNames.otherName: %v", err)
|
||||
}
|
||||
gname.OtherNames = append(gname.OtherNames, other)
|
||||
case tagRFC822Name:
|
||||
gname.EmailAddresses = append(gname.EmailAddresses, string(v.Bytes))
|
||||
case tagDNSName:
|
||||
dns := string(v.Bytes)
|
||||
gname.DNSNames = append(gname.DNSNames, dns)
|
||||
case tagDirectoryName:
|
||||
var rdnSeq pkix.RDNSequence
|
||||
if _, err := asn1.Unmarshal(v.Bytes, &rdnSeq); err != nil {
|
||||
return nil, fmt.Errorf("x509: failed to unmarshal GeneralNames.directoryName: %v", err)
|
||||
}
|
||||
var dirName pkix.Name
|
||||
dirName.FillFromRDNSequence(&rdnSeq)
|
||||
gname.DirectoryNames = append(gname.DirectoryNames, dirName)
|
||||
case tagURI:
|
||||
gname.URIs = append(gname.URIs, string(v.Bytes))
|
||||
case tagIPAddress:
|
||||
vlen := len(v.Bytes)
|
||||
if withMask {
|
||||
switch vlen {
|
||||
case (2 * net.IPv4len), (2 * net.IPv6len):
|
||||
ipNet := net.IPNet{IP: v.Bytes[0 : vlen/2], Mask: v.Bytes[vlen/2:]}
|
||||
gname.IPNets = append(gname.IPNets, ipNet)
|
||||
default:
|
||||
return nil, fmt.Errorf("x509: invalid IP/mask length %d in GeneralNames.iPAddress", vlen)
|
||||
}
|
||||
} else {
|
||||
switch vlen {
|
||||
case net.IPv4len, net.IPv6len:
|
||||
ipNet := net.IPNet{IP: v.Bytes}
|
||||
gname.IPNets = append(gname.IPNets, ipNet)
|
||||
default:
|
||||
return nil, fmt.Errorf("x509: invalid IP length %d in GeneralNames.iPAddress", vlen)
|
||||
}
|
||||
}
|
||||
case tagRegisteredID:
|
||||
var oid asn1.ObjectIdentifier
|
||||
v.FullBytes = append([]byte{}, v.FullBytes...)
|
||||
v.FullBytes[0] = asn1.TagOID
|
||||
_, err = asn1.Unmarshal(v.FullBytes, &oid)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("x509: failed to unmarshal GeneralNames.registeredID: %v", err)
|
||||
}
|
||||
gname.RegisteredIDs = append(gname.RegisteredIDs, oid)
|
||||
default:
|
||||
return nil, fmt.Errorf("x509: failed to unmarshal GeneralName: unknown tag %d", v.Tag)
|
||||
}
|
||||
return rest, nil
|
||||
}
|
267
vendor/github.com/google/certificate-transparency-go/x509/names_test.go
generated
vendored
Normal file
267
vendor/github.com/google/certificate-transparency-go/x509/names_test.go
generated
vendored
Normal file
@ -0,0 +1,267 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package x509
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"net"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/certificate-transparency-go/asn1"
|
||||
"github.com/google/certificate-transparency-go/x509/pkix"
|
||||
)
|
||||
|
||||
func TestParseGeneralNames(t *testing.T) {
|
||||
var tests = []struct {
|
||||
data string // as hex
|
||||
want GeneralNames
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
data: ("3012" +
|
||||
("8210" + "7777772e676f6f676c652e636f2e756b")),
|
||||
want: GeneralNames{
|
||||
DNSNames: []string{"www.google.co.uk"},
|
||||
},
|
||||
},
|
||||
{
|
||||
data: ("3024" +
|
||||
("8210" + "7777772e676f6f676c652e636f2e756b") +
|
||||
("8610" + "7777772e676f6f676c652e636f2e756b")),
|
||||
want: GeneralNames{
|
||||
DNSNames: []string{"www.google.co.uk"},
|
||||
URIs: []string{"www.google.co.uk"},
|
||||
},
|
||||
},
|
||||
{
|
||||
data: "0a0101",
|
||||
wantErr: "failed to parse GeneralNames sequence",
|
||||
},
|
||||
{
|
||||
data: "0a",
|
||||
wantErr: "failed to parse GeneralNames:",
|
||||
},
|
||||
{
|
||||
data: "03000a0101",
|
||||
wantErr: "trailing data",
|
||||
},
|
||||
{
|
||||
data: ("3005" + ("8703" + "010203")),
|
||||
wantErr: "invalid IP length",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
inData := fromHex(test.data)
|
||||
var got GeneralNames
|
||||
err := parseGeneralNames(inData, &got)
|
||||
if err != nil {
|
||||
if test.wantErr == "" {
|
||||
t.Errorf("parseGeneralNames(%s)=%v; want nil", test.data, err)
|
||||
} else if !strings.Contains(err.Error(), test.wantErr) {
|
||||
t.Errorf("parseGeneralNames(%s)=%v; want %q", test.data, err, test.wantErr)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if test.wantErr != "" {
|
||||
t.Errorf("parseGeneralNames(%s)=%+v,nil; want %q", test.data, got, test.wantErr)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(got, test.want) {
|
||||
t.Errorf("parseGeneralNames(%s)=%+v; want %+v", test.data, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseGeneralName(t *testing.T) {
|
||||
var tests = []struct {
|
||||
data string // as hex
|
||||
withMask bool
|
||||
want GeneralNames
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
data: ("a008" +
|
||||
("0603" + "551d0e") + // OID: subject-key-id
|
||||
("0a01" + "01")), // enum=1
|
||||
want: GeneralNames{
|
||||
OtherNames: []OtherName{
|
||||
{
|
||||
TypeID: OIDExtensionSubjectKeyId,
|
||||
Value: asn1.RawValue{
|
||||
Class: asn1.ClassUniversal,
|
||||
Tag: asn1.TagEnum,
|
||||
IsCompound: false,
|
||||
Bytes: fromHex("01"),
|
||||
FullBytes: fromHex("0a0101"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
data: ("8008" +
|
||||
("0603" + "551d0e") + // OID: subject-key-id
|
||||
("0a01" + "01")), // enum=1
|
||||
wantErr: "not compound",
|
||||
},
|
||||
{
|
||||
data: ("a005" +
|
||||
("0603" + "551d0e")), // OID: subject-key-id
|
||||
wantErr: "sequence truncated",
|
||||
},
|
||||
{
|
||||
data: ("8110" + "77777740676f6f676c652e636f2e756b"),
|
||||
want: GeneralNames{
|
||||
EmailAddresses: []string{"www@google.co.uk"},
|
||||
},
|
||||
},
|
||||
{
|
||||
data: ("8210" + "7777772e676f6f676c652e636f2e756b"),
|
||||
want: GeneralNames{
|
||||
DNSNames: []string{"www.google.co.uk"},
|
||||
},
|
||||
},
|
||||
{
|
||||
data: ("844b" +
|
||||
("3049" +
|
||||
("310b" +
|
||||
("3009" +
|
||||
("0603" + "550406") +
|
||||
("1302" + "5553"))) + // "US"
|
||||
("3113" +
|
||||
("3011" +
|
||||
("0603" + "55040a") +
|
||||
("130a" + "476f6f676c6520496e63"))) + // "Google Inc"
|
||||
("3125" +
|
||||
("3023" +
|
||||
("0603" + "550403") +
|
||||
("131c" + "476f6f676c6520496e7465726e657420417574686f72697479204732"))))), // "GoogleInternet Authority G2"
|
||||
want: GeneralNames{
|
||||
DirectoryNames: []pkix.Name{
|
||||
{
|
||||
Country: []string{"US"},
|
||||
Organization: []string{"Google Inc"},
|
||||
CommonName: "Google Internet Authority G2",
|
||||
Names: []pkix.AttributeTypeAndValue{
|
||||
{Type: pkix.OIDCountry, Value: "US"},
|
||||
{Type: pkix.OIDOrganization, Value: "Google Inc"},
|
||||
{Type: pkix.OIDCommonName, Value: "Google Internet Authority G2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
data: ("8410" + "7777772e676f6f676c652e636f2e756b"),
|
||||
wantErr: "failed to unmarshal GeneralNames.directoryName",
|
||||
},
|
||||
{
|
||||
data: ("8610" + "7777772e676f6f676c652e636f2e756b"),
|
||||
want: GeneralNames{
|
||||
URIs: []string{"www.google.co.uk"},
|
||||
},
|
||||
},
|
||||
{
|
||||
data: ("8704" + "01020304"),
|
||||
want: GeneralNames{
|
||||
IPNets: []net.IPNet{{IP: net.IP{1, 2, 3, 4}}},
|
||||
},
|
||||
},
|
||||
{
|
||||
data: ("8708" + "01020304ffffff00"),
|
||||
withMask: true,
|
||||
want: GeneralNames{
|
||||
IPNets: []net.IPNet{{IP: net.IP{1, 2, 3, 4}, Mask: net.IPMask{0xff, 0xff, 0xff, 0x00}}},
|
||||
},
|
||||
},
|
||||
{
|
||||
data: ("8710" + "01020304111213142122232431323334"),
|
||||
want: GeneralNames{
|
||||
IPNets: []net.IPNet{{IP: net.IP{1, 2, 3, 4, 0x11, 0x12, 0x13, 0x14, 0x21, 0x22, 0x23, 0x24, 0x31, 0x32, 0x33, 0x34}}},
|
||||
},
|
||||
},
|
||||
{
|
||||
data: ("8703" + "010203"),
|
||||
wantErr: "invalid IP length",
|
||||
},
|
||||
{
|
||||
data: ("8707" + "01020304ffffff"),
|
||||
withMask: true,
|
||||
wantErr: "invalid IP/mask length",
|
||||
},
|
||||
{
|
||||
data: ("8803" + "551d0e"), // OID: subject-key-id
|
||||
want: GeneralNames{
|
||||
RegisteredIDs: []asn1.ObjectIdentifier{OIDExtensionSubjectKeyId},
|
||||
},
|
||||
},
|
||||
{
|
||||
data: ("8803" + "551d8e"),
|
||||
wantErr: "syntax error",
|
||||
},
|
||||
{
|
||||
data: ("9003" + "551d8e"),
|
||||
wantErr: "unknown tag",
|
||||
},
|
||||
{
|
||||
data: ("8803"),
|
||||
wantErr: "data truncated",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
inData := fromHex(test.data)
|
||||
var got GeneralNames
|
||||
_, err := parseGeneralName(inData, &got, test.withMask)
|
||||
if err != nil {
|
||||
if test.wantErr == "" {
|
||||
t.Errorf("parseGeneralName(%s)=%v; want nil", test.data, err)
|
||||
} else if !strings.Contains(err.Error(), test.wantErr) {
|
||||
t.Errorf("parseGeneralName(%s)=%v; want %q", test.data, err, test.wantErr)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if test.wantErr != "" {
|
||||
t.Errorf("parseGeneralName(%s)=%+v,nil; want %q", test.data, got, test.wantErr)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(got, test.want) {
|
||||
t.Errorf("parseGeneralName(%s)=%+v; want %+v", test.data, got, test.want)
|
||||
}
|
||||
if got.Empty() {
|
||||
t.Errorf("parseGeneralName(%s).Empty(%+v)=true; want false", test.data, got)
|
||||
}
|
||||
if gotLen, wantLen := got.Len(), 1; gotLen != wantLen {
|
||||
t.Errorf("parseGeneralName(%s).Len(%+v)=%d; want %d", test.data, got, gotLen, wantLen)
|
||||
}
|
||||
if !bytes.Equal(inData, fromHex(test.data)) {
|
||||
t.Errorf("parseGeneralName(%s) modified data to %x", test.data, inData)
|
||||
}
|
||||
|
||||
// Wrap the GeneralName up in a SEQUENCE and check that we get the same result using parseGeneralNames.
|
||||
if test.withMask {
|
||||
continue
|
||||
}
|
||||
seqData := append([]byte{0x30, byte(len(inData))}, inData...)
|
||||
var gotSeq GeneralNames
|
||||
err = parseGeneralNames(seqData, &gotSeq)
|
||||
if err != nil {
|
||||
t.Errorf("parseGeneralNames(%x)=%v; want nil", seqData, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(gotSeq, test.want) {
|
||||
t.Errorf("parseGeneralNames(%x)=%+v; want %+v", seqData, gotSeq, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fromHex(s string) []byte {
|
||||
d, _ := hex.DecodeString(s)
|
||||
return d
|
||||
}
|
26
vendor/github.com/google/certificate-transparency-go/x509/nilref_nil_darwin.go
generated
vendored
Normal file
26
vendor/github.com/google/certificate-transparency-go/x509/nilref_nil_darwin.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2018 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 cgo,!arm,!arm64,!ios,!go1.10
|
||||
|
||||
package x509
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1080
|
||||
#cgo LDFLAGS: -framework CoreFoundation -framework Security
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// For Go versions before 1.10, nil values for Apple's CoreFoundation
|
||||
// CF*Ref types were represented by nil. See:
|
||||
// https://github.com/golang/go/commit/b868616b63a8
|
||||
func setNilCFRef(v *C.CFDataRef) {
|
||||
*v = nil
|
||||
}
|
||||
|
||||
func isNilCFRef(v C.CFDataRef) bool {
|
||||
return v == nil
|
||||
}
|
26
vendor/github.com/google/certificate-transparency-go/x509/nilref_zero_darwin.go
generated
vendored
Normal file
26
vendor/github.com/google/certificate-transparency-go/x509/nilref_zero_darwin.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2018 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 cgo,!arm,!arm64,!ios,go1.10
|
||||
|
||||
package x509
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1080
|
||||
#cgo LDFLAGS: -framework CoreFoundation -framework Security
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// For Go versions >= 1.10, nil values for Apple's CoreFoundation
|
||||
// CF*Ref types are represented by zero. See:
|
||||
// https://github.com/golang/go/commit/b868616b63a8
|
||||
func setNilCFRef(v *C.CFDataRef) {
|
||||
*v = 0
|
||||
}
|
||||
|
||||
func isNilCFRef(v C.CFDataRef) bool {
|
||||
return v == 0
|
||||
}
|
240
vendor/github.com/google/certificate-transparency-go/x509/pem_decrypt.go
generated
vendored
Normal file
240
vendor/github.com/google/certificate-transparency-go/x509/pem_decrypt.go
generated
vendored
Normal file
@ -0,0 +1,240 @@
|
||||
// Copyright 2012 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 x509
|
||||
|
||||
// RFC 1423 describes the encryption of PEM blocks. The algorithm used to
|
||||
// generate a key from the password was derived by looking at the OpenSSL
|
||||
// implementation.
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type PEMCipher int
|
||||
|
||||
// Possible values for the EncryptPEMBlock encryption algorithm.
|
||||
const (
|
||||
_ PEMCipher = iota
|
||||
PEMCipherDES
|
||||
PEMCipher3DES
|
||||
PEMCipherAES128
|
||||
PEMCipherAES192
|
||||
PEMCipherAES256
|
||||
)
|
||||
|
||||
// rfc1423Algo holds a method for enciphering a PEM block.
|
||||
type rfc1423Algo struct {
|
||||
cipher PEMCipher
|
||||
name string
|
||||
cipherFunc func(key []byte) (cipher.Block, error)
|
||||
keySize int
|
||||
blockSize int
|
||||
}
|
||||
|
||||
// rfc1423Algos holds a slice of the possible ways to encrypt a PEM
|
||||
// block. The ivSize numbers were taken from the OpenSSL source.
|
||||
var rfc1423Algos = []rfc1423Algo{{
|
||||
cipher: PEMCipherDES,
|
||||
name: "DES-CBC",
|
||||
cipherFunc: des.NewCipher,
|
||||
keySize: 8,
|
||||
blockSize: des.BlockSize,
|
||||
}, {
|
||||
cipher: PEMCipher3DES,
|
||||
name: "DES-EDE3-CBC",
|
||||
cipherFunc: des.NewTripleDESCipher,
|
||||
keySize: 24,
|
||||
blockSize: des.BlockSize,
|
||||
}, {
|
||||
cipher: PEMCipherAES128,
|
||||
name: "AES-128-CBC",
|
||||
cipherFunc: aes.NewCipher,
|
||||
keySize: 16,
|
||||
blockSize: aes.BlockSize,
|
||||
}, {
|
||||
cipher: PEMCipherAES192,
|
||||
name: "AES-192-CBC",
|
||||
cipherFunc: aes.NewCipher,
|
||||
keySize: 24,
|
||||
blockSize: aes.BlockSize,
|
||||
}, {
|
||||
cipher: PEMCipherAES256,
|
||||
name: "AES-256-CBC",
|
||||
cipherFunc: aes.NewCipher,
|
||||
keySize: 32,
|
||||
blockSize: aes.BlockSize,
|
||||
},
|
||||
}
|
||||
|
||||
// deriveKey uses a key derivation function to stretch the password into a key
|
||||
// with the number of bits our cipher requires. This algorithm was derived from
|
||||
// the OpenSSL source.
|
||||
func (c rfc1423Algo) deriveKey(password, salt []byte) []byte {
|
||||
hash := md5.New()
|
||||
out := make([]byte, c.keySize)
|
||||
var digest []byte
|
||||
|
||||
for i := 0; i < len(out); i += len(digest) {
|
||||
hash.Reset()
|
||||
hash.Write(digest)
|
||||
hash.Write(password)
|
||||
hash.Write(salt)
|
||||
digest = hash.Sum(digest[:0])
|
||||
copy(out[i:], digest)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// IsEncryptedPEMBlock returns if the PEM block is password encrypted.
|
||||
func IsEncryptedPEMBlock(b *pem.Block) bool {
|
||||
_, ok := b.Headers["DEK-Info"]
|
||||
return ok
|
||||
}
|
||||
|
||||
// IncorrectPasswordError is returned when an incorrect password is detected.
|
||||
var IncorrectPasswordError = errors.New("x509: decryption password incorrect")
|
||||
|
||||
// DecryptPEMBlock takes a password encrypted PEM block and the password used to
|
||||
// encrypt it and returns a slice of decrypted DER encoded bytes. It inspects
|
||||
// the DEK-Info header to determine the algorithm used for decryption. If no
|
||||
// DEK-Info header is present, an error is returned. If an incorrect password
|
||||
// is detected an IncorrectPasswordError is returned. Because of deficiencies
|
||||
// in the encrypted-PEM format, it's not always possible to detect an incorrect
|
||||
// password. In these cases no error will be returned but the decrypted DER
|
||||
// bytes will be random noise.
|
||||
func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error) {
|
||||
dek, ok := b.Headers["DEK-Info"]
|
||||
if !ok {
|
||||
return nil, errors.New("x509: no DEK-Info header in block")
|
||||
}
|
||||
|
||||
idx := strings.Index(dek, ",")
|
||||
if idx == -1 {
|
||||
return nil, errors.New("x509: malformed DEK-Info header")
|
||||
}
|
||||
|
||||
mode, hexIV := dek[:idx], dek[idx+1:]
|
||||
ciph := cipherByName(mode)
|
||||
if ciph == nil {
|
||||
return nil, errors.New("x509: unknown encryption mode")
|
||||
}
|
||||
iv, err := hex.DecodeString(hexIV)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(iv) != ciph.blockSize {
|
||||
return nil, errors.New("x509: incorrect IV size")
|
||||
}
|
||||
|
||||
// Based on the OpenSSL implementation. The salt is the first 8 bytes
|
||||
// of the initialization vector.
|
||||
key := ciph.deriveKey(password, iv[:8])
|
||||
block, err := ciph.cipherFunc(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(b.Bytes)%block.BlockSize() != 0 {
|
||||
return nil, errors.New("x509: encrypted PEM data is not a multiple of the block size")
|
||||
}
|
||||
|
||||
data := make([]byte, len(b.Bytes))
|
||||
dec := cipher.NewCBCDecrypter(block, iv)
|
||||
dec.CryptBlocks(data, b.Bytes)
|
||||
|
||||
// Blocks are padded using a scheme where the last n bytes of padding are all
|
||||
// equal to n. It can pad from 1 to blocksize bytes inclusive. See RFC 1423.
|
||||
// For example:
|
||||
// [x y z 2 2]
|
||||
// [x y 7 7 7 7 7 7 7]
|
||||
// If we detect a bad padding, we assume it is an invalid password.
|
||||
dlen := len(data)
|
||||
if dlen == 0 || dlen%ciph.blockSize != 0 {
|
||||
return nil, errors.New("x509: invalid padding")
|
||||
}
|
||||
last := int(data[dlen-1])
|
||||
if dlen < last {
|
||||
return nil, IncorrectPasswordError
|
||||
}
|
||||
if last == 0 || last > ciph.blockSize {
|
||||
return nil, IncorrectPasswordError
|
||||
}
|
||||
for _, val := range data[dlen-last:] {
|
||||
if int(val) != last {
|
||||
return nil, IncorrectPasswordError
|
||||
}
|
||||
}
|
||||
return data[:dlen-last], nil
|
||||
}
|
||||
|
||||
// EncryptPEMBlock returns a PEM block of the specified type holding the
|
||||
// given DER-encoded data encrypted with the specified algorithm and
|
||||
// password.
|
||||
func EncryptPEMBlock(rand io.Reader, blockType string, data, password []byte, alg PEMCipher) (*pem.Block, error) {
|
||||
ciph := cipherByKey(alg)
|
||||
if ciph == nil {
|
||||
return nil, errors.New("x509: unknown encryption mode")
|
||||
}
|
||||
iv := make([]byte, ciph.blockSize)
|
||||
if _, err := io.ReadFull(rand, iv); err != nil {
|
||||
return nil, errors.New("x509: cannot generate IV: " + err.Error())
|
||||
}
|
||||
// The salt is the first 8 bytes of the initialization vector,
|
||||
// matching the key derivation in DecryptPEMBlock.
|
||||
key := ciph.deriveKey(password, iv[:8])
|
||||
block, err := ciph.cipherFunc(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
enc := cipher.NewCBCEncrypter(block, iv)
|
||||
pad := ciph.blockSize - len(data)%ciph.blockSize
|
||||
encrypted := make([]byte, len(data), len(data)+pad)
|
||||
// We could save this copy by encrypting all the whole blocks in
|
||||
// the data separately, but it doesn't seem worth the additional
|
||||
// code.
|
||||
copy(encrypted, data)
|
||||
// See RFC 1423, section 1.1
|
||||
for i := 0; i < pad; i++ {
|
||||
encrypted = append(encrypted, byte(pad))
|
||||
}
|
||||
enc.CryptBlocks(encrypted, encrypted)
|
||||
|
||||
return &pem.Block{
|
||||
Type: blockType,
|
||||
Headers: map[string]string{
|
||||
"Proc-Type": "4,ENCRYPTED",
|
||||
"DEK-Info": ciph.name + "," + hex.EncodeToString(iv),
|
||||
},
|
||||
Bytes: encrypted,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func cipherByName(name string) *rfc1423Algo {
|
||||
for i := range rfc1423Algos {
|
||||
alg := &rfc1423Algos[i]
|
||||
if alg.name == name {
|
||||
return alg
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cipherByKey(key PEMCipher) *rfc1423Algo {
|
||||
for i := range rfc1423Algos {
|
||||
alg := &rfc1423Algos[i]
|
||||
if alg.cipher == key {
|
||||
return alg
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
247
vendor/github.com/google/certificate-transparency-go/x509/pem_decrypt_test.go
generated
vendored
Normal file
247
vendor/github.com/google/certificate-transparency-go/x509/pem_decrypt_test.go
generated
vendored
Normal file
@ -0,0 +1,247 @@
|
||||
// Copyright 2012 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 x509
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDecrypt(t *testing.T) {
|
||||
for i, data := range testData {
|
||||
t.Logf("test %v. %v", i, data.kind)
|
||||
block, rest := pem.Decode(data.pemData)
|
||||
if len(rest) > 0 {
|
||||
t.Error("extra data")
|
||||
}
|
||||
der, err := DecryptPEMBlock(block, data.password)
|
||||
if err != nil {
|
||||
t.Error("decrypt failed: ", err)
|
||||
continue
|
||||
}
|
||||
if _, err := ParsePKCS1PrivateKey(der); err != nil {
|
||||
t.Error("invalid private key: ", err)
|
||||
}
|
||||
plainDER, err := base64.StdEncoding.DecodeString(data.plainDER)
|
||||
if err != nil {
|
||||
t.Fatal("cannot decode test DER data: ", err)
|
||||
}
|
||||
if !bytes.Equal(der, plainDER) {
|
||||
t.Error("data mismatch")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncrypt(t *testing.T) {
|
||||
for i, data := range testData {
|
||||
t.Logf("test %v. %v", i, data.kind)
|
||||
plainDER, err := base64.StdEncoding.DecodeString(data.plainDER)
|
||||
if err != nil {
|
||||
t.Fatal("cannot decode test DER data: ", err)
|
||||
}
|
||||
password := []byte("kremvax1")
|
||||
block, err := EncryptPEMBlock(rand.Reader, "RSA PRIVATE KEY", plainDER, password, data.kind)
|
||||
if err != nil {
|
||||
t.Error("encrypt: ", err)
|
||||
continue
|
||||
}
|
||||
if !IsEncryptedPEMBlock(block) {
|
||||
t.Error("PEM block does not appear to be encrypted")
|
||||
}
|
||||
if block.Type != "RSA PRIVATE KEY" {
|
||||
t.Errorf("unexpected block type; got %q want %q", block.Type, "RSA PRIVATE KEY")
|
||||
}
|
||||
if block.Headers["Proc-Type"] != "4,ENCRYPTED" {
|
||||
t.Errorf("block does not have correct Proc-Type header")
|
||||
}
|
||||
der, err := DecryptPEMBlock(block, password)
|
||||
if err != nil {
|
||||
t.Error("decrypt: ", err)
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(der, plainDER) {
|
||||
t.Errorf("data mismatch")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var testData = []struct {
|
||||
kind PEMCipher
|
||||
password []byte
|
||||
pemData []byte
|
||||
plainDER string
|
||||
}{
|
||||
{
|
||||
kind: PEMCipherDES,
|
||||
password: []byte("asdf"),
|
||||
pemData: []byte(`
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: DES-CBC,34F09A4FC8DE22B5
|
||||
|
||||
WXxy8kbZdiZvANtKvhmPBLV7eVFj2A5z6oAxvI9KGyhG0ZK0skfnt00C24vfU7m5
|
||||
ICXeoqP67lzJ18xCzQfHjDaBNs53DSDT+Iz4e8QUep1xQ30+8QKX2NA2coee3nwc
|
||||
6oM1cuvhNUDemBH2i3dKgMVkfaga0zQiiOq6HJyGSncCMSruQ7F9iWEfRbFcxFCx
|
||||
qtHb1kirfGKEtgWTF+ynyco6+2gMXNu70L7nJcnxnV/RLFkHt7AUU1yrclxz7eZz
|
||||
XOH9VfTjb52q/I8Suozq9coVQwg4tXfIoYUdT//O+mB7zJb9HI9Ps77b9TxDE6Gm
|
||||
4C9brwZ3zg2vqXcwwV6QRZMtyll9rOpxkbw6NPlpfBqkc3xS51bbxivbO/Nve4KD
|
||||
r12ymjFNF4stXCfJnNqKoZ50BHmEEUDu5Wb0fpVn82XrGw7CYc4iug==
|
||||
-----END RSA PRIVATE KEY-----`),
|
||||
plainDER: `
|
||||
MIIBPAIBAAJBAPASZe+tCPU6p80AjHhDkVsLYa51D35e/YGa8QcZyooeZM8EHozo
|
||||
KD0fNiKI+53bHdy07N+81VQ8/ejPcRoXPlsCAwEAAQJBAMTxIuSq27VpR+zZ7WJf
|
||||
c6fvv1OBvpMZ0/d1pxL/KnOAgq2rD5hDtk9b0LGhTPgQAmrrMTKuSeGoIuYE+gKQ
|
||||
QvkCIQD+GC1m+/do+QRurr0uo46Kx1LzLeSCrjBk34wiOp2+dwIhAPHfTLRXS2fv
|
||||
7rljm0bYa4+eDZpz+E8RcXEgzhhvcQQ9AiAI5eHZJGOyml3MXnQjiPi55WcDOw0w
|
||||
glcRgT6QCEtz2wIhANSyqaFtosIkHKqrDUGfz/bb5tqMYTAnBruVPaf/WEOBAiEA
|
||||
9xORWeRG1tRpso4+dYy4KdDkuLPIO01KY6neYGm3BCM=`,
|
||||
},
|
||||
{
|
||||
kind: PEMCipher3DES,
|
||||
password: []byte("asdf"),
|
||||
pemData: []byte(`
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: DES-EDE3-CBC,C1F4A6A03682C2C7
|
||||
|
||||
0JqVdBEH6iqM7drTkj+e2W/bE3LqakaiWhb9WUVonFkhyu8ca/QzebY3b5gCvAZQ
|
||||
YwBvDcT/GHospKqPx+cxDHJNsUASDZws6bz8ZXWJGwZGExKzr0+Qx5fgXn44Ms3x
|
||||
8g1ENFuTXtxo+KoNK0zuAMAqp66Llcds3Fjl4XR18QaD0CrVNAfOdgATWZm5GJxk
|
||||
Fgx5f84nT+/ovvreG+xeOzWgvtKo0UUZVrhGOgfKLpa57adumcJ6SkUuBtEFpZFB
|
||||
ldw5w7WC7d13x2LsRkwo8ZrDKgIV+Y9GNvhuCCkTzNP0V3gNeJpd201HZHR+9n3w
|
||||
3z0VjR/MGqsfcy1ziEWMNOO53At3zlG6zP05aHMnMcZoVXadEK6L1gz++inSSDCq
|
||||
gI0UJP4e3JVB7AkgYymYAwiYALAkoEIuanxoc50njJk=
|
||||
-----END RSA PRIVATE KEY-----`),
|
||||
plainDER: `
|
||||
MIIBOwIBAAJBANOCXKdoNS/iP/MAbl9cf1/SF3P+Ns7ZeNL27CfmDh0O6Zduaax5
|
||||
NBiumd2PmjkaCu7lQ5JOibHfWn+xJsc3kw0CAwEAAQJANX/W8d1Q/sCqzkuAn4xl
|
||||
B5a7qfJWaLHndu1QRLNTRJPn0Ee7OKJ4H0QKOhQM6vpjRrz+P2u9thn6wUxoPsef
|
||||
QQIhAP/jCkfejFcy4v15beqKzwz08/tslVjF+Yq41eJGejmxAiEA05pMoqfkyjcx
|
||||
fyvGhpoOyoCp71vSGUfR2I9CR65oKh0CIC1Msjs66LlfJtQctRq6bCEtFCxEcsP+
|
||||
eEjYo/Sk6WphAiEAxpgWPMJeU/shFT28gS+tmhjPZLpEoT1qkVlC14u0b3ECIQDX
|
||||
tZZZxCtPAm7shftEib0VU77Lk8MsXJcx2C4voRsjEw==`,
|
||||
},
|
||||
{
|
||||
kind: PEMCipherAES128,
|
||||
password: []byte("asdf"),
|
||||
pemData: []byte(`
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: AES-128-CBC,D4492E793FC835CC038A728ED174F78A
|
||||
|
||||
EyfQSzXSjv6BaNH+NHdXRlkHdimpF9izWlugVJAPApgXrq5YldPe2aGIOFXyJ+QE
|
||||
ZIG20DYqaPzJRjTEbPNZ6Es0S2JJ5yCpKxwJuDkgJZKtF39Q2i36JeGbSZQIuWJE
|
||||
GZbBpf1jDH/pr0iGonuAdl2PCCZUiy+8eLsD2tyviHUkFLOB+ykYoJ5t8ngZ/B6D
|
||||
33U43LLb7+9zD4y3Q9OVHqBFGyHcxCY9+9Qh4ZnFp7DTf6RY5TNEvE3s4g6aDpBs
|
||||
3NbvRVvYTgs8K9EPk4K+5R+P2kD8J8KvEIGxVa1vz8QoCJ/jr7Ka2rvNgPCex5/E
|
||||
080LzLHPCrXKdlr/f50yhNWq08ZxMWQFkui+FDHPDUaEELKAXV8/5PDxw80Rtybo
|
||||
AVYoCVIbZXZCuCO81op8UcOgEpTtyU5Lgh3Mw5scQL0=
|
||||
-----END RSA PRIVATE KEY-----`),
|
||||
plainDER: `
|
||||
MIIBOgIBAAJBAMBlj5FxYtqbcy8wY89d/S7n0+r5MzD9F63BA/Lpl78vQKtdJ5dT
|
||||
cDGh/rBt1ufRrNp0WihcmZi7Mpl/3jHjiWECAwEAAQJABNOHYnKhtDIqFYj1OAJ3
|
||||
k3GlU0OlERmIOoeY/cL2V4lgwllPBEs7r134AY4wMmZSBUj8UR/O4SNO668ElKPE
|
||||
cQIhAOuqY7/115x5KCdGDMWi+jNaMxIvI4ETGwV40ykGzqlzAiEA0P9oEC3m9tHB
|
||||
kbpjSTxaNkrXxDgdEOZz8X0uOUUwHNsCIAwzcSCiGLyYJTULUmP1ESERfW1mlV78
|
||||
XzzESaJpIM/zAiBQkSTcl9VhcJreQqvjn5BnPZLP4ZHS4gPwJAGdsj5J4QIhAOVR
|
||||
B3WlRNTXR2WsJ5JdByezg9xzdXzULqmga0OE339a`,
|
||||
},
|
||||
{
|
||||
kind: PEMCipherAES192,
|
||||
password: []byte("asdf"),
|
||||
pemData: []byte(`
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: AES-192-CBC,E2C9FB02BCA23ADE1829F8D8BC5F5369
|
||||
|
||||
cqVslvHqDDM6qwU6YjezCRifXmKsrgEev7ng6Qs7UmDJOpHDgJQZI9fwMFUhIyn5
|
||||
FbCu1SHkLMW52Ld3CuEqMnzWMlhPrW8tFvUOrMWPYSisv7nNq88HobZEJcUNL2MM
|
||||
Y15XmHW6IJwPqhKyLHpWXyOCVEh4ODND2nV15PCoi18oTa475baxSk7+1qH7GuIs
|
||||
Rb7tshNTMqHbCpyo9Rn3UxeFIf9efdl8YLiMoIqc7J8E5e9VlbeQSdLMQOgDAQJG
|
||||
ReUtTw8exmKsY4gsSjhkg5uiw7/ZB1Ihto0qnfQJgjGc680qGkT1d6JfvOfeYAk6
|
||||
xn5RqS/h8rYAYm64KnepfC9vIujo4NqpaREDmaLdX5MJPQ+SlytITQvgUsUq3q/t
|
||||
Ss85xjQEZH3hzwjQqdJvmA4hYP6SUjxYpBM+02xZ1Xw=
|
||||
-----END RSA PRIVATE KEY-----`),
|
||||
plainDER: `
|
||||
MIIBOwIBAAJBAMGcRrZiNNmtF20zyS6MQ7pdGx17aFDl+lTl+qnLuJRUCMUG05xs
|
||||
OmxmL/O1Qlf+bnqR8Bgg65SfKg21SYuLhiMCAwEAAQJBAL94uuHyO4wux2VC+qpj
|
||||
IzPykjdU7XRcDHbbvksf4xokSeUFjjD3PB0Qa83M94y89ZfdILIqS9x5EgSB4/lX
|
||||
qNkCIQD6cCIqLfzq/lYbZbQgAAjpBXeQVYsbvVtJrPrXJAlVVQIhAMXpDKMeFPMn
|
||||
J0g2rbx1gngx0qOa5r5iMU5w/noN4W2XAiBjf+WzCG5yFvazD+dOx3TC0A8+4x3P
|
||||
uZ3pWbaXf5PNuQIgAcdXarvhelH2w2piY1g3BPeFqhzBSCK/yLGxR82KIh8CIQDD
|
||||
+qGKsd09NhQ/G27y/DARzOYtml1NvdmCQAgsDIIOLA==`,
|
||||
},
|
||||
{
|
||||
kind: PEMCipherAES256,
|
||||
password: []byte("asdf"),
|
||||
pemData: []byte(`
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: AES-256-CBC,8E7ED5CD731902CE938957A886A5FFBD
|
||||
|
||||
4Mxr+KIzRVwoOP0wwq6caSkvW0iS+GE2h2Ov/u+n9ZTMwL83PRnmjfjzBgfRZLVf
|
||||
JFPXxUK26kMNpIdssNnqGOds+DhB+oSrsNKoxgxSl5OBoYv9eJTVYm7qOyAFIsjr
|
||||
DRKAcjYCmzfesr7PVTowwy0RtHmYwyXMGDlAzzZrEvaiySFFmMyKKvtoavwaFoc7
|
||||
Pz3RZScwIuubzTGJ1x8EzdffYOsdCa9Mtgpp3L136+23dOd6L/qK2EG2fzrJSHs/
|
||||
2XugkleBFSMKzEp9mxXKRfa++uidQvMZTFLDK9w5YjrRvMBo/l2BoZIsq0jAIE1N
|
||||
sv5Z/KwlX+3MDEpPQpUwGPlGGdLnjI3UZ+cjgqBcoMiNc6HfgbBgYJSU6aDSHuCk
|
||||
clCwByxWkBNgJ2GrkwNrF26v+bGJJJNR4SKouY1jQf0=
|
||||
-----END RSA PRIVATE KEY-----`),
|
||||
plainDER: `
|
||||
MIIBOgIBAAJBAKy3GFkstoCHIEeUU/qO8207m8WSrjksR+p9B4tf1w5k+2O1V/GY
|
||||
AQ5WFCApItcOkQe/I0yZZJk/PmCqMzSxrc8CAwEAAQJAOCAz0F7AW9oNelVQSP8F
|
||||
Sfzx7O1yom+qWyAQQJF/gFR11gpf9xpVnnyu1WxIRnDUh1LZwUsjwlDYb7MB74id
|
||||
oQIhANPcOiLwOPT4sIUpRM5HG6BF1BI7L77VpyGVk8xNP7X/AiEA0LMHZtk4I+lJ
|
||||
nClgYp4Yh2JZ1Znbu7IoQMCEJCjwKDECIGd8Dzm5tViTkUW6Hs3Tlf73nNs65duF
|
||||
aRnSglss8I3pAiEAonEnKruawgD8RavDFR+fUgmQiPz4FnGGeVgfwpGG1JECIBYq
|
||||
PXHYtPqxQIbD2pScR5qum7iGUh11lEUPkmt+2uqS`,
|
||||
},
|
||||
{
|
||||
// generated with:
|
||||
// openssl genrsa -aes128 -passout pass:asdf -out server.orig.key 128
|
||||
kind: PEMCipherAES128,
|
||||
password: []byte("asdf"),
|
||||
pemData: []byte(`
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: AES-128-CBC,74611ABC2571AF11B1BF9B69E62C89E7
|
||||
|
||||
6ei/MlytjE0FFgZOGQ+jrwomKfpl8kdefeE0NSt/DMRrw8OacHAzBNi3pPEa0eX3
|
||||
eND9l7C9meCirWovjj9QWVHrXyugFuDIqgdhQ8iHTgCfF3lrmcttVrbIfMDw+smD
|
||||
hTP8O1mS/MHl92NE0nhv0w==
|
||||
-----END RSA PRIVATE KEY-----`),
|
||||
plainDER: `
|
||||
MGMCAQACEQC6ssxmYuauuHGOCDAI54RdAgMBAAECEQCWIn6Yv2O+kBcDF7STctKB
|
||||
AgkA8SEfu/2i3g0CCQDGNlXbBHX7kQIIK3Ww5o0cYbECCQDCimPb0dYGsQIIeQ7A
|
||||
jryIst8=`,
|
||||
},
|
||||
}
|
||||
|
||||
const incompleteBlockPEM = `
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: AES-128-CBC,74611ABC2571AF11B1BF9B69E62C89E7
|
||||
|
||||
6L8yXK2MTQUWBk4ZD6OvCiYp+mXyR1594TQ1K38MxGvDw5pwcDME2Lek8RrR5fd40P2XsL2Z4KKt
|
||||
ai+OP1BZUetfK6AW4MiqB2FDyIdOAJ8XeWuZy21Wtsh8wPD6yYOFM/w7WZL8weX3Y0TSeG/T
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
|
||||
func TestIncompleteBlock(t *testing.T) {
|
||||
// incompleteBlockPEM contains ciphertext that is not a multiple of the
|
||||
// block size. This previously panicked. See #11215.
|
||||
block, _ := pem.Decode([]byte(incompleteBlockPEM))
|
||||
_, err := DecryptPEMBlock(block, []byte("foo"))
|
||||
if err == nil {
|
||||
t.Fatal("Bad PEM data decrypted successfully")
|
||||
}
|
||||
const expectedSubstr = "block size"
|
||||
if e := err.Error(); !strings.Contains(e, expectedSubstr) {
|
||||
t.Fatalf("Expected error containing %q but got: %q", expectedSubstr, e)
|
||||
}
|
||||
}
|
155
vendor/github.com/google/certificate-transparency-go/x509/pkcs1.go
generated
vendored
Normal file
155
vendor/github.com/google/certificate-transparency-go/x509/pkcs1.go
generated
vendored
Normal file
@ -0,0 +1,155 @@
|
||||
// Copyright 2011 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 x509
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/google/certificate-transparency-go/asn1"
|
||||
)
|
||||
|
||||
// pkcs1PrivateKey is a structure which mirrors the PKCS#1 ASN.1 for an RSA private key.
|
||||
type pkcs1PrivateKey struct {
|
||||
Version int
|
||||
N *big.Int
|
||||
E int
|
||||
D *big.Int
|
||||
P *big.Int
|
||||
Q *big.Int
|
||||
// We ignore these values, if present, because rsa will calculate them.
|
||||
Dp *big.Int `asn1:"optional"`
|
||||
Dq *big.Int `asn1:"optional"`
|
||||
Qinv *big.Int `asn1:"optional"`
|
||||
|
||||
AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional,omitempty"`
|
||||
}
|
||||
|
||||
type pkcs1AdditionalRSAPrime struct {
|
||||
Prime *big.Int
|
||||
|
||||
// We ignore these values because rsa will calculate them.
|
||||
Exp *big.Int
|
||||
Coeff *big.Int
|
||||
}
|
||||
|
||||
// pkcs1PublicKey reflects the ASN.1 structure of a PKCS#1 public key.
|
||||
type pkcs1PublicKey struct {
|
||||
N *big.Int
|
||||
E int
|
||||
}
|
||||
|
||||
// ParsePKCS1PrivateKey returns an RSA private key from its ASN.1 PKCS#1 DER encoded form.
|
||||
func ParsePKCS1PrivateKey(der []byte) (*rsa.PrivateKey, error) {
|
||||
var priv pkcs1PrivateKey
|
||||
rest, err := asn1.Unmarshal(der, &priv)
|
||||
if len(rest) > 0 {
|
||||
return nil, asn1.SyntaxError{Msg: "trailing data"}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if priv.Version > 1 {
|
||||
return nil, errors.New("x509: unsupported private key version")
|
||||
}
|
||||
|
||||
if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 {
|
||||
return nil, errors.New("x509: private key contains zero or negative value")
|
||||
}
|
||||
|
||||
key := new(rsa.PrivateKey)
|
||||
key.PublicKey = rsa.PublicKey{
|
||||
E: priv.E,
|
||||
N: priv.N,
|
||||
}
|
||||
|
||||
key.D = priv.D
|
||||
key.Primes = make([]*big.Int, 2+len(priv.AdditionalPrimes))
|
||||
key.Primes[0] = priv.P
|
||||
key.Primes[1] = priv.Q
|
||||
for i, a := range priv.AdditionalPrimes {
|
||||
if a.Prime.Sign() <= 0 {
|
||||
return nil, errors.New("x509: private key contains zero or negative prime")
|
||||
}
|
||||
key.Primes[i+2] = a.Prime
|
||||
// We ignore the other two values because rsa will calculate
|
||||
// them as needed.
|
||||
}
|
||||
|
||||
err = key.Validate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key.Precompute()
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// MarshalPKCS1PrivateKey converts a private key to ASN.1 DER encoded form.
|
||||
func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte {
|
||||
key.Precompute()
|
||||
|
||||
version := 0
|
||||
if len(key.Primes) > 2 {
|
||||
version = 1
|
||||
}
|
||||
|
||||
priv := pkcs1PrivateKey{
|
||||
Version: version,
|
||||
N: key.N,
|
||||
E: key.PublicKey.E,
|
||||
D: key.D,
|
||||
P: key.Primes[0],
|
||||
Q: key.Primes[1],
|
||||
Dp: key.Precomputed.Dp,
|
||||
Dq: key.Precomputed.Dq,
|
||||
Qinv: key.Precomputed.Qinv,
|
||||
}
|
||||
|
||||
priv.AdditionalPrimes = make([]pkcs1AdditionalRSAPrime, len(key.Precomputed.CRTValues))
|
||||
for i, values := range key.Precomputed.CRTValues {
|
||||
priv.AdditionalPrimes[i].Prime = key.Primes[2+i]
|
||||
priv.AdditionalPrimes[i].Exp = values.Exp
|
||||
priv.AdditionalPrimes[i].Coeff = values.Coeff
|
||||
}
|
||||
|
||||
b, _ := asn1.Marshal(priv)
|
||||
return b
|
||||
}
|
||||
|
||||
// ParsePKCS1PublicKey parses a PKCS#1 public key in ASN.1 DER form.
|
||||
func ParsePKCS1PublicKey(der []byte) (*rsa.PublicKey, error) {
|
||||
var pub pkcs1PublicKey
|
||||
rest, err := asn1.Unmarshal(der, &pub)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(rest) > 0 {
|
||||
return nil, asn1.SyntaxError{Msg: "trailing data"}
|
||||
}
|
||||
|
||||
if pub.N.Sign() <= 0 || pub.E <= 0 {
|
||||
return nil, errors.New("x509: public key contains zero or negative value")
|
||||
}
|
||||
if pub.E > 1<<31-1 {
|
||||
return nil, errors.New("x509: public key contains large public exponent")
|
||||
}
|
||||
|
||||
return &rsa.PublicKey{
|
||||
E: pub.E,
|
||||
N: pub.N,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// MarshalPKCS1PublicKey converts an RSA public key to PKCS#1, ASN.1 DER form.
|
||||
func MarshalPKCS1PublicKey(key *rsa.PublicKey) []byte {
|
||||
derBytes, _ := asn1.Marshal(pkcs1PublicKey{
|
||||
N: key.N,
|
||||
E: key.E,
|
||||
})
|
||||
return derBytes
|
||||
}
|
102
vendor/github.com/google/certificate-transparency-go/x509/pkcs8.go
generated
vendored
Normal file
102
vendor/github.com/google/certificate-transparency-go/x509/pkcs8.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
// Copyright 2011 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 x509
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/certificate-transparency-go/asn1"
|
||||
"github.com/google/certificate-transparency-go/x509/pkix"
|
||||
)
|
||||
|
||||
// pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. See
|
||||
// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn
|
||||
// and RFC 5208.
|
||||
type pkcs8 struct {
|
||||
Version int
|
||||
Algo pkix.AlgorithmIdentifier
|
||||
PrivateKey []byte
|
||||
// optional attributes omitted.
|
||||
}
|
||||
|
||||
// ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key.
|
||||
// See RFC 5208.
|
||||
func ParsePKCS8PrivateKey(der []byte) (key interface{}, err error) {
|
||||
var privKey pkcs8
|
||||
if _, err := asn1.Unmarshal(der, &privKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch {
|
||||
case privKey.Algo.Algorithm.Equal(OIDPublicKeyRSA):
|
||||
key, err = ParsePKCS1PrivateKey(privKey.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, errors.New("x509: failed to parse RSA private key embedded in PKCS#8: " + err.Error())
|
||||
}
|
||||
return key, nil
|
||||
|
||||
case privKey.Algo.Algorithm.Equal(OIDPublicKeyECDSA):
|
||||
bytes := privKey.Algo.Parameters.FullBytes
|
||||
namedCurveOID := new(asn1.ObjectIdentifier)
|
||||
if _, err := asn1.Unmarshal(bytes, namedCurveOID); err != nil {
|
||||
namedCurveOID = nil
|
||||
}
|
||||
key, err = parseECPrivateKey(namedCurveOID, privKey.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, errors.New("x509: failed to parse EC private key embedded in PKCS#8: " + err.Error())
|
||||
}
|
||||
return key, nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("x509: PKCS#8 wrapping contained private key with unknown algorithm: %v", privKey.Algo.Algorithm)
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalPKCS8PrivateKey converts a private key to PKCS#8 encoded form.
|
||||
// The following key types are supported: *rsa.PrivateKey, *ecdsa.PublicKey.
|
||||
// Unsupported key types result in an error.
|
||||
//
|
||||
// See RFC 5208.
|
||||
func MarshalPKCS8PrivateKey(key interface{}) ([]byte, error) {
|
||||
var privKey pkcs8
|
||||
|
||||
switch k := key.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
privKey.Algo = pkix.AlgorithmIdentifier{
|
||||
Algorithm: OIDPublicKeyRSA,
|
||||
Parameters: asn1.NullRawValue,
|
||||
}
|
||||
privKey.PrivateKey = MarshalPKCS1PrivateKey(k)
|
||||
|
||||
case *ecdsa.PrivateKey:
|
||||
oid, ok := OIDFromNamedCurve(k.Curve)
|
||||
if !ok {
|
||||
return nil, errors.New("x509: unknown curve while marshalling to PKCS#8")
|
||||
}
|
||||
|
||||
oidBytes, err := asn1.Marshal(oid)
|
||||
if err != nil {
|
||||
return nil, errors.New("x509: failed to marshal curve OID: " + err.Error())
|
||||
}
|
||||
|
||||
privKey.Algo = pkix.AlgorithmIdentifier{
|
||||
Algorithm: OIDPublicKeyECDSA,
|
||||
Parameters: asn1.RawValue{
|
||||
FullBytes: oidBytes,
|
||||
},
|
||||
}
|
||||
|
||||
if privKey.PrivateKey, err = marshalECPrivateKeyWithOID(k, nil); err != nil {
|
||||
return nil, errors.New("x509: failed to marshal EC private key while building PKCS#8: " + err.Error())
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("x509: unknown key type while marshalling PKCS#8: %T", key)
|
||||
}
|
||||
|
||||
return asn1.Marshal(privKey)
|
||||
}
|
109
vendor/github.com/google/certificate-transparency-go/x509/pkcs8_test.go
generated
vendored
Normal file
109
vendor/github.com/google/certificate-transparency-go/x509/pkcs8_test.go
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
||||
// Copyright 2011 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 x509
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"encoding/hex"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Generated using:
|
||||
// openssl genrsa 1024 | openssl pkcs8 -topk8 -nocrypt
|
||||
var pkcs8RSAPrivateKeyHex = `30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100cfb1b5bf9685ffa97b4f99df4ff122b70e59ac9b992f3bc2b3dde17d53c1a34928719b02e8fd17839499bfbd515bd6ef99c7a1c47a239718fe36bfd824c0d96060084b5f67f0273443007a24dfaf5634f7772c9346e10eb294c2306671a5a5e719ae24b4de467291bc571014b0e02dec04534d66a9bb171d644b66b091780e8d020301000102818100b595778383c4afdbab95d2bfed12b3f93bb0a73a7ad952f44d7185fd9ec6c34de8f03a48770f2009c8580bcd275e9632714e9a5e3f32f29dc55474b2329ff0ebc08b3ffcb35bc96e6516b483df80a4a59cceb71918cbabf91564e64a39d7e35dce21cb3031824fdbc845dba6458852ec16af5dddf51a8397a8797ae0337b1439024100ea0eb1b914158c70db39031dd8904d6f18f408c85fbbc592d7d20dee7986969efbda081fdf8bc40e1b1336d6b638110c836bfdc3f314560d2e49cd4fbde1e20b024100e32a4e793b574c9c4a94c8803db5152141e72d03de64e54ef2c8ed104988ca780cd11397bc359630d01b97ebd87067c5451ba777cf045ca23f5912f1031308c702406dfcdbbd5a57c9f85abc4edf9e9e29153507b07ce0a7ef6f52e60dcfebe1b8341babd8b789a837485da6c8d55b29bbb142ace3c24a1f5b54b454d01b51e2ad03024100bd6a2b60dee01e1b3bfcef6a2f09ed027c273cdbbaf6ba55a80f6dcc64e4509ee560f84b4f3e076bd03b11e42fe71a3fdd2dffe7e0902c8584f8cad877cdc945024100aa512fa4ada69881f1d8bb8ad6614f192b83200aef5edf4811313d5ef30a86cbd0a90f7b025c71ea06ec6b34db6306c86b1040670fd8654ad7291d066d06d031`
|
||||
|
||||
// Generated using:
|
||||
// openssl ecparam -genkey -name secp224r1 | openssl pkcs8 -topk8 -nocrypt
|
||||
var pkcs8P224PrivateKeyHex = `3078020100301006072a8648ce3d020106052b810400210461305f020101041cca3d72b3e88fed2684576dad9b80a9180363a5424986900e3abcab3fa13c033a0004f8f2a6372872a4e61263ed893afb919576a4cacfecd6c081a2cbc76873cf4ba8530703c6042b3a00e2205087e87d2435d2e339e25702fae1`
|
||||
|
||||
// Generated using:
|
||||
// openssl ecparam -genkey -name secp256r1 | openssl pkcs8 -topk8 -nocrypt
|
||||
var pkcs8P256PrivateKeyHex = `308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b0201010420dad6b2f49ca774c36d8ae9517e935226f667c929498f0343d2424d0b9b591b43a14403420004b9c9b90095476afe7b860d8bd43568cab7bcb2eed7b8bf2fa0ce1762dd20b04193f859d2d782b1e4cbfd48492f1f533113a6804903f292258513837f07fda735`
|
||||
|
||||
// Generated using:
|
||||
// openssl ecparam -genkey -name secp384r1 | openssl pkcs8 -topk8 -nocrypt
|
||||
var pkcs8P384PrivateKeyHex = `3081b6020100301006072a8648ce3d020106052b8104002204819e30819b02010104309bf832f6aaaeacb78ce47ffb15e6fd0fd48683ae79df6eca39bfb8e33829ac94aa29d08911568684c2264a08a4ceb679a164036200049070ad4ed993c7770d700e9f6dc2baa83f63dd165b5507f98e8ff29b5d2e78ccbe05c8ddc955dbf0f7497e8222cfa49314fe4e269459f8e880147f70d785e530f2939e4bf9f838325bb1a80ad4cf59272ae0e5efe9a9dc33d874492596304bd3`
|
||||
|
||||
// Generated using:
|
||||
// openssl ecparam -genkey -name secp521r1 | openssl pkcs8 -topk8 -nocrypt
|
||||
//
|
||||
// Note that OpenSSL will truncate the private key if it can (i.e. it emits it
|
||||
// like an integer, even though it's an OCTET STRING field). Thus if you
|
||||
// regenerate this you may, randomly, find that it's a byte shorter than
|
||||
// expected and the Go test will fail to recreate it exactly.
|
||||
var pkcs8P521PrivateKeyHex = `3081ee020100301006072a8648ce3d020106052b810400230481d63081d3020101044200cfe0b87113a205cf291bb9a8cd1a74ac6c7b2ebb8199aaa9a5010d8b8012276fa3c22ac913369fa61beec2a3b8b4516bc049bde4fb3b745ac11b56ab23ac52e361a1818903818600040138f75acdd03fbafa4f047a8e4b272ba9d555c667962b76f6f232911a5786a0964e5edea6bd21a6f8725720958de049c6e3e6661c1c91b227cebee916c0319ed6ca003db0a3206d372229baf9dd25d868bf81140a518114803ce40c1855074d68c4e9dab9e65efba7064c703b400f1767f217dac82715ac1f6d88c74baf47a7971de4ea`
|
||||
|
||||
func TestPKCS8(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
keyHex string
|
||||
keyType reflect.Type
|
||||
curve elliptic.Curve
|
||||
}{
|
||||
{
|
||||
name: "RSA private key",
|
||||
keyHex: pkcs8RSAPrivateKeyHex,
|
||||
keyType: reflect.TypeOf(&rsa.PrivateKey{}),
|
||||
},
|
||||
{
|
||||
name: "P-224 private key",
|
||||
keyHex: pkcs8P224PrivateKeyHex,
|
||||
keyType: reflect.TypeOf(&ecdsa.PrivateKey{}),
|
||||
curve: elliptic.P224(),
|
||||
},
|
||||
{
|
||||
name: "P-256 private key",
|
||||
keyHex: pkcs8P256PrivateKeyHex,
|
||||
keyType: reflect.TypeOf(&ecdsa.PrivateKey{}),
|
||||
curve: elliptic.P256(),
|
||||
},
|
||||
{
|
||||
name: "P-384 private key",
|
||||
keyHex: pkcs8P384PrivateKeyHex,
|
||||
keyType: reflect.TypeOf(&ecdsa.PrivateKey{}),
|
||||
curve: elliptic.P384(),
|
||||
},
|
||||
{
|
||||
name: "P-521 private key",
|
||||
keyHex: pkcs8P521PrivateKeyHex,
|
||||
keyType: reflect.TypeOf(&ecdsa.PrivateKey{}),
|
||||
curve: elliptic.P521(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
derBytes, err := hex.DecodeString(test.keyHex)
|
||||
if err != nil {
|
||||
t.Errorf("%s: failed to decode hex: %s", test.name, err)
|
||||
continue
|
||||
}
|
||||
privKey, err := ParsePKCS8PrivateKey(derBytes)
|
||||
if err != nil {
|
||||
t.Errorf("%s: failed to decode PKCS#8: %s", test.name, err)
|
||||
continue
|
||||
}
|
||||
if reflect.TypeOf(privKey) != test.keyType {
|
||||
t.Errorf("%s: decoded PKCS#8 returned unexpected key type: %T", test.name, privKey)
|
||||
continue
|
||||
}
|
||||
if ecKey, isEC := privKey.(*ecdsa.PrivateKey); isEC && ecKey.Curve != test.curve {
|
||||
t.Errorf("%s: decoded PKCS#8 returned unexpected curve %#v", test.name, ecKey.Curve)
|
||||
continue
|
||||
}
|
||||
reserialised, err := MarshalPKCS8PrivateKey(privKey)
|
||||
if err != nil {
|
||||
t.Errorf("%s: failed to marshal into PKCS#8: %s", test.name, err)
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(derBytes, reserialised) {
|
||||
t.Errorf("%s: marshalled PKCS#8 didn't match original: got %x, want %x", test.name, reserialised, derBytes)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
288
vendor/github.com/google/certificate-transparency-go/x509/pkix/pkix.go
generated
vendored
Normal file
288
vendor/github.com/google/certificate-transparency-go/x509/pkix/pkix.go
generated
vendored
Normal file
@ -0,0 +1,288 @@
|
||||
// Copyright 2011 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 pkix contains shared, low level structures used for ASN.1 parsing
|
||||
// and serialization of X.509 certificates, CRL and OCSP.
|
||||
package pkix
|
||||
|
||||
import (
|
||||
// START CT CHANGES
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/certificate-transparency-go/asn1"
|
||||
// END CT CHANGES
|
||||
"math/big"
|
||||
"time"
|
||||
)
|
||||
|
||||
// AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC
|
||||
// 5280, section 4.1.1.2.
|
||||
type AlgorithmIdentifier struct {
|
||||
Algorithm asn1.ObjectIdentifier
|
||||
Parameters asn1.RawValue `asn1:"optional"`
|
||||
}
|
||||
|
||||
type RDNSequence []RelativeDistinguishedNameSET
|
||||
|
||||
var attributeTypeNames = map[string]string{
|
||||
"2.5.4.6": "C",
|
||||
"2.5.4.10": "O",
|
||||
"2.5.4.11": "OU",
|
||||
"2.5.4.3": "CN",
|
||||
"2.5.4.5": "SERIALNUMBER",
|
||||
"2.5.4.7": "L",
|
||||
"2.5.4.8": "ST",
|
||||
"2.5.4.9": "STREET",
|
||||
"2.5.4.17": "POSTALCODE",
|
||||
}
|
||||
|
||||
// String returns a string representation of the sequence r,
|
||||
// roughly following the RFC 2253 Distinguished Names syntax.
|
||||
func (r RDNSequence) String() string {
|
||||
s := ""
|
||||
for i := 0; i < len(r); i++ {
|
||||
rdn := r[len(r)-1-i]
|
||||
if i > 0 {
|
||||
s += ","
|
||||
}
|
||||
for j, tv := range rdn {
|
||||
if j > 0 {
|
||||
s += "+"
|
||||
}
|
||||
|
||||
oidString := tv.Type.String()
|
||||
typeName, ok := attributeTypeNames[oidString]
|
||||
if !ok {
|
||||
derBytes, err := asn1.Marshal(tv.Value)
|
||||
if err == nil {
|
||||
s += oidString + "=#" + hex.EncodeToString(derBytes)
|
||||
continue // No value escaping necessary.
|
||||
}
|
||||
|
||||
typeName = oidString
|
||||
}
|
||||
|
||||
valueString := fmt.Sprint(tv.Value)
|
||||
escaped := make([]rune, 0, len(valueString))
|
||||
|
||||
for k, c := range valueString {
|
||||
escape := false
|
||||
|
||||
switch c {
|
||||
case ',', '+', '"', '\\', '<', '>', ';':
|
||||
escape = true
|
||||
|
||||
case ' ':
|
||||
escape = k == 0 || k == len(valueString)-1
|
||||
|
||||
case '#':
|
||||
escape = k == 0
|
||||
}
|
||||
|
||||
if escape {
|
||||
escaped = append(escaped, '\\', c)
|
||||
} else {
|
||||
escaped = append(escaped, c)
|
||||
}
|
||||
}
|
||||
|
||||
s += typeName + "=" + string(escaped)
|
||||
}
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
type RelativeDistinguishedNameSET []AttributeTypeAndValue
|
||||
|
||||
// AttributeTypeAndValue mirrors the ASN.1 structure of the same name in
|
||||
// http://tools.ietf.org/html/rfc5280#section-4.1.2.4
|
||||
type AttributeTypeAndValue struct {
|
||||
Type asn1.ObjectIdentifier
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// AttributeTypeAndValueSET represents a set of ASN.1 sequences of
|
||||
// AttributeTypeAndValue sequences from RFC 2986 (PKCS #10).
|
||||
type AttributeTypeAndValueSET struct {
|
||||
Type asn1.ObjectIdentifier
|
||||
Value [][]AttributeTypeAndValue `asn1:"set"`
|
||||
}
|
||||
|
||||
// Extension represents the ASN.1 structure of the same name. See RFC
|
||||
// 5280, section 4.2.
|
||||
type Extension struct {
|
||||
Id asn1.ObjectIdentifier
|
||||
Critical bool `asn1:"optional"`
|
||||
Value []byte
|
||||
}
|
||||
|
||||
// Name represents an X.509 distinguished name. This only includes the common
|
||||
// elements of a DN. When parsing, all elements are stored in Names and
|
||||
// non-standard elements can be extracted from there. When marshaling, elements
|
||||
// in ExtraNames are appended and override other values with the same OID.
|
||||
type Name struct {
|
||||
Country, Organization, OrganizationalUnit []string
|
||||
Locality, Province []string
|
||||
StreetAddress, PostalCode []string
|
||||
SerialNumber, CommonName string
|
||||
|
||||
Names []AttributeTypeAndValue
|
||||
ExtraNames []AttributeTypeAndValue
|
||||
}
|
||||
|
||||
func (n *Name) FillFromRDNSequence(rdns *RDNSequence) {
|
||||
for _, rdn := range *rdns {
|
||||
if len(rdn) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, atv := range rdn {
|
||||
n.Names = append(n.Names, atv)
|
||||
value, ok := atv.Value.(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
t := atv.Type
|
||||
if len(t) == 4 && t[0] == OIDAttribute[0] && t[1] == OIDAttribute[1] && t[2] == OIDAttribute[2] {
|
||||
switch t[3] {
|
||||
case OIDCommonName[3]:
|
||||
n.CommonName = value
|
||||
case OIDSerialNumber[3]:
|
||||
n.SerialNumber = value
|
||||
case OIDCountry[3]:
|
||||
n.Country = append(n.Country, value)
|
||||
case OIDLocality[3]:
|
||||
n.Locality = append(n.Locality, value)
|
||||
case OIDProvince[3]:
|
||||
n.Province = append(n.Province, value)
|
||||
case OIDStreetAddress[3]:
|
||||
n.StreetAddress = append(n.StreetAddress, value)
|
||||
case OIDOrganization[3]:
|
||||
n.Organization = append(n.Organization, value)
|
||||
case OIDOrganizationalUnit[3]:
|
||||
n.OrganizationalUnit = append(n.OrganizationalUnit, value)
|
||||
case OIDPostalCode[3]:
|
||||
n.PostalCode = append(n.PostalCode, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
OIDAttribute = asn1.ObjectIdentifier{2, 5, 4}
|
||||
OIDCountry = asn1.ObjectIdentifier{2, 5, 4, 6}
|
||||
OIDOrganization = asn1.ObjectIdentifier{2, 5, 4, 10}
|
||||
OIDOrganizationalUnit = asn1.ObjectIdentifier{2, 5, 4, 11}
|
||||
OIDCommonName = asn1.ObjectIdentifier{2, 5, 4, 3}
|
||||
OIDSerialNumber = asn1.ObjectIdentifier{2, 5, 4, 5}
|
||||
OIDLocality = asn1.ObjectIdentifier{2, 5, 4, 7}
|
||||
OIDProvince = asn1.ObjectIdentifier{2, 5, 4, 8}
|
||||
OIDStreetAddress = asn1.ObjectIdentifier{2, 5, 4, 9}
|
||||
OIDPostalCode = asn1.ObjectIdentifier{2, 5, 4, 17}
|
||||
|
||||
OIDPseudonym = asn1.ObjectIdentifier{2, 5, 4, 65}
|
||||
OIDTitle = asn1.ObjectIdentifier{2, 5, 4, 12}
|
||||
OIDDnQualifier = asn1.ObjectIdentifier{2, 5, 4, 46}
|
||||
OIDName = asn1.ObjectIdentifier{2, 5, 4, 41}
|
||||
OIDSurname = asn1.ObjectIdentifier{2, 5, 4, 4}
|
||||
OIDGivenName = asn1.ObjectIdentifier{2, 5, 4, 42}
|
||||
OIDInitials = asn1.ObjectIdentifier{2, 5, 4, 43}
|
||||
OIDGenerationQualifier = asn1.ObjectIdentifier{2, 5, 4, 44}
|
||||
)
|
||||
|
||||
// appendRDNs appends a relativeDistinguishedNameSET to the given RDNSequence
|
||||
// and returns the new value. The relativeDistinguishedNameSET contains an
|
||||
// attributeTypeAndValue for each of the given values. See RFC 5280, A.1, and
|
||||
// search for AttributeTypeAndValue.
|
||||
func (n Name) appendRDNs(in RDNSequence, values []string, oid asn1.ObjectIdentifier) RDNSequence {
|
||||
if len(values) == 0 || oidInAttributeTypeAndValue(oid, n.ExtraNames) {
|
||||
return in
|
||||
}
|
||||
|
||||
s := make([]AttributeTypeAndValue, len(values))
|
||||
for i, value := range values {
|
||||
s[i].Type = oid
|
||||
s[i].Value = value
|
||||
}
|
||||
|
||||
return append(in, s)
|
||||
}
|
||||
|
||||
func (n Name) ToRDNSequence() (ret RDNSequence) {
|
||||
ret = n.appendRDNs(ret, n.Country, OIDCountry)
|
||||
ret = n.appendRDNs(ret, n.Province, OIDProvince)
|
||||
ret = n.appendRDNs(ret, n.Locality, OIDLocality)
|
||||
ret = n.appendRDNs(ret, n.StreetAddress, OIDStreetAddress)
|
||||
ret = n.appendRDNs(ret, n.PostalCode, OIDPostalCode)
|
||||
ret = n.appendRDNs(ret, n.Organization, OIDOrganization)
|
||||
ret = n.appendRDNs(ret, n.OrganizationalUnit, OIDOrganizationalUnit)
|
||||
if len(n.CommonName) > 0 {
|
||||
ret = n.appendRDNs(ret, []string{n.CommonName}, OIDCommonName)
|
||||
}
|
||||
if len(n.SerialNumber) > 0 {
|
||||
ret = n.appendRDNs(ret, []string{n.SerialNumber}, OIDSerialNumber)
|
||||
}
|
||||
for _, atv := range n.ExtraNames {
|
||||
ret = append(ret, []AttributeTypeAndValue{atv})
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// String returns the string form of n, roughly following
|
||||
// the RFC 2253 Distinguished Names syntax.
|
||||
func (n Name) String() string {
|
||||
return n.ToRDNSequence().String()
|
||||
}
|
||||
|
||||
// oidInAttributeTypeAndValue returns whether a type with the given OID exists
|
||||
// in atv.
|
||||
func oidInAttributeTypeAndValue(oid asn1.ObjectIdentifier, atv []AttributeTypeAndValue) bool {
|
||||
for _, a := range atv {
|
||||
if a.Type.Equal(oid) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// CertificateList represents the ASN.1 structure of the same name. See RFC
|
||||
// 5280, section 5.1. Use Certificate.CheckCRLSignature to verify the
|
||||
// signature.
|
||||
type CertificateList struct {
|
||||
TBSCertList TBSCertificateList
|
||||
SignatureAlgorithm AlgorithmIdentifier
|
||||
SignatureValue asn1.BitString
|
||||
}
|
||||
|
||||
// HasExpired reports whether certList should have been updated by now.
|
||||
func (certList *CertificateList) HasExpired(now time.Time) bool {
|
||||
return !now.Before(certList.TBSCertList.NextUpdate)
|
||||
}
|
||||
|
||||
// TBSCertificateList represents the ASN.1 structure TBSCertList. See RFC
|
||||
// 5280, section 5.1.
|
||||
type TBSCertificateList struct {
|
||||
Raw asn1.RawContent
|
||||
Version int `asn1:"optional,default:0"`
|
||||
Signature AlgorithmIdentifier
|
||||
Issuer RDNSequence
|
||||
ThisUpdate time.Time
|
||||
NextUpdate time.Time `asn1:"optional"`
|
||||
RevokedCertificates []RevokedCertificate `asn1:"optional"`
|
||||
Extensions []Extension `asn1:"tag:0,optional,explicit"`
|
||||
}
|
||||
|
||||
// RevokedCertificate represents the unnamed ASN.1 structure that makes up the
|
||||
// revokedCertificates member of the TBSCertList structure. See RFC
|
||||
// 5280, section 5.1.
|
||||
type RevokedCertificate struct {
|
||||
SerialNumber *big.Int
|
||||
RevocationTime time.Time
|
||||
Extensions []Extension `asn1:"optional"`
|
||||
}
|
362
vendor/github.com/google/certificate-transparency-go/x509/revoked.go
generated
vendored
Normal file
362
vendor/github.com/google/certificate-transparency-go/x509/revoked.go
generated
vendored
Normal file
@ -0,0 +1,362 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package x509
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/pem"
|
||||
"time"
|
||||
|
||||
"github.com/google/certificate-transparency-go/asn1"
|
||||
"github.com/google/certificate-transparency-go/x509/pkix"
|
||||
)
|
||||
|
||||
var (
|
||||
// OID values for CRL extensions (TBSCertList.Extensions), RFC 5280 s5.2.
|
||||
OIDExtensionCRLNumber = asn1.ObjectIdentifier{2, 5, 29, 20}
|
||||
OIDExtensionDeltaCRLIndicator = asn1.ObjectIdentifier{2, 5, 29, 27}
|
||||
OIDExtensionIssuingDistributionPoint = asn1.ObjectIdentifier{2, 5, 29, 28}
|
||||
// OID values for CRL entry extensions (RevokedCertificate.Extensions), RFC 5280 s5.3
|
||||
OIDExtensionCRLReasons = asn1.ObjectIdentifier{2, 5, 29, 21}
|
||||
OIDExtensionInvalidityDate = asn1.ObjectIdentifier{2, 5, 29, 24}
|
||||
OIDExtensionCertificateIssuer = asn1.ObjectIdentifier{2, 5, 29, 29}
|
||||
)
|
||||
|
||||
// RevocationReasonCode represents the reason for a certificate revocation; see RFC 5280 s5.3.1.
|
||||
type RevocationReasonCode asn1.Enumerated
|
||||
|
||||
// RevocationReasonCode values.
|
||||
var (
|
||||
Unspecified = RevocationReasonCode(0)
|
||||
KeyCompromise = RevocationReasonCode(1)
|
||||
CACompromise = RevocationReasonCode(2)
|
||||
AffiliationChanged = RevocationReasonCode(3)
|
||||
Superseded = RevocationReasonCode(4)
|
||||
CessationOfOperation = RevocationReasonCode(5)
|
||||
CertificateHold = RevocationReasonCode(6)
|
||||
RemoveFromCRL = RevocationReasonCode(8)
|
||||
PrivilegeWithdrawn = RevocationReasonCode(9)
|
||||
AACompromise = RevocationReasonCode(10)
|
||||
)
|
||||
|
||||
// ReasonFlag holds a bitmask of applicable revocation reasons, from RFC 5280 s4.2.1.13
|
||||
type ReasonFlag int
|
||||
|
||||
// ReasonFlag values.
|
||||
const (
|
||||
UnusedFlag ReasonFlag = 1 << iota
|
||||
KeyCompromiseFlag
|
||||
CACompromiseFlag
|
||||
AffiliationChangedFlag
|
||||
SupersededFlag
|
||||
CessationOfOperationFlag
|
||||
CertificateHoldFlag
|
||||
PrivilegeWithdrawnFlag
|
||||
AACompromiseFlag
|
||||
)
|
||||
|
||||
// CertificateList represents the ASN.1 structure of the same name from RFC 5280, s5.1.
|
||||
// It has the same content as pkix.CertificateList, but the contents include parsed versions
|
||||
// of any extensions.
|
||||
type CertificateList struct {
|
||||
Raw asn1.RawContent
|
||||
TBSCertList TBSCertList
|
||||
SignatureAlgorithm pkix.AlgorithmIdentifier
|
||||
SignatureValue asn1.BitString
|
||||
}
|
||||
|
||||
// ExpiredAt reports whether now is past the expiry time of certList.
|
||||
func (certList *CertificateList) ExpiredAt(now time.Time) bool {
|
||||
return now.After(certList.TBSCertList.NextUpdate)
|
||||
}
|
||||
|
||||
// Indication of whether extensions need to be critical or non-critical. Extensions that
|
||||
// can be either are omitted from the map.
|
||||
var listExtCritical = map[string]bool{
|
||||
// From RFC 5280...
|
||||
OIDExtensionAuthorityKeyId.String(): false, // s5.2.1
|
||||
OIDExtensionIssuerAltName.String(): false, // s5.2.2
|
||||
OIDExtensionCRLNumber.String(): false, // s5.2.3
|
||||
OIDExtensionDeltaCRLIndicator.String(): true, // s5.2.4
|
||||
OIDExtensionIssuingDistributionPoint.String(): true, // s5.2.5
|
||||
OIDExtensionFreshestCRL.String(): false, // s5.2.6
|
||||
OIDExtensionAuthorityInfoAccess.String(): false, // s5.2.7
|
||||
}
|
||||
|
||||
var certExtCritical = map[string]bool{
|
||||
// From RFC 5280...
|
||||
OIDExtensionCRLReasons.String(): false, // s5.3.1
|
||||
OIDExtensionInvalidityDate.String(): false, // s5.3.2
|
||||
OIDExtensionCertificateIssuer.String(): true, // s5.3.3
|
||||
}
|
||||
|
||||
// IssuingDistributionPoint represents the ASN.1 structure of the same
|
||||
// name
|
||||
type IssuingDistributionPoint struct {
|
||||
DistributionPoint distributionPointName `asn1:"optional,tag:0"`
|
||||
OnlyContainsUserCerts bool `asn1:"optional,tag:1"`
|
||||
OnlyContainsCACerts bool `asn1:"optional,tag:2"`
|
||||
OnlySomeReasons asn1.BitString `asn1:"optional,tag:3"`
|
||||
IndirectCRL bool `asn1:"optional,tag:4"`
|
||||
OnlyContainsAttributeCerts bool `asn1:"optional,tag:5"`
|
||||
}
|
||||
|
||||
// TBSCertList represents the ASN.1 structure of the same name from RFC
|
||||
// 5280, section 5.1. It has the same content as pkix.TBSCertificateList
|
||||
// but the extensions are included in a parsed format.
|
||||
type TBSCertList struct {
|
||||
Raw asn1.RawContent
|
||||
Version int
|
||||
Signature pkix.AlgorithmIdentifier
|
||||
Issuer pkix.RDNSequence
|
||||
ThisUpdate time.Time
|
||||
NextUpdate time.Time
|
||||
RevokedCertificates []*RevokedCertificate
|
||||
Extensions []pkix.Extension
|
||||
// Cracked out extensions:
|
||||
AuthorityKeyID []byte
|
||||
IssuerAltNames GeneralNames
|
||||
CRLNumber int
|
||||
BaseCRLNumber int // -1 if no delta CRL present
|
||||
IssuingDistributionPoint IssuingDistributionPoint
|
||||
IssuingDPFullNames GeneralNames
|
||||
FreshestCRLDistributionPoint []string
|
||||
OCSPServer []string
|
||||
IssuingCertificateURL []string
|
||||
}
|
||||
|
||||
// ParseCertificateList parses a CertificateList (e.g. a CRL) from the given
|
||||
// bytes. It's often the case that PEM encoded CRLs will appear where they
|
||||
// should be DER encoded, so this function will transparently handle PEM
|
||||
// encoding as long as there isn't any leading garbage.
|
||||
func ParseCertificateList(clBytes []byte) (*CertificateList, error) {
|
||||
if bytes.HasPrefix(clBytes, pemCRLPrefix) {
|
||||
block, _ := pem.Decode(clBytes)
|
||||
if block != nil && block.Type == pemType {
|
||||
clBytes = block.Bytes
|
||||
}
|
||||
}
|
||||
return ParseCertificateListDER(clBytes)
|
||||
}
|
||||
|
||||
// ParseCertificateListDER parses a DER encoded CertificateList from the given bytes.
|
||||
// For non-fatal errors, this function returns both an error and a CertificateList
|
||||
// object.
|
||||
func ParseCertificateListDER(derBytes []byte) (*CertificateList, error) {
|
||||
var errs Errors
|
||||
// First parse the DER into the pkix structures.
|
||||
pkixList := new(pkix.CertificateList)
|
||||
if rest, err := asn1.Unmarshal(derBytes, pkixList); err != nil {
|
||||
errs.AddID(ErrInvalidCertList, err)
|
||||
return nil, &errs
|
||||
} else if len(rest) != 0 {
|
||||
errs.AddID(ErrTrailingCertList)
|
||||
return nil, &errs
|
||||
}
|
||||
|
||||
// Transcribe the revoked certs but crack out extensions.
|
||||
revokedCerts := make([]*RevokedCertificate, len(pkixList.TBSCertList.RevokedCertificates))
|
||||
for i, pkixRevoked := range pkixList.TBSCertList.RevokedCertificates {
|
||||
revokedCerts[i] = parseRevokedCertificate(pkixRevoked, &errs)
|
||||
if revokedCerts[i] == nil {
|
||||
return nil, &errs
|
||||
}
|
||||
}
|
||||
|
||||
certList := CertificateList{
|
||||
Raw: derBytes,
|
||||
TBSCertList: TBSCertList{
|
||||
Raw: pkixList.TBSCertList.Raw,
|
||||
Version: pkixList.TBSCertList.Version,
|
||||
Signature: pkixList.TBSCertList.Signature,
|
||||
Issuer: pkixList.TBSCertList.Issuer,
|
||||
ThisUpdate: pkixList.TBSCertList.ThisUpdate,
|
||||
NextUpdate: pkixList.TBSCertList.NextUpdate,
|
||||
RevokedCertificates: revokedCerts,
|
||||
Extensions: pkixList.TBSCertList.Extensions,
|
||||
CRLNumber: -1,
|
||||
BaseCRLNumber: -1,
|
||||
},
|
||||
SignatureAlgorithm: pkixList.SignatureAlgorithm,
|
||||
SignatureValue: pkixList.SignatureValue,
|
||||
}
|
||||
|
||||
// Now crack out extensions.
|
||||
for _, e := range certList.TBSCertList.Extensions {
|
||||
if expectCritical, present := listExtCritical[e.Id.String()]; present {
|
||||
if e.Critical && !expectCritical {
|
||||
errs.AddID(ErrUnexpectedlyCriticalCertListExtension, e.Id)
|
||||
} else if !e.Critical && expectCritical {
|
||||
errs.AddID(ErrUnexpectedlyNonCriticalCertListExtension, e.Id)
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case e.Id.Equal(OIDExtensionAuthorityKeyId):
|
||||
// RFC 5280 s5.2.1
|
||||
var a authKeyId
|
||||
if rest, err := asn1.Unmarshal(e.Value, &a); err != nil {
|
||||
errs.AddID(ErrInvalidCertListAuthKeyID, err)
|
||||
} else if len(rest) != 0 {
|
||||
errs.AddID(ErrTrailingCertListAuthKeyID)
|
||||
}
|
||||
certList.TBSCertList.AuthorityKeyID = a.Id
|
||||
case e.Id.Equal(OIDExtensionIssuerAltName):
|
||||
// RFC 5280 s5.2.2
|
||||
if err := parseGeneralNames(e.Value, &certList.TBSCertList.IssuerAltNames); err != nil {
|
||||
errs.AddID(ErrInvalidCertListIssuerAltName, err)
|
||||
}
|
||||
case e.Id.Equal(OIDExtensionCRLNumber):
|
||||
// RFC 5280 s5.2.3
|
||||
if rest, err := asn1.Unmarshal(e.Value, &certList.TBSCertList.CRLNumber); err != nil {
|
||||
errs.AddID(ErrInvalidCertListCRLNumber, err)
|
||||
} else if len(rest) != 0 {
|
||||
errs.AddID(ErrTrailingCertListCRLNumber)
|
||||
}
|
||||
if certList.TBSCertList.CRLNumber < 0 {
|
||||
errs.AddID(ErrNegativeCertListCRLNumber, certList.TBSCertList.CRLNumber)
|
||||
}
|
||||
case e.Id.Equal(OIDExtensionDeltaCRLIndicator):
|
||||
// RFC 5280 s5.2.4
|
||||
if rest, err := asn1.Unmarshal(e.Value, &certList.TBSCertList.BaseCRLNumber); err != nil {
|
||||
errs.AddID(ErrInvalidCertListDeltaCRL, err)
|
||||
} else if len(rest) != 0 {
|
||||
errs.AddID(ErrTrailingCertListDeltaCRL)
|
||||
}
|
||||
if certList.TBSCertList.BaseCRLNumber < 0 {
|
||||
errs.AddID(ErrNegativeCertListDeltaCRL, certList.TBSCertList.BaseCRLNumber)
|
||||
}
|
||||
case e.Id.Equal(OIDExtensionIssuingDistributionPoint):
|
||||
parseIssuingDistributionPoint(e.Value, &certList.TBSCertList.IssuingDistributionPoint, &certList.TBSCertList.IssuingDPFullNames, &errs)
|
||||
case e.Id.Equal(OIDExtensionFreshestCRL):
|
||||
// RFC 5280 s5.2.6
|
||||
if err := parseDistributionPoints(e.Value, &certList.TBSCertList.FreshestCRLDistributionPoint); err != nil {
|
||||
errs.AddID(ErrInvalidCertListFreshestCRL, err)
|
||||
return nil, err
|
||||
}
|
||||
case e.Id.Equal(OIDExtensionAuthorityInfoAccess):
|
||||
// RFC 5280 s5.2.7
|
||||
var aia []authorityInfoAccess
|
||||
if rest, err := asn1.Unmarshal(e.Value, &aia); err != nil {
|
||||
errs.AddID(ErrInvalidCertListAuthInfoAccess, err)
|
||||
} else if len(rest) != 0 {
|
||||
errs.AddID(ErrTrailingCertListAuthInfoAccess)
|
||||
}
|
||||
|
||||
for _, v := range aia {
|
||||
// GeneralName: uniformResourceIdentifier [6] IA5String
|
||||
if v.Location.Tag != tagURI {
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case v.Method.Equal(OIDAuthorityInfoAccessOCSP):
|
||||
certList.TBSCertList.OCSPServer = append(certList.TBSCertList.OCSPServer, string(v.Location.Bytes))
|
||||
case v.Method.Equal(OIDAuthorityInfoAccessIssuers):
|
||||
certList.TBSCertList.IssuingCertificateURL = append(certList.TBSCertList.IssuingCertificateURL, string(v.Location.Bytes))
|
||||
}
|
||||
// TODO(drysdale): cope with more possibilities
|
||||
}
|
||||
default:
|
||||
if e.Critical {
|
||||
errs.AddID(ErrUnhandledCriticalCertListExtension, e.Id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if errs.Fatal() {
|
||||
return nil, &errs
|
||||
}
|
||||
if errs.Empty() {
|
||||
return &certList, nil
|
||||
}
|
||||
return &certList, &errs
|
||||
}
|
||||
|
||||
func parseIssuingDistributionPoint(data []byte, idp *IssuingDistributionPoint, name *GeneralNames, errs *Errors) {
|
||||
// RFC 5280 s5.2.5
|
||||
if rest, err := asn1.Unmarshal(data, idp); err != nil {
|
||||
errs.AddID(ErrInvalidCertListIssuingDP, err)
|
||||
} else if len(rest) != 0 {
|
||||
errs.AddID(ErrTrailingCertListIssuingDP)
|
||||
}
|
||||
|
||||
typeCount := 0
|
||||
if idp.OnlyContainsUserCerts {
|
||||
typeCount++
|
||||
}
|
||||
if idp.OnlyContainsCACerts {
|
||||
typeCount++
|
||||
}
|
||||
if idp.OnlyContainsAttributeCerts {
|
||||
typeCount++
|
||||
}
|
||||
if typeCount > 1 {
|
||||
errs.AddID(ErrCertListIssuingDPMultipleTypes, idp.OnlyContainsUserCerts, idp.OnlyContainsCACerts, idp.OnlyContainsAttributeCerts)
|
||||
}
|
||||
for _, fn := range idp.DistributionPoint.FullName {
|
||||
if _, err := parseGeneralName(fn.FullBytes, name, false); err != nil {
|
||||
errs.AddID(ErrCertListIssuingDPInvalidFullName, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RevokedCertificate represents the unnamed ASN.1 structure that makes up the
|
||||
// revokedCertificates member of the TBSCertList structure from RFC 5280, s5.1.
|
||||
// It has the same content as pkix.RevokedCertificate but the extensions are
|
||||
// included in a parsed format.
|
||||
type RevokedCertificate struct {
|
||||
pkix.RevokedCertificate
|
||||
// Cracked out extensions:
|
||||
RevocationReason RevocationReasonCode
|
||||
InvalidityDate time.Time
|
||||
Issuer GeneralNames
|
||||
}
|
||||
|
||||
func parseRevokedCertificate(pkixRevoked pkix.RevokedCertificate, errs *Errors) *RevokedCertificate {
|
||||
result := RevokedCertificate{RevokedCertificate: pkixRevoked}
|
||||
for _, e := range pkixRevoked.Extensions {
|
||||
if expectCritical, present := certExtCritical[e.Id.String()]; present {
|
||||
if e.Critical && !expectCritical {
|
||||
errs.AddID(ErrUnexpectedlyCriticalRevokedCertExtension, e.Id)
|
||||
} else if !e.Critical && expectCritical {
|
||||
errs.AddID(ErrUnexpectedlyNonCriticalRevokedCertExtension, e.Id)
|
||||
}
|
||||
}
|
||||
switch {
|
||||
case e.Id.Equal(OIDExtensionCRLReasons):
|
||||
// RFC 5280, s5.3.1
|
||||
var reason asn1.Enumerated
|
||||
if rest, err := asn1.Unmarshal(e.Value, &reason); err != nil {
|
||||
errs.AddID(ErrInvalidRevocationReason, err)
|
||||
} else if len(rest) != 0 {
|
||||
errs.AddID(ErrTrailingRevocationReason)
|
||||
}
|
||||
result.RevocationReason = RevocationReasonCode(reason)
|
||||
case e.Id.Equal(OIDExtensionInvalidityDate):
|
||||
// RFC 5280, s5.3.2
|
||||
if rest, err := asn1.Unmarshal(e.Value, &result.InvalidityDate); err != nil {
|
||||
errs.AddID(ErrInvalidRevocationInvalidityDate, err)
|
||||
} else if len(rest) != 0 {
|
||||
errs.AddID(ErrTrailingRevocationInvalidityDate)
|
||||
}
|
||||
case e.Id.Equal(OIDExtensionCertificateIssuer):
|
||||
// RFC 5280, s5.3.3
|
||||
if err := parseGeneralNames(e.Value, &result.Issuer); err != nil {
|
||||
errs.AddID(ErrInvalidRevocationIssuer, err)
|
||||
}
|
||||
default:
|
||||
if e.Critical {
|
||||
errs.AddID(ErrUnhandledCriticalRevokedCertExtension, e.Id)
|
||||
}
|
||||
}
|
||||
}
|
||||
return &result
|
||||
}
|
||||
|
||||
// CheckCertificateListSignature checks that the signature in crl is from c.
|
||||
func (c *Certificate) CheckCertificateListSignature(crl *CertificateList) error {
|
||||
algo := SignatureAlgorithmFromAI(crl.SignatureAlgorithm)
|
||||
return c.CheckSignature(algo, crl.TBSCertList.Raw, crl.SignatureValue.RightAlign())
|
||||
}
|
2123
vendor/github.com/google/certificate-transparency-go/x509/revoked_test.go
generated
vendored
Normal file
2123
vendor/github.com/google/certificate-transparency-go/x509/revoked_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
22
vendor/github.com/google/certificate-transparency-go/x509/root.go
generated
vendored
Normal file
22
vendor/github.com/google/certificate-transparency-go/x509/root.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2012 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 x509
|
||||
|
||||
import "sync"
|
||||
|
||||
var (
|
||||
once sync.Once
|
||||
systemRoots *CertPool
|
||||
systemRootsErr error
|
||||
)
|
||||
|
||||
func systemRootsPool() *CertPool {
|
||||
once.Do(initSystemRoots)
|
||||
return systemRoots
|
||||
}
|
||||
|
||||
func initSystemRoots() {
|
||||
systemRoots, systemRootsErr = loadSystemRoots()
|
||||
}
|
15
vendor/github.com/google/certificate-transparency-go/x509/root_bsd.go
generated
vendored
Normal file
15
vendor/github.com/google/certificate-transparency-go/x509/root_bsd.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
// 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.
|
||||
|
||||
// +build dragonfly freebsd netbsd openbsd
|
||||
|
||||
package x509
|
||||
|
||||
// Possible certificate files; stop after finding one.
|
||||
var certFiles = []string{
|
||||
"/usr/local/etc/ssl/cert.pem", // FreeBSD
|
||||
"/etc/ssl/cert.pem", // OpenBSD
|
||||
"/usr/local/share/certs/ca-root-nss.crt", // DragonFly
|
||||
"/etc/openssl/certs/ca-certificates.crt", // NetBSD
|
||||
}
|
252
vendor/github.com/google/certificate-transparency-go/x509/root_cgo_darwin.go
generated
vendored
Normal file
252
vendor/github.com/google/certificate-transparency-go/x509/root_cgo_darwin.go
generated
vendored
Normal file
@ -0,0 +1,252 @@
|
||||
// Copyright 2011 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 cgo,!arm,!arm64,!ios
|
||||
|
||||
package x509
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1080
|
||||
#cgo LDFLAGS: -framework CoreFoundation -framework Security
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <Security/Security.h>
|
||||
|
||||
// FetchPEMRootsCTX509_MountainLion is the version of FetchPEMRoots from Go 1.6
|
||||
// which still works on OS X 10.8 (Mountain Lion).
|
||||
// It lacks support for admin & user cert domains.
|
||||
// See golang.org/issue/16473
|
||||
int FetchPEMRootsCTX509_MountainLion(CFDataRef *pemRoots) {
|
||||
if (pemRoots == NULL) {
|
||||
return -1;
|
||||
}
|
||||
CFArrayRef certs = NULL;
|
||||
OSStatus err = SecTrustCopyAnchorCertificates(&certs);
|
||||
if (err != noErr) {
|
||||
return -1;
|
||||
}
|
||||
CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
|
||||
int i, ncerts = CFArrayGetCount(certs);
|
||||
for (i = 0; i < ncerts; i++) {
|
||||
CFDataRef data = NULL;
|
||||
SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, i);
|
||||
if (cert == NULL) {
|
||||
continue;
|
||||
}
|
||||
// Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport.
|
||||
// Once we support weak imports via cgo we should prefer that, and fall back to this
|
||||
// for older systems.
|
||||
err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
|
||||
if (err != noErr) {
|
||||
continue;
|
||||
}
|
||||
if (data != NULL) {
|
||||
CFDataAppendBytes(combinedData, CFDataGetBytePtr(data), CFDataGetLength(data));
|
||||
CFRelease(data);
|
||||
}
|
||||
}
|
||||
CFRelease(certs);
|
||||
*pemRoots = combinedData;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// useOldCodeCTX509 reports whether the running machine is OS X 10.8 Mountain Lion
|
||||
// or older. We only support Mountain Lion and higher, but we'll at least try our
|
||||
// best on older machines and continue to use the old code path.
|
||||
//
|
||||
// See golang.org/issue/16473
|
||||
int useOldCodeCTX509() {
|
||||
char str[256];
|
||||
size_t size = sizeof(str);
|
||||
memset(str, 0, size);
|
||||
sysctlbyname("kern.osrelease", str, &size, NULL, 0);
|
||||
// OS X 10.8 is osrelease "12.*", 10.7 is 11.*, 10.6 is 10.*.
|
||||
// We never supported things before that.
|
||||
return memcmp(str, "12.", 3) == 0 || memcmp(str, "11.", 3) == 0 || memcmp(str, "10.", 3) == 0;
|
||||
}
|
||||
|
||||
// FetchPEMRootsCTX509 fetches the system's list of trusted X.509 root certificates.
|
||||
//
|
||||
// On success it returns 0 and fills pemRoots with a CFDataRef that contains the extracted root
|
||||
// certificates of the system. On failure, the function returns -1.
|
||||
// Additionally, it fills untrustedPemRoots with certs that must be removed from pemRoots.
|
||||
//
|
||||
// Note: The CFDataRef returned in pemRoots and untrustedPemRoots must
|
||||
// be released (using CFRelease) after we've consumed its content.
|
||||
int FetchPEMRootsCTX509(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) {
|
||||
if (useOldCodeCTX509()) {
|
||||
return FetchPEMRootsCTX509_MountainLion(pemRoots);
|
||||
}
|
||||
|
||||
// Get certificates from all domains, not just System, this lets
|
||||
// the user add CAs to their "login" keychain, and Admins to add
|
||||
// to the "System" keychain
|
||||
SecTrustSettingsDomain domains[] = { kSecTrustSettingsDomainSystem,
|
||||
kSecTrustSettingsDomainAdmin,
|
||||
kSecTrustSettingsDomainUser };
|
||||
|
||||
int numDomains = sizeof(domains)/sizeof(SecTrustSettingsDomain);
|
||||
if (pemRoots == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// kSecTrustSettingsResult is defined as CFSTR("kSecTrustSettingsResult"),
|
||||
// but the Go linker's internal linking mode can't handle CFSTR relocations.
|
||||
// Create our own dynamic string instead and release it below.
|
||||
CFStringRef policy = CFStringCreateWithCString(NULL, "kSecTrustSettingsResult", kCFStringEncodingUTF8);
|
||||
|
||||
CFMutableDataRef combinedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
|
||||
CFMutableDataRef combinedUntrustedData = CFDataCreateMutable(kCFAllocatorDefault, 0);
|
||||
for (int i = 0; i < numDomains; i++) {
|
||||
CFArrayRef certs = NULL;
|
||||
OSStatus err = SecTrustSettingsCopyCertificates(domains[i], &certs);
|
||||
if (err != noErr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
CFIndex numCerts = CFArrayGetCount(certs);
|
||||
for (int j = 0; j < numCerts; j++) {
|
||||
CFDataRef data = NULL;
|
||||
CFErrorRef errRef = NULL;
|
||||
CFArrayRef trustSettings = NULL;
|
||||
SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(certs, j);
|
||||
if (cert == NULL) {
|
||||
continue;
|
||||
}
|
||||
// We only want trusted certs.
|
||||
int untrusted = 0;
|
||||
int trustAsRoot = 0;
|
||||
int trustRoot = 0;
|
||||
if (i == 0) {
|
||||
trustAsRoot = 1;
|
||||
} else {
|
||||
// Certs found in the system domain are always trusted. If the user
|
||||
// configures "Never Trust" on such a cert, it will also be found in the
|
||||
// admin or user domain, causing it to be added to untrustedPemRoots. The
|
||||
// Go code will then clean this up.
|
||||
|
||||
// Trust may be stored in any of the domains. According to Apple's
|
||||
// SecTrustServer.c, "user trust settings overrule admin trust settings",
|
||||
// so take the last trust settings array we find.
|
||||
// Skip the system domain since it is always trusted.
|
||||
for (int k = i; k < numDomains; k++) {
|
||||
CFArrayRef domainTrustSettings = NULL;
|
||||
err = SecTrustSettingsCopyTrustSettings(cert, domains[k], &domainTrustSettings);
|
||||
if (err == errSecSuccess && domainTrustSettings != NULL) {
|
||||
if (trustSettings) {
|
||||
CFRelease(trustSettings);
|
||||
}
|
||||
trustSettings = domainTrustSettings;
|
||||
}
|
||||
}
|
||||
if (trustSettings == NULL) {
|
||||
// "this certificate must be verified to a known trusted certificate"; aka not a root.
|
||||
continue;
|
||||
}
|
||||
for (CFIndex k = 0; k < CFArrayGetCount(trustSettings); k++) {
|
||||
CFNumberRef cfNum;
|
||||
CFDictionaryRef tSetting = (CFDictionaryRef)CFArrayGetValueAtIndex(trustSettings, k);
|
||||
if (CFDictionaryGetValueIfPresent(tSetting, policy, (const void**)&cfNum)){
|
||||
SInt32 result = 0;
|
||||
CFNumberGetValue(cfNum, kCFNumberSInt32Type, &result);
|
||||
// TODO: The rest of the dictionary specifies conditions for evaluation.
|
||||
if (result == kSecTrustSettingsResultDeny) {
|
||||
untrusted = 1;
|
||||
} else if (result == kSecTrustSettingsResultTrustAsRoot) {
|
||||
trustAsRoot = 1;
|
||||
} else if (result == kSecTrustSettingsResultTrustRoot) {
|
||||
trustRoot = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
CFRelease(trustSettings);
|
||||
}
|
||||
|
||||
if (trustRoot) {
|
||||
// We only want to add Root CAs, so make sure Subject and Issuer Name match
|
||||
CFDataRef subjectName = SecCertificateCopyNormalizedSubjectContent(cert, &errRef);
|
||||
if (errRef != NULL) {
|
||||
CFRelease(errRef);
|
||||
continue;
|
||||
}
|
||||
CFDataRef issuerName = SecCertificateCopyNormalizedIssuerContent(cert, &errRef);
|
||||
if (errRef != NULL) {
|
||||
CFRelease(subjectName);
|
||||
CFRelease(errRef);
|
||||
continue;
|
||||
}
|
||||
Boolean equal = CFEqual(subjectName, issuerName);
|
||||
CFRelease(subjectName);
|
||||
CFRelease(issuerName);
|
||||
if (!equal) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Note: SecKeychainItemExport is deprecated as of 10.7 in favor of SecItemExport.
|
||||
// Once we support weak imports via cgo we should prefer that, and fall back to this
|
||||
// for older systems.
|
||||
err = SecKeychainItemExport(cert, kSecFormatX509Cert, kSecItemPemArmour, NULL, &data);
|
||||
if (err != noErr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (data != NULL) {
|
||||
if (!trustRoot && !trustAsRoot) {
|
||||
untrusted = 1;
|
||||
}
|
||||
CFMutableDataRef appendTo = untrusted ? combinedUntrustedData : combinedData;
|
||||
CFDataAppendBytes(appendTo, CFDataGetBytePtr(data), CFDataGetLength(data));
|
||||
CFRelease(data);
|
||||
}
|
||||
}
|
||||
CFRelease(certs);
|
||||
}
|
||||
CFRelease(policy);
|
||||
*pemRoots = combinedData;
|
||||
*untrustedPemRoots = combinedUntrustedData;
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func loadSystemRoots() (*CertPool, error) {
|
||||
roots := NewCertPool()
|
||||
|
||||
var data C.CFDataRef
|
||||
setNilCFRef(&data)
|
||||
var untrustedData C.CFDataRef
|
||||
setNilCFRef(&untrustedData)
|
||||
err := C.FetchPEMRootsCTX509(&data, &untrustedData)
|
||||
if err == -1 {
|
||||
// TODO: better error message
|
||||
return nil, errors.New("crypto/x509: failed to load darwin system roots with cgo")
|
||||
}
|
||||
|
||||
defer C.CFRelease(C.CFTypeRef(data))
|
||||
buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data)))
|
||||
roots.AppendCertsFromPEM(buf)
|
||||
if isNilCFRef(untrustedData) {
|
||||
return roots, nil
|
||||
}
|
||||
defer C.CFRelease(C.CFTypeRef(untrustedData))
|
||||
buf = C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(untrustedData)), C.int(C.CFDataGetLength(untrustedData)))
|
||||
untrustedRoots := NewCertPool()
|
||||
untrustedRoots.AppendCertsFromPEM(buf)
|
||||
|
||||
trustedRoots := NewCertPool()
|
||||
for _, c := range roots.certs {
|
||||
if !untrustedRoots.contains(c) {
|
||||
trustedRoots.AddCert(c)
|
||||
}
|
||||
}
|
||||
return trustedRoots, nil
|
||||
}
|
264
vendor/github.com/google/certificate-transparency-go/x509/root_darwin.go
generated
vendored
Normal file
264
vendor/github.com/google/certificate-transparency-go/x509/root_darwin.go
generated
vendored
Normal file
@ -0,0 +1,264 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:generate go run root_darwin_arm_gen.go -output root_darwin_armx.go
|
||||
|
||||
package x509
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var debugExecDarwinRoots = strings.Contains(os.Getenv("GODEBUG"), "x509roots=1")
|
||||
|
||||
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// This code is only used when compiling without cgo.
|
||||
// It is here, instead of root_nocgo_darwin.go, so that tests can check it
|
||||
// even if the tests are run with cgo enabled.
|
||||
// The linker will not include these unused functions in binaries built with cgo enabled.
|
||||
|
||||
// execSecurityRoots finds the macOS list of trusted root certificates
|
||||
// using only command-line tools. This is our fallback path when cgo isn't available.
|
||||
//
|
||||
// The strategy is as follows:
|
||||
//
|
||||
// 1. Run "security trust-settings-export" and "security
|
||||
// trust-settings-export -d" to discover the set of certs with some
|
||||
// user-tweaked trust policy. We're too lazy to parse the XML (at
|
||||
// least at this stage of Go 1.8) to understand what the trust
|
||||
// policy actually is. We just learn that there is _some_ policy.
|
||||
//
|
||||
// 2. Run "security find-certificate" to dump the list of system root
|
||||
// CAs in PEM format.
|
||||
//
|
||||
// 3. For each dumped cert, conditionally verify it with "security
|
||||
// verify-cert" if that cert was in the set discovered in Step 1.
|
||||
// Without the Step 1 optimization, running "security verify-cert"
|
||||
// 150-200 times takes 3.5 seconds. With the optimization, the
|
||||
// whole process takes about 180 milliseconds with 1 untrusted root
|
||||
// CA. (Compared to 110ms in the cgo path)
|
||||
func execSecurityRoots() (*CertPool, error) {
|
||||
hasPolicy, err := getCertsWithTrustPolicy()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if debugExecDarwinRoots {
|
||||
println(fmt.Sprintf("crypto/x509: %d certs have a trust policy", len(hasPolicy)))
|
||||
}
|
||||
|
||||
args := []string{"find-certificate", "-a", "-p",
|
||||
"/System/Library/Keychains/SystemRootCertificates.keychain",
|
||||
"/Library/Keychains/System.keychain",
|
||||
}
|
||||
|
||||
u, err := user.Current()
|
||||
if err != nil {
|
||||
if debugExecDarwinRoots {
|
||||
println(fmt.Sprintf("crypto/x509: get current user: %v", err))
|
||||
}
|
||||
} else {
|
||||
args = append(args,
|
||||
filepath.Join(u.HomeDir, "/Library/Keychains/login.keychain"),
|
||||
|
||||
// Fresh installs of Sierra use a slightly different path for the login keychain
|
||||
filepath.Join(u.HomeDir, "/Library/Keychains/login.keychain-db"),
|
||||
)
|
||||
}
|
||||
|
||||
cmd := exec.Command("/usr/bin/security", args...)
|
||||
data, err := cmd.Output()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
roots = NewCertPool()
|
||||
numVerified int // number of execs of 'security verify-cert', for debug stats
|
||||
)
|
||||
|
||||
blockCh := make(chan *pem.Block)
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// Using 4 goroutines to pipe into verify-cert seems to be
|
||||
// about the best we can do. The verify-cert binary seems to
|
||||
// just RPC to another server with coarse locking anyway, so
|
||||
// running 16 at a time for instance doesn't help at all. Due
|
||||
// to the "if hasPolicy" check below, though, we will rarely
|
||||
// (or never) call verify-cert on stock macOS systems, though.
|
||||
// The hope is that we only call verify-cert when the user has
|
||||
// tweaked their trust policy. These 4 goroutines are only
|
||||
// defensive in the pathological case of many trust edits.
|
||||
for i := 0; i < 4; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for block := range blockCh {
|
||||
cert, err := ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
sha1CapHex := fmt.Sprintf("%X", sha1.Sum(block.Bytes))
|
||||
|
||||
valid := true
|
||||
verifyChecks := 0
|
||||
if hasPolicy[sha1CapHex] {
|
||||
verifyChecks++
|
||||
if !verifyCertWithSystem(block, cert) {
|
||||
valid = false
|
||||
}
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
numVerified += verifyChecks
|
||||
if valid {
|
||||
roots.AddCert(cert)
|
||||
}
|
||||
mu.Unlock()
|
||||
}
|
||||
}()
|
||||
}
|
||||
for len(data) > 0 {
|
||||
var block *pem.Block
|
||||
block, data = pem.Decode(data)
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
|
||||
continue
|
||||
}
|
||||
blockCh <- block
|
||||
}
|
||||
close(blockCh)
|
||||
wg.Wait()
|
||||
|
||||
if debugExecDarwinRoots {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
println(fmt.Sprintf("crypto/x509: ran security verify-cert %d times", numVerified))
|
||||
}
|
||||
|
||||
return roots, nil
|
||||
}
|
||||
|
||||
func verifyCertWithSystem(block *pem.Block, cert *Certificate) bool {
|
||||
data := pem.EncodeToMemory(block)
|
||||
|
||||
f, err := ioutil.TempFile("", "cert")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "can't create temporary file for cert: %v", err)
|
||||
return false
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
if _, err := f.Write(data); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "can't write temporary file for cert: %v", err)
|
||||
return false
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "can't write temporary file for cert: %v", err)
|
||||
return false
|
||||
}
|
||||
cmd := exec.Command("/usr/bin/security", "verify-cert", "-c", f.Name(), "-l", "-L")
|
||||
var stderr bytes.Buffer
|
||||
if debugExecDarwinRoots {
|
||||
cmd.Stderr = &stderr
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
if debugExecDarwinRoots {
|
||||
println(fmt.Sprintf("crypto/x509: verify-cert rejected %s: %q", cert.Subject.CommonName, bytes.TrimSpace(stderr.Bytes())))
|
||||
}
|
||||
return false
|
||||
}
|
||||
if debugExecDarwinRoots {
|
||||
println(fmt.Sprintf("crypto/x509: verify-cert approved %s", cert.Subject.CommonName))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// getCertsWithTrustPolicy returns the set of certs that have a
|
||||
// possibly-altered trust policy. The keys of the map are capitalized
|
||||
// sha1 hex of the raw cert.
|
||||
// They are the certs that should be checked against `security
|
||||
// verify-cert` to see whether the user altered the default trust
|
||||
// settings. This code is only used for cgo-disabled builds.
|
||||
func getCertsWithTrustPolicy() (map[string]bool, error) {
|
||||
set := map[string]bool{}
|
||||
td, err := ioutil.TempDir("", "x509trustpolicy")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer os.RemoveAll(td)
|
||||
run := func(file string, args ...string) error {
|
||||
file = filepath.Join(td, file)
|
||||
args = append(args, file)
|
||||
cmd := exec.Command("/usr/bin/security", args...)
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stderr = &stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
// If there are no trust settings, the
|
||||
// `security trust-settings-export` command
|
||||
// fails with:
|
||||
// exit status 1, SecTrustSettingsCreateExternalRepresentation: No Trust Settings were found.
|
||||
// Rather than match on English substrings that are probably
|
||||
// localized on macOS, just interpret any failure to mean that
|
||||
// there are no trust settings.
|
||||
if debugExecDarwinRoots {
|
||||
println(fmt.Sprintf("crypto/x509: exec %q: %v, %s", cmd.Args, err, stderr.Bytes()))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Gather all the runs of 40 capitalized hex characters.
|
||||
br := bufio.NewReader(f)
|
||||
var hexBuf bytes.Buffer
|
||||
for {
|
||||
b, err := br.ReadByte()
|
||||
isHex := ('A' <= b && b <= 'F') || ('0' <= b && b <= '9')
|
||||
if isHex {
|
||||
hexBuf.WriteByte(b)
|
||||
} else {
|
||||
if hexBuf.Len() == 40 {
|
||||
set[hexBuf.String()] = true
|
||||
}
|
||||
hexBuf.Reset()
|
||||
}
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
if err := run("user", "trust-settings-export"); err != nil {
|
||||
return nil, fmt.Errorf("dump-trust-settings (user): %v", err)
|
||||
}
|
||||
if err := run("admin", "trust-settings-export", "-d"); err != nil {
|
||||
return nil, fmt.Errorf("dump-trust-settings (admin): %v", err)
|
||||
}
|
||||
return set, nil
|
||||
}
|
187
vendor/github.com/google/certificate-transparency-go/x509/root_darwin_arm_gen.go
generated
vendored
Normal file
187
vendor/github.com/google/certificate-transparency-go/x509/root_darwin_arm_gen.go
generated
vendored
Normal file
@ -0,0 +1,187 @@
|
||||
// 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.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// Generates root_darwin_armx.go.
|
||||
//
|
||||
// As of iOS 8, there is no API for querying the system trusted X.509 root
|
||||
// certificates. We could use SecTrustEvaluate to verify that a trust chain
|
||||
// exists for a certificate, but the x509 API requires returning the entire
|
||||
// chain.
|
||||
//
|
||||
// Apple publishes the list of trusted root certificates for iOS on
|
||||
// support.apple.com. So we parse the list and extract the certificates from
|
||||
// an OS X machine and embed them into the x509 package.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
)
|
||||
|
||||
var output = flag.String("output", "root_darwin_armx.go", "file name to write")
|
||||
|
||||
func main() {
|
||||
certs, err := selectCerts()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
|
||||
fmt.Fprintf(buf, "// Code generated by root_darwin_arm_gen --output %s; DO NOT EDIT.\n", *output)
|
||||
fmt.Fprintf(buf, "%s", header)
|
||||
|
||||
fmt.Fprintf(buf, "const systemRootsPEM = `\n")
|
||||
for _, cert := range certs {
|
||||
b := &pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: cert.Raw,
|
||||
}
|
||||
if err := pem.Encode(buf, b); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
fmt.Fprintf(buf, "`")
|
||||
|
||||
source, err := format.Source(buf.Bytes())
|
||||
if err != nil {
|
||||
log.Fatal("source format error:", err)
|
||||
}
|
||||
if err := ioutil.WriteFile(*output, source, 0644); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func selectCerts() ([]*x509.Certificate, error) {
|
||||
ids, err := fetchCertIDs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scerts, err := sysCerts()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var certs []*x509.Certificate
|
||||
for _, id := range ids {
|
||||
if c, ok := scerts[id.fingerprint]; ok {
|
||||
certs = append(certs, c)
|
||||
} else {
|
||||
fmt.Printf("WARNING: cannot find certificate: %s (fingerprint: %s)\n", id.name, id.fingerprint)
|
||||
}
|
||||
}
|
||||
return certs, nil
|
||||
}
|
||||
|
||||
func sysCerts() (certs map[string]*x509.Certificate, err error) {
|
||||
cmd := exec.Command("/usr/bin/security", "find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain")
|
||||
data, err := cmd.Output()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
certs = make(map[string]*x509.Certificate)
|
||||
for len(data) > 0 {
|
||||
var block *pem.Block
|
||||
block, data = pem.Decode(data)
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
fingerprint := sha256.Sum256(cert.Raw)
|
||||
certs[hex.EncodeToString(fingerprint[:])] = cert
|
||||
}
|
||||
return certs, nil
|
||||
}
|
||||
|
||||
type certID struct {
|
||||
name string
|
||||
fingerprint string
|
||||
}
|
||||
|
||||
// fetchCertIDs fetches IDs of iOS X509 certificates from apple.com.
|
||||
func fetchCertIDs() ([]certID, error) {
|
||||
// Download the iOS 11 support page. The index for all iOS versions is here:
|
||||
// https://support.apple.com/en-us/HT204132
|
||||
resp, err := http.Get("https://support.apple.com/en-us/HT208125")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
text := string(body)
|
||||
text = text[strings.Index(text, "<div id=trusted"):]
|
||||
text = text[:strings.Index(text, "</div>")]
|
||||
|
||||
var ids []certID
|
||||
cols := make(map[string]int)
|
||||
for i, rowmatch := range regexp.MustCompile("(?s)<tr>(.*?)</tr>").FindAllStringSubmatch(text, -1) {
|
||||
row := rowmatch[1]
|
||||
if i == 0 {
|
||||
// Parse table header row to extract column names
|
||||
for i, match := range regexp.MustCompile("(?s)<th>(.*?)</th>").FindAllStringSubmatch(row, -1) {
|
||||
cols[match[1]] = i
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
values := regexp.MustCompile("(?s)<td>(.*?)</td>").FindAllStringSubmatch(row, -1)
|
||||
name := values[cols["Certificate name"]][1]
|
||||
fingerprint := values[cols["Fingerprint (SHA-256)"]][1]
|
||||
fingerprint = strings.Replace(fingerprint, "<br>", "", -1)
|
||||
fingerprint = strings.Replace(fingerprint, "\n", "", -1)
|
||||
fingerprint = strings.Replace(fingerprint, " ", "", -1)
|
||||
fingerprint = strings.ToLower(fingerprint)
|
||||
|
||||
ids = append(ids, certID{
|
||||
name: name,
|
||||
fingerprint: fingerprint,
|
||||
})
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
const header = `
|
||||
// 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.
|
||||
|
||||
// +build cgo
|
||||
// +build darwin
|
||||
// +build arm arm64 ios
|
||||
|
||||
package x509
|
||||
|
||||
func loadSystemRoots() (*CertPool, error) {
|
||||
p := NewCertPool()
|
||||
p.AppendCertsFromPEM([]byte(systemRootsPEM))
|
||||
return p, nil
|
||||
}
|
||||
`
|
4313
vendor/github.com/google/certificate-transparency-go/x509/root_darwin_armx.go
generated
vendored
Normal file
4313
vendor/github.com/google/certificate-transparency-go/x509/root_darwin_armx.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
80
vendor/github.com/google/certificate-transparency-go/x509/root_darwin_test.go
generated
vendored
Normal file
80
vendor/github.com/google/certificate-transparency-go/x509/root_darwin_test.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright 2013 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 x509
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestSystemRoots(t *testing.T) {
|
||||
switch runtime.GOARCH {
|
||||
case "arm", "arm64":
|
||||
t.Skipf("skipping on %s/%s, no system root", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
t.Skipf("skipping on %s/%s until cgo part of golang.org/issue/16532 has been implemented.", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
t0 := time.Now()
|
||||
sysRoots := systemRootsPool() // actual system roots
|
||||
sysRootsDuration := time.Since(t0)
|
||||
|
||||
t1 := time.Now()
|
||||
execRoots, err := execSecurityRoots() // non-cgo roots
|
||||
execSysRootsDuration := time.Since(t1)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read system roots: %v", err)
|
||||
}
|
||||
|
||||
t.Logf(" cgo sys roots: %v", sysRootsDuration)
|
||||
t.Logf("non-cgo sys roots: %v", execSysRootsDuration)
|
||||
|
||||
for _, tt := range []*CertPool{sysRoots, execRoots} {
|
||||
if tt == nil {
|
||||
t.Fatal("no system roots")
|
||||
}
|
||||
// On Mavericks, there are 212 bundled certs, at least
|
||||
// there was at one point in time on one machine.
|
||||
// (Maybe it was a corp laptop with extra certs?)
|
||||
// Other OS X users report
|
||||
// 135, 142, 145... Let's try requiring at least 100,
|
||||
// since this is just a sanity check.
|
||||
t.Logf("got %d roots", len(tt.certs))
|
||||
if want, have := 100, len(tt.certs); have < want {
|
||||
t.Fatalf("want at least %d system roots, have %d", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
// Check that the two cert pools are roughly the same;
|
||||
// |A∩B| > max(|A|, |B|) / 2 should be a reasonably robust check.
|
||||
|
||||
isect := make(map[string]bool, len(sysRoots.certs))
|
||||
for _, c := range sysRoots.certs {
|
||||
isect[string(c.Raw)] = true
|
||||
}
|
||||
|
||||
have := 0
|
||||
for _, c := range execRoots.certs {
|
||||
if isect[string(c.Raw)] {
|
||||
have++
|
||||
}
|
||||
}
|
||||
|
||||
var want int
|
||||
if nsys, nexec := len(sysRoots.certs), len(execRoots.certs); nsys > nexec {
|
||||
want = nsys / 2
|
||||
} else {
|
||||
want = nexec / 2
|
||||
}
|
||||
|
||||
if have < want {
|
||||
t.Errorf("insufficient overlap between cgo and non-cgo roots; want at least %d, have %d", want, have)
|
||||
}
|
||||
}
|
14
vendor/github.com/google/certificate-transparency-go/x509/root_linux.go
generated
vendored
Normal file
14
vendor/github.com/google/certificate-transparency-go/x509/root_linux.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
// 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 x509
|
||||
|
||||
// Possible certificate files; stop after finding one.
|
||||
var certFiles = []string{
|
||||
"/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.
|
||||
"/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL 6
|
||||
"/etc/ssl/ca-bundle.pem", // OpenSUSE
|
||||
"/etc/pki/tls/cacert.pem", // OpenELEC
|
||||
"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS/RHEL 7
|
||||
}
|
8
vendor/github.com/google/certificate-transparency-go/x509/root_nacl.go
generated
vendored
Normal file
8
vendor/github.com/google/certificate-transparency-go/x509/root_nacl.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
// 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 x509
|
||||
|
||||
// Possible certificate files; stop after finding one.
|
||||
var certFiles = []string{}
|
11
vendor/github.com/google/certificate-transparency-go/x509/root_nocgo_darwin.go
generated
vendored
Normal file
11
vendor/github.com/google/certificate-transparency-go/x509/root_nocgo_darwin.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
// Copyright 2013 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 !cgo
|
||||
|
||||
package x509
|
||||
|
||||
func loadSystemRoots() (*CertPool, error) {
|
||||
return execSecurityRoots()
|
||||
}
|
37
vendor/github.com/google/certificate-transparency-go/x509/root_plan9.go
generated
vendored
Normal file
37
vendor/github.com/google/certificate-transparency-go/x509/root_plan9.go
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2012 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 plan9
|
||||
|
||||
package x509
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Possible certificate files; stop after finding one.
|
||||
var certFiles = []string{
|
||||
"/sys/lib/tls/ca.pem",
|
||||
}
|
||||
|
||||
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func loadSystemRoots() (*CertPool, error) {
|
||||
roots := NewCertPool()
|
||||
var bestErr error
|
||||
for _, file := range certFiles {
|
||||
data, err := ioutil.ReadFile(file)
|
||||
if err == nil {
|
||||
roots.AppendCertsFromPEM(data)
|
||||
return roots, nil
|
||||
}
|
||||
if bestErr == nil || (os.IsNotExist(bestErr) && !os.IsNotExist(err)) {
|
||||
bestErr = err
|
||||
}
|
||||
}
|
||||
return nil, bestErr
|
||||
}
|
12
vendor/github.com/google/certificate-transparency-go/x509/root_solaris.go
generated
vendored
Normal file
12
vendor/github.com/google/certificate-transparency-go/x509/root_solaris.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
// 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 x509
|
||||
|
||||
// Possible certificate files; stop after finding one.
|
||||
var certFiles = []string{
|
||||
"/etc/certs/ca-certificates.crt", // Solaris 11.2+
|
||||
"/etc/ssl/certs/ca-certificates.crt", // Joyent SmartOS
|
||||
"/etc/ssl/cacert.pem", // OmniOS
|
||||
}
|
88
vendor/github.com/google/certificate-transparency-go/x509/root_unix.go
generated
vendored
Normal file
88
vendor/github.com/google/certificate-transparency-go/x509/root_unix.go
generated
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
// Copyright 2011 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 dragonfly freebsd linux nacl netbsd openbsd solaris
|
||||
|
||||
package x509
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Possible directories with certificate files; stop after successfully
|
||||
// reading at least one file from a directory.
|
||||
var certDirectories = []string{
|
||||
"/etc/ssl/certs", // SLES10/SLES11, https://golang.org/issue/12139
|
||||
"/system/etc/security/cacerts", // Android
|
||||
"/usr/local/share/certs", // FreeBSD
|
||||
"/etc/pki/tls/certs", // Fedora/RHEL
|
||||
"/etc/openssl/certs", // NetBSD
|
||||
}
|
||||
|
||||
const (
|
||||
// certFileEnv is the environment variable which identifies where to locate
|
||||
// the SSL certificate file. If set this overrides the system default.
|
||||
certFileEnv = "SSL_CERT_FILE"
|
||||
|
||||
// certDirEnv is the environment variable which identifies which directory
|
||||
// to check for SSL certificate files. If set this overrides the system default.
|
||||
certDirEnv = "SSL_CERT_DIR"
|
||||
)
|
||||
|
||||
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func loadSystemRoots() (*CertPool, error) {
|
||||
roots := NewCertPool()
|
||||
|
||||
files := certFiles
|
||||
if f := os.Getenv(certFileEnv); f != "" {
|
||||
files = []string{f}
|
||||
}
|
||||
|
||||
var firstErr error
|
||||
for _, file := range files {
|
||||
data, err := ioutil.ReadFile(file)
|
||||
if err == nil {
|
||||
roots.AppendCertsFromPEM(data)
|
||||
break
|
||||
}
|
||||
if firstErr == nil && !os.IsNotExist(err) {
|
||||
firstErr = err
|
||||
}
|
||||
}
|
||||
|
||||
dirs := certDirectories
|
||||
if d := os.Getenv(certDirEnv); d != "" {
|
||||
dirs = []string{d}
|
||||
}
|
||||
|
||||
for _, directory := range dirs {
|
||||
fis, err := ioutil.ReadDir(directory)
|
||||
if err != nil {
|
||||
if firstErr == nil && !os.IsNotExist(err) {
|
||||
firstErr = err
|
||||
}
|
||||
continue
|
||||
}
|
||||
rootsAdded := false
|
||||
for _, fi := range fis {
|
||||
data, err := ioutil.ReadFile(directory + "/" + fi.Name())
|
||||
if err == nil && roots.AppendCertsFromPEM(data) {
|
||||
rootsAdded = true
|
||||
}
|
||||
}
|
||||
if rootsAdded {
|
||||
return roots, nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(roots.certs) > 0 {
|
||||
return roots, nil
|
||||
}
|
||||
|
||||
return nil, firstErr
|
||||
}
|
127
vendor/github.com/google/certificate-transparency-go/x509/root_unix_test.go
generated
vendored
Normal file
127
vendor/github.com/google/certificate-transparency-go/x509/root_unix_test.go
generated
vendored
Normal file
@ -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.
|
||||
|
||||
// +build dragonfly freebsd linux netbsd openbsd solaris
|
||||
|
||||
package x509
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
testDir = "testdata"
|
||||
testDirCN = "test-dir"
|
||||
testFile = "test-file.crt"
|
||||
testFileCN = "test-file"
|
||||
testMissing = "missing"
|
||||
)
|
||||
|
||||
func TestEnvVars(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
fileEnv string
|
||||
dirEnv string
|
||||
files []string
|
||||
dirs []string
|
||||
cns []string
|
||||
}{
|
||||
{
|
||||
// Environment variables override the default locations preventing fall through.
|
||||
name: "override-defaults",
|
||||
fileEnv: testMissing,
|
||||
dirEnv: testMissing,
|
||||
files: []string{testFile},
|
||||
dirs: []string{testDir},
|
||||
cns: nil,
|
||||
},
|
||||
{
|
||||
// File environment overrides default file locations.
|
||||
name: "file",
|
||||
fileEnv: testFile,
|
||||
dirEnv: "",
|
||||
files: nil,
|
||||
dirs: nil,
|
||||
cns: []string{testFileCN},
|
||||
},
|
||||
{
|
||||
// Directory environment overrides default directory locations.
|
||||
name: "dir",
|
||||
fileEnv: "",
|
||||
dirEnv: testDir,
|
||||
files: nil,
|
||||
dirs: nil,
|
||||
cns: []string{testDirCN},
|
||||
},
|
||||
{
|
||||
// File & directory environment overrides both default locations.
|
||||
name: "file+dir",
|
||||
fileEnv: testFile,
|
||||
dirEnv: testDir,
|
||||
files: nil,
|
||||
dirs: nil,
|
||||
cns: []string{testFileCN, testDirCN},
|
||||
},
|
||||
{
|
||||
// Environment variable empty / unset uses default locations.
|
||||
name: "empty-fall-through",
|
||||
fileEnv: "",
|
||||
dirEnv: "",
|
||||
files: []string{testFile},
|
||||
dirs: []string{testDir},
|
||||
cns: []string{testFileCN, testDirCN},
|
||||
},
|
||||
}
|
||||
|
||||
// Save old settings so we can restore before the test ends.
|
||||
origCertFiles, origCertDirectories := certFiles, certDirectories
|
||||
origFile, origDir := os.Getenv(certFileEnv), os.Getenv(certDirEnv)
|
||||
defer func() {
|
||||
certFiles = origCertFiles
|
||||
certDirectories = origCertDirectories
|
||||
os.Setenv(certFileEnv, origFile)
|
||||
os.Setenv(certDirEnv, origDir)
|
||||
}()
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if err := os.Setenv(certFileEnv, tc.fileEnv); err != nil {
|
||||
t.Fatalf("setenv %q failed: %v", certFileEnv, err)
|
||||
}
|
||||
if err := os.Setenv(certDirEnv, tc.dirEnv); err != nil {
|
||||
t.Fatalf("setenv %q failed: %v", certDirEnv, err)
|
||||
}
|
||||
|
||||
certFiles, certDirectories = tc.files, tc.dirs
|
||||
|
||||
r, err := loadSystemRoots()
|
||||
if err != nil {
|
||||
t.Fatal("unexpected failure:", err)
|
||||
}
|
||||
|
||||
if r == nil {
|
||||
if tc.cns == nil {
|
||||
// Expected nil
|
||||
return
|
||||
}
|
||||
t.Fatal("nil roots")
|
||||
}
|
||||
|
||||
// Verify that the returned certs match, otherwise report where the mismatch is.
|
||||
for i, cn := range tc.cns {
|
||||
if i >= len(r.certs) {
|
||||
t.Errorf("missing cert %v @ %v", cn, i)
|
||||
} else if r.certs[i].Subject.CommonName != cn {
|
||||
fmt.Printf("%#v\n", r.certs[0].Subject)
|
||||
t.Errorf("unexpected cert common name %q, want %q", r.certs[i].Subject.CommonName, cn)
|
||||
}
|
||||
}
|
||||
if len(r.certs) > len(tc.cns) {
|
||||
t.Errorf("got %v certs, which is more than %v wanted", len(r.certs), len(tc.cns))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
266
vendor/github.com/google/certificate-transparency-go/x509/root_windows.go
generated
vendored
Normal file
266
vendor/github.com/google/certificate-transparency-go/x509/root_windows.go
generated
vendored
Normal file
@ -0,0 +1,266 @@
|
||||
// Copyright 2012 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 x509
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Creates a new *syscall.CertContext representing the leaf certificate in an in-memory
|
||||
// certificate store containing itself and all of the intermediate certificates specified
|
||||
// in the opts.Intermediates CertPool.
|
||||
//
|
||||
// A pointer to the in-memory store is available in the returned CertContext's Store field.
|
||||
// The store is automatically freed when the CertContext is freed using
|
||||
// syscall.CertFreeCertificateContext.
|
||||
func createStoreContext(leaf *Certificate, opts *VerifyOptions) (*syscall.CertContext, error) {
|
||||
var storeCtx *syscall.CertContext
|
||||
|
||||
leafCtx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &leaf.Raw[0], uint32(len(leaf.Raw)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer syscall.CertFreeCertificateContext(leafCtx)
|
||||
|
||||
handle, err := syscall.CertOpenStore(syscall.CERT_STORE_PROV_MEMORY, 0, 0, syscall.CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer syscall.CertCloseStore(handle, 0)
|
||||
|
||||
err = syscall.CertAddCertificateContextToStore(handle, leafCtx, syscall.CERT_STORE_ADD_ALWAYS, &storeCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if opts.Intermediates != nil {
|
||||
for _, intermediate := range opts.Intermediates.certs {
|
||||
ctx, err := syscall.CertCreateCertificateContext(syscall.X509_ASN_ENCODING|syscall.PKCS_7_ASN_ENCODING, &intermediate.Raw[0], uint32(len(intermediate.Raw)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = syscall.CertAddCertificateContextToStore(handle, ctx, syscall.CERT_STORE_ADD_ALWAYS, nil)
|
||||
syscall.CertFreeCertificateContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return storeCtx, nil
|
||||
}
|
||||
|
||||
// extractSimpleChain extracts the final certificate chain from a CertSimpleChain.
|
||||
func extractSimpleChain(simpleChain **syscall.CertSimpleChain, count int) (chain []*Certificate, err error) {
|
||||
if simpleChain == nil || count == 0 {
|
||||
return nil, errors.New("x509: invalid simple chain")
|
||||
}
|
||||
|
||||
simpleChains := (*[1 << 20]*syscall.CertSimpleChain)(unsafe.Pointer(simpleChain))[:]
|
||||
lastChain := simpleChains[count-1]
|
||||
elements := (*[1 << 20]*syscall.CertChainElement)(unsafe.Pointer(lastChain.Elements))[:]
|
||||
for i := 0; i < int(lastChain.NumElements); i++ {
|
||||
// Copy the buf, since ParseCertificate does not create its own copy.
|
||||
cert := elements[i].CertContext
|
||||
encodedCert := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:]
|
||||
buf := make([]byte, cert.Length)
|
||||
copy(buf, encodedCert[:])
|
||||
parsedCert, err := ParseCertificate(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
chain = append(chain, parsedCert)
|
||||
}
|
||||
|
||||
return chain, nil
|
||||
}
|
||||
|
||||
// checkChainTrustStatus checks the trust status of the certificate chain, translating
|
||||
// any errors it finds into Go errors in the process.
|
||||
func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) error {
|
||||
if chainCtx.TrustStatus.ErrorStatus != syscall.CERT_TRUST_NO_ERROR {
|
||||
status := chainCtx.TrustStatus.ErrorStatus
|
||||
switch status {
|
||||
case syscall.CERT_TRUST_IS_NOT_TIME_VALID:
|
||||
return CertificateInvalidError{c, Expired, ""}
|
||||
default:
|
||||
return UnknownAuthorityError{c, nil, nil}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkChainSSLServerPolicy checks that the certificate chain in chainCtx is valid for
|
||||
// use as a certificate chain for a SSL/TLS server.
|
||||
func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContext, opts *VerifyOptions) error {
|
||||
servernamep, err := syscall.UTF16PtrFromString(opts.DNSName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sslPara := &syscall.SSLExtraCertChainPolicyPara{
|
||||
AuthType: syscall.AUTHTYPE_SERVER,
|
||||
ServerName: servernamep,
|
||||
}
|
||||
sslPara.Size = uint32(unsafe.Sizeof(*sslPara))
|
||||
|
||||
para := &syscall.CertChainPolicyPara{
|
||||
ExtraPolicyPara: uintptr(unsafe.Pointer(sslPara)),
|
||||
}
|
||||
para.Size = uint32(unsafe.Sizeof(*para))
|
||||
|
||||
status := syscall.CertChainPolicyStatus{}
|
||||
err = syscall.CertVerifyCertificateChainPolicy(syscall.CERT_CHAIN_POLICY_SSL, chainCtx, para, &status)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO(mkrautz): use the lChainIndex and lElementIndex fields
|
||||
// of the CertChainPolicyStatus to provide proper context, instead
|
||||
// using c.
|
||||
if status.Error != 0 {
|
||||
switch status.Error {
|
||||
case syscall.CERT_E_EXPIRED:
|
||||
return CertificateInvalidError{c, Expired, ""}
|
||||
case syscall.CERT_E_CN_NO_MATCH:
|
||||
return HostnameError{c, opts.DNSName}
|
||||
case syscall.CERT_E_UNTRUSTEDROOT:
|
||||
return UnknownAuthorityError{c, nil, nil}
|
||||
default:
|
||||
return UnknownAuthorityError{c, nil, nil}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// systemVerify is like Verify, except that it uses CryptoAPI calls
|
||||
// to build certificate chains and verify them.
|
||||
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
|
||||
hasDNSName := opts != nil && len(opts.DNSName) > 0
|
||||
|
||||
storeCtx, err := createStoreContext(c, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer syscall.CertFreeCertificateContext(storeCtx)
|
||||
|
||||
para := new(syscall.CertChainPara)
|
||||
para.Size = uint32(unsafe.Sizeof(*para))
|
||||
|
||||
// If there's a DNSName set in opts, assume we're verifying
|
||||
// a certificate from a TLS server.
|
||||
if hasDNSName {
|
||||
oids := []*byte{
|
||||
&syscall.OID_PKIX_KP_SERVER_AUTH[0],
|
||||
// Both IE and Chrome allow certificates with
|
||||
// Server Gated Crypto as well. Some certificates
|
||||
// in the wild require them.
|
||||
&syscall.OID_SERVER_GATED_CRYPTO[0],
|
||||
&syscall.OID_SGC_NETSCAPE[0],
|
||||
}
|
||||
para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_OR
|
||||
para.RequestedUsage.Usage.Length = uint32(len(oids))
|
||||
para.RequestedUsage.Usage.UsageIdentifiers = &oids[0]
|
||||
} else {
|
||||
para.RequestedUsage.Type = syscall.USAGE_MATCH_TYPE_AND
|
||||
para.RequestedUsage.Usage.Length = 0
|
||||
para.RequestedUsage.Usage.UsageIdentifiers = nil
|
||||
}
|
||||
|
||||
var verifyTime *syscall.Filetime
|
||||
if opts != nil && !opts.CurrentTime.IsZero() {
|
||||
ft := syscall.NsecToFiletime(opts.CurrentTime.UnixNano())
|
||||
verifyTime = &ft
|
||||
}
|
||||
|
||||
// CertGetCertificateChain will traverse Windows's root stores
|
||||
// in an attempt to build a verified certificate chain. Once
|
||||
// it has found a verified chain, it stops. MSDN docs on
|
||||
// CERT_CHAIN_CONTEXT:
|
||||
//
|
||||
// When a CERT_CHAIN_CONTEXT is built, the first simple chain
|
||||
// begins with an end certificate and ends with a self-signed
|
||||
// certificate. If that self-signed certificate is not a root
|
||||
// or otherwise trusted certificate, an attempt is made to
|
||||
// build a new chain. CTLs are used to create the new chain
|
||||
// beginning with the self-signed certificate from the original
|
||||
// chain as the end certificate of the new chain. This process
|
||||
// continues building additional simple chains until the first
|
||||
// self-signed certificate is a trusted certificate or until
|
||||
// an additional simple chain cannot be built.
|
||||
//
|
||||
// The result is that we'll only get a single trusted chain to
|
||||
// return to our caller.
|
||||
var chainCtx *syscall.CertChainContext
|
||||
err = syscall.CertGetCertificateChain(syscall.Handle(0), storeCtx, verifyTime, storeCtx.Store, para, 0, 0, &chainCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer syscall.CertFreeCertificateChain(chainCtx)
|
||||
|
||||
err = checkChainTrustStatus(c, chainCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if hasDNSName {
|
||||
err = checkChainSSLServerPolicy(c, chainCtx, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
chain, err := extractSimpleChain(chainCtx.Chains, int(chainCtx.ChainCount))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
chains = append(chains, chain)
|
||||
|
||||
return chains, nil
|
||||
}
|
||||
|
||||
func loadSystemRoots() (*CertPool, error) {
|
||||
// TODO: restore this functionality on Windows. We tried to do
|
||||
// it in Go 1.8 but had to revert it. See Issue 18609.
|
||||
// Returning (nil, nil) was the old behavior, prior to CL 30578.
|
||||
return nil, nil
|
||||
|
||||
const CRYPT_E_NOT_FOUND = 0x80092004
|
||||
|
||||
store, err := syscall.CertOpenSystemStore(0, syscall.StringToUTF16Ptr("ROOT"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer syscall.CertCloseStore(store, 0)
|
||||
|
||||
roots := NewCertPool()
|
||||
var cert *syscall.CertContext
|
||||
for {
|
||||
cert, err = syscall.CertEnumCertificatesInStore(store, cert)
|
||||
if err != nil {
|
||||
if errno, ok := err.(syscall.Errno); ok {
|
||||
if errno == CRYPT_E_NOT_FOUND {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if cert == nil {
|
||||
break
|
||||
}
|
||||
// Copy the buf, since ParseCertificate does not create its own copy.
|
||||
buf := (*[1 << 20]byte)(unsafe.Pointer(cert.EncodedCert))[:]
|
||||
buf2 := make([]byte, cert.Length)
|
||||
copy(buf2, buf)
|
||||
if c, err := ParseCertificate(buf2); err == nil {
|
||||
roots.AddCert(c)
|
||||
}
|
||||
}
|
||||
return roots, nil
|
||||
}
|
112
vendor/github.com/google/certificate-transparency-go/x509/sec1.go
generated
vendored
Normal file
112
vendor/github.com/google/certificate-transparency-go/x509/sec1.go
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
// Copyright 2012 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 x509
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/google/certificate-transparency-go/asn1"
|
||||
)
|
||||
|
||||
const ecPrivKeyVersion = 1
|
||||
|
||||
// ecPrivateKey reflects an ASN.1 Elliptic Curve Private Key Structure.
|
||||
// References:
|
||||
// RFC 5915
|
||||
// SEC1 - http://www.secg.org/sec1-v2.pdf
|
||||
// Per RFC 5915 the NamedCurveOID is marked as ASN.1 OPTIONAL, however in
|
||||
// most cases it is not.
|
||||
type ecPrivateKey struct {
|
||||
Version int
|
||||
PrivateKey []byte
|
||||
NamedCurveOID asn1.ObjectIdentifier `asn1:"optional,explicit,tag:0"`
|
||||
PublicKey asn1.BitString `asn1:"optional,explicit,tag:1"`
|
||||
}
|
||||
|
||||
// ParseECPrivateKey parses an ASN.1 Elliptic Curve Private Key Structure.
|
||||
func ParseECPrivateKey(der []byte) (*ecdsa.PrivateKey, error) {
|
||||
return parseECPrivateKey(nil, der)
|
||||
}
|
||||
|
||||
// MarshalECPrivateKey marshals an EC private key into ASN.1, DER format.
|
||||
func MarshalECPrivateKey(key *ecdsa.PrivateKey) ([]byte, error) {
|
||||
oid, ok := OIDFromNamedCurve(key.Curve)
|
||||
if !ok {
|
||||
return nil, errors.New("x509: unknown elliptic curve")
|
||||
}
|
||||
|
||||
return marshalECPrivateKeyWithOID(key, oid)
|
||||
}
|
||||
|
||||
// marshalECPrivateKey marshals an EC private key into ASN.1, DER format and
|
||||
// sets the curve ID to the given OID, or omits it if OID is nil.
|
||||
func marshalECPrivateKeyWithOID(key *ecdsa.PrivateKey, oid asn1.ObjectIdentifier) ([]byte, error) {
|
||||
privateKeyBytes := key.D.Bytes()
|
||||
paddedPrivateKey := make([]byte, (key.Curve.Params().N.BitLen()+7)/8)
|
||||
copy(paddedPrivateKey[len(paddedPrivateKey)-len(privateKeyBytes):], privateKeyBytes)
|
||||
|
||||
return asn1.Marshal(ecPrivateKey{
|
||||
Version: 1,
|
||||
PrivateKey: paddedPrivateKey,
|
||||
NamedCurveOID: oid,
|
||||
PublicKey: asn1.BitString{Bytes: elliptic.Marshal(key.Curve, key.X, key.Y)},
|
||||
})
|
||||
}
|
||||
|
||||
// parseECPrivateKey parses an ASN.1 Elliptic Curve Private Key Structure.
|
||||
// The OID for the named curve may be provided from another source (such as
|
||||
// the PKCS8 container) - if it is provided then use this instead of the OID
|
||||
// that may exist in the EC private key structure.
|
||||
func parseECPrivateKey(namedCurveOID *asn1.ObjectIdentifier, der []byte) (key *ecdsa.PrivateKey, err error) {
|
||||
var privKey ecPrivateKey
|
||||
if _, err := asn1.Unmarshal(der, &privKey); err != nil {
|
||||
return nil, errors.New("x509: failed to parse EC private key: " + err.Error())
|
||||
}
|
||||
if privKey.Version != ecPrivKeyVersion {
|
||||
return nil, fmt.Errorf("x509: unknown EC private key version %d", privKey.Version)
|
||||
}
|
||||
|
||||
var curve elliptic.Curve
|
||||
if namedCurveOID != nil {
|
||||
curve = namedCurveFromOID(*namedCurveOID)
|
||||
} else {
|
||||
curve = namedCurveFromOID(privKey.NamedCurveOID)
|
||||
}
|
||||
if curve == nil {
|
||||
return nil, errors.New("x509: unknown elliptic curve")
|
||||
}
|
||||
|
||||
k := new(big.Int).SetBytes(privKey.PrivateKey)
|
||||
curveOrder := curve.Params().N
|
||||
if k.Cmp(curveOrder) >= 0 {
|
||||
return nil, errors.New("x509: invalid elliptic curve private key value")
|
||||
}
|
||||
priv := new(ecdsa.PrivateKey)
|
||||
priv.Curve = curve
|
||||
priv.D = k
|
||||
|
||||
privateKey := make([]byte, (curveOrder.BitLen()+7)/8)
|
||||
|
||||
// Some private keys have leading zero padding. This is invalid
|
||||
// according to [SEC1], but this code will ignore it.
|
||||
for len(privKey.PrivateKey) > len(privateKey) {
|
||||
if privKey.PrivateKey[0] != 0 {
|
||||
return nil, errors.New("x509: invalid private key length")
|
||||
}
|
||||
privKey.PrivateKey = privKey.PrivateKey[1:]
|
||||
}
|
||||
|
||||
// Some private keys remove all leading zeros, this is also invalid
|
||||
// according to [SEC1] but since OpenSSL used to do this, we ignore
|
||||
// this too.
|
||||
copy(privateKey[len(privateKey)-len(privKey.PrivateKey):], privKey.PrivateKey)
|
||||
priv.X, priv.Y = curve.ScalarBaseMult(privateKey)
|
||||
|
||||
return priv, nil
|
||||
}
|
44
vendor/github.com/google/certificate-transparency-go/x509/sec1_test.go
generated
vendored
Normal file
44
vendor/github.com/google/certificate-transparency-go/x509/sec1_test.go
generated
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright 2012 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 x509
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var ecKeyTests = []struct {
|
||||
derHex string
|
||||
shouldReserialize bool
|
||||
}{
|
||||
// Generated using:
|
||||
// openssl ecparam -genkey -name secp384r1 -outform PEM
|
||||
{"3081a40201010430bdb9839c08ee793d1157886a7a758a3c8b2a17a4df48f17ace57c72c56b4723cf21dcda21d4e1ad57ff034f19fcfd98ea00706052b81040022a16403620004feea808b5ee2429cfcce13c32160e1c960990bd050bb0fdf7222f3decd0a55008e32a6aa3c9062051c4cba92a7a3b178b24567412d43cdd2f882fa5addddd726fe3e208d2c26d733a773a597abb749714df7256ead5105fa6e7b3650de236b50", true},
|
||||
// This key was generated by GnuTLS and has illegal zero-padding of the
|
||||
// private key. See https://golang.org/issues/13699.
|
||||
{"3078020101042100f9f43a04b9bdc3ab01f53be6df80e7a7bc3eaf7b87fc24e630a4a0aa97633645a00a06082a8648ce3d030107a1440342000441a51bc318461b4c39a45048a16d4fc2a935b1ea7fe86e8c1fa219d6f2438f7c7fd62957d3442efb94b6a23eb0ea66dda663dc42f379cda6630b21b7888a5d3d", false},
|
||||
// This was generated using an old version of OpenSSL and is missing a
|
||||
// leading zero byte in the private key that should be present.
|
||||
{"3081db0201010441607b4f985774ac21e633999794542e09312073480baa69550914d6d43d8414441e61b36650567901da714f94dffb3ce0e2575c31928a0997d51df5c440e983ca17a00706052b81040023a181890381860004001661557afedd7ac8d6b70e038e576558c626eb62edda36d29c3a1310277c11f67a8c6f949e5430a37dcfb95d902c1b5b5379c389873b9dd17be3bdb088a4774a7401072f830fb9a08d93bfa50a03dd3292ea07928724ddb915d831917a338f6b0aecfbc3cf5352c4a1295d356890c41c34116d29eeb93779aab9d9d78e2613437740f6", false},
|
||||
}
|
||||
|
||||
func TestParseECPrivateKey(t *testing.T) {
|
||||
for i, test := range ecKeyTests {
|
||||
derBytes, _ := hex.DecodeString(test.derHex)
|
||||
key, err := ParseECPrivateKey(derBytes)
|
||||
if err != nil {
|
||||
t.Fatalf("#%d: failed to decode EC private key: %s", i, err)
|
||||
}
|
||||
serialized, err := MarshalECPrivateKey(key)
|
||||
if err != nil {
|
||||
t.Fatalf("#%d: failed to encode EC private key: %s", i, err)
|
||||
}
|
||||
matches := bytes.Equal(serialized, derBytes)
|
||||
if matches != test.shouldReserialize {
|
||||
t.Fatalf("#%d: when serializing key: matches=%t, should match=%t: original %x, reserialized %x", i, matches, test.shouldReserialize, serialized, derBytes)
|
||||
}
|
||||
}
|
||||
}
|
19
vendor/github.com/google/certificate-transparency-go/x509/sha2_windows_test.go
generated
vendored
Normal file
19
vendor/github.com/google/certificate-transparency-go/x509/sha2_windows_test.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
// 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 x509
|
||||
|
||||
import "syscall"
|
||||
|
||||
func init() {
|
||||
v, err := syscall.GetVersion()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if major := byte(v); major < 6 {
|
||||
// Windows XP SP2 and Windows 2003 do not support SHA2.
|
||||
// http://blogs.technet.com/b/pki/archive/2010/09/30/sha2-and-windows.aspx
|
||||
supportSHA2 = false
|
||||
}
|
||||
}
|
31
vendor/github.com/google/certificate-transparency-go/x509/test-dir.crt
generated
vendored
Normal file
31
vendor/github.com/google/certificate-transparency-go/x509/test-dir.crt
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFazCCA1OgAwIBAgIJAL8a/lsnspOqMA0GCSqGSIb3DQEBCwUAMEwxCzAJBgNV
|
||||
BAYTAlVLMRMwEQYDVQQIDApUZXN0LVN0YXRlMRUwEwYDVQQKDAxHb2xhbmcgVGVz
|
||||
dHMxETAPBgNVBAMMCHRlc3QtZGlyMB4XDTE3MDIwMTIzNTAyN1oXDTI3MDEzMDIz
|
||||
NTAyN1owTDELMAkGA1UEBhMCVUsxEzARBgNVBAgMClRlc3QtU3RhdGUxFTATBgNV
|
||||
BAoMDEdvbGFuZyBUZXN0czERMA8GA1UEAwwIdGVzdC1kaXIwggIiMA0GCSqGSIb3
|
||||
DQEBAQUAA4ICDwAwggIKAoICAQDzBoi43Yn30KN13PKFHu8LA4UmgCRToTukLItM
|
||||
WK2Je45grs/axg9n3YJOXC6hmsyrkOnyBcx1xVNgSrOAll7fSjtChRIX72Xrloxu
|
||||
XewtWVIrijqz6oylbvEmbRT3O8uynu5rF82Pmdiy8oiSfdywjKuPnE0hjV1ZSCql
|
||||
MYcXqA+f0JFD8kMv4pbtxjGH8f2DkYQz+hHXLrJH4/MEYdVMQXoz/GDzLyOkrXBN
|
||||
hpMaBBqg1p0P+tRdfLXuliNzA9vbZylzpF1YZ0gvsr0S5Y6LVtv7QIRygRuLY4kF
|
||||
k+UYuFq8NrV8TykS7FVnO3tf4XcYZ7r2KV5FjYSrJtNNo85BV5c3xMD3fJ2XcOWk
|
||||
+oD1ATdgAM3aKmSOxNtNItKKxBe1mkqDH41NbWx7xMad78gDznyeT0tjEOltN2bM
|
||||
uXU1R/jgR/vq5Ec0AhXJyL/ziIcmuV2fSl/ZxT4ARD+16tgPiIx+welTf0v27/JY
|
||||
adlfkkL5XsPRrbSguISrj7JeaO/gjG3KnDVHcZvYBpDfHqRhCgrosfe26TZcTXx2
|
||||
cRxOfvBjMz1zJAg+esuUzSkerreyRhzD7RpeZTwi6sxvx82MhYMbA3w1LtgdABio
|
||||
9JRqZy3xqsIbNv7N46WO/qXL1UMRKb1UyHeW8g8btboz+B4zv1U0Nj+9qxPBbQui
|
||||
dgL9LQIDAQABo1AwTjAdBgNVHQ4EFgQUy0/0W8nwQfz2tO6AZ2jPkEiTzvUwHwYD
|
||||
VR0jBBgwFoAUy0/0W8nwQfz2tO6AZ2jPkEiTzvUwDAYDVR0TBAUwAwEB/zANBgkq
|
||||
hkiG9w0BAQsFAAOCAgEAvEVnUYsIOt87rggmLPqEueynkuQ+562M8EDHSQl82zbe
|
||||
xDCxeg3DvPgKb+RvaUdt1362z/szK10SoeMgx6+EQLoV9LiVqXwNqeYfixrhrdw3
|
||||
ppAhYYhymdkbUQCEMHypmXP1vPhAz4o8Bs+eES1M+zO6ErBiD7SqkmBElT+GixJC
|
||||
6epC9ZQFs+dw3lPlbiZSsGE85sqc3VAs0/JgpL/pb1/Eg4s0FUhZD2C2uWdSyZGc
|
||||
g0/v3aXJCp4j/9VoNhI1WXz3M45nysZIL5OQgXymLqJElQa1pZ3Wa4i/nidvT4AT
|
||||
Xlxc/qijM8set/nOqp7hVd5J0uG6qdwLRILUddZ6OpXd7ZNi1EXg+Bpc7ehzGsDt
|
||||
3UFGzYXDjxYnK2frQfjLS8stOQIqSrGthW6x0fdkVx0y8BByvd5J6+JmZl4UZfzA
|
||||
m99VxXSt4B9x6BvnY7ktzcFDOjtuLc4B/7yg9fv1eQuStA4cHGGAttsCg1X/Kx8W
|
||||
PvkkeH0UWDZ9vhH9K36703z89da6MWF+bz92B0+4HoOmlVaXRkvblsNaynJnL0LC
|
||||
Ayry7QBxuh5cMnDdRwJB3AVJIiJ1GVpb7aGvBOnx+s2lwRv9HWtghb+cbwwktx1M
|
||||
JHyBf3GZNSWTpKY7cD8V+NnBv3UuioOVVo+XAU4LF/bYUjdRpxWADJizNtZrtFo=
|
||||
-----END CERTIFICATE-----
|
32
vendor/github.com/google/certificate-transparency-go/x509/test-file.crt
generated
vendored
Normal file
32
vendor/github.com/google/certificate-transparency-go/x509/test-file.crt
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFbTCCA1WgAwIBAgIJAN338vEmMtLsMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNV
|
||||
BAYTAlVLMRMwEQYDVQQIDApUZXN0LVN0YXRlMRUwEwYDVQQKDAxHb2xhbmcgVGVz
|
||||
dHMxEjAQBgNVBAMMCXRlc3QtZmlsZTAeFw0xNzAyMDEyMzUyMDhaFw0yNzAxMzAy
|
||||
MzUyMDhaME0xCzAJBgNVBAYTAlVLMRMwEQYDVQQIDApUZXN0LVN0YXRlMRUwEwYD
|
||||
VQQKDAxHb2xhbmcgVGVzdHMxEjAQBgNVBAMMCXRlc3QtZmlsZTCCAiIwDQYJKoZI
|
||||
hvcNAQEBBQADggIPADCCAgoCggIBAPMGiLjdiffQo3Xc8oUe7wsDhSaAJFOhO6Qs
|
||||
i0xYrYl7jmCuz9rGD2fdgk5cLqGazKuQ6fIFzHXFU2BKs4CWXt9KO0KFEhfvZeuW
|
||||
jG5d7C1ZUiuKOrPqjKVu8SZtFPc7y7Ke7msXzY+Z2LLyiJJ93LCMq4+cTSGNXVlI
|
||||
KqUxhxeoD5/QkUPyQy/ilu3GMYfx/YORhDP6Edcuskfj8wRh1UxBejP8YPMvI6St
|
||||
cE2GkxoEGqDWnQ/61F18te6WI3MD29tnKXOkXVhnSC+yvRLljotW2/tAhHKBG4tj
|
||||
iQWT5Ri4Wrw2tXxPKRLsVWc7e1/hdxhnuvYpXkWNhKsm002jzkFXlzfEwPd8nZdw
|
||||
5aT6gPUBN2AAzdoqZI7E200i0orEF7WaSoMfjU1tbHvExp3vyAPOfJ5PS2MQ6W03
|
||||
Zsy5dTVH+OBH++rkRzQCFcnIv/OIhya5XZ9KX9nFPgBEP7Xq2A+IjH7B6VN/S/bv
|
||||
8lhp2V+SQvlew9GttKC4hKuPsl5o7+CMbcqcNUdxm9gGkN8epGEKCuix97bpNlxN
|
||||
fHZxHE5+8GMzPXMkCD56y5TNKR6ut7JGHMPtGl5lPCLqzG/HzYyFgxsDfDUu2B0A
|
||||
GKj0lGpnLfGqwhs2/s3jpY7+pcvVQxEpvVTId5byDxu1ujP4HjO/VTQ2P72rE8Ft
|
||||
C6J2Av0tAgMBAAGjUDBOMB0GA1UdDgQWBBTLT/RbyfBB/Pa07oBnaM+QSJPO9TAf
|
||||
BgNVHSMEGDAWgBTLT/RbyfBB/Pa07oBnaM+QSJPO9TAMBgNVHRMEBTADAQH/MA0G
|
||||
CSqGSIb3DQEBCwUAA4ICAQB3sCntCcQwhMgRPPyvOCMyTcQ/Iv+cpfxz2Ck14nlx
|
||||
AkEAH2CH0ov5GWTt07/ur3aa5x+SAKi0J3wTD1cdiw4U/6Uin6jWGKKxvoo4IaeK
|
||||
SbM8w/6eKx6UbmHx7PA/eRABY9tTlpdPCVgw7/o3WDr03QM+IAtatzvaCPPczake
|
||||
pbdLwmBZB/v8V+6jUajy6jOgdSH0PyffGnt7MWgDETmNC6p/Xigp5eh+C8Fb4NGT
|
||||
xgHES5PBC+sruWp4u22bJGDKTvYNdZHsnw/CaKQWNsQqwisxa3/8N5v+PCff/pxl
|
||||
r05pE3PdHn9JrCl4iWdVlgtiI9BoPtQyDfa/OEFaScE8KYR8LxaAgdgp3zYncWls
|
||||
BpwQ6Y/A2wIkhlD9eEp5Ib2hz7isXOs9UwjdriKqrBXqcIAE5M+YIk3+KAQKxAtd
|
||||
4YsK3CSJ010uphr12YKqlScj4vuKFjuOtd5RyyMIxUG3lrrhAu2AzCeKCLdVgA8+
|
||||
75FrYMApUdvcjp4uzbBoED4XRQlx9kdFHVbYgmE/+yddBYJM8u4YlgAL0hW2/D8p
|
||||
z9JWIfxVmjJnBnXaKGBuiUyZ864A3PJndP6EMMo7TzS2CDnfCYuJjvI0KvDjFNmc
|
||||
rQA04+qfMSEz3nmKhbbZu4eYLzlADhfH8tT4GMtXf71WLA5AUHGf2Y4+HIHTsmHG
|
||||
vQ==
|
||||
-----END CERTIFICATE-----
|
31
vendor/github.com/google/certificate-transparency-go/x509/testdata/test-dir.crt
generated
vendored
Normal file
31
vendor/github.com/google/certificate-transparency-go/x509/testdata/test-dir.crt
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIFazCCA1OgAwIBAgIJAL8a/lsnspOqMA0GCSqGSIb3DQEBCwUAMEwxCzAJBgNV
|
||||
BAYTAlVLMRMwEQYDVQQIDApUZXN0LVN0YXRlMRUwEwYDVQQKDAxHb2xhbmcgVGVz
|
||||
dHMxETAPBgNVBAMMCHRlc3QtZGlyMB4XDTE3MDIwMTIzNTAyN1oXDTI3MDEzMDIz
|
||||
NTAyN1owTDELMAkGA1UEBhMCVUsxEzARBgNVBAgMClRlc3QtU3RhdGUxFTATBgNV
|
||||
BAoMDEdvbGFuZyBUZXN0czERMA8GA1UEAwwIdGVzdC1kaXIwggIiMA0GCSqGSIb3
|
||||
DQEBAQUAA4ICDwAwggIKAoICAQDzBoi43Yn30KN13PKFHu8LA4UmgCRToTukLItM
|
||||
WK2Je45grs/axg9n3YJOXC6hmsyrkOnyBcx1xVNgSrOAll7fSjtChRIX72Xrloxu
|
||||
XewtWVIrijqz6oylbvEmbRT3O8uynu5rF82Pmdiy8oiSfdywjKuPnE0hjV1ZSCql
|
||||
MYcXqA+f0JFD8kMv4pbtxjGH8f2DkYQz+hHXLrJH4/MEYdVMQXoz/GDzLyOkrXBN
|
||||
hpMaBBqg1p0P+tRdfLXuliNzA9vbZylzpF1YZ0gvsr0S5Y6LVtv7QIRygRuLY4kF
|
||||
k+UYuFq8NrV8TykS7FVnO3tf4XcYZ7r2KV5FjYSrJtNNo85BV5c3xMD3fJ2XcOWk
|
||||
+oD1ATdgAM3aKmSOxNtNItKKxBe1mkqDH41NbWx7xMad78gDznyeT0tjEOltN2bM
|
||||
uXU1R/jgR/vq5Ec0AhXJyL/ziIcmuV2fSl/ZxT4ARD+16tgPiIx+welTf0v27/JY
|
||||
adlfkkL5XsPRrbSguISrj7JeaO/gjG3KnDVHcZvYBpDfHqRhCgrosfe26TZcTXx2
|
||||
cRxOfvBjMz1zJAg+esuUzSkerreyRhzD7RpeZTwi6sxvx82MhYMbA3w1LtgdABio
|
||||
9JRqZy3xqsIbNv7N46WO/qXL1UMRKb1UyHeW8g8btboz+B4zv1U0Nj+9qxPBbQui
|
||||
dgL9LQIDAQABo1AwTjAdBgNVHQ4EFgQUy0/0W8nwQfz2tO6AZ2jPkEiTzvUwHwYD
|
||||
VR0jBBgwFoAUy0/0W8nwQfz2tO6AZ2jPkEiTzvUwDAYDVR0TBAUwAwEB/zANBgkq
|
||||
hkiG9w0BAQsFAAOCAgEAvEVnUYsIOt87rggmLPqEueynkuQ+562M8EDHSQl82zbe
|
||||
xDCxeg3DvPgKb+RvaUdt1362z/szK10SoeMgx6+EQLoV9LiVqXwNqeYfixrhrdw3
|
||||
ppAhYYhymdkbUQCEMHypmXP1vPhAz4o8Bs+eES1M+zO6ErBiD7SqkmBElT+GixJC
|
||||
6epC9ZQFs+dw3lPlbiZSsGE85sqc3VAs0/JgpL/pb1/Eg4s0FUhZD2C2uWdSyZGc
|
||||
g0/v3aXJCp4j/9VoNhI1WXz3M45nysZIL5OQgXymLqJElQa1pZ3Wa4i/nidvT4AT
|
||||
Xlxc/qijM8set/nOqp7hVd5J0uG6qdwLRILUddZ6OpXd7ZNi1EXg+Bpc7ehzGsDt
|
||||
3UFGzYXDjxYnK2frQfjLS8stOQIqSrGthW6x0fdkVx0y8BByvd5J6+JmZl4UZfzA
|
||||
m99VxXSt4B9x6BvnY7ktzcFDOjtuLc4B/7yg9fv1eQuStA4cHGGAttsCg1X/Kx8W
|
||||
PvkkeH0UWDZ9vhH9K36703z89da6MWF+bz92B0+4HoOmlVaXRkvblsNaynJnL0LC
|
||||
Ayry7QBxuh5cMnDdRwJB3AVJIiJ1GVpb7aGvBOnx+s2lwRv9HWtghb+cbwwktx1M
|
||||
JHyBf3GZNSWTpKY7cD8V+NnBv3UuioOVVo+XAU4LF/bYUjdRpxWADJizNtZrtFo=
|
||||
-----END CERTIFICATE-----
|
1035
vendor/github.com/google/certificate-transparency-go/x509/verify.go
generated
vendored
Normal file
1035
vendor/github.com/google/certificate-transparency-go/x509/verify.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1744
vendor/github.com/google/certificate-transparency-go/x509/verify_test.go
generated
vendored
Normal file
1744
vendor/github.com/google/certificate-transparency-go/x509/verify_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2841
vendor/github.com/google/certificate-transparency-go/x509/x509.go
generated
vendored
Normal file
2841
vendor/github.com/google/certificate-transparency-go/x509/x509.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2370
vendor/github.com/google/certificate-transparency-go/x509/x509_test.go
generated
vendored
Normal file
2370
vendor/github.com/google/certificate-transparency-go/x509/x509_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
55
vendor/github.com/google/certificate-transparency-go/x509/x509_test_import.go
generated
vendored
Normal file
55
vendor/github.com/google/certificate-transparency-go/x509/x509_test_import.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2013 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 ignore
|
||||
|
||||
// This file is run by the x509 tests to ensure that a program with minimal
|
||||
// imports can sign certificates without errors resulting from missing hash
|
||||
// functions.
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
// START CT CHANGES
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
"github.com/google/certificate-transparency-go/x509/pkix"
|
||||
// END CT CHANGES
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
block, _ := pem.Decode([]byte(pemPrivateKey))
|
||||
rsaPriv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
panic("Failed to parse private key: " + err.Error())
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
Subject: pkix.Name{
|
||||
CommonName: "test",
|
||||
Organization: []string{"Σ Acme Co"},
|
||||
},
|
||||
NotBefore: time.Unix(1000, 0),
|
||||
NotAfter: time.Unix(100000, 0),
|
||||
KeyUsage: x509.KeyUsageCertSign,
|
||||
}
|
||||
|
||||
if _, err = x509.CreateCertificate(rand.Reader, &template, &template, &rsaPriv.PublicKey, rsaPriv); err != nil {
|
||||
panic("failed to create certificate with basic imports: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
var pemPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0
|
||||
fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu
|
||||
/ThglAXJmZhOMPVn4eiu7/ROixi9sex436MaVeMqSNf7Ex9a8fRNfWss7Sqd9eWu
|
||||
RTUCIQDasvGASLqmjeffBNLTXV2A5g4t+kLVCpsEIZAycV5GswIhANEPLmax0ME/
|
||||
EO+ZJ79TJKN5yiGBRsv5yvx5UiHxajEXAiAhAol5N4EUyq6I9w1rYdhPMGpLfk7A
|
||||
IU2snfRJ6Nq2CQIgFrPsWRCkV+gOYcajD17rEqmuLrdIRexpg8N1DOSXoJ8CIGlS
|
||||
tAboUGBxTDq3ZroNism3DaMIbKPyYrAqhKov1h5V
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`
|
73
vendor/github.com/google/certificate-transparency-go/x509util/files.go
generated
vendored
Normal file
73
vendor/github.com/google/certificate-transparency-go/x509util/files.go
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package x509util
|
||||
|
||||
import (
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ReadPossiblePEMFile loads data from a file which may be in DER format
|
||||
// or may be in PEM format (with the given blockname).
|
||||
func ReadPossiblePEMFile(filename, blockname string) ([][]byte, error) {
|
||||
data, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: failed to read data: %v", filename, err)
|
||||
}
|
||||
return dePEM(data, blockname), nil
|
||||
}
|
||||
|
||||
// ReadPossiblePEMURL attempts to determine if the given target is a local file or a
|
||||
// URL, and return the file contents regardless. It also copes with either PEM or DER
|
||||
// format data.
|
||||
func ReadPossiblePEMURL(target, blockname string) ([][]byte, error) {
|
||||
if !strings.HasPrefix(target, "http://") && !strings.HasPrefix(target, "https://") {
|
||||
// Assume it's a filename
|
||||
return ReadPossiblePEMFile(target, blockname)
|
||||
}
|
||||
|
||||
rsp, err := http.Get(target)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to http.Get(%q): %v", target, err)
|
||||
}
|
||||
data, err := ioutil.ReadAll(rsp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to ioutil.ReadAll(%q): %v", target, err)
|
||||
}
|
||||
return dePEM(data, blockname), nil
|
||||
}
|
||||
|
||||
func dePEM(data []byte, blockname string) [][]byte {
|
||||
var results [][]byte
|
||||
if strings.Contains(string(data), "BEGIN "+blockname) {
|
||||
rest := data
|
||||
for {
|
||||
var block *pem.Block
|
||||
block, rest = pem.Decode(rest)
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
if block.Type == blockname {
|
||||
results = append(results, block.Bytes)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
results = append(results, data)
|
||||
}
|
||||
return results
|
||||
}
|
26
vendor/github.com/google/certificate-transparency-go/x509util/fuzz.go
generated
vendored
Normal file
26
vendor/github.com/google/certificate-transparency-go/x509util/fuzz.go
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package x509util
|
||||
|
||||
import "github.com/google/certificate-transparency-go/x509"
|
||||
|
||||
// Fuzz is a go-fuzz (https://github.com/dvyukov/go-fuzz) entrypoint
|
||||
// for fuzzing the parsing of X509 certificates.
|
||||
func Fuzz(data []byte) int {
|
||||
if _, err := x509.ParseCertificate(data); err == nil {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
169
vendor/github.com/google/certificate-transparency-go/x509util/revoked.go
generated
vendored
Normal file
169
vendor/github.com/google/certificate-transparency-go/x509util/revoked.go
generated
vendored
Normal file
@ -0,0 +1,169 @@
|
||||
// Copyright 2017 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package x509util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
"github.com/google/certificate-transparency-go/x509/pkix"
|
||||
)
|
||||
|
||||
// RevocationReasonToString generates a string describing a revocation reason code.
|
||||
func RevocationReasonToString(reason x509.RevocationReasonCode) string {
|
||||
switch reason {
|
||||
case x509.Unspecified:
|
||||
return "Unspecified"
|
||||
case x509.KeyCompromise:
|
||||
return "Key Compromise"
|
||||
case x509.CACompromise:
|
||||
return "CA Compromise"
|
||||
case x509.AffiliationChanged:
|
||||
return "Affiliation Changed"
|
||||
case x509.Superseded:
|
||||
return "Superseded"
|
||||
case x509.CessationOfOperation:
|
||||
return "Cessation Of Operation"
|
||||
case x509.CertificateHold:
|
||||
return "Certificate Hold"
|
||||
case x509.RemoveFromCRL:
|
||||
return "Remove From CRL"
|
||||
case x509.PrivilegeWithdrawn:
|
||||
return "Privilege Withdrawn"
|
||||
case x509.AACompromise:
|
||||
return "AA Compromise"
|
||||
default:
|
||||
return strconv.Itoa(int(reason))
|
||||
}
|
||||
}
|
||||
|
||||
// CRLToString generates a string describing the given certificate revocation list.
|
||||
// The output roughly resembles that from openssl crl -text.
|
||||
func CRLToString(crl *x509.CertificateList) string {
|
||||
var result bytes.Buffer
|
||||
var showCritical = func(critical bool) {
|
||||
if critical {
|
||||
result.WriteString(" critical")
|
||||
}
|
||||
result.WriteString("\n")
|
||||
}
|
||||
result.WriteString("Certificate Revocation List (CRL):\n")
|
||||
result.WriteString(fmt.Sprintf(" Version: %d (%#x)\n", crl.TBSCertList.Version+1, crl.TBSCertList.Version))
|
||||
result.WriteString(fmt.Sprintf(" Signature Algorithm: %v\n", x509.SignatureAlgorithmFromAI(crl.TBSCertList.Signature)))
|
||||
var issuer pkix.Name
|
||||
issuer.FillFromRDNSequence(&crl.TBSCertList.Issuer)
|
||||
result.WriteString(fmt.Sprintf(" Issuer: %v\n", NameToString(issuer)))
|
||||
result.WriteString(fmt.Sprintf(" Last Update: %v\n", crl.TBSCertList.ThisUpdate))
|
||||
result.WriteString(fmt.Sprintf(" Next Update: %v\n", crl.TBSCertList.NextUpdate))
|
||||
|
||||
if len(crl.TBSCertList.Extensions) > 0 {
|
||||
result.WriteString(" CRL extensions:\n")
|
||||
}
|
||||
|
||||
count, critical := OIDInExtensions(x509.OIDExtensionAuthorityKeyId, crl.TBSCertList.Extensions)
|
||||
if count > 0 {
|
||||
result.WriteString(fmt.Sprintf(" X509v3 Authority Key Identifier:"))
|
||||
showCritical(critical)
|
||||
result.WriteString(fmt.Sprintf(" keyid:%v\n", hex.EncodeToString(crl.TBSCertList.AuthorityKeyID)))
|
||||
}
|
||||
count, critical = OIDInExtensions(x509.OIDExtensionIssuerAltName, crl.TBSCertList.Extensions)
|
||||
if count > 0 {
|
||||
result.WriteString(fmt.Sprintf(" X509v3 Issuer Alt Name:"))
|
||||
showCritical(critical)
|
||||
result.WriteString(fmt.Sprintf(" %s\n", GeneralNamesToString(&crl.TBSCertList.IssuerAltNames)))
|
||||
}
|
||||
count, critical = OIDInExtensions(x509.OIDExtensionCRLNumber, crl.TBSCertList.Extensions)
|
||||
if count > 0 {
|
||||
result.WriteString(fmt.Sprintf(" X509v3 CRLNumber:"))
|
||||
showCritical(critical)
|
||||
result.WriteString(fmt.Sprintf(" %d\n", crl.TBSCertList.CRLNumber))
|
||||
}
|
||||
count, critical = OIDInExtensions(x509.OIDExtensionDeltaCRLIndicator, crl.TBSCertList.Extensions)
|
||||
if count > 0 {
|
||||
result.WriteString(fmt.Sprintf(" X509v3 Delta CRL Indicator:"))
|
||||
showCritical(critical)
|
||||
result.WriteString(fmt.Sprintf(" %d\n", crl.TBSCertList.BaseCRLNumber))
|
||||
}
|
||||
count, critical = OIDInExtensions(x509.OIDExtensionIssuingDistributionPoint, crl.TBSCertList.Extensions)
|
||||
if count > 0 {
|
||||
result.WriteString(fmt.Sprintf(" X509v3 Issuing Distribution Point:"))
|
||||
showCritical(critical)
|
||||
result.WriteString(fmt.Sprintf(" %s\n", GeneralNamesToString(&crl.TBSCertList.IssuingDPFullNames)))
|
||||
}
|
||||
count, critical = OIDInExtensions(x509.OIDExtensionFreshestCRL, crl.TBSCertList.Extensions)
|
||||
if count > 0 {
|
||||
result.WriteString(fmt.Sprintf(" X509v3 Freshest CRL:"))
|
||||
showCritical(critical)
|
||||
result.WriteString(fmt.Sprintf(" Full Name:\n"))
|
||||
var buf bytes.Buffer
|
||||
for _, pt := range crl.TBSCertList.FreshestCRLDistributionPoint {
|
||||
commaAppend(&buf, "URI:"+pt)
|
||||
}
|
||||
result.WriteString(fmt.Sprintf(" %v\n", buf.String()))
|
||||
}
|
||||
count, critical = OIDInExtensions(x509.OIDExtensionAuthorityInfoAccess, crl.TBSCertList.Extensions)
|
||||
if count > 0 {
|
||||
result.WriteString(fmt.Sprintf(" Authority Information Access:"))
|
||||
showCritical(critical)
|
||||
var issuerBuf bytes.Buffer
|
||||
for _, issuer := range crl.TBSCertList.IssuingCertificateURL {
|
||||
commaAppend(&issuerBuf, "URI:"+issuer)
|
||||
}
|
||||
if issuerBuf.Len() > 0 {
|
||||
result.WriteString(fmt.Sprintf(" CA Issuers - %v\n", issuerBuf.String()))
|
||||
}
|
||||
var ocspBuf bytes.Buffer
|
||||
for _, ocsp := range crl.TBSCertList.OCSPServer {
|
||||
commaAppend(&ocspBuf, "URI:"+ocsp)
|
||||
}
|
||||
if ocspBuf.Len() > 0 {
|
||||
result.WriteString(fmt.Sprintf(" OCSP - %v\n", ocspBuf.String()))
|
||||
}
|
||||
// TODO(drysdale): Display other GeneralName types
|
||||
}
|
||||
|
||||
result.WriteString("\n")
|
||||
result.WriteString("Revoked Certificates:\n")
|
||||
for _, c := range crl.TBSCertList.RevokedCertificates {
|
||||
result.WriteString(fmt.Sprintf(" Serial Number: %d (%#[1]x)\n", c.SerialNumber))
|
||||
result.WriteString(fmt.Sprintf(" Revocation Date : %v\n", c.RevocationTime))
|
||||
count, critical = OIDInExtensions(x509.OIDExtensionCRLReasons, c.Extensions)
|
||||
if count > 0 {
|
||||
result.WriteString(fmt.Sprintf(" X509v3 CRL Reason Code:"))
|
||||
showCritical(critical)
|
||||
result.WriteString(fmt.Sprintf(" %s\n", RevocationReasonToString(c.RevocationReason)))
|
||||
}
|
||||
count, critical = OIDInExtensions(x509.OIDExtensionInvalidityDate, c.Extensions)
|
||||
if count > 0 {
|
||||
result.WriteString(fmt.Sprintf(" Invalidity Date:"))
|
||||
showCritical(critical)
|
||||
result.WriteString(fmt.Sprintf(" %s\n", c.InvalidityDate))
|
||||
}
|
||||
count, critical = OIDInExtensions(x509.OIDExtensionCertificateIssuer, c.Extensions)
|
||||
if count > 0 {
|
||||
result.WriteString(fmt.Sprintf(" Issuer:"))
|
||||
showCritical(critical)
|
||||
result.WriteString(fmt.Sprintf(" %s\n", GeneralNamesToString(&c.Issuer)))
|
||||
}
|
||||
}
|
||||
result.WriteString(fmt.Sprintf(" Signature Algorithm: %v\n", x509.SignatureAlgorithmFromAI(crl.SignatureAlgorithm)))
|
||||
appendHexData(&result, crl.SignatureValue.Bytes, 18, " ")
|
||||
result.WriteString("\n")
|
||||
|
||||
return result.String()
|
||||
}
|
670
vendor/github.com/google/certificate-transparency-go/x509util/x509util.go
generated
vendored
Normal file
670
vendor/github.com/google/certificate-transparency-go/x509util/x509util.go
generated
vendored
Normal file
@ -0,0 +1,670 @@
|
||||
// Copyright 2016 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package x509util includes utility code for working with X.509
|
||||
// certificates from the x509 package.
|
||||
package x509util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
ct "github.com/google/certificate-transparency-go"
|
||||
"github.com/google/certificate-transparency-go/asn1"
|
||||
"github.com/google/certificate-transparency-go/tls"
|
||||
"github.com/google/certificate-transparency-go/x509"
|
||||
"github.com/google/certificate-transparency-go/x509/pkix"
|
||||
)
|
||||
|
||||
// OIDForStandardExtension indicates whether oid identifies a standard extension.
|
||||
// Standard extensions are listed in RFC 5280 (and other RFCs).
|
||||
func OIDForStandardExtension(oid asn1.ObjectIdentifier) bool {
|
||||
if oid.Equal(x509.OIDExtensionSubjectKeyId) ||
|
||||
oid.Equal(x509.OIDExtensionKeyUsage) ||
|
||||
oid.Equal(x509.OIDExtensionExtendedKeyUsage) ||
|
||||
oid.Equal(x509.OIDExtensionAuthorityKeyId) ||
|
||||
oid.Equal(x509.OIDExtensionBasicConstraints) ||
|
||||
oid.Equal(x509.OIDExtensionSubjectAltName) ||
|
||||
oid.Equal(x509.OIDExtensionCertificatePolicies) ||
|
||||
oid.Equal(x509.OIDExtensionNameConstraints) ||
|
||||
oid.Equal(x509.OIDExtensionCRLDistributionPoints) ||
|
||||
oid.Equal(x509.OIDExtensionIssuerAltName) ||
|
||||
oid.Equal(x509.OIDExtensionSubjectDirectoryAttributes) ||
|
||||
oid.Equal(x509.OIDExtensionInhibitAnyPolicy) ||
|
||||
oid.Equal(x509.OIDExtensionPolicyConstraints) ||
|
||||
oid.Equal(x509.OIDExtensionPolicyMappings) ||
|
||||
oid.Equal(x509.OIDExtensionFreshestCRL) ||
|
||||
oid.Equal(x509.OIDExtensionSubjectInfoAccess) ||
|
||||
oid.Equal(x509.OIDExtensionAuthorityInfoAccess) ||
|
||||
oid.Equal(x509.OIDExtensionCTPoison) ||
|
||||
oid.Equal(x509.OIDExtensionCTSCT) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// OIDInExtensions checks whether the extension identified by oid is present in extensions
|
||||
// and returns how many times it occurs together with an indication of whether any of them
|
||||
// are marked critical.
|
||||
func OIDInExtensions(oid asn1.ObjectIdentifier, extensions []pkix.Extension) (int, bool) {
|
||||
count := 0
|
||||
critical := false
|
||||
for _, ext := range extensions {
|
||||
if ext.Id.Equal(oid) {
|
||||
count++
|
||||
if ext.Critical {
|
||||
critical = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return count, critical
|
||||
}
|
||||
|
||||
// String formatting for various X.509/ASN.1 types
|
||||
func bitStringToString(b asn1.BitString) string {
|
||||
result := hex.EncodeToString(b.Bytes)
|
||||
bitsLeft := b.BitLength % 8
|
||||
if bitsLeft != 0 {
|
||||
result += " (" + strconv.Itoa(8-bitsLeft) + " unused bits)"
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func publicKeyAlgorithmToString(algo x509.PublicKeyAlgorithm) string {
|
||||
// Use OpenSSL-compatible strings for the algorithms.
|
||||
switch algo {
|
||||
case x509.RSA:
|
||||
return "rsaEncryption"
|
||||
case x509.DSA:
|
||||
return "dsaEncryption"
|
||||
case x509.ECDSA:
|
||||
return "id-ecPublicKey"
|
||||
default:
|
||||
return strconv.Itoa(int(algo))
|
||||
}
|
||||
}
|
||||
|
||||
// appendHexData adds a hex dump of binary data to buf, with line breaks
|
||||
// after each set of count bytes, and with each new line prefixed with the
|
||||
// given prefix.
|
||||
func appendHexData(buf *bytes.Buffer, data []byte, count int, prefix string) {
|
||||
for ii, byte := range data {
|
||||
if ii%count == 0 {
|
||||
if ii > 0 {
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
buf.WriteString(prefix)
|
||||
}
|
||||
buf.WriteString(fmt.Sprintf("%02x:", byte))
|
||||
}
|
||||
}
|
||||
|
||||
func curveOIDToString(oid asn1.ObjectIdentifier) (t string, bitlen int) {
|
||||
switch {
|
||||
case oid.Equal(x509.OIDNamedCurveP224):
|
||||
return "secp224r1", 224
|
||||
case oid.Equal(x509.OIDNamedCurveP256):
|
||||
return "prime256v1", 256
|
||||
case oid.Equal(x509.OIDNamedCurveP384):
|
||||
return "secp384r1", 384
|
||||
case oid.Equal(x509.OIDNamedCurveP521):
|
||||
return "secp521r1", 521
|
||||
}
|
||||
return fmt.Sprintf("%v", oid), -1
|
||||
}
|
||||
|
||||
func publicKeyToString(algo x509.PublicKeyAlgorithm, pub interface{}) string {
|
||||
var buf bytes.Buffer
|
||||
switch pub := pub.(type) {
|
||||
case *rsa.PublicKey:
|
||||
bitlen := pub.N.BitLen()
|
||||
buf.WriteString(fmt.Sprintf(" Public Key: (%d bit)\n", bitlen))
|
||||
buf.WriteString(" Modulus:\n")
|
||||
data := pub.N.Bytes()
|
||||
appendHexData(&buf, data, 15, " ")
|
||||
buf.WriteString("\n")
|
||||
buf.WriteString(fmt.Sprintf(" Exponent: %d (0x%x)", pub.E, pub.E))
|
||||
case *dsa.PublicKey:
|
||||
buf.WriteString(" pub:\n")
|
||||
appendHexData(&buf, pub.Y.Bytes(), 15, " ")
|
||||
buf.WriteString("\n")
|
||||
buf.WriteString(" P:\n")
|
||||
appendHexData(&buf, pub.P.Bytes(), 15, " ")
|
||||
buf.WriteString("\n")
|
||||
buf.WriteString(" Q:\n")
|
||||
appendHexData(&buf, pub.Q.Bytes(), 15, " ")
|
||||
buf.WriteString("\n")
|
||||
buf.WriteString(" G:\n")
|
||||
appendHexData(&buf, pub.G.Bytes(), 15, " ")
|
||||
case *ecdsa.PublicKey:
|
||||
data := elliptic.Marshal(pub.Curve, pub.X, pub.Y)
|
||||
oid, ok := x509.OIDFromNamedCurve(pub.Curve)
|
||||
if !ok {
|
||||
return " <unsupported elliptic curve>"
|
||||
}
|
||||
oidname, bitlen := curveOIDToString(oid)
|
||||
buf.WriteString(fmt.Sprintf(" Public Key: (%d bit)\n", bitlen))
|
||||
buf.WriteString(" pub:\n")
|
||||
appendHexData(&buf, data, 15, " ")
|
||||
buf.WriteString("\n")
|
||||
buf.WriteString(fmt.Sprintf(" ASN1 OID: %s", oidname))
|
||||
default:
|
||||
buf.WriteString(fmt.Sprintf("%v", pub))
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func commaAppend(buf *bytes.Buffer, s string) {
|
||||
if buf.Len() > 0 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
buf.WriteString(s)
|
||||
}
|
||||
|
||||
func keyUsageToString(k x509.KeyUsage) string {
|
||||
var buf bytes.Buffer
|
||||
if k&x509.KeyUsageDigitalSignature != 0 {
|
||||
commaAppend(&buf, "Digital Signature")
|
||||
}
|
||||
if k&x509.KeyUsageContentCommitment != 0 {
|
||||
commaAppend(&buf, "Content Commitment")
|
||||
}
|
||||
if k&x509.KeyUsageKeyEncipherment != 0 {
|
||||
commaAppend(&buf, "Key Encipherment")
|
||||
}
|
||||
if k&x509.KeyUsageDataEncipherment != 0 {
|
||||
commaAppend(&buf, "Data Encipherment")
|
||||
}
|
||||
if k&x509.KeyUsageKeyAgreement != 0 {
|
||||
commaAppend(&buf, "Key Agreement")
|
||||
}
|
||||
if k&x509.KeyUsageCertSign != 0 {
|
||||
commaAppend(&buf, "Certificate Signing")
|
||||
}
|
||||
if k&x509.KeyUsageCRLSign != 0 {
|
||||
commaAppend(&buf, "CRL Signing")
|
||||
}
|
||||
if k&x509.KeyUsageEncipherOnly != 0 {
|
||||
commaAppend(&buf, "Encipher Only")
|
||||
}
|
||||
if k&x509.KeyUsageDecipherOnly != 0 {
|
||||
commaAppend(&buf, "Decipher Only")
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func extKeyUsageToString(u x509.ExtKeyUsage) string {
|
||||
switch u {
|
||||
case x509.ExtKeyUsageAny:
|
||||
return "Any"
|
||||
case x509.ExtKeyUsageServerAuth:
|
||||
return "TLS Web server authentication"
|
||||
case x509.ExtKeyUsageClientAuth:
|
||||
return "TLS Web client authentication"
|
||||
case x509.ExtKeyUsageCodeSigning:
|
||||
return "Signing of executable code"
|
||||
case x509.ExtKeyUsageEmailProtection:
|
||||
return "Email protection"
|
||||
case x509.ExtKeyUsageIPSECEndSystem:
|
||||
return "IPSEC end system"
|
||||
case x509.ExtKeyUsageIPSECTunnel:
|
||||
return "IPSEC tunnel"
|
||||
case x509.ExtKeyUsageIPSECUser:
|
||||
return "IPSEC user"
|
||||
case x509.ExtKeyUsageTimeStamping:
|
||||
return "Time stamping"
|
||||
case x509.ExtKeyUsageOCSPSigning:
|
||||
return "OCSP signing"
|
||||
case x509.ExtKeyUsageMicrosoftServerGatedCrypto:
|
||||
return "Microsoft server gated cryptography"
|
||||
case x509.ExtKeyUsageNetscapeServerGatedCrypto:
|
||||
return "Netscape server gated cryptography"
|
||||
case x509.ExtKeyUsageCertificateTransparency:
|
||||
return "Certificate transparency"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
func attributeOIDToString(oid asn1.ObjectIdentifier) string {
|
||||
switch {
|
||||
case oid.Equal(pkix.OIDCountry):
|
||||
return "Country"
|
||||
case oid.Equal(pkix.OIDOrganization):
|
||||
return "Organization"
|
||||
case oid.Equal(pkix.OIDOrganizationalUnit):
|
||||
return "OrganizationalUnit"
|
||||
case oid.Equal(pkix.OIDCommonName):
|
||||
return "CommonName"
|
||||
case oid.Equal(pkix.OIDSerialNumber):
|
||||
return "SerialNumber"
|
||||
case oid.Equal(pkix.OIDLocality):
|
||||
return "Locality"
|
||||
case oid.Equal(pkix.OIDProvince):
|
||||
return "Province"
|
||||
case oid.Equal(pkix.OIDStreetAddress):
|
||||
return "StreetAddress"
|
||||
case oid.Equal(pkix.OIDPostalCode):
|
||||
return "PostalCode"
|
||||
case oid.Equal(pkix.OIDPseudonym):
|
||||
return "Pseudonym"
|
||||
case oid.Equal(pkix.OIDTitle):
|
||||
return "Title"
|
||||
case oid.Equal(pkix.OIDDnQualifier):
|
||||
return "DnQualifier"
|
||||
case oid.Equal(pkix.OIDName):
|
||||
return "Name"
|
||||
case oid.Equal(pkix.OIDSurname):
|
||||
return "Surname"
|
||||
case oid.Equal(pkix.OIDGivenName):
|
||||
return "GivenName"
|
||||
case oid.Equal(pkix.OIDInitials):
|
||||
return "Initials"
|
||||
case oid.Equal(pkix.OIDGenerationQualifier):
|
||||
return "GenerationQualifier"
|
||||
default:
|
||||
return oid.String()
|
||||
}
|
||||
}
|
||||
|
||||
// NameToString creates a string description of a pkix.Name object.
|
||||
func NameToString(name pkix.Name) string {
|
||||
var result bytes.Buffer
|
||||
addSingle := func(prefix, item string) {
|
||||
if len(item) == 0 {
|
||||
return
|
||||
}
|
||||
commaAppend(&result, prefix)
|
||||
result.WriteString(item)
|
||||
}
|
||||
addList := func(prefix string, items []string) {
|
||||
for _, item := range items {
|
||||
addSingle(prefix, item)
|
||||
}
|
||||
}
|
||||
addList("C=", name.Country)
|
||||
addList("O=", name.Organization)
|
||||
addList("OU=", name.OrganizationalUnit)
|
||||
addList("L=", name.Locality)
|
||||
addList("ST=", name.Province)
|
||||
addList("streetAddress=", name.StreetAddress)
|
||||
addList("postalCode=", name.PostalCode)
|
||||
addSingle("serialNumber=", name.SerialNumber)
|
||||
addSingle("CN=", name.CommonName)
|
||||
for _, atv := range name.Names {
|
||||
value, ok := atv.Value.(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
t := atv.Type
|
||||
// All of the defined attribute OIDs are of the form 2.5.4.N, and OIDAttribute is
|
||||
// the 2.5.4 prefix ('id-at' in RFC 5280).
|
||||
if len(t) == 4 && t[0] == pkix.OIDAttribute[0] && t[1] == pkix.OIDAttribute[1] && t[2] == pkix.OIDAttribute[2] {
|
||||
// OID is 'id-at N', so check the final value to figure out which attribute.
|
||||
switch t[3] {
|
||||
case pkix.OIDCommonName[3], pkix.OIDSerialNumber[3], pkix.OIDCountry[3], pkix.OIDLocality[3], pkix.OIDProvince[3],
|
||||
pkix.OIDStreetAddress[3], pkix.OIDOrganization[3], pkix.OIDOrganizationalUnit[3], pkix.OIDPostalCode[3]:
|
||||
continue // covered by explicit fields
|
||||
case pkix.OIDPseudonym[3]:
|
||||
addSingle("pseudonym=", value)
|
||||
continue
|
||||
case pkix.OIDTitle[3]:
|
||||
addSingle("title=", value)
|
||||
continue
|
||||
case pkix.OIDDnQualifier[3]:
|
||||
addSingle("dnQualifier=", value)
|
||||
continue
|
||||
case pkix.OIDName[3]:
|
||||
addSingle("name=", value)
|
||||
continue
|
||||
case pkix.OIDSurname[3]:
|
||||
addSingle("surname=", value)
|
||||
continue
|
||||
case pkix.OIDGivenName[3]:
|
||||
addSingle("givenName=", value)
|
||||
continue
|
||||
case pkix.OIDInitials[3]:
|
||||
addSingle("initials=", value)
|
||||
continue
|
||||
case pkix.OIDGenerationQualifier[3]:
|
||||
addSingle("generationQualifier=", value)
|
||||
continue
|
||||
}
|
||||
}
|
||||
addSingle(t.String()+"=", value)
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
|
||||
// OtherNameToString creates a string description of an x509.OtherName object.
|
||||
func OtherNameToString(other x509.OtherName) string {
|
||||
return fmt.Sprintf("%v=%v", other.TypeID, hex.EncodeToString(other.Value.Bytes))
|
||||
}
|
||||
|
||||
// GeneralNamesToString creates a string description of an x509.GeneralNames object.
|
||||
func GeneralNamesToString(gname *x509.GeneralNames) string {
|
||||
var buf bytes.Buffer
|
||||
for _, name := range gname.DNSNames {
|
||||
commaAppend(&buf, "DNS:"+name)
|
||||
}
|
||||
for _, email := range gname.EmailAddresses {
|
||||
commaAppend(&buf, "email:"+email)
|
||||
}
|
||||
for _, name := range gname.DirectoryNames {
|
||||
commaAppend(&buf, "DirName:"+NameToString(name))
|
||||
}
|
||||
for _, uri := range gname.URIs {
|
||||
commaAppend(&buf, "URI:"+uri)
|
||||
}
|
||||
for _, ip := range gname.IPNets {
|
||||
if ip.Mask == nil {
|
||||
commaAppend(&buf, "IP Address:"+ip.IP.String())
|
||||
} else {
|
||||
commaAppend(&buf, "IP Address:"+ip.IP.String()+"/"+ip.Mask.String())
|
||||
}
|
||||
}
|
||||
for _, id := range gname.RegisteredIDs {
|
||||
commaAppend(&buf, "Registered ID:"+id.String())
|
||||
}
|
||||
for _, other := range gname.OtherNames {
|
||||
commaAppend(&buf, "othername:"+OtherNameToString(other))
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// CertificateToString generates a string describing the given certificate.
|
||||
// The output roughly resembles that from openssl x509 -text.
|
||||
func CertificateToString(cert *x509.Certificate) string {
|
||||
var result bytes.Buffer
|
||||
result.WriteString(fmt.Sprintf("Certificate:\n"))
|
||||
result.WriteString(fmt.Sprintf(" Data:\n"))
|
||||
result.WriteString(fmt.Sprintf(" Version: %d (%#x)\n", cert.Version, cert.Version-1))
|
||||
result.WriteString(fmt.Sprintf(" Serial Number: %d (%#[1]x)\n", cert.SerialNumber))
|
||||
result.WriteString(fmt.Sprintf(" Signature Algorithm: %v\n", cert.SignatureAlgorithm))
|
||||
result.WriteString(fmt.Sprintf(" Issuer: %v\n", NameToString(cert.Issuer)))
|
||||
result.WriteString(fmt.Sprintf(" Validity:\n"))
|
||||
result.WriteString(fmt.Sprintf(" Not Before: %v\n", cert.NotBefore))
|
||||
result.WriteString(fmt.Sprintf(" Not After : %v\n", cert.NotAfter))
|
||||
result.WriteString(fmt.Sprintf(" Subject: %v\n", NameToString(cert.Subject)))
|
||||
result.WriteString(fmt.Sprintf(" Subject Public Key Info:\n"))
|
||||
result.WriteString(fmt.Sprintf(" Public Key Algorithm: %v\n", publicKeyAlgorithmToString(cert.PublicKeyAlgorithm)))
|
||||
result.WriteString(fmt.Sprintf("%v\n", publicKeyToString(cert.PublicKeyAlgorithm, cert.PublicKey)))
|
||||
|
||||
if len(cert.Extensions) > 0 {
|
||||
result.WriteString(fmt.Sprintf(" X509v3 extensions:\n"))
|
||||
}
|
||||
// First display the extensions that are already cracked out
|
||||
showAuthKeyID(&result, cert)
|
||||
showSubjectKeyID(&result, cert)
|
||||
showKeyUsage(&result, cert)
|
||||
showExtendedKeyUsage(&result, cert)
|
||||
showBasicConstraints(&result, cert)
|
||||
showSubjectAltName(&result, cert)
|
||||
showNameConstraints(&result, cert)
|
||||
showCertPolicies(&result, cert)
|
||||
showCRLDPs(&result, cert)
|
||||
showAuthInfoAccess(&result, cert)
|
||||
showCTPoison(&result, cert)
|
||||
showCTSCT(&result, cert)
|
||||
|
||||
showUnhandledExtensions(&result, cert)
|
||||
showSignature(&result, cert)
|
||||
|
||||
return result.String()
|
||||
}
|
||||
|
||||
func showCritical(result *bytes.Buffer, critical bool) {
|
||||
if critical {
|
||||
result.WriteString(" critical")
|
||||
}
|
||||
result.WriteString("\n")
|
||||
}
|
||||
|
||||
func showAuthKeyID(result *bytes.Buffer, cert *x509.Certificate) {
|
||||
count, critical := OIDInExtensions(x509.OIDExtensionAuthorityKeyId, cert.Extensions)
|
||||
if count > 0 {
|
||||
result.WriteString(fmt.Sprintf(" X509v3 Authority Key Identifier:"))
|
||||
showCritical(result, critical)
|
||||
result.WriteString(fmt.Sprintf(" keyid:%v\n", hex.EncodeToString(cert.AuthorityKeyId)))
|
||||
}
|
||||
}
|
||||
|
||||
func showSubjectKeyID(result *bytes.Buffer, cert *x509.Certificate) {
|
||||
count, critical := OIDInExtensions(x509.OIDExtensionSubjectKeyId, cert.Extensions)
|
||||
if count > 0 {
|
||||
result.WriteString(fmt.Sprintf(" X509v3 Subject Key Identifier:"))
|
||||
showCritical(result, critical)
|
||||
result.WriteString(fmt.Sprintf(" keyid:%v\n", hex.EncodeToString(cert.SubjectKeyId)))
|
||||
}
|
||||
}
|
||||
|
||||
func showKeyUsage(result *bytes.Buffer, cert *x509.Certificate) {
|
||||
count, critical := OIDInExtensions(x509.OIDExtensionKeyUsage, cert.Extensions)
|
||||
if count > 0 {
|
||||
result.WriteString(fmt.Sprintf(" X509v3 Key Usage:"))
|
||||
showCritical(result, critical)
|
||||
result.WriteString(fmt.Sprintf(" %v\n", keyUsageToString(cert.KeyUsage)))
|
||||
}
|
||||
}
|
||||
|
||||
func showExtendedKeyUsage(result *bytes.Buffer, cert *x509.Certificate) {
|
||||
count, critical := OIDInExtensions(x509.OIDExtensionExtendedKeyUsage, cert.Extensions)
|
||||
if count > 0 {
|
||||
result.WriteString(fmt.Sprintf(" X509v3 Extended Key Usage:"))
|
||||
showCritical(result, critical)
|
||||
var usages bytes.Buffer
|
||||
for _, usage := range cert.ExtKeyUsage {
|
||||
commaAppend(&usages, extKeyUsageToString(usage))
|
||||
}
|
||||
for _, oid := range cert.UnknownExtKeyUsage {
|
||||
commaAppend(&usages, oid.String())
|
||||
}
|
||||
result.WriteString(fmt.Sprintf(" %v\n", usages.String()))
|
||||
}
|
||||
}
|
||||
|
||||
func showBasicConstraints(result *bytes.Buffer, cert *x509.Certificate) {
|
||||
count, critical := OIDInExtensions(x509.OIDExtensionBasicConstraints, cert.Extensions)
|
||||
if count > 0 {
|
||||
result.WriteString(fmt.Sprintf(" X509v3 Basic Constraints:"))
|
||||
showCritical(result, critical)
|
||||
result.WriteString(fmt.Sprintf(" CA:%t", cert.IsCA))
|
||||
if cert.MaxPathLen > 0 || cert.MaxPathLenZero {
|
||||
result.WriteString(fmt.Sprintf(", pathlen:%d", cert.MaxPathLen))
|
||||
}
|
||||
result.WriteString(fmt.Sprintf("\n"))
|
||||
}
|
||||
}
|
||||
|
||||
func showSubjectAltName(result *bytes.Buffer, cert *x509.Certificate) {
|
||||
count, critical := OIDInExtensions(x509.OIDExtensionSubjectAltName, cert.Extensions)
|
||||
if count > 0 {
|
||||
result.WriteString(fmt.Sprintf(" X509v3 Subject Alternative Name:"))
|
||||
showCritical(result, critical)
|
||||
var buf bytes.Buffer
|
||||
for _, name := range cert.DNSNames {
|
||||
commaAppend(&buf, "DNS:"+name)
|
||||
}
|
||||
for _, email := range cert.EmailAddresses {
|
||||
commaAppend(&buf, "email:"+email)
|
||||
}
|
||||
for _, ip := range cert.IPAddresses {
|
||||
commaAppend(&buf, "IP Address:"+ip.String())
|
||||
}
|
||||
|
||||
result.WriteString(fmt.Sprintf(" %v\n", buf.String()))
|
||||
// TODO(drysdale): include other name forms
|
||||
}
|
||||
}
|
||||
|
||||
func showNameConstraints(result *bytes.Buffer, cert *x509.Certificate) {
|
||||
count, critical := OIDInExtensions(x509.OIDExtensionNameConstraints, cert.Extensions)
|
||||
if count > 0 {
|
||||
result.WriteString(fmt.Sprintf(" X509v3 Name Constraints:"))
|
||||
showCritical(result, critical)
|
||||
if len(cert.PermittedDNSDomains) > 0 {
|
||||
result.WriteString(fmt.Sprintf(" Permitted:\n"))
|
||||
var buf bytes.Buffer
|
||||
for _, name := range cert.PermittedDNSDomains {
|
||||
commaAppend(&buf, "DNS:"+name)
|
||||
}
|
||||
result.WriteString(fmt.Sprintf(" %v\n", buf.String()))
|
||||
}
|
||||
// TODO(drysdale): include other name forms
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func showCertPolicies(result *bytes.Buffer, cert *x509.Certificate) {
|
||||
count, critical := OIDInExtensions(x509.OIDExtensionCertificatePolicies, cert.Extensions)
|
||||
if count > 0 {
|
||||
result.WriteString(fmt.Sprintf(" X509v3 Certificate Policies:"))
|
||||
showCritical(result, critical)
|
||||
for _, oid := range cert.PolicyIdentifiers {
|
||||
result.WriteString(fmt.Sprintf(" Policy: %v\n", oid.String()))
|
||||
// TODO(drysdale): Display any qualifiers associated with the policy
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func showCRLDPs(result *bytes.Buffer, cert *x509.Certificate) {
|
||||
count, critical := OIDInExtensions(x509.OIDExtensionCRLDistributionPoints, cert.Extensions)
|
||||
if count > 0 {
|
||||
result.WriteString(fmt.Sprintf(" X509v3 CRL Distribution Points:"))
|
||||
showCritical(result, critical)
|
||||
result.WriteString(fmt.Sprintf(" Full Name:\n"))
|
||||
var buf bytes.Buffer
|
||||
for _, pt := range cert.CRLDistributionPoints {
|
||||
commaAppend(&buf, "URI:"+pt)
|
||||
}
|
||||
result.WriteString(fmt.Sprintf(" %v\n", buf.String()))
|
||||
// TODO(drysdale): Display other GeneralNames types, plus issuer/reasons/relative-name
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func showAuthInfoAccess(result *bytes.Buffer, cert *x509.Certificate) {
|
||||
count, critical := OIDInExtensions(x509.OIDExtensionAuthorityInfoAccess, cert.Extensions)
|
||||
if count > 0 {
|
||||
result.WriteString(fmt.Sprintf(" Authority Information Access:"))
|
||||
showCritical(result, critical)
|
||||
var issuerBuf bytes.Buffer
|
||||
for _, issuer := range cert.IssuingCertificateURL {
|
||||
commaAppend(&issuerBuf, "URI:"+issuer)
|
||||
}
|
||||
if issuerBuf.Len() > 0 {
|
||||
result.WriteString(fmt.Sprintf(" CA Issuers - %v\n", issuerBuf.String()))
|
||||
}
|
||||
var ocspBuf bytes.Buffer
|
||||
for _, ocsp := range cert.OCSPServer {
|
||||
commaAppend(&ocspBuf, "URI:"+ocsp)
|
||||
}
|
||||
if ocspBuf.Len() > 0 {
|
||||
result.WriteString(fmt.Sprintf(" OCSP - %v\n", ocspBuf.String()))
|
||||
}
|
||||
// TODO(drysdale): Display other GeneralNames types
|
||||
}
|
||||
}
|
||||
|
||||
func showCTPoison(result *bytes.Buffer, cert *x509.Certificate) {
|
||||
count, critical := OIDInExtensions(x509.OIDExtensionCTPoison, cert.Extensions)
|
||||
if count > 0 {
|
||||
result.WriteString(fmt.Sprintf(" RFC6962 Pre-Certificate Poison:"))
|
||||
showCritical(result, critical)
|
||||
result.WriteString(" .....\n")
|
||||
}
|
||||
}
|
||||
|
||||
func showCTSCT(result *bytes.Buffer, cert *x509.Certificate) {
|
||||
count, critical := OIDInExtensions(x509.OIDExtensionCTSCT, cert.Extensions)
|
||||
if count > 0 {
|
||||
result.WriteString(fmt.Sprintf(" RFC6962 Certificate Transparency SCT:"))
|
||||
showCritical(result, critical)
|
||||
for i, sctData := range cert.SCTList.SCTList {
|
||||
result.WriteString(fmt.Sprintf(" SCT [%d]:\n", i))
|
||||
var sct ct.SignedCertificateTimestamp
|
||||
_, err := tls.Unmarshal(sctData.Val, &sct)
|
||||
if err != nil {
|
||||
appendHexData(result, sctData.Val, 16, " ")
|
||||
result.WriteString("\n")
|
||||
continue
|
||||
}
|
||||
result.WriteString(fmt.Sprintf(" Version: %d\n", sct.SCTVersion))
|
||||
result.WriteString(fmt.Sprintf(" LogID: %s\n", base64.StdEncoding.EncodeToString(sct.LogID.KeyID[:])))
|
||||
result.WriteString(fmt.Sprintf(" Timestamp: %d\n", sct.Timestamp))
|
||||
result.WriteString(fmt.Sprintf(" Signature: %s\n", sct.Signature.Algorithm))
|
||||
result.WriteString(fmt.Sprintf(" Signature:\n"))
|
||||
appendHexData(result, sct.Signature.Signature, 16, " ")
|
||||
result.WriteString("\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func showUnhandledExtensions(result *bytes.Buffer, cert *x509.Certificate) {
|
||||
for _, ext := range cert.Extensions {
|
||||
// Skip extensions that are already cracked out
|
||||
if oidAlreadyPrinted(ext.Id) {
|
||||
continue
|
||||
}
|
||||
result.WriteString(fmt.Sprintf(" %v:", ext.Id))
|
||||
showCritical(result, ext.Critical)
|
||||
appendHexData(result, ext.Value, 16, " ")
|
||||
result.WriteString("\n")
|
||||
}
|
||||
}
|
||||
|
||||
func showSignature(result *bytes.Buffer, cert *x509.Certificate) {
|
||||
result.WriteString(fmt.Sprintf(" Signature Algorithm: %v\n", cert.SignatureAlgorithm))
|
||||
appendHexData(result, cert.Signature, 18, " ")
|
||||
result.WriteString("\n")
|
||||
}
|
||||
|
||||
// TODO(drysdale): remove this once all standard OIDs are parsed and printed.
|
||||
func oidAlreadyPrinted(oid asn1.ObjectIdentifier) bool {
|
||||
if oid.Equal(x509.OIDExtensionSubjectKeyId) ||
|
||||
oid.Equal(x509.OIDExtensionKeyUsage) ||
|
||||
oid.Equal(x509.OIDExtensionExtendedKeyUsage) ||
|
||||
oid.Equal(x509.OIDExtensionAuthorityKeyId) ||
|
||||
oid.Equal(x509.OIDExtensionBasicConstraints) ||
|
||||
oid.Equal(x509.OIDExtensionSubjectAltName) ||
|
||||
oid.Equal(x509.OIDExtensionCertificatePolicies) ||
|
||||
oid.Equal(x509.OIDExtensionNameConstraints) ||
|
||||
oid.Equal(x509.OIDExtensionCRLDistributionPoints) ||
|
||||
oid.Equal(x509.OIDExtensionAuthorityInfoAccess) ||
|
||||
oid.Equal(x509.OIDExtensionCTPoison) ||
|
||||
oid.Equal(x509.OIDExtensionCTSCT) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// CertificateFromPEM takes a string representing a certificate in PEM format
|
||||
// and returns the corresponding x509.Certificate object.
|
||||
func CertificateFromPEM(pemBytes string) (*x509.Certificate, error) {
|
||||
block, _ := pem.Decode([]byte(pemBytes))
|
||||
if block == nil {
|
||||
return nil, errors.New("failed to decode PEM")
|
||||
}
|
||||
return x509.ParseCertificate(block.Bytes)
|
||||
}
|
Reference in New Issue
Block a user