Merge pull request #60373 from sttts/sttts-1.10-cfssl

Automatic merge from submit-queue (batch tested with PRs 60373, 61098, 61352, 61359, 61362). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Bump cfssl to be compatible with Go 1.10
This commit is contained in:
Kubernetes Submit Queue 2018-03-21 22:39:09 -07:00 committed by GitHub
commit ef3539e69e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
142 changed files with 19716 additions and 5727 deletions

131
Godeps/Godeps.json generated
View File

@ -367,68 +367,68 @@
},
{
"ImportPath": "github.com/cloudflare/cfssl/auth",
"Comment": "1.2.0",
"Rev": "db0d0650b6496bfe8061ec56a92edd32d8e75c30"
"Comment": "1.3.1",
"Rev": "4e2dcbde500472449917533851bf4bae9bdff562"
},
{
"ImportPath": "github.com/cloudflare/cfssl/certdb",
"Comment": "1.2.0",
"Rev": "db0d0650b6496bfe8061ec56a92edd32d8e75c30"
"Comment": "1.3.1",
"Rev": "4e2dcbde500472449917533851bf4bae9bdff562"
},
{
"ImportPath": "github.com/cloudflare/cfssl/config",
"Comment": "1.2.0",
"Rev": "db0d0650b6496bfe8061ec56a92edd32d8e75c30"
"Comment": "1.3.1",
"Rev": "4e2dcbde500472449917533851bf4bae9bdff562"
},
{
"ImportPath": "github.com/cloudflare/cfssl/crypto/pkcs7",
"Comment": "1.2.0",
"Rev": "db0d0650b6496bfe8061ec56a92edd32d8e75c30"
"Comment": "1.3.1",
"Rev": "4e2dcbde500472449917533851bf4bae9bdff562"
},
{
"ImportPath": "github.com/cloudflare/cfssl/csr",
"Comment": "1.2.0",
"Rev": "db0d0650b6496bfe8061ec56a92edd32d8e75c30"
"Comment": "1.3.1",
"Rev": "4e2dcbde500472449917533851bf4bae9bdff562"
},
{
"ImportPath": "github.com/cloudflare/cfssl/errors",
"Comment": "1.2.0",
"Rev": "db0d0650b6496bfe8061ec56a92edd32d8e75c30"
"Comment": "1.3.1",
"Rev": "4e2dcbde500472449917533851bf4bae9bdff562"
},
{
"ImportPath": "github.com/cloudflare/cfssl/helpers",
"Comment": "1.2.0",
"Rev": "db0d0650b6496bfe8061ec56a92edd32d8e75c30"
"Comment": "1.3.1",
"Rev": "4e2dcbde500472449917533851bf4bae9bdff562"
},
{
"ImportPath": "github.com/cloudflare/cfssl/helpers/derhelpers",
"Comment": "1.2.0",
"Rev": "db0d0650b6496bfe8061ec56a92edd32d8e75c30"
"Comment": "1.3.1",
"Rev": "4e2dcbde500472449917533851bf4bae9bdff562"
},
{
"ImportPath": "github.com/cloudflare/cfssl/info",
"Comment": "1.2.0",
"Rev": "db0d0650b6496bfe8061ec56a92edd32d8e75c30"
"Comment": "1.3.1",
"Rev": "4e2dcbde500472449917533851bf4bae9bdff562"
},
{
"ImportPath": "github.com/cloudflare/cfssl/log",
"Comment": "1.2.0",
"Rev": "db0d0650b6496bfe8061ec56a92edd32d8e75c30"
"Comment": "1.3.1",
"Rev": "4e2dcbde500472449917533851bf4bae9bdff562"
},
{
"ImportPath": "github.com/cloudflare/cfssl/ocsp/config",
"Comment": "1.2.0",
"Rev": "db0d0650b6496bfe8061ec56a92edd32d8e75c30"
"Comment": "1.3.1",
"Rev": "4e2dcbde500472449917533851bf4bae9bdff562"
},
{
"ImportPath": "github.com/cloudflare/cfssl/signer",
"Comment": "1.2.0",
"Rev": "db0d0650b6496bfe8061ec56a92edd32d8e75c30"
"Comment": "1.3.1",
"Rev": "4e2dcbde500472449917533851bf4bae9bdff562"
},
{
"ImportPath": "github.com/cloudflare/cfssl/signer/local",
"Comment": "1.2.0",
"Rev": "db0d0650b6496bfe8061ec56a92edd32d8e75c30"
"Comment": "1.3.1",
"Rev": "4e2dcbde500472449917533851bf4bae9bdff562"
},
{
"ImportPath": "github.com/clusterhq/flocker-go",
@ -1689,24 +1689,36 @@
"Rev": "2e02d28350c5fbbad9cfb7e5a1733468b75ab3f9"
},
{
"ImportPath": "github.com/google/certificate-transparency/go",
"Rev": "af98904302724c29aa6659ca372d41c9687de2b7"
"ImportPath": "github.com/google/certificate-transparency-go",
"Rev": "1bec4527572c443752ad4f2830bef88be0533236"
},
{
"ImportPath": "github.com/google/certificate-transparency/go/asn1",
"Rev": "af98904302724c29aa6659ca372d41c9687de2b7"
"ImportPath": "github.com/google/certificate-transparency-go/asn1",
"Rev": "1bec4527572c443752ad4f2830bef88be0533236"
},
{
"ImportPath": "github.com/google/certificate-transparency/go/client",
"Rev": "af98904302724c29aa6659ca372d41c9687de2b7"
"ImportPath": "github.com/google/certificate-transparency-go/client",
"Rev": "1bec4527572c443752ad4f2830bef88be0533236"
},
{
"ImportPath": "github.com/google/certificate-transparency/go/x509",
"Rev": "af98904302724c29aa6659ca372d41c9687de2b7"
"ImportPath": "github.com/google/certificate-transparency-go/client/configpb",
"Rev": "1bec4527572c443752ad4f2830bef88be0533236"
},
{
"ImportPath": "github.com/google/certificate-transparency/go/x509/pkix",
"Rev": "af98904302724c29aa6659ca372d41c9687de2b7"
"ImportPath": "github.com/google/certificate-transparency-go/jsonclient",
"Rev": "1bec4527572c443752ad4f2830bef88be0533236"
},
{
"ImportPath": "github.com/google/certificate-transparency-go/tls",
"Rev": "1bec4527572c443752ad4f2830bef88be0533236"
},
{
"ImportPath": "github.com/google/certificate-transparency-go/x509",
"Rev": "1bec4527572c443752ad4f2830bef88be0533236"
},
{
"ImportPath": "github.com/google/certificate-transparency-go/x509/pkix",
"Rev": "1bec4527572c443752ad4f2830bef88be0533236"
},
{
"ImportPath": "github.com/google/gofuzz",
@ -1964,7 +1976,6 @@
},
{
"ImportPath": "github.com/inconshreveable/mousetrap",
"Comment": "v1.0",
"Rev": "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
},
{
@ -2122,10 +2133,6 @@
"ImportPath": "github.com/mohae/deepcopy",
"Rev": "491d3605edfb866af34a48075bd4355ac1bf46ca"
},
{
"ImportPath": "github.com/mreiferson/go-httpclient",
"Rev": "31f0106b4474f14bc441575c19d3a5fa21aa1f6c"
},
{
"ImportPath": "github.com/mrunalp/fileutils",
"Rev": "4ee1cc9a80582a0c75febdd5cfa779ee4361cbca"
@ -2449,7 +2456,6 @@
},
{
"ImportPath": "github.com/pelletier/go-buffruneio",
"Comment": "v0.1.0",
"Rev": "df1e16fde7fc330a0ca68167c23bf7ed6ac31d6d"
},
{
@ -2799,7 +2805,6 @@
},
{
"ImportPath": "github.com/xiang90/probing",
"Comment": "0.0.1",
"Rev": "07dd2e8dfe18522e9c447ba95f2fe95262f63bb2"
},
{
@ -2813,51 +2818,67 @@
},
{
"ImportPath": "golang.org/x/crypto/bcrypt",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/blowfish",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/cryptobyte",
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/cryptobyte/asn1",
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/curve25519",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/ed25519",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/ed25519/internal/edwards25519",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/internal/chacha20",
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/nacl/secretbox",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/ocsp",
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/pkcs12",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/pkcs12/internal/rc2",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/poly1305",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/salsa20/salsa",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/ssh",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/ssh/terminal",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/exp/inotify",

819
Godeps/LICENSES generated
View File

@ -56118,7 +56118,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================================================
= vendor/github.com/google/certificate-transparency/go licensed under: =
= vendor/github.com/google/certificate-transparency-go licensed under: =
Apache License
@ -56323,12 +56323,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
See the License for the specific language governing permissions and
limitations under the License.
= vendor/github.com/google/certificate-transparency/LICENSE 3b83ef96387f14655fc854ddc3c6bd57
= vendor/github.com/google/certificate-transparency-go/LICENSE 3b83ef96387f14655fc854ddc3c6bd57
================================================================================
================================================================================
= vendor/github.com/google/certificate-transparency/go/asn1 licensed under: =
= vendor/github.com/google/certificate-transparency-go/asn1 licensed under: =
Apache License
@ -56533,12 +56533,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
See the License for the specific language governing permissions and
limitations under the License.
= vendor/github.com/google/certificate-transparency/LICENSE 3b83ef96387f14655fc854ddc3c6bd57
= vendor/github.com/google/certificate-transparency-go/LICENSE 3b83ef96387f14655fc854ddc3c6bd57
================================================================================
================================================================================
= vendor/github.com/google/certificate-transparency/go/client licensed under: =
= vendor/github.com/google/certificate-transparency-go/client licensed under: =
Apache License
@ -56743,12 +56743,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
See the License for the specific language governing permissions and
limitations under the License.
= vendor/github.com/google/certificate-transparency/LICENSE 3b83ef96387f14655fc854ddc3c6bd57
= vendor/github.com/google/certificate-transparency-go/LICENSE 3b83ef96387f14655fc854ddc3c6bd57
================================================================================
================================================================================
= vendor/github.com/google/certificate-transparency/go/x509 licensed under: =
= vendor/github.com/google/certificate-transparency-go/client/configpb licensed under: =
Apache License
@ -56953,12 +56953,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
See the License for the specific language governing permissions and
limitations under the License.
= vendor/github.com/google/certificate-transparency/LICENSE 3b83ef96387f14655fc854ddc3c6bd57
= vendor/github.com/google/certificate-transparency-go/LICENSE 3b83ef96387f14655fc854ddc3c6bd57
================================================================================
================================================================================
= vendor/github.com/google/certificate-transparency/go/x509/pkix licensed under: =
= vendor/github.com/google/certificate-transparency-go/jsonclient licensed under: =
Apache License
@ -57163,7 +57163,637 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
See the License for the specific language governing permissions and
limitations under the License.
= vendor/github.com/google/certificate-transparency/LICENSE 3b83ef96387f14655fc854ddc3c6bd57
= vendor/github.com/google/certificate-transparency-go/LICENSE 3b83ef96387f14655fc854ddc3c6bd57
================================================================================
================================================================================
= vendor/github.com/google/certificate-transparency-go/tls licensed under: =
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.
= vendor/github.com/google/certificate-transparency-go/LICENSE 3b83ef96387f14655fc854ddc3c6bd57
================================================================================
================================================================================
= vendor/github.com/google/certificate-transparency-go/x509 licensed under: =
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.
= vendor/github.com/google/certificate-transparency-go/LICENSE 3b83ef96387f14655fc854ddc3c6bd57
================================================================================
================================================================================
= vendor/github.com/google/certificate-transparency-go/x509/pkix licensed under: =
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.
= vendor/github.com/google/certificate-transparency-go/LICENSE 3b83ef96387f14655fc854ddc3c6bd57
================================================================================
@ -72127,35 +72757,6 @@ SOFTWARE.
================================================================================
================================================================================
= vendor/github.com/mreiferson/go-httpclient licensed under: =
The MIT License (MIT)
Copyright (c) 2012 Matt Reiferson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
= vendor/github.com/mreiferson/go-httpclient/LICENSE 443af26efefd8192911d22fb48b35824
================================================================================
================================================================================
= vendor/github.com/mrunalp/fileutils licensed under: =
@ -88299,6 +88900,76 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================================================
================================================================================
= vendor/golang.org/x/crypto/cryptobyte licensed under: =
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
= vendor/golang.org/x/crypto/LICENSE 5d4950ecb7b26d2c5e4e7b4e0dd74707
================================================================================
================================================================================
= vendor/golang.org/x/crypto/cryptobyte/asn1 licensed under: =
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
= vendor/golang.org/x/crypto/LICENSE 5d4950ecb7b26d2c5e4e7b4e0dd74707
================================================================================
================================================================================
= vendor/golang.org/x/crypto/curve25519 licensed under: =
@ -88404,6 +89075,41 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================================================
================================================================================
= vendor/golang.org/x/crypto/internal/chacha20 licensed under: =
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
= vendor/golang.org/x/crypto/LICENSE 5d4950ecb7b26d2c5e4e7b4e0dd74707
================================================================================
================================================================================
= vendor/golang.org/x/crypto/nacl/secretbox licensed under: =
@ -88439,6 +89145,41 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
================================================================================
================================================================================
= vendor/golang.org/x/crypto/ocsp licensed under: =
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
= vendor/golang.org/x/crypto/LICENSE 5d4950ecb7b26d2c5e4e7b4e0dd74707
================================================================================
================================================================================
= vendor/golang.org/x/crypto/pkcs12 licensed under: =

View File

@ -608,15 +608,15 @@
},
{
"ImportPath": "golang.org/x/crypto/bcrypt",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/blowfish",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/ssh/terminal",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/net/context",

View File

@ -584,35 +584,35 @@
},
{
"ImportPath": "golang.org/x/crypto/bcrypt",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/blowfish",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/ed25519",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/ed25519/internal/edwards25519",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/nacl/secretbox",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/poly1305",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/salsa20/salsa",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/crypto/ssh/terminal",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/net/context",

View File

@ -164,7 +164,7 @@
},
{
"ImportPath": "golang.org/x/crypto/ssh/terminal",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/net/context",

View File

@ -284,7 +284,7 @@
},
{
"ImportPath": "golang.org/x/crypto/ssh/terminal",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/net/context",

View File

@ -68,7 +68,7 @@
},
{
"ImportPath": "golang.org/x/crypto/ssh/terminal",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/net/context",

View File

@ -264,7 +264,7 @@
},
{
"ImportPath": "golang.org/x/crypto/ssh/terminal",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/net/context",

View File

@ -92,7 +92,7 @@
},
{
"ImportPath": "golang.org/x/crypto/ssh/terminal",
"Rev": "81e90905daefcd6fd217b62423c0908922eadb30"
"Rev": "49796115aa4b964c318aad4f3084fdb41e9aa067"
},
{
"ImportPath": "golang.org/x/net/context",

6
vendor/BUILD vendored
View File

@ -240,7 +240,7 @@ filegroup(
"//vendor/github.com/google/cadvisor/validate:all-srcs",
"//vendor/github.com/google/cadvisor/version:all-srcs",
"//vendor/github.com/google/cadvisor/zfs:all-srcs",
"//vendor/github.com/google/certificate-transparency/go:all-srcs",
"//vendor/github.com/google/certificate-transparency-go:all-srcs",
"//vendor/github.com/google/gofuzz:all-srcs",
"//vendor/github.com/google/uuid:all-srcs",
"//vendor/github.com/googleapis/gnostic/OpenAPIv2:all-srcs",
@ -293,7 +293,6 @@ filegroup(
"//vendor/github.com/mitchellh/go-wordwrap:all-srcs",
"//vendor/github.com/mitchellh/mapstructure:all-srcs",
"//vendor/github.com/mohae/deepcopy:all-srcs",
"//vendor/github.com/mreiferson/go-httpclient:all-srcs",
"//vendor/github.com/mrunalp/fileutils:all-srcs",
"//vendor/github.com/mvdan/xurls:all-srcs",
"//vendor/github.com/mxk/go-flowrate/flowrate:all-srcs",
@ -353,9 +352,12 @@ filegroup(
"//vendor/go4.org/errorutil:all-srcs",
"//vendor/golang.org/x/crypto/bcrypt:all-srcs",
"//vendor/golang.org/x/crypto/blowfish:all-srcs",
"//vendor/golang.org/x/crypto/cryptobyte:all-srcs",
"//vendor/golang.org/x/crypto/curve25519:all-srcs",
"//vendor/golang.org/x/crypto/ed25519:all-srcs",
"//vendor/golang.org/x/crypto/internal/chacha20:all-srcs",
"//vendor/golang.org/x/crypto/nacl/secretbox:all-srcs",
"//vendor/golang.org/x/crypto/ocsp:all-srcs",
"//vendor/golang.org/x/crypto/pkcs12:all-srcs",
"//vendor/golang.org/x/crypto/poly1305:all-srcs",
"//vendor/golang.org/x/crypto/salsa20/salsa:all-srcs",

View File

@ -16,21 +16,26 @@ A database is required for the following:
This directory stores [goose](https://bitbucket.org/liamstask/goose/) db migration scripts for various DB backends.
Currently supported:
- SQLite in sqlite
- MySQL in mysql
- PostgreSQL in pg
- SQLite in sqlite
### Get goose
go get https://bitbucket.org/liamstask/goose/
go get bitbucket.org/liamstask/goose/cmd/goose
### Use goose to start and terminate a SQLite DB
To start a SQLite DB using goose:
### Use goose to start and terminate a MySQL DB
To start a MySQL using goose:
goose -path $GOPATH/src/github.com/cloudflare/cfssl/certdb/sqlite up'
goose -path $GOPATH/src/github.com/cloudflare/cfssl/certdb/mysql up
To tear down a SQLite DB using goose
To tear down a MySQL DB using goose
goose -path $GOPATH/src/github.com/cloudflare/cfssl/certdb/sqlite down
goose -path $GOPATH/src/github.com/cloudflare/cfssl/certdb/mysql down
Note: the administration of MySQL DB is not included. We assume
the databases being connected to are already created and access control
is properly handled.
### Use goose to start and terminate a PostgreSQL DB
To start a PostgreSQL using goose:
@ -43,7 +48,16 @@ To tear down a PostgreSQL DB using goose
Note: the administration of PostgreSQL DB is not included. We assume
the databases being connected to are already created and access control
are properly handled.
is properly handled.
### Use goose to start and terminate a SQLite DB
To start a SQLite DB using goose:
goose -path $GOPATH/src/github.com/cloudflare/cfssl/certdb/sqlite up
To tear down a SQLite DB using goose
goose -path $GOPATH/src/github.com/cloudflare/cfssl/certdb/sqlite down
## CFSSL Configuration
@ -56,3 +70,6 @@ or
{"driver":"postgres","data_source":"postgres://user:password@host/db"}
or
{"driver":"mysql","data_source":"user:password@tcp(hostname:3306)/db?parseTime=true"}

View File

@ -31,6 +31,8 @@ type Accessor interface {
InsertCertificate(cr CertificateRecord) error
GetCertificate(serial, aki string) ([]CertificateRecord, error)
GetUnexpiredCertificates() ([]CertificateRecord, error)
GetRevokedAndUnexpiredCertificates() ([]CertificateRecord, error)
GetRevokedAndUnexpiredCertificatesByLabel(label string) ([]CertificateRecord, error)
RevokeCertificate(serial, aki string, reasonCode int) error
InsertOCSP(rr OCSPRecord) error
GetOCSP(serial, aki string) ([]OCSPRecord, error)

View File

@ -2,6 +2,7 @@
package config
import (
"crypto/tls"
"crypto/x509"
"encoding/asn1"
"encoding/json"
@ -59,6 +60,15 @@ type AuthRemote struct {
AuthKeyName string `json:"auth_key"`
}
// CAConstraint specifies various CA constraints on the signed certificate.
// CAConstraint would verify against (and override) the CA
// extensions in the given CSR.
type CAConstraint struct {
IsCA bool `json:"is_ca"`
MaxPathLen int `json:"max_path_len"`
MaxPathLenZero bool `json:"max_path_len_zero"`
}
// A SigningProfile stores information that the CA needs to store
// signature policy.
type SigningProfile struct {
@ -66,7 +76,7 @@ type SigningProfile struct {
IssuerURL []string `json:"issuer_urls"`
OCSP string `json:"ocsp_url"`
CRL string `json:"crl_url"`
CA bool `json:"is_ca"`
CAConstraint CAConstraint `json:"ca_constraint"`
OCSPNoCheck bool `json:"ocsp_no_check"`
ExpiryString string `json:"expiry"`
BackdateString string `json:"backdate"`
@ -86,6 +96,8 @@ type SigningProfile struct {
Provider auth.Provider
RemoteProvider auth.Provider
RemoteServer string
RemoteCAs *x509.CertPool
ClientCert *tls.Certificate
CSRWhitelist *CSRWhitelist
NameWhitelist *regexp.Regexp
ExtensionWhitelist map[string]bool
@ -303,6 +315,44 @@ func (p *Signing) OverrideRemotes(remote string) error {
return nil
}
// SetClientCertKeyPairFromFile updates the properties to set client certificates for mutual
// authenticated TLS remote requests
func (p *Signing) SetClientCertKeyPairFromFile(certFile string, keyFile string) error {
if certFile != "" && keyFile != "" {
cert, err := helpers.LoadClientCertificate(certFile, keyFile)
if err != nil {
return err
}
for _, profile := range p.Profiles {
profile.ClientCert = cert
}
p.Default.ClientCert = cert
}
return nil
}
// SetRemoteCAsFromFile reads root CAs from file and updates the properties to set remote CAs for TLS
// remote requests
func (p *Signing) SetRemoteCAsFromFile(caFile string) error {
if caFile != "" {
remoteCAs, err := helpers.LoadPEMCertPool(caFile)
if err != nil {
return err
}
p.SetRemoteCAs(remoteCAs)
}
return nil
}
// SetRemoteCAs updates the properties to set remote CAs for TLS
// remote requests
func (p *Signing) SetRemoteCAs(remoteCAs *x509.CertPool) {
for _, profile := range p.Profiles {
profile.RemoteCAs = remoteCAs
}
p.Default.RemoteCAs = remoteCAs
}
// NeedsRemoteSigner returns true if one of the profiles has a remote set
func (p *Signing) NeedsRemoteSigner() bool {
for _, profile := range p.Profiles {
@ -360,6 +410,11 @@ func (p *SigningProfile) validProfile(isDefault bool) bool {
return false
}
if p.AuthRemote.RemoteName == "" && p.AuthRemote.AuthKeyName != "" {
log.Debugf("invalid auth remote profile: no remote signer specified")
return false
}
if p.RemoteName != "" {
log.Debugf("validate remote profile")
@ -375,6 +430,7 @@ func (p *SigningProfile) validProfile(isDefault bool) bool {
if p.AuthRemote.RemoteName != "" {
log.Debugf("invalid remote profile: auth remote is also specified")
return false
}
} else if p.AuthRemote.RemoteName != "" {
log.Debugf("validate auth remote profile")
@ -409,6 +465,43 @@ func (p *SigningProfile) validProfile(isDefault bool) bool {
return true
}
// This checks if the SigningProfile object contains configurations that are only effective with a local signer
// which has access to CA private key.
func (p *SigningProfile) hasLocalConfig() bool {
if p.Usage != nil ||
p.IssuerURL != nil ||
p.OCSP != "" ||
p.ExpiryString != "" ||
p.BackdateString != "" ||
p.CAConstraint.IsCA != false ||
!p.NotBefore.IsZero() ||
!p.NotAfter.IsZero() ||
p.NameWhitelistString != "" ||
len(p.CTLogServers) != 0 {
return true
}
return false
}
// warnSkippedSettings prints a log warning message about skipped settings
// in a SigningProfile, usually due to remote signer.
func (p *Signing) warnSkippedSettings() {
const warningMessage = `The configuration value by "usages", "issuer_urls", "ocsp_url", "crl_url", "ca_constraint", "expiry", "backdate", "not_before", "not_after", "cert_store" and "ct_log_servers" are skipped`
if p == nil {
return
}
if (p.Default.RemoteName != "" || p.Default.AuthRemote.RemoteName != "") && p.Default.hasLocalConfig() {
log.Warning("default profile points to a remote signer: ", warningMessage)
}
for name, profile := range p.Profiles {
if (profile.RemoteName != "" || profile.AuthRemote.RemoteName != "") && profile.hasLocalConfig() {
log.Warningf("Profiles[%s] points to a remote signer: %s", name, warningMessage)
}
}
}
// Signing codifies the signature configuration policy for a CA.
type Signing struct {
Profiles map[string]*SigningProfile `json:"profiles"`
@ -450,6 +543,9 @@ func (p *Signing) Valid() bool {
return false
}
}
p.warnSkippedSettings()
return true
}

View File

@ -1,7 +1,7 @@
// Package pkcs7 implements the subset of the CMS PKCS #7 datatype that is typically
// used to package certificates and CRLs. Using openssl, every certificate converted
// to PKCS #7 format from another encoding such as PEM conforms to this implementation.
// reference: https://www.openssl.org/docs/apps/crl2pkcs7.html
// reference: https://www.openssl.org/docs/man1.1.0/apps/crl2pkcs7.html
//
// PKCS #7 Data type, reference: https://tools.ietf.org/html/rfc2315
//

View File

@ -9,6 +9,7 @@ import (
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"errors"
"net"
@ -46,8 +47,8 @@ type KeyRequest interface {
// A BasicKeyRequest contains the algorithm and key size for a new private key.
type BasicKeyRequest struct {
A string `json:"algo"`
S int `json:"size"`
A string `json:"algo" yaml:"algo"`
S int `json:"size" yaml:"size"`
}
// NewBasicKeyRequest returns a default BasicKeyRequest.
@ -129,19 +130,21 @@ func (kr *BasicKeyRequest) SigAlgo() x509.SignatureAlgorithm {
// CAConfig is a section used in the requests initialising a new CA.
type CAConfig struct {
PathLength int `json:"pathlen"`
Expiry string `json:"expiry"`
PathLength int `json:"pathlen" yaml:"pathlen"`
PathLenZero bool `json:"pathlenzero" yaml:"pathlenzero"`
Expiry string `json:"expiry" yaml:"expiry"`
Backdate string `json:"backdate" yaml:"backdate"`
}
// A CertificateRequest encapsulates the API interface to the
// certificate request functionality.
type CertificateRequest struct {
CN string
Names []Name `json:"names"`
Hosts []string `json:"hosts"`
KeyRequest KeyRequest `json:"key,omitempty"`
CA *CAConfig `json:"ca,omitempty"`
SerialNumber string `json:"serialnumber,omitempty"`
Names []Name `json:"names" yaml:"names"`
Hosts []string `json:"hosts" yaml:"hosts"`
KeyRequest KeyRequest `json:"key,omitempty" yaml:"key,omitempty"`
CA *CAConfig `json:"ca,omitempty" yaml:"ca,omitempty"`
SerialNumber string `json:"serialnumber,omitempty" yaml:"serialnumber,omitempty"`
}
// New returns a new, empty CertificateRequest with a
@ -175,6 +178,12 @@ func (cr *CertificateRequest) Name() pkix.Name {
return name
}
// BasicConstraints CSR information RFC 5280, 4.2.1.9
type BasicConstraints struct {
IsCA bool `asn1:"optional"`
MaxPathLen int `asn1:"optional,default:-1"`
}
// ParseRequest takes a certificate request and generates a key and
// CSR from it. It does no validation -- caveat emptor. It will,
// however, fail if the key request is not valid (i.e., an unsupported
@ -217,34 +226,11 @@ func ParseRequest(req *CertificateRequest) (csr, key []byte, err error) {
panic("Generate should have failed to produce a valid key.")
}
var tpl = x509.CertificateRequest{
Subject: req.Name(),
SignatureAlgorithm: req.KeyRequest.SigAlgo(),
}
for i := range req.Hosts {
if ip := net.ParseIP(req.Hosts[i]); ip != nil {
tpl.IPAddresses = append(tpl.IPAddresses, ip)
} else if email, err := mail.ParseAddress(req.Hosts[i]); err == nil && email != nil {
tpl.EmailAddresses = append(tpl.EmailAddresses, req.Hosts[i])
} else {
tpl.DNSNames = append(tpl.DNSNames, req.Hosts[i])
}
}
csr, err = x509.CreateCertificateRequest(rand.Reader, &tpl, priv)
csr, err = Generate(priv.(crypto.Signer), req)
if err != nil {
log.Errorf("failed to generate a CSR: %v", err)
err = cferr.Wrap(cferr.CSRError, cferr.BadRequest, err)
return
}
block := pem.Block{
Type: "CERTIFICATE REQUEST",
Bytes: csr,
}
log.Info("encoded CSR")
csr = pem.EncodeToMemory(&block)
return
}
@ -265,6 +251,7 @@ func ExtractCertificateRequest(cert *x509.Certificate) *CertificateRequest {
// issue date and expiry date.
req.CA.Expiry = cert.NotAfter.Sub(cert.NotBefore).String()
req.CA.PathLength = cert.MaxPathLen
req.CA.PathLenZero = cert.MaxPathLenZero
}
return req
@ -341,7 +328,7 @@ func (g *Generator) ProcessRequest(req *CertificateRequest) (csr, key []byte, er
err = g.Validator(req)
if err != nil {
log.Warningf("invalid request: %v", err)
return
return nil, nil, err
}
csr, key, err = ParseRequest(req)
@ -377,7 +364,7 @@ func Regenerate(priv crypto.Signer, csr []byte) ([]byte, error) {
// Generate creates a new CSR from a CertificateRequest structure and
// an existing key. The KeyRequest field is ignored.
func Generate(priv crypto.Signer, req *CertificateRequest) (csr []byte, err error) {
sigAlgo := helpers.SignerAlgo(priv, crypto.SHA256)
sigAlgo := helpers.SignerAlgo(priv)
if sigAlgo == x509.UnknownSignatureAlgorithm {
return nil, cferr.New(cferr.PrivateKeyError, cferr.Unavailable)
}
@ -397,6 +384,14 @@ func Generate(priv crypto.Signer, req *CertificateRequest) (csr []byte, err erro
}
}
if req.CA != nil {
err = appendCAInfoToCSR(req.CA, &tpl)
if err != nil {
err = cferr.Wrap(cferr.CSRError, cferr.GenerationFailed, err)
return
}
}
csr, err = x509.CreateCertificateRequest(rand.Reader, &tpl, priv)
if err != nil {
log.Errorf("failed to generate a CSR: %v", err)
@ -412,3 +407,26 @@ func Generate(priv crypto.Signer, req *CertificateRequest) (csr []byte, err erro
csr = pem.EncodeToMemory(&block)
return
}
// appendCAInfoToCSR appends CAConfig BasicConstraint extension to a CSR
func appendCAInfoToCSR(reqConf *CAConfig, csr *x509.CertificateRequest) error {
pathlen := reqConf.PathLength
if pathlen == 0 && !reqConf.PathLenZero {
pathlen = -1
}
val, err := asn1.Marshal(BasicConstraints{true, pathlen})
if err != nil {
return err
}
csr.ExtraExtensions = []pkix.Extension{
{
Id: asn1.ObjectIdentifier{2, 5, 29, 19},
Value: val,
Critical: true,
},
}
return nil
}

View File

@ -149,6 +149,8 @@ const (
// UnknownProfile indicates that the profile does not exist.
UnknownProfile // 54XX
UnmatchedWhitelist // 55xx
)
// The following are API client related errors, and should be
@ -189,6 +191,9 @@ const (
// PrecertSubmissionFailed occurs when submitting a precertificate to
// a log server fails
PrecertSubmissionFailed = 100 * (iota + 1)
// CTClientConstructionFailed occurs when the construction of a new
// github.com/google/certificate-transparency client fails.
CTClientConstructionFailed
)
// Certificate persistence related errors specified with CertStoreError
@ -313,6 +318,8 @@ func New(category Category, reason Reason) *Error {
msg = "Policy violation request"
case UnknownProfile:
msg = "Unknown policy profile"
case UnmatchedWhitelist:
msg = "Request does not match policy whitelist"
default:
panic(fmt.Sprintf("Unsupported CFSSL error reason %d under category PolicyError.",
reason))

View File

@ -10,6 +10,9 @@ go_library(
"//vendor/github.com/cloudflare/cfssl/errors:go_default_library",
"//vendor/github.com/cloudflare/cfssl/helpers/derhelpers:go_default_library",
"//vendor/github.com/cloudflare/cfssl/log:go_default_library",
"//vendor/github.com/google/certificate-transparency-go:go_default_library",
"//vendor/github.com/google/certificate-transparency-go/tls:go_default_library",
"//vendor/golang.org/x/crypto/ocsp:go_default_library",
"//vendor/golang.org/x/crypto/pkcs12:go_default_library",
],
)

View File

@ -6,13 +6,23 @@ import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/binary"
"encoding/pem"
"errors"
"fmt"
"io"
"io/ioutil"
"math/big"
"os"
"github.com/google/certificate-transparency-go"
cttls "github.com/google/certificate-transparency-go/tls"
"golang.org/x/crypto/ocsp"
"strings"
"time"
@ -310,11 +320,23 @@ func ParseOneCertificateFromPEM(certsPEM []byte) ([]*x509.Certificate, []byte, e
// LoadPEMCertPool loads a pool of PEM certificates from file.
func LoadPEMCertPool(certsFile string) (*x509.CertPool, error) {
if certsFile == "" {
return nil, nil
}
pemCerts, err := ioutil.ReadFile(certsFile)
if err != nil {
return nil, err
}
return PEMToCertPool(pemCerts)
}
// PEMToCertPool concerts PEM certificates to a CertPool.
func PEMToCertPool(pemCerts []byte) (*x509.CertPool, error) {
if len(pemCerts) == 0 {
return nil, nil
}
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(pemCerts) {
return nil, errors.New("failed to load cert pool")
@ -360,57 +382,12 @@ func GetKeyDERFromPEM(in []byte, password []byte) ([]byte, error) {
return nil, cferr.New(cferr.PrivateKeyError, cferr.DecodeFailed)
}
// CheckSignature verifies a signature made by the key on a CSR, such
// as on the CSR itself.
func CheckSignature(csr *x509.CertificateRequest, algo x509.SignatureAlgorithm, signed, signature []byte) error {
var hashType crypto.Hash
switch algo {
case x509.SHA1WithRSA, x509.ECDSAWithSHA1:
hashType = crypto.SHA1
case x509.SHA256WithRSA, x509.ECDSAWithSHA256:
hashType = crypto.SHA256
case x509.SHA384WithRSA, x509.ECDSAWithSHA384:
hashType = crypto.SHA384
case x509.SHA512WithRSA, x509.ECDSAWithSHA512:
hashType = crypto.SHA512
default:
return x509.ErrUnsupportedAlgorithm
}
if !hashType.Available() {
return x509.ErrUnsupportedAlgorithm
}
h := hashType.New()
h.Write(signed)
digest := h.Sum(nil)
switch pub := csr.PublicKey.(type) {
case *rsa.PublicKey:
return rsa.VerifyPKCS1v15(pub, hashType, digest, signature)
case *ecdsa.PublicKey:
ecdsaSig := new(struct{ R, S *big.Int })
if _, err := asn1.Unmarshal(signature, ecdsaSig); err != nil {
return err
}
if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
return errors.New("x509: ECDSA signature contained zero or negative values")
}
if !ecdsa.Verify(pub, digest, ecdsaSig.R, ecdsaSig.S) {
return errors.New("x509: ECDSA verification failure")
}
return nil
}
return x509.ErrUnsupportedAlgorithm
}
// ParseCSR parses a PEM- or DER-encoded PKCS #10 certificate signing request.
func ParseCSR(in []byte) (csr *x509.CertificateRequest, rest []byte, err error) {
in = bytes.TrimSpace(in)
p, rest := pem.Decode(in)
if p != nil {
if p.Type != "CERTIFICATE REQUEST" {
if p.Type != "NEW CERTIFICATE REQUEST" && p.Type != "CERTIFICATE REQUEST" {
return nil, rest, cferr.New(cferr.CSRError, cferr.BadRequest)
}
@ -423,7 +400,7 @@ func ParseCSR(in []byte) (csr *x509.CertificateRequest, rest []byte, err error)
return nil, rest, err
}
err = CheckSignature(csr, csr.SignatureAlgorithm, csr.RawTBSCertificateRequest, csr.Signature)
err = csr.CheckSignature()
if err != nil {
return nil, rest, err
}
@ -436,8 +413,10 @@ func ParseCSR(in []byte) (csr *x509.CertificateRequest, rest []byte, err error)
// locally.
func ParseCSRPEM(csrPEM []byte) (*x509.CertificateRequest, error) {
block, _ := pem.Decode([]byte(csrPEM))
der := block.Bytes
csrObject, err := x509.ParseCertificateRequest(der)
if block == nil {
return nil, cferr.New(cferr.CSRError, cferr.DecodeFailed)
}
csrObject, err := x509.ParseCertificateRequest(block.Bytes)
if err != nil {
return nil, err
@ -446,28 +425,28 @@ func ParseCSRPEM(csrPEM []byte) (*x509.CertificateRequest, error) {
return csrObject, nil
}
// SignerAlgo returns an X.509 signature algorithm corresponding to
// the crypto.Hash provided from a crypto.Signer.
func SignerAlgo(priv crypto.Signer, h crypto.Hash) x509.SignatureAlgorithm {
switch priv.Public().(type) {
// SignerAlgo returns an X.509 signature algorithm from a crypto.Signer.
func SignerAlgo(priv crypto.Signer) x509.SignatureAlgorithm {
switch pub := priv.Public().(type) {
case *rsa.PublicKey:
switch h {
case crypto.SHA512:
bitLength := pub.N.BitLen()
switch {
case bitLength >= 4096:
return x509.SHA512WithRSA
case crypto.SHA384:
case bitLength >= 3072:
return x509.SHA384WithRSA
case crypto.SHA256:
case bitLength >= 2048:
return x509.SHA256WithRSA
default:
return x509.SHA1WithRSA
}
case *ecdsa.PublicKey:
switch h {
case crypto.SHA512:
switch pub.Curve {
case elliptic.P521():
return x509.ECDSAWithSHA512
case crypto.SHA384:
case elliptic.P384():
return x509.ECDSAWithSHA384
case crypto.SHA256:
case elliptic.P256():
return x509.ECDSAWithSHA256
default:
return x509.ECDSAWithSHA1
@ -476,3 +455,149 @@ func SignerAlgo(priv crypto.Signer, h crypto.Hash) x509.SignatureAlgorithm {
return x509.UnknownSignatureAlgorithm
}
}
// LoadClientCertificate load key/certificate from pem files
func LoadClientCertificate(certFile string, keyFile string) (*tls.Certificate, error) {
if certFile != "" && keyFile != "" {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
log.Critical("Unable to read client certificate from file: %s or key from file: %s", certFile, keyFile)
return nil, err
}
log.Debug("Client certificate loaded ")
return &cert, nil
}
return nil, nil
}
// CreateTLSConfig creates a tls.Config object from certs and roots
func CreateTLSConfig(remoteCAs *x509.CertPool, cert *tls.Certificate) *tls.Config {
var certs []tls.Certificate
if cert != nil {
certs = []tls.Certificate{*cert}
}
return &tls.Config{
Certificates: certs,
RootCAs: remoteCAs,
}
}
// SerializeSCTList serializes a list of SCTs.
func SerializeSCTList(sctList []ct.SignedCertificateTimestamp) ([]byte, error) {
var buf bytes.Buffer
for _, sct := range sctList {
sct, err := cttls.Marshal(sct)
if err != nil {
return nil, err
}
binary.Write(&buf, binary.BigEndian, uint16(len(sct)))
buf.Write(sct)
}
var sctListLengthField = make([]byte, 2)
binary.BigEndian.PutUint16(sctListLengthField, uint16(buf.Len()))
return bytes.Join([][]byte{sctListLengthField, buf.Bytes()}, nil), nil
}
// DeserializeSCTList deserializes a list of SCTs.
func DeserializeSCTList(serializedSCTList []byte) (*[]ct.SignedCertificateTimestamp, error) {
sctList := new([]ct.SignedCertificateTimestamp)
sctReader := bytes.NewBuffer(serializedSCTList)
var sctListLen uint16
err := binary.Read(sctReader, binary.BigEndian, &sctListLen)
if err != nil {
if err == io.EOF {
return sctList, cferr.Wrap(cferr.CTError, cferr.Unknown,
errors.New("serialized SCT list could not be read"))
}
return sctList, cferr.Wrap(cferr.CTError, cferr.Unknown, err)
}
if sctReader.Len() != int(sctListLen) {
return sctList, errors.New("SCT length field and SCT length don't match")
}
for err != io.EOF {
var sctLen uint16
err = binary.Read(sctReader, binary.BigEndian, &sctLen)
if err != nil {
if err == io.EOF {
return sctList, nil
}
return sctList, cferr.Wrap(cferr.CTError, cferr.Unknown, err)
}
if sctReader.Len() < int(sctLen) {
return sctList, errors.New("SCT length field and SCT length don't match")
}
serializedSCT := sctReader.Next(int(sctLen))
var sct ct.SignedCertificateTimestamp
if _, err := cttls.Unmarshal(serializedSCT, &sct); err != nil {
return sctList, cferr.Wrap(cferr.CTError, cferr.Unknown, err)
}
temp := append(*sctList, sct)
sctList = &temp
}
return sctList, cferr.Wrap(cferr.CTError, cferr.Unknown, err)
}
// SCTListFromOCSPResponse extracts the SCTList from an ocsp.Response,
// returning an empty list if the SCT extension was not found or could not be
// unmarshalled.
func SCTListFromOCSPResponse(response *ocsp.Response) ([]ct.SignedCertificateTimestamp, error) {
// This loop finds the SCTListExtension in the OCSP response.
var SCTListExtension, ext pkix.Extension
for _, ext = range response.Extensions {
// sctExtOid is the ObjectIdentifier of a Signed Certificate Timestamp.
sctExtOid := asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 5}
if ext.Id.Equal(sctExtOid) {
SCTListExtension = ext
break
}
}
// This code block extracts the sctList from the SCT extension.
var emptySCTList []ct.SignedCertificateTimestamp
sctList := &emptySCTList
var err error
if numBytes := len(SCTListExtension.Value); numBytes != 0 {
serializedSCTList := new([]byte)
rest := make([]byte, numBytes)
copy(rest, SCTListExtension.Value)
for len(rest) != 0 {
rest, err = asn1.Unmarshal(rest, serializedSCTList)
if err != nil {
return nil, cferr.Wrap(cferr.CTError, cferr.Unknown, err)
}
}
sctList, err = DeserializeSCTList(*serializedSCTList)
}
return *sctList, err
}
// ReadBytes reads a []byte either from a file or an environment variable.
// If valFile has a prefix of 'env:', the []byte is read from the environment
// using the subsequent name. If the prefix is 'file:' the []byte is read from
// the subsequent file. If no prefix is provided, valFile is assumed to be a
// file path.
func ReadBytes(valFile string) ([]byte, error) {
switch splitVal := strings.SplitN(valFile, ":", 2); len(splitVal) {
case 1:
return ioutil.ReadFile(valFile)
case 2:
switch splitVal[0] {
case "env":
return []byte(os.Getenv(splitVal[1])), nil
case "file":
return ioutil.ReadFile(splitVal[1])
default:
return nil, fmt.Errorf("unknown prefix: %s", splitVal[0])
}
default:
return nil, fmt.Errorf("multiple prefixes: %s",
strings.Join(splitVal[:len(splitVal)-1], ", "))
}
}

View File

@ -6,7 +6,6 @@
package log
import (
"flag"
"fmt"
"log"
"os"
@ -45,12 +44,12 @@ var Level = LevelInfo
//
// SyslogWriter is satisfied by *syslog.Writer.
type SyslogWriter interface {
Debug(string) error
Info(string) error
Warning(string) error
Err(string) error
Crit(string) error
Emerg(string) error
Debug(string)
Info(string)
Warning(string)
Err(string)
Crit(string)
Emerg(string)
}
// syslogWriter stores the SetLogger() parameter.
@ -63,33 +62,22 @@ func SetLogger(logger SyslogWriter) {
syslogWriter = logger
}
func init() {
// Only define loglevel flag once.
if flag.Lookup("loglevel") == nil {
flag.IntVar(&Level, "loglevel", LevelInfo, "Log level (0 = DEBUG, 5 = FATAL)")
}
}
func print(l int, msg string) {
if l >= Level {
if syslogWriter != nil {
var err error
switch l {
case LevelDebug:
err = syslogWriter.Debug(msg)
syslogWriter.Debug(msg)
case LevelInfo:
err = syslogWriter.Info(msg)
syslogWriter.Info(msg)
case LevelWarning:
err = syslogWriter.Warning(msg)
syslogWriter.Warning(msg)
case LevelError:
err = syslogWriter.Err(msg)
syslogWriter.Err(msg)
case LevelCritical:
err = syslogWriter.Crit(msg)
syslogWriter.Crit(msg)
case LevelFatal:
err = syslogWriter.Emerg(msg)
}
if err != nil {
log.Printf("Unable to write syslog: %v for msg: %s\n", err, msg)
syslogWriter.Emerg(msg)
}
} else {
log.Printf("[%s] %s", levelPrefix[l], msg)

View File

@ -10,7 +10,6 @@ go_library(
"//vendor/github.com/cloudflare/cfssl/config:go_default_library",
"//vendor/github.com/cloudflare/cfssl/csr:go_default_library",
"//vendor/github.com/cloudflare/cfssl/errors:go_default_library",
"//vendor/github.com/cloudflare/cfssl/helpers:go_default_library",
"//vendor/github.com/cloudflare/cfssl/info:go_default_library",
],
)

View File

@ -13,8 +13,10 @@ go_library(
"//vendor/github.com/cloudflare/cfssl/info:go_default_library",
"//vendor/github.com/cloudflare/cfssl/log:go_default_library",
"//vendor/github.com/cloudflare/cfssl/signer:go_default_library",
"//vendor/github.com/google/certificate-transparency/go:go_default_library",
"//vendor/github.com/google/certificate-transparency/go/client:go_default_library",
"//vendor/github.com/google/certificate-transparency-go:go_default_library",
"//vendor/github.com/google/certificate-transparency-go/client:go_default_library",
"//vendor/github.com/google/certificate-transparency-go/jsonclient:go_default_library",
"//vendor/golang.org/x/net/context:go_default_library",
],
)

View File

@ -8,14 +8,13 @@ import (
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/binary"
"encoding/hex"
"encoding/pem"
"errors"
"io"
"io/ioutil"
"math/big"
"net"
"net/http"
"net/mail"
"os"
@ -26,8 +25,10 @@ import (
"github.com/cloudflare/cfssl/info"
"github.com/cloudflare/cfssl/log"
"github.com/cloudflare/cfssl/signer"
"github.com/google/certificate-transparency/go"
"github.com/google/certificate-transparency/go/client"
"github.com/google/certificate-transparency-go"
"github.com/google/certificate-transparency-go/client"
"github.com/google/certificate-transparency-go/jsonclient"
"golang.org/x/net/context"
)
// Signer contains a signer that uses the standard library to
@ -65,12 +66,12 @@ func NewSigner(priv crypto.Signer, cert *x509.Certificate, sigAlgo x509.Signatur
// and a caKey file, both PEM encoded.
func NewSignerFromFile(caFile, caKeyFile string, policy *config.Signing) (*Signer, error) {
log.Debug("Loading CA: ", caFile)
ca, err := ioutil.ReadFile(caFile)
ca, err := helpers.ReadBytes(caFile)
if err != nil {
return nil, err
}
log.Debug("Loading CA key: ", caKeyFile)
cakey, err := ioutil.ReadFile(caKeyFile)
cakey, err := helpers.ReadBytes(caKeyFile)
if err != nil {
return nil, cferr.Wrap(cferr.CertificateError, cferr.ReadFailed, err)
}
@ -95,12 +96,7 @@ func NewSignerFromFile(caFile, caKeyFile string, policy *config.Signing) (*Signe
return NewSigner(priv, parsedCa, signer.DefaultSigAlgo(priv), policy)
}
func (s *Signer) sign(template *x509.Certificate, profile *config.SigningProfile) (cert []byte, err error) {
err = signer.FillTemplate(template, s.policy.Default, profile)
if err != nil {
return
}
func (s *Signer) sign(template *x509.Certificate) (cert []byte, err error) {
var initRoot bool
if s.ca == nil {
if !template.IsCA {
@ -111,11 +107,6 @@ func (s *Signer) sign(template *x509.Certificate, profile *config.SigningProfile
template.EmailAddresses = nil
s.ca = template
initRoot = true
template.MaxPathLen = signer.MaxPathLen
} else if template.IsCA {
template.MaxPathLen = 1
template.DNSNames = nil
template.EmailAddresses = nil
}
derBytes, err := x509.CreateCertificate(rand.Reader, template, s.ca, template.PublicKey, s.priv)
@ -203,9 +194,9 @@ func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) {
return nil, cferr.New(cferr.CSRError, cferr.DecodeFailed)
}
if block.Type != "CERTIFICATE REQUEST" {
if block.Type != "NEW CERTIFICATE REQUEST" && block.Type != "CERTIFICATE REQUEST" {
return nil, cferr.Wrap(cferr.CSRError,
cferr.BadRequest, errors.New("not a certificate or csr"))
cferr.BadRequest, errors.New("not a csr"))
}
csrTemplate, err := signer.ParseCertificateRequest(s, block.Bytes)
@ -243,6 +234,29 @@ func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) {
}
}
if req.CRLOverride != "" {
safeTemplate.CRLDistributionPoints = []string{req.CRLOverride}
}
if safeTemplate.IsCA {
if !profile.CAConstraint.IsCA {
log.Error("local signer policy disallows issuing CA certificate")
return nil, cferr.New(cferr.PolicyError, cferr.InvalidRequest)
}
if s.ca != nil && s.ca.MaxPathLen > 0 {
if safeTemplate.MaxPathLen >= s.ca.MaxPathLen {
log.Error("local signer certificate disallows CA MaxPathLen extending")
// do not sign a cert with pathlen > current
return nil, cferr.New(cferr.PolicyError, cferr.InvalidRequest)
}
} else if s.ca != nil && s.ca.MaxPathLen == 0 && s.ca.MaxPathLenZero {
log.Error("local signer certificate disallows issuing CA certificate")
// signer has pathlen of 0, do not sign more intermediate CAs
return nil, cferr.New(cferr.PolicyError, cferr.InvalidRequest)
}
}
OverrideHosts(&safeTemplate, req.Hosts)
safeTemplate.Subject = PopulateSubjectFromCSR(req.Subject, safeTemplate.Subject)
@ -250,17 +264,17 @@ func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) {
if profile.NameWhitelist != nil {
if safeTemplate.Subject.CommonName != "" {
if profile.NameWhitelist.Find([]byte(safeTemplate.Subject.CommonName)) == nil {
return nil, cferr.New(cferr.PolicyError, cferr.InvalidPolicy)
return nil, cferr.New(cferr.PolicyError, cferr.UnmatchedWhitelist)
}
}
for _, name := range safeTemplate.DNSNames {
if profile.NameWhitelist.Find([]byte(name)) == nil {
return nil, cferr.New(cferr.PolicyError, cferr.InvalidPolicy)
return nil, cferr.New(cferr.PolicyError, cferr.UnmatchedWhitelist)
}
}
for _, name := range safeTemplate.EmailAddresses {
if profile.NameWhitelist.Find([]byte(name)) == nil {
return nil, cferr.New(cferr.PolicyError, cferr.InvalidPolicy)
return nil, cferr.New(cferr.PolicyError, cferr.UnmatchedWhitelist)
}
}
}
@ -312,6 +326,15 @@ func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) {
}
}
var distPoints = safeTemplate.CRLDistributionPoints
err = signer.FillTemplate(&safeTemplate, s.policy.Default, profile, req.NotBefore, req.NotAfter)
if err != nil {
return nil, err
}
if distPoints != nil && len(distPoints) > 0 {
safeTemplate.CRLDistributionPoints = distPoints
}
var certTBS = safeTemplate
if len(profile.CTLogServers) > 0 {
@ -319,20 +342,24 @@ func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) {
var poisonExtension = pkix.Extension{Id: signer.CTPoisonOID, Critical: true, Value: []byte{0x05, 0x00}}
var poisonedPreCert = certTBS
poisonedPreCert.ExtraExtensions = append(safeTemplate.ExtraExtensions, poisonExtension)
cert, err = s.sign(&poisonedPreCert, profile)
cert, err = s.sign(&poisonedPreCert)
if err != nil {
return
}
derCert, _ := pem.Decode(cert)
prechain := []ct.ASN1Cert{derCert.Bytes, s.ca.Raw}
prechain := []ct.ASN1Cert{{Data: derCert.Bytes}, {Data: s.ca.Raw}}
var sctList []ct.SignedCertificateTimestamp
for _, server := range profile.CTLogServers {
log.Infof("submitting poisoned precertificate to %s", server)
var ctclient = client.New(server)
ctclient, err := client.New(server, nil, jsonclient.Options{})
if err != nil {
return nil, cferr.Wrap(cferr.CTError, cferr.PrecertSubmissionFailed, err)
}
var resp *ct.SignedCertificateTimestamp
resp, err = ctclient.AddPreChain(prechain)
ctx := context.Background()
resp, err = ctclient.AddPreChain(ctx, prechain)
if err != nil {
return nil, cferr.Wrap(cferr.CTError, cferr.PrecertSubmissionFailed, err)
}
@ -340,7 +367,7 @@ func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) {
}
var serializedSCTList []byte
serializedSCTList, err = serializeSCTList(sctList)
serializedSCTList, err = helpers.SerializeSCTList(sctList)
if err != nil {
return nil, cferr.Wrap(cferr.CTError, cferr.Unknown, err)
}
@ -355,17 +382,22 @@ func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) {
certTBS.ExtraExtensions = append(certTBS.ExtraExtensions, SCTListExtension)
}
var signedCert []byte
signedCert, err = s.sign(&certTBS, profile)
signedCert, err = s.sign(&certTBS)
if err != nil {
return nil, err
}
// Get the AKI from signedCert. This is required to support Go 1.9+.
// In prior versions of Go, x509.CreateCertificate updated the
// AuthorityKeyId of certTBS.
parsedCert, _ := helpers.ParseCertificatePEM(signedCert)
if s.dbAccessor != nil {
var certRecord = certdb.CertificateRecord{
Serial: certTBS.SerialNumber.String(),
// this relies on the specific behavior of x509.CreateCertificate
// which updates certTBS AuthorityKeyId from the signer's SubjectKeyId
AKI: hex.EncodeToString(certTBS.AuthorityKeyId),
// which sets the AuthorityKeyId from the signer's SubjectKeyId
AKI: hex.EncodeToString(parsedCert.AuthorityKeyId),
CALabel: req.Label,
Status: "good",
Expiry: certTBS.NotAfter,
@ -382,22 +414,6 @@ func (s *Signer) Sign(req signer.SignRequest) (cert []byte, err error) {
return signedCert, nil
}
func serializeSCTList(sctList []ct.SignedCertificateTimestamp) ([]byte, error) {
var buf bytes.Buffer
for _, sct := range sctList {
sct, err := ct.SerializeSCT(sct)
if err != nil {
return nil, err
}
binary.Write(&buf, binary.BigEndian, uint16(len(sct)))
buf.Write(sct)
}
var sctListLengthField = make([]byte, 2)
binary.BigEndian.PutUint16(sctListLengthField, uint16(buf.Len()))
return bytes.Join([][]byte{sctListLengthField, buf.Bytes()}, nil), nil
}
// Info return a populated info.Resp struct or an error.
func (s *Signer) Info(req info.Req) (resp *info.Resp, err error) {
cert, err := s.Certificate(req.Label, req.Profile)
@ -441,6 +457,16 @@ func (s *Signer) SetDBAccessor(dba certdb.Accessor) {
s.dbAccessor = dba
}
// GetDBAccessor returns the signers' cert db accessor
func (s *Signer) GetDBAccessor() certdb.Accessor {
return s.dbAccessor
}
// SetReqModifier does nothing for local
func (s *Signer) SetReqModifier(func(*http.Request, []byte)) {
// noop
}
// Policy returns the signer's policy.
func (s *Signer) Policy() *config.Signing {
return s.policy

View File

@ -12,6 +12,7 @@ import (
"encoding/asn1"
"errors"
"math/big"
"net/http"
"strings"
"time"
@ -19,13 +20,9 @@ import (
"github.com/cloudflare/cfssl/config"
"github.com/cloudflare/cfssl/csr"
cferr "github.com/cloudflare/cfssl/errors"
"github.com/cloudflare/cfssl/helpers"
"github.com/cloudflare/cfssl/info"
)
// MaxPathLen is the default path length for a new CA certificate.
var MaxPathLen = 2
// Subject contains the information that should be used to override the
// subject information when signing a certificate.
type Subject struct {
@ -54,9 +51,19 @@ type SignRequest struct {
Request string `json:"certificate_request"`
Subject *Subject `json:"subject,omitempty"`
Profile string `json:"profile"`
CRLOverride string `json:"crl_override"`
Label string `json:"label"`
Serial *big.Int `json:"serial,omitempty"`
Extensions []Extension `json:"extensions,omitempty"`
// If provided, NotBefore will be used without modification (except
// for canonicalization) as the value of the notBefore field of the
// certificate. In particular no backdating adjustment will be made
// when NotBefore is provided.
NotBefore time.Time
// If provided, NotAfter will be used without modification (except
// for canonicalization) as the value of the notAfter field of the
// certificate.
NotAfter time.Time
}
// appendIf appends to a if s is not an empty string.
@ -98,9 +105,11 @@ type Signer interface {
Info(info.Req) (*info.Resp, error)
Policy() *config.Signing
SetDBAccessor(certdb.Accessor)
GetDBAccessor() certdb.Accessor
SetPolicy(*config.Signing)
SigAlgo() x509.SignatureAlgorithm
Sign(req SignRequest) (cert []byte, err error)
SetReqModifier(func(*http.Request, []byte))
}
// Profile gets the specific profile from the signer
@ -157,26 +166,46 @@ func DefaultSigAlgo(priv crypto.Signer) x509.SignatureAlgorithm {
// ParseCertificateRequest takes an incoming certificate request and
// builds a certificate template from it.
func ParseCertificateRequest(s Signer, csrBytes []byte) (template *x509.Certificate, err error) {
csr, err := x509.ParseCertificateRequest(csrBytes)
csrv, err := x509.ParseCertificateRequest(csrBytes)
if err != nil {
err = cferr.Wrap(cferr.CSRError, cferr.ParseFailed, err)
return
}
err = helpers.CheckSignature(csr, csr.SignatureAlgorithm, csr.RawTBSCertificateRequest, csr.Signature)
err = csrv.CheckSignature()
if err != nil {
err = cferr.Wrap(cferr.CSRError, cferr.KeyMismatch, err)
return
}
template = &x509.Certificate{
Subject: csr.Subject,
PublicKeyAlgorithm: csr.PublicKeyAlgorithm,
PublicKey: csr.PublicKey,
Subject: csrv.Subject,
PublicKeyAlgorithm: csrv.PublicKeyAlgorithm,
PublicKey: csrv.PublicKey,
SignatureAlgorithm: s.SigAlgo(),
DNSNames: csr.DNSNames,
IPAddresses: csr.IPAddresses,
EmailAddresses: csr.EmailAddresses,
DNSNames: csrv.DNSNames,
IPAddresses: csrv.IPAddresses,
EmailAddresses: csrv.EmailAddresses,
}
for _, val := range csrv.Extensions {
// Check the CSR for the X.509 BasicConstraints (RFC 5280, 4.2.1.9)
// extension and append to template if necessary
if val.Id.Equal(asn1.ObjectIdentifier{2, 5, 29, 19}) {
var constraints csr.BasicConstraints
var rest []byte
if rest, err = asn1.Unmarshal(val.Value, &constraints); err != nil {
return nil, cferr.Wrap(cferr.CSRError, cferr.ParseFailed, err)
} else if len(rest) != 0 {
return nil, cferr.Wrap(cferr.CSRError, cferr.ParseFailed, errors.New("x509: trailing data after X.509 BasicConstraints"))
}
template.BasicConstraintsValid = true
template.IsCA = constraints.IsCA
template.MaxPathLen = constraints.MaxPathLen
template.MaxPathLenZero = template.MaxPathLen == 0
}
}
return
@ -211,17 +240,19 @@ func ComputeSKI(template *x509.Certificate) ([]byte, error) {
// the certificate template as possible from the profiles and current
// template. It fills in the key uses, expiration, revocation URLs
// and SKI.
func FillTemplate(template *x509.Certificate, defaultProfile, profile *config.SigningProfile) error {
func FillTemplate(template *x509.Certificate, defaultProfile, profile *config.SigningProfile, notBefore time.Time, notAfter time.Time) error {
ski, err := ComputeSKI(template)
if err != nil {
return err
}
var (
eku []x509.ExtKeyUsage
ku x509.KeyUsage
backdate time.Duration
expiry time.Duration
notBefore time.Time
notAfter time.Time
crlURL, ocspURL string
issuerURL = profile.IssuerURL
)
// The third value returned from Usages is a list of unknown key usages.
@ -229,7 +260,7 @@ func FillTemplate(template *x509.Certificate, defaultProfile, profile *config.Si
// here.
ku, eku, _ = profile.Usages()
if profile.IssuerURL == nil {
profile.IssuerURL = defaultProfile.IssuerURL
issuerURL = defaultProfile.IssuerURL
}
if ku == 0 && len(eku) == 0 {
@ -246,30 +277,44 @@ func FillTemplate(template *x509.Certificate, defaultProfile, profile *config.Si
if ocspURL = profile.OCSP; ocspURL == "" {
ocspURL = defaultProfile.OCSP
}
if notBefore.IsZero() {
if !profile.NotBefore.IsZero() {
notBefore = profile.NotBefore
} else {
if backdate = profile.Backdate; backdate == 0 {
backdate = -5 * time.Minute
} else {
backdate = -1 * profile.Backdate
}
if !profile.NotBefore.IsZero() {
notBefore = profile.NotBefore.UTC()
} else {
notBefore = time.Now().Round(time.Minute).Add(backdate).UTC()
notBefore = time.Now().Round(time.Minute).Add(backdate)
}
}
notBefore = notBefore.UTC()
if notAfter.IsZero() {
if !profile.NotAfter.IsZero() {
notAfter = profile.NotAfter.UTC()
notAfter = profile.NotAfter
} else {
notAfter = notBefore.Add(expiry).UTC()
notAfter = notBefore.Add(expiry)
}
}
notAfter = notAfter.UTC()
template.NotBefore = notBefore
template.NotAfter = notAfter
template.KeyUsage = ku
template.ExtKeyUsage = eku
template.BasicConstraintsValid = true
template.IsCA = profile.CA
template.IsCA = profile.CAConstraint.IsCA
if template.IsCA {
template.MaxPathLen = profile.CAConstraint.MaxPathLen
if template.MaxPathLen == 0 {
template.MaxPathLenZero = profile.CAConstraint.MaxPathLenZero
}
template.DNSNames = nil
template.EmailAddresses = nil
}
template.SubjectKeyId = ski
if ocspURL != "" {
@ -279,8 +324,8 @@ func FillTemplate(template *x509.Certificate, defaultProfile, profile *config.Si
template.CRLDistributionPoints = []string{crlURL}
}
if len(profile.IssuerURL) != 0 {
template.IssuingCertificateURL = profile.IssuerURL
if len(issuerURL) != 0 {
template.IssuingCertificateURL = issuerURL
}
if len(profile.Policies) != 0 {
err = addPolicies(template, profile.Policies)

View 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

View 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)

View File

@ -10,13 +10,17 @@
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>

View File

@ -0,0 +1,37 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"serialization.go",
"signatures.go",
"types.go",
],
importpath = "github.com/google/certificate-transparency-go",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/google/certificate-transparency-go/tls:go_default_library",
"//vendor/github.com/google/certificate-transparency-go/x509:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//vendor/github.com/google/certificate-transparency-go/asn1:all-srcs",
"//vendor/github.com/google/certificate-transparency-go/client:all-srcs",
"//vendor/github.com/google/certificate-transparency-go/jsonclient:all-srcs",
"//vendor/github.com/google/certificate-transparency-go/tls:all-srcs",
"//vendor/github.com/google/certificate-transparency-go/x509:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View 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

View File

@ -26,13 +26,20 @@ 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>
@ -41,7 +48,10 @@ 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>

View File

@ -0,0 +1,144 @@
# Certificate Transparency: Go Code
[![Build Status](https://travis-ci.org/google/certificate-transparency-go.svg?branch=master)](https://travis-ci.org/google/certificate-transparency-go)
[![Go Report Card](https://goreportcard.com/badge/github.com/google/certificate-transparency-go)](https://goreportcard.com/report/github.com/google/certificate-transparency-go)
[![GoDoc](https://godoc.org/github.com/google/certificate-transparency-go?status.svg)](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 ./...
```

View File

@ -7,7 +7,7 @@ go_library(
"common.go",
"marshal.go",
],
importpath = "github.com/google/certificate-transparency/go/asn1",
importpath = "github.com/google/certificate-transparency-go/asn1",
visibility = ["//visibility:public"],
)

View File

@ -8,12 +8,10 @@
// See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,''
// http://luca.ntop.org/Teaching/Appunti/asn1.html.
//
// START CT CHANGES
// This is a fork of the Go standard library ASN.1 implementation
// (encoding/asn1). The main difference is that this version tries to correct
// for errors (e.g. use of tagPrintableString when the string data is really
// ISO8859-1 - a common error present in many x509 certificates in the wild.)
// END CT CHANGES
package asn1
// ASN.1 is a syntax for specifying abstract objects and BER, DER, PER, XER etc
@ -27,40 +25,53 @@ package asn1
// everything by any means.
import (
// START CT CHANGES
"errors"
"fmt"
// END CT CHANGES
"math"
"math/big"
"reflect"
// START CT CHANGES
"strconv"
"strings"
// END CT CHANGES
"time"
"unicode/utf8"
)
// A StructuralError suggests that the ASN.1 data is valid, but the Go type
// which is receiving it doesn't match.
type StructuralError struct {
Msg string
Field string
}
func (e StructuralError) Error() string { return "asn1: structure error: " + e.Msg }
func (e StructuralError) Error() string {
var prefix string
if e.Field != "" {
prefix = e.Field + ": "
}
return "asn1: structure error: " + prefix + e.Msg
}
// A SyntaxError suggests that the ASN.1 data is invalid.
type SyntaxError struct {
Msg string
Field string
}
func (e SyntaxError) Error() string { return "asn1: syntax error: " + e.Msg }
func (e SyntaxError) Error() string {
var prefix string
if e.Field != "" {
prefix = e.Field + ": "
}
return "asn1: syntax error: " + prefix + e.Msg
}
// We start by dealing with each of the primitive types in turn.
// BOOLEAN
func parseBool(bytes []byte) (ret bool, err error) {
func parseBool(bytes []byte, fieldName string) (ret bool, err error) {
if len(bytes) != 1 {
err = SyntaxError{"invalid boolean"}
err = SyntaxError{"invalid boolean", fieldName}
return
}
@ -73,7 +84,7 @@ func parseBool(bytes []byte) (ret bool, err error) {
case 0xff:
ret = true
default:
err = SyntaxError{"invalid boolean"}
err = SyntaxError{"invalid boolean", fieldName}
}
return
@ -81,12 +92,31 @@ func parseBool(bytes []byte) (ret bool, err error) {
// INTEGER
// checkInteger returns nil if the given bytes are a valid DER-encoded
// INTEGER and an error otherwise.
func checkInteger(bytes []byte, fieldName string) error {
if len(bytes) == 0 {
return StructuralError{"empty integer", fieldName}
}
if len(bytes) == 1 {
return nil
}
if (bytes[0] == 0 && bytes[1]&0x80 == 0) || (bytes[0] == 0xff && bytes[1]&0x80 == 0x80) {
return StructuralError{"integer not minimally-encoded", fieldName}
}
return nil
}
// parseInt64 treats the given bytes as a big-endian, signed integer and
// returns the result.
func parseInt64(bytes []byte) (ret int64, err error) {
func parseInt64(bytes []byte, fieldName string) (ret int64, err error) {
err = checkInteger(bytes, fieldName)
if err != nil {
return
}
if len(bytes) > 8 {
// We'll overflow an int64 in this case.
err = StructuralError{"integer too large"}
err = StructuralError{"integer too large", fieldName}
return
}
for bytesRead := 0; bytesRead < len(bytes); bytesRead++ {
@ -102,13 +132,16 @@ func parseInt64(bytes []byte) (ret int64, err error) {
// parseInt treats the given bytes as a big-endian, signed integer and returns
// the result.
func parseInt32(bytes []byte) (int32, error) {
ret64, err := parseInt64(bytes)
func parseInt32(bytes []byte, fieldName string) (int32, error) {
if err := checkInteger(bytes, fieldName); err != nil {
return 0, err
}
ret64, err := parseInt64(bytes, fieldName)
if err != nil {
return 0, err
}
if ret64 != int64(int32(ret64)) {
return 0, StructuralError{"integer too large"}
return 0, StructuralError{"integer too large", fieldName}
}
return int32(ret64), nil
}
@ -117,7 +150,10 @@ var bigOne = big.NewInt(1)
// parseBigInt treats the given bytes as a big-endian, signed integer and returns
// the result.
func parseBigInt(bytes []byte) *big.Int {
func parseBigInt(bytes []byte, fieldName string) (*big.Int, error) {
if err := checkInteger(bytes, fieldName); err != nil {
return nil, err
}
ret := new(big.Int)
if len(bytes) > 0 && bytes[0]&0x80 == 0x80 {
// This is a negative number.
@ -128,10 +164,10 @@ func parseBigInt(bytes []byte) *big.Int {
ret.SetBytes(notBytes)
ret.Add(ret, bigOne)
ret.Neg(ret)
return ret
return ret, nil
}
ret.SetBytes(bytes)
return ret
return ret, nil
}
// BIT STRING
@ -174,16 +210,16 @@ func (b BitString) RightAlign() []byte {
}
// parseBitString parses an ASN.1 bit string from the given byte slice and returns it.
func parseBitString(bytes []byte) (ret BitString, err error) {
func parseBitString(bytes []byte, fieldName string) (ret BitString, err error) {
if len(bytes) == 0 {
err = SyntaxError{"zero length BIT STRING"}
err = SyntaxError{"zero length BIT STRING", fieldName}
return
}
paddingBits := int(bytes[0])
if paddingBits > 7 ||
len(bytes) == 1 && paddingBits > 0 ||
bytes[len(bytes)-1]&((1<<bytes[0])-1) != 0 {
err = SyntaxError{"invalid padding bits in BIT STRING"}
err = SyntaxError{"invalid padding bits in BIT STRING", fieldName}
return
}
ret.BitLength = (len(bytes)-1)*8 - paddingBits
@ -191,6 +227,14 @@ func parseBitString(bytes []byte) (ret BitString, err error) {
return
}
// NULL
// NullRawValue is a RawValue with its Tag set to the ASN.1 NULL type tag (5).
var NullRawValue = RawValue{Tag: TagNull}
// NullBytes contains bytes representing the DER-encoded ASN.1 NULL type.
var NullBytes = []byte{TagNull, 0}
// OBJECT IDENTIFIER
// An ObjectIdentifier represents an ASN.1 OBJECT IDENTIFIER.
@ -210,12 +254,25 @@ func (oi ObjectIdentifier) Equal(other ObjectIdentifier) bool {
return true
}
func (oi ObjectIdentifier) String() string {
var s string
for i, v := range oi {
if i > 0 {
s += "."
}
s += strconv.Itoa(v)
}
return s
}
// parseObjectIdentifier parses an OBJECT IDENTIFIER from the given bytes and
// returns it. An object identifier is a sequence of variable length integers
// that are assigned in a hierarchy.
func parseObjectIdentifier(bytes []byte) (s []int, err error) {
func parseObjectIdentifier(bytes []byte, fieldName string) (s []int, err error) {
if len(bytes) == 0 {
err = SyntaxError{"zero length OBJECT IDENTIFIER"}
err = SyntaxError{"zero length OBJECT IDENTIFIER", fieldName}
return
}
@ -227,7 +284,7 @@ func parseObjectIdentifier(bytes []byte) (s []int, err error) {
// According to this packing, value1 can take the values 0, 1 and 2 only.
// When value1 = 0 or value1 = 1, then value2 is <= 39. When value1 = 2,
// then there are no restrictions on value2.
v, offset, err := parseBase128Int(bytes, 0)
v, offset, err := parseBase128Int(bytes, 0, fieldName)
if err != nil {
return
}
@ -241,7 +298,7 @@ func parseObjectIdentifier(bytes []byte) (s []int, err error) {
i := 2
for ; offset < len(bytes); i++ {
v, offset, err = parseBase128Int(bytes, offset)
v, offset, err = parseBase128Int(bytes, offset, fieldName)
if err != nil {
return
}
@ -263,22 +320,30 @@ type Flag bool
// parseBase128Int parses a base-128 encoded int from the given offset in the
// given byte slice. It returns the value and the new offset.
func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err error) {
func parseBase128Int(bytes []byte, initOffset int, fieldName string) (ret, offset int, err error) {
offset = initOffset
var ret64 int64
for shifted := 0; offset < len(bytes); shifted++ {
if shifted > 4 {
err = StructuralError{"base 128 integer too large"}
// 5 * 7 bits per byte == 35 bits of data
// Thus the representation is either non-minimal or too large for an int32
if shifted == 5 {
err = StructuralError{"base 128 integer too large", fieldName}
return
}
ret <<= 7
ret64 <<= 7
b := bytes[offset]
ret |= int(b & 0x7f)
ret64 |= int64(b & 0x7f)
offset++
if b&0x80 == 0 {
ret = int(ret64)
// Ensure that the returned value fits in an int on all platforms
if ret64 > math.MaxInt32 {
err = StructuralError{"base 128 integer too large", fieldName}
}
return
}
}
err = SyntaxError{"truncated base 128 integer"}
err = SyntaxError{"truncated base 128 integer", fieldName}
return
}
@ -286,11 +351,23 @@ func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err error)
func parseUTCTime(bytes []byte) (ret time.Time, err error) {
s := string(bytes)
ret, err = time.Parse("0601021504Z0700", s)
formatStr := "0601021504Z0700"
ret, err = time.Parse(formatStr, s)
if err != nil {
ret, err = time.Parse("060102150405Z0700", s)
formatStr = "060102150405Z0700"
ret, err = time.Parse(formatStr, s)
}
if err == nil && ret.Year() >= 2050 {
if err != nil {
return
}
if serialized := ret.Format(formatStr); serialized != s {
err = fmt.Errorf("asn1: time did not serialize back to the original value and may be invalid: given %q, but serialized as %q", s, serialized)
return
}
if ret.Year() >= 2050 {
// UTCTime only encodes times prior to 2050. See https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1
ret = ret.AddDate(-100, 0, 0)
}
@ -301,17 +378,47 @@ func parseUTCTime(bytes []byte) (ret time.Time, err error) {
// parseGeneralizedTime parses the GeneralizedTime from the given byte slice
// and returns the resulting time.
func parseGeneralizedTime(bytes []byte) (ret time.Time, err error) {
return time.Parse("20060102150405Z0700", string(bytes))
const formatStr = "20060102150405Z0700"
s := string(bytes)
if ret, err = time.Parse(formatStr, s); err != nil {
return
}
if serialized := ret.Format(formatStr); serialized != s {
err = fmt.Errorf("asn1: time did not serialize back to the original value and may be invalid: given %q, but serialized as %q", s, serialized)
}
return
}
// NumericString
// parseNumericString parses an ASN.1 NumericString from the given byte array
// and returns it.
func parseNumericString(bytes []byte, fieldName string) (ret string, err error) {
for _, b := range bytes {
if !isNumeric(b) {
return "", SyntaxError{"NumericString contains invalid character", fieldName}
}
}
return string(bytes), nil
}
// isNumeric reports whether the given b is in the ASN.1 NumericString set.
func isNumeric(b byte) bool {
return '0' <= b && b <= '9' ||
b == ' '
}
// PrintableString
// parsePrintableString parses a ASN.1 PrintableString from the given byte
// parsePrintableString parses an ASN.1 PrintableString from the given byte
// array and returns it.
func parsePrintableString(bytes []byte) (ret string, err error) {
func parsePrintableString(bytes []byte, fieldName string) (ret string, err error) {
for _, b := range bytes {
if !isPrintable(b) {
err = SyntaxError{"PrintableString contains invalid character"}
if !isPrintable(b, allowAsterisk, allowAmpersand) {
err = SyntaxError{"PrintableString contains invalid character", fieldName}
return
}
}
@ -319,8 +426,21 @@ func parsePrintableString(bytes []byte) (ret string, err error) {
return
}
// isPrintable returns true iff the given b is in the ASN.1 PrintableString set.
func isPrintable(b byte) bool {
type asteriskFlag bool
type ampersandFlag bool
const (
allowAsterisk asteriskFlag = true
rejectAsterisk asteriskFlag = false
allowAmpersand ampersandFlag = true
rejectAmpersand ampersandFlag = false
)
// isPrintable reports whether the given b is in the ASN.1 PrintableString set.
// If asterisk is allowAsterisk then '*' is also allowed, reflecting existing
// practice. If ampersand is allowAmpersand then '&' is allowed as well.
func isPrintable(b byte, asterisk asteriskFlag, ampersand ampersandFlag) bool {
return 'a' <= b && b <= 'z' ||
'A' <= b && b <= 'Z' ||
'0' <= b && b <= '9' ||
@ -333,17 +453,22 @@ func isPrintable(b byte) bool {
// This is technically not allowed in a PrintableString.
// However, x509 certificates with wildcard strings don't
// always use the correct string type so we permit it.
b == '*'
(bool(asterisk) && b == '*') ||
// This is not technically allowed either. However, not
// only is it relatively common, but there are also a
// handful of CA certificates that contain it. At least
// one of which will not expire until 2027.
(bool(ampersand) && b == '&')
}
// IA5String
// parseIA5String parses a ASN.1 IA5String (ASCII string) from the given
// parseIA5String parses an ASN.1 IA5String (ASCII string) from the given
// byte slice and returns it.
func parseIA5String(bytes []byte) (ret string, err error) {
func parseIA5String(bytes []byte, fieldName string) (ret string, err error) {
for _, b := range bytes {
if b >= 0x80 {
err = SyntaxError{"IA5String contains invalid character"}
if b >= utf8.RuneSelf {
err = SyntaxError{"IA5String contains invalid character", fieldName}
return
}
}
@ -353,7 +478,7 @@ func parseIA5String(bytes []byte) (ret string, err error) {
// T61String
// parseT61String parses a ASN.1 T61String (8-bit clean string) from the given
// parseT61String parses an ASN.1 T61String (8-bit clean string) from the given
// byte slice and returns it.
func parseT61String(bytes []byte) (ret string, err error) {
return string(bytes), nil
@ -361,9 +486,12 @@ func parseT61String(bytes []byte) (ret string, err error) {
// UTF8String
// parseUTF8String parses a ASN.1 UTF8String (raw UTF-8) from the given byte
// parseUTF8String parses an ASN.1 UTF8String (raw UTF-8) from the given byte
// array and returns it.
func parseUTF8String(bytes []byte) (ret string, err error) {
if !utf8.Valid(bytes) {
return "", errors.New("asn1: invalid UTF-8 string")
}
return string(bytes), nil
}
@ -386,8 +514,14 @@ type RawContent []byte
// into a byte slice. It returns the parsed data and the new offset. SET and
// SET OF (tag 17) are mapped to SEQUENCE and SEQUENCE OF (tag 16) since we
// don't distinguish between ordered and unordered objects in this code.
func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset int, err error) {
func parseTagAndLength(bytes []byte, initOffset int, fieldName string) (ret tagAndLength, offset int, err error) {
offset = initOffset
// parseTagAndLength should not be called without at least a single
// byte to read. Thus this check is for robustness:
if offset >= len(bytes) {
err = errors.New("asn1: internal error in parseTagAndLength")
return
}
b := bytes[offset]
offset++
ret.class = int(b >> 6)
@ -397,13 +531,18 @@ func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset i
// If the bottom five bits are set, then the tag number is actually base 128
// encoded afterwards
if ret.tag == 0x1f {
ret.tag, offset, err = parseBase128Int(bytes, offset)
ret.tag, offset, err = parseBase128Int(bytes, offset, fieldName)
if err != nil {
return
}
// Tags should be encoded in minimal form.
if ret.tag < 0x1f {
err = SyntaxError{"non-minimal tag", fieldName}
return
}
}
if offset >= len(bytes) {
err = SyntaxError{"truncated tag or length"}
err = SyntaxError{"truncated tag or length", fieldName}
return
}
b = bytes[offset]
@ -415,13 +554,13 @@ func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset i
// Bottom 7 bits give the number of length bytes to follow.
numBytes := int(b & 0x7f)
if numBytes == 0 {
err = SyntaxError{"indefinite length found (not DER)"}
err = SyntaxError{"indefinite length found (not DER)", fieldName}
return
}
ret.length = 0
for i := 0; i < numBytes; i++ {
if offset >= len(bytes) {
err = SyntaxError{"truncated tag or length"}
err = SyntaxError{"truncated tag or length", fieldName}
return
}
b = bytes[offset]
@ -429,17 +568,22 @@ func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset i
if ret.length >= 1<<23 {
// We can't shift ret.length up without
// overflowing.
err = StructuralError{"length too large"}
err = StructuralError{"length too large", fieldName}
return
}
ret.length <<= 8
ret.length |= int(b)
if ret.length == 0 {
// DER requires that lengths be minimal.
err = StructuralError{"superfluous leading zeros in length"}
err = StructuralError{"superfluous leading zeros in length", fieldName}
return
}
}
// Short lengths must be encoded in short form.
if ret.length < 0x80 {
err = StructuralError{"non-minimal length", fieldName}
return
}
}
return
@ -448,10 +592,10 @@ func parseTagAndLength(bytes []byte, initOffset int) (ret tagAndLength, offset i
// parseSequenceOf is used for SEQUENCE OF and SET OF values. It tries to parse
// a number of ASN.1 values from the given byte slice and returns them as a
// slice of Go values of the given type.
func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type) (ret reflect.Value, err error) {
expectedTag, compoundType, ok := getUniversalType(elemType)
func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type, fieldName string) (ret reflect.Value, err error) {
matchAny, expectedTag, compoundType, ok := getUniversalType(elemType)
if !ok {
err = StructuralError{"unknown Go type for slice"}
err = StructuralError{"unknown Go type for slice", fieldName}
return
}
@ -460,21 +604,27 @@ func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type
numElements := 0
for offset := 0; offset < len(bytes); {
var t tagAndLength
t, offset, err = parseTagAndLength(bytes, offset)
t, offset, err = parseTagAndLength(bytes, offset, fieldName)
if err != nil {
return
}
// We pretend that GENERAL STRINGs are PRINTABLE STRINGs so
// that a sequence of them can be parsed into a []string.
if t.tag == tagGeneralString {
t.tag = tagPrintableString
switch t.tag {
case TagIA5String, TagGeneralString, TagT61String, TagUTF8String, TagNumericString:
// We pretend that various other string types are
// PRINTABLE STRINGs so that a sequence of them can be
// parsed into a []string.
t.tag = TagPrintableString
case TagGeneralizedTime, TagUTCTime:
// Likewise, both time types are treated the same.
t.tag = TagUTCTime
}
if t.class != classUniversal || t.isCompound != compoundType || t.tag != expectedTag {
err = StructuralError{"sequence tag mismatch"}
if !matchAny && (t.class != ClassUniversal || t.isCompound != compoundType || t.tag != expectedTag) {
err = StructuralError{fmt.Sprintf("sequence tag mismatch (got:%+v, want:0/%d/%t)", t, expectedTag, compoundType), fieldName}
return
}
if invalidLength(offset, t.length, len(bytes)) {
err = SyntaxError{"truncated sequence"}
err = SyntaxError{"truncated sequence", fieldName}
return
}
offset += t.length
@ -509,8 +659,6 @@ func invalidLength(offset, length, sliceLength int) bool {
return offset+length < offset || offset+length > sliceLength
}
// START CT CHANGES
// Tests whether the data in |bytes| would be a valid ISO8859-1 string.
// Clearly, a sequence of bytes comprised solely of valid ISO8859-1
// codepoints does not imply that the encoding MUST be ISO8859-1, rather that
@ -556,8 +704,6 @@ func iso8859_1ToUTF8(bytes []byte) string {
return string(buf)
}
// END CT CHANGES
// parseField is the main parsing function. Given a byte slice and an offset
// into the array, it will try to parse a suitable ASN.1 value out and store it
// in the given Value.
@ -568,46 +714,28 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
// If we have run out of data, it may be that there are optional elements at the end.
if offset == len(bytes) {
if !setDefaultValue(v, params) {
err = SyntaxError{"sequence truncated"}
err = SyntaxError{"sequence truncated", params.name}
}
return
}
// Deal with raw values.
if fieldType == rawValueType {
var t tagAndLength
t, offset, err = parseTagAndLength(bytes, offset)
if err != nil {
return
}
if invalidLength(offset, t.length, len(bytes)) {
err = SyntaxError{"data truncated"}
return
}
result := RawValue{t.class, t.tag, t.isCompound, bytes[offset : offset+t.length], bytes[initOffset : offset+t.length]}
offset += t.length
v.Set(reflect.ValueOf(result))
return
}
// Deal with the ANY type.
if ifaceType := fieldType; ifaceType.Kind() == reflect.Interface && ifaceType.NumMethod() == 0 {
var t tagAndLength
t, offset, err = parseTagAndLength(bytes, offset)
t, offset, err = parseTagAndLength(bytes, offset, params.name)
if err != nil {
return
}
if invalidLength(offset, t.length, len(bytes)) {
err = SyntaxError{"data truncated"}
err = SyntaxError{"data truncated", params.name}
return
}
var result interface{}
if !t.isCompound && t.class == classUniversal {
if !t.isCompound && t.class == ClassUniversal {
innerBytes := bytes[offset : offset+t.length]
switch t.tag {
case tagPrintableString:
result, err = parsePrintableString(innerBytes)
// START CT CHANGES
case TagPrintableString:
result, err = parsePrintableString(innerBytes, params.name)
if err != nil && strings.Contains(err.Error(), "PrintableString contains invalid character") {
// Probably an ISO8859-1 string stuffed in, check if it
// would be valid and assume that's what's happened if so,
@ -623,22 +751,25 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
err = errors.New("PrintableString contains invalid character, but couldn't determine correct String type.")
}
}
// END CT CHANGES
case tagIA5String:
result, err = parseIA5String(innerBytes)
case tagT61String:
case TagNumericString:
result, err = parseNumericString(innerBytes, params.name)
case TagIA5String:
result, err = parseIA5String(innerBytes, params.name)
case TagT61String:
result, err = parseT61String(innerBytes)
case tagUTF8String:
case TagUTF8String:
result, err = parseUTF8String(innerBytes)
case tagInteger:
result, err = parseInt64(innerBytes)
case tagBitString:
result, err = parseBitString(innerBytes)
case tagOID:
result, err = parseObjectIdentifier(innerBytes)
case tagUTCTime:
case TagInteger:
result, err = parseInt64(innerBytes, params.name)
case TagBitString:
result, err = parseBitString(innerBytes, params.name)
case TagOID:
result, err = parseObjectIdentifier(innerBytes, params.name)
case TagUTCTime:
result, err = parseUTCTime(innerBytes)
case tagOctetString:
case TagGeneralizedTime:
result, err = parseGeneralizedTime(innerBytes)
case TagOctetString:
result = innerBytes
default:
// If we don't know how to handle the type, we just leave Value as nil.
@ -653,30 +784,31 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
}
return
}
universalTag, compoundType, ok1 := getUniversalType(fieldType)
if !ok1 {
err = StructuralError{fmt.Sprintf("unknown Go type: %v", fieldType)}
return
}
t, offset, err := parseTagAndLength(bytes, offset)
t, offset, err := parseTagAndLength(bytes, offset, params.name)
if err != nil {
return
}
if params.explicit {
expectedClass := classContextSpecific
expectedClass := ClassContextSpecific
if params.application {
expectedClass = classApplication
expectedClass = ClassApplication
}
if offset == len(bytes) {
err = StructuralError{"explicit tag has no child", params.name}
return
}
if t.class == expectedClass && t.tag == *params.tag && (t.length == 0 || t.isCompound) {
if t.length > 0 {
t, offset, err = parseTagAndLength(bytes, offset)
if fieldType == rawValueType {
// The inner element should not be parsed for RawValues.
} else if t.length > 0 {
t, offset, err = parseTagAndLength(bytes, offset, params.name)
if err != nil {
return
}
} else {
if fieldType != flagType {
err = StructuralError{"zero length explicit tag was not an asn1.Flag"}
err = StructuralError{"zero length explicit tag was not an asn1.Flag", params.name}
return
}
v.SetBool(true)
@ -688,55 +820,73 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
if ok {
offset = initOffset
} else {
err = StructuralError{"explicitly tagged member didn't match"}
err = StructuralError{"explicitly tagged member didn't match", params.name}
}
return
}
}
matchAny, universalTag, compoundType, ok1 := getUniversalType(fieldType)
if !ok1 {
err = StructuralError{fmt.Sprintf("unknown Go type: %v", fieldType), params.name}
return
}
// Special case for strings: all the ASN.1 string types map to the Go
// type string. getUniversalType returns the tag for PrintableString
// when it sees a string, so if we see a different string type on the
// wire, we change the universal type to match.
if universalTag == tagPrintableString {
if universalTag == TagPrintableString {
if t.class == ClassUniversal {
switch t.tag {
case tagIA5String, tagGeneralString, tagT61String, tagUTF8String:
case TagIA5String, TagGeneralString, TagT61String, TagUTF8String, TagNumericString:
universalTag = t.tag
}
} else if params.stringType != 0 {
universalTag = params.stringType
}
}
// Special case for time: UTCTime and GeneralizedTime both map to the
// Go type time.Time.
if universalTag == tagUTCTime && t.tag == tagGeneralizedTime {
universalTag = tagGeneralizedTime
if universalTag == TagUTCTime && t.tag == TagGeneralizedTime && t.class == ClassUniversal {
universalTag = TagGeneralizedTime
}
expectedClass := classUniversal
if params.set {
universalTag = TagSet
}
matchAnyClassAndTag := matchAny
expectedClass := ClassUniversal
expectedTag := universalTag
if !params.explicit && params.tag != nil {
expectedClass = classContextSpecific
expectedClass = ClassContextSpecific
expectedTag = *params.tag
matchAnyClassAndTag = false
}
if !params.explicit && params.application && params.tag != nil {
expectedClass = classApplication
expectedClass = ClassApplication
expectedTag = *params.tag
matchAnyClassAndTag = false
}
// We have unwrapped any explicit tagging at this point.
if t.class != expectedClass || t.tag != expectedTag || t.isCompound != compoundType {
if !matchAnyClassAndTag && (t.class != expectedClass || t.tag != expectedTag) ||
(!matchAny && t.isCompound != compoundType) {
// Tags don't match. Again, it could be an optional element.
ok := setDefaultValue(v, params)
if ok {
offset = initOffset
} else {
err = StructuralError{fmt.Sprintf("tags don't match (%d vs %+v) %+v %s @%d", expectedTag, t, params, fieldType.Name(), offset)}
err = StructuralError{fmt.Sprintf("tags don't match (%d vs %+v) %+v %s @%d", expectedTag, t, params, fieldType.Name(), offset), params.name}
}
return
}
if invalidLength(offset, t.length, len(bytes)) {
err = SyntaxError{"data truncated"}
err = SyntaxError{"data truncated", params.name}
return
}
innerBytes := bytes[offset : offset+t.length]
@ -744,8 +894,12 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
// We deal with the structures defined in this package first.
switch fieldType {
case rawValueType:
result := RawValue{t.class, t.tag, t.isCompound, innerBytes, bytes[initOffset:offset]}
v.Set(reflect.ValueOf(result))
return
case objectIdentifierType:
newSlice, err1 := parseObjectIdentifier(innerBytes)
newSlice, err1 := parseObjectIdentifier(innerBytes, params.name)
v.Set(reflect.MakeSlice(v.Type(), len(newSlice), len(newSlice)))
if err1 == nil {
reflect.Copy(v, reflect.ValueOf(newSlice))
@ -753,7 +907,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
err = err1
return
case bitStringType:
bs, err1 := parseBitString(innerBytes)
bs, err1 := parseBitString(innerBytes, params.name)
if err1 == nil {
v.Set(reflect.ValueOf(bs))
}
@ -762,7 +916,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
case timeType:
var time time.Time
var err1 error
if universalTag == tagUTCTime {
if universalTag == TagUTCTime {
time, err1 = parseUTCTime(innerBytes)
} else {
time, err1 = parseGeneralizedTime(innerBytes)
@ -773,7 +927,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
err = err1
return
case enumeratedType:
parsedInt, err1 := parseInt32(innerBytes)
parsedInt, err1 := parseInt32(innerBytes, params.name)
if err1 == nil {
v.SetInt(int64(parsedInt))
}
@ -783,13 +937,16 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
v.SetBool(true)
return
case bigIntType:
parsedInt := parseBigInt(innerBytes)
parsedInt, err1 := parseBigInt(innerBytes, params.name)
if err1 == nil {
v.Set(reflect.ValueOf(parsedInt))
}
err = err1
return
}
switch val := v; val.Kind() {
case reflect.Bool:
parsedBool, err1 := parseBool(innerBytes)
parsedBool, err1 := parseBool(innerBytes, params.name)
if err1 == nil {
val.SetBool(parsedBool)
}
@ -797,13 +954,13 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
return
case reflect.Int, reflect.Int32, reflect.Int64:
if val.Type().Size() == 4 {
parsedInt, err1 := parseInt32(innerBytes)
parsedInt, err1 := parseInt32(innerBytes, params.name)
if err1 == nil {
val.SetInt(int64(parsedInt))
}
err = err1
} else {
parsedInt, err1 := parseInt64(innerBytes)
parsedInt, err1 := parseInt64(innerBytes, params.name)
if err1 == nil {
val.SetInt(parsedInt)
}
@ -814,6 +971,13 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
case reflect.Struct:
structType := fieldType
for i := 0; i < structType.NumField(); i++ {
if structType.Field(i).PkgPath != "" {
err = StructuralError{"struct contains unexported fields", structType.Field(i).Name}
return
}
}
if structType.NumField() > 0 &&
structType.Field(0).Type == rawContentsType {
bytes := bytes[initOffset:offset]
@ -826,7 +990,9 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
if i == 0 && field.Type == rawContentsType {
continue
}
innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, parseFieldParameters(field.Tag.Get("asn1")))
innerParams := parseFieldParameters(field.Tag.Get("asn1"))
innerParams.name = field.Name
innerOffset, err = parseField(val.Field(i), innerBytes, innerOffset, innerParams)
if err != nil {
return
}
@ -842,7 +1008,7 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
reflect.Copy(val, reflect.ValueOf(innerBytes))
return
}
newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem())
newSlice, err1 := parseSequenceOf(innerBytes, sliceType, sliceType.Elem(), params.name)
if err1 == nil {
val.Set(newSlice)
}
@ -851,34 +1017,47 @@ func parseField(v reflect.Value, bytes []byte, initOffset int, params fieldParam
case reflect.String:
var v string
switch universalTag {
case tagPrintableString:
v, err = parsePrintableString(innerBytes)
case tagIA5String:
v, err = parseIA5String(innerBytes)
case tagT61String:
case TagPrintableString:
v, err = parsePrintableString(innerBytes, params.name)
case TagNumericString:
v, err = parseNumericString(innerBytes, params.name)
case TagIA5String:
v, err = parseIA5String(innerBytes, params.name)
case TagT61String:
v, err = parseT61String(innerBytes)
case tagUTF8String:
case TagUTF8String:
v, err = parseUTF8String(innerBytes)
case tagGeneralString:
case TagGeneralString:
// GeneralString is specified in ISO-2022/ECMA-35,
// A brief review suggests that it includes structures
// that allow the encoding to change midstring and
// such. We give up and pass it as an 8-bit string.
v, err = parseT61String(innerBytes)
default:
err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag)}
err = SyntaxError{fmt.Sprintf("internal error: unknown string type %d", universalTag), params.name}
}
if err == nil {
val.SetString(v)
}
return
}
err = StructuralError{"unsupported: " + v.Type().String()}
err = StructuralError{"unsupported: " + v.Type().String(), params.name}
return
}
// canHaveDefaultValue reports whether k is a Kind that we will set a default
// value for. (A signed integer, essentially.)
func canHaveDefaultValue(k reflect.Kind) bool {
switch k {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return true
}
return false
}
// setDefaultValue is used to install a default value, from a tag string, into
// a Value. It is successful is the field was optional, even if a default value
// a Value. It is successful if the field was optional, even if a default value
// wasn't provided or it failed to install it into the Value.
func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
if !params.optional {
@ -888,9 +1067,8 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
if params.defaultValue == nil {
return
}
switch val := v; val.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
val.SetInt(*params.defaultValue)
if canHaveDefaultValue(v.Kind()) {
v.SetInt(*params.defaultValue)
}
return
}
@ -916,7 +1094,7 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
//
// An ASN.1 UTCTIME or GENERALIZEDTIME can be written to a time.Time.
//
// An ASN.1 PrintableString or IA5String can be written to a string.
// An ASN.1 PrintableString, IA5String, or NumericString can be written to a string.
//
// Any of the above ASN.1 values can be written to an interface{}.
// The value stored in the interface has the corresponding Go type.
@ -931,13 +1109,20 @@ func setDefaultValue(v reflect.Value, params fieldParameters) (ok bool) {
//
// The following tags on struct fields have special meaning to Unmarshal:
//
// application specifies that an APPLICATION tag is used
// default:x sets the default value for optional integer fields (only used if optional is also present)
// explicit specifies that an additional, explicit tag wraps the implicit one
// optional marks the field as ASN.1 OPTIONAL
// [explicit] tag:x specifies the ASN.1 tag number; implies ASN.1 CONTEXT SPECIFIC
// default:x sets the default value for optional integer fields
// set causes a SET, rather than a SEQUENCE type to be expected
// tag:x specifies the ASN.1 tag number; implies ASN.1 CONTEXT SPECIFIC
//
// If the type of the first field of a structure is RawContent then the raw
// ASN1 contents of the struct will be stored in it.
//
// If the type name of a slice element ends with "SET" then it's treated as if
// the "set" tag was set on it. This can be used with nested slices where a
// struct tag cannot be given.
//
// Other ASN.1 types are not supported; if it encounters them,
// Unmarshal returns a parse error.
func Unmarshal(b []byte, val interface{}) (rest []byte, err error) {

View File

@ -18,29 +18,33 @@ import (
// 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
tagOID = 6
tagEnum = 10
tagUTF8String = 12
tagSequence = 16
tagSet = 17
tagPrintableString = 19
tagT61String = 20
tagIA5String = 22
tagUTCTime = 23
tagGeneralizedTime = 24
tagGeneralString = 27
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
ClassUniversal = 0
ClassApplication = 1
ClassContextSpecific = 2
ClassPrivate = 3
)
type tagAndLength struct {
@ -74,8 +78,10 @@ type fieldParameters struct {
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.
@ -94,12 +100,18 @@ func parseFieldParameters(str string) (ret fieldParameters) {
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
ret.stringType = TagIA5String
case part == "printable":
ret.stringType = tagPrintableString
ret.stringType = TagPrintableString
case part == "numeric":
ret.stringType = TagNumericString
case part == "utf8":
ret.stringType = tagUTF8String
ret.stringType = TagUTF8String
case strings.HasPrefix(part, "default:"):
i, err := strconv.ParseInt(part[8:], 10, 64)
if err == nil {
@ -128,36 +140,38 @@ func parseFieldParameters(str string) (ret fieldParameters) {
// Given a reflected Go type, getUniversalType returns the default tag number
// and expected compound flag.
func getUniversalType(t reflect.Type) (tagNumber int, isCompound, ok bool) {
func getUniversalType(t reflect.Type) (matchAny bool, tagNumber int, isCompound, ok bool) {
switch t {
case rawValueType:
return true, -1, false, true
case objectIdentifierType:
return tagOID, false, true
return false, TagOID, false, true
case bitStringType:
return tagBitString, false, true
return false, TagBitString, false, true
case timeType:
return tagUTCTime, false, true
return false, TagUTCTime, false, true
case enumeratedType:
return tagEnum, false, true
return false, TagEnum, false, true
case bigIntType:
return tagInteger, false, true
return false, TagInteger, false, true
}
switch t.Kind() {
case reflect.Bool:
return tagBoolean, false, true
return false, TagBoolean, false, true
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return tagInteger, false, true
return false, TagInteger, false, true
case reflect.Struct:
return tagSequence, true, true
return false, TagSequence, true, true
case reflect.Slice:
if t.Elem().Kind() == reflect.Uint8 {
return tagOctetString, false, true
return false, TagOctetString, false, true
}
if strings.HasSuffix(t.Name(), "SET") {
return tagSet, true, true
return false, TagSet, true, true
}
return tagSequence, true, true
return false, TagSequence, true, true
case reflect.String:
return tagPrintableString, false, true
return false, TagPrintableString, false, true
}
return 0, false, false
return false, 0, false, false
}

View 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
}

View File

@ -0,0 +1,38 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"getentries.go",
"logclient.go",
"multilog.go",
],
importpath = "github.com/google/certificate-transparency-go/client",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/golang/protobuf/proto:go_default_library",
"//vendor/github.com/golang/protobuf/ptypes:go_default_library",
"//vendor/github.com/google/certificate-transparency-go:go_default_library",
"//vendor/github.com/google/certificate-transparency-go/client/configpb:go_default_library",
"//vendor/github.com/google/certificate-transparency-go/jsonclient:go_default_library",
"//vendor/github.com/google/certificate-transparency-go/tls:go_default_library",
"//vendor/github.com/google/certificate-transparency-go/x509:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//vendor/github.com/google/certificate-transparency-go/client/configpb:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,35 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
filegroup(
name = "go_default_library_protos",
srcs = ["multilog.proto"],
visibility = ["//visibility:public"],
)
go_library(
name = "go_default_library",
srcs = [
"gen.go",
"multilog.pb.go",
],
importpath = "github.com/google/certificate-transparency-go/client/configpb",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/golang/protobuf/proto:go_default_library",
"//vendor/github.com/golang/protobuf/ptypes/timestamp:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View 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

View 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,
}

View 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;
}

View 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
}

View 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
}

View 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/golang/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
}

View File

@ -0,0 +1,27 @@
{
"Linters": {
"license": "./scripts/check_license.sh:PATH:LINE:MESSAGE",
"forked": "./scripts/check_forked.sh:PATH:LINE:MESSAGE",
"unforked": "./scripts/check_unforked.sh:PATH:LINE:MESSAGE"
},
"Enable": [
"forked",
"gocyclo",
"gofmt",
"goimports",
"golint",
"license",
"misspell",
"unforked",
"vet"
],
"Exclude": [
"x509/",
"asn1/",
".+\\.pb\\.go",
".+\\.pb\\.gw\\.go",
"mock_.+\\.go"
],
"Cyclo": 40,
"Vendor": true
}

View File

@ -2,13 +2,16 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["logclient.go"],
importpath = "github.com/google/certificate-transparency/go/client",
srcs = [
"backoff.go",
"client.go",
],
importpath = "github.com/google/certificate-transparency-go/jsonclient",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/google/certificate-transparency/go:go_default_library",
"//vendor/github.com/mreiferson/go-httpclient:go_default_library",
"//vendor/golang.org/x/net/context:go_default_library",
"//vendor/github.com/google/certificate-transparency-go:go_default_library",
"//vendor/github.com/google/certificate-transparency-go/x509:go_default_library",
"//vendor/golang.org/x/net/context/ctxhttp:go_default_library",
],
)

View 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
}

View 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
}
}
}

View 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
}

View File

@ -1,3 +1,17 @@
// 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 (
@ -6,14 +20,13 @@ import (
"crypto/elliptic"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/asn1"
"encoding/pem"
"errors"
"flag"
"fmt"
"log"
"math/big"
"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,
@ -64,61 +77,18 @@ func NewSignatureVerifier(pk crypto.PublicKey) (*SignatureVerifier, error) {
}, nil
}
// verifySignature verifies that the passed in signature over data was created by our PublicKey.
// Currently, only SHA256 is supported as a HashAlgorithm, and only ECDSA and RSA signatures are supported.
func (s SignatureVerifier) verifySignature(data []byte, sig DigitallySigned) error {
if sig.HashAlgorithm != SHA256 {
return fmt.Errorf("unsupported HashAlgorithm in signature: %v", sig.HashAlgorithm)
}
hasherType := crypto.SHA256
hasher := hasherType.New()
if _, err := hasher.Write(data); err != nil {
return fmt.Errorf("failed to write to hasher: %v", err)
}
hash := hasher.Sum([]byte{})
switch sig.SignatureAlgorithm {
case RSA:
rsaKey, ok := s.pubKey.(*rsa.PublicKey)
if !ok {
return fmt.Errorf("cannot verify RSA signature with %T key", s.pubKey)
}
if err := rsa.VerifyPKCS1v15(rsaKey, hasherType, hash, sig.Signature); err != nil {
return fmt.Errorf("failed to verify rsa signature: %v", err)
}
case ECDSA:
ecdsaKey, ok := s.pubKey.(*ecdsa.PublicKey)
if !ok {
return fmt.Errorf("cannot verify ECDSA signature with %T key", s.pubKey)
}
var ecdsaSig struct {
R, S *big.Int
}
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 !ecdsa.Verify(ecdsaKey, hash, ecdsaSig.R, ecdsaSig.S) {
return errors.New("failed to verify ecdsa signature")
}
default:
return fmt.Errorf("unsupported signature type %v", sig.SignatureAlgorithm)
}
return 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
// 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, sct.Signature)
return s.VerifySignature(sctData, tls.DigitallySigned(sct.Signature))
}
// VerifySTHSignature verifies that the STH's signature is valid.
@ -127,5 +97,5 @@ func (s SignatureVerifier) VerifySTHSignature(sth SignedTreeHead) error {
if err != nil {
return err
}
return s.verifySignature(sthData, sth.TreeHeadSignature)
return s.VerifySignature(sthData, tls.DigitallySigned(sth.TreeHeadSignature))
}

View File

@ -0,0 +1,27 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"signature.go",
"tls.go",
"types.go",
],
importpath = "github.com/google/certificate-transparency-go/tls",
visibility = ["//visibility:public"],
deps = ["//vendor/github.com/google/certificate-transparency-go/asn1:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View 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)
}
}

View 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())}
}
}

View 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)
}
}

View 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
}

View File

@ -0,0 +1,122 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"cert_pool.go",
"error.go",
"errors.go",
"names.go",
"pem_decrypt.go",
"pkcs1.go",
"pkcs8.go",
"revoked.go",
"root.go",
"sec1.go",
"verify.go",
"x509.go",
] + select({
"@io_bazel_rules_go//go/platform:darwin": [
"root_darwin.go",
"root_nocgo_darwin.go",
],
"@io_bazel_rules_go//go/platform:dragonfly": [
"root_bsd.go",
"root_unix.go",
],
"@io_bazel_rules_go//go/platform:freebsd": [
"root_bsd.go",
"root_unix.go",
],
"@io_bazel_rules_go//go/platform:linux": [
"root_linux.go",
"root_unix.go",
],
"@io_bazel_rules_go//go/platform:nacl": [
"root_nacl.go",
"root_unix.go",
],
"@io_bazel_rules_go//go/platform:netbsd": [
"root_bsd.go",
"root_unix.go",
],
"@io_bazel_rules_go//go/platform:openbsd": [
"root_bsd.go",
"root_unix.go",
],
"@io_bazel_rules_go//go/platform:plan9": [
"root_plan9.go",
],
"@io_bazel_rules_go//go/platform:solaris": [
"root_solaris.go",
"root_unix.go",
],
"@io_bazel_rules_go//go/platform:windows": [
"root_windows.go",
],
"//conditions:default": [],
}) + select({
"@io_bazel_rules_go//go/platform:darwin_386": [
"nilref_nil_darwin.go",
"nilref_zero_darwin.go",
"root_cgo_darwin.go",
],
"@io_bazel_rules_go//go/platform:darwin_amd64": [
"nilref_nil_darwin.go",
"nilref_zero_darwin.go",
"root_cgo_darwin.go",
],
"@io_bazel_rules_go//go/platform:darwin_arm": [
"root_darwin_armx.go",
],
"@io_bazel_rules_go//go/platform:darwin_arm64": [
"root_darwin_armx.go",
],
"//conditions:default": [],
}),
cgo = True,
clinkopts = select({
"@io_bazel_rules_go//go/platform:darwin_386": [
"-framework CoreFoundation -framework Security",
],
"@io_bazel_rules_go//go/platform:darwin_amd64": [
"-framework CoreFoundation -framework Security",
],
"//conditions:default": [],
}),
copts = select({
"@io_bazel_rules_go//go/platform:darwin_386": [
"-mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1080",
],
"@io_bazel_rules_go//go/platform:darwin_amd64": [
"-mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1080",
],
"//conditions:default": [],
}),
importpath = "github.com/google/certificate-transparency-go/x509",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/google/certificate-transparency-go/asn1:go_default_library",
"//vendor/github.com/google/certificate-transparency-go/tls:go_default_library",
"//vendor/github.com/google/certificate-transparency-go/x509/pkix:go_default_library",
"//vendor/golang.org/x/crypto/cryptobyte:go_default_library",
"//vendor/golang.org/x/crypto/cryptobyte/asn1:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//vendor/github.com/google/certificate-transparency-go/x509/pkix:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -6,6 +6,8 @@ package x509
import (
"encoding/pem"
"errors"
"runtime"
)
// CertPool is a set of certificates.
@ -18,12 +20,24 @@ type CertPool struct {
// NewCertPool returns a new, empty CertPool.
func NewCertPool() *CertPool {
return &CertPool{
make(map[string][]int),
make(map[string][]int),
nil,
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
@ -52,6 +66,21 @@ func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int, errCer
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 {
@ -59,11 +88,9 @@ func (s *CertPool) AddCert(cert *Certificate) {
}
// Check that the certificate isn't being added twice.
for _, c := range s.certs {
if c.Equal(cert) {
if s.contains(cert) {
return
}
}
n := len(s.certs)
s.certs = append(s.certs, cert)
@ -77,7 +104,7 @@ func (s *CertPool) AddCert(cert *Certificate) {
}
// AppendCertsFromPEM attempts to parse a series of PEM encoded certificates.
// It appends any certificates found to s and returns true if any 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
@ -107,10 +134,10 @@ func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) {
// Subjects returns a list of the DER-encoded subjects of
// all of the certificates in the pool.
func (s *CertPool) Subjects() (res [][]byte) {
res = make([][]byte, len(s.certs))
func (s *CertPool) Subjects() [][]byte {
res := make([][]byte, len(s.certs))
for i, c := range s.certs {
res[i] = c.RawSubject
}
return
return res
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View File

@ -108,7 +108,10 @@ var IncorrectPasswordError = errors.New("x509: decryption password incorrect")
// 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.
// 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 {
@ -141,6 +144,10 @@ func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error) {
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)

View File

@ -6,11 +6,10 @@ package x509
import (
"crypto/rsa"
// START CT CHANGES
"github.com/google/certificate-transparency/go/asn1"
// END CT CHANGES
"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.
@ -37,16 +36,21 @@ type pkcs1AdditionalRSAPrime struct {
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) (key *rsa.PrivateKey, err error) {
func ParsePKCS1PrivateKey(der []byte) (*rsa.PrivateKey, error) {
var priv pkcs1PrivateKey
rest, err := asn1.Unmarshal(der, &priv)
if len(rest) > 0 {
err = asn1.SyntaxError{Msg: "trailing data"}
return
return nil, asn1.SyntaxError{Msg: "trailing data"}
}
if err != nil {
return
return nil, err
}
if priv.Version > 1 {
@ -57,7 +61,7 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error) {
return nil, errors.New("x509: private key contains zero or negative value")
}
key = new(rsa.PrivateKey)
key := new(rsa.PrivateKey)
key.PublicKey = rsa.PublicKey{
E: priv.E,
N: priv.N,
@ -82,7 +86,7 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error) {
}
key.Precompute()
return
return key, nil
}
// MarshalPKCS1PrivateKey converts a private key to ASN.1 DER encoded form.
@ -117,8 +121,35 @@ func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte {
return b
}
// rsaPublicKey reflects the ASN.1 structure of a PKCS#1 public key.
type rsaPublicKey struct {
N *big.Int
E int
// 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
}

View 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)
}

View File

@ -3,9 +3,9 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["pkix.go"],
importpath = "github.com/google/certificate-transparency/go/x509/pkix",
importpath = "github.com/google/certificate-transparency-go/x509/pkix",
visibility = ["//visibility:public"],
deps = ["//vendor/github.com/google/certificate-transparency/go/asn1:go_default_library"],
deps = ["//vendor/github.com/google/certificate-transparency-go/asn1:go_default_library"],
)
filegroup(

View 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"`
}

View 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())
}

View File

@ -9,9 +9,14 @@ import "sync"
var (
once sync.Once
systemRoots *CertPool
systemRootsErr error
)
func systemRootsPool() *CertPool {
once.Do(initSystemRoots)
return systemRoots
}
func initSystemRoots() {
systemRoots, systemRootsErr = loadSystemRoots()
}

View 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
}

View 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
}

View 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
}

View 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
}
`

File diff suppressed because it is too large Load Diff

View 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
}

View 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{}

View 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()
}

View File

@ -6,7 +6,10 @@
package x509
import "io/ioutil"
import (
"io/ioutil"
"os"
)
// Possible certificate files; stop after finding one.
var certFiles = []string{
@ -17,17 +20,18 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
return nil, nil
}
func initSystemRoots() {
func loadSystemRoots() (*CertPool, error) {
roots := NewCertPool()
var bestErr error
for _, file := range certFiles {
data, err := ioutil.ReadFile(file)
if err == nil {
roots.AppendCertsFromPEM(data)
systemRoots = roots
return
return roots, nil
}
if bestErr == nil || (os.IsNotExist(bestErr) && !os.IsNotExist(err)) {
bestErr = err
}
}
// All of the files failed to load. systemRoots will be nil which will
// trigger a specific error at verification time.
return nil, bestErr
}

View 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
}

View 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
}

View File

@ -87,7 +87,7 @@ func checkChainTrustStatus(c *Certificate, chainCtx *syscall.CertChainContext) e
status := chainCtx.TrustStatus.ErrorStatus
switch status {
case syscall.CERT_TRUST_IS_NOT_TIME_VALID:
return CertificateInvalidError{c, Expired}
return CertificateInvalidError{c, Expired, ""}
default:
return UnknownAuthorityError{c, nil, nil}
}
@ -125,7 +125,7 @@ func checkChainSSLServerPolicy(c *Certificate, chainCtx *syscall.CertChainContex
if status.Error != 0 {
switch status.Error {
case syscall.CERT_E_EXPIRED:
return CertificateInvalidError{c, Expired}
return CertificateInvalidError{c, Expired, ""}
case syscall.CERT_E_CN_NO_MATCH:
return HostnameError{c, opts.DNSName}
case syscall.CERT_E_UNTRUSTEDROOT:
@ -225,5 +225,42 @@ func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate
return chains, nil
}
func initSystemRoots() {
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
}

View File

@ -7,21 +7,20 @@ package x509
import (
"crypto/ecdsa"
"crypto/elliptic"
// START CT CHANGES
"github.com/google/certificate-transparency/go/asn1"
// START CT CHANGES
"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:
// RFC5915
// SEC1 - http://www.secg.org/download/aid-780/sec1-v2.pdf
// Per RFC5915 the NamedCurveOID is marked as ASN.1 OPTIONAL, however in
// 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
@ -31,19 +30,30 @@ type ecPrivateKey struct {
}
// ParseECPrivateKey parses an ASN.1 Elliptic Curve Private Key Structure.
func ParseECPrivateKey(der []byte) (key *ecdsa.PrivateKey, err error) {
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)
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: key.D.Bytes(),
PrivateKey: paddedPrivateKey,
NamedCurveOID: oid,
PublicKey: asn1.BitString{Bytes: elliptic.Marshal(key.Curve, key.X, key.Y)},
})
@ -73,13 +83,30 @@ func parseECPrivateKey(namedCurveOID *asn1.ObjectIdentifier, der []byte) (key *e
}
k := new(big.Int).SetBytes(privKey.PrivateKey)
if k.Cmp(curve.Params().N) >= 0 {
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
priv.X, priv.Y = curve.ScalarBaseMult(privKey.PrivateKey)
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
}

View 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-----

View 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-----

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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-----
`

View File

@ -1,32 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"serialization.go",
"signatures.go",
"types.go",
],
importpath = "github.com/google/certificate-transparency/go",
visibility = ["//visibility:public"],
deps = ["//vendor/github.com/google/certificate-transparency/go/x509:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//vendor/github.com/google/certificate-transparency/go/asn1:all-srcs",
"//vendor/github.com/google/certificate-transparency/go/client:all-srcs",
"//vendor/github.com/google/certificate-transparency/go/x509:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -1,25 +0,0 @@
This is the really early beginnings of a certificate transparency log
client written in Go, along with a log scanner tool.
You'll need go v1.1 or higher to compile.
# Installation
This go code must be imported into your go workspace before you can
use it, which can be done with:
go get github.com/google/certificate-transparency/go/client
go get github.com/google/certificate-transparency/go/scanner
etc.
# Building the binaries
To compile the log scanner run:
go build github.com/google/certificate-transparency/go/scanner/main/scanner.go
# Contributing
When sending pull requests, please ensure that everything's been run
through ```gofmt``` beforehand so we can keep everything nice and
tidy.

View File

@ -1,581 +0,0 @@
// 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"
"errors"
"fmt"
"io"
"math/big"
"reflect"
"time"
"unicode/utf8"
)
// A forkableWriter is an in-memory buffer that can be
// 'forked' to create new forkableWriters that bracket the
// original. After
// pre, post := w.fork();
// the overall sequence of bytes represented is logically w+pre+post.
type forkableWriter struct {
*bytes.Buffer
pre, post *forkableWriter
}
func newForkableWriter() *forkableWriter {
return &forkableWriter{new(bytes.Buffer), nil, nil}
}
func (f *forkableWriter) fork() (pre, post *forkableWriter) {
if f.pre != nil || f.post != nil {
panic("have already forked")
}
f.pre = newForkableWriter()
f.post = newForkableWriter()
return f.pre, f.post
}
func (f *forkableWriter) Len() (l int) {
l += f.Buffer.Len()
if f.pre != nil {
l += f.pre.Len()
}
if f.post != nil {
l += f.post.Len()
}
return
}
func (f *forkableWriter) writeTo(out io.Writer) (n int, err error) {
n, err = out.Write(f.Bytes())
if err != nil {
return
}
var nn int
if f.pre != nil {
nn, err = f.pre.writeTo(out)
n += nn
if err != nil {
return
}
}
if f.post != nil {
nn, err = f.post.writeTo(out)
n += nn
}
return
}
func marshalBase128Int(out *forkableWriter, n int64) (err error) {
if n == 0 {
err = out.WriteByte(0)
return
}
l := 0
for i := n; i > 0; i >>= 7 {
l++
}
for i := l - 1; i >= 0; i-- {
o := byte(n >> uint(i*7))
o &= 0x7f
if i != 0 {
o |= 0x80
}
err = out.WriteByte(o)
if err != nil {
return
}
}
return nil
}
func marshalInt64(out *forkableWriter, i int64) (err error) {
n := int64Length(i)
for ; n > 0; n-- {
err = out.WriteByte(byte(i >> uint((n-1)*8)))
if err != nil {
return
}
}
return nil
}
func int64Length(i int64) (numBytes int) {
numBytes = 1
for i > 127 {
numBytes++
i >>= 8
}
for i < -128 {
numBytes++
i >>= 8
}
return
}
func marshalBigInt(out *forkableWriter, n *big.Int) (err error) {
if n.Sign() < 0 {
// A negative number has to be converted to two's-complement
// form. So we'll subtract 1 and invert. 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 {
err = out.WriteByte(0xff)
if err != nil {
return
}
}
_, err = out.Write(bytes)
} else if n.Sign() == 0 {
// Zero is written as a single 0 zero rather than no bytes.
err = out.WriteByte(0x00)
} 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.
err = out.WriteByte(0)
if err != nil {
return
}
}
_, err = out.Write(bytes)
}
return
}
func marshalLength(out *forkableWriter, i int) (err error) {
n := lengthLength(i)
for ; n > 0; n-- {
err = out.WriteByte(byte(i >> uint((n-1)*8)))
if err != nil {
return
}
}
return nil
}
func lengthLength(i int) (numBytes int) {
numBytes = 1
for i > 255 {
numBytes++
i >>= 8
}
return
}
func marshalTagAndLength(out *forkableWriter, t tagAndLength) (err error) {
b := uint8(t.class) << 6
if t.isCompound {
b |= 0x20
}
if t.tag >= 31 {
b |= 0x1f
err = out.WriteByte(b)
if err != nil {
return
}
err = marshalBase128Int(out, int64(t.tag))
if err != nil {
return
}
} else {
b |= uint8(t.tag)
err = out.WriteByte(b)
if err != nil {
return
}
}
if t.length >= 128 {
l := lengthLength(t.length)
err = out.WriteByte(0x80 | byte(l))
if err != nil {
return
}
err = marshalLength(out, t.length)
if err != nil {
return
}
} else {
err = out.WriteByte(byte(t.length))
if err != nil {
return
}
}
return nil
}
func marshalBitString(out *forkableWriter, b BitString) (err error) {
paddingBits := byte((8 - b.BitLength%8) % 8)
err = out.WriteByte(paddingBits)
if err != nil {
return
}
_, err = out.Write(b.Bytes)
return
}
func marshalObjectIdentifier(out *forkableWriter, oid []int) (err error) {
if len(oid) < 2 || oid[0] > 2 || (oid[0] < 2 && oid[1] >= 40) {
return StructuralError{"invalid object identifier"}
}
err = marshalBase128Int(out, int64(oid[0]*40+oid[1]))
if err != nil {
return
}
for i := 2; i < len(oid); i++ {
err = marshalBase128Int(out, int64(oid[i]))
if err != nil {
return
}
}
return
}
func marshalPrintableString(out *forkableWriter, s string) (err error) {
b := []byte(s)
for _, c := range b {
if !isPrintable(c) {
return StructuralError{"PrintableString contains invalid character"}
}
}
_, err = out.Write(b)
return
}
func marshalIA5String(out *forkableWriter, s string) (err error) {
b := []byte(s)
for _, c := range b {
if c > 127 {
return StructuralError{"IA5String contains invalid character"}
}
}
_, err = out.Write(b)
return
}
func marshalUTF8String(out *forkableWriter, s string) (err error) {
_, err = out.Write([]byte(s))
return
}
func marshalTwoDigits(out *forkableWriter, v int) (err error) {
err = out.WriteByte(byte('0' + (v/10)%10))
if err != nil {
return
}
return out.WriteByte(byte('0' + v%10))
}
func marshalUTCTime(out *forkableWriter, t time.Time) (err error) {
year, month, day := t.Date()
switch {
case 1950 <= year && year < 2000:
err = marshalTwoDigits(out, int(year-1900))
case 2000 <= year && year < 2050:
err = marshalTwoDigits(out, int(year-2000))
default:
return StructuralError{"cannot represent time as UTCTime"}
}
if err != nil {
return
}
err = marshalTwoDigits(out, int(month))
if err != nil {
return
}
err = marshalTwoDigits(out, day)
if err != nil {
return
}
hour, min, sec := t.Clock()
err = marshalTwoDigits(out, hour)
if err != nil {
return
}
err = marshalTwoDigits(out, min)
if err != nil {
return
}
err = marshalTwoDigits(out, sec)
if err != nil {
return
}
_, offset := t.Zone()
switch {
case offset/60 == 0:
err = out.WriteByte('Z')
return
case offset > 0:
err = out.WriteByte('+')
case offset < 0:
err = out.WriteByte('-')
}
if err != nil {
return
}
offsetMinutes := offset / 60
if offsetMinutes < 0 {
offsetMinutes = -offsetMinutes
}
err = marshalTwoDigits(out, offsetMinutes/60)
if err != nil {
return
}
err = marshalTwoDigits(out, offsetMinutes%60)
return
}
func stripTagAndLength(in []byte) []byte {
_, offset, err := parseTagAndLength(in, 0)
if err != nil {
return in
}
return in[offset:]
}
func marshalBody(out *forkableWriter, value reflect.Value, params fieldParameters) (err error) {
switch value.Type() {
case timeType:
return marshalUTCTime(out, value.Interface().(time.Time))
case bitStringType:
return marshalBitString(out, value.Interface().(BitString))
case objectIdentifierType:
return marshalObjectIdentifier(out, value.Interface().(ObjectIdentifier))
case bigIntType:
return marshalBigInt(out, value.Interface().(*big.Int))
}
switch v := value; v.Kind() {
case reflect.Bool:
if v.Bool() {
return out.WriteByte(255)
} else {
return out.WriteByte(0)
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return marshalInt64(out, int64(v.Int()))
case reflect.Struct:
t := v.Type()
startingField := 0
// If the first element of the structure is a non-empty
// RawContents, then we don't bother serializing the rest.
if t.NumField() > 0 && t.Field(0).Type == rawContentsType {
s := v.Field(0)
if s.Len() > 0 {
bytes := make([]byte, s.Len())
for i := 0; i < s.Len(); i++ {
bytes[i] = uint8(s.Index(i).Uint())
}
/* The RawContents will contain the tag and
* length fields but we'll also be writing
* those ourselves, so we strip them out of
* bytes */
_, err = out.Write(stripTagAndLength(bytes))
return
} else {
startingField = 1
}
}
for i := startingField; i < t.NumField(); i++ {
var pre *forkableWriter
pre, out = out.fork()
err = marshalField(pre, v.Field(i), parseFieldParameters(t.Field(i).Tag.Get("asn1")))
if err != nil {
return
}
}
return
case reflect.Slice:
sliceType := v.Type()
if sliceType.Elem().Kind() == reflect.Uint8 {
bytes := make([]byte, v.Len())
for i := 0; i < v.Len(); i++ {
bytes[i] = uint8(v.Index(i).Uint())
}
_, err = out.Write(bytes)
return
}
var fp fieldParameters
for i := 0; i < v.Len(); i++ {
var pre *forkableWriter
pre, out = out.fork()
err = marshalField(pre, v.Index(i), fp)
if err != nil {
return
}
}
return
case reflect.String:
switch params.stringType {
case tagIA5String:
return marshalIA5String(out, v.String())
case tagPrintableString:
return marshalPrintableString(out, v.String())
default:
return marshalUTF8String(out, v.String())
}
}
return StructuralError{"unknown Go type"}
}
func marshalField(out *forkableWriter, v reflect.Value, params fieldParameters) (err error) {
// If the field is an interface{} then recurse into it.
if v.Kind() == reflect.Interface && v.Type().NumMethod() == 0 {
return marshalField(out, v.Elem(), params)
}
if v.Kind() == reflect.Slice && v.Len() == 0 && params.omitEmpty {
return
}
if params.optional && reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface()) {
return
}
if v.Type() == rawValueType {
rv := v.Interface().(RawValue)
if len(rv.FullBytes) != 0 {
_, err = out.Write(rv.FullBytes)
} else {
err = marshalTagAndLength(out, tagAndLength{rv.Class, rv.Tag, len(rv.Bytes), rv.IsCompound})
if err != nil {
return
}
_, err = out.Write(rv.Bytes)
}
return
}
tag, isCompound, ok := getUniversalType(v.Type())
if !ok {
err = StructuralError{fmt.Sprintf("unknown Go type: %v", v.Type())}
return
}
class := classUniversal
if params.stringType != 0 && tag != tagPrintableString {
return StructuralError{"explicit string type given to non-string member"}
}
if tag == 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)) {
if !utf8.ValidString(v.String()) {
return errors.New("asn1: string not valid UTF-8")
}
tag = tagUTF8String
break
}
}
} else {
tag = params.stringType
}
}
if params.set {
if tag != tagSequence {
return StructuralError{"non sequence tagged as set"}
}
tag = tagSet
}
tags, body := out.fork()
err = marshalBody(body, v, params)
if err != nil {
return
}
bodyLen := body.Len()
var explicitTag *forkableWriter
if params.explicit {
explicitTag, tags = tags.fork()
}
if !params.explicit && params.tag != nil {
// implicit tag.
tag = *params.tag
class = classContextSpecific
}
err = marshalTagAndLength(tags, tagAndLength{class, tag, bodyLen, isCompound})
if err != nil {
return
}
if params.explicit {
err = marshalTagAndLength(explicitTag, tagAndLength{
class: classContextSpecific,
tag: *params.tag,
length: bodyLen + tags.Len(),
isCompound: true,
})
}
return nil
}
// Marshal returns the ASN.1 encoding of val.
func Marshal(val interface{}) ([]byte, error) {
var out bytes.Buffer
v := reflect.ValueOf(val)
f := newForkableWriter()
err := marshalField(f, v, fieldParameters{})
if err != nil {
return nil, err
}
_, err = f.writeTo(&out)
return out.Bytes(), nil
}

View File

@ -1,358 +0,0 @@
// 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 (
"bytes"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"log"
"net/http"
"strconv"
"time"
"github.com/google/certificate-transparency/go"
"github.com/mreiferson/go-httpclient"
"golang.org/x/net/context"
)
// URI paths for CT Log endpoints
const (
AddChainPath = "/ct/v1/add-chain"
AddPreChainPath = "/ct/v1/add-pre-chain"
GetSTHPath = "/ct/v1/get-sth"
GetEntriesPath = "/ct/v1/get-entries"
)
// LogClient represents a client for a given CT Log instance
type LogClient struct {
uri string // the base URI of the log. e.g. http://ct.googleapis/pilot
httpClient *http.Client // used to interact with the log via HTTP
}
//////////////////////////////////////////////////////////////////////////////////
// JSON structures follow.
// These represent the structures returned by the CT Log server.
//////////////////////////////////////////////////////////////////////////////////
// addChainRequest represents the JSON request body sent to the add-chain CT
// method.
type addChainRequest struct {
Chain []string `json:"chain"`
}
// addChainResponse represents the JSON response to the add-chain CT method.
// 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 ct.Version `json:"sct_version"` // SCT structure version
ID string `json:"id"` // Log ID
Timestamp uint64 `json:"timestamp"` // Timestamp of issuance
Extensions string `json:"extensions"` // Holder for any CT extensions
Signature string `json:"signature"` // Log signature for this SCT
}
// getSTHResponse respresents the JSON response to the get-sth CT method
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 string `json:"sha256_root_hash"` // Root hash of the tree
TreeHeadSignature string `json:"tree_head_signature"` // Log signature for this STH
}
// base64LeafEntry respresents a Base64 encoded leaf entry
type base64LeafEntry struct {
LeafInput string `json:"leaf_input"`
ExtraData string `json:"extra_data"`
}
// getEntriesReponse respresents the JSON response to the CT get-entries method
type getEntriesResponse struct {
Entries []base64LeafEntry `json:"entries"` // the list of returned entries
}
// getConsistencyProofResponse represents the JSON response to the CT get-consistency-proof method
type getConsistencyProofResponse struct {
Consistency []string `json:"consistency"`
}
// getAuditProofResponse represents the JSON response to the CT get-audit-proof method
type getAuditProofResponse struct {
Hash []string `json:"hash"` // the hashes which make up the proof
TreeSize uint64 `json:"tree_size"` // the tree size against which this proof is constructed
}
// getAcceptedRootsResponse represents the JSON response to the CT get-roots method.
type getAcceptedRootsResponse struct {
Certificates []string `json:"certificates"`
}
// getEntryAndProodReponse represents the JSON response to the CT get-entry-and-proof method
type getEntryAndProofResponse struct {
LeafInput string `json:"leaf_input"` // the entry itself
ExtraData string `json:"extra_data"` // any chain provided when the entry was added to the log
AuditPath []string `json:"audit_path"` // the corresponding proof
}
// 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
func New(uri string) *LogClient {
var c LogClient
c.uri = uri
transport := &httpclient.Transport{
ConnectTimeout: 10 * time.Second,
RequestTimeout: 30 * time.Second,
ResponseHeaderTimeout: 30 * time.Second,
MaxIdleConnsPerHost: 10,
DisableKeepAlives: false,
}
c.httpClient = &http.Client{Transport: transport}
return &c
}
// Makes a HTTP call to |uri|, and attempts to parse the response as a JSON
// representation of the structure in |res|.
// Returns a non-nil |error| if there was a problem.
func (c *LogClient) fetchAndParse(uri string, res interface{}) error {
req, err := http.NewRequest("GET", uri, nil)
if err != nil {
return err
}
req.Header.Set("Keep-Alive", "timeout=15, max=100")
resp, err := c.httpClient.Do(req)
var body []byte
if resp != nil {
body, err = ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return err
}
}
if err != nil {
return err
}
if err = json.Unmarshal(body, &res); err != nil {
return err
}
return nil
}
// Makes a HTTP POST call to |uri|, and attempts to parse the response as a JSON
// representation of the structure in |res|.
// Returns a non-nil |error| if there was a problem.
func (c *LogClient) postAndParse(uri string, req interface{}, res interface{}) (*http.Response, string, error) {
postBody, err := json.Marshal(req)
if err != nil {
return nil, "", err
}
httpReq, err := http.NewRequest("POST", uri, bytes.NewReader(postBody))
if err != nil {
return nil, "", err
}
httpReq.Header.Set("Keep-Alive", "timeout=15, max=100")
httpReq.Header.Set("Content-Type", "application/json")
resp, err := c.httpClient.Do(httpReq)
// Read all of the body, if there is one, so that the http.Client can do
// Keep-Alive:
var body []byte
if resp != nil {
body, err = ioutil.ReadAll(resp.Body)
resp.Body.Close()
}
if err != nil {
return resp, string(body), err
}
if resp.StatusCode == 200 {
if err != nil {
return resp, string(body), err
}
if err = json.Unmarshal(body, &res); err != nil {
return resp, string(body), err
}
}
return resp, string(body), nil
}
func backoffForRetry(ctx context.Context, d time.Duration) error {
backoffTimer := time.NewTimer(d)
if ctx != nil {
select {
case <-ctx.Done():
return ctx.Err()
case <-backoffTimer.C:
}
} else {
<-backoffTimer.C
}
return nil
}
// 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, path string, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) {
var resp addChainResponse
var req addChainRequest
for _, link := range chain {
req.Chain = append(req.Chain, base64.StdEncoding.EncodeToString(link))
}
httpStatus := "Unknown"
backoffSeconds := 0
done := false
for !done {
if backoffSeconds > 0 {
log.Printf("Got %s, backing-off %d seconds", httpStatus, backoffSeconds)
}
err := backoffForRetry(ctx, time.Second*time.Duration(backoffSeconds))
if err != nil {
return nil, err
}
if backoffSeconds > 0 {
backoffSeconds = 0
}
httpResp, errorBody, err := c.postAndParse(c.uri+path, &req, &resp)
if err != nil {
backoffSeconds = 10
continue
}
switch {
case httpResp.StatusCode == 200:
done = true
case httpResp.StatusCode == 408:
// request timeout, retry immediately
case httpResp.StatusCode == 503:
// Retry
backoffSeconds = 10
if retryAfter := httpResp.Header.Get("Retry-After"); retryAfter != "" {
if seconds, err := strconv.Atoi(retryAfter); err == nil {
backoffSeconds = seconds
}
}
default:
return nil, fmt.Errorf("got HTTP Status %s: %s", httpResp.Status, errorBody)
}
httpStatus = httpResp.Status
}
rawLogID, err := base64.StdEncoding.DecodeString(resp.ID)
if err != nil {
return nil, err
}
rawSignature, err := base64.StdEncoding.DecodeString(resp.Signature)
if err != nil {
return nil, err
}
ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(rawSignature))
if err != nil {
return nil, err
}
var logID ct.SHA256Hash
copy(logID[:], rawLogID)
return &ct.SignedCertificateTimestamp{
SCTVersion: resp.SCTVersion,
LogID: logID,
Timestamp: resp.Timestamp,
Extensions: ct.CTExtensions(resp.Extensions),
Signature: *ds}, nil
}
// AddChain adds the (DER represented) X509 |chain| to the log.
func (c *LogClient) AddChain(chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) {
return c.addChainWithRetry(nil, AddChainPath, chain)
}
// AddPreChain adds the (DER represented) Precertificate |chain| to the log.
func (c *LogClient) AddPreChain(chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) {
return c.addChainWithRetry(nil, AddPreChainPath, chain)
}
// AddChainWithContext adds the (DER represented) X509 |chain| to the log and
// fails if the provided context expires before the chain is submitted.
func (c *LogClient) AddChainWithContext(ctx context.Context, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) {
return c.addChainWithRetry(ctx, AddChainPath, chain)
}
// GetSTH retrieves the current STH from the log.
// Returns a populated SignedTreeHead, or a non-nil error.
func (c *LogClient) GetSTH() (sth *ct.SignedTreeHead, err error) {
var resp getSTHResponse
if err = c.fetchAndParse(c.uri+GetSTHPath, &resp); err != nil {
return
}
sth = &ct.SignedTreeHead{
TreeSize: resp.TreeSize,
Timestamp: resp.Timestamp,
}
rawRootHash, err := base64.StdEncoding.DecodeString(resp.SHA256RootHash)
if err != nil {
return nil, fmt.Errorf("invalid base64 encoding in sha256_root_hash: %v", err)
}
if len(rawRootHash) != sha256.Size {
return nil, fmt.Errorf("sha256_root_hash is invalid length, expected %d got %d", sha256.Size, len(rawRootHash))
}
copy(sth.SHA256RootHash[:], rawRootHash)
rawSignature, err := base64.StdEncoding.DecodeString(resp.TreeHeadSignature)
if err != nil {
return nil, errors.New("invalid base64 encoding in tree_head_signature")
}
ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(rawSignature))
if err != nil {
return nil, err
}
// TODO(alcutter): Verify signature
sth.TreeHeadSignature = *ds
return
}
// GetEntries attempts to retrieve the entries in the sequence [|start|, |end|] from the CT
// log server. (see section 4.6.)
// Returns a slice of LeafInputs or a non-nil error.
func (c *LogClient) GetEntries(start, end int64) ([]ct.LogEntry, error) {
if end < 0 {
return nil, errors.New("end should be >= 0")
}
if end < start {
return nil, errors.New("start should be <= end")
}
var resp getEntriesResponse
err := c.fetchAndParse(fmt.Sprintf("%s%s?start=%d&end=%d", c.uri, GetEntriesPath, start, end), &resp)
if err != nil {
return nil, err
}
entries := make([]ct.LogEntry, len(resp.Entries))
for index, entry := range resp.Entries {
leafBytes, err := base64.StdEncoding.DecodeString(entry.LeafInput)
leaf, err := ct.ReadMerkleTreeLeaf(bytes.NewBuffer(leafBytes))
if err != nil {
return nil, err
}
entries[index].Leaf = *leaf
chainBytes, err := base64.StdEncoding.DecodeString(entry.ExtraData)
var chain []ct.ASN1Cert
switch leaf.TimestampedEntry.EntryType {
case ct.X509LogEntryType:
chain, err = ct.UnmarshalX509ChainArray(chainBytes)
case ct.PrecertLogEntryType:
chain, err = ct.UnmarshalPrecertChainArray(chainBytes)
default:
return nil, fmt.Errorf("saw unknown entry type: %v", leaf.TimestampedEntry.EntryType)
}
if err != nil {
return nil, err
}
entries[index].Chain = chain
entries[index].Index = start + int64(index)
}
return entries, nil
}

View File

@ -1,512 +0,0 @@
package ct
import (
"bytes"
"container/list"
"crypto"
"encoding/binary"
"errors"
"fmt"
"io"
)
// Variable size structure prefix-header byte lengths
const (
CertificateLengthBytes = 3
PreCertificateLengthBytes = 3
ExtensionsLengthBytes = 2
CertificateChainLengthBytes = 3
SignatureLengthBytes = 2
)
// Max lengths
const (
MaxCertificateLength = (1 << 24) - 1
MaxExtensionsLength = (1 << 16) - 1
)
func writeUint(w io.Writer, value uint64, numBytes int) error {
buf := make([]uint8, numBytes)
for i := 0; i < numBytes; i++ {
buf[numBytes-i-1] = uint8(value & 0xff)
value >>= 8
}
if value != 0 {
return errors.New("numBytes was insufficiently large to represent value")
}
if _, err := w.Write(buf); err != nil {
return err
}
return nil
}
func writeVarBytes(w io.Writer, value []byte, numLenBytes int) error {
if err := writeUint(w, uint64(len(value)), numLenBytes); err != nil {
return err
}
if _, err := w.Write(value); err != nil {
return err
}
return nil
}
func readUint(r io.Reader, numBytes int) (uint64, error) {
var l uint64
for i := 0; i < numBytes; i++ {
l <<= 8
var t uint8
if err := binary.Read(r, binary.BigEndian, &t); err != nil {
return 0, err
}
l |= uint64(t)
}
return l, nil
}
// Reads a variable length array of bytes from |r|. |numLenBytes| specifies the
// number of (BigEndian) prefix-bytes which contain the length of the actual
// array data bytes that follow.
// Allocates an array to hold the contents and returns a slice view into it if
// the read was successful, or an error otherwise.
func readVarBytes(r io.Reader, numLenBytes int) ([]byte, error) {
switch {
case numLenBytes > 8:
return nil, fmt.Errorf("numLenBytes too large (%d)", numLenBytes)
case numLenBytes == 0:
return nil, errors.New("numLenBytes should be > 0")
}
l, err := readUint(r, numLenBytes)
if err != nil {
return nil, err
}
data := make([]byte, l)
n, err := r.Read(data)
if err != nil {
return nil, err
}
if n != int(l) {
return nil, fmt.Errorf("short read: expected %d but got %d", l, n)
}
return data, nil
}
// Reads a list of ASN1Cert types from |r|
func readASN1CertList(r io.Reader, totalLenBytes int, elementLenBytes int) ([]ASN1Cert, error) {
listBytes, err := readVarBytes(r, totalLenBytes)
if err != nil {
return []ASN1Cert{}, err
}
list := list.New()
listReader := bytes.NewReader(listBytes)
var entry []byte
for err == nil {
entry, err = readVarBytes(listReader, elementLenBytes)
if err != nil {
if err != io.EOF {
return []ASN1Cert{}, err
}
} else {
list.PushBack(entry)
}
}
ret := make([]ASN1Cert, list.Len())
i := 0
for e := list.Front(); e != nil; e = e.Next() {
ret[i] = e.Value.([]byte)
i++
}
return ret, nil
}
// ReadTimestampedEntryInto parses the byte-stream representation of a
// TimestampedEntry from |r| and populates the struct |t| with the data. See
// RFC section 3.4 for details on the format.
// Returns a non-nil error if there was a problem.
func ReadTimestampedEntryInto(r io.Reader, t *TimestampedEntry) error {
var err error
if err = binary.Read(r, binary.BigEndian, &t.Timestamp); err != nil {
return err
}
if err = binary.Read(r, binary.BigEndian, &t.EntryType); err != nil {
return err
}
switch t.EntryType {
case X509LogEntryType:
if t.X509Entry, err = readVarBytes(r, CertificateLengthBytes); err != nil {
return err
}
case PrecertLogEntryType:
if err := binary.Read(r, binary.BigEndian, &t.PrecertEntry.IssuerKeyHash); err != nil {
return err
}
if t.PrecertEntry.TBSCertificate, err = readVarBytes(r, PreCertificateLengthBytes); err != nil {
return err
}
default:
return fmt.Errorf("unknown EntryType: %d", t.EntryType)
}
t.Extensions, err = readVarBytes(r, ExtensionsLengthBytes)
return nil
}
// ReadMerkleTreeLeaf parses the byte-stream representation of a MerkleTreeLeaf
// and returns a pointer to a new MerkleTreeLeaf structure containing the
// parsed data.
// See RFC section 3.4 for details on the format.
// Returns a pointer to a new MerkleTreeLeaf or non-nil error if there was a
// problem
func ReadMerkleTreeLeaf(r io.Reader) (*MerkleTreeLeaf, error) {
var m MerkleTreeLeaf
if err := binary.Read(r, binary.BigEndian, &m.Version); err != nil {
return nil, err
}
if m.Version != V1 {
return nil, fmt.Errorf("unknown Version %d", m.Version)
}
if err := binary.Read(r, binary.BigEndian, &m.LeafType); err != nil {
return nil, err
}
if m.LeafType != TimestampedEntryLeafType {
return nil, fmt.Errorf("unknown LeafType %d", m.LeafType)
}
if err := ReadTimestampedEntryInto(r, &m.TimestampedEntry); err != nil {
return nil, err
}
return &m, nil
}
// UnmarshalX509ChainArray unmarshalls the contents of the "chain:" entry in a
// GetEntries response in the case where the entry refers to an X509 leaf.
func UnmarshalX509ChainArray(b []byte) ([]ASN1Cert, error) {
return readASN1CertList(bytes.NewReader(b), CertificateChainLengthBytes, CertificateLengthBytes)
}
// UnmarshalPrecertChainArray unmarshalls the contents of the "chain:" entry in
// a GetEntries response in the case where the entry refers to a Precertificate
// leaf.
func UnmarshalPrecertChainArray(b []byte) ([]ASN1Cert, error) {
var chain []ASN1Cert
reader := bytes.NewReader(b)
// read the pre-cert entry:
precert, err := readVarBytes(reader, CertificateLengthBytes)
if err != nil {
return chain, err
}
chain = append(chain, precert)
// and then read and return the chain up to the root:
remainingChain, err := readASN1CertList(reader, CertificateChainLengthBytes, CertificateLengthBytes)
if err != nil {
return chain, err
}
chain = append(chain, remainingChain...)
return chain, nil
}
// UnmarshalDigitallySigned reconstructs a DigitallySigned structure from a Reader
func UnmarshalDigitallySigned(r io.Reader) (*DigitallySigned, error) {
var h byte
if err := binary.Read(r, binary.BigEndian, &h); err != nil {
return nil, fmt.Errorf("failed to read HashAlgorithm: %v", err)
}
var s byte
if err := binary.Read(r, binary.BigEndian, &s); err != nil {
return nil, fmt.Errorf("failed to read SignatureAlgorithm: %v", err)
}
sig, err := readVarBytes(r, SignatureLengthBytes)
if err != nil {
return nil, fmt.Errorf("failed to read Signature bytes: %v", err)
}
return &DigitallySigned{
HashAlgorithm: HashAlgorithm(h),
SignatureAlgorithm: SignatureAlgorithm(s),
Signature: sig,
}, nil
}
func marshalDigitallySignedHere(ds DigitallySigned, here []byte) ([]byte, error) {
sigLen := len(ds.Signature)
dsOutLen := 2 + SignatureLengthBytes + sigLen
if here == nil {
here = make([]byte, dsOutLen)
}
if len(here) < dsOutLen {
return nil, ErrNotEnoughBuffer
}
here = here[0:dsOutLen]
here[0] = byte(ds.HashAlgorithm)
here[1] = byte(ds.SignatureAlgorithm)
binary.BigEndian.PutUint16(here[2:4], uint16(sigLen))
copy(here[4:], ds.Signature)
return here, nil
}
// MarshalDigitallySigned marshalls a DigitallySigned structure into a byte array
func MarshalDigitallySigned(ds DigitallySigned) ([]byte, error) {
return marshalDigitallySignedHere(ds, nil)
}
func checkCertificateFormat(cert ASN1Cert) error {
if len(cert) == 0 {
return errors.New("certificate is zero length")
}
if len(cert) > MaxCertificateLength {
return errors.New("certificate too large")
}
return nil
}
func checkExtensionsFormat(ext CTExtensions) error {
if len(ext) > MaxExtensionsLength {
return errors.New("extensions too large")
}
return nil
}
func serializeV1CertSCTSignatureInput(timestamp uint64, cert ASN1Cert, ext CTExtensions) ([]byte, error) {
if err := checkCertificateFormat(cert); err != nil {
return nil, err
}
if err := checkExtensionsFormat(ext); err != nil {
return nil, err
}
var buf bytes.Buffer
if err := binary.Write(&buf, binary.BigEndian, V1); err != nil {
return nil, err
}
if err := binary.Write(&buf, binary.BigEndian, CertificateTimestampSignatureType); err != nil {
return nil, err
}
if err := binary.Write(&buf, binary.BigEndian, timestamp); err != nil {
return nil, err
}
if err := binary.Write(&buf, binary.BigEndian, X509LogEntryType); err != nil {
return nil, err
}
if err := writeVarBytes(&buf, cert, CertificateLengthBytes); err != nil {
return nil, err
}
if err := writeVarBytes(&buf, ext, ExtensionsLengthBytes); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func serializeV1PrecertSCTSignatureInput(timestamp uint64, issuerKeyHash [issuerKeyHashLength]byte, tbs []byte, ext CTExtensions) ([]byte, error) {
if err := checkCertificateFormat(tbs); err != nil {
return nil, err
}
if err := checkExtensionsFormat(ext); err != nil {
return nil, err
}
var buf bytes.Buffer
if err := binary.Write(&buf, binary.BigEndian, V1); err != nil {
return nil, err
}
if err := binary.Write(&buf, binary.BigEndian, CertificateTimestampSignatureType); err != nil {
return nil, err
}
if err := binary.Write(&buf, binary.BigEndian, timestamp); err != nil {
return nil, err
}
if err := binary.Write(&buf, binary.BigEndian, PrecertLogEntryType); err != nil {
return nil, err
}
if _, err := buf.Write(issuerKeyHash[:]); err != nil {
return nil, err
}
if err := writeVarBytes(&buf, tbs, CertificateLengthBytes); err != nil {
return nil, err
}
if err := writeVarBytes(&buf, ext, ExtensionsLengthBytes); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func serializeV1SCTSignatureInput(sct SignedCertificateTimestamp, entry LogEntry) ([]byte, error) {
if sct.SCTVersion != V1 {
return nil, fmt.Errorf("unsupported SCT version, expected V1, but got %s", sct.SCTVersion)
}
if entry.Leaf.LeafType != TimestampedEntryLeafType {
return nil, fmt.Errorf("Unsupported leaf type %s", entry.Leaf.LeafType)
}
switch entry.Leaf.TimestampedEntry.EntryType {
case X509LogEntryType:
return serializeV1CertSCTSignatureInput(sct.Timestamp, entry.Leaf.TimestampedEntry.X509Entry, entry.Leaf.TimestampedEntry.Extensions)
case PrecertLogEntryType:
return serializeV1PrecertSCTSignatureInput(sct.Timestamp, entry.Leaf.TimestampedEntry.PrecertEntry.IssuerKeyHash,
entry.Leaf.TimestampedEntry.PrecertEntry.TBSCertificate,
entry.Leaf.TimestampedEntry.Extensions)
default:
return nil, fmt.Errorf("unknown TimestampedEntryLeafType %s", entry.Leaf.TimestampedEntry.EntryType)
}
}
// 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:
return serializeV1SCTSignatureInput(sct, entry)
default:
return nil, fmt.Errorf("unknown SCT version %d", sct.SCTVersion)
}
}
// SerializedLength will return the space (in bytes)
func (sct SignedCertificateTimestamp) SerializedLength() (int, error) {
switch sct.SCTVersion {
case V1:
extLen := len(sct.Extensions)
sigLen := len(sct.Signature.Signature)
return 1 + 32 + 8 + 2 + extLen + 2 + 2 + sigLen, nil
default:
return 0, ErrInvalidVersion
}
}
func serializeV1SCTHere(sct SignedCertificateTimestamp, here []byte) ([]byte, error) {
if sct.SCTVersion != V1 {
return nil, ErrInvalidVersion
}
sctLen, err := sct.SerializedLength()
if err != nil {
return nil, err
}
if here == nil {
here = make([]byte, sctLen)
}
if len(here) < sctLen {
return nil, ErrNotEnoughBuffer
}
if err := checkExtensionsFormat(sct.Extensions); err != nil {
return nil, err
}
here = here[0:sctLen]
// Write Version
here[0] = byte(sct.SCTVersion)
// Write LogID
copy(here[1:33], sct.LogID[:])
// Write Timestamp
binary.BigEndian.PutUint64(here[33:41], sct.Timestamp)
// Write Extensions
extLen := len(sct.Extensions)
binary.BigEndian.PutUint16(here[41:43], uint16(extLen))
n := 43 + extLen
copy(here[43:n], sct.Extensions)
// Write Signature
_, err = marshalDigitallySignedHere(sct.Signature, here[n:])
if err != nil {
return nil, err
}
return here, nil
}
// SerializeSCTHere serializes the passed in sct into the format specified
// by RFC6962 section 3.2.
// If a bytes slice here is provided then it will attempt to serialize into the
// provided byte slice, ErrNotEnoughBuffer will be returned if the buffer is
// too small.
// If a nil byte slice is provided, a buffer for will be allocated for you
// The returned slice will be sliced to the correct length.
func SerializeSCTHere(sct SignedCertificateTimestamp, here []byte) ([]byte, error) {
switch sct.SCTVersion {
case V1:
return serializeV1SCTHere(sct, here)
default:
return nil, fmt.Errorf("unknown SCT version %d", sct.SCTVersion)
}
}
// SerializeSCT serializes the passed in sct into the format specified
// by RFC6962 section 3.2
// Equivalent to SerializeSCTHere(sct, nil)
func SerializeSCT(sct SignedCertificateTimestamp) ([]byte, error) {
return SerializeSCTHere(sct, nil)
}
func deserializeSCTV1(r io.Reader, sct *SignedCertificateTimestamp) error {
if err := binary.Read(r, binary.BigEndian, &sct.LogID); err != nil {
return err
}
if err := binary.Read(r, binary.BigEndian, &sct.Timestamp); err != nil {
return err
}
ext, err := readVarBytes(r, ExtensionsLengthBytes)
if err != nil {
return err
}
sct.Extensions = ext
ds, err := UnmarshalDigitallySigned(r)
if err != nil {
return err
}
sct.Signature = *ds
return nil
}
func DeserializeSCT(r io.Reader) (*SignedCertificateTimestamp, error) {
var sct SignedCertificateTimestamp
if err := binary.Read(r, binary.BigEndian, &sct.SCTVersion); err != nil {
return nil, err
}
switch sct.SCTVersion {
case V1:
return &sct, deserializeSCTV1(r, &sct)
default:
return nil, fmt.Errorf("unknown SCT version %d", sct.SCTVersion)
}
}
func serializeV1STHSignatureInput(sth SignedTreeHead) ([]byte, error) {
if sth.Version != V1 {
return nil, fmt.Errorf("invalid STH version %d", sth.Version)
}
if sth.TreeSize < 0 {
return nil, fmt.Errorf("invalid tree size %d", sth.TreeSize)
}
if len(sth.SHA256RootHash) != crypto.SHA256.Size() {
return nil, fmt.Errorf("invalid TreeHash length, got %d expected %d", len(sth.SHA256RootHash), crypto.SHA256.Size())
}
var buf bytes.Buffer
if err := binary.Write(&buf, binary.BigEndian, V1); err != nil {
return nil, err
}
if err := binary.Write(&buf, binary.BigEndian, TreeHashSignatureType); err != nil {
return nil, err
}
if err := binary.Write(&buf, binary.BigEndian, sth.Timestamp); err != nil {
return nil, err
}
if err := binary.Write(&buf, binary.BigEndian, sth.TreeSize); err != nil {
return nil, err
}
if err := binary.Write(&buf, binary.BigEndian, sth.SHA256RootHash); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// SerializeSTHSignatureInput serializes the passed in sth into the correct
// format for signing.
func SerializeSTHSignatureInput(sth SignedTreeHead) ([]byte, error) {
switch sth.Version {
case V1:
return serializeV1STHSignatureInput(sth)
default:
return nil, fmt.Errorf("unsupported STH version %d", sth.Version)
}
}

View File

@ -1,363 +0,0 @@
package ct
import (
"bytes"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"github.com/google/certificate-transparency/go/x509"
)
const (
issuerKeyHashLength = 32
)
///////////////////////////////////////////////////////////////////////////////
// The following structures represent those outlined in the RFC6962 document:
///////////////////////////////////////////////////////////////////////////////
// LogEntryType represents the LogEntryType enum from section 3.1 of the RFC:
// enum { x509_entry(0), precert_entry(1), (65535) } LogEntryType;
type LogEntryType uint16
func (e LogEntryType) String() string {
switch e {
case X509LogEntryType:
return "X509LogEntryType"
case PrecertLogEntryType:
return "PrecertLogEntryType"
}
panic(fmt.Sprintf("No string defined for LogEntryType constant value %d", e))
}
// LogEntryType constants, see section 3.1 of RFC6962.
const (
X509LogEntryType LogEntryType = 0
PrecertLogEntryType LogEntryType = 1
)
// MerkleLeafType represents the MerkleLeafType enum from section 3.4 of the
// RFC: enum { timestamped_entry(0), (255) } MerkleLeafType;
type MerkleLeafType uint8
func (m MerkleLeafType) String() string {
switch m {
case TimestampedEntryLeafType:
return "TimestampedEntryLeafType"
default:
return fmt.Sprintf("UnknownLeafType(%d)", m)
}
}
// MerkleLeafType constants, see section 3.4 of the RFC.
const (
TimestampedEntryLeafType MerkleLeafType = 0 // Entry type for an SCT
)
// Version represents the Version enum from section 3.2 of the RFC:
// enum { v1(0), (255) } Version;
type Version uint8
func (v Version) String() string {
switch v {
case V1:
return "V1"
default:
return fmt.Sprintf("UnknownVersion(%d)", v)
}
}
// CT Version constants, see section 3.2 of the RFC.
const (
V1 Version = 0
)
// SignatureType differentiates STH signatures from SCT signatures, see RFC
// section 3.2
type SignatureType uint8
func (st SignatureType) String() string {
switch st {
case CertificateTimestampSignatureType:
return "CertificateTimestamp"
case TreeHashSignatureType:
return "TreeHash"
default:
return fmt.Sprintf("UnknownSignatureType(%d)", st)
}
}
// SignatureType constants, see RFC section 3.2
const (
CertificateTimestampSignatureType SignatureType = 0
TreeHashSignatureType SignatureType = 1
)
// ASN1Cert type for holding the raw DER bytes of an ASN.1 Certificate
// (section 3.1)
type ASN1Cert []byte
// PreCert represents a Precertificate (section 3.2)
type PreCert struct {
IssuerKeyHash [issuerKeyHashLength]byte
TBSCertificate []byte
}
// CTExtensions is a representation of the raw bytes of any CtExtension
// structure (see section 3.2)
type CTExtensions []byte
// 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
// HashAlgorithm from the DigitallySigned struct
type HashAlgorithm byte
// HashAlgorithm constants
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 from the the DigitallySigned struct
type SignatureAlgorithm byte
// SignatureAlgorithm constants
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)
}
}
// DigitallySigned represents an RFC5246 DigitallySigned structure
type DigitallySigned struct {
HashAlgorithm HashAlgorithm
SignatureAlgorithm SignatureAlgorithm
Signature []byte
}
// 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)
}
ds, err := UnmarshalDigitallySigned(bytes.NewReader(raw))
if err != nil {
return fmt.Errorf("failed to unmarshal DigitallySigned: %v", err)
}
*d = *ds
return nil
}
// Base64String returns the base64 representation of the DigitallySigned struct.
func (d DigitallySigned) Base64String() (string, error) {
b, err := MarshalDigitallySigned(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 contents of an entry in a CT log, see section 3.1.
type LogEntry struct {
Index int64
Leaf MerkleTreeLeaf
X509Cert *x509.Certificate
Precert *Precertificate
Chain []ASN1Cert
}
// 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 in the RFC)
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"` // The Log's signature for this STH (see RFC section 3.5)
LogID SHA256Hash `json:"log_id"` // The SHA256 hash of the log's public key
}
// SignedCertificateTimestamp represents the structure returned by the
// add-chain and add-pre-chain methods after base64 decoding. (see RFC sections
// 3.2 ,4.1 and 4.2)
type SignedCertificateTimestamp struct {
SCTVersion Version // The version of the protocol to which the SCT conforms
LogID SHA256Hash // the SHA-256 hash of the log's public key, calculated over
// the DER encoding of the key represented as SubjectPublicKeyInfo.
Timestamp uint64 // Timestamp (in ms since unix epoc) at which the SCT was issued
Extensions CTExtensions // For future extensions to the protocol
Signature DigitallySigned // The Log's signature for this SCT
}
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[:]),
s.Timestamp,
s.Extensions,
s.Signature)
}
// TimestampedEntry is part of the MerkleTreeLeaf structure.
// See RFC section 3.4
type TimestampedEntry struct {
Timestamp uint64
EntryType LogEntryType
X509Entry ASN1Cert
PrecertEntry PreCert
Extensions CTExtensions
}
// MerkleTreeLeaf represents the deserialized sructure of the hash input for the
// leaves of a log's Merkle tree. See RFC section 3.4
type MerkleTreeLeaf struct {
Version Version // the version of the protocol to which the MerkleTreeLeaf corresponds
LeafType MerkleLeafType // The type of the leaf input, currently only TimestampedEntry can exist
TimestampedEntry TimestampedEntry // The entry data itself
}
// Precertificate represents the parsed CT Precertificate structure.
type Precertificate struct {
// Raw DER bytes of the precert
Raw []byte
// SHA256 hash of the issuing key
IssuerKeyHash [issuerKeyHashLength]byte
// Parsed TBSCertificate structure (held in an x509.Certificate for ease of
// access.
TBSCertificate x509.Certificate
}
// X509Certificate returns the X.509 Certificate contained within the
// MerkleTreeLeaf.
// Returns a pointer to an x509.Certificate or a non-nil error.
func (m *MerkleTreeLeaf) X509Certificate() (*x509.Certificate, error) {
return x509.ParseCertificate(m.TimestampedEntry.X509Entry)
}
type sctError int
// Preallocate errors for performance
var (
ErrInvalidVersion error = sctError(1)
ErrNotEnoughBuffer error = sctError(2)
)
func (e sctError) Error() string {
switch e {
case ErrInvalidVersion:
return "invalid SCT version detected"
case ErrNotEnoughBuffer:
return "provided buffer was too small"
default:
return "unknown error"
}
}

View File

@ -1,78 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"cert_pool.go",
"pem_decrypt.go",
"pkcs1.go",
"pkcs8.go",
"root.go",
"sec1.go",
"verify.go",
"x509.go",
] + select({
"@io_bazel_rules_go//go/platform:darwin": [
"root_darwin.go",
"root_stub.go",
],
"@io_bazel_rules_go//go/platform:dragonfly": [
"root_unix.go",
],
"@io_bazel_rules_go//go/platform:freebsd": [
"root_unix.go",
],
"@io_bazel_rules_go//go/platform:linux": [
"root_unix.go",
],
"@io_bazel_rules_go//go/platform:netbsd": [
"root_unix.go",
],
"@io_bazel_rules_go//go/platform:openbsd": [
"root_unix.go",
],
"@io_bazel_rules_go//go/platform:plan9": [
"root_plan9.go",
],
"@io_bazel_rules_go//go/platform:windows": [
"root_windows.go",
],
"//conditions:default": [],
}),
cgo = True,
clinkopts = select({
"@io_bazel_rules_go//go/platform:darwin": [
"-framework CoreFoundation -framework Security",
],
"//conditions:default": [],
}),
copts = select({
"@io_bazel_rules_go//go/platform:darwin": [
"-mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1060",
],
"//conditions:default": [],
}),
importpath = "github.com/google/certificate-transparency/go/x509",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/google/certificate-transparency/go/asn1:go_default_library",
"//vendor/github.com/google/certificate-transparency/go/x509/pkix:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//vendor/github.com/google/certificate-transparency/go/x509/pkix:all-srcs",
],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -1,56 +0,0 @@
// 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 (
// START CT CHANGES
"github.com/google/certificate-transparency/go/asn1"
"github.com/google/certificate-transparency/go/x509/pkix"
// END CT CHANGES
"errors"
"fmt"
)
// pkcs8 reflects an ASN.1, PKCS#8 PrivateKey. See
// ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-8/pkcs-8v1_2.asn
// and RFC5208.
type pkcs8 struct {
Version int
Algo pkix.AlgorithmIdentifier
PrivateKey []byte
// optional attributes omitted.
}
// ParsePKCS8PrivateKey parses an unencrypted, PKCS#8 private key. See
// http://www.rsa.com/rsalabs/node.asp?id=2130 and RFC5208.
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)
}
}

View File

@ -1,173 +0,0 @@
// 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
"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
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{}
}
// 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. Additional elements in the name are ignored.
type Name struct {
Country, Organization, OrganizationalUnit []string
Locality, Province []string
StreetAddress, PostalCode []string
SerialNumber, CommonName string
Names []AttributeTypeAndValue
}
func (n *Name) FillFromRDNSequence(rdns *RDNSequence) {
for _, rdn := range *rdns {
if len(rdn) == 0 {
continue
}
atv := rdn[0]
n.Names = append(n.Names, atv)
value, ok := atv.Value.(string)
if !ok {
continue
}
t := atv.Type
if len(t) == 4 && t[0] == 2 && t[1] == 5 && t[2] == 4 {
switch t[3] {
case 3:
n.CommonName = value
case 5:
n.SerialNumber = value
case 6:
n.Country = append(n.Country, value)
case 7:
n.Locality = append(n.Locality, value)
case 8:
n.Province = append(n.Province, value)
case 9:
n.StreetAddress = append(n.StreetAddress, value)
case 10:
n.Organization = append(n.Organization, value)
case 11:
n.OrganizationalUnit = append(n.OrganizationalUnit, value)
case 17:
n.PostalCode = append(n.PostalCode, value)
}
}
}
}
var (
oidCountry = []int{2, 5, 4, 6}
oidOrganization = []int{2, 5, 4, 10}
oidOrganizationalUnit = []int{2, 5, 4, 11}
oidCommonName = []int{2, 5, 4, 3}
oidSerialNumber = []int{2, 5, 4, 5}
oidLocality = []int{2, 5, 4, 7}
oidProvince = []int{2, 5, 4, 8}
oidStreetAddress = []int{2, 5, 4, 9}
oidPostalCode = []int{2, 5, 4, 17}
)
// 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 appendRDNs(in RDNSequence, values []string, oid asn1.ObjectIdentifier) RDNSequence {
if len(values) == 0 {
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 = appendRDNs(ret, n.Country, oidCountry)
ret = appendRDNs(ret, n.Organization, oidOrganization)
ret = appendRDNs(ret, n.OrganizationalUnit, oidOrganizationalUnit)
ret = appendRDNs(ret, n.Locality, oidLocality)
ret = appendRDNs(ret, n.Province, oidProvince)
ret = appendRDNs(ret, n.StreetAddress, oidStreetAddress)
ret = appendRDNs(ret, n.PostalCode, oidPostalCode)
if len(n.CommonName) > 0 {
ret = appendRDNs(ret, []string{n.CommonName}, oidCommonName)
}
if len(n.SerialNumber) > 0 {
ret = appendRDNs(ret, []string{n.SerialNumber}, oidSerialNumber)
}
return ret
}
// 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 now is past the expiry time of certList.
func (certList *CertificateList) HasExpired(now time.Time) bool {
return now.After(certList.TBSCertList.NextUpdate)
}
// TBSCertificateList represents the ASN.1 structure of the same name. See RFC
// 5280, section 5.1.
type TBSCertificateList struct {
Raw asn1.RawContent
Version int `asn1:"optional,default:2"`
Signature AlgorithmIdentifier
Issuer RDNSequence
ThisUpdate time.Time
NextUpdate time.Time
RevokedCertificates []RevokedCertificate `asn1:"optional"`
Extensions []Extension `asn1:"tag:0,optional,explicit"`
}
// RevokedCertificate represents the ASN.1 structure of the same name. See RFC
// 5280, section 5.1.
type RevokedCertificate struct {
SerialNumber *big.Int
RevocationTime time.Time
Extensions []Extension `asn1:"optional"`
}

View File

@ -1,83 +0,0 @@
// 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 darwin,cgo
package x509
/*
#cgo CFLAGS: -mmacosx-version-min=10.6 -D__MAC_OS_X_VERSION_MAX_ALLOWED=1060
#cgo LDFLAGS: -framework CoreFoundation -framework Security
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
// 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.
//
// Note: The CFDataRef returned in pemRoots must be released (using CFRelease) after
// we've consumed its content.
int FetchPEMRootsCTX509(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;
}
*/
import "C"
import "unsafe"
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
return nil, nil
}
func initSystemRoots() {
roots := NewCertPool()
var data C.CFDataRef = nil
err := C.FetchPEMRootsCTX509(&data)
if err == -1 {
return
}
defer C.CFRelease(C.CFTypeRef(data))
buf := C.GoBytes(unsafe.Pointer(C.CFDataGetBytePtr(data)), C.int(C.CFDataGetLength(data)))
roots.AppendCertsFromPEM(buf)
systemRoots = roots
}

View File

@ -1,14 +0,0 @@
// 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 darwin,!cgo
package x509
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
return nil, nil
}
func initSystemRoots() {
}

View File

@ -1,37 +0,0 @@
// 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 openbsd netbsd
package x509
import "io/ioutil"
// 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
"/etc/ssl/ca-bundle.pem", // OpenSUSE
"/etc/ssl/cert.pem", // OpenBSD
"/usr/local/share/certs/ca-root-nss.crt", // FreeBSD/DragonFly
}
func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
return nil, nil
}
func initSystemRoots() {
roots := NewCertPool()
for _, file := range certFiles {
data, err := ioutil.ReadFile(file)
if err == nil {
roots.AppendCertsFromPEM(data)
systemRoots = roots
return
}
}
// All of the files failed to load. systemRoots will be nil which will
// trigger a specific error at verification time.
}

View File

@ -1,476 +0,0 @@
// 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 (
"fmt"
"net"
"runtime"
"strings"
"time"
"unicode/utf8"
)
type InvalidReason int
const (
// NotAuthorizedToSign results when a certificate is signed by another
// which isn't marked as a CA certificate.
NotAuthorizedToSign InvalidReason = iota
// Expired results when a certificate has expired, based on the time
// given in the VerifyOptions.
Expired
// CANotAuthorizedForThisName results when an intermediate or root
// certificate has a name constraint which doesn't include the name
// being checked.
CANotAuthorizedForThisName
// TooManyIntermediates results when a path length constraint is
// violated.
TooManyIntermediates
// IncompatibleUsage results when the certificate's key usage indicates
// that it may only be used for a different purpose.
IncompatibleUsage
)
// CertificateInvalidError results when an odd error occurs. Users of this
// library probably want to handle all these errors uniformly.
type CertificateInvalidError struct {
Cert *Certificate
Reason InvalidReason
}
func (e CertificateInvalidError) Error() string {
switch e.Reason {
case NotAuthorizedToSign:
return "x509: certificate is not authorized to sign other certificates"
case Expired:
return "x509: certificate has expired or is not yet valid"
case CANotAuthorizedForThisName:
return "x509: a root or intermediate certificate is not authorized to sign in this domain"
case TooManyIntermediates:
return "x509: too many intermediates for path length constraint"
case IncompatibleUsage:
return "x509: certificate specifies an incompatible key usage"
}
return "x509: unknown error"
}
// HostnameError results when the set of authorized names doesn't match the
// requested name.
type HostnameError struct {
Certificate *Certificate
Host string
}
func (h HostnameError) Error() string {
c := h.Certificate
var valid string
if ip := net.ParseIP(h.Host); ip != nil {
// Trying to validate an IP
if len(c.IPAddresses) == 0 {
return "x509: cannot validate certificate for " + h.Host + " because it doesn't contain any IP SANs"
}
for _, san := range c.IPAddresses {
if len(valid) > 0 {
valid += ", "
}
valid += san.String()
}
} else {
if len(c.DNSNames) > 0 {
valid = strings.Join(c.DNSNames, ", ")
} else {
valid = c.Subject.CommonName
}
}
return "x509: certificate is valid for " + valid + ", not " + h.Host
}
// UnknownAuthorityError results when the certificate issuer is unknown
type UnknownAuthorityError struct {
cert *Certificate
// hintErr contains an error that may be helpful in determining why an
// authority wasn't found.
hintErr error
// hintCert contains a possible authority certificate that was rejected
// because of the error in hintErr.
hintCert *Certificate
}
func (e UnknownAuthorityError) Error() string {
s := "x509: certificate signed by unknown authority"
if e.hintErr != nil {
certName := e.hintCert.Subject.CommonName
if len(certName) == 0 {
if len(e.hintCert.Subject.Organization) > 0 {
certName = e.hintCert.Subject.Organization[0]
}
certName = "serial:" + e.hintCert.SerialNumber.String()
}
s += fmt.Sprintf(" (possibly because of %q while trying to verify candidate authority certificate %q)", e.hintErr, certName)
}
return s
}
// SystemRootsError results when we fail to load the system root certificates.
type SystemRootsError struct {
}
func (e SystemRootsError) Error() string {
return "x509: failed to load system roots and no roots provided"
}
// VerifyOptions contains parameters for Certificate.Verify. It's a structure
// because other PKIX verification APIs have ended up needing many options.
type VerifyOptions struct {
DNSName string
Intermediates *CertPool
Roots *CertPool // if nil, the system roots are used
CurrentTime time.Time // if zero, the current time is used
DisableTimeChecks bool
// KeyUsage specifies which Extended Key Usage values are acceptable.
// An empty list means ExtKeyUsageServerAuth. Key usage is considered a
// constraint down the chain which mirrors Windows CryptoAPI behaviour,
// but not the spec. To accept any key usage, include ExtKeyUsageAny.
KeyUsages []ExtKeyUsage
}
const (
leafCertificate = iota
intermediateCertificate
rootCertificate
)
// isValid performs validity checks on the c.
func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *VerifyOptions) error {
if !opts.DisableTimeChecks {
now := opts.CurrentTime
if now.IsZero() {
now = time.Now()
}
if now.Before(c.NotBefore) || now.After(c.NotAfter) {
return CertificateInvalidError{c, Expired}
}
}
if len(c.PermittedDNSDomains) > 0 {
ok := false
for _, domain := range c.PermittedDNSDomains {
if opts.DNSName == domain ||
(strings.HasSuffix(opts.DNSName, domain) &&
len(opts.DNSName) >= 1+len(domain) &&
opts.DNSName[len(opts.DNSName)-len(domain)-1] == '.') {
ok = true
break
}
}
if !ok {
return CertificateInvalidError{c, CANotAuthorizedForThisName}
}
}
// KeyUsage status flags are ignored. From Engineering Security, Peter
// Gutmann: A European government CA marked its signing certificates as
// being valid for encryption only, but no-one noticed. Another
// European CA marked its signature keys as not being valid for
// signatures. A different CA marked its own trusted root certificate
// as being invalid for certificate signing. Another national CA
// distributed a certificate to be used to encrypt data for the
// countrys tax authority that was marked as only being usable for
// digital signatures but not for encryption. Yet another CA reversed
// the order of the bit flags in the keyUsage due to confusion over
// encoding endianness, essentially setting a random keyUsage in
// certificates that it issued. Another CA created a self-invalidating
// certificate by adding a certificate policy statement stipulating
// that the certificate had to be used strictly as specified in the
// keyUsage, and a keyUsage containing a flag indicating that the RSA
// encryption key could only be used for Diffie-Hellman key agreement.
if certType == intermediateCertificate && (!c.BasicConstraintsValid || !c.IsCA) {
return CertificateInvalidError{c, NotAuthorizedToSign}
}
if c.BasicConstraintsValid && c.MaxPathLen >= 0 {
numIntermediates := len(currentChain) - 1
if numIntermediates > c.MaxPathLen {
return CertificateInvalidError{c, TooManyIntermediates}
}
}
return nil
}
// Verify attempts to verify c by building one or more chains from c to a
// certificate in opts.Roots, using certificates in opts.Intermediates if
// needed. If successful, it returns one or more chains where the first
// element of the chain is c and the last element is from opts.Roots.
//
// WARNING: this doesn't do any revocation checking.
func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err error) {
// Use Windows's own verification and chain building.
if opts.Roots == nil && runtime.GOOS == "windows" {
return c.systemVerify(&opts)
}
if opts.Roots == nil {
opts.Roots = systemRootsPool()
if opts.Roots == nil {
return nil, SystemRootsError{}
}
}
err = c.isValid(leafCertificate, nil, &opts)
if err != nil {
return
}
if len(opts.DNSName) > 0 {
err = c.VerifyHostname(opts.DNSName)
if err != nil {
return
}
}
candidateChains, err := c.buildChains(make(map[int][][]*Certificate), []*Certificate{c}, &opts)
if err != nil {
return
}
keyUsages := opts.KeyUsages
if len(keyUsages) == 0 {
keyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth}
}
// If any key usage is acceptable then we're done.
for _, usage := range keyUsages {
if usage == ExtKeyUsageAny {
chains = candidateChains
return
}
}
for _, candidate := range candidateChains {
if checkChainForKeyUsage(candidate, keyUsages) {
chains = append(chains, candidate)
}
}
if len(chains) == 0 {
err = CertificateInvalidError{c, IncompatibleUsage}
}
return
}
func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate {
n := make([]*Certificate, len(chain)+1)
copy(n, chain)
n[len(chain)] = cert
return n
}
func (c *Certificate) buildChains(cache map[int][][]*Certificate, currentChain []*Certificate, opts *VerifyOptions) (chains [][]*Certificate, err error) {
possibleRoots, failedRoot, rootErr := opts.Roots.findVerifiedParents(c)
for _, rootNum := range possibleRoots {
root := opts.Roots.certs[rootNum]
err = root.isValid(rootCertificate, currentChain, opts)
if err != nil {
continue
}
chains = append(chains, appendToFreshChain(currentChain, root))
}
possibleIntermediates, failedIntermediate, intermediateErr := opts.Intermediates.findVerifiedParents(c)
nextIntermediate:
for _, intermediateNum := range possibleIntermediates {
intermediate := opts.Intermediates.certs[intermediateNum]
for _, cert := range currentChain {
if cert == intermediate {
continue nextIntermediate
}
}
err = intermediate.isValid(intermediateCertificate, currentChain, opts)
if err != nil {
continue
}
var childChains [][]*Certificate
childChains, ok := cache[intermediateNum]
if !ok {
childChains, err = intermediate.buildChains(cache, appendToFreshChain(currentChain, intermediate), opts)
cache[intermediateNum] = childChains
}
chains = append(chains, childChains...)
}
if len(chains) > 0 {
err = nil
}
if len(chains) == 0 && err == nil {
hintErr := rootErr
hintCert := failedRoot
if hintErr == nil {
hintErr = intermediateErr
hintCert = failedIntermediate
}
err = UnknownAuthorityError{c, hintErr, hintCert}
}
return
}
func matchHostnames(pattern, host string) bool {
if len(pattern) == 0 || len(host) == 0 {
return false
}
patternParts := strings.Split(pattern, ".")
hostParts := strings.Split(host, ".")
if len(patternParts) != len(hostParts) {
return false
}
for i, patternPart := range patternParts {
if patternPart == "*" {
continue
}
if patternPart != hostParts[i] {
return false
}
}
return true
}
// toLowerCaseASCII returns a lower-case version of in. See RFC 6125 6.4.1. We use
// an explicitly ASCII function to avoid any sharp corners resulting from
// performing Unicode operations on DNS labels.
func toLowerCaseASCII(in string) string {
// If the string is already lower-case then there's nothing to do.
isAlreadyLowerCase := true
for _, c := range in {
if c == utf8.RuneError {
// If we get a UTF-8 error then there might be
// upper-case ASCII bytes in the invalid sequence.
isAlreadyLowerCase = false
break
}
if 'A' <= c && c <= 'Z' {
isAlreadyLowerCase = false
break
}
}
if isAlreadyLowerCase {
return in
}
out := []byte(in)
for i, c := range out {
if 'A' <= c && c <= 'Z' {
out[i] += 'a' - 'A'
}
}
return string(out)
}
// VerifyHostname returns nil if c is a valid certificate for the named host.
// Otherwise it returns an error describing the mismatch.
func (c *Certificate) VerifyHostname(h string) error {
// IP addresses may be written in [ ].
candidateIP := h
if len(h) >= 3 && h[0] == '[' && h[len(h)-1] == ']' {
candidateIP = h[1 : len(h)-1]
}
if ip := net.ParseIP(candidateIP); ip != nil {
// We only match IP addresses against IP SANs.
// https://tools.ietf.org/html/rfc6125#appendix-B.2
for _, candidate := range c.IPAddresses {
if ip.Equal(candidate) {
return nil
}
}
return HostnameError{c, candidateIP}
}
lowered := toLowerCaseASCII(h)
if len(c.DNSNames) > 0 {
for _, match := range c.DNSNames {
if matchHostnames(toLowerCaseASCII(match), lowered) {
return nil
}
}
// If Subject Alt Name is given, we ignore the common name.
} else if matchHostnames(toLowerCaseASCII(c.Subject.CommonName), lowered) {
return nil
}
return HostnameError{c, h}
}
func checkChainForKeyUsage(chain []*Certificate, keyUsages []ExtKeyUsage) bool {
usages := make([]ExtKeyUsage, len(keyUsages))
copy(usages, keyUsages)
if len(chain) == 0 {
return false
}
usagesRemaining := len(usages)
// We walk down the list and cross out any usages that aren't supported
// by each certificate. If we cross out all the usages, then the chain
// is unacceptable.
for i := len(chain) - 1; i >= 0; i-- {
cert := chain[i]
if len(cert.ExtKeyUsage) == 0 && len(cert.UnknownExtKeyUsage) == 0 {
// The certificate doesn't have any extended key usage specified.
continue
}
for _, usage := range cert.ExtKeyUsage {
if usage == ExtKeyUsageAny {
// The certificate is explicitly good for any usage.
continue
}
}
const invalidUsage ExtKeyUsage = -1
NextRequestedUsage:
for i, requestedUsage := range usages {
if requestedUsage == invalidUsage {
continue
}
for _, usage := range cert.ExtKeyUsage {
if requestedUsage == usage {
continue NextRequestedUsage
} else if requestedUsage == ExtKeyUsageServerAuth &&
(usage == ExtKeyUsageNetscapeServerGatedCrypto ||
usage == ExtKeyUsageMicrosoftServerGatedCrypto) {
// In order to support COMODO
// certificate chains, we have to
// accept Netscape or Microsoft SGC
// usages as equal to ServerAuth.
continue NextRequestedUsage
}
}
usages[i] = invalidUsage
usagesRemaining--
if usagesRemaining == 0 {
return false
}
}
}
return true
}

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More