Configure and apply golangci-lint
This commit is contained in:
parent
63c3716b5b
commit
baf6d0f037
23 changed files with 847 additions and 81 deletions
76
.golangci.yml
Normal file
76
.golangci.yml
Normal file
|
@ -0,0 +1,76 @@
|
|||
---
|
||||
run:
|
||||
skip-files:
|
||||
- pkg/config/amd64.go
|
||||
- pkg/config/arm64.go
|
||||
- pkg/config/armhf.go
|
||||
|
||||
output:
|
||||
sort-results: true
|
||||
|
||||
linters-settings:
|
||||
cyclop:
|
||||
max-complexity: 15
|
||||
goheader:
|
||||
values:
|
||||
const:
|
||||
ORGANIZATION: CAcert Inc.
|
||||
template: |-
|
||||
Copyright {{ YEAR-RANGE }} {{ ORGANIZATION }}
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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.
|
||||
gomnd:
|
||||
ignored-functions:
|
||||
- 'strconv.*'
|
||||
ignored-numbers: ["2", "16", "128", "0o600"]
|
||||
goimports:
|
||||
local-prefixes: code.cacert.org,git.cacert.org
|
||||
misspell:
|
||||
locale: US
|
||||
ignore-words:
|
||||
- CAcert
|
||||
|
||||
linters:
|
||||
disable-all: false
|
||||
enable:
|
||||
- bodyclose
|
||||
- containedctx
|
||||
- contextcheck
|
||||
- cyclop
|
||||
- decorder
|
||||
- errorlint
|
||||
- exportloopref
|
||||
- forbidigo
|
||||
- forcetypeassert
|
||||
- gocognit
|
||||
- goconst
|
||||
- gocritic
|
||||
- gofmt
|
||||
- goheader
|
||||
- goimports
|
||||
- gomnd
|
||||
- gosec
|
||||
- lll
|
||||
- makezero
|
||||
- misspell
|
||||
- nakedret
|
||||
- nestif
|
||||
- nlreturn
|
||||
- nolintlint
|
||||
- predeclared
|
||||
- revive
|
||||
- rowserrcheck
|
||||
- sqlclosecheck
|
||||
- wrapcheck
|
||||
- wsl
|
202
LICENSE
Normal file
202
LICENSE
Normal file
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
Copyright 2022 CAcert Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 main
|
||||
|
||||
import (
|
||||
|
@ -51,10 +68,12 @@ func main() {
|
|||
if err != nil {
|
||||
log.Fatalf("could not load CA hierarchy: %v", err)
|
||||
}
|
||||
|
||||
opts = append(opts, hsm.CaConfigOption(caConfig))
|
||||
|
||||
if setupMode {
|
||||
log.Print("running in setup mode")
|
||||
|
||||
opts = append(opts, hsm.SetupModeOption())
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,22 @@
|
|||
//go:build linux && amd64
|
||||
|
||||
/*
|
||||
Copyright 2022 CAcert Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 config
|
||||
|
||||
const SoftHsmModule = "/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so"
|
||||
|
|
|
@ -1,5 +1,22 @@
|
|||
//go:build linux && arm64
|
||||
|
||||
/*
|
||||
Copyright 2022 CAcert Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 config
|
||||
|
||||
const SoftHsmModule = "/usr/lib/aarch64-linux-gnu/softhsm/libsofthsm2.so"
|
||||
|
|
|
@ -1,5 +1,22 @@
|
|||
//go:build linux && arm
|
||||
|
||||
/*
|
||||
Copyright 2022 CAcert Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 config
|
||||
|
||||
const SoftHsmModule = "/usr/lib/arm-linux-gnueabihf/softhsm/libsofthsm2.so"
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
Copyright 2022 CAcert Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 config
|
||||
|
||||
import (
|
||||
|
@ -66,12 +83,14 @@ func (s *Settings) UnmarshalYAML(n *yaml.Node) error {
|
|||
}
|
||||
|
||||
if data.ValidityYears.Root < data.ValidityYears.Intermediary {
|
||||
return SettingsError{"validity of root CA certificates must be equal or greater than those if intermediary CA certificates"}
|
||||
return SettingsError{"validity of root CA certificates must be equal or greater than those if" +
|
||||
" intermediary CA certificates"}
|
||||
}
|
||||
|
||||
if data.URLPatterns.Ocsp == "" {
|
||||
return SettingsError{"you must specify an 'ocsp' URL pattern"}
|
||||
}
|
||||
|
||||
if strings.Count(data.URLPatterns.Ocsp, "%s") > 1 {
|
||||
return SettingsError{"url-pattern 'ocsp' must contain zero or one '%s' placeholder"}
|
||||
}
|
||||
|
@ -79,6 +98,7 @@ func (s *Settings) UnmarshalYAML(n *yaml.Node) error {
|
|||
if data.URLPatterns.CRL == "" {
|
||||
return SettingsError{"you must specify an 'crl' URL pattern"}
|
||||
}
|
||||
|
||||
if strings.Count(data.URLPatterns.CRL, "%s") != 1 {
|
||||
return SettingsError{"url-pattern 'crl' must contain one '%s' placeholder"}
|
||||
}
|
||||
|
@ -86,6 +106,7 @@ func (s *Settings) UnmarshalYAML(n *yaml.Node) error {
|
|||
if data.URLPatterns.Issuer == "" {
|
||||
return SettingsError{"you must specify an 'issuer' URL pattern"}
|
||||
}
|
||||
|
||||
if strings.Count(data.URLPatterns.Issuer, "%s") != 1 {
|
||||
return SettingsError{"url-pattern 'issuer' must contain one '%s' placeholder"}
|
||||
}
|
||||
|
@ -131,6 +152,7 @@ func (k *KeyStorage) UnmarshalYAML(n *yaml.Node) error {
|
|||
if ks.Module == "" {
|
||||
return errors.New("specify a 'module' field when using the 'p11module' type")
|
||||
}
|
||||
|
||||
k.Module = ks.Module
|
||||
default:
|
||||
return fmt.Errorf("unsupported KeyStorage type '%s'", ks.TokenType)
|
||||
|
@ -139,6 +161,7 @@ func (k *KeyStorage) UnmarshalYAML(n *yaml.Node) error {
|
|||
if ks.Label == "" {
|
||||
return errors.New("element 'label' must be specified")
|
||||
}
|
||||
|
||||
k.Label = ks.Label
|
||||
|
||||
return nil
|
||||
|
@ -155,6 +178,7 @@ func (c *SignerConfig) GetCADefinition(label string) (*CaCertificateEntry, error
|
|||
if !ok {
|
||||
return nil, fmt.Errorf("no CA definition found for label %s", label)
|
||||
}
|
||||
|
||||
return entry, nil
|
||||
}
|
||||
|
||||
|
@ -167,6 +191,7 @@ func (c *SignerConfig) CalculateValidity(cert *CaCertificateEntry, relativeTo ti
|
|||
} else {
|
||||
notAfter = notBefore.AddDate(c.global.ValidityYears.Intermediary, 0, 0)
|
||||
}
|
||||
|
||||
return notBefore, notAfter
|
||||
}
|
||||
|
||||
|
@ -179,6 +204,7 @@ func (c *SignerConfig) CalculateSubject(cert *CaCertificateEntry) pkix.Name {
|
|||
PostalCode: c.global.Organization.PostalCode,
|
||||
}
|
||||
subject.CommonName = cert.CommonName
|
||||
|
||||
return subject
|
||||
}
|
||||
|
||||
|
@ -195,6 +221,7 @@ func (c *SignerConfig) BuildOCSPURL(cert *CaCertificateEntry) string {
|
|||
if strings.Count(c.global.URLPatterns.Ocsp, "%s") == 1 {
|
||||
return fmt.Sprintf(c.global.URLPatterns.Ocsp, cert.Parent)
|
||||
}
|
||||
|
||||
return c.global.URLPatterns.Ocsp
|
||||
}
|
||||
|
||||
|
@ -218,6 +245,7 @@ func (c *SignerConfig) GetParentCA(label string) (*CaCertificateEntry, error) {
|
|||
// RootCAs returns the labels of all configured root CAs
|
||||
func (c *SignerConfig) RootCAs() []string {
|
||||
roots := make([]string, 0)
|
||||
|
||||
for label, entry := range c.caMap {
|
||||
if entry.IsRoot() {
|
||||
roots = append(roots, label)
|
||||
|
@ -230,6 +258,7 @@ func (c *SignerConfig) RootCAs() []string {
|
|||
// IntermediaryCAs returns the labels of all configured intermediary CAs
|
||||
func (c *SignerConfig) IntermediaryCAs() []string {
|
||||
intermediaries := make([]string, 0)
|
||||
|
||||
for label, entry := range c.caMap {
|
||||
if !entry.IsRoot() {
|
||||
intermediaries = append(intermediaries, label)
|
||||
|
@ -294,15 +323,19 @@ func (p *PrivateKeyInfo) UnmarshalYAML(value *yaml.Node) error {
|
|||
switch internalStructure.Algorithm {
|
||||
case "RSA":
|
||||
p.Algorithm = x509.RSA
|
||||
|
||||
if internalStructure.RSABits == nil {
|
||||
return errors.New("element 'rsa-bits' with RSA key length required for algorithm RSA")
|
||||
}
|
||||
|
||||
p.RSABits = *internalStructure.RSABits
|
||||
case "EC":
|
||||
p.Algorithm = x509.ECDSA
|
||||
|
||||
if internalStructure.EccCurve == "" {
|
||||
return errors.New("element 'ecc-curve' required for algorithm EC")
|
||||
}
|
||||
|
||||
p.EccCurve, err = nameToCurve(internalStructure.EccCurve)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -325,16 +358,19 @@ func (p *PrivateKeyInfo) MarshalYAML() (interface{}, error) {
|
|||
EccCurve string `yaml:"ecc-curve,omitempty"`
|
||||
RSABits *int `yaml:"rsa-bits,omitempty"`
|
||||
}{}
|
||||
|
||||
switch p.Algorithm {
|
||||
case x509.RSA:
|
||||
internalStructure.Algorithm = "RSA"
|
||||
internalStructure.RSABits = &p.RSABits
|
||||
case x509.ECDSA:
|
||||
internalStructure.Algorithm = "EC"
|
||||
|
||||
curveName, err := curveToName(p.EccCurve)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
internalStructure.EccCurve = curveName
|
||||
}
|
||||
|
||||
|
@ -385,17 +421,18 @@ type CaCertificateEntry struct {
|
|||
|
||||
func (c *CaCertificateEntry) UnmarshalYAML(value *yaml.Node) error {
|
||||
var m struct {
|
||||
KeyInfo *PrivateKeyInfo `yaml:"key-info"`
|
||||
CommonName string `yaml:"common-name"`
|
||||
MaxPathLen int `yaml:"max-path-len,omitempty"` // maximum path length should be 0 for CAs that issue end entity certificates
|
||||
ExtKeyUsage []string `yaml:"ext-key-usages,omitempty"`
|
||||
Parent string `yaml:"parent"`
|
||||
Storage string `yaml:"storage"`
|
||||
KeyInfo *PrivateKeyInfo `yaml:"key-info"`
|
||||
CommonName string `yaml:"common-name"`
|
||||
// maximum path length should be 0 for CAs that issue end entity certificates
|
||||
MaxPathLen int `yaml:"max-path-len,omitempty"`
|
||||
ExtKeyUsage []string `yaml:"ext-key-usages,omitempty"`
|
||||
Parent string `yaml:"parent"`
|
||||
Storage string `yaml:"storage"`
|
||||
}
|
||||
|
||||
err := value.Decode(&m)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("could not unmarshal CA certificate entry: %w", err)
|
||||
}
|
||||
|
||||
if m.KeyInfo == nil {
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
Copyright 2022 CAcert Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 config_test
|
||||
|
||||
import (
|
||||
|
@ -116,6 +133,7 @@ ecc-curve: P-521
|
|||
data, err := yaml.Marshal(item.pkInfo)
|
||||
if item.expectErr {
|
||||
require.Error(t, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -375,6 +393,7 @@ ext-key-usages:
|
|||
err := yaml.Unmarshal([]byte(item.yaml), &entry)
|
||||
if item.expectErr {
|
||||
require.Error(t, err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -544,6 +563,7 @@ func loadSignerConfig(t *testing.T) *config.SignerConfig {
|
|||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, sc)
|
||||
|
||||
return sc
|
||||
}
|
||||
|
||||
|
@ -726,7 +746,8 @@ url-patterns:
|
|||
crl: http://crl.example.org/%s.crl
|
||||
issuer: http://issuer.example.org/%s.crt
|
||||
`,
|
||||
errMsg: "invalid Settings validity of root CA certificates must be equal or greater than those if intermediary CA certificates",
|
||||
errMsg: "invalid Settings validity of root CA certificates must be equal or greater than " +
|
||||
"those of intermediary CA certificates",
|
||||
},
|
||||
"no OCSP pattern": {
|
||||
yaml: `
|
||||
|
|
|
@ -1,7 +1,25 @@
|
|||
/*
|
||||
Copyright 2022 CAcert Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 hsm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/ThalesIgnite/crypto11"
|
||||
|
||||
|
@ -78,7 +96,10 @@ func IsVerbose(ctx context.Context) bool {
|
|||
}
|
||||
|
||||
func GetP11Context(ctx context.Context, entry *config.CaCertificateEntry) (*crypto11.Context, error) {
|
||||
contexts := ctx.Value(ctxP11Contexts).(map[string]*crypto11.Context)
|
||||
contexts, ok := ctx.Value(ctxP11Contexts).(map[string]*crypto11.Context)
|
||||
if !ok {
|
||||
return nil, errors.New("type assertion failed")
|
||||
}
|
||||
|
||||
if p11Context, ok := contexts[entry.Storage]; ok {
|
||||
return p11Context, nil
|
||||
|
|
110
pkg/hsm/hsm.go
110
pkg/hsm/hsm.go
|
@ -1,3 +1,21 @@
|
|||
/*
|
||||
Copyright 2022 CAcert Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 hsm handles hardware security modules.
|
||||
package hsm
|
||||
|
||||
import (
|
||||
|
@ -40,7 +58,7 @@ func GetRootCACertificate(ctx context.Context, label string) (*x509.Certificate,
|
|||
|
||||
caCert, err := sc.GetCADefinition(label)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not get CA definition for label %s: %w", label, err)
|
||||
}
|
||||
|
||||
if !caCert.IsRoot() {
|
||||
|
@ -122,7 +140,7 @@ func GetIntermediaryCACertificate(ctx context.Context, certLabel string) (*x509.
|
|||
|
||||
caCert, err := sc.GetCADefinition(certLabel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not get CA definition for label %s: %w", certLabel, err)
|
||||
}
|
||||
|
||||
if caCert.IsRoot() {
|
||||
|
@ -200,10 +218,15 @@ func GetIntermediaryCACertificate(ctx context.Context, certLabel string) (*x509.
|
|||
return certificate, nil
|
||||
}
|
||||
|
||||
func generateIntermediaryCACertificate(config *config.SignerConfig, certLabel string, publicKey crypto.PublicKey, template *x509.Certificate) (*x509.Certificate, error) {
|
||||
func generateIntermediaryCACertificate(
|
||||
config *config.SignerConfig,
|
||||
certLabel string,
|
||||
publicKey crypto.PublicKey,
|
||||
template *x509.Certificate,
|
||||
) (*x509.Certificate, error) {
|
||||
parent, err := config.GetParentCA(certLabel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not get parent CA for label %s: %w", certLabel, err)
|
||||
}
|
||||
|
||||
serial, err := randomSerialNumber()
|
||||
|
@ -250,12 +273,12 @@ func generateIntermediaryCACertificate(config *config.SignerConfig, certLabel st
|
|||
}
|
||||
|
||||
func addCertificate(p11Context *crypto11.Context, label string, certificate *x509.Certificate) error {
|
||||
objectId, err := randomObjectId()
|
||||
objectID, err := randomObjectID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = p11Context.ImportCertificateWithLabel(objectId, []byte(label), certificate)
|
||||
err = p11Context.ImportCertificateWithLabel(objectID, []byte(label), certificate)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not import certificate into token: %w", err)
|
||||
}
|
||||
|
@ -265,9 +288,10 @@ func addCertificate(p11Context *crypto11.Context, label string, certificate *x50
|
|||
|
||||
func getKeyPair(ctx context.Context, label string, keyInfo *config.PrivateKeyInfo) (crypto.Signer, error) {
|
||||
sc := GetSignerConfig(ctx)
|
||||
|
||||
cert, err := sc.GetCADefinition(label)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not get CA definition for label %s: %w", label, err)
|
||||
}
|
||||
|
||||
if cert.KeyPair != nil {
|
||||
|
@ -296,48 +320,90 @@ func getKeyPair(ctx context.Context, label string, keyInfo *config.PrivateKeyInf
|
|||
case x509.RSA:
|
||||
keyPair, err = generateRSAKeyPair(p11Context, label, keyInfo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not generate RSA key pair: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
case x509.ECDSA:
|
||||
keyPair, err = generateECDSAKeyPair(p11Context, label, keyInfo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not generate ECDSA key pair: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("could not generate private key with label %s with unsupported key algorithm %s", label, keyInfo.Algorithm)
|
||||
return nil, fmt.Errorf(
|
||||
"could not generate private key with label %s with unsupported key algorithm %s",
|
||||
label,
|
||||
keyInfo.Algorithm,
|
||||
)
|
||||
}
|
||||
|
||||
return keyPair, nil
|
||||
}
|
||||
|
||||
func generateECDSAKeyPair(p11Context *crypto11.Context, label string, keyInfo *config.PrivateKeyInfo) (crypto11.Signer, error) {
|
||||
newObjectId, err := randomObjectId()
|
||||
func generateECDSAKeyPair(
|
||||
p11Context *crypto11.Context,
|
||||
label string,
|
||||
keyInfo *config.PrivateKeyInfo,
|
||||
) (crypto11.Signer, error) {
|
||||
var (
|
||||
err error
|
||||
newObjectID []byte
|
||||
signer crypto11.Signer
|
||||
)
|
||||
|
||||
newObjectID, err = randomObjectID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p11Context.GenerateECDSAKeyPairWithLabel(newObjectId, []byte(label), keyInfo.EccCurve)
|
||||
signer, err = p11Context.GenerateECDSAKeyPairWithLabel(newObjectID, []byte(label), keyInfo.EccCurve)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not generate ECDSA key pair: %w", err)
|
||||
}
|
||||
|
||||
return signer, nil
|
||||
}
|
||||
|
||||
func generateRSAKeyPair(p11Context *crypto11.Context, label string, keyInfo *config.PrivateKeyInfo) (crypto11.Signer, error) {
|
||||
newObjectId, err := randomObjectId()
|
||||
func generateRSAKeyPair(
|
||||
p11Context *crypto11.Context,
|
||||
label string,
|
||||
keyInfo *config.PrivateKeyInfo,
|
||||
) (crypto11.Signer, error) {
|
||||
var (
|
||||
err error
|
||||
newObjectID []byte
|
||||
signer crypto11.Signer
|
||||
)
|
||||
|
||||
newObjectID, err = randomObjectID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return p11Context.GenerateRSAKeyPairWithLabel(newObjectId, []byte(label), keyInfo.RSABits)
|
||||
signer, err = p11Context.GenerateRSAKeyPairWithLabel(newObjectID, []byte(label), keyInfo.RSABits)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not generate RSA key pair: %w", err)
|
||||
}
|
||||
|
||||
return signer, nil
|
||||
}
|
||||
|
||||
func randomObjectId() ([]byte, error) {
|
||||
result := make([]byte, 20)
|
||||
func randomObjectID() ([]byte, error) {
|
||||
const objectIDSize = 20
|
||||
|
||||
result := make([]byte, objectIDSize)
|
||||
|
||||
_, err := rand.Read(result)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create new random object id: %w", err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func generateRootCACertificate(certFile string, keyPair crypto.Signer, template *x509.Certificate) (*x509.Certificate, error) {
|
||||
func generateRootCACertificate(
|
||||
certFile string,
|
||||
keyPair crypto.Signer,
|
||||
template *x509.Certificate,
|
||||
) (*x509.Certificate, error) {
|
||||
serial, err := randomSerialNumber()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -414,6 +480,7 @@ func certificateMatches(certificate *x509.Certificate, key crypto.Signer) bool {
|
|||
certificate.PublicKey,
|
||||
key.Public(),
|
||||
)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -423,6 +490,7 @@ func loadCertificate(certFile string) (*x509.Certificate, error) {
|
|||
if errors.Is(err, syscall.ENOENT) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("could not get info for %s: %w", certFile, err)
|
||||
}
|
||||
|
||||
|
@ -453,10 +521,10 @@ func loadCertificate(certFile string) (*x509.Certificate, error) {
|
|||
}
|
||||
|
||||
func randomSerialNumber() (*big.Int, error) {
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not generate serial number: %w", err)
|
||||
}
|
||||
|
||||
return serialNumber, nil
|
||||
}
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
Copyright 2022 CAcert Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 hsm
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
Copyright 2022 CAcert Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 hsm
|
||||
|
||||
import (
|
||||
|
@ -39,13 +56,15 @@ func prepareCrypto11Context(ctx context.Context, label string) (*crypto11.Contex
|
|||
|
||||
p11Context, err = crypto11.Configure(p11Config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not configure PKCS#11 library: %v", err)
|
||||
return nil, fmt.Errorf("could not configure PKCS#11 library: %w", err)
|
||||
}
|
||||
|
||||
return p11Context, nil
|
||||
}
|
||||
|
||||
func getPin(p11Config *crypto11.Config) (string, error) {
|
||||
var err error
|
||||
|
||||
tokenPinEnv := strings.ReplaceAll(p11Config.TokenLabel, "-", "_")
|
||||
tokenPinEnv = strings.ReplaceAll(tokenPinEnv, " ", "_")
|
||||
tokenPinEnv = strings.ToUpper(tokenPinEnv)
|
||||
|
@ -59,14 +78,20 @@ func getPin(p11Config *crypto11.Config) (string, error) {
|
|||
return "", errors.New("stdin is not a terminal")
|
||||
}
|
||||
|
||||
fmt.Printf("Enter PIN for token %s: ", p11Config.TokenLabel)
|
||||
_, err = fmt.Fprintf(os.Stdout, "Enter PIN for token %s: ", p11Config.TokenLabel)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not write PIN prompt: %w", err)
|
||||
}
|
||||
|
||||
bytePin, err := term.ReadPassword(syscall.Stdin)
|
||||
if err != nil {
|
||||
return "", errors.New("could not read PIN")
|
||||
return "", fmt.Errorf("could not read PIN: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
_, err = fmt.Fprintln(os.Stdout)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not write to stdout: %w", err)
|
||||
}
|
||||
|
||||
pin = string(bytePin)
|
||||
}
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
Copyright 2021-2022 CAcert Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 signing
|
||||
|
||||
type Repository interface {
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
Copyright 2021-2022 CAcert Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 signing
|
||||
|
||||
type OpenPGPSigning struct{}
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
Copyright 2021-2022 CAcert Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 openssl
|
||||
|
||||
import (
|
||||
|
@ -27,6 +44,15 @@ const (
|
|||
certificateExpired indexStatus = "E"
|
||||
)
|
||||
|
||||
const (
|
||||
posStatus int = iota
|
||||
posExpiry
|
||||
posRevocation
|
||||
posSerial
|
||||
posFilename
|
||||
posSubject
|
||||
)
|
||||
|
||||
// An indexEntry represents a line in an openssl ca compatible index.txt file
|
||||
// a format specification is available at https://pki-tutorial.readthedocs.io/en/latest/cadb.html
|
||||
type indexEntry struct {
|
||||
|
@ -134,6 +160,7 @@ func (r *Repository) StoreRevocation(revoked *pkix.RevokedCertificate) error {
|
|||
entry.markRevoked(revoked.RevocationTime, reason)
|
||||
|
||||
err = r.writeIndex()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -189,6 +216,7 @@ func (r *Repository) RevokedCertificates() ([]pkix.RevokedCertificate, error) {
|
|||
}
|
||||
|
||||
result := make([]pkix.RevokedCertificate, 0)
|
||||
|
||||
for _, entry := range r.entries {
|
||||
if entry.revokedAt != nil {
|
||||
result = append(result, pkix.RevokedCertificate{
|
||||
|
@ -203,7 +231,9 @@ func (r *Repository) RevokedCertificates() ([]pkix.RevokedCertificate, error) {
|
|||
}
|
||||
|
||||
func (r *Repository) loadIndex() error {
|
||||
entries := make([]indexEntry, 0, 100)
|
||||
const reservedEntries = 100 // use 100 as base capacity to avoid unnecessary array resizing
|
||||
|
||||
entries := make([]indexEntry, 0, reservedEntries)
|
||||
|
||||
f, err := os.Open(r.indexFileName)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
|
@ -224,10 +254,12 @@ func (r *Repository) loadIndex() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
entries = append(entries, *indexEntry)
|
||||
}
|
||||
|
||||
if err := indexScanner.Err(); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("could not scan index file: %w", err)
|
||||
}
|
||||
|
||||
r.entries = entries
|
||||
|
@ -240,6 +272,7 @@ func (r *Repository) writeIndex() error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("could not create index file %s: %w", r.indexFileName, err)
|
||||
}
|
||||
|
||||
defer func(f *os.File) {
|
||||
_ = f.Close()
|
||||
}(f)
|
||||
|
@ -251,6 +284,7 @@ func (r *Repository) writeIndex() error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("could not write entry for serial %s: %w", entry.serialNumber, err)
|
||||
}
|
||||
|
||||
if i < len(r.entries)-1 {
|
||||
_, err = w.WriteString("\n")
|
||||
if err != nil {
|
||||
|
@ -258,6 +292,7 @@ func (r *Repository) writeIndex() error {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = w.Flush()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not write to %s: %w", r.indexFileName, err)
|
||||
|
@ -278,11 +313,11 @@ func (r *Repository) addIndexEntry(ie *indexEntry) error {
|
|||
}
|
||||
|
||||
func (r *Repository) newIndexEntryFromLine(text string) (*indexEntry, error) {
|
||||
const expectedFieldNumber = 6
|
||||
|
||||
var err error
|
||||
|
||||
fields := strings.Split(text, "\t")
|
||||
|
||||
const expectedFieldNumber = 6
|
||||
fields := strings.SplitN(text, "\t", expectedFieldNumber)
|
||||
if len(fields) != expectedFieldNumber {
|
||||
return nil, fmt.Errorf(
|
||||
"unexpected number of fields %d instead of %d",
|
||||
|
@ -291,59 +326,63 @@ func (r *Repository) newIndexEntryFromLine(text string) (*indexEntry, error) {
|
|||
)
|
||||
}
|
||||
|
||||
expirationParsed, err := time.Parse(TimeSpec, fields[1])
|
||||
expirationParsed, err := time.Parse(TimeSpec, fields[posExpiry])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not parse expiration time %s: %w", fields[posExpiry], err)
|
||||
}
|
||||
|
||||
var revocationTimeParsed time.Time
|
||||
var revocationReason revoking.CRLReason
|
||||
var (
|
||||
revocationTimeParsed time.Time
|
||||
revocationReason revoking.CRLReason
|
||||
)
|
||||
|
||||
if fields[2] != "" {
|
||||
var timeString string
|
||||
if strings.Contains(fields[2], ",") {
|
||||
parts := strings.SplitN(fields[2], ",", 2)
|
||||
timeString = parts[0]
|
||||
revocationReason = revoking.ParseReason(parts[1])
|
||||
} else {
|
||||
timeString = fields[2]
|
||||
if fields[posRevocation] != "" {
|
||||
timeString, reasonText, found := strings.Cut(fields[revocationReason], ",")
|
||||
if found {
|
||||
revocationReason = revoking.ParseReason(reasonText)
|
||||
}
|
||||
|
||||
revocationTimeParsed, err = time.Parse(TimeSpec, timeString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not parse revocation time %s: %w", timeString, err)
|
||||
}
|
||||
}
|
||||
|
||||
serialParsed := new(big.Int)
|
||||
if _, ok := serialParsed.SetString(fields[3], 16); !ok {
|
||||
if _, ok := serialParsed.SetString(fields[posSerial], 16); !ok {
|
||||
return nil, fmt.Errorf("could not parse serial number %s", fields[3])
|
||||
}
|
||||
|
||||
fileNameParsed := "unknown"
|
||||
if fields[4] != "" {
|
||||
_, err = os.Stat(path.Join(path.Dir(r.indexFileName), fields[4]))
|
||||
|
||||
if fields[posFilename] != "" {
|
||||
certificateFile := path.Join(path.Dir(r.indexFileName), fields[posFilename])
|
||||
|
||||
_, err = os.Stat(certificateFile)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not check certificate file %s: %w", certificateFile, err)
|
||||
}
|
||||
fileNameParsed = fields[4]
|
||||
|
||||
fileNameParsed = fields[posFilename]
|
||||
}
|
||||
|
||||
return &indexEntry{
|
||||
statusFlag: indexStatus(fields[0]),
|
||||
statusFlag: indexStatus(fields[posStatus]),
|
||||
expiresAt: expirationParsed,
|
||||
revokedAt: &revocationTimeParsed,
|
||||
revocationReason: revocationReason,
|
||||
serialNumber: serialParsed,
|
||||
fileName: fileNameParsed,
|
||||
certificateSubjectDN: fields[5],
|
||||
certificateSubjectDN: fields[posSubject],
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewFileRepository(baseDirectory string) (*Repository, error) {
|
||||
err := os.Chdir(baseDirectory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not change to base directory %s: %w", baseDirectory, err)
|
||||
}
|
||||
|
||||
return &Repository{
|
||||
indexFileName: path.Join(baseDirectory, "index.txt"),
|
||||
lock: &sync.Mutex{},
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
Copyright 2021-2022 CAcert Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 openssl_test
|
||||
|
||||
import (
|
||||
|
@ -11,10 +28,11 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"git.cacert.org/cacert-gosigner/pkg/x509/openssl"
|
||||
"git.cacert.org/cacert-gosigner/pkg/x509/revoking"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"git.cacert.org/cacert-gosigner/pkg/x509/openssl"
|
||||
"git.cacert.org/cacert-gosigner/pkg/x509/revoking"
|
||||
)
|
||||
|
||||
func TestStoreRevocation(t *testing.T) {
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
Copyright 2021-2022 CAcert Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 revoking
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,21 @@
|
|||
/*
|
||||
Copyright 2021-2022 CAcert Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 revoking takes care of handling certificate revocation requests.
|
||||
package revoking
|
||||
|
||||
import (
|
||||
|
@ -85,12 +103,6 @@ type RevokeCertificate struct {
|
|||
reason CRLReason
|
||||
}
|
||||
|
||||
type CertificateRevoked struct {
|
||||
serialNumber *big.Int
|
||||
revocationTime time.Time
|
||||
reason string
|
||||
}
|
||||
|
||||
type CRLInformation struct {
|
||||
CRL []byte // DER encoded CRL
|
||||
}
|
||||
|
@ -103,7 +115,7 @@ func (r *X509Revoking) Revoke(revokeCertificate *RevokeCertificate) (*pkix.Revok
|
|||
}
|
||||
|
||||
if err := r.repository.StoreRevocation(revoked); err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not store revocation %w", err)
|
||||
}
|
||||
|
||||
return revoked, nil
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
Copyright 2021-2022 CAcert Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 revoking
|
||||
|
||||
import (
|
||||
|
@ -20,8 +37,10 @@ func (t *testRepo) RevokedCertificates() ([]pkix.RevokedCertificate, error) {
|
|||
result := make([]pkix.RevokedCertificate, len(t.revoked))
|
||||
|
||||
for i, s := range t.revoked {
|
||||
serialNumber := s
|
||||
|
||||
result[i] = pkix.RevokedCertificate{
|
||||
SerialNumber: &s,
|
||||
SerialNumber: &serialNumber,
|
||||
RevocationTime: time.Now(),
|
||||
}
|
||||
}
|
||||
|
@ -31,15 +50,18 @@ func (t *testRepo) RevokedCertificates() ([]pkix.RevokedCertificate, error) {
|
|||
|
||||
func (t *testRepo) StoreRevocation(revoked *pkix.RevokedCertificate) error {
|
||||
t.revoked = append(t.revoked, *revoked.SerialNumber)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func randomSerial(t *testing.T) *big.Int {
|
||||
t.Helper()
|
||||
|
||||
serial, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
|
||||
if err != nil {
|
||||
t.Fatalf("could not generate random serial number: %v", err)
|
||||
}
|
||||
|
||||
return serial
|
||||
}
|
||||
|
||||
|
@ -50,12 +72,14 @@ func TestRevoking(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("could not generate key pair: %v", err)
|
||||
}
|
||||
|
||||
caTemplate := &x509.Certificate{Subject: pkix.Name{CommonName: "Test CA"}, SerialNumber: randomSerial(t)}
|
||||
|
||||
certificateBytes, err := x509.CreateCertificate(rand.Reader, caTemplate, caTemplate, caKey.Public(), caKey)
|
||||
if err != nil {
|
||||
t.Fatalf("could not self-sign CA certificate: %v", err)
|
||||
}
|
||||
|
||||
caCertificate, err := x509.ParseCertificate(certificateBytes)
|
||||
if err != nil {
|
||||
t.Fatalf("could not create test CA certificate: %v", err)
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
Copyright 2021-2022 CAcert Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 signing
|
||||
|
||||
import "crypto/x509"
|
||||
|
|
|
@ -1,3 +1,21 @@
|
|||
/*
|
||||
Copyright 2021-2022 CAcert Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 signing takes care of X.509 certificate signing.
|
||||
package signing
|
||||
|
||||
import (
|
||||
|
@ -10,7 +28,7 @@ type SignerRequest struct {
|
|||
CSR *x509.CertificateRequest
|
||||
SubjectDN pkix.Name
|
||||
Emails []string
|
||||
DnsNames []string
|
||||
DNSNames []string
|
||||
Duration time.Duration
|
||||
SignatureAlgorithm x509.SignatureAlgorithm
|
||||
}
|
||||
|
@ -26,7 +44,7 @@ func NewSignerRequest(
|
|||
CSR: csr,
|
||||
SubjectDN: subjectDN,
|
||||
Emails: emails,
|
||||
DnsNames: dnsNames,
|
||||
DNSNames: dnsNames,
|
||||
Duration: duration,
|
||||
SignatureAlgorithm: signatureAlgorithm,
|
||||
}
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
Copyright 2021-2022 CAcert Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 signing
|
||||
|
||||
import (
|
||||
|
@ -16,8 +33,6 @@ func NewX509Signing(signer Signer, repo Repository) *X509Signing {
|
|||
return &X509Signing{signer: signer, repo: repo}
|
||||
}
|
||||
|
||||
type CertificatePolicyId int
|
||||
|
||||
type RequestSignature struct {
|
||||
rawCSRData []byte
|
||||
subjectCommonName string
|
||||
|
@ -70,12 +85,12 @@ func (x *X509Signing) Sign(signingRequest *RequestSignature) (*CertificateSigned
|
|||
),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not sign certificate: %w", err)
|
||||
}
|
||||
|
||||
err = x.repo.StoreCertificate(certificateFromSigner.Certificate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not store certificate: %w", err)
|
||||
}
|
||||
|
||||
return &CertificateSigned{certificate: certificateFromSigner.Certificate}, nil
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
Copyright 2021-2022 CAcert Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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 signing_test
|
||||
|
||||
import (
|
||||
|
@ -6,12 +23,14 @@ import (
|
|||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.cacert.org/cacert-gosigner/pkg/x509/signing"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"git.cacert.org/cacert-gosigner/pkg/x509/signing"
|
||||
)
|
||||
|
||||
type testRepo struct {
|
||||
|
@ -20,6 +39,7 @@ type testRepo struct {
|
|||
|
||||
func (r *testRepo) StoreCertificate(certificate *x509.Certificate) error {
|
||||
r.certs[certificate.SerialNumber.Text(16)] = *certificate
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -35,10 +55,12 @@ func newTestSignerResponse(certificate *x509.Certificate) *signing.SignerRespons
|
|||
|
||||
func randomSerial(t *testing.T) *big.Int {
|
||||
t.Helper()
|
||||
|
||||
serial, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
|
||||
if err != nil {
|
||||
t.Fatalf("could not generate random serial number: %v", err)
|
||||
}
|
||||
|
||||
return serial
|
||||
}
|
||||
|
||||
|
@ -57,12 +79,12 @@ func (s *testSigner) SignCertificate(request *signing.SignerRequest) (*signing.S
|
|||
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, template, s.certificate, request.CSR.PublicKey, s.key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not sign certificate: %w", err)
|
||||
}
|
||||
|
||||
certificate, err := x509.ParseCertificate(certBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("could not parse certificate: %w", err)
|
||||
}
|
||||
|
||||
return newTestSignerResponse(certificate), nil
|
||||
|
@ -79,12 +101,21 @@ func TestSigning(t *testing.T) {
|
|||
}
|
||||
|
||||
csrTemplate := &x509.CertificateRequest{PublicKey: csrKey.Public()}
|
||||
|
||||
csrBytes, err := x509.CreateCertificateRequest(rand.Reader, csrTemplate, csrKey)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
testRequest := signing.NewRequestSignature(csrBytes, "Test Subject", []string{"test@example.org"}, nil, 365*24*time.Hour, x509.SHA384WithRSA)
|
||||
testRequest := signing.NewRequestSignature(
|
||||
csrBytes,
|
||||
"Test Subject",
|
||||
[]string{"test@example.org"},
|
||||
nil,
|
||||
365*24*time.Hour,
|
||||
x509.SHA384WithRSA,
|
||||
)
|
||||
|
||||
signed, err := s.Sign(testRequest)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
@ -98,19 +129,23 @@ func TestSigning(t *testing.T) {
|
|||
|
||||
func newTestSigner(t *testing.T) *testSigner {
|
||||
t.Helper()
|
||||
|
||||
caKey, err := rsa.GenerateKey(rand.Reader, 3072)
|
||||
if err != nil {
|
||||
t.Fatalf("could not generate key pair: %v", err)
|
||||
}
|
||||
|
||||
caTemplate := &x509.Certificate{Subject: pkix.Name{CommonName: "Test CA"}, SerialNumber: randomSerial(t)}
|
||||
|
||||
certificateBytes, err := x509.CreateCertificate(rand.Reader, caTemplate, caTemplate, caKey.Public(), caKey)
|
||||
if err != nil {
|
||||
t.Fatalf("could not self-sign CA certificate: %v", err)
|
||||
}
|
||||
|
||||
caCertificate, err := x509.ParseCertificate(certificateBytes)
|
||||
if err != nil {
|
||||
t.Fatalf("could not create test CA certificate: %v", err)
|
||||
}
|
||||
|
||||
return &testSigner{key: caKey, certificate: caCertificate, t: t}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue