Switch from globalsign/certlint to zmap/zlint (#3745)
Switch linting library to zmap/zlint. ``` github.com/zmap/zlint$ go test ./... ok github.com/zmap/zlint 0.190s ? github.com/zmap/zlint/cmd/zlint [no test files] ok github.com/zmap/zlint/lints 0.216s ok github.com/zmap/zlint/util (cached) ```
This commit is contained in:
parent
b29fe6559d
commit
72949d5915
|
|
@ -6,250 +6,95 @@
|
|||
"./..."
|
||||
],
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "github.com/asaskevich/govalidator",
|
||||
"Comment": "v6-25-g73945b6",
|
||||
"Rev": "73945b6115bfbbcc57d89b7316e28109364124e1"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/beorn7/perks/quantile",
|
||||
"Rev": "3ac7bf7a47d159a033b107610db8a1b6575507a4"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/auth",
|
||||
"Comment": "1.3.1-12-g8d76cdf",
|
||||
"Comment": "1.3.1-12-g8d76cdf7",
|
||||
"Rev": "8d76cdf742f917ba01551bf7ca41f8a7f82da15a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/certdb",
|
||||
"Comment": "1.3.1-12-g8d76cdf",
|
||||
"Comment": "1.3.1-12-g8d76cdf7",
|
||||
"Rev": "8d76cdf742f917ba01551bf7ca41f8a7f82da15a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/certdb/dbconf",
|
||||
"Comment": "1.3.1-12-g8d76cdf",
|
||||
"Comment": "1.3.1-12-g8d76cdf7",
|
||||
"Rev": "8d76cdf742f917ba01551bf7ca41f8a7f82da15a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/certdb/sql",
|
||||
"Comment": "1.3.1-12-g8d76cdf",
|
||||
"Comment": "1.3.1-12-g8d76cdf7",
|
||||
"Rev": "8d76cdf742f917ba01551bf7ca41f8a7f82da15a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/config",
|
||||
"Comment": "1.3.1-12-g8d76cdf",
|
||||
"Comment": "1.3.1-12-g8d76cdf7",
|
||||
"Rev": "8d76cdf742f917ba01551bf7ca41f8a7f82da15a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/crypto/pkcs7",
|
||||
"Comment": "1.3.1-12-g8d76cdf",
|
||||
"Comment": "1.3.1-12-g8d76cdf7",
|
||||
"Rev": "8d76cdf742f917ba01551bf7ca41f8a7f82da15a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/csr",
|
||||
"Comment": "1.3.1-12-g8d76cdf",
|
||||
"Comment": "1.3.1-12-g8d76cdf7",
|
||||
"Rev": "8d76cdf742f917ba01551bf7ca41f8a7f82da15a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/errors",
|
||||
"Comment": "1.3.1-12-g8d76cdf",
|
||||
"Comment": "1.3.1-12-g8d76cdf7",
|
||||
"Rev": "8d76cdf742f917ba01551bf7ca41f8a7f82da15a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/helpers",
|
||||
"Comment": "1.3.1-12-g8d76cdf",
|
||||
"Comment": "1.3.1-12-g8d76cdf7",
|
||||
"Rev": "8d76cdf742f917ba01551bf7ca41f8a7f82da15a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/helpers/derhelpers",
|
||||
"Comment": "1.3.1-12-g8d76cdf",
|
||||
"Comment": "1.3.1-12-g8d76cdf7",
|
||||
"Rev": "8d76cdf742f917ba01551bf7ca41f8a7f82da15a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/info",
|
||||
"Comment": "1.3.1-12-g8d76cdf",
|
||||
"Comment": "1.3.1-12-g8d76cdf7",
|
||||
"Rev": "8d76cdf742f917ba01551bf7ca41f8a7f82da15a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/log",
|
||||
"Comment": "1.3.1-12-g8d76cdf",
|
||||
"Comment": "1.3.1-12-g8d76cdf7",
|
||||
"Rev": "8d76cdf742f917ba01551bf7ca41f8a7f82da15a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/ocsp",
|
||||
"Comment": "1.3.1-12-g8d76cdf",
|
||||
"Comment": "1.3.1-12-g8d76cdf7",
|
||||
"Rev": "8d76cdf742f917ba01551bf7ca41f8a7f82da15a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/ocsp/config",
|
||||
"Comment": "1.3.1-12-g8d76cdf",
|
||||
"Comment": "1.3.1-12-g8d76cdf7",
|
||||
"Rev": "8d76cdf742f917ba01551bf7ca41f8a7f82da15a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/signer",
|
||||
"Comment": "1.3.1-12-g8d76cdf",
|
||||
"Comment": "1.3.1-12-g8d76cdf7",
|
||||
"Rev": "8d76cdf742f917ba01551bf7ca41f8a7f82da15a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/cloudflare/cfssl/signer/local",
|
||||
"Comment": "1.3.1-12-g8d76cdf",
|
||||
"Comment": "1.3.1-12-g8d76cdf7",
|
||||
"Rev": "8d76cdf742f917ba01551bf7ca41f8a7f82da15a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/asn1",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/certdata",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/certificate/aiaissuers",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/certificate/all",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/certificate/basicconstraints",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/certificate/extensions",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/certificate/extkeyusage",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/certificate/internal",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/certificate/issuerdn",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/certificate/keyusage",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/certificate/publickey",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/certificate/publickey/goodkey",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/certificate/publicsuffix",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/certificate/revocation",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/certificate/serialnumber",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/certificate/signaturealgorithm",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/certificate/subject",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/certificate/subjectaltname",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/certificate/validity",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/certificate/version",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/certificate/wildcard",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/extensions/adobetimestamp",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/extensions/all",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/extensions/authorityinfoaccess",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/extensions/authoritykeyid",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/extensions/basicconstraints",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/extensions/crldistributionpoints",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/extensions/ct",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/extensions/extkeyusage",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/extensions/keyusage",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/extensions/nameconstraints",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/extensions/ocspmuststaple",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/extensions/ocspnocheck",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/extensions/pdfrevocation",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/extensions/policyidentifiers",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/extensions/smimecapabilities",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/extensions/subjectaltname",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/checks/extensions/subjectkeyid",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/globalsign/certlint/errors",
|
||||
"Rev": "d4a45be06892f3e664f69892aca79a48df510be0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/go-sql-driver/mysql",
|
||||
"Comment": "v1.3.0-28-g3955978",
|
||||
|
|
@ -342,7 +187,7 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "github.com/jmhodges/clock",
|
||||
"Comment": "v1.1",
|
||||
"Comment": "v1.0-4-g880ee4c",
|
||||
"Rev": "880ee4c335489bc78d01e4d0a254ae880734bc15"
|
||||
},
|
||||
{
|
||||
|
|
@ -366,7 +211,7 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "github.com/matttproud/golang_protobuf_extensions/pbutil",
|
||||
"Comment": "v1.0.1",
|
||||
"Comment": "v1.0.0-2-gc12348c",
|
||||
"Rev": "c12348ce28de40eed0136aa2b644d0ee0650e56c"
|
||||
},
|
||||
{
|
||||
|
|
@ -417,6 +262,34 @@
|
|||
"Comment": "v0.4.0-13-g67ec7c1",
|
||||
"Rev": "67ec7c1b9678b719e579f2118c36fca4e11a0fb1"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/zmap/zcrypto/json",
|
||||
"Rev": "cf96f6a8516687d345fe4ba98b5defa4e823f736"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/zmap/zcrypto/x509",
|
||||
"Rev": "cf96f6a8516687d345fe4ba98b5defa4e823f736"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/zmap/zcrypto/x509/ct",
|
||||
"Rev": "cf96f6a8516687d345fe4ba98b5defa4e823f736"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/zmap/zcrypto/x509/pkix",
|
||||
"Rev": "cf96f6a8516687d345fe4ba98b5defa4e823f736"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/zmap/zlint",
|
||||
"Rev": "9bebe5e32c2c4b27892021e8e2f0459ef3b075ab"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/zmap/zlint/lints",
|
||||
"Rev": "9bebe5e32c2c4b27892021e8e2f0459ef3b075ab"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/zmap/zlint/util",
|
||||
"Rev": "9bebe5e32c2c4b27892021e8e2f0459ef3b075ab"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/crypto/cryptobyte",
|
||||
"Rev": "650f4a345ab4e5b245a3034b110ebc7299e68186"
|
||||
|
|
@ -473,10 +346,6 @@
|
|||
"ImportPath": "golang.org/x/net/internal/timeseries",
|
||||
"Rev": "d11bb6cd8e3c4e60239c9cb20ef68586d74500d0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/publicsuffix",
|
||||
"Rev": "d11bb6cd8e3c4e60239c9cb20ef68586d74500d0"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/net/trace",
|
||||
"Rev": "d11bb6cd8e3c4e60239c9cb20ef68586d74500d0"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
|
|
@ -16,6 +16,9 @@ import (
|
|||
|
||||
"github.com/jmhodges/clock"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/zmap/zcrypto/x509"
|
||||
"github.com/zmap/zlint"
|
||||
"github.com/zmap/zlint/lints"
|
||||
|
||||
"github.com/letsencrypt/boulder/cmd"
|
||||
"github.com/letsencrypt/boulder/core"
|
||||
|
|
@ -24,31 +27,17 @@ import (
|
|||
"github.com/letsencrypt/boulder/metrics"
|
||||
"github.com/letsencrypt/boulder/policy"
|
||||
"github.com/letsencrypt/boulder/sa"
|
||||
|
||||
lintasn1 "github.com/globalsign/certlint/asn1"
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
_ "github.com/globalsign/certlint/checks/certificate/all"
|
||||
_ "github.com/globalsign/certlint/checks/extensions/all"
|
||||
)
|
||||
|
||||
const (
|
||||
good = "valid"
|
||||
bad = "invalid"
|
||||
|
||||
certlintCNError = "commonName field is deprecated"
|
||||
|
||||
filenameLayout = "20060102"
|
||||
|
||||
expectedValidityPeriod = time.Hour * 24 * 90
|
||||
)
|
||||
|
||||
// certlintPSLErrPattern is a regex for matching Certlint error strings
|
||||
// complaining about a CN or SAN matching a public suffix list entry.
|
||||
var certlintPSLErrPattern = regexp.MustCompile(
|
||||
`^Certificate (?:CommonName|subjectAltName) "[a-z0-9*][a-z0-9-.]+" ` +
|
||||
`equals "[a-z0-9][a-z0-9-.]+" from the public suffix list$`)
|
||||
|
||||
// For defense-in-depth in addition to using the PA & its hostnamePolicy to
|
||||
// check domain names we also perform a check against the regex's from the
|
||||
// forbiddenDomains array
|
||||
|
|
@ -196,54 +185,49 @@ func (c *certChecker) processCerts(wg *sync.WaitGroup, badResultsOnly bool) {
|
|||
wg.Done()
|
||||
}
|
||||
|
||||
// Extensions that we allow in certificates
|
||||
var allowedExtensions = map[string]bool{
|
||||
"1.3.6.1.5.5.7.1.1": true, // Authority info access
|
||||
"2.5.29.35": true, // Authority key identifier
|
||||
"2.5.29.19": true, // Basic constraints
|
||||
"2.5.29.32": true, // Certificate policies
|
||||
"2.5.29.31": true, // CRL distribution points
|
||||
"2.5.29.37": true, // Extended key usage
|
||||
"2.5.29.15": true, // Key usage
|
||||
"2.5.29.17": true, // Subject alternative name
|
||||
"2.5.29.14": true, // Subject key identifier
|
||||
"1.3.6.1.4.1.11129.2.4.2": true, // SCT list
|
||||
"1.3.6.1.5.5.7.1.24": true, // TLS feature
|
||||
}
|
||||
|
||||
// For extensions that have a fixed value we check that it contains that value
|
||||
var expectedExtensionContent = map[string][]byte{
|
||||
"1.3.6.1.5.5.7.1.24": []byte{0x30, 0x03, 0x02, 0x01, 0x05}, // Must staple feature
|
||||
}
|
||||
|
||||
func (c *certChecker) checkCert(cert core.Certificate) (problems []string) {
|
||||
// Check digests match
|
||||
if cert.Digest != core.Fingerprint256(cert.DER) {
|
||||
problems = append(problems, "Stored digest doesn't match certificate digest")
|
||||
}
|
||||
|
||||
// Run linter
|
||||
linter := new(lintasn1.Linter)
|
||||
errs := linter.CheckStruct(cert.DER)
|
||||
if errs != nil {
|
||||
for _, err := range errs.List() {
|
||||
problems = append(problems, err.Error())
|
||||
}
|
||||
}
|
||||
d, err := certdata.Load(cert.DER)
|
||||
if err != nil {
|
||||
problems = append(problems, err.Error())
|
||||
}
|
||||
errs = checks.Certificate.Check(d)
|
||||
if errs != nil {
|
||||
for _, err := range errs.List() {
|
||||
// commonName has been deprecated for years, but common practice is still
|
||||
// to include it for compatibility reasons. For instance, Chrome on macOS
|
||||
// until very recently would error on an empty Subject (which is what we
|
||||
// would have if we omitted CommonName). There have been proposals at
|
||||
// CA/Browser Forum for an alternate contentless field whose purpose would
|
||||
// just be to make Subject non-empty, but so far they have not been
|
||||
// successful. If the check error is `certlintCNError`, ignore it.
|
||||
if err.Error() == certlintCNError {
|
||||
continue
|
||||
}
|
||||
// certlint incorrectly flags certificates that have a subj. CN or SAN
|
||||
// exactly equal to a *private* entry on the public suffix list. Since
|
||||
// this is allowed and LE issues certificates for such names we ignore
|
||||
// errors of this form until the upstream bug can be addressed. See
|
||||
// https://github.com/globalsign/certlint/issues/17
|
||||
if certlintPSLErrPattern.MatchString(err.Error()) {
|
||||
continue
|
||||
}
|
||||
problems = append(problems, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Parse certificate
|
||||
parsedCert, err := x509.ParseCertificate(cert.DER)
|
||||
if err != nil {
|
||||
problems = append(problems, fmt.Sprintf("Couldn't parse stored certificate: %s", err))
|
||||
} else {
|
||||
// Run zlint checks
|
||||
results := zlint.LintCertificate(parsedCert)
|
||||
for name, res := range results.Results {
|
||||
// ignore notices and warnings
|
||||
if res.Status >= lints.Error {
|
||||
prob := fmt.Sprintf("zlint %s: %s", res.Status, name)
|
||||
if res.Details != "" {
|
||||
prob = fmt.Sprintf("%s %s", prob, res.Details)
|
||||
}
|
||||
problems = append(problems, prob)
|
||||
}
|
||||
}
|
||||
// Check stored serial is correct
|
||||
storedSerial, err := core.StringToSerial(cert.Serial)
|
||||
if err != nil {
|
||||
|
|
@ -308,6 +292,17 @@ func (c *certChecker) checkCert(cert core.Certificate) (problems []string) {
|
|||
if !reflect.DeepEqual(parsedCert.ExtKeyUsage, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}) {
|
||||
problems = append(problems, "Certificate has incorrect key usage extensions")
|
||||
}
|
||||
|
||||
for _, ext := range parsedCert.Extensions {
|
||||
if _, ok := allowedExtensions[ext.Id.String()]; !ok {
|
||||
problems = append(problems, fmt.Sprintf("Certificate contains an unexpected extension: %s", ext.Id))
|
||||
}
|
||||
if expectedContent, ok := expectedExtensionContent[ext.Id.String()]; ok {
|
||||
if !bytes.Equal(ext.Value, expectedContent) {
|
||||
problems = append(problems, fmt.Sprintf("Certificate extension %s contains unexpected content: has %x, expected %x", ext.Id, ext.Value, expectedContent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return problems
|
||||
}
|
||||
|
|
|
|||
|
|
@ -176,7 +176,8 @@ func TestCheckCert(t *testing.T) {
|
|||
"*.foodnotbombs.mil",
|
||||
// `dev-myqnapcloud.com` is included because it is an exact private
|
||||
// entry on the public suffix list
|
||||
"dev-myqnapcloud.com"},
|
||||
"dev-myqnapcloud.com",
|
||||
},
|
||||
SerialNumber: serial,
|
||||
BasicConstraintsValid: false,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
|
|
@ -211,8 +212,7 @@ func TestCheckCert(t *testing.T) {
|
|||
"Certificate has incorrect key usage extensions": 1,
|
||||
"Certificate has common name >64 characters long (65)": 1,
|
||||
"Policy Authority isn't willing to issue for '*.foodnotbombs.mil': Wildcard names not supported": 1,
|
||||
"commonName exceeding max length of 64": 1,
|
||||
"Certificate contains unknown extension (1.3.3.7)": 1,
|
||||
"Certificate contains an unexpected extension: 1.3.3.7": 1,
|
||||
}
|
||||
for _, p := range problems {
|
||||
_, ok := problemsMap[p]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.1
|
||||
- 1.2
|
||||
- 1.3
|
||||
- 1.4
|
||||
- 1.5
|
||||
- 1.6
|
||||
- tip
|
||||
|
||||
notifications:
|
||||
email:
|
||||
- bwatas@gmail.com
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Alex Saskevich
|
||||
|
||||
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.
|
||||
|
|
@ -0,0 +1,423 @@
|
|||
govalidator
|
||||
===========
|
||||
[](https://gitter.im/asaskevich/govalidator?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [](https://godoc.org/github.com/asaskevich/govalidator) [](https://coveralls.io/r/asaskevich/govalidator?branch=master) [](https://app.wercker.com/project/bykey/1ec990b09ea86c910d5f08b0e02c6043)
|
||||
[](https://travis-ci.org/asaskevich/govalidator) [](https://goreportcard.com/report/github.com/asaskevich/govalidator) [](http://go-search.org/view?id=github.com%2Fasaskevich%2Fgovalidator)
|
||||
|
||||
A package of validators and sanitizers for strings, structs and collections. Based on [validator.js](https://github.com/chriso/validator.js).
|
||||
|
||||
#### Installation
|
||||
Make sure that Go is installed on your computer.
|
||||
Type the following command in your terminal:
|
||||
|
||||
go get github.com/asaskevich/govalidator
|
||||
|
||||
or you can get specified release of the package with `gopkg.in`:
|
||||
|
||||
go get gopkg.in/asaskevich/govalidator.v4
|
||||
|
||||
After it the package is ready to use.
|
||||
|
||||
|
||||
#### Import package in your project
|
||||
Add following line in your `*.go` file:
|
||||
```go
|
||||
import "github.com/asaskevich/govalidator"
|
||||
```
|
||||
If you are unhappy to use long `govalidator`, you can do something like this:
|
||||
```go
|
||||
import (
|
||||
valid "github.com/asaskevich/govalidator"
|
||||
)
|
||||
```
|
||||
|
||||
#### Activate behavior to require all fields have a validation tag by default
|
||||
`SetFieldsRequiredByDefault` causes validation to fail when struct fields do not include validations or are not explicitly marked as exempt (using `valid:"-"` or `valid:"email,optional"`). A good place to activate this is a package init function or the main() function.
|
||||
|
||||
```go
|
||||
import "github.com/asaskevich/govalidator"
|
||||
|
||||
func init() {
|
||||
govalidator.SetFieldsRequiredByDefault(true)
|
||||
}
|
||||
```
|
||||
|
||||
Here's some code to explain it:
|
||||
```go
|
||||
// this struct definition will fail govalidator.ValidateStruct() (and the field values do not matter):
|
||||
type exampleStruct struct {
|
||||
Name string ``
|
||||
Email string `valid:"email"`
|
||||
}
|
||||
|
||||
// this, however, will only fail when Email is empty or an invalid email address:
|
||||
type exampleStruct2 struct {
|
||||
Name string `valid:"-"`
|
||||
Email string `valid:"email"`
|
||||
}
|
||||
|
||||
// lastly, this will only fail when Email is an invalid email address but not when it's empty:
|
||||
type exampleStruct2 struct {
|
||||
Name string `valid:"-"`
|
||||
Email string `valid:"email,optional"`
|
||||
}
|
||||
```
|
||||
|
||||
#### Recent breaking changes (see [#123](https://github.com/asaskevich/govalidator/pull/123))
|
||||
##### Custom validator function signature
|
||||
A context was added as the second parameter, for structs this is the object being validated – this makes dependent validation possible.
|
||||
```go
|
||||
import "github.com/asaskevich/govalidator"
|
||||
|
||||
// old signature
|
||||
func(i interface{}) bool
|
||||
|
||||
// new signature
|
||||
func(i interface{}, o interface{}) bool
|
||||
```
|
||||
|
||||
##### Adding a custom validator
|
||||
This was changed to prevent data races when accessing custom validators.
|
||||
```go
|
||||
import "github.com/asaskevich/govalidator"
|
||||
|
||||
// before
|
||||
govalidator.CustomTypeTagMap["customByteArrayValidator"] = CustomTypeValidator(func(i interface{}, o interface{}) bool {
|
||||
// ...
|
||||
})
|
||||
|
||||
// after
|
||||
govalidator.CustomTypeTagMap.Set("customByteArrayValidator", CustomTypeValidator(func(i interface{}, o interface{}) bool {
|
||||
// ...
|
||||
}))
|
||||
```
|
||||
|
||||
#### List of functions:
|
||||
```go
|
||||
func Abs(value float64) float64
|
||||
func BlackList(str, chars string) string
|
||||
func ByteLength(str string, params ...string) bool
|
||||
func CamelCaseToUnderscore(str string) string
|
||||
func Contains(str, substring string) bool
|
||||
func Count(array []interface{}, iterator ConditionIterator) int
|
||||
func Each(array []interface{}, iterator Iterator)
|
||||
func ErrorByField(e error, field string) string
|
||||
func ErrorsByField(e error) map[string]string
|
||||
func Filter(array []interface{}, iterator ConditionIterator) []interface{}
|
||||
func Find(array []interface{}, iterator ConditionIterator) interface{}
|
||||
func GetLine(s string, index int) (string, error)
|
||||
func GetLines(s string) []string
|
||||
func InRange(value, left, right float64) bool
|
||||
func IsASCII(str string) bool
|
||||
func IsAlpha(str string) bool
|
||||
func IsAlphanumeric(str string) bool
|
||||
func IsBase64(str string) bool
|
||||
func IsByteLength(str string, min, max int) bool
|
||||
func IsCIDR(str string) bool
|
||||
func IsCreditCard(str string) bool
|
||||
func IsDNSName(str string) bool
|
||||
func IsDataURI(str string) bool
|
||||
func IsDialString(str string) bool
|
||||
func IsDivisibleBy(str, num string) bool
|
||||
func IsEmail(str string) bool
|
||||
func IsFilePath(str string) (bool, int)
|
||||
func IsFloat(str string) bool
|
||||
func IsFullWidth(str string) bool
|
||||
func IsHalfWidth(str string) bool
|
||||
func IsHexadecimal(str string) bool
|
||||
func IsHexcolor(str string) bool
|
||||
func IsHost(str string) bool
|
||||
func IsIP(str string) bool
|
||||
func IsIPv4(str string) bool
|
||||
func IsIPv6(str string) bool
|
||||
func IsISBN(str string, version int) bool
|
||||
func IsISBN10(str string) bool
|
||||
func IsISBN13(str string) bool
|
||||
func IsISO3166Alpha2(str string) bool
|
||||
func IsISO3166Alpha3(str string) bool
|
||||
func IsISO693Alpha2(str string) bool
|
||||
func IsISO693Alpha3b(str string) bool
|
||||
func IsISO4217(str string) bool
|
||||
func IsIn(str string, params ...string) bool
|
||||
func IsInt(str string) bool
|
||||
func IsJSON(str string) bool
|
||||
func IsLatitude(str string) bool
|
||||
func IsLongitude(str string) bool
|
||||
func IsLowerCase(str string) bool
|
||||
func IsMAC(str string) bool
|
||||
func IsMongoID(str string) bool
|
||||
func IsMultibyte(str string) bool
|
||||
func IsNatural(value float64) bool
|
||||
func IsNegative(value float64) bool
|
||||
func IsNonNegative(value float64) bool
|
||||
func IsNonPositive(value float64) bool
|
||||
func IsNull(str string) bool
|
||||
func IsNumeric(str string) bool
|
||||
func IsPort(str string) bool
|
||||
func IsPositive(value float64) bool
|
||||
func IsPrintableASCII(str string) bool
|
||||
func IsRFC3339(str string) bool
|
||||
func IsRGBcolor(str string) bool
|
||||
func IsRequestURI(rawurl string) bool
|
||||
func IsRequestURL(rawurl string) bool
|
||||
func IsSSN(str string) bool
|
||||
func IsSemver(str string) bool
|
||||
func IsTime(str string, format string) bool
|
||||
func IsURL(str string) bool
|
||||
func IsUTFDigit(str string) bool
|
||||
func IsUTFLetter(str string) bool
|
||||
func IsUTFLetterNumeric(str string) bool
|
||||
func IsUTFNumeric(str string) bool
|
||||
func IsUUID(str string) bool
|
||||
func IsUUIDv3(str string) bool
|
||||
func IsUUIDv4(str string) bool
|
||||
func IsUUIDv5(str string) bool
|
||||
func IsUpperCase(str string) bool
|
||||
func IsVariableWidth(str string) bool
|
||||
func IsWhole(value float64) bool
|
||||
func LeftTrim(str, chars string) string
|
||||
func Map(array []interface{}, iterator ResultIterator) []interface{}
|
||||
func Matches(str, pattern string) bool
|
||||
func NormalizeEmail(str string) (string, error)
|
||||
func PadBoth(str string, padStr string, padLen int) string
|
||||
func PadLeft(str string, padStr string, padLen int) string
|
||||
func PadRight(str string, padStr string, padLen int) string
|
||||
func Range(str string, params ...string) bool
|
||||
func RemoveTags(s string) string
|
||||
func ReplacePattern(str, pattern, replace string) string
|
||||
func Reverse(s string) string
|
||||
func RightTrim(str, chars string) string
|
||||
func RuneLength(str string, params ...string) bool
|
||||
func SafeFileName(str string) string
|
||||
func SetFieldsRequiredByDefault(value bool)
|
||||
func Sign(value float64) float64
|
||||
func StringLength(str string, params ...string) bool
|
||||
func StringMatches(s string, params ...string) bool
|
||||
func StripLow(str string, keepNewLines bool) string
|
||||
func ToBoolean(str string) (bool, error)
|
||||
func ToFloat(str string) (float64, error)
|
||||
func ToInt(str string) (int64, error)
|
||||
func ToJSON(obj interface{}) (string, error)
|
||||
func ToString(obj interface{}) string
|
||||
func Trim(str, chars string) string
|
||||
func Truncate(str string, length int, ending string) string
|
||||
func UnderscoreToCamelCase(s string) string
|
||||
func ValidateStruct(s interface{}) (bool, error)
|
||||
func WhiteList(str, chars string) string
|
||||
type ConditionIterator
|
||||
type CustomTypeValidator
|
||||
type Error
|
||||
func (e Error) Error() string
|
||||
type Errors
|
||||
func (es Errors) Error() string
|
||||
func (es Errors) Errors() []error
|
||||
type ISO3166Entry
|
||||
type Iterator
|
||||
type ParamValidator
|
||||
type ResultIterator
|
||||
type UnsupportedTypeError
|
||||
func (e *UnsupportedTypeError) Error() string
|
||||
type Validator
|
||||
```
|
||||
|
||||
#### Examples
|
||||
###### IsURL
|
||||
```go
|
||||
println(govalidator.IsURL(`http://user@pass:domain.com/path/page`))
|
||||
```
|
||||
###### ToString
|
||||
```go
|
||||
type User struct {
|
||||
FirstName string
|
||||
LastName string
|
||||
}
|
||||
|
||||
str := govalidator.ToString(&User{"John", "Juan"})
|
||||
println(str)
|
||||
```
|
||||
###### Each, Map, Filter, Count for slices
|
||||
Each iterates over the slice/array and calls Iterator for every item
|
||||
```go
|
||||
data := []interface{}{1, 2, 3, 4, 5}
|
||||
var fn govalidator.Iterator = func(value interface{}, index int) {
|
||||
println(value.(int))
|
||||
}
|
||||
govalidator.Each(data, fn)
|
||||
```
|
||||
```go
|
||||
data := []interface{}{1, 2, 3, 4, 5}
|
||||
var fn govalidator.ResultIterator = func(value interface{}, index int) interface{} {
|
||||
return value.(int) * 3
|
||||
}
|
||||
_ = govalidator.Map(data, fn) // result = []interface{}{1, 6, 9, 12, 15}
|
||||
```
|
||||
```go
|
||||
data := []interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||
var fn govalidator.ConditionIterator = func(value interface{}, index int) bool {
|
||||
return value.(int)%2 == 0
|
||||
}
|
||||
_ = govalidator.Filter(data, fn) // result = []interface{}{2, 4, 6, 8, 10}
|
||||
_ = govalidator.Count(data, fn) // result = 5
|
||||
```
|
||||
###### ValidateStruct [#2](https://github.com/asaskevich/govalidator/pull/2)
|
||||
If you want to validate structs, you can use tag `valid` for any field in your structure. All validators used with this field in one tag are separated by comma. If you want to skip validation, place `-` in your tag. If you need a validator that is not on the list below, you can add it like this:
|
||||
```go
|
||||
govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool {
|
||||
return str == "duck"
|
||||
})
|
||||
```
|
||||
For completely custom validators (interface-based), see below.
|
||||
|
||||
Here is a list of available validators for struct fields (validator - used function):
|
||||
```go
|
||||
"email": IsEmail,
|
||||
"url": IsURL,
|
||||
"dialstring": IsDialString,
|
||||
"requrl": IsRequestURL,
|
||||
"requri": IsRequestURI,
|
||||
"alpha": IsAlpha,
|
||||
"utfletter": IsUTFLetter,
|
||||
"alphanum": IsAlphanumeric,
|
||||
"utfletternum": IsUTFLetterNumeric,
|
||||
"numeric": IsNumeric,
|
||||
"utfnumeric": IsUTFNumeric,
|
||||
"utfdigit": IsUTFDigit,
|
||||
"hexadecimal": IsHexadecimal,
|
||||
"hexcolor": IsHexcolor,
|
||||
"rgbcolor": IsRGBcolor,
|
||||
"lowercase": IsLowerCase,
|
||||
"uppercase": IsUpperCase,
|
||||
"int": IsInt,
|
||||
"float": IsFloat,
|
||||
"null": IsNull,
|
||||
"uuid": IsUUID,
|
||||
"uuidv3": IsUUIDv3,
|
||||
"uuidv4": IsUUIDv4,
|
||||
"uuidv5": IsUUIDv5,
|
||||
"creditcard": IsCreditCard,
|
||||
"isbn10": IsISBN10,
|
||||
"isbn13": IsISBN13,
|
||||
"json": IsJSON,
|
||||
"multibyte": IsMultibyte,
|
||||
"ascii": IsASCII,
|
||||
"printableascii": IsPrintableASCII,
|
||||
"fullwidth": IsFullWidth,
|
||||
"halfwidth": IsHalfWidth,
|
||||
"variablewidth": IsVariableWidth,
|
||||
"base64": IsBase64,
|
||||
"datauri": IsDataURI,
|
||||
"ip": IsIP,
|
||||
"port": IsPort,
|
||||
"ipv4": IsIPv4,
|
||||
"ipv6": IsIPv6,
|
||||
"dns": IsDNSName,
|
||||
"host": IsHost,
|
||||
"mac": IsMAC,
|
||||
"latitude": IsLatitude,
|
||||
"longitude": IsLongitude,
|
||||
"ssn": IsSSN,
|
||||
"semver": IsSemver,
|
||||
"rfc3339": IsRFC3339,
|
||||
"ISO3166Alpha2": IsISO3166Alpha2,
|
||||
"ISO3166Alpha3": IsISO3166Alpha3,
|
||||
```
|
||||
Validators with parameters
|
||||
|
||||
```go
|
||||
"range(min|max)": Range,
|
||||
"length(min|max)": ByteLength,
|
||||
"runelength(min|max)": RuneLength,
|
||||
"matches(pattern)": StringMatches,
|
||||
"in(string1|string2|...|stringN)": IsIn,
|
||||
```
|
||||
|
||||
And here is small example of usage:
|
||||
```go
|
||||
type Post struct {
|
||||
Title string `valid:"alphanum,required"`
|
||||
Message string `valid:"duck,ascii"`
|
||||
AuthorIP string `valid:"ipv4"`
|
||||
Date string `valid:"-"`
|
||||
}
|
||||
post := &Post{
|
||||
Title: "My Example Post",
|
||||
Message: "duck",
|
||||
AuthorIP: "123.234.54.3",
|
||||
}
|
||||
|
||||
// Add your own struct validation tags
|
||||
govalidator.TagMap["duck"] = govalidator.Validator(func(str string) bool {
|
||||
return str == "duck"
|
||||
})
|
||||
|
||||
result, err := govalidator.ValidateStruct(post)
|
||||
if err != nil {
|
||||
println("error: " + err.Error())
|
||||
}
|
||||
println(result)
|
||||
```
|
||||
###### WhiteList
|
||||
```go
|
||||
// Remove all characters from string ignoring characters between "a" and "z"
|
||||
println(govalidator.WhiteList("a3a43a5a4a3a2a23a4a5a4a3a4", "a-z") == "aaaaaaaaaaaa")
|
||||
```
|
||||
|
||||
###### Custom validation functions
|
||||
Custom validation using your own domain specific validators is also available - here's an example of how to use it:
|
||||
```go
|
||||
import "github.com/asaskevich/govalidator"
|
||||
|
||||
type CustomByteArray [6]byte // custom types are supported and can be validated
|
||||
|
||||
type StructWithCustomByteArray struct {
|
||||
ID CustomByteArray `valid:"customByteArrayValidator,customMinLengthValidator"` // multiple custom validators are possible as well and will be evaluated in sequence
|
||||
Email string `valid:"email"`
|
||||
CustomMinLength int `valid:"-"`
|
||||
}
|
||||
|
||||
govalidator.CustomTypeTagMap.Set("customByteArrayValidator", CustomTypeValidator(func(i interface{}, context interface{}) bool {
|
||||
switch v := context.(type) { // you can type switch on the context interface being validated
|
||||
case StructWithCustomByteArray:
|
||||
// you can check and validate against some other field in the context,
|
||||
// return early or not validate against the context at all – your choice
|
||||
case SomeOtherType:
|
||||
// ...
|
||||
default:
|
||||
// expecting some other type? Throw/panic here or continue
|
||||
}
|
||||
|
||||
switch v := i.(type) { // type switch on the struct field being validated
|
||||
case CustomByteArray:
|
||||
for _, e := range v { // this validator checks that the byte array is not empty, i.e. not all zeroes
|
||||
if e != 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}))
|
||||
govalidator.CustomTypeTagMap.Set("customMinLengthValidator", CustomTypeValidator(func(i interface{}, context interface{}) bool {
|
||||
switch v := context.(type) { // this validates a field against the value in another field, i.e. dependent validation
|
||||
case StructWithCustomByteArray:
|
||||
return len(v.ID) >= v.CustomMinLength
|
||||
}
|
||||
return false
|
||||
}))
|
||||
```
|
||||
|
||||
#### Notes
|
||||
Documentation is available here: [godoc.org](https://godoc.org/github.com/asaskevich/govalidator).
|
||||
Full information about code coverage is also available here: [govalidator on gocover.io](http://gocover.io/github.com/asaskevich/govalidator).
|
||||
|
||||
#### Support
|
||||
If you do have a contribution for the package feel free to put up a Pull Request or open Issue.
|
||||
|
||||
#### Special thanks to [contributors](https://github.com/asaskevich/govalidator/graphs/contributors)
|
||||
* [Daniel Lohse](https://github.com/annismckenzie)
|
||||
* [Attila Oláh](https://github.com/attilaolah)
|
||||
* [Daniel Korner](https://github.com/Dadie)
|
||||
* [Steven Wilkin](https://github.com/stevenwilkin)
|
||||
* [Deiwin Sarjas](https://github.com/deiwin)
|
||||
* [Noah Shibley](https://github.com/slugmobile)
|
||||
* [Nathan Davies](https://github.com/nathj07)
|
||||
* [Matt Sanford](https://github.com/mzsanford)
|
||||
* [Simon ccl1115](https://github.com/ccl1115)
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package govalidator
|
||||
|
||||
// Iterator is the function that accepts element of slice/array and its index
|
||||
type Iterator func(interface{}, int)
|
||||
|
||||
// ResultIterator is the function that accepts element of slice/array and its index and returns any result
|
||||
type ResultIterator func(interface{}, int) interface{}
|
||||
|
||||
// ConditionIterator is the function that accepts element of slice/array and its index and returns boolean
|
||||
type ConditionIterator func(interface{}, int) bool
|
||||
|
||||
// Each iterates over the slice and apply Iterator to every item
|
||||
func Each(array []interface{}, iterator Iterator) {
|
||||
for index, data := range array {
|
||||
iterator(data, index)
|
||||
}
|
||||
}
|
||||
|
||||
// Map iterates over the slice and apply ResultIterator to every item. Returns new slice as a result.
|
||||
func Map(array []interface{}, iterator ResultIterator) []interface{} {
|
||||
var result = make([]interface{}, len(array))
|
||||
for index, data := range array {
|
||||
result[index] = iterator(data, index)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Find iterates over the slice and apply ConditionIterator to every item. Returns first item that meet ConditionIterator or nil otherwise.
|
||||
func Find(array []interface{}, iterator ConditionIterator) interface{} {
|
||||
for index, data := range array {
|
||||
if iterator(data, index) {
|
||||
return data
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Filter iterates over the slice and apply ConditionIterator to every item. Returns new slice.
|
||||
func Filter(array []interface{}, iterator ConditionIterator) []interface{} {
|
||||
var result = make([]interface{}, 0)
|
||||
for index, data := range array {
|
||||
if iterator(data, index) {
|
||||
result = append(result, data)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Count iterates over the slice and apply ConditionIterator to every item. Returns count of items that meets ConditionIterator.
|
||||
func Count(array []interface{}, iterator ConditionIterator) int {
|
||||
count := 0
|
||||
for index, data := range array {
|
||||
if iterator(data, index) {
|
||||
count = count + 1
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package govalidator
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// ToString convert the input to a string.
|
||||
func ToString(obj interface{}) string {
|
||||
res := fmt.Sprintf("%v", obj)
|
||||
return string(res)
|
||||
}
|
||||
|
||||
// ToJSON convert the input to a valid JSON string
|
||||
func ToJSON(obj interface{}) (string, error) {
|
||||
res, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
res = []byte("")
|
||||
}
|
||||
return string(res), err
|
||||
}
|
||||
|
||||
// ToFloat convert the input string to a float, or 0.0 if the input is not a float.
|
||||
func ToFloat(str string) (float64, error) {
|
||||
res, err := strconv.ParseFloat(str, 64)
|
||||
if err != nil {
|
||||
res = 0.0
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
// ToInt convert the input string to an integer, or 0 if the input is not an integer.
|
||||
func ToInt(str string) (int64, error) {
|
||||
res, err := strconv.ParseInt(str, 0, 64)
|
||||
if err != nil {
|
||||
res = 0
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
// ToBoolean convert the input string to a boolean.
|
||||
func ToBoolean(str string) (bool, error) {
|
||||
return strconv.ParseBool(str)
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package govalidator
|
||||
|
||||
// Errors is an array of multiple errors and conforms to the error interface.
|
||||
type Errors []error
|
||||
|
||||
// Errors returns itself.
|
||||
func (es Errors) Errors() []error {
|
||||
return es
|
||||
}
|
||||
|
||||
func (es Errors) Error() string {
|
||||
var err string
|
||||
for _, e := range es {
|
||||
err += e.Error() + ";"
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Error encapsulates a name, an error and whether there's a custom error message or not.
|
||||
type Error struct {
|
||||
Name string
|
||||
Err error
|
||||
CustomErrorMessageExists bool
|
||||
}
|
||||
|
||||
func (e Error) Error() string {
|
||||
if e.CustomErrorMessageExists {
|
||||
return e.Err.Error()
|
||||
}
|
||||
return e.Name + ": " + e.Err.Error()
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package govalidator
|
||||
|
||||
import "math"
|
||||
|
||||
// Abs returns absolute value of number
|
||||
func Abs(value float64) float64 {
|
||||
return math.Abs(value)
|
||||
}
|
||||
|
||||
// Sign returns signum of number: 1 in case of value > 0, -1 in case of value < 0, 0 otherwise
|
||||
func Sign(value float64) float64 {
|
||||
if value > 0 {
|
||||
return 1
|
||||
} else if value < 0 {
|
||||
return -1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// IsNegative returns true if value < 0
|
||||
func IsNegative(value float64) bool {
|
||||
return value < 0
|
||||
}
|
||||
|
||||
// IsPositive returns true if value > 0
|
||||
func IsPositive(value float64) bool {
|
||||
return value > 0
|
||||
}
|
||||
|
||||
// IsNonNegative returns true if value >= 0
|
||||
func IsNonNegative(value float64) bool {
|
||||
return value >= 0
|
||||
}
|
||||
|
||||
// IsNonPositive returns true if value <= 0
|
||||
func IsNonPositive(value float64) bool {
|
||||
return value <= 0
|
||||
}
|
||||
|
||||
// InRange returns true if value lies between left and right border
|
||||
func InRange(value, left, right float64) bool {
|
||||
if left > right {
|
||||
left, right = right, left
|
||||
}
|
||||
return value >= left && value <= right
|
||||
}
|
||||
|
||||
// IsWhole returns true if value is whole number
|
||||
func IsWhole(value float64) bool {
|
||||
return math.Remainder(value, 1) == 0
|
||||
}
|
||||
|
||||
// IsNatural returns true if value is natural number (positive and whole)
|
||||
func IsNatural(value float64) bool {
|
||||
return IsWhole(value) && IsPositive(value)
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
package govalidator
|
||||
|
||||
import "regexp"
|
||||
|
||||
// Basic regular expressions for validating strings
|
||||
const (
|
||||
Email string = "^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$"
|
||||
CreditCard string = "^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\\d{11})$"
|
||||
ISBN10 string = "^(?:[0-9]{9}X|[0-9]{10})$"
|
||||
ISBN13 string = "^(?:[0-9]{13})$"
|
||||
UUID3 string = "^[0-9a-f]{8}-[0-9a-f]{4}-3[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$"
|
||||
UUID4 string = "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
||||
UUID5 string = "^[0-9a-f]{8}-[0-9a-f]{4}-5[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
||||
UUID string = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
|
||||
Alpha string = "^[a-zA-Z]+$"
|
||||
Alphanumeric string = "^[a-zA-Z0-9]+$"
|
||||
Numeric string = "^[0-9]+$"
|
||||
Int string = "^(?:[-+]?(?:0|[1-9][0-9]*))$"
|
||||
Float string = "^(?:[-+]?(?:[0-9]+))?(?:\\.[0-9]*)?(?:[eE][\\+\\-]?(?:[0-9]+))?$"
|
||||
Hexadecimal string = "^[0-9a-fA-F]+$"
|
||||
Hexcolor string = "^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$"
|
||||
RGBcolor string = "^rgb\\(\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*,\\s*(0|[1-9]\\d?|1\\d\\d?|2[0-4]\\d|25[0-5])\\s*\\)$"
|
||||
ASCII string = "^[\x00-\x7F]+$"
|
||||
Multibyte string = "[^\x00-\x7F]"
|
||||
FullWidth string = "[^\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]"
|
||||
HalfWidth string = "[\u0020-\u007E\uFF61-\uFF9F\uFFA0-\uFFDC\uFFE8-\uFFEE0-9a-zA-Z]"
|
||||
Base64 string = "^(?:[A-Za-z0-9+\\/]{4})*(?:[A-Za-z0-9+\\/]{2}==|[A-Za-z0-9+\\/]{3}=|[A-Za-z0-9+\\/]{4})$"
|
||||
PrintableASCII string = "^[\x20-\x7E]+$"
|
||||
DataURI string = "^data:.+\\/(.+);base64$"
|
||||
Latitude string = "^[-+]?([1-8]?\\d(\\.\\d+)?|90(\\.0+)?)$"
|
||||
Longitude string = "^[-+]?(180(\\.0+)?|((1[0-7]\\d)|([1-9]?\\d))(\\.\\d+)?)$"
|
||||
DNSName string = `^([a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*[\._]?$`
|
||||
IP string = `(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))`
|
||||
URLSchema string = `((ftp|tcp|udp|wss?|https?):\/\/)`
|
||||
URLUsername string = `(\S+(:\S*)?@)`
|
||||
Hostname string = ``
|
||||
URLPath string = `((\/|\?|#)[^\s]*)`
|
||||
URLPort string = `(:(\d{1,5}))`
|
||||
URLIP string = `([1-9]\d?|1\d\d|2[01]\d|22[0-3])(\.(1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.([0-9]\d?|1\d\d|2[0-4]\d|25[0-4]))`
|
||||
URLSubdomain string = `((www\.)|([a-zA-Z0-9]([-\.][-\._a-zA-Z0-9]+)*))`
|
||||
URL string = `^` + URLSchema + `?` + URLUsername + `?` + `((` + URLIP + `|(\[` + IP + `\])|(([a-zA-Z0-9]([a-zA-Z0-9-_]+)?[a-zA-Z0-9]([-\.][a-zA-Z0-9]+)*)|(` + URLSubdomain + `?))?(([a-zA-Z\x{00a1}-\x{ffff}0-9]+-?-?)*[a-zA-Z\x{00a1}-\x{ffff}0-9]+)(?:\.([a-zA-Z\x{00a1}-\x{ffff}]{1,}))?))\.?` + URLPort + `?` + URLPath + `?$`
|
||||
SSN string = `^\d{3}[- ]?\d{2}[- ]?\d{4}$`
|
||||
WinPath string = `^[a-zA-Z]:\\(?:[^\\/:*?"<>|\r\n]+\\)*[^\\/:*?"<>|\r\n]*$`
|
||||
UnixPath string = `^(/[^/\x00]*)+/?$`
|
||||
Semver string = "^v?(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)\\.(?:0|[1-9]\\d*)(-(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(\\.(0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*)?(\\+[0-9a-zA-Z-]+(\\.[0-9a-zA-Z-]+)*)?$"
|
||||
tagName string = "valid"
|
||||
)
|
||||
|
||||
// Used by IsFilePath func
|
||||
const (
|
||||
// Unknown is unresolved OS type
|
||||
Unknown = iota
|
||||
// Win is Windows type
|
||||
Win
|
||||
// Unix is *nix OS types
|
||||
Unix
|
||||
)
|
||||
|
||||
var (
|
||||
rxEmail = regexp.MustCompile(Email)
|
||||
rxCreditCard = regexp.MustCompile(CreditCard)
|
||||
rxISBN10 = regexp.MustCompile(ISBN10)
|
||||
rxISBN13 = regexp.MustCompile(ISBN13)
|
||||
rxUUID3 = regexp.MustCompile(UUID3)
|
||||
rxUUID4 = regexp.MustCompile(UUID4)
|
||||
rxUUID5 = regexp.MustCompile(UUID5)
|
||||
rxUUID = regexp.MustCompile(UUID)
|
||||
rxAlpha = regexp.MustCompile(Alpha)
|
||||
rxAlphanumeric = regexp.MustCompile(Alphanumeric)
|
||||
rxNumeric = regexp.MustCompile(Numeric)
|
||||
rxInt = regexp.MustCompile(Int)
|
||||
rxFloat = regexp.MustCompile(Float)
|
||||
rxHexadecimal = regexp.MustCompile(Hexadecimal)
|
||||
rxHexcolor = regexp.MustCompile(Hexcolor)
|
||||
rxRGBcolor = regexp.MustCompile(RGBcolor)
|
||||
rxASCII = regexp.MustCompile(ASCII)
|
||||
rxPrintableASCII = regexp.MustCompile(PrintableASCII)
|
||||
rxMultibyte = regexp.MustCompile(Multibyte)
|
||||
rxFullWidth = regexp.MustCompile(FullWidth)
|
||||
rxHalfWidth = regexp.MustCompile(HalfWidth)
|
||||
rxBase64 = regexp.MustCompile(Base64)
|
||||
rxDataURI = regexp.MustCompile(DataURI)
|
||||
rxLatitude = regexp.MustCompile(Latitude)
|
||||
rxLongitude = regexp.MustCompile(Longitude)
|
||||
rxDNSName = regexp.MustCompile(DNSName)
|
||||
rxURL = regexp.MustCompile(URL)
|
||||
rxSSN = regexp.MustCompile(SSN)
|
||||
rxWinPath = regexp.MustCompile(WinPath)
|
||||
rxUnixPath = regexp.MustCompile(UnixPath)
|
||||
rxSemver = regexp.MustCompile(Semver)
|
||||
)
|
||||
|
|
@ -0,0 +1,613 @@
|
|||
package govalidator
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Validator is a wrapper for a validator function that returns bool and accepts string.
|
||||
type Validator func(str string) bool
|
||||
|
||||
// CustomTypeValidator is a wrapper for validator functions that returns bool and accepts any type.
|
||||
// The second parameter should be the context (in the case of validating a struct: the whole object being validated).
|
||||
type CustomTypeValidator func(i interface{}, o interface{}) bool
|
||||
|
||||
// ParamValidator is a wrapper for validator functions that accepts additional parameters.
|
||||
type ParamValidator func(str string, params ...string) bool
|
||||
type tagOptionsMap map[string]string
|
||||
|
||||
// UnsupportedTypeError is a wrapper for reflect.Type
|
||||
type UnsupportedTypeError struct {
|
||||
Type reflect.Type
|
||||
}
|
||||
|
||||
// stringValues is a slice of reflect.Value holding *reflect.StringValue.
|
||||
// It implements the methods to sort by string.
|
||||
type stringValues []reflect.Value
|
||||
|
||||
// ParamTagMap is a map of functions accept variants parameters
|
||||
var ParamTagMap = map[string]ParamValidator{
|
||||
"length": ByteLength,
|
||||
"range": Range,
|
||||
"runelength": RuneLength,
|
||||
"stringlength": StringLength,
|
||||
"matches": StringMatches,
|
||||
"in": isInRaw,
|
||||
}
|
||||
|
||||
// ParamTagRegexMap maps param tags to their respective regexes.
|
||||
var ParamTagRegexMap = map[string]*regexp.Regexp{
|
||||
"range": regexp.MustCompile("^range\\((\\d+)\\|(\\d+)\\)$"),
|
||||
"length": regexp.MustCompile("^length\\((\\d+)\\|(\\d+)\\)$"),
|
||||
"runelength": regexp.MustCompile("^runelength\\((\\d+)\\|(\\d+)\\)$"),
|
||||
"stringlength": regexp.MustCompile("^stringlength\\((\\d+)\\|(\\d+)\\)$"),
|
||||
"in": regexp.MustCompile(`^in\((.*)\)`),
|
||||
"matches": regexp.MustCompile(`^matches\((.+)\)$`),
|
||||
}
|
||||
|
||||
type customTypeTagMap struct {
|
||||
validators map[string]CustomTypeValidator
|
||||
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func (tm *customTypeTagMap) Get(name string) (CustomTypeValidator, bool) {
|
||||
tm.RLock()
|
||||
defer tm.RUnlock()
|
||||
v, ok := tm.validators[name]
|
||||
return v, ok
|
||||
}
|
||||
|
||||
func (tm *customTypeTagMap) Set(name string, ctv CustomTypeValidator) {
|
||||
tm.Lock()
|
||||
defer tm.Unlock()
|
||||
tm.validators[name] = ctv
|
||||
}
|
||||
|
||||
// CustomTypeTagMap is a map of functions that can be used as tags for ValidateStruct function.
|
||||
// Use this to validate compound or custom types that need to be handled as a whole, e.g.
|
||||
// `type UUID [16]byte` (this would be handled as an array of bytes).
|
||||
var CustomTypeTagMap = &customTypeTagMap{validators: make(map[string]CustomTypeValidator)}
|
||||
|
||||
// TagMap is a map of functions, that can be used as tags for ValidateStruct function.
|
||||
var TagMap = map[string]Validator{
|
||||
"email": IsEmail,
|
||||
"url": IsURL,
|
||||
"dialstring": IsDialString,
|
||||
"requrl": IsRequestURL,
|
||||
"requri": IsRequestURI,
|
||||
"alpha": IsAlpha,
|
||||
"utfletter": IsUTFLetter,
|
||||
"alphanum": IsAlphanumeric,
|
||||
"utfletternum": IsUTFLetterNumeric,
|
||||
"numeric": IsNumeric,
|
||||
"utfnumeric": IsUTFNumeric,
|
||||
"utfdigit": IsUTFDigit,
|
||||
"hexadecimal": IsHexadecimal,
|
||||
"hexcolor": IsHexcolor,
|
||||
"rgbcolor": IsRGBcolor,
|
||||
"lowercase": IsLowerCase,
|
||||
"uppercase": IsUpperCase,
|
||||
"int": IsInt,
|
||||
"float": IsFloat,
|
||||
"null": IsNull,
|
||||
"uuid": IsUUID,
|
||||
"uuidv3": IsUUIDv3,
|
||||
"uuidv4": IsUUIDv4,
|
||||
"uuidv5": IsUUIDv5,
|
||||
"creditcard": IsCreditCard,
|
||||
"isbn10": IsISBN10,
|
||||
"isbn13": IsISBN13,
|
||||
"json": IsJSON,
|
||||
"multibyte": IsMultibyte,
|
||||
"ascii": IsASCII,
|
||||
"printableascii": IsPrintableASCII,
|
||||
"fullwidth": IsFullWidth,
|
||||
"halfwidth": IsHalfWidth,
|
||||
"variablewidth": IsVariableWidth,
|
||||
"base64": IsBase64,
|
||||
"datauri": IsDataURI,
|
||||
"ip": IsIP,
|
||||
"port": IsPort,
|
||||
"ipv4": IsIPv4,
|
||||
"ipv6": IsIPv6,
|
||||
"dns": IsDNSName,
|
||||
"host": IsHost,
|
||||
"mac": IsMAC,
|
||||
"latitude": IsLatitude,
|
||||
"longitude": IsLongitude,
|
||||
"ssn": IsSSN,
|
||||
"semver": IsSemver,
|
||||
"rfc3339": IsRFC3339,
|
||||
"ISO3166Alpha2": IsISO3166Alpha2,
|
||||
"ISO3166Alpha3": IsISO3166Alpha3,
|
||||
"ISO4217": IsISO4217,
|
||||
}
|
||||
|
||||
// ISO3166Entry stores country codes
|
||||
type ISO3166Entry struct {
|
||||
EnglishShortName string
|
||||
FrenchShortName string
|
||||
Alpha2Code string
|
||||
Alpha3Code string
|
||||
Numeric string
|
||||
}
|
||||
|
||||
//ISO3166List based on https://www.iso.org/obp/ui/#search/code/ Code Type "Officially Assigned Codes"
|
||||
var ISO3166List = []ISO3166Entry{
|
||||
{"Afghanistan", "Afghanistan (l')", "AF", "AFG", "004"},
|
||||
{"Albania", "Albanie (l')", "AL", "ALB", "008"},
|
||||
{"Antarctica", "Antarctique (l')", "AQ", "ATA", "010"},
|
||||
{"Algeria", "Algérie (l')", "DZ", "DZA", "012"},
|
||||
{"American Samoa", "Samoa américaines (les)", "AS", "ASM", "016"},
|
||||
{"Andorra", "Andorre (l')", "AD", "AND", "020"},
|
||||
{"Angola", "Angola (l')", "AO", "AGO", "024"},
|
||||
{"Antigua and Barbuda", "Antigua-et-Barbuda", "AG", "ATG", "028"},
|
||||
{"Azerbaijan", "Azerbaïdjan (l')", "AZ", "AZE", "031"},
|
||||
{"Argentina", "Argentine (l')", "AR", "ARG", "032"},
|
||||
{"Australia", "Australie (l')", "AU", "AUS", "036"},
|
||||
{"Austria", "Autriche (l')", "AT", "AUT", "040"},
|
||||
{"Bahamas (the)", "Bahamas (les)", "BS", "BHS", "044"},
|
||||
{"Bahrain", "Bahreïn", "BH", "BHR", "048"},
|
||||
{"Bangladesh", "Bangladesh (le)", "BD", "BGD", "050"},
|
||||
{"Armenia", "Arménie (l')", "AM", "ARM", "051"},
|
||||
{"Barbados", "Barbade (la)", "BB", "BRB", "052"},
|
||||
{"Belgium", "Belgique (la)", "BE", "BEL", "056"},
|
||||
{"Bermuda", "Bermudes (les)", "BM", "BMU", "060"},
|
||||
{"Bhutan", "Bhoutan (le)", "BT", "BTN", "064"},
|
||||
{"Bolivia (Plurinational State of)", "Bolivie (État plurinational de)", "BO", "BOL", "068"},
|
||||
{"Bosnia and Herzegovina", "Bosnie-Herzégovine (la)", "BA", "BIH", "070"},
|
||||
{"Botswana", "Botswana (le)", "BW", "BWA", "072"},
|
||||
{"Bouvet Island", "Bouvet (l'Île)", "BV", "BVT", "074"},
|
||||
{"Brazil", "Brésil (le)", "BR", "BRA", "076"},
|
||||
{"Belize", "Belize (le)", "BZ", "BLZ", "084"},
|
||||
{"British Indian Ocean Territory (the)", "Indien (le Territoire britannique de l'océan)", "IO", "IOT", "086"},
|
||||
{"Solomon Islands", "Salomon (Îles)", "SB", "SLB", "090"},
|
||||
{"Virgin Islands (British)", "Vierges britanniques (les Îles)", "VG", "VGB", "092"},
|
||||
{"Brunei Darussalam", "Brunéi Darussalam (le)", "BN", "BRN", "096"},
|
||||
{"Bulgaria", "Bulgarie (la)", "BG", "BGR", "100"},
|
||||
{"Myanmar", "Myanmar (le)", "MM", "MMR", "104"},
|
||||
{"Burundi", "Burundi (le)", "BI", "BDI", "108"},
|
||||
{"Belarus", "Bélarus (le)", "BY", "BLR", "112"},
|
||||
{"Cambodia", "Cambodge (le)", "KH", "KHM", "116"},
|
||||
{"Cameroon", "Cameroun (le)", "CM", "CMR", "120"},
|
||||
{"Canada", "Canada (le)", "CA", "CAN", "124"},
|
||||
{"Cabo Verde", "Cabo Verde", "CV", "CPV", "132"},
|
||||
{"Cayman Islands (the)", "Caïmans (les Îles)", "KY", "CYM", "136"},
|
||||
{"Central African Republic (the)", "République centrafricaine (la)", "CF", "CAF", "140"},
|
||||
{"Sri Lanka", "Sri Lanka", "LK", "LKA", "144"},
|
||||
{"Chad", "Tchad (le)", "TD", "TCD", "148"},
|
||||
{"Chile", "Chili (le)", "CL", "CHL", "152"},
|
||||
{"China", "Chine (la)", "CN", "CHN", "156"},
|
||||
{"Taiwan (Province of China)", "Taïwan (Province de Chine)", "TW", "TWN", "158"},
|
||||
{"Christmas Island", "Christmas (l'Île)", "CX", "CXR", "162"},
|
||||
{"Cocos (Keeling) Islands (the)", "Cocos (les Îles)/ Keeling (les Îles)", "CC", "CCK", "166"},
|
||||
{"Colombia", "Colombie (la)", "CO", "COL", "170"},
|
||||
{"Comoros (the)", "Comores (les)", "KM", "COM", "174"},
|
||||
{"Mayotte", "Mayotte", "YT", "MYT", "175"},
|
||||
{"Congo (the)", "Congo (le)", "CG", "COG", "178"},
|
||||
{"Congo (the Democratic Republic of the)", "Congo (la République démocratique du)", "CD", "COD", "180"},
|
||||
{"Cook Islands (the)", "Cook (les Îles)", "CK", "COK", "184"},
|
||||
{"Costa Rica", "Costa Rica (le)", "CR", "CRI", "188"},
|
||||
{"Croatia", "Croatie (la)", "HR", "HRV", "191"},
|
||||
{"Cuba", "Cuba", "CU", "CUB", "192"},
|
||||
{"Cyprus", "Chypre", "CY", "CYP", "196"},
|
||||
{"Czech Republic (the)", "tchèque (la République)", "CZ", "CZE", "203"},
|
||||
{"Benin", "Bénin (le)", "BJ", "BEN", "204"},
|
||||
{"Denmark", "Danemark (le)", "DK", "DNK", "208"},
|
||||
{"Dominica", "Dominique (la)", "DM", "DMA", "212"},
|
||||
{"Dominican Republic (the)", "dominicaine (la République)", "DO", "DOM", "214"},
|
||||
{"Ecuador", "Équateur (l')", "EC", "ECU", "218"},
|
||||
{"El Salvador", "El Salvador", "SV", "SLV", "222"},
|
||||
{"Equatorial Guinea", "Guinée équatoriale (la)", "GQ", "GNQ", "226"},
|
||||
{"Ethiopia", "Éthiopie (l')", "ET", "ETH", "231"},
|
||||
{"Eritrea", "Érythrée (l')", "ER", "ERI", "232"},
|
||||
{"Estonia", "Estonie (l')", "EE", "EST", "233"},
|
||||
{"Faroe Islands (the)", "Féroé (les Îles)", "FO", "FRO", "234"},
|
||||
{"Falkland Islands (the) [Malvinas]", "Falkland (les Îles)/Malouines (les Îles)", "FK", "FLK", "238"},
|
||||
{"South Georgia and the South Sandwich Islands", "Géorgie du Sud-et-les Îles Sandwich du Sud (la)", "GS", "SGS", "239"},
|
||||
{"Fiji", "Fidji (les)", "FJ", "FJI", "242"},
|
||||
{"Finland", "Finlande (la)", "FI", "FIN", "246"},
|
||||
{"Åland Islands", "Åland(les Îles)", "AX", "ALA", "248"},
|
||||
{"France", "France (la)", "FR", "FRA", "250"},
|
||||
{"French Guiana", "Guyane française (la )", "GF", "GUF", "254"},
|
||||
{"French Polynesia", "Polynésie française (la)", "PF", "PYF", "258"},
|
||||
{"French Southern Territories (the)", "Terres australes françaises (les)", "TF", "ATF", "260"},
|
||||
{"Djibouti", "Djibouti", "DJ", "DJI", "262"},
|
||||
{"Gabon", "Gabon (le)", "GA", "GAB", "266"},
|
||||
{"Georgia", "Géorgie (la)", "GE", "GEO", "268"},
|
||||
{"Gambia (the)", "Gambie (la)", "GM", "GMB", "270"},
|
||||
{"Palestine, State of", "Palestine, État de", "PS", "PSE", "275"},
|
||||
{"Germany", "Allemagne (l')", "DE", "DEU", "276"},
|
||||
{"Ghana", "Ghana (le)", "GH", "GHA", "288"},
|
||||
{"Gibraltar", "Gibraltar", "GI", "GIB", "292"},
|
||||
{"Kiribati", "Kiribati", "KI", "KIR", "296"},
|
||||
{"Greece", "Grèce (la)", "GR", "GRC", "300"},
|
||||
{"Greenland", "Groenland (le)", "GL", "GRL", "304"},
|
||||
{"Grenada", "Grenade (la)", "GD", "GRD", "308"},
|
||||
{"Guadeloupe", "Guadeloupe (la)", "GP", "GLP", "312"},
|
||||
{"Guam", "Guam", "GU", "GUM", "316"},
|
||||
{"Guatemala", "Guatemala (le)", "GT", "GTM", "320"},
|
||||
{"Guinea", "Guinée (la)", "GN", "GIN", "324"},
|
||||
{"Guyana", "Guyana (le)", "GY", "GUY", "328"},
|
||||
{"Haiti", "Haïti", "HT", "HTI", "332"},
|
||||
{"Heard Island and McDonald Islands", "Heard-et-Îles MacDonald (l'Île)", "HM", "HMD", "334"},
|
||||
{"Holy See (the)", "Saint-Siège (le)", "VA", "VAT", "336"},
|
||||
{"Honduras", "Honduras (le)", "HN", "HND", "340"},
|
||||
{"Hong Kong", "Hong Kong", "HK", "HKG", "344"},
|
||||
{"Hungary", "Hongrie (la)", "HU", "HUN", "348"},
|
||||
{"Iceland", "Islande (l')", "IS", "ISL", "352"},
|
||||
{"India", "Inde (l')", "IN", "IND", "356"},
|
||||
{"Indonesia", "Indonésie (l')", "ID", "IDN", "360"},
|
||||
{"Iran (Islamic Republic of)", "Iran (République Islamique d')", "IR", "IRN", "364"},
|
||||
{"Iraq", "Iraq (l')", "IQ", "IRQ", "368"},
|
||||
{"Ireland", "Irlande (l')", "IE", "IRL", "372"},
|
||||
{"Israel", "Israël", "IL", "ISR", "376"},
|
||||
{"Italy", "Italie (l')", "IT", "ITA", "380"},
|
||||
{"Côte d'Ivoire", "Côte d'Ivoire (la)", "CI", "CIV", "384"},
|
||||
{"Jamaica", "Jamaïque (la)", "JM", "JAM", "388"},
|
||||
{"Japan", "Japon (le)", "JP", "JPN", "392"},
|
||||
{"Kazakhstan", "Kazakhstan (le)", "KZ", "KAZ", "398"},
|
||||
{"Jordan", "Jordanie (la)", "JO", "JOR", "400"},
|
||||
{"Kenya", "Kenya (le)", "KE", "KEN", "404"},
|
||||
{"Korea (the Democratic People's Republic of)", "Corée (la République populaire démocratique de)", "KP", "PRK", "408"},
|
||||
{"Korea (the Republic of)", "Corée (la République de)", "KR", "KOR", "410"},
|
||||
{"Kuwait", "Koweït (le)", "KW", "KWT", "414"},
|
||||
{"Kyrgyzstan", "Kirghizistan (le)", "KG", "KGZ", "417"},
|
||||
{"Lao People's Democratic Republic (the)", "Lao, République démocratique populaire", "LA", "LAO", "418"},
|
||||
{"Lebanon", "Liban (le)", "LB", "LBN", "422"},
|
||||
{"Lesotho", "Lesotho (le)", "LS", "LSO", "426"},
|
||||
{"Latvia", "Lettonie (la)", "LV", "LVA", "428"},
|
||||
{"Liberia", "Libéria (le)", "LR", "LBR", "430"},
|
||||
{"Libya", "Libye (la)", "LY", "LBY", "434"},
|
||||
{"Liechtenstein", "Liechtenstein (le)", "LI", "LIE", "438"},
|
||||
{"Lithuania", "Lituanie (la)", "LT", "LTU", "440"},
|
||||
{"Luxembourg", "Luxembourg (le)", "LU", "LUX", "442"},
|
||||
{"Macao", "Macao", "MO", "MAC", "446"},
|
||||
{"Madagascar", "Madagascar", "MG", "MDG", "450"},
|
||||
{"Malawi", "Malawi (le)", "MW", "MWI", "454"},
|
||||
{"Malaysia", "Malaisie (la)", "MY", "MYS", "458"},
|
||||
{"Maldives", "Maldives (les)", "MV", "MDV", "462"},
|
||||
{"Mali", "Mali (le)", "ML", "MLI", "466"},
|
||||
{"Malta", "Malte", "MT", "MLT", "470"},
|
||||
{"Martinique", "Martinique (la)", "MQ", "MTQ", "474"},
|
||||
{"Mauritania", "Mauritanie (la)", "MR", "MRT", "478"},
|
||||
{"Mauritius", "Maurice", "MU", "MUS", "480"},
|
||||
{"Mexico", "Mexique (le)", "MX", "MEX", "484"},
|
||||
{"Monaco", "Monaco", "MC", "MCO", "492"},
|
||||
{"Mongolia", "Mongolie (la)", "MN", "MNG", "496"},
|
||||
{"Moldova (the Republic of)", "Moldova , République de", "MD", "MDA", "498"},
|
||||
{"Montenegro", "Monténégro (le)", "ME", "MNE", "499"},
|
||||
{"Montserrat", "Montserrat", "MS", "MSR", "500"},
|
||||
{"Morocco", "Maroc (le)", "MA", "MAR", "504"},
|
||||
{"Mozambique", "Mozambique (le)", "MZ", "MOZ", "508"},
|
||||
{"Oman", "Oman", "OM", "OMN", "512"},
|
||||
{"Namibia", "Namibie (la)", "NA", "NAM", "516"},
|
||||
{"Nauru", "Nauru", "NR", "NRU", "520"},
|
||||
{"Nepal", "Népal (le)", "NP", "NPL", "524"},
|
||||
{"Netherlands (the)", "Pays-Bas (les)", "NL", "NLD", "528"},
|
||||
{"Curaçao", "Curaçao", "CW", "CUW", "531"},
|
||||
{"Aruba", "Aruba", "AW", "ABW", "533"},
|
||||
{"Sint Maarten (Dutch part)", "Saint-Martin (partie néerlandaise)", "SX", "SXM", "534"},
|
||||
{"Bonaire, Sint Eustatius and Saba", "Bonaire, Saint-Eustache et Saba", "BQ", "BES", "535"},
|
||||
{"New Caledonia", "Nouvelle-Calédonie (la)", "NC", "NCL", "540"},
|
||||
{"Vanuatu", "Vanuatu (le)", "VU", "VUT", "548"},
|
||||
{"New Zealand", "Nouvelle-Zélande (la)", "NZ", "NZL", "554"},
|
||||
{"Nicaragua", "Nicaragua (le)", "NI", "NIC", "558"},
|
||||
{"Niger (the)", "Niger (le)", "NE", "NER", "562"},
|
||||
{"Nigeria", "Nigéria (le)", "NG", "NGA", "566"},
|
||||
{"Niue", "Niue", "NU", "NIU", "570"},
|
||||
{"Norfolk Island", "Norfolk (l'Île)", "NF", "NFK", "574"},
|
||||
{"Norway", "Norvège (la)", "NO", "NOR", "578"},
|
||||
{"Northern Mariana Islands (the)", "Mariannes du Nord (les Îles)", "MP", "MNP", "580"},
|
||||
{"United States Minor Outlying Islands (the)", "Îles mineures éloignées des États-Unis (les)", "UM", "UMI", "581"},
|
||||
{"Micronesia (Federated States of)", "Micronésie (États fédérés de)", "FM", "FSM", "583"},
|
||||
{"Marshall Islands (the)", "Marshall (Îles)", "MH", "MHL", "584"},
|
||||
{"Palau", "Palaos (les)", "PW", "PLW", "585"},
|
||||
{"Pakistan", "Pakistan (le)", "PK", "PAK", "586"},
|
||||
{"Panama", "Panama (le)", "PA", "PAN", "591"},
|
||||
{"Papua New Guinea", "Papouasie-Nouvelle-Guinée (la)", "PG", "PNG", "598"},
|
||||
{"Paraguay", "Paraguay (le)", "PY", "PRY", "600"},
|
||||
{"Peru", "Pérou (le)", "PE", "PER", "604"},
|
||||
{"Philippines (the)", "Philippines (les)", "PH", "PHL", "608"},
|
||||
{"Pitcairn", "Pitcairn", "PN", "PCN", "612"},
|
||||
{"Poland", "Pologne (la)", "PL", "POL", "616"},
|
||||
{"Portugal", "Portugal (le)", "PT", "PRT", "620"},
|
||||
{"Guinea-Bissau", "Guinée-Bissau (la)", "GW", "GNB", "624"},
|
||||
{"Timor-Leste", "Timor-Leste (le)", "TL", "TLS", "626"},
|
||||
{"Puerto Rico", "Porto Rico", "PR", "PRI", "630"},
|
||||
{"Qatar", "Qatar (le)", "QA", "QAT", "634"},
|
||||
{"Réunion", "Réunion (La)", "RE", "REU", "638"},
|
||||
{"Romania", "Roumanie (la)", "RO", "ROU", "642"},
|
||||
{"Russian Federation (the)", "Russie (la Fédération de)", "RU", "RUS", "643"},
|
||||
{"Rwanda", "Rwanda (le)", "RW", "RWA", "646"},
|
||||
{"Saint Barthélemy", "Saint-Barthélemy", "BL", "BLM", "652"},
|
||||
{"Saint Helena, Ascension and Tristan da Cunha", "Sainte-Hélène, Ascension et Tristan da Cunha", "SH", "SHN", "654"},
|
||||
{"Saint Kitts and Nevis", "Saint-Kitts-et-Nevis", "KN", "KNA", "659"},
|
||||
{"Anguilla", "Anguilla", "AI", "AIA", "660"},
|
||||
{"Saint Lucia", "Sainte-Lucie", "LC", "LCA", "662"},
|
||||
{"Saint Martin (French part)", "Saint-Martin (partie française)", "MF", "MAF", "663"},
|
||||
{"Saint Pierre and Miquelon", "Saint-Pierre-et-Miquelon", "PM", "SPM", "666"},
|
||||
{"Saint Vincent and the Grenadines", "Saint-Vincent-et-les Grenadines", "VC", "VCT", "670"},
|
||||
{"San Marino", "Saint-Marin", "SM", "SMR", "674"},
|
||||
{"Sao Tome and Principe", "Sao Tomé-et-Principe", "ST", "STP", "678"},
|
||||
{"Saudi Arabia", "Arabie saoudite (l')", "SA", "SAU", "682"},
|
||||
{"Senegal", "Sénégal (le)", "SN", "SEN", "686"},
|
||||
{"Serbia", "Serbie (la)", "RS", "SRB", "688"},
|
||||
{"Seychelles", "Seychelles (les)", "SC", "SYC", "690"},
|
||||
{"Sierra Leone", "Sierra Leone (la)", "SL", "SLE", "694"},
|
||||
{"Singapore", "Singapour", "SG", "SGP", "702"},
|
||||
{"Slovakia", "Slovaquie (la)", "SK", "SVK", "703"},
|
||||
{"Viet Nam", "Viet Nam (le)", "VN", "VNM", "704"},
|
||||
{"Slovenia", "Slovénie (la)", "SI", "SVN", "705"},
|
||||
{"Somalia", "Somalie (la)", "SO", "SOM", "706"},
|
||||
{"South Africa", "Afrique du Sud (l')", "ZA", "ZAF", "710"},
|
||||
{"Zimbabwe", "Zimbabwe (le)", "ZW", "ZWE", "716"},
|
||||
{"Spain", "Espagne (l')", "ES", "ESP", "724"},
|
||||
{"South Sudan", "Soudan du Sud (le)", "SS", "SSD", "728"},
|
||||
{"Sudan (the)", "Soudan (le)", "SD", "SDN", "729"},
|
||||
{"Western Sahara*", "Sahara occidental (le)*", "EH", "ESH", "732"},
|
||||
{"Suriname", "Suriname (le)", "SR", "SUR", "740"},
|
||||
{"Svalbard and Jan Mayen", "Svalbard et l'Île Jan Mayen (le)", "SJ", "SJM", "744"},
|
||||
{"Swaziland", "Swaziland (le)", "SZ", "SWZ", "748"},
|
||||
{"Sweden", "Suède (la)", "SE", "SWE", "752"},
|
||||
{"Switzerland", "Suisse (la)", "CH", "CHE", "756"},
|
||||
{"Syrian Arab Republic", "République arabe syrienne (la)", "SY", "SYR", "760"},
|
||||
{"Tajikistan", "Tadjikistan (le)", "TJ", "TJK", "762"},
|
||||
{"Thailand", "Thaïlande (la)", "TH", "THA", "764"},
|
||||
{"Togo", "Togo (le)", "TG", "TGO", "768"},
|
||||
{"Tokelau", "Tokelau (les)", "TK", "TKL", "772"},
|
||||
{"Tonga", "Tonga (les)", "TO", "TON", "776"},
|
||||
{"Trinidad and Tobago", "Trinité-et-Tobago (la)", "TT", "TTO", "780"},
|
||||
{"United Arab Emirates (the)", "Émirats arabes unis (les)", "AE", "ARE", "784"},
|
||||
{"Tunisia", "Tunisie (la)", "TN", "TUN", "788"},
|
||||
{"Turkey", "Turquie (la)", "TR", "TUR", "792"},
|
||||
{"Turkmenistan", "Turkménistan (le)", "TM", "TKM", "795"},
|
||||
{"Turks and Caicos Islands (the)", "Turks-et-Caïcos (les Îles)", "TC", "TCA", "796"},
|
||||
{"Tuvalu", "Tuvalu (les)", "TV", "TUV", "798"},
|
||||
{"Uganda", "Ouganda (l')", "UG", "UGA", "800"},
|
||||
{"Ukraine", "Ukraine (l')", "UA", "UKR", "804"},
|
||||
{"Macedonia (the former Yugoslav Republic of)", "Macédoine (l'ex‑République yougoslave de)", "MK", "MKD", "807"},
|
||||
{"Egypt", "Égypte (l')", "EG", "EGY", "818"},
|
||||
{"United Kingdom of Great Britain and Northern Ireland (the)", "Royaume-Uni de Grande-Bretagne et d'Irlande du Nord (le)", "GB", "GBR", "826"},
|
||||
{"Guernsey", "Guernesey", "GG", "GGY", "831"},
|
||||
{"Jersey", "Jersey", "JE", "JEY", "832"},
|
||||
{"Isle of Man", "Île de Man", "IM", "IMN", "833"},
|
||||
{"Tanzania, United Republic of", "Tanzanie, République-Unie de", "TZ", "TZA", "834"},
|
||||
{"United States of America (the)", "États-Unis d'Amérique (les)", "US", "USA", "840"},
|
||||
{"Virgin Islands (U.S.)", "Vierges des États-Unis (les Îles)", "VI", "VIR", "850"},
|
||||
{"Burkina Faso", "Burkina Faso (le)", "BF", "BFA", "854"},
|
||||
{"Uruguay", "Uruguay (l')", "UY", "URY", "858"},
|
||||
{"Uzbekistan", "Ouzbékistan (l')", "UZ", "UZB", "860"},
|
||||
{"Venezuela (Bolivarian Republic of)", "Venezuela (République bolivarienne du)", "VE", "VEN", "862"},
|
||||
{"Wallis and Futuna", "Wallis-et-Futuna", "WF", "WLF", "876"},
|
||||
{"Samoa", "Samoa (le)", "WS", "WSM", "882"},
|
||||
{"Yemen", "Yémen (le)", "YE", "YEM", "887"},
|
||||
{"Zambia", "Zambie (la)", "ZM", "ZMB", "894"},
|
||||
}
|
||||
|
||||
// ISO4217List is the list of ISO currency codes
|
||||
var ISO4217List = []string{
|
||||
"AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN",
|
||||
"BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BOV", "BRL", "BSD", "BTN", "BWP", "BYN", "BZD",
|
||||
"CAD", "CDF", "CHE", "CHF", "CHW", "CLF", "CLP", "CNY", "COP", "COU", "CRC", "CUC", "CUP", "CVE", "CZK",
|
||||
"DJF", "DKK", "DOP", "DZD",
|
||||
"EGP", "ERN", "ETB", "EUR",
|
||||
"FJD", "FKP",
|
||||
"GBP", "GEL", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD",
|
||||
"HKD", "HNL", "HRK", "HTG", "HUF",
|
||||
"IDR", "ILS", "INR", "IQD", "IRR", "ISK",
|
||||
"JMD", "JOD", "JPY",
|
||||
"KES", "KGS", "KHR", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT",
|
||||
"LAK", "LBP", "LKR", "LRD", "LSL", "LYD",
|
||||
"MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRO", "MUR", "MVR", "MWK", "MXN", "MXV", "MYR", "MZN",
|
||||
"NAD", "NGN", "NIO", "NOK", "NPR", "NZD",
|
||||
"OMR",
|
||||
"PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG",
|
||||
"QAR",
|
||||
"RON", "RSD", "RUB", "RWF",
|
||||
"SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLL", "SOS", "SRD", "SSP", "STD", "SVC", "SYP", "SZL",
|
||||
"THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD", "TWD", "TZS",
|
||||
"UAH", "UGX", "USD", "USN", "UYI", "UYU", "UZS",
|
||||
"VEF", "VND", "VUV",
|
||||
"WST",
|
||||
"XAF", "XAG", "XAU", "XBA", "XBB", "XBC", "XBD", "XCD", "XDR", "XOF", "XPD", "XPF", "XPT", "XSU", "XTS", "XUA", "XXX",
|
||||
"YER",
|
||||
"ZAR", "ZMW", "ZWL",
|
||||
}
|
||||
|
||||
// ISO693Entry stores ISO language codes
|
||||
type ISO693Entry struct {
|
||||
Alpha3bCode string
|
||||
Alpha2Code string
|
||||
English string
|
||||
}
|
||||
|
||||
//ISO693List based on http://data.okfn.org/data/core/language-codes/r/language-codes-3b2.json
|
||||
var ISO693List = []ISO693Entry{
|
||||
{Alpha3bCode: "aar", Alpha2Code: "aa", English: "Afar"},
|
||||
{Alpha3bCode: "abk", Alpha2Code: "ab", English: "Abkhazian"},
|
||||
{Alpha3bCode: "afr", Alpha2Code: "af", English: "Afrikaans"},
|
||||
{Alpha3bCode: "aka", Alpha2Code: "ak", English: "Akan"},
|
||||
{Alpha3bCode: "alb", Alpha2Code: "sq", English: "Albanian"},
|
||||
{Alpha3bCode: "amh", Alpha2Code: "am", English: "Amharic"},
|
||||
{Alpha3bCode: "ara", Alpha2Code: "ar", English: "Arabic"},
|
||||
{Alpha3bCode: "arg", Alpha2Code: "an", English: "Aragonese"},
|
||||
{Alpha3bCode: "arm", Alpha2Code: "hy", English: "Armenian"},
|
||||
{Alpha3bCode: "asm", Alpha2Code: "as", English: "Assamese"},
|
||||
{Alpha3bCode: "ava", Alpha2Code: "av", English: "Avaric"},
|
||||
{Alpha3bCode: "ave", Alpha2Code: "ae", English: "Avestan"},
|
||||
{Alpha3bCode: "aym", Alpha2Code: "ay", English: "Aymara"},
|
||||
{Alpha3bCode: "aze", Alpha2Code: "az", English: "Azerbaijani"},
|
||||
{Alpha3bCode: "bak", Alpha2Code: "ba", English: "Bashkir"},
|
||||
{Alpha3bCode: "bam", Alpha2Code: "bm", English: "Bambara"},
|
||||
{Alpha3bCode: "baq", Alpha2Code: "eu", English: "Basque"},
|
||||
{Alpha3bCode: "bel", Alpha2Code: "be", English: "Belarusian"},
|
||||
{Alpha3bCode: "ben", Alpha2Code: "bn", English: "Bengali"},
|
||||
{Alpha3bCode: "bih", Alpha2Code: "bh", English: "Bihari languages"},
|
||||
{Alpha3bCode: "bis", Alpha2Code: "bi", English: "Bislama"},
|
||||
{Alpha3bCode: "bos", Alpha2Code: "bs", English: "Bosnian"},
|
||||
{Alpha3bCode: "bre", Alpha2Code: "br", English: "Breton"},
|
||||
{Alpha3bCode: "bul", Alpha2Code: "bg", English: "Bulgarian"},
|
||||
{Alpha3bCode: "bur", Alpha2Code: "my", English: "Burmese"},
|
||||
{Alpha3bCode: "cat", Alpha2Code: "ca", English: "Catalan; Valencian"},
|
||||
{Alpha3bCode: "cha", Alpha2Code: "ch", English: "Chamorro"},
|
||||
{Alpha3bCode: "che", Alpha2Code: "ce", English: "Chechen"},
|
||||
{Alpha3bCode: "chi", Alpha2Code: "zh", English: "Chinese"},
|
||||
{Alpha3bCode: "chu", Alpha2Code: "cu", English: "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic"},
|
||||
{Alpha3bCode: "chv", Alpha2Code: "cv", English: "Chuvash"},
|
||||
{Alpha3bCode: "cor", Alpha2Code: "kw", English: "Cornish"},
|
||||
{Alpha3bCode: "cos", Alpha2Code: "co", English: "Corsican"},
|
||||
{Alpha3bCode: "cre", Alpha2Code: "cr", English: "Cree"},
|
||||
{Alpha3bCode: "cze", Alpha2Code: "cs", English: "Czech"},
|
||||
{Alpha3bCode: "dan", Alpha2Code: "da", English: "Danish"},
|
||||
{Alpha3bCode: "div", Alpha2Code: "dv", English: "Divehi; Dhivehi; Maldivian"},
|
||||
{Alpha3bCode: "dut", Alpha2Code: "nl", English: "Dutch; Flemish"},
|
||||
{Alpha3bCode: "dzo", Alpha2Code: "dz", English: "Dzongkha"},
|
||||
{Alpha3bCode: "eng", Alpha2Code: "en", English: "English"},
|
||||
{Alpha3bCode: "epo", Alpha2Code: "eo", English: "Esperanto"},
|
||||
{Alpha3bCode: "est", Alpha2Code: "et", English: "Estonian"},
|
||||
{Alpha3bCode: "ewe", Alpha2Code: "ee", English: "Ewe"},
|
||||
{Alpha3bCode: "fao", Alpha2Code: "fo", English: "Faroese"},
|
||||
{Alpha3bCode: "fij", Alpha2Code: "fj", English: "Fijian"},
|
||||
{Alpha3bCode: "fin", Alpha2Code: "fi", English: "Finnish"},
|
||||
{Alpha3bCode: "fre", Alpha2Code: "fr", English: "French"},
|
||||
{Alpha3bCode: "fry", Alpha2Code: "fy", English: "Western Frisian"},
|
||||
{Alpha3bCode: "ful", Alpha2Code: "ff", English: "Fulah"},
|
||||
{Alpha3bCode: "geo", Alpha2Code: "ka", English: "Georgian"},
|
||||
{Alpha3bCode: "ger", Alpha2Code: "de", English: "German"},
|
||||
{Alpha3bCode: "gla", Alpha2Code: "gd", English: "Gaelic; Scottish Gaelic"},
|
||||
{Alpha3bCode: "gle", Alpha2Code: "ga", English: "Irish"},
|
||||
{Alpha3bCode: "glg", Alpha2Code: "gl", English: "Galician"},
|
||||
{Alpha3bCode: "glv", Alpha2Code: "gv", English: "Manx"},
|
||||
{Alpha3bCode: "gre", Alpha2Code: "el", English: "Greek, Modern (1453-)"},
|
||||
{Alpha3bCode: "grn", Alpha2Code: "gn", English: "Guarani"},
|
||||
{Alpha3bCode: "guj", Alpha2Code: "gu", English: "Gujarati"},
|
||||
{Alpha3bCode: "hat", Alpha2Code: "ht", English: "Haitian; Haitian Creole"},
|
||||
{Alpha3bCode: "hau", Alpha2Code: "ha", English: "Hausa"},
|
||||
{Alpha3bCode: "heb", Alpha2Code: "he", English: "Hebrew"},
|
||||
{Alpha3bCode: "her", Alpha2Code: "hz", English: "Herero"},
|
||||
{Alpha3bCode: "hin", Alpha2Code: "hi", English: "Hindi"},
|
||||
{Alpha3bCode: "hmo", Alpha2Code: "ho", English: "Hiri Motu"},
|
||||
{Alpha3bCode: "hrv", Alpha2Code: "hr", English: "Croatian"},
|
||||
{Alpha3bCode: "hun", Alpha2Code: "hu", English: "Hungarian"},
|
||||
{Alpha3bCode: "ibo", Alpha2Code: "ig", English: "Igbo"},
|
||||
{Alpha3bCode: "ice", Alpha2Code: "is", English: "Icelandic"},
|
||||
{Alpha3bCode: "ido", Alpha2Code: "io", English: "Ido"},
|
||||
{Alpha3bCode: "iii", Alpha2Code: "ii", English: "Sichuan Yi; Nuosu"},
|
||||
{Alpha3bCode: "iku", Alpha2Code: "iu", English: "Inuktitut"},
|
||||
{Alpha3bCode: "ile", Alpha2Code: "ie", English: "Interlingue; Occidental"},
|
||||
{Alpha3bCode: "ina", Alpha2Code: "ia", English: "Interlingua (International Auxiliary Language Association)"},
|
||||
{Alpha3bCode: "ind", Alpha2Code: "id", English: "Indonesian"},
|
||||
{Alpha3bCode: "ipk", Alpha2Code: "ik", English: "Inupiaq"},
|
||||
{Alpha3bCode: "ita", Alpha2Code: "it", English: "Italian"},
|
||||
{Alpha3bCode: "jav", Alpha2Code: "jv", English: "Javanese"},
|
||||
{Alpha3bCode: "jpn", Alpha2Code: "ja", English: "Japanese"},
|
||||
{Alpha3bCode: "kal", Alpha2Code: "kl", English: "Kalaallisut; Greenlandic"},
|
||||
{Alpha3bCode: "kan", Alpha2Code: "kn", English: "Kannada"},
|
||||
{Alpha3bCode: "kas", Alpha2Code: "ks", English: "Kashmiri"},
|
||||
{Alpha3bCode: "kau", Alpha2Code: "kr", English: "Kanuri"},
|
||||
{Alpha3bCode: "kaz", Alpha2Code: "kk", English: "Kazakh"},
|
||||
{Alpha3bCode: "khm", Alpha2Code: "km", English: "Central Khmer"},
|
||||
{Alpha3bCode: "kik", Alpha2Code: "ki", English: "Kikuyu; Gikuyu"},
|
||||
{Alpha3bCode: "kin", Alpha2Code: "rw", English: "Kinyarwanda"},
|
||||
{Alpha3bCode: "kir", Alpha2Code: "ky", English: "Kirghiz; Kyrgyz"},
|
||||
{Alpha3bCode: "kom", Alpha2Code: "kv", English: "Komi"},
|
||||
{Alpha3bCode: "kon", Alpha2Code: "kg", English: "Kongo"},
|
||||
{Alpha3bCode: "kor", Alpha2Code: "ko", English: "Korean"},
|
||||
{Alpha3bCode: "kua", Alpha2Code: "kj", English: "Kuanyama; Kwanyama"},
|
||||
{Alpha3bCode: "kur", Alpha2Code: "ku", English: "Kurdish"},
|
||||
{Alpha3bCode: "lao", Alpha2Code: "lo", English: "Lao"},
|
||||
{Alpha3bCode: "lat", Alpha2Code: "la", English: "Latin"},
|
||||
{Alpha3bCode: "lav", Alpha2Code: "lv", English: "Latvian"},
|
||||
{Alpha3bCode: "lim", Alpha2Code: "li", English: "Limburgan; Limburger; Limburgish"},
|
||||
{Alpha3bCode: "lin", Alpha2Code: "ln", English: "Lingala"},
|
||||
{Alpha3bCode: "lit", Alpha2Code: "lt", English: "Lithuanian"},
|
||||
{Alpha3bCode: "ltz", Alpha2Code: "lb", English: "Luxembourgish; Letzeburgesch"},
|
||||
{Alpha3bCode: "lub", Alpha2Code: "lu", English: "Luba-Katanga"},
|
||||
{Alpha3bCode: "lug", Alpha2Code: "lg", English: "Ganda"},
|
||||
{Alpha3bCode: "mac", Alpha2Code: "mk", English: "Macedonian"},
|
||||
{Alpha3bCode: "mah", Alpha2Code: "mh", English: "Marshallese"},
|
||||
{Alpha3bCode: "mal", Alpha2Code: "ml", English: "Malayalam"},
|
||||
{Alpha3bCode: "mao", Alpha2Code: "mi", English: "Maori"},
|
||||
{Alpha3bCode: "mar", Alpha2Code: "mr", English: "Marathi"},
|
||||
{Alpha3bCode: "may", Alpha2Code: "ms", English: "Malay"},
|
||||
{Alpha3bCode: "mlg", Alpha2Code: "mg", English: "Malagasy"},
|
||||
{Alpha3bCode: "mlt", Alpha2Code: "mt", English: "Maltese"},
|
||||
{Alpha3bCode: "mon", Alpha2Code: "mn", English: "Mongolian"},
|
||||
{Alpha3bCode: "nau", Alpha2Code: "na", English: "Nauru"},
|
||||
{Alpha3bCode: "nav", Alpha2Code: "nv", English: "Navajo; Navaho"},
|
||||
{Alpha3bCode: "nbl", Alpha2Code: "nr", English: "Ndebele, South; South Ndebele"},
|
||||
{Alpha3bCode: "nde", Alpha2Code: "nd", English: "Ndebele, North; North Ndebele"},
|
||||
{Alpha3bCode: "ndo", Alpha2Code: "ng", English: "Ndonga"},
|
||||
{Alpha3bCode: "nep", Alpha2Code: "ne", English: "Nepali"},
|
||||
{Alpha3bCode: "nno", Alpha2Code: "nn", English: "Norwegian Nynorsk; Nynorsk, Norwegian"},
|
||||
{Alpha3bCode: "nob", Alpha2Code: "nb", English: "Bokmål, Norwegian; Norwegian Bokmål"},
|
||||
{Alpha3bCode: "nor", Alpha2Code: "no", English: "Norwegian"},
|
||||
{Alpha3bCode: "nya", Alpha2Code: "ny", English: "Chichewa; Chewa; Nyanja"},
|
||||
{Alpha3bCode: "oci", Alpha2Code: "oc", English: "Occitan (post 1500); Provençal"},
|
||||
{Alpha3bCode: "oji", Alpha2Code: "oj", English: "Ojibwa"},
|
||||
{Alpha3bCode: "ori", Alpha2Code: "or", English: "Oriya"},
|
||||
{Alpha3bCode: "orm", Alpha2Code: "om", English: "Oromo"},
|
||||
{Alpha3bCode: "oss", Alpha2Code: "os", English: "Ossetian; Ossetic"},
|
||||
{Alpha3bCode: "pan", Alpha2Code: "pa", English: "Panjabi; Punjabi"},
|
||||
{Alpha3bCode: "per", Alpha2Code: "fa", English: "Persian"},
|
||||
{Alpha3bCode: "pli", Alpha2Code: "pi", English: "Pali"},
|
||||
{Alpha3bCode: "pol", Alpha2Code: "pl", English: "Polish"},
|
||||
{Alpha3bCode: "por", Alpha2Code: "pt", English: "Portuguese"},
|
||||
{Alpha3bCode: "pus", Alpha2Code: "ps", English: "Pushto; Pashto"},
|
||||
{Alpha3bCode: "que", Alpha2Code: "qu", English: "Quechua"},
|
||||
{Alpha3bCode: "roh", Alpha2Code: "rm", English: "Romansh"},
|
||||
{Alpha3bCode: "rum", Alpha2Code: "ro", English: "Romanian; Moldavian; Moldovan"},
|
||||
{Alpha3bCode: "run", Alpha2Code: "rn", English: "Rundi"},
|
||||
{Alpha3bCode: "rus", Alpha2Code: "ru", English: "Russian"},
|
||||
{Alpha3bCode: "sag", Alpha2Code: "sg", English: "Sango"},
|
||||
{Alpha3bCode: "san", Alpha2Code: "sa", English: "Sanskrit"},
|
||||
{Alpha3bCode: "sin", Alpha2Code: "si", English: "Sinhala; Sinhalese"},
|
||||
{Alpha3bCode: "slo", Alpha2Code: "sk", English: "Slovak"},
|
||||
{Alpha3bCode: "slv", Alpha2Code: "sl", English: "Slovenian"},
|
||||
{Alpha3bCode: "sme", Alpha2Code: "se", English: "Northern Sami"},
|
||||
{Alpha3bCode: "smo", Alpha2Code: "sm", English: "Samoan"},
|
||||
{Alpha3bCode: "sna", Alpha2Code: "sn", English: "Shona"},
|
||||
{Alpha3bCode: "snd", Alpha2Code: "sd", English: "Sindhi"},
|
||||
{Alpha3bCode: "som", Alpha2Code: "so", English: "Somali"},
|
||||
{Alpha3bCode: "sot", Alpha2Code: "st", English: "Sotho, Southern"},
|
||||
{Alpha3bCode: "spa", Alpha2Code: "es", English: "Spanish; Castilian"},
|
||||
{Alpha3bCode: "srd", Alpha2Code: "sc", English: "Sardinian"},
|
||||
{Alpha3bCode: "srp", Alpha2Code: "sr", English: "Serbian"},
|
||||
{Alpha3bCode: "ssw", Alpha2Code: "ss", English: "Swati"},
|
||||
{Alpha3bCode: "sun", Alpha2Code: "su", English: "Sundanese"},
|
||||
{Alpha3bCode: "swa", Alpha2Code: "sw", English: "Swahili"},
|
||||
{Alpha3bCode: "swe", Alpha2Code: "sv", English: "Swedish"},
|
||||
{Alpha3bCode: "tah", Alpha2Code: "ty", English: "Tahitian"},
|
||||
{Alpha3bCode: "tam", Alpha2Code: "ta", English: "Tamil"},
|
||||
{Alpha3bCode: "tat", Alpha2Code: "tt", English: "Tatar"},
|
||||
{Alpha3bCode: "tel", Alpha2Code: "te", English: "Telugu"},
|
||||
{Alpha3bCode: "tgk", Alpha2Code: "tg", English: "Tajik"},
|
||||
{Alpha3bCode: "tgl", Alpha2Code: "tl", English: "Tagalog"},
|
||||
{Alpha3bCode: "tha", Alpha2Code: "th", English: "Thai"},
|
||||
{Alpha3bCode: "tib", Alpha2Code: "bo", English: "Tibetan"},
|
||||
{Alpha3bCode: "tir", Alpha2Code: "ti", English: "Tigrinya"},
|
||||
{Alpha3bCode: "ton", Alpha2Code: "to", English: "Tonga (Tonga Islands)"},
|
||||
{Alpha3bCode: "tsn", Alpha2Code: "tn", English: "Tswana"},
|
||||
{Alpha3bCode: "tso", Alpha2Code: "ts", English: "Tsonga"},
|
||||
{Alpha3bCode: "tuk", Alpha2Code: "tk", English: "Turkmen"},
|
||||
{Alpha3bCode: "tur", Alpha2Code: "tr", English: "Turkish"},
|
||||
{Alpha3bCode: "twi", Alpha2Code: "tw", English: "Twi"},
|
||||
{Alpha3bCode: "uig", Alpha2Code: "ug", English: "Uighur; Uyghur"},
|
||||
{Alpha3bCode: "ukr", Alpha2Code: "uk", English: "Ukrainian"},
|
||||
{Alpha3bCode: "urd", Alpha2Code: "ur", English: "Urdu"},
|
||||
{Alpha3bCode: "uzb", Alpha2Code: "uz", English: "Uzbek"},
|
||||
{Alpha3bCode: "ven", Alpha2Code: "ve", English: "Venda"},
|
||||
{Alpha3bCode: "vie", Alpha2Code: "vi", English: "Vietnamese"},
|
||||
{Alpha3bCode: "vol", Alpha2Code: "vo", English: "Volapük"},
|
||||
{Alpha3bCode: "wel", Alpha2Code: "cy", English: "Welsh"},
|
||||
{Alpha3bCode: "wln", Alpha2Code: "wa", English: "Walloon"},
|
||||
{Alpha3bCode: "wol", Alpha2Code: "wo", English: "Wolof"},
|
||||
{Alpha3bCode: "xho", Alpha2Code: "xh", English: "Xhosa"},
|
||||
{Alpha3bCode: "yid", Alpha2Code: "yi", English: "Yiddish"},
|
||||
{Alpha3bCode: "yor", Alpha2Code: "yo", English: "Yoruba"},
|
||||
{Alpha3bCode: "zha", Alpha2Code: "za", English: "Zhuang; Chuang"},
|
||||
{Alpha3bCode: "zul", Alpha2Code: "zu", English: "Zulu"},
|
||||
}
|
||||
|
|
@ -0,0 +1,262 @@
|
|||
package govalidator
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"html"
|
||||
"math"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Contains check if the string contains the substring.
|
||||
func Contains(str, substring string) bool {
|
||||
return strings.Contains(str, substring)
|
||||
}
|
||||
|
||||
// Matches check if string matches the pattern (pattern is regular expression)
|
||||
// In case of error return false
|
||||
func Matches(str, pattern string) bool {
|
||||
match, _ := regexp.MatchString(pattern, str)
|
||||
return match
|
||||
}
|
||||
|
||||
// LeftTrim trim characters from the left-side of the input.
|
||||
// If second argument is empty, it's will be remove leading spaces.
|
||||
func LeftTrim(str, chars string) string {
|
||||
if chars == "" {
|
||||
return strings.TrimLeftFunc(str, unicode.IsSpace)
|
||||
}
|
||||
r, _ := regexp.Compile("^[" + chars + "]+")
|
||||
return r.ReplaceAllString(str, "")
|
||||
}
|
||||
|
||||
// RightTrim trim characters from the right-side of the input.
|
||||
// If second argument is empty, it's will be remove spaces.
|
||||
func RightTrim(str, chars string) string {
|
||||
if chars == "" {
|
||||
return strings.TrimRightFunc(str, unicode.IsSpace)
|
||||
}
|
||||
r, _ := regexp.Compile("[" + chars + "]+$")
|
||||
return r.ReplaceAllString(str, "")
|
||||
}
|
||||
|
||||
// Trim trim characters from both sides of the input.
|
||||
// If second argument is empty, it's will be remove spaces.
|
||||
func Trim(str, chars string) string {
|
||||
return LeftTrim(RightTrim(str, chars), chars)
|
||||
}
|
||||
|
||||
// WhiteList remove characters that do not appear in the whitelist.
|
||||
func WhiteList(str, chars string) string {
|
||||
pattern := "[^" + chars + "]+"
|
||||
r, _ := regexp.Compile(pattern)
|
||||
return r.ReplaceAllString(str, "")
|
||||
}
|
||||
|
||||
// BlackList remove characters that appear in the blacklist.
|
||||
func BlackList(str, chars string) string {
|
||||
pattern := "[" + chars + "]+"
|
||||
r, _ := regexp.Compile(pattern)
|
||||
return r.ReplaceAllString(str, "")
|
||||
}
|
||||
|
||||
// StripLow remove characters with a numerical value < 32 and 127, mostly control characters.
|
||||
// If keep_new_lines is true, newline characters are preserved (\n and \r, hex 0xA and 0xD).
|
||||
func StripLow(str string, keepNewLines bool) string {
|
||||
chars := ""
|
||||
if keepNewLines {
|
||||
chars = "\x00-\x09\x0B\x0C\x0E-\x1F\x7F"
|
||||
} else {
|
||||
chars = "\x00-\x1F\x7F"
|
||||
}
|
||||
return BlackList(str, chars)
|
||||
}
|
||||
|
||||
// ReplacePattern replace regular expression pattern in string
|
||||
func ReplacePattern(str, pattern, replace string) string {
|
||||
r, _ := regexp.Compile(pattern)
|
||||
return r.ReplaceAllString(str, replace)
|
||||
}
|
||||
|
||||
// Escape replace <, >, & and " with HTML entities.
|
||||
var Escape = html.EscapeString
|
||||
|
||||
func addSegment(inrune, segment []rune) []rune {
|
||||
if len(segment) == 0 {
|
||||
return inrune
|
||||
}
|
||||
if len(inrune) != 0 {
|
||||
inrune = append(inrune, '_')
|
||||
}
|
||||
inrune = append(inrune, segment...)
|
||||
return inrune
|
||||
}
|
||||
|
||||
// UnderscoreToCamelCase converts from underscore separated form to camel case form.
|
||||
// Ex.: my_func => MyFunc
|
||||
func UnderscoreToCamelCase(s string) string {
|
||||
return strings.Replace(strings.Title(strings.Replace(strings.ToLower(s), "_", " ", -1)), " ", "", -1)
|
||||
}
|
||||
|
||||
// CamelCaseToUnderscore converts from camel case form to underscore separated form.
|
||||
// Ex.: MyFunc => my_func
|
||||
func CamelCaseToUnderscore(str string) string {
|
||||
var output []rune
|
||||
var segment []rune
|
||||
for _, r := range str {
|
||||
if !unicode.IsLower(r) {
|
||||
output = addSegment(output, segment)
|
||||
segment = nil
|
||||
}
|
||||
segment = append(segment, unicode.ToLower(r))
|
||||
}
|
||||
output = addSegment(output, segment)
|
||||
return string(output)
|
||||
}
|
||||
|
||||
// Reverse return reversed string
|
||||
func Reverse(s string) string {
|
||||
r := []rune(s)
|
||||
for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
|
||||
r[i], r[j] = r[j], r[i]
|
||||
}
|
||||
return string(r)
|
||||
}
|
||||
|
||||
// GetLines split string by "\n" and return array of lines
|
||||
func GetLines(s string) []string {
|
||||
return strings.Split(s, "\n")
|
||||
}
|
||||
|
||||
// GetLine return specified line of multiline string
|
||||
func GetLine(s string, index int) (string, error) {
|
||||
lines := GetLines(s)
|
||||
if index < 0 || index >= len(lines) {
|
||||
return "", errors.New("line index out of bounds")
|
||||
}
|
||||
return lines[index], nil
|
||||
}
|
||||
|
||||
// RemoveTags remove all tags from HTML string
|
||||
func RemoveTags(s string) string {
|
||||
return ReplacePattern(s, "<[^>]*>", "")
|
||||
}
|
||||
|
||||
// SafeFileName return safe string that can be used in file names
|
||||
func SafeFileName(str string) string {
|
||||
name := strings.ToLower(str)
|
||||
name = path.Clean(path.Base(name))
|
||||
name = strings.Trim(name, " ")
|
||||
separators, err := regexp.Compile(`[ &_=+:]`)
|
||||
if err == nil {
|
||||
name = separators.ReplaceAllString(name, "-")
|
||||
}
|
||||
legal, err := regexp.Compile(`[^[:alnum:]-.]`)
|
||||
if err == nil {
|
||||
name = legal.ReplaceAllString(name, "")
|
||||
}
|
||||
for strings.Contains(name, "--") {
|
||||
name = strings.Replace(name, "--", "-", -1)
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
// NormalizeEmail canonicalize an email address.
|
||||
// The local part of the email address is lowercased for all domains; the hostname is always lowercased and
|
||||
// the local part of the email address is always lowercased for hosts that are known to be case-insensitive (currently only GMail).
|
||||
// Normalization follows special rules for known providers: currently, GMail addresses have dots removed in the local part and
|
||||
// are stripped of tags (e.g. some.one+tag@gmail.com becomes someone@gmail.com) and all @googlemail.com addresses are
|
||||
// normalized to @gmail.com.
|
||||
func NormalizeEmail(str string) (string, error) {
|
||||
if !IsEmail(str) {
|
||||
return "", fmt.Errorf("%s is not an email", str)
|
||||
}
|
||||
parts := strings.Split(str, "@")
|
||||
parts[0] = strings.ToLower(parts[0])
|
||||
parts[1] = strings.ToLower(parts[1])
|
||||
if parts[1] == "gmail.com" || parts[1] == "googlemail.com" {
|
||||
parts[1] = "gmail.com"
|
||||
parts[0] = strings.Split(ReplacePattern(parts[0], `\.`, ""), "+")[0]
|
||||
}
|
||||
return strings.Join(parts, "@"), nil
|
||||
}
|
||||
|
||||
// Truncate a string to the closest length without breaking words.
|
||||
func Truncate(str string, length int, ending string) string {
|
||||
var aftstr, befstr string
|
||||
if len(str) > length {
|
||||
words := strings.Fields(str)
|
||||
before, present := 0, 0
|
||||
for i := range words {
|
||||
befstr = aftstr
|
||||
before = present
|
||||
aftstr = aftstr + words[i] + " "
|
||||
present = len(aftstr)
|
||||
if present > length && i != 0 {
|
||||
if (length - before) < (present - length) {
|
||||
return Trim(befstr, " /\\.,\"'#!?&@+-") + ending
|
||||
}
|
||||
return Trim(aftstr, " /\\.,\"'#!?&@+-") + ending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
// PadLeft pad left side of string if size of string is less then indicated pad length
|
||||
func PadLeft(str string, padStr string, padLen int) string {
|
||||
return buildPadStr(str, padStr, padLen, true, false)
|
||||
}
|
||||
|
||||
// PadRight pad right side of string if size of string is less then indicated pad length
|
||||
func PadRight(str string, padStr string, padLen int) string {
|
||||
return buildPadStr(str, padStr, padLen, false, true)
|
||||
}
|
||||
|
||||
// PadBoth pad sides of string if size of string is less then indicated pad length
|
||||
func PadBoth(str string, padStr string, padLen int) string {
|
||||
return buildPadStr(str, padStr, padLen, true, true)
|
||||
}
|
||||
|
||||
// PadString either left, right or both sides, not the padding string can be unicode and more then one
|
||||
// character
|
||||
func buildPadStr(str string, padStr string, padLen int, padLeft bool, padRight bool) string {
|
||||
|
||||
// When padded length is less then the current string size
|
||||
if padLen < utf8.RuneCountInString(str) {
|
||||
return str
|
||||
}
|
||||
|
||||
padLen -= utf8.RuneCountInString(str)
|
||||
|
||||
targetLen := padLen
|
||||
|
||||
targetLenLeft := targetLen
|
||||
targetLenRight := targetLen
|
||||
if padLeft && padRight {
|
||||
targetLenLeft = padLen / 2
|
||||
targetLenRight = padLen - targetLenLeft
|
||||
}
|
||||
|
||||
strToRepeatLen := utf8.RuneCountInString(padStr)
|
||||
|
||||
repeatTimes := int(math.Ceil(float64(targetLen) / float64(strToRepeatLen)))
|
||||
repeatedString := strings.Repeat(padStr, repeatTimes)
|
||||
|
||||
leftSide := ""
|
||||
if padLeft {
|
||||
leftSide = repeatedString[0:targetLenLeft]
|
||||
}
|
||||
|
||||
rightSide := ""
|
||||
if padRight {
|
||||
rightSide = repeatedString[0:targetLenRight]
|
||||
}
|
||||
|
||||
return leftSide + str + rightSide
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,15 @@
|
|||
box: golang
|
||||
build:
|
||||
steps:
|
||||
- setup-go-workspace
|
||||
|
||||
- script:
|
||||
name: go get
|
||||
code: |
|
||||
go version
|
||||
go get -t ./...
|
||||
|
||||
- script:
|
||||
name: go test
|
||||
code: |
|
||||
go test -race ./...
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
package asn1
|
||||
|
||||
import (
|
||||
"encoding/asn1"
|
||||
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
type Linter struct {
|
||||
e errors.Errors
|
||||
}
|
||||
|
||||
// CheckStruct returns a list of errors based on strict checks on the raw ASN1
|
||||
// encoding of the input der.
|
||||
func (l *Linter) CheckStruct(der []byte) *errors.Errors {
|
||||
l.walk(der)
|
||||
if l.e.IsError() {
|
||||
return &l.e
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// walk is a recursive call that walks over the ASN1 structured data until no
|
||||
// remaining bytes are left. For each non compound is will call the ASN1 format
|
||||
// checker.
|
||||
func (l *Linter) walk(der []byte) {
|
||||
var err error
|
||||
var d asn1.RawValue
|
||||
|
||||
for len(der) > 0 {
|
||||
der, err = asn1.Unmarshal(der, &d)
|
||||
if err != nil {
|
||||
// Errors should be included in the report, but allow format checking when
|
||||
// data has been decoded.
|
||||
l.e.Err(err.Error())
|
||||
if len(d.Bytes) == 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// A compound is an ASN.1 container that contains other structs.
|
||||
if d.IsCompound {
|
||||
l.walk(d.Bytes)
|
||||
} else {
|
||||
l.CheckFormat(d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,233 +0,0 @@
|
|||
package asn1
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/asn1"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// RFC 5280 4.1.2.5.1: UTCTime MUST include seconds, even when 00
|
||||
var formatUTCTime = regexp.MustCompile("^([0-9]{2})([01][0-9])([0-3][0-9])([012][0-9])([0-5][0-9]){2}Z$")
|
||||
var formatGeneralizedTime = regexp.MustCompile("^([0-9]{4})([01][0-9])([0-3][0-9])([012][0-9])([0-5][0-9]){2}Z$")
|
||||
|
||||
// CheckFormat returns a list of formatting errors based on the expected ASN1
|
||||
// encoding according to the class and tag of the raw value.
|
||||
// TODO: Create checks for remaining class 0 tags
|
||||
// TODO: Should we create extensions for other classes, even include class 0?
|
||||
func (l *Linter) CheckFormat(d asn1.RawValue) {
|
||||
if d.Class == 0 {
|
||||
switch d.Tag {
|
||||
case 0: // "reserved for BER"
|
||||
case 1: // "BOOLEAN"
|
||||
case 2: // "INTEGER"
|
||||
case 3: // "BIT STRING"
|
||||
case 4: // "OCTET STRING"
|
||||
case 5: // "NULL"
|
||||
case 6: // "OBJECT IDENTIFIER"
|
||||
case 7: // "ObjectDescriptor"
|
||||
case 8: // "INSTANCE OF, EXTERNAL"
|
||||
case 9: // "REAL"
|
||||
case 10: // "ENUMERATED"
|
||||
case 11: // "EMBEDDED PDV"
|
||||
case 12: // "UTF8String"
|
||||
if !utf8.Valid(d.Bytes) {
|
||||
l.e.Err("Invalid UTF8 encoding in UTF8String")
|
||||
}
|
||||
if isForbiddenString(d.Bytes) {
|
||||
l.e.Err("Forbidden value in UTF8String '%s'", string(d.Bytes))
|
||||
}
|
||||
if isControlCharacter(d.Bytes) {
|
||||
l.e.Err("Control character in UTF8String '%s'", string(d.Bytes))
|
||||
}
|
||||
case 13: // "RELATIVE-OID"
|
||||
case 16: // "SEQUENCE, SEQUENCE OF"
|
||||
case 17: // "SET, SET OF"
|
||||
case 18: // "NumericString"
|
||||
if !isNumericString(d.Bytes) {
|
||||
l.e.Err("Invalid character in NumericString '%s'", string(d.Bytes))
|
||||
}
|
||||
case 19: // "PrintableString"
|
||||
for _, b := range d.Bytes {
|
||||
if !isPrintable(b) {
|
||||
l.e.Err("Invalid character in PrintableString '%s'", string(d.Bytes))
|
||||
}
|
||||
}
|
||||
if isForbiddenString(d.Bytes) {
|
||||
l.e.Err("Forbidden value in PrintableString '%s'", string(d.Bytes))
|
||||
}
|
||||
case 20: // "TeletexString, T61String"
|
||||
l.e.Warning("Using deprecated TeletexString for '%s'", string(d.Bytes))
|
||||
if isForbiddenString(d.Bytes) {
|
||||
l.e.Err("Forbidden value in TeletexString '%s'", string(d.Bytes))
|
||||
}
|
||||
if isControlCharacter(d.Bytes) {
|
||||
l.e.Err("Control character in TeletexString '%s'", string(d.Bytes))
|
||||
}
|
||||
case 21: // "VideotexString"
|
||||
l.e.Warning("Using deprecated VideotexString for '%s'", string(d.Bytes))
|
||||
if isForbiddenString(d.Bytes) {
|
||||
l.e.Err("Forbidden value in VideotexString '%s'", string(d.Bytes))
|
||||
}
|
||||
if isControlCharacter(d.Bytes) {
|
||||
l.e.Err("Control character in VideotexString '%s'", string(d.Bytes))
|
||||
}
|
||||
case 22: // "IA5String"
|
||||
if !isIA5String(d.Bytes) {
|
||||
l.e.Err("Invalid character in IA5String '%s'", string(d.Bytes))
|
||||
}
|
||||
if isForbiddenString(d.Bytes) {
|
||||
l.e.Err("Forbidden value in IA5String '%s'", string(d.Bytes))
|
||||
}
|
||||
|
||||
case 23: // "UTCTime"
|
||||
// RFC 5280 4.1.2.5: times must be in Z (GMT)
|
||||
if !bytes.HasSuffix(d.Bytes, []byte{90}) {
|
||||
l.e.Err("UTCTime not in Zulu/GMT")
|
||||
}
|
||||
if !formatUTCTime.Match(d.Bytes) {
|
||||
l.e.Err("Invalid UTCTime")
|
||||
}
|
||||
case 24: // "GeneralizedTime"
|
||||
var v time.Time
|
||||
_, err := asn1.Unmarshal(d.FullBytes, &v)
|
||||
if err != nil {
|
||||
l.e.Err("Failed to parse Generalized Time: %s", err.Error())
|
||||
}
|
||||
|
||||
// RFC 5280 4.1.2.5: times must be in Z (GMT)
|
||||
if !bytes.HasSuffix(d.Bytes, []byte{90}) {
|
||||
l.e.Err("Generalized Time not in Zulu/GMT")
|
||||
}
|
||||
// TODO: Can we use binary.BigEndian.Varint(d.Bytes[0:3]), for better performance?
|
||||
if v.Year() < 2050 {
|
||||
l.e.Err("Generalized Time before 2050")
|
||||
}
|
||||
|
||||
if !formatGeneralizedTime.Match(d.Bytes) {
|
||||
l.e.Err("Invalid Generalized Time")
|
||||
}
|
||||
|
||||
case 25: // "GraphicString"
|
||||
l.e.Warning("Using deprecated GraphicString for '%s'", string(d.Bytes))
|
||||
if isForbiddenString(d.Bytes) {
|
||||
l.e.Err("Forbidden value in GraphicString '%s'", string(d.Bytes))
|
||||
}
|
||||
if isControlCharacter(d.Bytes) {
|
||||
l.e.Err("Control character in GraphicString '%s'", string(d.Bytes))
|
||||
}
|
||||
case 26: // "VisibleString, ISO646String"
|
||||
case 27: // "GeneralString"
|
||||
l.e.Warning("Using deprecated GeneralString for '%s'", string(d.Bytes))
|
||||
if isForbiddenString(d.Bytes) {
|
||||
l.e.Err("Forbidden value in GeneralString '%s'", string(d.Bytes))
|
||||
}
|
||||
if isControlCharacter(d.Bytes) {
|
||||
l.e.Err("Control character in GeneralString '%s'", string(d.Bytes))
|
||||
}
|
||||
case 28: // "UniversalString"
|
||||
l.e.Warning("Using deprecated UniversalString for '%s'", string(d.Bytes))
|
||||
if isForbiddenString(d.Bytes) {
|
||||
l.e.Err("Forbidden value in UniversalString '%s'", string(d.Bytes))
|
||||
}
|
||||
if isControlCharacter(d.Bytes) {
|
||||
l.e.Err("Control character in UniversalString '%s'", string(d.Bytes))
|
||||
}
|
||||
case 29: // "CHARACTER STRING"
|
||||
case 30: // "BMPString"
|
||||
l.e.Warning("Using deprecated BMPString for '%s'", string(d.Bytes))
|
||||
if isForbiddenString(d.Bytes) {
|
||||
l.e.Err("Forbidden value in BMPString '%s'", string(d.Bytes))
|
||||
}
|
||||
if isControlCharacter(d.Bytes) {
|
||||
l.e.Err("Control character in BMPString '%s'", string(d.Bytes))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Version of isPrintable without allowing a *
|
||||
// Source: https://golang.org/src/encoding/asn1/asn1.go
|
||||
func isPrintable(b byte) bool {
|
||||
return 'a' <= b && b <= 'z' ||
|
||||
'A' <= b && b <= 'Z' ||
|
||||
'0' <= b && b <= '9' ||
|
||||
'\'' <= b && b <= ')' ||
|
||||
'+' <= b && b <= '/' ||
|
||||
b == ' ' ||
|
||||
b == ':' ||
|
||||
b == '=' ||
|
||||
b == '?'
|
||||
}
|
||||
|
||||
// Range from: http://www.zytrax.com/tech/ia5.html
|
||||
func isIA5String(b []byte) bool {
|
||||
for len(b) > 0 {
|
||||
r, size := utf8.DecodeRune(b)
|
||||
if r < 0 || r > 127 {
|
||||
return false
|
||||
}
|
||||
b = b[size:]
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, and SPACE
|
||||
func isNumericString(b []byte) bool {
|
||||
for len(b) > 0 {
|
||||
r, size := utf8.DecodeRune(b)
|
||||
if !unicode.IsNumber(r) && !unicode.IsSpace(r) {
|
||||
return false
|
||||
}
|
||||
b = b[size:]
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// The BR state that attributes MUST NOT contain metadata such as '.', '-', ' ',
|
||||
// this check implements a structure wide validation for values that indication
|
||||
// that the field is absent, incomplete, or not applicable.
|
||||
//
|
||||
// ASCII range of forbidden metadata characters are 32 - 47, 58 -64, 91 - 96,
|
||||
// 123 - 126, if the value does only contain metadata this value is forbidden.
|
||||
// This check does also detect double characters or any combination of metadata
|
||||
// characters.
|
||||
func isForbiddenString(b []byte) bool {
|
||||
for len(b) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
switch strings.ToLower(string(b)) {
|
||||
case "n/a":
|
||||
return true
|
||||
}
|
||||
|
||||
for len(b) > 0 {
|
||||
r, size := utf8.DecodeRune(b)
|
||||
if !((r >= 32 && r <= 47) ||
|
||||
(r >= 58 && r <= 64) ||
|
||||
(r >= 91 && r <= 96) ||
|
||||
(r >= 123 && r <= 126)) {
|
||||
// non metadata character included in value
|
||||
return false
|
||||
}
|
||||
b = b[size:]
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// isControlCharacter checks if Control characters are included in the given bytes
|
||||
func isControlCharacter(b []byte) bool {
|
||||
for len(b) > 0 {
|
||||
r, size := utf8.DecodeRune(b)
|
||||
if unicode.IsControl(r) || unicode.Is(unicode.C, r) {
|
||||
return true
|
||||
}
|
||||
b = b[size:]
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
package certdata
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Data holds the certificate and relevant information
|
||||
// Type can be DV, OV, EV, PS, CS, EVCS, TS, OCSP, CA
|
||||
type Data struct {
|
||||
Cert *x509.Certificate
|
||||
Issuer *x509.Certificate
|
||||
Type string
|
||||
}
|
||||
|
||||
// Load raw certificate bytes into a Data struct
|
||||
func Load(der []byte) (*Data, error) {
|
||||
var err error
|
||||
|
||||
d := new(Data)
|
||||
d.Cert, err = x509.ParseCertificate(der)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = d.setCertificateType(); err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// SetIssuer sets the issuer of a certificate
|
||||
// TODO: Validate if the correct issuer is given
|
||||
func (d *Data) SetIssuer(der []byte) error {
|
||||
var err error
|
||||
d.Issuer, err = x509.ParseCertificate(der)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
package certdata
|
||||
|
||||
import "encoding/asn1"
|
||||
|
||||
// Source GlobalSign CP and Cabforum BR
|
||||
// https://www.globalsign.com/en/repository/GlobalSign_CP_v5.3.pdf
|
||||
var polOidType []oidType
|
||||
|
||||
type oidType struct {
|
||||
ObjectIdentifier asn1.ObjectIdentifier
|
||||
Type string
|
||||
}
|
||||
|
||||
func getType(oid []asn1.ObjectIdentifier) string {
|
||||
for _, poid := range oid {
|
||||
for _, oidt := range polOidType {
|
||||
if poid.Equal(oidt.ObjectIdentifier) {
|
||||
return oidt.Type
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// TODO: Can we handle this differently, we might want to use a constant here?
|
||||
func init() {
|
||||
// Extended Validation
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 4146, 1, 1}, "EV"}) // Extended Validation Certificates Policy – SSL
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 4146, 1, 2}, "CS"}) // Extended Validation Certificates Policy – Code Signing
|
||||
|
||||
// Domain Validation
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 4146, 1, 10}, "DV"}) // Domain Validation Certificates Policy
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 4146, 1, 10, 10}, "DV"}) // Domain Validation Certificates Policy – AlphaSSL
|
||||
|
||||
// Organization Validation
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 4146, 1, 20}, "OV"}) // Organization Validation Certificates Policy
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 4146, 1, 21}, "-"}) // Untrusted OneClickSSL Test Certificate (not in cp)
|
||||
|
||||
// Intranet Validation
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 4146, 1, 25}, "IN"}) // IntranetSSL Validation Certificates Policy
|
||||
|
||||
// Time Stamping
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 4146, 1, 30}, "TS"}) // Time Stamping Certificates Policy
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 4146, 1, 31}, "TS"}) // Time Stamping Certificates Policy – AATL
|
||||
|
||||
// Client Certificates
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 4146, 1, 40}, "PS"}) // Client Certificates Policy (Generic)
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 4146, 1, 40, 10}, "PS"}) // Client Certificates Policy (ePKI – Enterprise PKI)
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 4146, 1, 40, 20}, "PS"}) // Client Certificates Policy (JCAN – Japan CA Network)
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 4146, 1, 40, 30}, "PS"}) // Client Certificates Policy (AATL)
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 4146, 1, 40, 40}, "PS"}) // Client Certificates Policy (ePKI for private CAs)
|
||||
|
||||
// Code Signing
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 4146, 1, 50}, "CS"}) // Code Signing Certificates Policy
|
||||
|
||||
// CA Chaining and Cross Signing
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 4146, 1, 60}, "CA"}) // CA Chaining Policy – Trusted Root and Hosted Root
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 4146, 1, 60, 1}, "CA"}) // CA Chaining Policy – Trusted Root (Baseline Requirements Compatible)
|
||||
|
||||
// Others
|
||||
/*polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1,3,6,1,4,1,4146,1,80}, "XX"}) // Retail Industry Electronic Data Interchange Client Certificate Policy
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1,3,6,1,4,1,4146,1,81}, "XX"}) // Retail Industry Electronic Data Interchange Server Certificate Policy
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1,3,6,1,4,1,4146,1,90}, "XX"}) // Trusted Root TPM Policy
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1,3,6,1,4,1,4146,1,95}, "XX"}) // Online Certificate Status Protocol Policy
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1,3,6,1,4,1,4146,1,70}, "XX"}) // High Volume CA Policy
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1,3,6,1,4,1,4146,1,26}, "XX"}) // Test Certificate Policy (Should not be trusted)
|
||||
|
||||
// In addition to these identifiers, all Certificates that comply with the NAESB Business
|
||||
// Practice Standards will include one of the following additional identifiers:-
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{2,16,840,1,114505,1,12,1,2}, "XX"}) // NAESB Rudimentary Assurance
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{2,16,840,1,114505,1,12,2,2}, "XX"}) // NAESB Basic Assurance
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{2,16,840,1,114505,1,12,3,2}, "XX"}) // NAESB Medium Assurance
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{2,16,840,1,114505,1,12,4,2}, "XX"}) // NAESB High Assurance
|
||||
*/
|
||||
// In addition to these identifiers, all Certificates that comply with the Baseline
|
||||
// Requirements will include the following additional identifiers:-
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{2, 23, 140, 1, 1}, "EV"}) // Extended Validation Certificate Policy
|
||||
//polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{2,23,140,1,2}, ""}) // BR Compliance Certificate Policy
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{2, 23, 140, 1, 3}, "EVCS"}) // Extended Validation Code Signing Certificates Policy
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{2, 23, 140, 1, 4}, "CS"}) // BR Compliance Code Signing Certificates Policy
|
||||
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{2, 23, 140, 1, 2, 1}, "DV"}) // Domain Validation Certificates Policy
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{2, 23, 140, 1, 2, 2}, "OV"}) // Organization Validation Certificates Policy
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{2, 23, 140, 1, 2, 3}, "IV"}) // Individual Validation Certificates Policy
|
||||
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1, 2, 840, 113583, 1, 2, 1}, "PS"}) // Adobe Certificate Policy Attribute Object Identifier (PDF)
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1, 2, 840, 113583, 1, 2, 2}, "PS"}) // Test Adobe Certificate Policy Attribute Object Identifier
|
||||
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 4146, 1, 40, 30, 2}, "PS"}) // AATL Adobe Certificate Policy Attribute Object Identifier
|
||||
polOidType = append(polOidType, oidType{asn1.ObjectIdentifier{1, 2, 392, 200063, 30, 5300}, "PS"}) // JCAN
|
||||
}
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
package certdata
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
psl "golang.org/x/net/publicsuffix"
|
||||
)
|
||||
|
||||
// setCertificateType set the base on how we check for other requirements of the
|
||||
// certificate. It's important that we reliably identify the purpose to apply
|
||||
// the right checks for that certificate type.
|
||||
func (d *Data) setCertificateType() error {
|
||||
// We want to be able to detect 'false' CA certificates, classify as CA
|
||||
// certificate is basic contains and key usage certsign are set.
|
||||
if d.Cert.IsCA && d.Cert.KeyUsage&x509.KeyUsageCertSign != 0 {
|
||||
d.Type = "CA"
|
||||
return nil
|
||||
}
|
||||
|
||||
// The fallback type is used when a certificate could be any of a range
|
||||
// but further checks need to define the exact type. When these checks fail
|
||||
// the fallback type is used.
|
||||
var fallbackType string
|
||||
|
||||
// Based on ExtKeyUsage
|
||||
for _, ku := range d.Cert.ExtKeyUsage {
|
||||
switch ku {
|
||||
case x509.ExtKeyUsageServerAuth:
|
||||
// Try to determine certificate type via policy oid
|
||||
d.Type = getType(d.Cert.PolicyIdentifiers)
|
||||
fallbackType = "DV"
|
||||
case x509.ExtKeyUsageClientAuth:
|
||||
fallbackType = "PS"
|
||||
case x509.ExtKeyUsageEmailProtection:
|
||||
d.Type = "PS"
|
||||
case x509.ExtKeyUsageCodeSigning:
|
||||
d.Type = "CS"
|
||||
case x509.ExtKeyUsageTimeStamping:
|
||||
d.Type = "TS"
|
||||
case x509.ExtKeyUsageOCSPSigning:
|
||||
d.Type = "OCSP"
|
||||
}
|
||||
}
|
||||
|
||||
// If we have no kown key usage, try the policy list again
|
||||
if d.Type == "" {
|
||||
d.Type = getType(d.Cert.PolicyIdentifiers)
|
||||
}
|
||||
|
||||
// When determined by Policy Identifier we can stop
|
||||
if d.Type != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Based on UnknownExtKeyUsage
|
||||
for _, ku := range d.Cert.UnknownExtKeyUsage {
|
||||
switch {
|
||||
case ku.Equal(asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 21, 19}):
|
||||
// dsEmailReplication
|
||||
d.Type = "PS"
|
||||
return nil
|
||||
case ku.Equal(asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 8, 2, 2}):
|
||||
// IPSEC Protection
|
||||
d.Type = "IPSEC"
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the e-mailAddress is set in the DN
|
||||
for _, n := range d.Cert.Subject.Names {
|
||||
switch {
|
||||
case n.Type.Equal(asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}): // e-mailAddress
|
||||
d.Type = "PS"
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// An @ sing in the common name is often used in PS.
|
||||
if strings.Contains(d.Cert.Subject.CommonName, "@") {
|
||||
d.Type = "PS"
|
||||
return nil
|
||||
} else if strings.Contains(d.Cert.Subject.CommonName, " ") {
|
||||
d.Type = "PS"
|
||||
return nil
|
||||
}
|
||||
|
||||
// If it's a fqdn, it's a EV, OV or DV
|
||||
if suffix, _ := psl.PublicSuffix(strings.ToLower(d.Cert.Subject.CommonName)); len(suffix) > 0 {
|
||||
if len(d.Cert.Subject.Organization) > 0 {
|
||||
if len(d.Cert.Subject.SerialNumber) > 0 {
|
||||
d.Type = "EV"
|
||||
return nil
|
||||
}
|
||||
|
||||
d.Type = "OV"
|
||||
return nil
|
||||
}
|
||||
|
||||
d.Type = "DV"
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(fallbackType) > 0 {
|
||||
d.Type = fallbackType
|
||||
return nil
|
||||
}
|
||||
|
||||
if d.Type == "" {
|
||||
return fmt.Errorf("Could not determine certificate type")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
package checks
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
var certMutex = &sync.Mutex{}
|
||||
|
||||
type certificate []certificateCheck
|
||||
|
||||
type certificateCheck struct {
|
||||
name string
|
||||
filter *Filter
|
||||
f func(*certdata.Data) *errors.Errors
|
||||
}
|
||||
|
||||
// Certificate contains all imported certificate checks
|
||||
var Certificate certificate
|
||||
|
||||
// RegisterCertificateCheck adds a new check to Cerificates
|
||||
func RegisterCertificateCheck(name string, filter *Filter, f func(*certdata.Data) *errors.Errors) {
|
||||
certMutex.Lock()
|
||||
Certificate = append(Certificate, certificateCheck{name, filter, f})
|
||||
certMutex.Unlock()
|
||||
}
|
||||
|
||||
// Check runs all the registered certificate checks
|
||||
func (c certificate) Check(d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
for _, cc := range c {
|
||||
if cc.filter != nil && !cc.filter.Check(d) {
|
||||
continue
|
||||
}
|
||||
e.Append(cc.f(d))
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
56
vendor/github.com/globalsign/certlint/checks/certificate/aiaissuers/aiaissuers.go
generated
vendored
56
vendor/github.com/globalsign/certlint/checks/certificate/aiaissuers/aiaissuers.go
generated
vendored
|
|
@ -1,56 +0,0 @@
|
|||
package aiaissuers
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "Authority Info Access Issuers Check"
|
||||
|
||||
func init() {
|
||||
checks.RegisterCertificateCheck(checkName, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
// OCSP signing certificates should not any Authority Info Access Issuers
|
||||
if d.Type == "OCSP" {
|
||||
if len(d.Cert.IssuingCertificateURL) > 0 {
|
||||
e.Warning("OCSP signing certificate contains any Authority Info Access Issuers")
|
||||
}
|
||||
|
||||
// no extra checks needed
|
||||
return e
|
||||
}
|
||||
|
||||
// Self signed CA certificates should not contain any AIA Issuers
|
||||
if d.Type == "CA" && d.Cert.CheckSignatureFrom(d.Cert) == nil {
|
||||
if len(d.Cert.IssuingCertificateURL) != 0 {
|
||||
e.Warning("Self signed CA certificates should not contain any Authority Info Access Issuers")
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// Other certificates should contain at least one Authority Info Access Issuer
|
||||
if len(d.Cert.IssuingCertificateURL) == 0 {
|
||||
e.Err("Certificate contains no Authority Info Access Issuers")
|
||||
return e
|
||||
}
|
||||
|
||||
for _, icu := range d.Cert.IssuingCertificateURL {
|
||||
l, err := url.Parse(icu)
|
||||
if err != nil {
|
||||
e.Err("Certificate contains an invalid Authority Info Access Issuer URL (%s)", icu)
|
||||
}
|
||||
if l.Scheme != "http" {
|
||||
e.Warning("Certificate contains a Authority Info Access Issuer with an non-preferred scheme (%s)", l.Scheme)
|
||||
}
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
package all
|
||||
|
||||
import (
|
||||
// Import all default checks
|
||||
_ "github.com/globalsign/certlint/checks/certificate/aiaissuers"
|
||||
_ "github.com/globalsign/certlint/checks/certificate/basicconstraints"
|
||||
_ "github.com/globalsign/certlint/checks/certificate/extensions"
|
||||
_ "github.com/globalsign/certlint/checks/certificate/extkeyusage"
|
||||
_ "github.com/globalsign/certlint/checks/certificate/internal"
|
||||
_ "github.com/globalsign/certlint/checks/certificate/issuerdn"
|
||||
_ "github.com/globalsign/certlint/checks/certificate/keyusage"
|
||||
_ "github.com/globalsign/certlint/checks/certificate/publickey"
|
||||
_ "github.com/globalsign/certlint/checks/certificate/publicsuffix"
|
||||
_ "github.com/globalsign/certlint/checks/certificate/revocation"
|
||||
_ "github.com/globalsign/certlint/checks/certificate/serialnumber"
|
||||
_ "github.com/globalsign/certlint/checks/certificate/signaturealgorithm"
|
||||
_ "github.com/globalsign/certlint/checks/certificate/subject"
|
||||
_ "github.com/globalsign/certlint/checks/certificate/subjectaltname"
|
||||
_ "github.com/globalsign/certlint/checks/certificate/validity"
|
||||
_ "github.com/globalsign/certlint/checks/certificate/version"
|
||||
_ "github.com/globalsign/certlint/checks/certificate/wildcard"
|
||||
)
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
package basicconstraints
|
||||
|
||||
import (
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "Basic Constraints Check"
|
||||
|
||||
func init() {
|
||||
checks.RegisterCertificateCheck(checkName, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
switch d.Type {
|
||||
case "DV", "OV", "EV":
|
||||
if d.Cert.IsCA {
|
||||
e.Err("Certificate has set CA true")
|
||||
}
|
||||
if d.Cert.MaxPathLen == 0 && d.Cert.MaxPathLenZero {
|
||||
//e.Err("Certificate has set CA true")
|
||||
}
|
||||
if d.Cert.BasicConstraintsValid {
|
||||
//e.Err("Certificate has set CA true")
|
||||
}
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
23
vendor/github.com/globalsign/certlint/checks/certificate/extensions/extensions.go
generated
vendored
23
vendor/github.com/globalsign/certlint/checks/certificate/extensions/extensions.go
generated
vendored
|
|
@ -1,23 +0,0 @@
|
|||
package extensions
|
||||
|
||||
import (
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "Extensions Check"
|
||||
|
||||
func init() {
|
||||
checks.RegisterCertificateCheck(checkName, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
for _, ext := range d.Cert.Extensions {
|
||||
// Check for any imported extensions and run all matching
|
||||
e.Append(checks.Extensions.Check(ext, d))
|
||||
}
|
||||
return e
|
||||
}
|
||||
56
vendor/github.com/globalsign/certlint/checks/certificate/extkeyusage/extkeyusage.go
generated
vendored
56
vendor/github.com/globalsign/certlint/checks/certificate/extkeyusage/extkeyusage.go
generated
vendored
|
|
@ -1,56 +0,0 @@
|
|||
package extkeyusage
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "Extended Key Usage Check"
|
||||
|
||||
func init() {
|
||||
checks.RegisterCertificateCheck(checkName, nil, Check)
|
||||
}
|
||||
|
||||
// Check verifies if the the required/allowed extended keyusages
|
||||
// are set in relation to the certificate type.
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc5280#section-4.2.1.12
|
||||
//
|
||||
func Check(d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
if len(d.Cert.ExtKeyUsage) == 0 && len(d.Cert.UnknownExtKeyUsage) == 0 {
|
||||
e.Err("Certificate contains no extended key usage")
|
||||
return e
|
||||
}
|
||||
|
||||
for _, ku := range d.Cert.ExtKeyUsage {
|
||||
switch d.Type {
|
||||
case "DV", "OV", "EV":
|
||||
if ku != x509.ExtKeyUsageServerAuth && ku != x509.ExtKeyUsageClientAuth && ku != x509.ExtKeyUsageMicrosoftServerGatedCrypto {
|
||||
e.Err("Certificate contains an extended key usage different from ServerAuth, ClientAuth or ServerGatedCrypto")
|
||||
return e
|
||||
}
|
||||
case "PS":
|
||||
if ku != x509.ExtKeyUsageClientAuth && ku != x509.ExtKeyUsageEmailProtection {
|
||||
e.Err("Certificate contains an extended key usage different from ClientAuth or EmailProtection")
|
||||
return e
|
||||
}
|
||||
case "CS":
|
||||
if ku != x509.ExtKeyUsageCodeSigning {
|
||||
e.Err("Certificate contains an extended key usage different from ClientAuth or EmailProtection")
|
||||
return e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(d.Cert.UnknownExtKeyUsage) > 0 {
|
||||
// encryptedFileSystem 1.3.6.1.4.1.311.10.3.4
|
||||
//return fmt.Errorf("%s Certificate contains an unknown extented key usage", d.Type)
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
psl "golang.org/x/net/publicsuffix"
|
||||
)
|
||||
|
||||
// All official domain suffixes are registered by icann, but because some
|
||||
// subdomains are not only check against the last part of the fqdn.
|
||||
func checkInternalName(fqdn string) bool {
|
||||
if ip := net.ParseIP(fqdn); ip != nil {
|
||||
return checkInternalIP(ip)
|
||||
}
|
||||
|
||||
suffix := strings.Split(strings.ToLower(fqdn), ".")
|
||||
_, icann := psl.PublicSuffix(suffix[len(suffix)-1])
|
||||
if icann {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "Internal Names and IP addresses Check"
|
||||
|
||||
func init() {
|
||||
filter := &checks.Filter{
|
||||
Type: []string{"DV", "OV", "IV", "EV"},
|
||||
}
|
||||
checks.RegisterCertificateCheck(checkName, filter, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
// TODO: Add more checks https://golang.org/src/crypto/x509/x509.go?s=15439:18344#L1157
|
||||
func Check(d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
if checkInternalName(d.Cert.Subject.CommonName) {
|
||||
e.Err("Certificate contains an internal server name in the common name '%s'", d.Cert.Subject.CommonName)
|
||||
}
|
||||
for _, n := range d.Cert.DNSNames {
|
||||
if checkInternalName(n) {
|
||||
e.Err("Certificate subjectAltName '%s' contains an internal server name", n)
|
||||
}
|
||||
}
|
||||
|
||||
// Check for internal IP addresses
|
||||
for _, ip := range d.Cert.IPAddresses {
|
||||
if !ip.IsGlobalUnicast() {
|
||||
e.Err("Certificate subjectAltName '%v' contains a non global unicast IP address", ip)
|
||||
}
|
||||
if checkInternalIP(ip) {
|
||||
e.Err("Certificate subjectAltName '%v' contains a private or local IP address", ip)
|
||||
}
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
32
vendor/github.com/globalsign/certlint/checks/certificate/internal/ipaddress.go
generated
vendored
32
vendor/github.com/globalsign/certlint/checks/certificate/internal/ipaddress.go
generated
vendored
|
|
@ -1,32 +0,0 @@
|
|||
package internal
|
||||
|
||||
import "net"
|
||||
|
||||
// checkInternalIP verifies is an IP address in a registered internal range.
|
||||
// TODO: Check if we need to verify any IPv6 ranges
|
||||
// TODO: We should also check for special purpose IP ranges, we might want to do that in a specific function
|
||||
func checkInternalIP(ip net.IP) bool {
|
||||
var privIPSpace []net.IPNet
|
||||
// 10.0.0.0/8
|
||||
privIPSpace = append(privIPSpace, net.IPNet{
|
||||
IP: net.IP{0xa, 0x0, 0x0, 0x0},
|
||||
Mask: net.IPMask{0xff, 0x0, 0x0, 0x0},
|
||||
})
|
||||
// 172.16.0.0/12"
|
||||
privIPSpace = append(privIPSpace, net.IPNet{
|
||||
IP: net.IP{0xac, 0x10, 0x0, 0x0},
|
||||
Mask: net.IPMask{0xff, 0xf0, 0x0, 0x0},
|
||||
})
|
||||
// 192.168.0.0/16
|
||||
privIPSpace = append(privIPSpace, net.IPNet{
|
||||
IP: net.IP{0xc0, 0xa8, 0x0, 0x0},
|
||||
Mask: net.IPMask{0xff, 0xff, 0x0, 0x0},
|
||||
})
|
||||
|
||||
for _, ipSpace := range privIPSpace {
|
||||
if ipSpace.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
package version
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "Issuer DN Check"
|
||||
|
||||
func init() {
|
||||
checks.RegisterCertificateCheck(checkName, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
if d.Issuer != nil && !bytes.Equal(d.Cert.RawIssuer, d.Issuer.RawSubject) {
|
||||
e.Err("Certificate Issuer Distinguished Name field MUST match the Subject DN of the Issuing CA")
|
||||
return e
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
package keyusage
|
||||
|
||||
import (
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "Key Usage Check"
|
||||
|
||||
func init() {
|
||||
checks.RegisterCertificateCheck(checkName, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
// checkKeyUsageExtension verifies if the the required/allowed keyusages are set
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc5280#section-4.2.1.3
|
||||
//
|
||||
// TODO: Check if we can or need to do something with dh.PublicKey
|
||||
func Check(d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
var forbidden []x509.KeyUsage
|
||||
|
||||
// Source
|
||||
// https://github.com/awslabs/certlint/blob/master/lib/certlint/extensions/keyusage.rb
|
||||
switch d.Cert.PublicKey.(type) {
|
||||
case *rsa.PublicKey:
|
||||
forbidden = []x509.KeyUsage{
|
||||
x509.KeyUsageKeyAgreement,
|
||||
x509.KeyUsageEncipherOnly,
|
||||
x509.KeyUsageDecipherOnly,
|
||||
}
|
||||
case *ecdsa.PublicKey:
|
||||
forbidden = []x509.KeyUsage{
|
||||
x509.KeyUsageKeyEncipherment,
|
||||
x509.KeyUsageDataEncipherment,
|
||||
}
|
||||
case *dsa.PublicKey:
|
||||
forbidden = []x509.KeyUsage{
|
||||
x509.KeyUsageKeyEncipherment,
|
||||
x509.KeyUsageDataEncipherment,
|
||||
x509.KeyUsageKeyAgreement,
|
||||
x509.KeyUsageEncipherOnly,
|
||||
x509.KeyUsageDecipherOnly,
|
||||
}
|
||||
// case *dh.PublicKey:
|
||||
// forbidden = []x509.KeyUsage{
|
||||
// x509.KeyUsageDigitalSignature,
|
||||
// x509.KeyUsageContentCommitment,
|
||||
// x509.KeyUsageKeyEncipherment,
|
||||
// x509.KeyUsageDataEncipherment,
|
||||
// x509.KeyUsageCertSign,
|
||||
// x509.KeyUsageCRLSign,
|
||||
// }
|
||||
}
|
||||
|
||||
// If we have not defined this certificate as a CA certificate, the following
|
||||
// key ussages would not be allowed
|
||||
if d.Type != "CA" {
|
||||
if d.Cert.KeyUsage == 0 {
|
||||
e.Err("Certificate has no key usage set")
|
||||
return e
|
||||
}
|
||||
|
||||
forbidden = append(forbidden, x509.KeyUsageCertSign)
|
||||
forbidden = append(forbidden, x509.KeyUsageCRLSign)
|
||||
}
|
||||
|
||||
// Check if there are any forbidden key usages set
|
||||
for _, fku := range forbidden {
|
||||
if d.Cert.KeyUsage&fku != 0 {
|
||||
e.Err("Certificate has key usage %s set", keyUsageString(fku))
|
||||
}
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
28
vendor/github.com/globalsign/certlint/checks/certificate/keyusage/keyusagestring.go
generated
vendored
28
vendor/github.com/globalsign/certlint/checks/certificate/keyusage/keyusagestring.go
generated
vendored
|
|
@ -1,28 +0,0 @@
|
|||
package keyusage
|
||||
|
||||
import "crypto/x509"
|
||||
|
||||
// keyUsageString returns the name of the keyusage as string
|
||||
func keyUsageString(ku x509.KeyUsage) string {
|
||||
switch ku {
|
||||
case x509.KeyUsageDigitalSignature:
|
||||
return "DigitalSignature"
|
||||
case x509.KeyUsageContentCommitment:
|
||||
return "ContentCommitment"
|
||||
case x509.KeyUsageKeyEncipherment:
|
||||
return "KeyEncipherment"
|
||||
case x509.KeyUsageDataEncipherment:
|
||||
return "DataEncipherment"
|
||||
case x509.KeyUsageKeyAgreement:
|
||||
return "KeyAgreement"
|
||||
case x509.KeyUsageCertSign:
|
||||
return "CertSign"
|
||||
case x509.KeyUsageCRLSign:
|
||||
return "CRLSign"
|
||||
case x509.KeyUsageEncipherOnly:
|
||||
return "EncipherOnly"
|
||||
case x509.KeyUsageDecipherOnly:
|
||||
return "DecipherOnly"
|
||||
}
|
||||
return "Unknown"
|
||||
}
|
||||
375
vendor/github.com/globalsign/certlint/checks/certificate/publickey/goodkey/LICENSE.txt
generated
vendored
375
vendor/github.com/globalsign/certlint/checks/certificate/publickey/goodkey/LICENSE.txt
generated
vendored
|
|
@ -1,375 +0,0 @@
|
|||
Copyright 2016 ISRG. All rights reserved.
|
||||
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
246
vendor/github.com/globalsign/certlint/checks/certificate/publickey/goodkey/goodkey.go
generated
vendored
246
vendor/github.com/globalsign/certlint/checks/certificate/publickey/goodkey/goodkey.go
generated
vendored
|
|
@ -1,246 +0,0 @@
|
|||
// Package goodkey copied from "github.com/letsencrypt/boulder/goodkey"
|
||||
//
|
||||
// This package is vovered under the Mozilla Public License Version 2.0
|
||||
//
|
||||
// Removed depency on letsencrypt core and allow key size above 4096
|
||||
package goodkey
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// To generate, run: primes 2 752 | tr '\n' ,
|
||||
var smallPrimeInts = []int64{
|
||||
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
|
||||
53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107,
|
||||
109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167,
|
||||
173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229,
|
||||
233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283,
|
||||
293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359,
|
||||
367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431,
|
||||
433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491,
|
||||
499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571,
|
||||
577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641,
|
||||
643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709,
|
||||
719, 727, 733, 739, 743, 751,
|
||||
}
|
||||
|
||||
// singleton defines the object of a Singleton pattern
|
||||
var (
|
||||
smallPrimesSingleton sync.Once
|
||||
smallPrimes []*big.Int
|
||||
)
|
||||
|
||||
// KeyPolicy determines which types of key may be used with various boulder
|
||||
// operations.
|
||||
type KeyPolicy struct {
|
||||
AllowRSA bool // Whether RSA keys should be allowed.
|
||||
AllowECDSANISTP256 bool // Whether ECDSA NISTP256 keys should be allowed.
|
||||
AllowECDSANISTP384 bool // Whether ECDSA NISTP384 keys should be allowed.
|
||||
}
|
||||
|
||||
// NewKeyPolicy returns a KeyPolicy that allows RSA, ECDSA256 and ECDSA384.
|
||||
func NewKeyPolicy() KeyPolicy {
|
||||
return KeyPolicy{
|
||||
AllowRSA: true,
|
||||
AllowECDSANISTP256: true,
|
||||
AllowECDSANISTP384: true,
|
||||
}
|
||||
}
|
||||
|
||||
// GoodKey returns true if the key is acceptable for both TLS use and account
|
||||
// key use (our requirements are the same for either one), according to basic
|
||||
// strength and algorithm checking.
|
||||
// TODO: Support JsonWebKeys once go-jose migration is done.
|
||||
func (policy *KeyPolicy) GoodKey(key crypto.PublicKey) error {
|
||||
switch t := key.(type) {
|
||||
case rsa.PublicKey:
|
||||
return policy.goodKeyRSA(t)
|
||||
case *rsa.PublicKey:
|
||||
return policy.goodKeyRSA(*t)
|
||||
case ecdsa.PublicKey:
|
||||
return policy.goodKeyECDSA(t)
|
||||
case *ecdsa.PublicKey:
|
||||
return policy.goodKeyECDSA(*t)
|
||||
default:
|
||||
return fmt.Errorf("Unknown key type %s", reflect.TypeOf(key))
|
||||
}
|
||||
}
|
||||
|
||||
// GoodKeyECDSA determines if an ECDSA pubkey meets our requirements
|
||||
func (policy *KeyPolicy) goodKeyECDSA(key ecdsa.PublicKey) (err error) {
|
||||
// Check the curve.
|
||||
//
|
||||
// The validity of the curve is an assumption for all following tests.
|
||||
err = policy.goodCurve(key.Curve)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Key validation routine adapted from NIST SP800-56A § 5.6.2.3.2.
|
||||
// <http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar2.pdf>
|
||||
//
|
||||
// Assuming a prime field since a) we are only allowing such curves and b)
|
||||
// crypto/elliptic only supports prime curves. Where this assumption
|
||||
// simplifies the code below, it is explicitly stated and explained. If ever
|
||||
// adapting this code to support non-prime curves, refer to NIST SP800-56A §
|
||||
// 5.6.2.3.2 and adapt this code appropriately.
|
||||
params := key.Params()
|
||||
|
||||
// SP800-56A § 5.6.2.3.2 Step 1.
|
||||
// Partial check of the public key for an invalid range in the EC group:
|
||||
// Verify that key is not the point at infinity O.
|
||||
// This code assumes that the point at infinity is (0,0), which is the
|
||||
// case for all supported curves.
|
||||
if isPointAtInfinityNISTP(key.X, key.Y) {
|
||||
return fmt.Errorf("Key x, y must not be the point at infinity")
|
||||
}
|
||||
|
||||
// SP800-56A § 5.6.2.3.2 Step 2.
|
||||
// "Verify that x_Q and y_Q are integers in the interval [0,p-1] in the
|
||||
// case that q is an odd prime p, or that x_Q and y_Q are bit strings
|
||||
// of length m bits in the case that q = 2**m."
|
||||
//
|
||||
// Prove prime field: ASSUMED.
|
||||
// Prove q != 2: ASSUMED. (Curve parameter. No supported curve has q == 2.)
|
||||
// Prime field && q != 2 => q is an odd prime p
|
||||
// Therefore "verify that x, y are in [0, p-1]" satisfies step 2.
|
||||
//
|
||||
// Therefore verify that both x and y of the public key point have the unique
|
||||
// correct representation of an element in the underlying field by verifying
|
||||
// that x and y are integers in [0, p-1].
|
||||
if key.X.Sign() < 0 || key.Y.Sign() < 0 {
|
||||
return fmt.Errorf("Key x, y must not be negative")
|
||||
}
|
||||
|
||||
if key.X.Cmp(params.P) >= 0 || key.Y.Cmp(params.P) >= 0 {
|
||||
return fmt.Errorf("Key x, y must not exceed P-1")
|
||||
}
|
||||
|
||||
// SP800-56A § 5.6.2.3.2 Step 3.
|
||||
// "If q is an odd prime p, verify that (y_Q)**2 === (x_Q)***3 + a*x_Q + b (mod p).
|
||||
// If q = 2**m, verify that (y_Q)**2 + (x_Q)*(y_Q) == (x_Q)**3 + a*(x_Q)*2 + b in
|
||||
// the finite field of size 2**m.
|
||||
// (Ensures that the public key is on the correct elliptic curve.)"
|
||||
//
|
||||
// q is an odd prime p: proven/assumed above.
|
||||
// a = -3 for all supported curves.
|
||||
//
|
||||
// Therefore step 3 is satisfied simply by showing that
|
||||
// y**2 === x**3 - 3*x + B (mod P).
|
||||
//
|
||||
// This proves that the public key is on the correct elliptic curve.
|
||||
// But in practice, this test is provided by crypto/elliptic, so use that.
|
||||
if !key.Curve.IsOnCurve(key.X, key.Y) {
|
||||
return fmt.Errorf("Key point is not on the curve")
|
||||
}
|
||||
|
||||
// SP800-56A § 5.6.2.3.2 Step 4.
|
||||
// "Verify that n*Q == O.
|
||||
// (Ensures that the public key has the correct order. Along with check 1,
|
||||
// ensures that the public key is in the correct range in the correct EC
|
||||
// subgroup, that is, it is in the correct EC subgroup and is not the
|
||||
// identity element.)"
|
||||
//
|
||||
// Ensure that public key has the correct order:
|
||||
// verify that n*Q = O.
|
||||
//
|
||||
// n*Q = O iff n*Q is the point at infinity (see step 1).
|
||||
ox, oy := key.Curve.ScalarMult(key.X, key.Y, params.N.Bytes())
|
||||
if !isPointAtInfinityNISTP(ox, oy) {
|
||||
return fmt.Errorf("Public key does not have correct order")
|
||||
}
|
||||
|
||||
// End of SP800-56A § 5.6.2.3.2 Public Key Validation Routine.
|
||||
// Key is valid.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns true iff the point (x,y) on NIST P-256, NIST P-384 or NIST P-521 is
|
||||
// the point at infinity. These curves all have the same point at infinity
|
||||
// (0,0). This function must ONLY be used on points on curves verified to have
|
||||
// (0,0) as their point at infinity.
|
||||
func isPointAtInfinityNISTP(x, y *big.Int) bool {
|
||||
return x.Sign() == 0 && y.Sign() == 0
|
||||
}
|
||||
|
||||
// GoodCurve determines if an elliptic curve meets our requirements.
|
||||
func (policy *KeyPolicy) goodCurve(c elliptic.Curve) (err error) {
|
||||
// Simply use a whitelist for now.
|
||||
params := c.Params()
|
||||
switch {
|
||||
case policy.AllowECDSANISTP256 && params == elliptic.P256().Params():
|
||||
return nil
|
||||
case policy.AllowECDSANISTP384 && params == elliptic.P384().Params():
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf(fmt.Sprintf("ECDSA curve %v not allowed", params.Name))
|
||||
}
|
||||
}
|
||||
|
||||
// GoodKeyRSA determines if a RSA pubkey meets our requirements
|
||||
func (policy *KeyPolicy) goodKeyRSA(key rsa.PublicKey) (err error) {
|
||||
if !policy.AllowRSA {
|
||||
return fmt.Errorf("RSA keys are not allowed")
|
||||
}
|
||||
|
||||
// Baseline Requirements Appendix A
|
||||
// Modulus must be >= 2048 bits
|
||||
// Removed max keySize from original package version
|
||||
modulus := key.N
|
||||
modulusBitLen := modulus.BitLen()
|
||||
if modulusBitLen < 2048 {
|
||||
return fmt.Errorf(fmt.Sprintf("Key too small: %d", modulusBitLen))
|
||||
}
|
||||
// Bit lengths that are not a multiple of 8 may cause problems on some
|
||||
// client implementations.
|
||||
if modulusBitLen%8 != 0 {
|
||||
return fmt.Errorf(fmt.Sprintf("Key length wasn't a multiple of 8: %d", modulusBitLen))
|
||||
}
|
||||
// The CA SHALL confirm that the value of the public exponent is an
|
||||
// odd number equal to 3 or more. Additionally, the public exponent
|
||||
// SHOULD be in the range between 2^16 + 1 and 2^256-1.
|
||||
// NOTE: rsa.PublicKey cannot represent an exponent part greater than
|
||||
// 2^32 - 1 or 2^64 - 1, because it stores E as an integer. So we
|
||||
// don't need to check the upper bound.
|
||||
if (key.E%2) == 0 || key.E < ((1<<16)+1) {
|
||||
return fmt.Errorf(fmt.Sprintf("Key exponent should be odd and >2^16: %d", key.E))
|
||||
}
|
||||
// The modulus SHOULD also have the following characteristics: an odd
|
||||
// number, not the power of a prime, and have no factors smaller than 752.
|
||||
// TODO: We don't yet check for "power of a prime."
|
||||
if checkSmallPrimes(modulus) {
|
||||
return fmt.Errorf("Key divisible by small prime")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns true iff integer i is divisible by any of the primes in smallPrimes.
|
||||
//
|
||||
// Short circuits; execution time is dependent on i. Do not use this on secret
|
||||
// values.
|
||||
func checkSmallPrimes(i *big.Int) bool {
|
||||
smallPrimesSingleton.Do(func() {
|
||||
for _, prime := range smallPrimeInts {
|
||||
smallPrimes = append(smallPrimes, big.NewInt(prime))
|
||||
}
|
||||
})
|
||||
|
||||
for _, prime := range smallPrimes {
|
||||
var result big.Int
|
||||
result.Mod(i, prime)
|
||||
if result.Sign() == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
30
vendor/github.com/globalsign/certlint/checks/certificate/publickey/publickey.go
generated
vendored
30
vendor/github.com/globalsign/certlint/checks/certificate/publickey/publickey.go
generated
vendored
|
|
@ -1,30 +0,0 @@
|
|||
package publickey
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/checks/certificate/publickey/goodkey"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "Public Key Check"
|
||||
|
||||
func init() {
|
||||
checks.RegisterCertificateCheck(checkName, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
gkp := goodkey.NewKeyPolicy()
|
||||
err := gkp.GoodKey(d.Cert.PublicKey)
|
||||
if err != nil {
|
||||
e.Err("Certificate %s", strings.ToLower(err.Error()))
|
||||
return e
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
48
vendor/github.com/globalsign/certlint/checks/certificate/publicsuffix/publicsuffix.go
generated
vendored
48
vendor/github.com/globalsign/certlint/checks/certificate/publicsuffix/publicsuffix.go
generated
vendored
|
|
@ -1,48 +0,0 @@
|
|||
package publicsuffix
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
|
||||
psl "golang.org/x/net/publicsuffix"
|
||||
)
|
||||
|
||||
const checkName = "Public Suffix (xTLD) Check"
|
||||
|
||||
func init() {
|
||||
filter := &checks.Filter{
|
||||
Type: []string{"DV", "OV", "IV", "EV"},
|
||||
}
|
||||
checks.RegisterCertificateCheck(checkName, filter, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
if len(d.Cert.Subject.CommonName) > 0 {
|
||||
suffix, icann := psl.PublicSuffix(strings.ToLower(d.Cert.Subject.CommonName))
|
||||
if fmt.Sprintf("*.%s", suffix) == d.Cert.Subject.CommonName || suffix == d.Cert.Subject.CommonName {
|
||||
// if there is a dot on the suffix, it must be on the psl
|
||||
if icann || strings.Count(suffix, ".") > 0 {
|
||||
e.Err("Certificate CommonName %q equals %q from the public suffix list", d.Cert.Subject.CommonName, suffix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, n := range d.Cert.DNSNames {
|
||||
suffix, icann := psl.PublicSuffix(strings.ToLower(n))
|
||||
if fmt.Sprintf("*.%s", suffix) == n || suffix == n {
|
||||
// if there is a dot on the suffix, it must be on the psl
|
||||
if icann || strings.Count(suffix, ".") > 0 {
|
||||
e.Err("Certificate subjectAltName %q equals %q from the public suffix list", n, suffix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
65
vendor/github.com/globalsign/certlint/checks/certificate/revocation/revocation.go
generated
vendored
65
vendor/github.com/globalsign/certlint/checks/certificate/revocation/revocation.go
generated
vendored
|
|
@ -1,65 +0,0 @@
|
|||
package revocation
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "Certificate Revocation Information Check"
|
||||
|
||||
func init() {
|
||||
checks.RegisterCertificateCheck(checkName, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
// OCSP signing certificates should not contain an OCSP server
|
||||
if d.Type == "OCSP" {
|
||||
if len(d.Cert.OCSPServer) > 0 {
|
||||
e.Warning("OCSP signing certificate contains an OCSP server")
|
||||
}
|
||||
|
||||
// no extra checks needed
|
||||
return e
|
||||
}
|
||||
|
||||
// Self signed CA certificates should not contain any revocation sources
|
||||
if d.Type == "CA" && d.Cert.CheckSignatureFrom(d.Cert) == nil {
|
||||
if len(d.Cert.CRLDistributionPoints) != 0 && len(d.Cert.OCSPServer) != 0 {
|
||||
e.Warning("Self signed CA certificates should not contain any revocation sources")
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
if len(d.Cert.CRLDistributionPoints) == 0 && len(d.Cert.OCSPServer) == 0 {
|
||||
e.Err("Certificate contains no CRL or OCSP server")
|
||||
return e
|
||||
}
|
||||
|
||||
// Check CRL information
|
||||
for _, crl := range d.Cert.CRLDistributionPoints {
|
||||
l, err := url.Parse(crl)
|
||||
if err != nil {
|
||||
e.Err("Certificate contains an invalid CRL (%s)", crl)
|
||||
} else if l.Scheme != "http" {
|
||||
e.Err("Certificate contains a CRL with an non-preferred scheme (%s)", l.Scheme)
|
||||
}
|
||||
}
|
||||
|
||||
// Check OCSP information
|
||||
for _, server := range d.Cert.OCSPServer {
|
||||
s, err := url.Parse(server)
|
||||
if err != nil {
|
||||
e.Err("Certificate contains an invalid OCSP server (%s)", s)
|
||||
} else if s.Scheme != "http" {
|
||||
e.Err("Certificate contains a OCSP server with an non-preferred scheme (%s)", s.Scheme)
|
||||
}
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
44
vendor/github.com/globalsign/certlint/checks/certificate/serialnumber/serialnumber.go
generated
vendored
44
vendor/github.com/globalsign/certlint/checks/certificate/serialnumber/serialnumber.go
generated
vendored
|
|
@ -1,44 +0,0 @@
|
|||
package serialnumber
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "Certificate Serial Number Check"
|
||||
|
||||
func init() {
|
||||
checks.RegisterCertificateCheck(checkName, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
if d.Cert.SerialNumber.Cmp(big.NewInt(0)) == -1 {
|
||||
e.Err("Certificate serial number MUST be a positive integer (%d)", d.Cert.SerialNumber)
|
||||
}
|
||||
|
||||
// Remaining checks are not relevant for CA certificates
|
||||
if d.Cert.IsCA {
|
||||
return e
|
||||
}
|
||||
|
||||
// https://cabforum.org/2016/07/08/ballot-164/
|
||||
if d.Cert.NotBefore.After(time.Date(2016, 9, 30, 0, 0, 0, 0, time.UTC)) {
|
||||
if d.Cert.SerialNumber.BitLen() < 64 {
|
||||
e.Err("Certificate serial number should be 64 bits but contains %d bits", d.Cert.SerialNumber.BitLen())
|
||||
}
|
||||
} else {
|
||||
// all new end-entity certificates must contain at least 20 bits of unpredictable random data (preferably in the serial number).
|
||||
if d.Cert.SerialNumber.BitLen() < 20 {
|
||||
e.Warning("Certificate serial number must contain at least 20 bits of unpredictable random data, found only %d bits", d.Cert.SerialNumber.BitLen())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
package signaturealgorithm
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"time"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "Signature Algorithm Check"
|
||||
|
||||
func init() {
|
||||
filter := &checks.Filter{
|
||||
Type: []string{"DV", "OV", "IV", "EV"},
|
||||
}
|
||||
checks.RegisterCertificateCheck(checkName, filter, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
// Check if we use SHA1 or less (MD5, MD2)
|
||||
if d.Cert.SignatureAlgorithm > x509.SHA1WithRSA &&
|
||||
d.Cert.SignatureAlgorithm != x509.DSAWithSHA1 &&
|
||||
d.Cert.SignatureAlgorithm != x509.ECDSAWithSHA1 {
|
||||
return e
|
||||
}
|
||||
|
||||
if d.Cert.NotBefore.After(time.Date(2016, 1, 1, 0, 0, 0, 0, time.UTC)) {
|
||||
e.Err("Certificate is using SHA1, but is issued on/after 1 Jan 2016")
|
||||
return e
|
||||
}
|
||||
|
||||
if d.Cert.NotBefore.After(time.Date(2015, 1, 16, 0, 0, 0, 0, time.UTC)) &&
|
||||
d.Cert.NotAfter.After(time.Date(2017, 1, 1, 0, 0, 0, 0, time.UTC)) {
|
||||
e.Err("Certificate is using SHA1, but is still valid on/after 1 Jan 2017")
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
package subject
|
||||
|
||||
import (
|
||||
"encoding/asn1"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Max field length:
|
||||
// https://www.itu.int/ITU-T/formal-language/itu-t/x/x520/2001/UpperBounds.html
|
||||
type object struct {
|
||||
oid asn1.ObjectIdentifier
|
||||
maxLength int
|
||||
}
|
||||
|
||||
func (o *object) Equal(oid asn1.ObjectIdentifier) bool {
|
||||
return o.oid.Equal(oid)
|
||||
}
|
||||
|
||||
func (o *object) Valid(v interface{}) error {
|
||||
if o.maxLength > 0 && len([]rune(v.(string))) > o.maxLength {
|
||||
return fmt.Errorf("exceeding max length of %d", o.maxLength)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// http://www.alvestrand.no/objectid/2.5.4.html
|
||||
var (
|
||||
objectClass = object{asn1.ObjectIdentifier{2, 5, 4, 0}, 0}
|
||||
aliasedEntryName = object{asn1.ObjectIdentifier{2, 5, 4, 1}, 0}
|
||||
knowldgeinformation = object{asn1.ObjectIdentifier{2, 5, 4, 2}, 0}
|
||||
commonName = object{asn1.ObjectIdentifier{2, 5, 4, 3}, 64}
|
||||
surname = object{asn1.ObjectIdentifier{2, 5, 4, 4}, 40}
|
||||
serialNumber = object{asn1.ObjectIdentifier{2, 5, 4, 5}, 64}
|
||||
countryName = object{asn1.ObjectIdentifier{2, 5, 4, 6}, 2}
|
||||
localityName = object{asn1.ObjectIdentifier{2, 5, 4, 7}, 128}
|
||||
stateOrProvinceName = object{asn1.ObjectIdentifier{2, 5, 4, 8}, 128}
|
||||
streetAddress = object{asn1.ObjectIdentifier{2, 5, 4, 9}, 128}
|
||||
organizationName = object{asn1.ObjectIdentifier{2, 5, 4, 10}, 64}
|
||||
organizationalUnitName = object{asn1.ObjectIdentifier{2, 5, 4, 11}, 64}
|
||||
title = object{asn1.ObjectIdentifier{2, 5, 4, 12}, 64}
|
||||
description = object{asn1.ObjectIdentifier{2, 5, 4, 13}, 1024}
|
||||
searchGuide = object{asn1.ObjectIdentifier{2, 5, 4, 14}, 32768}
|
||||
businessCategory = object{asn1.ObjectIdentifier{2, 5, 4, 15}, 128}
|
||||
postalAddress = object{asn1.ObjectIdentifier{2, 5, 4, 16}, 128}
|
||||
postalCode = object{asn1.ObjectIdentifier{2, 5, 4, 17}, 40}
|
||||
postOfficeBox = object{asn1.ObjectIdentifier{2, 5, 4, 18}, 40}
|
||||
physicalDeliveryOfficeName = object{asn1.ObjectIdentifier{2, 5, 4, 19}, 128}
|
||||
telephoneNumber = object{asn1.ObjectIdentifier{2, 5, 4, 20}, 32}
|
||||
telexNumber = object{asn1.ObjectIdentifier{2, 5, 4, 21}, 14}
|
||||
teletexTerminalIdentifier = object{asn1.ObjectIdentifier{2, 5, 4, 22}, 1024}
|
||||
facsimileTelephoneNumber = object{asn1.ObjectIdentifier{2, 5, 4, 23}, 32}
|
||||
x121Address = object{asn1.ObjectIdentifier{2, 5, 4, 24}, 15}
|
||||
internationalISDNNumber = object{asn1.ObjectIdentifier{2, 5, 4, 25}, 16}
|
||||
registeredAddress = object{asn1.ObjectIdentifier{2, 5, 4, 26}, 128}
|
||||
destinationIndicator = object{asn1.ObjectIdentifier{2, 5, 4, 27}, 128}
|
||||
preferredDeliveryMethod = object{asn1.ObjectIdentifier{2, 5, 4, 28}, 0}
|
||||
presentationAddress = object{asn1.ObjectIdentifier{2, 5, 4, 29}, 0}
|
||||
supportedApplicationContext = object{asn1.ObjectIdentifier{2, 5, 4, 30}, 0}
|
||||
member = object{asn1.ObjectIdentifier{2, 5, 4, 31}, 0}
|
||||
owner = object{asn1.ObjectIdentifier{2, 5, 4, 32}, 0}
|
||||
roleOccupant = object{asn1.ObjectIdentifier{2, 5, 4, 33}, 0}
|
||||
seeAlso = object{asn1.ObjectIdentifier{2, 5, 4, 34}, 0}
|
||||
userPassword = object{asn1.ObjectIdentifier{2, 5, 4, 35}, 128}
|
||||
userCertificate = object{asn1.ObjectIdentifier{2, 5, 4, 36}, 0}
|
||||
cACertificate = object{asn1.ObjectIdentifier{2, 5, 4, 37}, 0}
|
||||
authorityRevocationList = object{asn1.ObjectIdentifier{2, 5, 4, 38}, 0}
|
||||
certificateRevocationList = object{asn1.ObjectIdentifier{2, 5, 4, 39}, 0}
|
||||
crossCertificatePair = object{asn1.ObjectIdentifier{2, 5, 4, 40}, 0}
|
||||
name = object{asn1.ObjectIdentifier{2, 5, 4, 41}, 128}
|
||||
givenName = object{asn1.ObjectIdentifier{2, 5, 4, 42}, 128}
|
||||
initials = object{asn1.ObjectIdentifier{2, 5, 4, 43}, 0}
|
||||
generationQualifier = object{asn1.ObjectIdentifier{2, 5, 4, 44}, 0}
|
||||
uniqueIdentifier = object{asn1.ObjectIdentifier{2, 5, 4, 45}, 0}
|
||||
dnQualifier = object{asn1.ObjectIdentifier{2, 5, 4, 46}, 0}
|
||||
enhancedSearchGuide = object{asn1.ObjectIdentifier{2, 5, 4, 47}, 0}
|
||||
protocolInformation = object{asn1.ObjectIdentifier{2, 5, 4, 48}, 0}
|
||||
distinguishedName = object{asn1.ObjectIdentifier{2, 5, 4, 49}, 0}
|
||||
uniqueMember = object{asn1.ObjectIdentifier{2, 5, 4, 50}, 0}
|
||||
houseIdentifier = object{asn1.ObjectIdentifier{2, 5, 4, 51}, 0}
|
||||
supportedAlgorithms = object{asn1.ObjectIdentifier{2, 5, 4, 52}, 0}
|
||||
deltaRevocationList = object{asn1.ObjectIdentifier{2, 5, 4, 53}, 0}
|
||||
attributeCertificate = object{asn1.ObjectIdentifier{2, 5, 4, 58}, 0}
|
||||
pseudonym = object{asn1.ObjectIdentifier{2, 5, 4, 65}, 0}
|
||||
|
||||
emailAddress = object{asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, 255}
|
||||
|
||||
jurisdictionLocalityName = object{asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 60, 2, 1, 1}, 128}
|
||||
jurisdictionStateOrProvinceName = object{asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 60, 2, 1, 2}, 128}
|
||||
jurisdictionCountryName = object{asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 60, 2, 1, 3}, 2}
|
||||
)
|
||||
|
|
@ -1,225 +0,0 @@
|
|||
package subject
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "Subject Check"
|
||||
|
||||
func init() {
|
||||
filter := &checks.Filter{
|
||||
//Type: []string{"DV", "OV", "IV", "EV"},
|
||||
}
|
||||
checks.RegisterCertificateCheck(checkName, filter, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(d *certdata.Data) *errors.Errors {
|
||||
return checkDN(d.Type, d.Cert.Subject.Names)
|
||||
}
|
||||
|
||||
// Subject Distinguished Name Fields
|
||||
func checkDN(vetting string, dn []pkix.AttributeTypeAndValue) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
// DNS must not be empty
|
||||
if len(dn) == 0 {
|
||||
e.Err("Distinguished Name contains no values")
|
||||
return e
|
||||
}
|
||||
|
||||
// OV & EV requirements
|
||||
if vetting == "OV" || vetting == "EV" {
|
||||
if !inDN(dn, organizationName) {
|
||||
e.Err("organizationName is required for %s certificates", vetting)
|
||||
}
|
||||
}
|
||||
|
||||
// EV specific requirements
|
||||
if vetting == "EV" {
|
||||
if !inDN(dn, localityName) {
|
||||
e.Err("localityName is required for %s certificates", vetting)
|
||||
}
|
||||
if !inDN(dn, businessCategory) {
|
||||
e.Err("businessCategory is required for %s certificates", vetting)
|
||||
}
|
||||
if !inDN(dn, jurisdictionCountryName) {
|
||||
e.Err("jurisdictionCountryName is required for %s certificates", vetting)
|
||||
}
|
||||
if !inDN(dn, serialNumber) {
|
||||
e.Err("serialNumber is required for %s certificates", vetting)
|
||||
}
|
||||
}
|
||||
|
||||
// Field related requirements
|
||||
//
|
||||
// Max field length:
|
||||
// https://www.itu.int/ITU-T/formal-language/itu-t/x/x520/2001/UpperBounds.html
|
||||
for _, n := range dn {
|
||||
switch {
|
||||
|
||||
// commonName
|
||||
// If present, this field MUST contain a single IP address or Fully‐Qualified Domain Name
|
||||
case commonName.Equal(n.Type):
|
||||
// report deprecated common name field as info until not commenly used/accepted
|
||||
e.Info("commonName field is deprecated")
|
||||
|
||||
// check if value is exceeding max length
|
||||
if err := commonName.Valid(n.Value); err != nil {
|
||||
e.Err("commonName %s", err.Error())
|
||||
}
|
||||
|
||||
case emailAddress.Equal(n.Type):
|
||||
// report deprecated email address field as info until not commenly used/accepted
|
||||
e.Info("emailAddress field is deprecated")
|
||||
|
||||
// RFC5280: ub-emailaddress-length was changed from 128 to 255 in order to
|
||||
// align with PKCS #9 [RFC2985].
|
||||
if err := emailAddress.Valid(n.Value); err != nil {
|
||||
e.Err("emailAddress %s", err.Error())
|
||||
}
|
||||
|
||||
// surname
|
||||
// A Certificate containing a givenName field or surname field MUST contain
|
||||
// the (2.23.140.1.2.3) Certificate Policy OID.
|
||||
case surname.Equal(n.Type):
|
||||
// Prohibited
|
||||
if !inDN(dn, givenName) {
|
||||
e.Err("surname may only set in combination with givenName")
|
||||
}
|
||||
// Require field if surname is set
|
||||
if !inDN(dn, localityName) && !inDN(dn, stateOrProvinceName) {
|
||||
e.Err("localityName or stateOrProvinceName is required if surname is set")
|
||||
}
|
||||
|
||||
// ub-surname-length INTEGER ::= 40
|
||||
if err := surname.Valid(n.Value); err != nil {
|
||||
e.Err("surname %s", err.Error())
|
||||
}
|
||||
|
||||
// countryName
|
||||
case countryName.Equal(n.Type):
|
||||
// TODO: Check against the values in ISO 3166‐1
|
||||
if len(n.Value.(string)) != 2 {
|
||||
e.Err("countryName MUST contain the two-letter ISO 3166-1 country code")
|
||||
}
|
||||
|
||||
// jurisdictionCountryName
|
||||
case jurisdictionCountryName.Equal(n.Type):
|
||||
// TODO: Check against the values in ISO 3166‐1
|
||||
if len(n.Value.(string)) != 2 {
|
||||
e.Err("jurisdictionCountryName MUST contain the two-letter ISO 3166-1 country code")
|
||||
}
|
||||
|
||||
// localityName
|
||||
case localityName.Equal(n.Type):
|
||||
// Prohibited
|
||||
if !inDN(dn, organizationName) && !(inDN(dn, givenName) && inDN(dn, surname)) {
|
||||
e.Err("localityName is not allowed without organizationName or givenName and surname")
|
||||
}
|
||||
|
||||
if err := localityName.Valid(n.Value); err != nil {
|
||||
e.Err("localityName %s", err.Error())
|
||||
}
|
||||
|
||||
// stateOrProvinceName
|
||||
case stateOrProvinceName.Equal(n.Type):
|
||||
// Prohibited
|
||||
if !inDN(dn, organizationName) && !(inDN(dn, givenName) && inDN(dn, surname)) {
|
||||
e.Err("stateOrProvinceName is not allowed without organizationName or givenName and surname")
|
||||
}
|
||||
|
||||
if err := stateOrProvinceName.Valid(n.Value); err != nil {
|
||||
e.Err("stateOrProvinceName %s", err.Error())
|
||||
}
|
||||
|
||||
// streetAddress
|
||||
case streetAddress.Equal(n.Type):
|
||||
// Prohibited
|
||||
if !inDN(dn, organizationName) && !(inDN(dn, givenName) && inDN(dn, surname)) {
|
||||
e.Err("streetAddress is not allowed without organizationName or givenName and surname")
|
||||
}
|
||||
|
||||
if err := streetAddress.Valid(n.Value); err != nil {
|
||||
e.Err("streetAddress %s", err.Error())
|
||||
}
|
||||
|
||||
// postalCode
|
||||
case postalCode.Equal(n.Type):
|
||||
// Prohibited
|
||||
if !inDN(dn, organizationName) && !(inDN(dn, givenName) && inDN(dn, surname)) {
|
||||
e.Err("postalCode is not allowed without organizationName or givenName and surname")
|
||||
}
|
||||
|
||||
if err := postalCode.Valid(n.Value); err != nil {
|
||||
e.Err("postalCode %s", err.Error())
|
||||
}
|
||||
|
||||
// organizationName
|
||||
case organizationName.Equal(n.Type):
|
||||
// Require field if organizationName is set
|
||||
if !inDN(dn, localityName) && !inDN(dn, stateOrProvinceName) {
|
||||
e.Err("localityName or stateOrProvinceName is required if organizationName is set")
|
||||
}
|
||||
if !inDN(dn, stateOrProvinceName) {
|
||||
e.Err("stateOrProvinceName is required if organizationName is set")
|
||||
}
|
||||
if !inDN(dn, countryName) {
|
||||
e.Err("countryName is required if organizationName is set")
|
||||
}
|
||||
|
||||
if err := organizationName.Valid(n.Value); err != nil {
|
||||
e.Err("organizationName %s", err.Error())
|
||||
}
|
||||
|
||||
// organizationalUnitName
|
||||
case organizationalUnitName.Equal(n.Type):
|
||||
if err := organizationalUnitName.Valid(n.Value); err != nil {
|
||||
e.Err("organizationalUnitName %s", err.Error())
|
||||
}
|
||||
|
||||
// businessCategory
|
||||
case businessCategory.Equal(n.Type):
|
||||
bc := n.Value.(string)
|
||||
if bc != "Private Organization" && bc != "Government Entity" && bc != "Business Entity" && bc != "Non-Commercial Entity" {
|
||||
e.Err("businessCategory should contain 'Private Organization', 'Government Entity', 'Business Entity', or 'Non-Commercial Entity'")
|
||||
}
|
||||
|
||||
if err := businessCategory.Valid(n.Value); err != nil {
|
||||
e.Err("businessCategory %s", err.Error())
|
||||
}
|
||||
|
||||
// serialNumber
|
||||
case serialNumber.Equal(n.Type):
|
||||
if err := serialNumber.Valid(n.Value); err != nil {
|
||||
e.Err("serialNumber %s", err.Error())
|
||||
}
|
||||
|
||||
// givenName
|
||||
case givenName.Equal(n.Type):
|
||||
// Prohibited
|
||||
if !inDN(dn, surname) {
|
||||
e.Err("givenName may only set in combination with surname")
|
||||
}
|
||||
|
||||
if err := givenName.Valid(n.Value); err != nil {
|
||||
e.Err("givenName %s", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
func inDN(dn []pkix.AttributeTypeAndValue, attr object) bool {
|
||||
for _, n := range dn {
|
||||
if attr.Equal(n.Type) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
104
vendor/github.com/globalsign/certlint/checks/certificate/subjectaltname/subjectaltname.go
generated
vendored
104
vendor/github.com/globalsign/certlint/checks/certificate/subjectaltname/subjectaltname.go
generated
vendored
|
|
@ -1,104 +0,0 @@
|
|||
package subjectaltname
|
||||
|
||||
import (
|
||||
goerr "errors"
|
||||
"strings"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
|
||||
"golang.org/x/net/idna"
|
||||
)
|
||||
|
||||
const checkName = "Subject Alternative Names Check"
|
||||
|
||||
var idnaProfile *idna.Profile
|
||||
var idnaUnderscoreError = goerr.New("idna: disallowed rune U+005F")
|
||||
|
||||
func init() {
|
||||
idnaProfile = idna.New(
|
||||
idna.BidiRule(),
|
||||
idna.MapForLookup(),
|
||||
idna.ValidateForRegistration(),
|
||||
idna.ValidateLabels(true),
|
||||
idna.VerifyDNSLength(true),
|
||||
idna.StrictDomainName(true),
|
||||
idna.Transitional(false))
|
||||
|
||||
checks.RegisterCertificateCheck(checkName, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
// TODO: Should we check against cross usage of certificate types (for example DV certificate with emial address)?
|
||||
|
||||
switch d.Type {
|
||||
case "PS":
|
||||
// TODO: Check EmailAddresses in the Subject DN
|
||||
if len(d.Cert.EmailAddresses) == 0 {
|
||||
e.Err("Certificate doesn't contain any subjectAltName")
|
||||
return e
|
||||
}
|
||||
for _, s := range d.Cert.EmailAddresses {
|
||||
// Splitting domain of mail address, using lowercase
|
||||
em := strings.SplitAfter(strings.ToLower(s), "@")
|
||||
if len(em) != 2 {
|
||||
e.Err("Certificate subjectAltName '%s' contains an invalid email address", s)
|
||||
continue
|
||||
}
|
||||
|
||||
// Check email address domain part
|
||||
if _, err := idnaProfile.ToASCII(em[1]); err != nil {
|
||||
e.Err("Certificate subjectAltName '%s', %s", s, err.Error())
|
||||
}
|
||||
|
||||
// TODO: Implement more checks for the left side of the @ sign
|
||||
if strings.Contains(em[0], " ") {
|
||||
e.Err("Certificate subjectAltName '%s' contains a whitespace", s)
|
||||
}
|
||||
}
|
||||
|
||||
case "DV", "OV", "EV":
|
||||
if len(d.Cert.DNSNames) == 0 && len(d.Cert.IPAddresses) == 0 {
|
||||
e.Err("Certificate doesn't contain any subjectAltName")
|
||||
return e
|
||||
}
|
||||
|
||||
// While the commonname is not a subjectAltName we use the same rule to
|
||||
// validate the domain name. Check with stripped wildcards as they are non
|
||||
// registrable.
|
||||
if _, err := idnaProfile.ToASCII(strings.TrimPrefix(strings.ToLower(d.Cert.Subject.CommonName), "*.")); err != nil && err != idnaUnderscoreError {
|
||||
e.Err("Certificate CommonName '%s', %s", d.Cert.Subject.CommonName, err.Error())
|
||||
}
|
||||
|
||||
var cnInSan bool
|
||||
for _, s := range d.Cert.DNSNames {
|
||||
if strings.EqualFold(d.Cert.Subject.CommonName, s) {
|
||||
cnInSan = true
|
||||
}
|
||||
|
||||
// Check subjectAltName with stripped wildcards as they are non registrable
|
||||
if _, err := idnaProfile.ToASCII(strings.TrimPrefix(strings.ToLower(s), "*.")); err != nil && err != idnaUnderscoreError {
|
||||
e.Err("Certificate subjectAltName '%s', %s", s, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Maybe it's an IP address
|
||||
if !cnInSan {
|
||||
for _, s := range d.Cert.IPAddresses {
|
||||
if strings.EqualFold(d.Cert.Subject.CommonName, s.String()) {
|
||||
cnInSan = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !cnInSan {
|
||||
e.Err("Certificate CN is not listed in subjectAltName")
|
||||
}
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package validity
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "Validity Check"
|
||||
|
||||
func init() {
|
||||
checks.RegisterCertificateCheck(checkName, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
switch d.Type {
|
||||
case "EV":
|
||||
if d.Cert.NotBefore.After(d.Cert.NotBefore.AddDate(0, 27, 0)) {
|
||||
e.Err("EV Certificate LifeTime exceeds 27 months")
|
||||
return e
|
||||
}
|
||||
case "DV", "OV":
|
||||
if d.Cert.NotBefore.After(time.Date(2015, 4, 1, 0, 0, 0, 0, time.UTC)) {
|
||||
if d.Cert.NotBefore.After(d.Cert.NotBefore.AddDate(0, 39, 0)) {
|
||||
e.Err("Certificate LifeTime exceeds 39 months")
|
||||
return e
|
||||
}
|
||||
} else {
|
||||
if d.Cert.NotBefore.After(d.Cert.NotBefore.AddDate(0, 60, 0)) {
|
||||
e.Err("Certificate LifeTime exceeds 60 months")
|
||||
return e
|
||||
}
|
||||
}
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
package version
|
||||
|
||||
import (
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "Certificate Version Check"
|
||||
|
||||
func init() {
|
||||
checks.RegisterCertificateCheck(checkName, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
if d.Cert.Version != 3 {
|
||||
e.Err("Certificate is not V3 (%d)", d.Cert.Version)
|
||||
return e
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
package wildcard
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "Wildcard(s) Check"
|
||||
|
||||
func init() {
|
||||
checks.RegisterCertificateCheck(checkName, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
switch d.Type {
|
||||
case "EV":
|
||||
if strings.LastIndex(d.Cert.Subject.CommonName, "*") > -1 {
|
||||
e.Err("Certificate should not contain a wildcard")
|
||||
}
|
||||
for _, n := range d.Cert.DNSNames {
|
||||
if strings.LastIndex(n, "*") > -1 {
|
||||
e.Err("Certificate subjectAltName '%s' should not contain a wildcard", n)
|
||||
}
|
||||
}
|
||||
case "DV", "OV":
|
||||
if strings.LastIndex(d.Cert.Subject.CommonName, "*") > 0 {
|
||||
e.Err("Certificate wildcard is only allowed as prefix")
|
||||
}
|
||||
for _, n := range d.Cert.DNSNames {
|
||||
if strings.LastIndex(n, "*") > 0 {
|
||||
e.Err("Certificate subjectAltName '%s' wildcard is only allowed as prefix", n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
package checks
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
var extMutex = &sync.Mutex{}
|
||||
|
||||
type extensions []extensionCheck
|
||||
|
||||
type extensionCheck struct {
|
||||
name string
|
||||
oid asn1.ObjectIdentifier
|
||||
filter *Filter
|
||||
f func(pkix.Extension, *certdata.Data) *errors.Errors
|
||||
}
|
||||
|
||||
// Extensions contains all imported extension checks
|
||||
var Extensions extensions
|
||||
|
||||
// RegisterExtensionCheck adds a new check to Extensions
|
||||
func RegisterExtensionCheck(name string, oid asn1.ObjectIdentifier, filter *Filter, f func(pkix.Extension, *certdata.Data) *errors.Errors) {
|
||||
extMutex.Lock()
|
||||
Extensions = append(Extensions, extensionCheck{name, oid, filter, f})
|
||||
extMutex.Unlock()
|
||||
}
|
||||
|
||||
// Check lookups the registered extension checks and runs all checks with the
|
||||
// same Object Identifier.
|
||||
func (ex extensions) Check(ext pkix.Extension, d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
var found bool
|
||||
|
||||
for _, ec := range ex {
|
||||
if ec.oid.Equal(ext.Id) {
|
||||
found = true
|
||||
if ec.filter != nil && ec.filter.Check(d) {
|
||||
continue
|
||||
}
|
||||
e.Append(ec.f(ext, d))
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
// Don't report private enterprise extensions as unknown, registered private
|
||||
// extensions have still been checked above.
|
||||
if !strings.HasPrefix(ext.Id.String(), "1.3.6.1.4.1.") {
|
||||
e.Warning("Certificate contains unknown extension (%s)", ext.Id.String())
|
||||
}
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
29
vendor/github.com/globalsign/certlint/checks/extensions/adobetimestamp/adobetimestamp.go
generated
vendored
29
vendor/github.com/globalsign/certlint/checks/extensions/adobetimestamp/adobetimestamp.go
generated
vendored
|
|
@ -1,29 +0,0 @@
|
|||
package adobetimestamp
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "Adobe Timestamp Extension Check"
|
||||
|
||||
var extensionOid = asn1.ObjectIdentifier{1, 2, 840, 113583, 1, 1, 9, 1}
|
||||
|
||||
func init() {
|
||||
checks.RegisterExtensionCheck(checkName, extensionOid, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(ex pkix.Extension, d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
if ex.Critical {
|
||||
e.Err("Adobe Timestamp extension set critical")
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
package all
|
||||
|
||||
import (
|
||||
// Import all default extensions
|
||||
_ "github.com/globalsign/certlint/checks/extensions/adobetimestamp"
|
||||
_ "github.com/globalsign/certlint/checks/extensions/authorityinfoaccess"
|
||||
_ "github.com/globalsign/certlint/checks/extensions/authoritykeyid"
|
||||
_ "github.com/globalsign/certlint/checks/extensions/basicconstraints"
|
||||
_ "github.com/globalsign/certlint/checks/extensions/crldistributionpoints"
|
||||
_ "github.com/globalsign/certlint/checks/extensions/ct"
|
||||
_ "github.com/globalsign/certlint/checks/extensions/extkeyusage"
|
||||
_ "github.com/globalsign/certlint/checks/extensions/keyusage"
|
||||
_ "github.com/globalsign/certlint/checks/extensions/nameconstraints"
|
||||
_ "github.com/globalsign/certlint/checks/extensions/ocspmuststaple"
|
||||
_ "github.com/globalsign/certlint/checks/extensions/ocspnocheck"
|
||||
_ "github.com/globalsign/certlint/checks/extensions/pdfrevocation"
|
||||
_ "github.com/globalsign/certlint/checks/extensions/policyidentifiers"
|
||||
_ "github.com/globalsign/certlint/checks/extensions/smimecapabilities"
|
||||
_ "github.com/globalsign/certlint/checks/extensions/subjectaltname"
|
||||
_ "github.com/globalsign/certlint/checks/extensions/subjectkeyid"
|
||||
)
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
package authorityinfoaccess
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "AuthorityInfoAccess Extension Check"
|
||||
|
||||
var extensionOid = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 1}
|
||||
|
||||
func init() {
|
||||
checks.RegisterExtensionCheck(checkName, extensionOid, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
// TODO: Add more checks https://golang.org/src/github.com/globalsign/certlint/certdata/x509.go?s=15439:18344#L1157
|
||||
func Check(ex pkix.Extension, d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
if ex.Critical {
|
||||
e.Err("AuthorityInfoAccess extension set critical")
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
29
vendor/github.com/globalsign/certlint/checks/extensions/authoritykeyid/authoritykeyid.go
generated
vendored
29
vendor/github.com/globalsign/certlint/checks/extensions/authoritykeyid/authoritykeyid.go
generated
vendored
|
|
@ -1,29 +0,0 @@
|
|||
package authoritykeyid
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "AuthorityKeyId Extension Check"
|
||||
|
||||
var extensionOid = asn1.ObjectIdentifier{2, 5, 29, 35}
|
||||
|
||||
func init() {
|
||||
checks.RegisterExtensionCheck(checkName, extensionOid, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(ex pkix.Extension, d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
if ex.Critical {
|
||||
e.Err("AuthorityKeyId extension set critical")
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
package basicconstraints
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "BasicConstraints Extension Check"
|
||||
|
||||
var extensionOid = asn1.ObjectIdentifier{2, 5, 29, 19}
|
||||
|
||||
func init() {
|
||||
checks.RegisterExtensionCheck(checkName, extensionOid, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc5280#section-4.2.1.9
|
||||
//
|
||||
func Check(ex pkix.Extension, d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
// This extension MAY appear as a critical or non-critical extension in end
|
||||
// entity certificates.
|
||||
if d.Cert.IsCA {
|
||||
|
||||
// Conforming CAs MUST include this extension in all CA certificates
|
||||
// that contain public keys used to validate digital signatures on
|
||||
// certificates and MUST mark the extension as critical in such
|
||||
// certificates. This extension MAY appear as a critical or non-
|
||||
// critical extension in CA certificates that contain public keys used
|
||||
// exclusively for purposes other than validating digital signatures on
|
||||
// certificates. Such CA certificates include ones that contain public
|
||||
// keys used exclusively for validating digital signatures on CRLs and
|
||||
// ones that contain key management public keys used with certificate
|
||||
// enrollment protocols.
|
||||
//
|
||||
// The CA Browser Forum BR 1.4.1 state that it should always be true for
|
||||
// CA certificates.
|
||||
if !ex.Critical {
|
||||
e.Err("BasicConstraints extension must be critical in CA certificates")
|
||||
}
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
package crldistributionpoints
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "CRLDistributionPoints Extension Check"
|
||||
|
||||
var extensionOid = asn1.ObjectIdentifier{2, 5, 29, 31}
|
||||
|
||||
func init() {
|
||||
checks.RegisterExtensionCheck(checkName, extensionOid, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(ex pkix.Extension, d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
if ex.Critical {
|
||||
e.Err("CRLDistributionPoints extension set critical")
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
package ct
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "Certificate Transparency Extension Check"
|
||||
|
||||
var extensionOid = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 2}
|
||||
|
||||
func init() {
|
||||
checks.RegisterExtensionCheck(checkName, extensionOid, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc6962
|
||||
//
|
||||
// TODO: Check it's present in EV certificates issued after xxx
|
||||
func Check(ex pkix.Extension, d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
if ex.Critical {
|
||||
e.Err("Certificate Transparency extension set critical")
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
47
vendor/github.com/globalsign/certlint/checks/extensions/extkeyusage/extkeyusage.go
generated
vendored
47
vendor/github.com/globalsign/certlint/checks/extensions/extkeyusage/extkeyusage.go
generated
vendored
|
|
@ -1,47 +0,0 @@
|
|||
package extkeyusage
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "ExtKeyUsage Extension Check"
|
||||
|
||||
var extensionOid = asn1.ObjectIdentifier{2, 5, 29, 37}
|
||||
|
||||
func init() {
|
||||
checks.RegisterExtensionCheck(checkName, extensionOid, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc5280#section-4.2.1.12
|
||||
//
|
||||
// This extension MAY, at the option of the certificate issuer, be either critical or non-critical.
|
||||
//
|
||||
func Check(ex pkix.Extension, d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
// RFC: In general, this extension will appear only in end entity certificates.
|
||||
if d.Cert.IsCA {
|
||||
e.Err("In general ExtKeyUsage will appear only in end entity certificates")
|
||||
}
|
||||
|
||||
// RFC: Conforming CAs SHOULD NOT mark this extension as critical if the
|
||||
// anyExtendedKeyUsage KeyPurposeId is present.
|
||||
if ex.Critical {
|
||||
for _, ku := range d.Cert.ExtKeyUsage {
|
||||
if ku == x509.ExtKeyUsageAny {
|
||||
e.Err("ExtKeyUsage extension SHOULD NOT be critical if anyExtendedKeyUsage is present")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
package keyusage
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "KeyUsage Extension Check"
|
||||
|
||||
var extensionOid = asn1.ObjectIdentifier{2, 5, 29, 15}
|
||||
|
||||
func init() {
|
||||
checks.RegisterExtensionCheck(checkName, extensionOid, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc5280#section-4.2.1.3
|
||||
//
|
||||
func Check(ex pkix.Extension, d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
if !ex.Critical {
|
||||
e.Err("KeyUsage extension SHOULD be marked as critical when present")
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
package nameconstraints
|
||||
|
||||
import (
|
||||
"encoding/asn1"
|
||||
|
||||
"crypto/x509/pkix"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "NameConstraints Extension Check"
|
||||
|
||||
var extensionOid = asn1.ObjectIdentifier{2, 5, 29, 30}
|
||||
|
||||
func init() {
|
||||
checks.RegisterExtensionCheck(checkName, extensionOid, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(ex pkix.Extension, d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
// NameConstraints do officially need to be set critical, often they are not
|
||||
// because many implementations still don't support Name Constraints.
|
||||
if !ex.Critical {
|
||||
e.Warning("NameConstraints extension set non-critical")
|
||||
}
|
||||
|
||||
// NameConstraints should only be included in CA or subordinate certificates
|
||||
if !d.Cert.IsCA {
|
||||
e.Err("End entity certificate should not contain a NameConstraints extension")
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
65
vendor/github.com/globalsign/certlint/checks/extensions/ocspmuststaple/ocspmuststaple.go
generated
vendored
65
vendor/github.com/globalsign/certlint/checks/extensions/ocspmuststaple/ocspmuststaple.go
generated
vendored
|
|
@ -1,65 +0,0 @@
|
|||
package ocspmuststaple
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"fmt"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
checkName = "OCSP Must Staple Extension Check"
|
||||
certTypeErr = "OCSP Must Staple extension set in non end-entity/issuer certificate"
|
||||
critExtErr = "OCSP Must Staple extension set critical"
|
||||
)
|
||||
|
||||
var (
|
||||
// RFC 7633 OID of the OCSP Must Staple TLS Extension Feature
|
||||
extensionOid = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 1, 24}
|
||||
// Expected extension value (DER encoded ASN.1 bytestring)
|
||||
expectedExtensionValue = []uint8{0x30, 0x3, 0x2, 0x1, 0x5}
|
||||
extValueErr = fmt.Sprintf(
|
||||
"OCSP Must Staple extension had incorrect value. "+
|
||||
"Should be ASN.1 DER %#v", expectedExtensionValue)
|
||||
)
|
||||
|
||||
// RFC 7633 only defines this extension for PKIX end-entity certificates,
|
||||
// certificate signing requests, and certificate signing certificates (CAs).
|
||||
// We should not allow it for cert types like "OCSP", "PS", "CS", etc.
|
||||
var allowedCertTypes = map[string]bool{
|
||||
"DV": true,
|
||||
"OV": true,
|
||||
"EV": true,
|
||||
"CA": true,
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Register this check for the OCSP Must Staple extension OID.
|
||||
checks.RegisterExtensionCheck(checkName, extensionOid, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(ex pkix.Extension, d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
// If the cert type isn't one of the `allowedCertTypes`, return an error
|
||||
if _, allowed := allowedCertTypes[d.Type]; !allowed {
|
||||
e.Err(certTypeErr)
|
||||
}
|
||||
|
||||
// Per RFC 7633 "The TLS feature extension SHOULD NOT be marked critical"
|
||||
if ex.Critical {
|
||||
e.Err(critExtErr)
|
||||
}
|
||||
|
||||
// Check that the extension value is the expected slice of DER encoded ASN.1
|
||||
if bytes.Compare(ex.Value, expectedExtensionValue) != 0 {
|
||||
e.Err(extValueErr)
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
33
vendor/github.com/globalsign/certlint/checks/extensions/ocspnocheck/ocspnocheck.go
generated
vendored
33
vendor/github.com/globalsign/certlint/checks/extensions/ocspnocheck/ocspnocheck.go
generated
vendored
|
|
@ -1,33 +0,0 @@
|
|||
package ocspnocheck
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "OCSP Nocheck Extension Check"
|
||||
|
||||
var extensionOid = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1, 5}
|
||||
|
||||
func init() {
|
||||
checks.RegisterExtensionCheck(checkName, extensionOid, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(ex pkix.Extension, d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
if d.Type != "OCSP" {
|
||||
e.Err("OCSP Nocheck extension set in non OCSP signing certificate")
|
||||
}
|
||||
|
||||
if ex.Critical {
|
||||
e.Err("OCSP Nocheck Capabilities extension set critical")
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
29
vendor/github.com/globalsign/certlint/checks/extensions/pdfrevocation/pdfrevocation.go
generated
vendored
29
vendor/github.com/globalsign/certlint/checks/extensions/pdfrevocation/pdfrevocation.go
generated
vendored
|
|
@ -1,29 +0,0 @@
|
|||
package pdfrevocation
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "PDF Certificate Revocation Check"
|
||||
|
||||
var extensionOid = asn1.ObjectIdentifier{1, 2, 840, 113583, 1, 1, 8}
|
||||
|
||||
func init() {
|
||||
checks.RegisterExtensionCheck(checkName, extensionOid, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(ex pkix.Extension, d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
if ex.Critical {
|
||||
e.Err("PDF Certificate Revocation extension set critical")
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
package policyidentifiers
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "PolicyIdentifiers Extension Check"
|
||||
|
||||
var extensionOid = asn1.ObjectIdentifier{2, 5, 29, 32}
|
||||
|
||||
func init() {
|
||||
checks.RegisterExtensionCheck(checkName, extensionOid, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
//
|
||||
// Section 7.1.2.3 (a) of the Baseline Requirements states:
|
||||
//
|
||||
// This extension MUST be present and SHOULD NOT be marked critical.
|
||||
//
|
||||
// A Policy Identifier, defined by the issuing CA, that indicates a
|
||||
// Certificate Policy asserting the issuing CA's adherence to and compliance
|
||||
// with these Requirements.
|
||||
func Check(ex pkix.Extension, d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
// certificatePolicies SHOULD NOT be marked critical
|
||||
if ex.Critical {
|
||||
e.Err("PolicyIdentifiers extension set critical")
|
||||
}
|
||||
|
||||
// A policy must be defined
|
||||
if len(d.Cert.PolicyIdentifiers) == 0 {
|
||||
e.Err("PolicyIdentifiers not present")
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
package smimecapabilities
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "S/MIME Capabilities Extension Check"
|
||||
|
||||
var extensionOid = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 15}
|
||||
|
||||
func init() {
|
||||
checks.RegisterExtensionCheck(checkName, extensionOid, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(ex pkix.Extension, d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
if ex.Critical {
|
||||
e.Err("S/MIME Capabilities extension set critical")
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
29
vendor/github.com/globalsign/certlint/checks/extensions/subjectaltname/subjectaltname.go
generated
vendored
29
vendor/github.com/globalsign/certlint/checks/extensions/subjectaltname/subjectaltname.go
generated
vendored
|
|
@ -1,29 +0,0 @@
|
|||
package subjectaltname
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "SubjectAltName Extension Check"
|
||||
|
||||
var extensionOid = asn1.ObjectIdentifier{2, 5, 29, 17}
|
||||
|
||||
func init() {
|
||||
checks.RegisterExtensionCheck(checkName, extensionOid, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(ex pkix.Extension, d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
if ex.Critical {
|
||||
e.Err("SubjectAltName extension set critical")
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
29
vendor/github.com/globalsign/certlint/checks/extensions/subjectkeyid/subjectkeyid.go
generated
vendored
29
vendor/github.com/globalsign/certlint/checks/extensions/subjectkeyid/subjectkeyid.go
generated
vendored
|
|
@ -1,29 +0,0 @@
|
|||
package subjectkeyid
|
||||
|
||||
import (
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
"github.com/globalsign/certlint/checks"
|
||||
"github.com/globalsign/certlint/errors"
|
||||
)
|
||||
|
||||
const checkName = "SubjectKeyId Extension Check"
|
||||
|
||||
var extensionOid = asn1.ObjectIdentifier{2, 5, 29, 14}
|
||||
|
||||
func init() {
|
||||
checks.RegisterExtensionCheck(checkName, extensionOid, nil, Check)
|
||||
}
|
||||
|
||||
// Check performs a strict verification on the extension according to the standard(s)
|
||||
func Check(ex pkix.Extension, d *certdata.Data) *errors.Errors {
|
||||
var e = errors.New(nil)
|
||||
|
||||
if ex.Critical {
|
||||
e.Err("SubjectKeyId extension set critical")
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
package checks
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/globalsign/certlint/certdata"
|
||||
)
|
||||
|
||||
// Filter defines condition on when a check is performed or not
|
||||
type Filter struct {
|
||||
Type []string
|
||||
IssuedBefore *time.Time
|
||||
IssuedAfter *time.Time
|
||||
ExpiresBefore *time.Time
|
||||
ExpiresAfter *time.Time
|
||||
}
|
||||
|
||||
// Check returns true if a certificate complies with the given filter
|
||||
func (f *Filter) Check(d *certdata.Data) bool {
|
||||
// Is certificate recognised as one of the given types
|
||||
if len(f.Type) > 0 {
|
||||
var inFilter bool
|
||||
for _, t := range f.Type {
|
||||
if d.Type == t {
|
||||
inFilter = true
|
||||
}
|
||||
}
|
||||
if !inFilter {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Issued before given date
|
||||
if f.IssuedBefore != nil && !d.Cert.NotBefore.Before(*f.IssuedBefore) {
|
||||
return false
|
||||
}
|
||||
// Issued after given date
|
||||
if f.IssuedAfter != nil && !d.Cert.NotBefore.After(*f.IssuedAfter) {
|
||||
return false
|
||||
}
|
||||
// Expires before given date
|
||||
if f.ExpiresBefore != nil && !d.Cert.NotBefore.Before(*f.ExpiresBefore) {
|
||||
return false
|
||||
}
|
||||
// Expires after given date
|
||||
if f.ExpiresAfter != nil && !d.Cert.NotAfter.After(*f.ExpiresAfter) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
@ -1,178 +0,0 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
//go:generate stringer -type=Priority
|
||||
|
||||
// Priority defines how an error should be threaded
|
||||
type Priority int
|
||||
|
||||
// Priorities that can be used to create and list errors
|
||||
const (
|
||||
Unknown Priority = iota
|
||||
Debug
|
||||
Info
|
||||
Notice
|
||||
Warning
|
||||
Error
|
||||
Critical
|
||||
Alert
|
||||
Emergency
|
||||
)
|
||||
|
||||
// Config defines the error configuration, currently no configuration options
|
||||
// are available.
|
||||
type Config struct {
|
||||
}
|
||||
|
||||
// Err contains a single error
|
||||
type Err struct {
|
||||
p Priority
|
||||
msg string
|
||||
}
|
||||
|
||||
// Priority returns the priority of this error
|
||||
func (e Err) Priority() Priority {
|
||||
return e.p
|
||||
}
|
||||
|
||||
// String returns the message of this error
|
||||
func (e Err) Error() string {
|
||||
return e.msg
|
||||
}
|
||||
|
||||
// Errors contains a list of Error
|
||||
type Errors struct {
|
||||
err []Err
|
||||
config *Config
|
||||
p Priority
|
||||
m sync.Mutex
|
||||
}
|
||||
|
||||
// New creates an empty error container
|
||||
func New(c *Config) *Errors {
|
||||
return &Errors{config: c}
|
||||
}
|
||||
|
||||
// IsError returns true on one or more errors
|
||||
func (e *Errors) IsError() bool {
|
||||
if len(e.err) > 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Priority returns the priority of this error
|
||||
func (e *Errors) Priority() Priority {
|
||||
return e.p
|
||||
}
|
||||
|
||||
// List returns all errors of a given priority, if no priority is given all
|
||||
// errors are returned.
|
||||
func (e *Errors) List(p ...Priority) []Err {
|
||||
if len(p) == 0 {
|
||||
return e.err
|
||||
}
|
||||
|
||||
// filter errors to given priorities
|
||||
var l []Err
|
||||
for _, e := range e.err {
|
||||
for _, priority := range p {
|
||||
if e.p == priority {
|
||||
l = append(l, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// Append add all Errors to existing Errors
|
||||
func (e *Errors) Append(err *Errors) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
e.m.Lock()
|
||||
|
||||
// append Errors at the end of the current list
|
||||
e.err = append(e.err, err.err...)
|
||||
|
||||
// set highest priority
|
||||
if err.p > e.p {
|
||||
e.p = err.p
|
||||
}
|
||||
|
||||
e.m.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Emerg log an error with severity Emergency
|
||||
func (e *Errors) Emerg(format string, a ...interface{}) error {
|
||||
return e.add(Emergency, format, a...)
|
||||
}
|
||||
|
||||
// Alert log an error with severity Alert
|
||||
func (e *Errors) Alert(format string, a ...interface{}) error {
|
||||
return e.add(Alert, format, a...)
|
||||
}
|
||||
|
||||
// Crit log an error with severity Critical
|
||||
func (e *Errors) Crit(format string, a ...interface{}) error {
|
||||
return e.add(Critical, format, a...)
|
||||
}
|
||||
|
||||
// Err log an error with severity Error
|
||||
func (e *Errors) Err(format string, a ...interface{}) error {
|
||||
return e.add(Error, format, a...)
|
||||
}
|
||||
|
||||
// Warning log an error with severity Warning
|
||||
func (e *Errors) Warning(format string, a ...interface{}) error {
|
||||
return e.add(Warning, format, a...)
|
||||
}
|
||||
|
||||
// Notice log an error with severity Notice
|
||||
func (e *Errors) Notice(format string, a ...interface{}) error {
|
||||
return e.add(Notice, format, a...)
|
||||
}
|
||||
|
||||
// Info log an error with severity Info
|
||||
func (e *Errors) Info(format string, a ...interface{}) error {
|
||||
return e.add(Info, format, a...)
|
||||
}
|
||||
|
||||
// Debug log an error with severity Debug
|
||||
func (e *Errors) Debug(format string, a ...interface{}) error {
|
||||
return e.add(Debug, format, a...)
|
||||
}
|
||||
|
||||
func (e *Errors) add(p Priority, format string, a ...interface{}) error {
|
||||
// no error in request
|
||||
if len(format) == 0 && len(a) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
e.m.Lock()
|
||||
|
||||
msg := format
|
||||
if len(a) > 0 {
|
||||
msg = fmt.Sprintf(format, a...)
|
||||
}
|
||||
|
||||
// add this priority to the end of the list
|
||||
e.err = append(e.err, Err{
|
||||
p: p,
|
||||
msg: msg,
|
||||
})
|
||||
|
||||
// set highest priority in this list
|
||||
if p > e.p {
|
||||
e.p = p
|
||||
}
|
||||
|
||||
e.m.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
// Code generated by "stringer -type=Priority"; DO NOT EDIT.
|
||||
|
||||
package errors
|
||||
|
||||
import "strconv"
|
||||
|
||||
const _Priority_name = "UnknownDebugInfoNoticeWarningErrorCriticalAlertEmergency"
|
||||
|
||||
var _Priority_index = [...]uint8{0, 7, 12, 16, 22, 29, 34, 42, 47, 56}
|
||||
|
||||
func (i Priority) String() string {
|
||||
if i < 0 || i >= Priority(len(_Priority_index)-1) {
|
||||
return "Priority(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _Priority_name[_Priority_index[i]:_Priority_index[i+1]]
|
||||
}
|
||||
|
|
@ -0,0 +1,229 @@
|
|||
ZCrypto is an original work created at the University of Michigan, and is
|
||||
licensed under the Apache 2.0 license. However, ZCrypto contains a fork of
|
||||
several packages from Golang standard library, as well as code from the
|
||||
BoringSSL test runner. Files that were created by Google, and new files in
|
||||
forks of packages maintained by Google have a Google copyright and fall under
|
||||
the ISC license. All other files are copyright Regents of the University of
|
||||
Michigan, and fall under the Apache 2.0 license. Both licenses are reproduced
|
||||
at the bottom of this file.
|
||||
|
||||
--------
|
||||
|
||||
ISC License used for Google code
|
||||
|
||||
/* Copyright (c) 2015, Google Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
|
||||
|
||||
|
||||
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.
|
||||
|
||||
ZCrypto Copyright 2015 Regents of the University of Michigan
|
||||
|
||||
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.
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* ZGrab Copyright 2015 Regents of the University of Michigan
|
||||
*
|
||||
* 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 json
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// DHParams can be used to store finite-field Diffie-Hellman parameters. At any
|
||||
// point in time, it is unlikely that both OurPrivate and TheirPrivate will be
|
||||
// non-nil.
|
||||
type DHParams struct {
|
||||
Prime *big.Int
|
||||
Generator *big.Int
|
||||
ServerPublic *big.Int
|
||||
ServerPrivate *big.Int
|
||||
ClientPublic *big.Int
|
||||
ClientPrivate *big.Int
|
||||
SessionKey *big.Int
|
||||
}
|
||||
|
||||
type auxDHParams struct {
|
||||
Prime *cryptoParameter `json:"prime"`
|
||||
Generator *cryptoParameter `json:"generator"`
|
||||
ServerPublic *cryptoParameter `json:"server_public,omitempty"`
|
||||
ServerPrivate *cryptoParameter `json:"server_private,omitempty"`
|
||||
ClientPublic *cryptoParameter `json:"client_public,omitempty"`
|
||||
ClientPrivate *cryptoParameter `json:"client_private,omitempty"`
|
||||
SessionKey *cryptoParameter `json:"session_key,omitempty"`
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshal interface
|
||||
func (p *DHParams) MarshalJSON() ([]byte, error) {
|
||||
aux := auxDHParams{
|
||||
Prime: &cryptoParameter{Int: p.Prime},
|
||||
Generator: &cryptoParameter{Int: p.Generator},
|
||||
}
|
||||
if p.ServerPublic != nil {
|
||||
aux.ServerPublic = &cryptoParameter{Int: p.ServerPublic}
|
||||
}
|
||||
if p.ServerPrivate != nil {
|
||||
aux.ServerPrivate = &cryptoParameter{Int: p.ServerPrivate}
|
||||
}
|
||||
if p.ClientPublic != nil {
|
||||
aux.ClientPublic = &cryptoParameter{Int: p.ClientPublic}
|
||||
}
|
||||
if p.ClientPrivate != nil {
|
||||
aux.ClientPrivate = &cryptoParameter{Int: p.ClientPrivate}
|
||||
}
|
||||
if p.SessionKey != nil {
|
||||
aux.SessionKey = &cryptoParameter{Int: p.SessionKey}
|
||||
}
|
||||
return json.Marshal(aux)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implement the json.Unmarshaler interface
|
||||
func (p *DHParams) UnmarshalJSON(b []byte) error {
|
||||
var aux auxDHParams
|
||||
if err := json.Unmarshal(b, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
if aux.Prime != nil {
|
||||
p.Prime = aux.Prime.Int
|
||||
}
|
||||
if aux.Generator != nil {
|
||||
p.Generator = aux.Generator.Int
|
||||
}
|
||||
if aux.ServerPublic != nil {
|
||||
p.ServerPublic = aux.ServerPublic.Int
|
||||
}
|
||||
if aux.ServerPrivate != nil {
|
||||
p.ServerPrivate = aux.ServerPrivate.Int
|
||||
}
|
||||
if aux.ClientPublic != nil {
|
||||
p.ClientPublic = aux.ClientPublic.Int
|
||||
}
|
||||
if aux.ClientPrivate != nil {
|
||||
p.ClientPrivate = aux.ClientPrivate.Int
|
||||
}
|
||||
if aux.SessionKey != nil {
|
||||
p.SessionKey = aux.SessionKey.Int
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CryptoParameter represents a big.Int used a parameter in some cryptography.
|
||||
// It serializes to json as a tupe of a base64-encoded number and a length in
|
||||
// bits.
|
||||
type cryptoParameter struct {
|
||||
*big.Int
|
||||
}
|
||||
|
||||
type auxCryptoParameter struct {
|
||||
Raw []byte `json:"value"`
|
||||
Length int `json:"length"`
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface
|
||||
func (p *cryptoParameter) MarshalJSON() ([]byte, error) {
|
||||
var aux auxCryptoParameter
|
||||
if p.Int != nil {
|
||||
aux.Raw = p.Bytes()
|
||||
aux.Length = 8 * len(aux.Raw)
|
||||
}
|
||||
return json.Marshal(&aux)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshal interface
|
||||
func (p *cryptoParameter) UnmarshalJSON(b []byte) error {
|
||||
var aux auxCryptoParameter
|
||||
if err := json.Unmarshal(b, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
p.Int = new(big.Int)
|
||||
p.SetBytes(aux.Raw)
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* ZGrab Copyright 2015 Regents of the University of Michigan
|
||||
*
|
||||
* 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 json
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"encoding/json"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// TLSCurveID is the type of a TLS identifier for an elliptic curve. See
|
||||
// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8
|
||||
type TLSCurveID uint16
|
||||
|
||||
// ECDHPrivateParams are the TLS key exchange parameters for ECDH keys.
|
||||
type ECDHPrivateParams struct {
|
||||
Value []byte `json:"value,omitempty"`
|
||||
Length int `json:"length,omitempty"`
|
||||
}
|
||||
|
||||
// ECDHParams stores elliptic-curve Diffie-Hellman paramters.At any point in
|
||||
// time, it is unlikely that both ServerPrivate and ClientPrivate will be non-nil.
|
||||
type ECDHParams struct {
|
||||
TLSCurveID TLSCurveID `json:"curve_id,omitempty"`
|
||||
Curve elliptic.Curve `json:"-"`
|
||||
ServerPublic *ECPoint `json:"server_public,omitempty"`
|
||||
ServerPrivate *ECDHPrivateParams `json:"server_private,omitempty"`
|
||||
ClientPublic *ECPoint `json:"client_public,omitempty"`
|
||||
ClientPrivate *ECDHPrivateParams `json:"client_private,omitempty"`
|
||||
}
|
||||
|
||||
// ECPoint represents an elliptic curve point and serializes nicely to JSON
|
||||
type ECPoint struct {
|
||||
X *big.Int
|
||||
Y *big.Int
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshler interface
|
||||
func (p *ECPoint) MarshalJSON() ([]byte, error) {
|
||||
aux := struct {
|
||||
X *cryptoParameter `json:"x"`
|
||||
Y *cryptoParameter `json:"y"`
|
||||
}{
|
||||
X: &cryptoParameter{Int: p.X},
|
||||
Y: &cryptoParameter{Int: p.Y},
|
||||
}
|
||||
return json.Marshal(&aux)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshler interface
|
||||
func (p *ECPoint) UnmarshalJSON(b []byte) error {
|
||||
aux := struct {
|
||||
X *cryptoParameter `json:"x"`
|
||||
Y *cryptoParameter `json:"y"`
|
||||
}{}
|
||||
if err := json.Unmarshal(b, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
p.X = aux.X.Int
|
||||
p.Y = aux.Y.Int
|
||||
return nil
|
||||
}
|
||||
|
||||
// Description returns the description field for the given ID. See
|
||||
// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8
|
||||
func (c *TLSCurveID) Description() string {
|
||||
if desc, ok := ecIDToName[*c]; ok {
|
||||
return desc
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface
|
||||
func (c *TLSCurveID) MarshalJSON() ([]byte, error) {
|
||||
aux := struct {
|
||||
Name string `json:"name"`
|
||||
ID uint16 `json:"id"`
|
||||
}{
|
||||
Name: c.Description(),
|
||||
ID: uint16(*c),
|
||||
}
|
||||
return json.Marshal(&aux)
|
||||
}
|
||||
|
||||
//UnmarshalJSON implements the json.Unmarshaler interface
|
||||
func (c *TLSCurveID) UnmarshalJSON(b []byte) error {
|
||||
aux := struct {
|
||||
ID uint16 `json:"id"`
|
||||
}{}
|
||||
if err := json.Unmarshal(b, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
*c = TLSCurveID(aux.ID)
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* ZGrab Copyright 2015 Regents of the University of Michigan
|
||||
*
|
||||
* 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 json
|
||||
|
||||
// IANA-assigned curve ID values, see
|
||||
// http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-8
|
||||
const (
|
||||
Sect163k1 TLSCurveID = 1
|
||||
Sect163r1 TLSCurveID = 2
|
||||
Sect163r2 TLSCurveID = 3
|
||||
Sect193r1 TLSCurveID = 4
|
||||
Sect193r2 TLSCurveID = 5
|
||||
Sect233k1 TLSCurveID = 6
|
||||
Sect233r1 TLSCurveID = 7
|
||||
Sect239k1 TLSCurveID = 8
|
||||
Sect283k1 TLSCurveID = 9
|
||||
Sect283r1 TLSCurveID = 10
|
||||
Sect409k1 TLSCurveID = 11
|
||||
Sect409r1 TLSCurveID = 12
|
||||
Sect571k1 TLSCurveID = 13
|
||||
Sect571r1 TLSCurveID = 14
|
||||
Secp160k1 TLSCurveID = 15
|
||||
Secp160r1 TLSCurveID = 16
|
||||
Secp160r2 TLSCurveID = 17
|
||||
Secp192k1 TLSCurveID = 18
|
||||
Secp192r1 TLSCurveID = 19
|
||||
Secp224k1 TLSCurveID = 20
|
||||
Secp224r1 TLSCurveID = 21
|
||||
Secp256k1 TLSCurveID = 22
|
||||
Secp256r1 TLSCurveID = 23
|
||||
Secp384r1 TLSCurveID = 24
|
||||
Secp521r1 TLSCurveID = 25
|
||||
BrainpoolP256r1 TLSCurveID = 26
|
||||
BrainpoolP384r1 TLSCurveID = 27
|
||||
BrainpoolP512r1 TLSCurveID = 28
|
||||
)
|
||||
|
||||
var ecIDToName map[TLSCurveID]string
|
||||
var ecNameToID map[string]TLSCurveID
|
||||
|
||||
func init() {
|
||||
ecIDToName = make(map[TLSCurveID]string, 64)
|
||||
ecIDToName[Sect163k1] = "sect163k1"
|
||||
ecIDToName[Sect163r1] = "sect163r1"
|
||||
ecIDToName[Sect163r2] = "sect163r2"
|
||||
ecIDToName[Sect193r1] = "sect193r1"
|
||||
ecIDToName[Sect193r2] = "sect193r2"
|
||||
ecIDToName[Sect233k1] = "sect233k1"
|
||||
ecIDToName[Sect233r1] = "sect233r1"
|
||||
ecIDToName[Sect239k1] = "sect239k1"
|
||||
ecIDToName[Sect283k1] = "sect283k1"
|
||||
ecIDToName[Sect283r1] = "sect283r1"
|
||||
ecIDToName[Sect409k1] = "sect409k1"
|
||||
ecIDToName[Sect409r1] = "sect409r1"
|
||||
ecIDToName[Sect571k1] = "sect571k1"
|
||||
ecIDToName[Sect571r1] = "sect571r1"
|
||||
ecIDToName[Secp160k1] = "secp160k1"
|
||||
ecIDToName[Secp160r1] = "secp160r1"
|
||||
ecIDToName[Secp160r2] = "secp160r2"
|
||||
ecIDToName[Secp192k1] = "secp192k1"
|
||||
ecIDToName[Secp192r1] = "secp192r1"
|
||||
ecIDToName[Secp224k1] = "secp224k1"
|
||||
ecIDToName[Secp224r1] = "secp224r1"
|
||||
ecIDToName[Secp256k1] = "secp256k1"
|
||||
ecIDToName[Secp256r1] = "secp256r1"
|
||||
ecIDToName[Secp384r1] = "secp384r1"
|
||||
ecIDToName[Secp521r1] = "secp521r1"
|
||||
ecIDToName[BrainpoolP256r1] = "brainpoolp256r1"
|
||||
ecIDToName[BrainpoolP384r1] = "brainpoolp384r1"
|
||||
ecIDToName[BrainpoolP512r1] = "brainpoolp512r1"
|
||||
|
||||
ecNameToID = make(map[string]TLSCurveID, 64)
|
||||
ecNameToID["sect163k1"] = Sect163k1
|
||||
ecNameToID["sect163r1"] = Sect163r1
|
||||
ecNameToID["sect163r2"] = Sect163r2
|
||||
ecNameToID["sect193r1"] = Sect193r1
|
||||
ecNameToID["sect193r2"] = Sect193r2
|
||||
ecNameToID["sect233k1"] = Sect233k1
|
||||
ecNameToID["sect233r1"] = Sect233r1
|
||||
ecNameToID["sect239k1"] = Sect239k1
|
||||
ecNameToID["sect283k1"] = Sect283k1
|
||||
ecNameToID["sect283r1"] = Sect283r1
|
||||
ecNameToID["sect409k1"] = Sect409k1
|
||||
ecNameToID["sect409r1"] = Sect409r1
|
||||
ecNameToID["sect571k1"] = Sect571k1
|
||||
ecNameToID["sect571r1"] = Sect571r1
|
||||
ecNameToID["secp160k1"] = Secp160k1
|
||||
ecNameToID["secp160r1"] = Secp160r1
|
||||
ecNameToID["secp160r2"] = Secp160r2
|
||||
ecNameToID["secp192k1"] = Secp192k1
|
||||
ecNameToID["secp192r1"] = Secp192r1
|
||||
ecNameToID["secp224k1"] = Secp224k1
|
||||
ecNameToID["secp224r1"] = Secp224r1
|
||||
ecNameToID["secp256k1"] = Secp256k1
|
||||
ecNameToID["secp256r1"] = Secp256r1
|
||||
ecNameToID["secp384r1"] = Secp384r1
|
||||
ecNameToID["secp521r1"] = Secp521r1
|
||||
ecNameToID["brainpoolp256r1"] = BrainpoolP256r1
|
||||
ecNameToID["brainpoolp384r1"] = BrainpoolP384r1
|
||||
ecNameToID["brainpoolp512r1"] = BrainpoolP512r1
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* ZGrab Copyright 2015 Regents of the University of Michigan
|
||||
*
|
||||
* 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 json
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// RSAPublicKey provides JSON methods for the standard rsa.PublicKey.
|
||||
type RSAPublicKey struct {
|
||||
*rsa.PublicKey
|
||||
}
|
||||
|
||||
type auxRSAPublicKey struct {
|
||||
Exponent int `json:"exponent"`
|
||||
Modulus []byte `json:"modulus"`
|
||||
Length int `json:"length"`
|
||||
}
|
||||
|
||||
// RSAClientParams are the TLS key exchange parameters for RSA keys.
|
||||
type RSAClientParams struct {
|
||||
Length uint16 `json:"length,omitempty"`
|
||||
EncryptedPMS []byte `json:"encrypted_pre_master_secret,omitempty"`
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshal interface
|
||||
func (rp *RSAPublicKey) MarshalJSON() ([]byte, error) {
|
||||
var aux auxRSAPublicKey
|
||||
if rp.PublicKey != nil {
|
||||
aux.Exponent = rp.E
|
||||
aux.Modulus = rp.N.Bytes()
|
||||
aux.Length = len(aux.Modulus) * 8
|
||||
}
|
||||
return json.Marshal(&aux)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshal interface
|
||||
func (rp *RSAPublicKey) UnmarshalJSON(b []byte) error {
|
||||
var aux auxRSAPublicKey
|
||||
if err := json.Unmarshal(b, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
if rp.PublicKey == nil {
|
||||
rp.PublicKey = new(rsa.PublicKey)
|
||||
}
|
||||
rp.E = aux.Exponent
|
||||
rp.N = big.NewInt(0).SetBytes(aux.Modulus)
|
||||
if len(aux.Modulus)*8 != aux.Length {
|
||||
return fmt.Errorf("mismatched length (got %d, field specified %d)", len(aux.Modulus), aux.Length)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
This package is a port of github.com/golang/go/crypto/x509 package at:
|
||||
branch: release-branch.go1.9
|
||||
revision: c03ee1985cb6e4467246a2bdb07bb1c62e05f8e9
|
||||
|
||||
Modifications to shared files are located in $name_modified.go to
|
||||
facilitate tracking the stdlib updates.
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package x509
|
||||
|
||||
import (
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
//// CertPool is a set of certificates.
|
||||
//type CertPool struct {
|
||||
// bySubjectKeyId map[string][]int
|
||||
// byName map[string][]int
|
||||
// certs []*Certificate
|
||||
//}
|
||||
//
|
||||
//// NewCertPool returns a new, empty CertPool.
|
||||
//func NewCertPool() *CertPool {
|
||||
// return &CertPool{
|
||||
// bySubjectKeyId: make(map[string][]int),
|
||||
// byName: make(map[string][]int),
|
||||
// }
|
||||
//}
|
||||
|
||||
// SystemCertPool returns a copy of the system cert pool.
|
||||
//
|
||||
// Any mutations to the returned pool are not written to disk and do
|
||||
// not affect any other pool.
|
||||
func SystemCertPool() (*CertPool, error) {
|
||||
if runtime.GOOS == "windows" {
|
||||
// Issue 16736, 18609:
|
||||
return nil, errors.New("crypto/x509: system root pool is not available on Windows")
|
||||
}
|
||||
|
||||
return loadSystemRoots()
|
||||
}
|
||||
|
||||
//// findVerifiedParents attempts to find certificates in s which have signed the
|
||||
//// given certificate. If any candidates were rejected then errCert will be set
|
||||
//// to one of them, arbitrarily, and err will contain the reason that it was
|
||||
//// rejected.
|
||||
//func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int, errCert *Certificate, err error) {
|
||||
// if s == nil {
|
||||
// return
|
||||
// }
|
||||
// var candidates []int
|
||||
//
|
||||
// if len(cert.AuthorityKeyId) > 0 {
|
||||
// candidates = s.bySubjectKeyId[string(cert.AuthorityKeyId)]
|
||||
// }
|
||||
// if len(candidates) == 0 {
|
||||
// candidates = s.byName[string(cert.RawIssuer)]
|
||||
// }
|
||||
//
|
||||
// for _, c := range candidates {
|
||||
// if err = cert.CheckSignatureFrom(s.certs[c]); err == nil {
|
||||
// parents = append(parents, c)
|
||||
// } else {
|
||||
// errCert = s.certs[c]
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return
|
||||
//}
|
||||
|
||||
func (s *CertPool) contains(cert *Certificate) bool {
|
||||
if s == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
candidates := s.byName[string(cert.RawSubject)]
|
||||
for _, c := range candidates {
|
||||
if s.certs[c].Equal(cert) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
//// AddCert adds a certificate to a pool.
|
||||
//func (s *CertPool) AddCert(cert *Certificate) {
|
||||
// if cert == nil {
|
||||
// panic("adding nil Certificate to CertPool")
|
||||
// }
|
||||
//
|
||||
// // Check that the certificate isn't being added twice.
|
||||
// if s.contains(cert) {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// n := len(s.certs)
|
||||
// s.certs = append(s.certs, cert)
|
||||
//
|
||||
// if len(cert.SubjectKeyId) > 0 {
|
||||
// keyId := string(cert.SubjectKeyId)
|
||||
// s.bySubjectKeyId[keyId] = append(s.bySubjectKeyId[keyId], n)
|
||||
// }
|
||||
// name := string(cert.RawSubject)
|
||||
// s.byName[name] = append(s.byName[name], n)
|
||||
//}
|
||||
|
||||
// AppendCertsFromPEM attempts to parse a series of PEM encoded certificates.
|
||||
// It appends any certificates found to s and reports whether any certificates
|
||||
// were successfully parsed.
|
||||
//
|
||||
// On many Linux systems, /etc/ssl/cert.pem will contain the system wide set
|
||||
// of root CAs in a format suitable for this function.
|
||||
func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) {
|
||||
for len(pemCerts) > 0 {
|
||||
var block *pem.Block
|
||||
block, pemCerts = pem.Decode(pemCerts)
|
||||
if block == nil {
|
||||
break
|
||||
}
|
||||
if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
cert, err := ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
s.AddCert(cert)
|
||||
ok = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Subjects returns a list of the DER-encoded subjects of
|
||||
// all of the certificates in the pool.
|
||||
func (s *CertPool) Subjects() [][]byte {
|
||||
res := make([][]byte, len(s.certs))
|
||||
for i, c := range s.certs {
|
||||
res[i] = c.RawSubject
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
// 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
|
||||
|
||||
// CertPool is a set of certificates.
|
||||
type CertPool struct {
|
||||
bySubjectKeyId map[string][]int
|
||||
byName map[string][]int
|
||||
bySHA256 map[string]int
|
||||
certs []*Certificate
|
||||
}
|
||||
|
||||
// NewCertPool returns a new, empty CertPool.
|
||||
func NewCertPool() *CertPool {
|
||||
return &CertPool{
|
||||
bySubjectKeyId: make(map[string][]int),
|
||||
byName: make(map[string][]int),
|
||||
bySHA256: make(map[string]int),
|
||||
}
|
||||
}
|
||||
|
||||
// findVerifiedParents attempts to find certificates in s which have signed the
|
||||
// given certificate. If any candidates were rejected then errCert will be set
|
||||
// to one of them, arbitrarily, and err will contain the reason that it was
|
||||
// rejected.
|
||||
func (s *CertPool) findVerifiedParents(cert *Certificate) (parents []int, errCert *Certificate, err error) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
var candidates []int
|
||||
|
||||
if len(cert.AuthorityKeyId) > 0 {
|
||||
candidates, _ = s.bySubjectKeyId[string(cert.AuthorityKeyId)]
|
||||
}
|
||||
if len(candidates) == 0 {
|
||||
candidates, _ = s.byName[string(cert.RawIssuer)]
|
||||
}
|
||||
|
||||
for _, c := range candidates {
|
||||
if err = cert.CheckSignatureFrom(s.certs[c]); err == nil {
|
||||
cert.validSignature = true
|
||||
parents = append(parents, c)
|
||||
} else {
|
||||
errCert = s.certs[c]
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// AddCert adds a certificate to a pool.
|
||||
func (s *CertPool) AddCert(cert *Certificate) {
|
||||
if cert == nil {
|
||||
panic("adding nil Certificate to CertPool")
|
||||
}
|
||||
|
||||
// Check that the certificate isn't being added twice.
|
||||
sha256fp := string(cert.FingerprintSHA256)
|
||||
if _, ok := s.bySHA256[sha256fp]; ok {
|
||||
return
|
||||
}
|
||||
|
||||
n := len(s.certs)
|
||||
s.certs = append(s.certs, cert)
|
||||
|
||||
if len(cert.SubjectKeyId) > 0 {
|
||||
keyId := string(cert.SubjectKeyId)
|
||||
s.bySubjectKeyId[keyId] = append(s.bySubjectKeyId[keyId], n)
|
||||
}
|
||||
name := string(cert.RawSubject)
|
||||
s.byName[name] = append(s.byName[name], n)
|
||||
s.bySHA256[sha256fp] = n
|
||||
}
|
||||
|
||||
// Contains returns true if c is in s.
|
||||
func (s *CertPool) Contains(c *Certificate) bool {
|
||||
if s == nil {
|
||||
return false
|
||||
}
|
||||
_, ok := s.bySHA256[string(c.FingerprintSHA256)]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Covers returns true if all certs in pool are in s.
|
||||
func (s *CertPool) Covers(pool *CertPool) bool {
|
||||
if pool == nil {
|
||||
return true
|
||||
}
|
||||
for _, c := range pool.certs {
|
||||
if !s.Contains(c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Certificates returns a list of parsed certificates in the pool.
|
||||
func (s *CertPool) Certificates() []*Certificate {
|
||||
out := make([]*Certificate, 0, len(s.certs))
|
||||
out = append(out, s.certs...)
|
||||
return out
|
||||
}
|
||||
|
||||
// Size returns the number of unique certificates in the CertPool.
|
||||
func (s *CertPool) Size() int {
|
||||
if s == nil {
|
||||
return 0
|
||||
}
|
||||
return len(s.certs)
|
||||
}
|
||||
|
||||
// Sum returns the union of two certificate pools as a new certificate pool.
|
||||
func (s *CertPool) Sum(other *CertPool) (sum *CertPool) {
|
||||
sum = NewCertPool()
|
||||
if s != nil {
|
||||
for _, c := range s.certs {
|
||||
sum.AddCert(c)
|
||||
}
|
||||
}
|
||||
if other != nil {
|
||||
for _, c := range other.certs {
|
||||
sum.AddCert(c)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package x509
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
// TODO: Automatically generate this file from a CSV
|
||||
|
||||
// CertificateType represents whether a certificate is a root, intermediate, or
|
||||
// leaf.
|
||||
type CertificateType int
|
||||
|
||||
// CertificateType constants. Values should not be considered significant aside
|
||||
// from CertificateTypeUnknown is the zero value.
|
||||
const (
|
||||
CertificateTypeUnknown CertificateType = 0
|
||||
CertificateTypeLeaf CertificateType = 1
|
||||
CertificateTypeIntermediate CertificateType = 2
|
||||
CertificateTypeRoot CertificateType = 3
|
||||
)
|
||||
|
||||
const (
|
||||
certificateTypeStringLeaf = "leaf"
|
||||
certificateTypeStringIntermediate = "intermediate"
|
||||
certificateTypeStringRoot = "root"
|
||||
certificateTypeStringUnknown = "unknown"
|
||||
)
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface. Any unknown integer
|
||||
// value is considered the same as CertificateTypeUnknown.
|
||||
func (t CertificateType) MarshalJSON() ([]byte, error) {
|
||||
switch t {
|
||||
case CertificateTypeLeaf:
|
||||
return json.Marshal(certificateTypeStringLeaf)
|
||||
case CertificateTypeIntermediate:
|
||||
return json.Marshal(certificateTypeStringIntermediate)
|
||||
case CertificateTypeRoot:
|
||||
return json.Marshal(certificateTypeStringRoot)
|
||||
default:
|
||||
return json.Marshal(certificateTypeStringUnknown)
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface. Any unknown string
|
||||
// is considered the same CertificateTypeUnknown.
|
||||
func (t *CertificateType) UnmarshalJSON(b []byte) error {
|
||||
var certificateTypeString string
|
||||
if err := json.Unmarshal(b, &certificateTypeString); err != nil {
|
||||
return err
|
||||
}
|
||||
switch certificateTypeString {
|
||||
case certificateTypeStringLeaf:
|
||||
*t = CertificateTypeLeaf
|
||||
case certificateTypeStringIntermediate:
|
||||
*t = CertificateTypeIntermediate
|
||||
case certificateTypeStringRoot:
|
||||
*t = CertificateTypeRoot
|
||||
default:
|
||||
*t = CertificateTypeUnknown
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package x509
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CertificateChain is a slice of certificates. The 0'th element is the leaf,
|
||||
// and the last element is a root. Successive elements have a child-parent
|
||||
// relationship.
|
||||
type CertificateChain []*Certificate
|
||||
|
||||
// Range runs a function on each element of chain. It can modify each
|
||||
// certificate in place.
|
||||
func (chain CertificateChain) Range(f func(int, *Certificate)) {
|
||||
for i, c := range chain {
|
||||
f(i, c)
|
||||
}
|
||||
}
|
||||
|
||||
// SubjectAndKeyInChain returns true if the given SubjectAndKey is found in any
|
||||
// certificate in the chain.
|
||||
func (chain CertificateChain) SubjectAndKeyInChain(sk *SubjectAndKey) bool {
|
||||
for _, cert := range chain {
|
||||
if bytes.Equal(sk.RawSubject, cert.RawSubject) && bytes.Equal(sk.RawSubjectPublicKeyInfo, cert.RawSubjectPublicKeyInfo) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// CertificateSubjectAndKeyInChain returns true if the SubjectAndKey from c is
|
||||
// found in any certificate in the chain.
|
||||
func (chain CertificateChain) CertificateSubjectAndKeyInChain(c *Certificate) bool {
|
||||
for _, cert := range chain {
|
||||
if bytes.Equal(c.RawSubject, cert.RawSubject) && bytes.Equal(c.RawSubjectPublicKeyInfo, cert.RawSubjectPublicKeyInfo) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// CertificateInChain returns true if c is in the chain.
|
||||
func (chain CertificateChain) CertificateInChain(c *Certificate) bool {
|
||||
for _, cert := range chain {
|
||||
if bytes.Equal(c.Raw, cert.Raw) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (chain CertificateChain) AppendToFreshChain(c *Certificate) CertificateChain {
|
||||
n := make([]*Certificate, len(chain)+1)
|
||||
copy(n, chain)
|
||||
n[len(chain)] = c
|
||||
return n
|
||||
}
|
||||
|
||||
func (chain CertificateChain) chainID() string {
|
||||
var parts []string
|
||||
for _, c := range chain {
|
||||
parts = append(parts, string(c.FingerprintSHA256))
|
||||
}
|
||||
return strings.Join(parts, "")
|
||||
}
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
package ct
|
||||
|
||||
// This file contains selectively chosen snippets of
|
||||
// github.com/google/certificate-transparency-go@ 5cfe585726ad9d990d4db524d6ce2567b13e2f80
|
||||
//
|
||||
// These snippets only perform deserialization for SCTs and are recreated here to prevent pulling in the whole of the ct
|
||||
// which contains yet another version of x509,asn1 and tls
|
||||
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Variable size structure prefix-header byte lengths
|
||||
const (
|
||||
CertificateLengthBytes = 3
|
||||
PreCertificateLengthBytes = 3
|
||||
ExtensionsLengthBytes = 2
|
||||
CertificateChainLengthBytes = 3
|
||||
SignatureLengthBytes = 2
|
||||
)
|
||||
|
||||
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)
|
||||
if n, err := io.ReadFull(r, data); err != nil {
|
||||
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
||||
return nil, fmt.Errorf("short read: expected %d but got %d", l, n)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return data, 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 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
package ct
|
||||
|
||||
// This file contains selectively chosen snippets of
|
||||
// github.com/google/certificate-transparency-go@ 5cfe585726ad9d990d4db524d6ce2567b13e2f80
|
||||
//
|
||||
// These snippets only perform deserialization for SCTs and are recreated here to prevent pulling in the whole of the ct
|
||||
// which contains yet another version of x509,asn1 and tls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// CTExtensions is a representation of the raw bytes of any CtExtension
|
||||
// structure (see section 3.2)
|
||||
type CTExtensions []byte
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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
|
||||
)
|
||||
|
||||
// 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 `json:"version"` // The version of the protocol to which the SCT conforms
|
||||
LogID SHA256Hash `json:"log_id"` // the SHA-256 hash of the log's public key, calculated over
|
||||
// the DER encoding of the key represented as SubjectPublicKeyInfo.
|
||||
Timestamp uint64 `json:"timestamp,omitempty"` // Timestamp (in ms since unix epoc) at which the SCT was issued
|
||||
Extensions CTExtensions `json:"extensions,omitempty"` // For future extensions to the protocol
|
||||
Signature DigitallySigned `json:"signature"` // The Log's signature for this SCT
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
{
|
||||
"domain": null,
|
||||
"certificate": {
|
||||
"version": 3,
|
||||
"serial_number": 123893,
|
||||
"signature_algorithm": {
|
||||
"id": 123,
|
||||
"name": "SHA1"
|
||||
},
|
||||
"issuer": {
|
||||
"common_name": "Starfield CA",
|
||||
"attributes": [
|
||||
{ "organization": "Startfield" },
|
||||
{ "location": "Scottsdale" },
|
||||
{ "state": "Arizona" },
|
||||
{ "country": "US" }
|
||||
]
|
||||
},
|
||||
"validity": {
|
||||
"start": "20140102",
|
||||
"end": "20150102",
|
||||
"length" :8760
|
||||
},
|
||||
"subject": {
|
||||
"common_name": "*.tools.ieft.org",
|
||||
"attributes": [
|
||||
{ "organization_unit": "Domain Control Validated" }
|
||||
]
|
||||
},
|
||||
"subject_key_info": {
|
||||
"algorithm": {
|
||||
"id": 234,
|
||||
"name": "RSA"
|
||||
},
|
||||
"key": {
|
||||
"modulus": "base64encodedmodulus",
|
||||
"exponent": 65537
|
||||
}
|
||||
},
|
||||
"extensions": [
|
||||
{
|
||||
"id": 345,
|
||||
"name": "Certificate Basic Constraints",
|
||||
"is_ca": false
|
||||
},
|
||||
{
|
||||
"id": 456,
|
||||
"name": "Alt Names",
|
||||
"alt_names": [
|
||||
"*.tools.ietf.org",
|
||||
"tools.ietf.org"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"signature_algorithm": {
|
||||
"id": 123,
|
||||
"name": "SHA1"
|
||||
},
|
||||
"signature": {
|
||||
"value": "base64encodedsignature",
|
||||
"is_valid": true,
|
||||
"matches_domain": null
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,665 @@
|
|||
// Created by extended_key_usage_gen; DO NOT EDIT
|
||||
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package x509
|
||||
|
||||
import (
|
||||
"encoding/asn1"
|
||||
)
|
||||
|
||||
const (
|
||||
OID_EKU_APPLE_CODE_SIGNING = "1.2.840.113635.100.4.1"
|
||||
OID_EKU_APPLE_CRYPTO_ENV = "1.2.840.113635.100.4.5"
|
||||
OID_EKU_MICROSOFT_SYSTEM_HEALTH_LOOPHOLE = "1.3.6.1.4.1.311.47.1.3"
|
||||
OID_EKU_ANY = "2.5.29.37.0"
|
||||
OID_EKU_APPLE_CRYPTO_MAINTENANCE_ENV = "1.2.840.113635.100.4.5.2"
|
||||
OID_EKU_IPSEC_TUNNEL = "1.3.6.1.5.5.7.3.6"
|
||||
OID_EKU_MICROSOFT_LIFETIME_SIGNING = "1.3.6.1.4.1.311.10.3.13"
|
||||
OID_EKU_MICROSOFT_CSP_SIGNATURE = "1.3.6.1.4.1.311.10.3.16"
|
||||
OID_EKU_MICROSOFT_SGC_SERIALIZED = "1.3.6.1.4.1.311.10.3.3.1"
|
||||
OID_EKU_SBGP_CERT_AA_SERVICE_AUTH = "1.3.6.1.5.5.7.3.11"
|
||||
OID_EKU_EAP_OVER_LAN = "1.3.6.1.5.5.7.3.14"
|
||||
OID_EKU_APPLE_SYSTEM_IDENTITY = "1.2.840.113635.100.4.4"
|
||||
OID_EKU_MICROSOFT_KEY_RECOVERY_3 = "1.3.6.1.4.1.311.10.3.11"
|
||||
OID_EKU_MICROSOFT_CERT_TRUST_LIST_SIGNING = "1.3.6.1.4.1.311.10.3.1"
|
||||
OID_EKU_MICROSOFT_DRM = "1.3.6.1.4.1.311.10.5.1"
|
||||
OID_EKU_MICROSOFT_LICENSES = "1.3.6.1.4.1.311.10.5.3"
|
||||
OID_EKU_MICROSOFT_LICENSE_SERVER = "1.3.6.1.4.1.311.10.5.4"
|
||||
OID_EKU_MICROSOFT_ENROLLMENT_AGENT = "1.3.6.1.4.1.311.20.2.1"
|
||||
OID_EKU_APPLE_SOFTWARE_UPDATE_SIGNING = "1.2.840.113635.100.4.1.2"
|
||||
OID_EKU_APPLE_CRYPTO_TEST_ENV = "1.2.840.113635.100.4.5.3"
|
||||
OID_EKU_MICROSOFT_EMBEDDED_NT_CRYPTO = "1.3.6.1.4.1.311.10.3.8"
|
||||
OID_EKU_MICROSOFT_DRM_INDIVIDUALIZATION = "1.3.6.1.4.1.311.10.5.2"
|
||||
OID_EKU_MICROSOFT_KEY_RECOVERY_21 = "1.3.6.1.4.1.311.21.6"
|
||||
OID_EKU_EAP_OVER_PPP = "1.3.6.1.5.5.7.3.13"
|
||||
OID_EKU_OCSP_SIGNING = "1.3.6.1.5.5.7.3.9"
|
||||
OID_EKU_APPLE_CRYPTO_PRODUCTION_ENV = "1.2.840.113635.100.4.5.1"
|
||||
OID_EKU_APPLE_CRYPTO_TIER1_QOS = "1.2.840.113635.100.4.6.2"
|
||||
OID_EKU_CLIENT_AUTH = "1.3.6.1.5.5.7.3.2"
|
||||
OID_EKU_APPLE_CRYPTO_DEVELOPMENT_ENV = "1.2.840.113635.100.4.5.4"
|
||||
OID_EKU_APPLE_CRYPTO_TIER0_QOS = "1.2.840.113635.100.4.6.1"
|
||||
OID_EKU_APPLE_CRYPTO_TIER2_QOS = "1.2.840.113635.100.4.6.3"
|
||||
OID_EKU_MICROSOFT_MOBILE_DEVICE_SOFTWARE = "1.3.6.1.4.1.311.10.3.14"
|
||||
OID_EKU_MICROSOFT_NT5_CRYPTO = "1.3.6.1.4.1.311.10.3.6"
|
||||
OID_EKU_CODE_SIGNING = "1.3.6.1.5.5.7.3.3"
|
||||
OID_EKU_APPLE_CODE_SIGNING_THIRD_PARTY = "1.2.840.113635.100.4.1.3"
|
||||
OID_EKU_MICROSOFT_TIMESTAMP_SIGNING = "1.3.6.1.4.1.311.10.3.2"
|
||||
OID_EKU_APPLE_CODE_SIGNING_DEVELOPMENT = "1.2.840.113635.100.4.1.1"
|
||||
OID_EKU_APPLE_CRYPTO_QOS = "1.2.840.113635.100.4.6"
|
||||
OID_EKU_MICROSOFT_DOCUMENT_SIGNING = "1.3.6.1.4.1.311.10.3.12"
|
||||
OID_EKU_MICROSOFT_ENCRYPTED_FILE_SYSTEM = "1.3.6.1.4.1.311.10.3.4"
|
||||
OID_EKU_MICROSOFT_WHQL_CRYPTO = "1.3.6.1.4.1.311.10.3.5"
|
||||
OID_EKU_MICROSOFT_ROOT_LIST_SIGNER = "1.3.6.1.4.1.311.10.3.9"
|
||||
OID_EKU_MICROSOFT_SYSTEM_HEALTH = "1.3.6.1.4.1.311.47.1.1"
|
||||
OID_EKU_TIME_STAMPING = "1.3.6.1.5.5.7.3.8"
|
||||
OID_EKU_NETSCAPE_SERVER_GATED_CRYPTO = "2.16.840.1.113730.4.1"
|
||||
OID_EKU_APPLE_CRYPTO_TIER3_QOS = "1.2.840.113635.100.4.6.4"
|
||||
OID_EKU_MICROSOFT_SMART_DISPLAY = "1.3.6.1.4.1.311.10.3.15"
|
||||
OID_EKU_MICROSOFT_EFS_RECOVERY = "1.3.6.1.4.1.311.10.3.4.1"
|
||||
OID_EKU_MICROSOFT_KERNEL_MODE_CODE_SIGNING = "1.3.6.1.4.1.311.61.1.1"
|
||||
OID_EKU_SERVER_AUTH = "1.3.6.1.5.5.7.3.1"
|
||||
OID_EKU_IPSEC_END_SYSTEM = "1.3.6.1.5.5.7.3.5"
|
||||
OID_EKU_IPSEC_USER = "1.3.6.1.5.5.7.3.7"
|
||||
OID_EKU_MICROSOFT_QUALIFIED_SUBORDINATE = "1.3.6.1.4.1.311.10.3.10"
|
||||
OID_EKU_APPLE_RESOURCE_SIGNING = "1.2.840.113635.100.4.1.4"
|
||||
OID_EKU_MICROSOFT_OEM_WHQL_CRYPTO = "1.3.6.1.4.1.311.10.3.7"
|
||||
OID_EKU_MICROSOFT_SMARTCARD_LOGON = "1.3.6.1.4.1.311.20.2.2"
|
||||
OID_EKU_EMAIL_PROTECTION = "1.3.6.1.5.5.7.3.4"
|
||||
OID_EKU_MICROSOFT_SERVER_GATED_CRYPTO = "1.3.6.1.4.1.311.10.3.3"
|
||||
OID_EKU_MICROSOFT_CA_EXCHANGE = "1.3.6.1.4.1.311.21.5"
|
||||
OID_EKU_DVCS = "1.3.6.1.5.5.7.3.10"
|
||||
OID_EKU_APPLE_ICHAT_SIGNING = "1.2.840.113635.100.4.2"
|
||||
OID_EKU_APPLE_ICHAT_ENCRYPTION = "1.2.840.113635.100.4.3"
|
||||
)
|
||||
var (
|
||||
oidExtKeyUsageTimeStamping = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 8}
|
||||
oidExtKeyUsageAppleCodeSigningDevelopment = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 4, 1, 1}
|
||||
oidExtKeyUsageAppleCryptoQos = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 4, 6}
|
||||
oidExtKeyUsageMicrosoftDocumentSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 12}
|
||||
oidExtKeyUsageMicrosoftEncryptedFileSystem = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 4}
|
||||
oidExtKeyUsageMicrosoftWhqlCrypto = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 5}
|
||||
oidExtKeyUsageMicrosoftRootListSigner = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 9}
|
||||
oidExtKeyUsageMicrosoftSystemHealth = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 47, 1, 1}
|
||||
oidExtKeyUsageNetscapeServerGatedCrypto = asn1.ObjectIdentifier{2, 16, 840, 1, 113730, 4, 1}
|
||||
oidExtKeyUsageAppleCryptoTier3Qos = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 4, 6, 4}
|
||||
oidExtKeyUsageMicrosoftSmartDisplay = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 15}
|
||||
oidExtKeyUsageMicrosoftEfsRecovery = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 4, 1}
|
||||
oidExtKeyUsageMicrosoftKernelModeCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 61, 1, 1}
|
||||
oidExtKeyUsageServerAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 1}
|
||||
oidExtKeyUsageIpsecEndSystem = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 5}
|
||||
oidExtKeyUsageIpsecUser = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 7}
|
||||
oidExtKeyUsageMicrosoftQualifiedSubordinate = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 10}
|
||||
oidExtKeyUsageAppleResourceSigning = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 4, 1, 4}
|
||||
oidExtKeyUsageMicrosoftOemWhqlCrypto = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 7}
|
||||
oidExtKeyUsageMicrosoftSmartcardLogon = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 20, 2, 2}
|
||||
oidExtKeyUsageEmailProtection = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 4}
|
||||
oidExtKeyUsageMicrosoftServerGatedCrypto = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 3}
|
||||
oidExtKeyUsageMicrosoftCaExchange = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 21, 5}
|
||||
oidExtKeyUsageDvcs = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 10}
|
||||
oidExtKeyUsageAppleIchatSigning = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 4, 2}
|
||||
oidExtKeyUsageAppleIchatEncryption = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 4, 3}
|
||||
oidExtKeyUsageAppleCodeSigning = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 4, 1}
|
||||
oidExtKeyUsageAppleCryptoEnv = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 4, 5}
|
||||
oidExtKeyUsageMicrosoftSystemHealthLoophole = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 47, 1, 3}
|
||||
oidExtKeyUsageAny = asn1.ObjectIdentifier{2, 5, 29, 37, 0}
|
||||
oidExtKeyUsageAppleCryptoMaintenanceEnv = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 4, 5, 2}
|
||||
oidExtKeyUsageIpsecTunnel = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 6}
|
||||
oidExtKeyUsageMicrosoftLifetimeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 13}
|
||||
oidExtKeyUsageMicrosoftCspSignature = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 16}
|
||||
oidExtKeyUsageMicrosoftSgcSerialized = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 3, 1}
|
||||
oidExtKeyUsageSbgpCertAaServiceAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 11}
|
||||
oidExtKeyUsageEapOverLan = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 14}
|
||||
oidExtKeyUsageAppleSystemIdentity = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 4, 4}
|
||||
oidExtKeyUsageMicrosoftKeyRecovery3 = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 11}
|
||||
oidExtKeyUsageMicrosoftCertTrustListSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 1}
|
||||
oidExtKeyUsageMicrosoftDrm = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 5, 1}
|
||||
oidExtKeyUsageMicrosoftLicenses = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 5, 3}
|
||||
oidExtKeyUsageMicrosoftLicenseServer = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 5, 4}
|
||||
oidExtKeyUsageMicrosoftEnrollmentAgent = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 20, 2, 1}
|
||||
oidExtKeyUsageAppleSoftwareUpdateSigning = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 4, 1, 2}
|
||||
oidExtKeyUsageAppleCryptoTestEnv = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 4, 5, 3}
|
||||
oidExtKeyUsageMicrosoftEmbeddedNtCrypto = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 8}
|
||||
oidExtKeyUsageMicrosoftDrmIndividualization = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 5, 2}
|
||||
oidExtKeyUsageMicrosoftKeyRecovery21 = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 21, 6}
|
||||
oidExtKeyUsageEapOverPpp = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 13}
|
||||
oidExtKeyUsageOcspSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 9}
|
||||
oidExtKeyUsageAppleCryptoProductionEnv = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 4, 5, 1}
|
||||
oidExtKeyUsageAppleCryptoTier1Qos = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 4, 6, 2}
|
||||
oidExtKeyUsageClientAuth = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 2}
|
||||
oidExtKeyUsageAppleCryptoDevelopmentEnv = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 4, 5, 4}
|
||||
oidExtKeyUsageAppleCryptoTier0Qos = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 4, 6, 1}
|
||||
oidExtKeyUsageAppleCryptoTier2Qos = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 4, 6, 3}
|
||||
oidExtKeyUsageMicrosoftMobileDeviceSoftware = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 14}
|
||||
oidExtKeyUsageMicrosoftNt5Crypto = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 6}
|
||||
oidExtKeyUsageCodeSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 3, 3}
|
||||
oidExtKeyUsageAppleCodeSigningThirdParty = asn1.ObjectIdentifier{1, 2, 840, 113635, 100, 4, 1, 3}
|
||||
oidExtKeyUsageMicrosoftTimestampSigning = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 311, 10, 3, 2}
|
||||
)
|
||||
const (
|
||||
ExtKeyUsageAppleResourceSigning ExtKeyUsage = iota
|
||||
ExtKeyUsageMicrosoftOemWhqlCrypto
|
||||
ExtKeyUsageMicrosoftSmartcardLogon
|
||||
ExtKeyUsageEmailProtection
|
||||
ExtKeyUsageMicrosoftServerGatedCrypto
|
||||
ExtKeyUsageMicrosoftCaExchange
|
||||
ExtKeyUsageDvcs
|
||||
ExtKeyUsageAppleIchatSigning
|
||||
ExtKeyUsageAppleIchatEncryption
|
||||
ExtKeyUsageAppleCodeSigning
|
||||
ExtKeyUsageAppleCryptoEnv
|
||||
ExtKeyUsageMicrosoftSystemHealthLoophole
|
||||
ExtKeyUsageAny
|
||||
ExtKeyUsageAppleCryptoMaintenanceEnv
|
||||
ExtKeyUsageIpsecTunnel
|
||||
ExtKeyUsageMicrosoftLifetimeSigning
|
||||
ExtKeyUsageMicrosoftCspSignature
|
||||
ExtKeyUsageMicrosoftSgcSerialized
|
||||
ExtKeyUsageSbgpCertAaServiceAuth
|
||||
ExtKeyUsageEapOverLan
|
||||
ExtKeyUsageMicrosoftEnrollmentAgent
|
||||
ExtKeyUsageAppleSystemIdentity
|
||||
ExtKeyUsageMicrosoftKeyRecovery3
|
||||
ExtKeyUsageMicrosoftCertTrustListSigning
|
||||
ExtKeyUsageMicrosoftDrm
|
||||
ExtKeyUsageMicrosoftLicenses
|
||||
ExtKeyUsageMicrosoftLicenseServer
|
||||
ExtKeyUsageOcspSigning
|
||||
ExtKeyUsageAppleSoftwareUpdateSigning
|
||||
ExtKeyUsageAppleCryptoTestEnv
|
||||
ExtKeyUsageMicrosoftEmbeddedNtCrypto
|
||||
ExtKeyUsageMicrosoftDrmIndividualization
|
||||
ExtKeyUsageMicrosoftKeyRecovery21
|
||||
ExtKeyUsageEapOverPpp
|
||||
ExtKeyUsageAppleCryptoProductionEnv
|
||||
ExtKeyUsageAppleCryptoTier1Qos
|
||||
ExtKeyUsageClientAuth
|
||||
ExtKeyUsageAppleCryptoDevelopmentEnv
|
||||
ExtKeyUsageAppleCryptoTier0Qos
|
||||
ExtKeyUsageAppleCryptoTier2Qos
|
||||
ExtKeyUsageMicrosoftMobileDeviceSoftware
|
||||
ExtKeyUsageMicrosoftNt5Crypto
|
||||
ExtKeyUsageCodeSigning
|
||||
ExtKeyUsageAppleCodeSigningThirdParty
|
||||
ExtKeyUsageMicrosoftTimestampSigning
|
||||
ExtKeyUsageMicrosoftSystemHealth
|
||||
ExtKeyUsageTimeStamping
|
||||
ExtKeyUsageAppleCodeSigningDevelopment
|
||||
ExtKeyUsageAppleCryptoQos
|
||||
ExtKeyUsageMicrosoftDocumentSigning
|
||||
ExtKeyUsageMicrosoftEncryptedFileSystem
|
||||
ExtKeyUsageMicrosoftWhqlCrypto
|
||||
ExtKeyUsageMicrosoftRootListSigner
|
||||
ExtKeyUsageNetscapeServerGatedCrypto
|
||||
ExtKeyUsageAppleCryptoTier3Qos
|
||||
ExtKeyUsageMicrosoftSmartDisplay
|
||||
ExtKeyUsageMicrosoftEfsRecovery
|
||||
ExtKeyUsageMicrosoftKernelModeCodeSigning
|
||||
ExtKeyUsageServerAuth
|
||||
ExtKeyUsageIpsecEndSystem
|
||||
ExtKeyUsageIpsecUser
|
||||
ExtKeyUsageMicrosoftQualifiedSubordinate
|
||||
)
|
||||
type auxExtendedKeyUsage struct {
|
||||
EapOverPpp bool `json:"eap_over_ppp,omitempty" oid:"1.3.6.1.5.5.7.3.13"`
|
||||
OcspSigning bool `json:"ocsp_signing,omitempty" oid:"1.3.6.1.5.5.7.3.9"`
|
||||
AppleSoftwareUpdateSigning bool `json:"apple_software_update_signing,omitempty" oid:"1.2.840.113635.100.4.1.2"`
|
||||
AppleCryptoTestEnv bool `json:"apple_crypto_test_env,omitempty" oid:"1.2.840.113635.100.4.5.3"`
|
||||
MicrosoftEmbeddedNtCrypto bool `json:"microsoft_embedded_nt_crypto,omitempty" oid:"1.3.6.1.4.1.311.10.3.8"`
|
||||
MicrosoftDrmIndividualization bool `json:"microsoft_drm_individualization,omitempty" oid:"1.3.6.1.4.1.311.10.5.2"`
|
||||
MicrosoftKeyRecovery21 bool `json:"microsoft_key_recovery_21,omitempty" oid:"1.3.6.1.4.1.311.21.6"`
|
||||
AppleCryptoProductionEnv bool `json:"apple_crypto_production_env,omitempty" oid:"1.2.840.113635.100.4.5.1"`
|
||||
AppleCryptoTier1Qos bool `json:"apple_crypto_tier1_qos,omitempty" oid:"1.2.840.113635.100.4.6.2"`
|
||||
ClientAuth bool `json:"client_auth,omitempty" oid:"1.3.6.1.5.5.7.3.2"`
|
||||
CodeSigning bool `json:"code_signing,omitempty" oid:"1.3.6.1.5.5.7.3.3"`
|
||||
AppleCryptoDevelopmentEnv bool `json:"apple_crypto_development_env,omitempty" oid:"1.2.840.113635.100.4.5.4"`
|
||||
AppleCryptoTier0Qos bool `json:"apple_crypto_tier0_qos,omitempty" oid:"1.2.840.113635.100.4.6.1"`
|
||||
AppleCryptoTier2Qos bool `json:"apple_crypto_tier2_qos,omitempty" oid:"1.2.840.113635.100.4.6.3"`
|
||||
MicrosoftMobileDeviceSoftware bool `json:"microsoft_mobile_device_software,omitempty" oid:"1.3.6.1.4.1.311.10.3.14"`
|
||||
MicrosoftNt5Crypto bool `json:"microsoft_nt5_crypto,omitempty" oid:"1.3.6.1.4.1.311.10.3.6"`
|
||||
AppleCodeSigningThirdParty bool `json:"apple_code_signing_third_party,omitempty" oid:"1.2.840.113635.100.4.1.3"`
|
||||
MicrosoftTimestampSigning bool `json:"microsoft_timestamp_signing,omitempty" oid:"1.3.6.1.4.1.311.10.3.2"`
|
||||
MicrosoftRootListSigner bool `json:"microsoft_root_list_signer,omitempty" oid:"1.3.6.1.4.1.311.10.3.9"`
|
||||
MicrosoftSystemHealth bool `json:"microsoft_system_health,omitempty" oid:"1.3.6.1.4.1.311.47.1.1"`
|
||||
TimeStamping bool `json:"time_stamping,omitempty" oid:"1.3.6.1.5.5.7.3.8"`
|
||||
AppleCodeSigningDevelopment bool `json:"apple_code_signing_development,omitempty" oid:"1.2.840.113635.100.4.1.1"`
|
||||
AppleCryptoQos bool `json:"apple_crypto_qos,omitempty" oid:"1.2.840.113635.100.4.6"`
|
||||
MicrosoftDocumentSigning bool `json:"microsoft_document_signing,omitempty" oid:"1.3.6.1.4.1.311.10.3.12"`
|
||||
MicrosoftEncryptedFileSystem bool `json:"microsoft_encrypted_file_system,omitempty" oid:"1.3.6.1.4.1.311.10.3.4"`
|
||||
MicrosoftWhqlCrypto bool `json:"microsoft_whql_crypto,omitempty" oid:"1.3.6.1.4.1.311.10.3.5"`
|
||||
NetscapeServerGatedCrypto bool `json:"netscape_server_gated_crypto,omitempty" oid:"2.16.840.1.113730.4.1"`
|
||||
AppleCryptoTier3Qos bool `json:"apple_crypto_tier3_qos,omitempty" oid:"1.2.840.113635.100.4.6.4"`
|
||||
MicrosoftSmartDisplay bool `json:"microsoft_smart_display,omitempty" oid:"1.3.6.1.4.1.311.10.3.15"`
|
||||
MicrosoftEfsRecovery bool `json:"microsoft_efs_recovery,omitempty" oid:"1.3.6.1.4.1.311.10.3.4.1"`
|
||||
MicrosoftKernelModeCodeSigning bool `json:"microsoft_kernel_mode_code_signing,omitempty" oid:"1.3.6.1.4.1.311.61.1.1"`
|
||||
ServerAuth bool `json:"server_auth,omitempty" oid:"1.3.6.1.5.5.7.3.1"`
|
||||
IpsecEndSystem bool `json:"ipsec_end_system,omitempty" oid:"1.3.6.1.5.5.7.3.5"`
|
||||
IpsecUser bool `json:"ipsec_user,omitempty" oid:"1.3.6.1.5.5.7.3.7"`
|
||||
MicrosoftQualifiedSubordinate bool `json:"microsoft_qualified_subordinate,omitempty" oid:"1.3.6.1.4.1.311.10.3.10"`
|
||||
AppleResourceSigning bool `json:"apple_resource_signing,omitempty" oid:"1.2.840.113635.100.4.1.4"`
|
||||
MicrosoftOemWhqlCrypto bool `json:"microsoft_oem_whql_crypto,omitempty" oid:"1.3.6.1.4.1.311.10.3.7"`
|
||||
MicrosoftSmartcardLogon bool `json:"microsoft_smartcard_logon,omitempty" oid:"1.3.6.1.4.1.311.20.2.2"`
|
||||
EmailProtection bool `json:"email_protection,omitempty" oid:"1.3.6.1.5.5.7.3.4"`
|
||||
MicrosoftServerGatedCrypto bool `json:"microsoft_server_gated_crypto,omitempty" oid:"1.3.6.1.4.1.311.10.3.3"`
|
||||
MicrosoftCaExchange bool `json:"microsoft_ca_exchange,omitempty" oid:"1.3.6.1.4.1.311.21.5"`
|
||||
Dvcs bool `json:"dvcs,omitempty" oid:"1.3.6.1.5.5.7.3.10"`
|
||||
AppleIchatSigning bool `json:"apple_ichat_signing,omitempty" oid:"1.2.840.113635.100.4.2"`
|
||||
AppleIchatEncryption bool `json:"apple_ichat_encryption,omitempty" oid:"1.2.840.113635.100.4.3"`
|
||||
AppleCodeSigning bool `json:"apple_code_signing,omitempty" oid:"1.2.840.113635.100.4.1"`
|
||||
AppleCryptoEnv bool `json:"apple_crypto_env,omitempty" oid:"1.2.840.113635.100.4.5"`
|
||||
MicrosoftSystemHealthLoophole bool `json:"microsoft_system_health_loophole,omitempty" oid:"1.3.6.1.4.1.311.47.1.3"`
|
||||
Any bool `json:"any,omitempty" oid:"2.5.29.37.0"`
|
||||
AppleCryptoMaintenanceEnv bool `json:"apple_crypto_maintenance_env,omitempty" oid:"1.2.840.113635.100.4.5.2"`
|
||||
IpsecTunnel bool `json:"ipsec_tunnel,omitempty" oid:"1.3.6.1.5.5.7.3.6"`
|
||||
MicrosoftLifetimeSigning bool `json:"microsoft_lifetime_signing,omitempty" oid:"1.3.6.1.4.1.311.10.3.13"`
|
||||
MicrosoftCspSignature bool `json:"microsoft_csp_signature,omitempty" oid:"1.3.6.1.4.1.311.10.3.16"`
|
||||
MicrosoftSgcSerialized bool `json:"microsoft_sgc_serialized,omitempty" oid:"1.3.6.1.4.1.311.10.3.3.1"`
|
||||
SbgpCertAaServiceAuth bool `json:"sbgp_cert_aa_service_auth,omitempty" oid:"1.3.6.1.5.5.7.3.11"`
|
||||
EapOverLan bool `json:"eap_over_lan,omitempty" oid:"1.3.6.1.5.5.7.3.14"`
|
||||
MicrosoftLicenseServer bool `json:"microsoft_license_server,omitempty" oid:"1.3.6.1.4.1.311.10.5.4"`
|
||||
MicrosoftEnrollmentAgent bool `json:"microsoft_enrollment_agent,omitempty" oid:"1.3.6.1.4.1.311.20.2.1"`
|
||||
AppleSystemIdentity bool `json:"apple_system_identity,omitempty" oid:"1.2.840.113635.100.4.4"`
|
||||
MicrosoftKeyRecovery3 bool `json:"microsoft_key_recovery_3,omitempty" oid:"1.3.6.1.4.1.311.10.3.11"`
|
||||
MicrosoftCertTrustListSigning bool `json:"microsoft_cert_trust_list_signing,omitempty" oid:"1.3.6.1.4.1.311.10.3.1"`
|
||||
MicrosoftDrm bool `json:"microsoft_drm,omitempty" oid:"1.3.6.1.4.1.311.10.5.1"`
|
||||
MicrosoftLicenses bool `json:"microsoft_licenses,omitempty" oid:"1.3.6.1.4.1.311.10.5.3"`
|
||||
Unknown []string `json:"unknown,omitempty"`}
|
||||
|
||||
func (aux *auxExtendedKeyUsage) populateFromASN1(oid asn1.ObjectIdentifier) {
|
||||
s := oid.String()
|
||||
switch s {
|
||||
case OID_EKU_MICROSOFT_EMBEDDED_NT_CRYPTO:
|
||||
aux.MicrosoftEmbeddedNtCrypto = true
|
||||
case OID_EKU_MICROSOFT_DRM_INDIVIDUALIZATION:
|
||||
aux.MicrosoftDrmIndividualization = true
|
||||
case OID_EKU_MICROSOFT_KEY_RECOVERY_21:
|
||||
aux.MicrosoftKeyRecovery21 = true
|
||||
case OID_EKU_EAP_OVER_PPP:
|
||||
aux.EapOverPpp = true
|
||||
case OID_EKU_OCSP_SIGNING:
|
||||
aux.OcspSigning = true
|
||||
case OID_EKU_APPLE_SOFTWARE_UPDATE_SIGNING:
|
||||
aux.AppleSoftwareUpdateSigning = true
|
||||
case OID_EKU_APPLE_CRYPTO_TEST_ENV:
|
||||
aux.AppleCryptoTestEnv = true
|
||||
case OID_EKU_CLIENT_AUTH:
|
||||
aux.ClientAuth = true
|
||||
case OID_EKU_APPLE_CRYPTO_PRODUCTION_ENV:
|
||||
aux.AppleCryptoProductionEnv = true
|
||||
case OID_EKU_APPLE_CRYPTO_TIER1_QOS:
|
||||
aux.AppleCryptoTier1Qos = true
|
||||
case OID_EKU_APPLE_CRYPTO_TIER2_QOS:
|
||||
aux.AppleCryptoTier2Qos = true
|
||||
case OID_EKU_MICROSOFT_MOBILE_DEVICE_SOFTWARE:
|
||||
aux.MicrosoftMobileDeviceSoftware = true
|
||||
case OID_EKU_MICROSOFT_NT5_CRYPTO:
|
||||
aux.MicrosoftNt5Crypto = true
|
||||
case OID_EKU_CODE_SIGNING:
|
||||
aux.CodeSigning = true
|
||||
case OID_EKU_APPLE_CRYPTO_DEVELOPMENT_ENV:
|
||||
aux.AppleCryptoDevelopmentEnv = true
|
||||
case OID_EKU_APPLE_CRYPTO_TIER0_QOS:
|
||||
aux.AppleCryptoTier0Qos = true
|
||||
case OID_EKU_APPLE_CODE_SIGNING_THIRD_PARTY:
|
||||
aux.AppleCodeSigningThirdParty = true
|
||||
case OID_EKU_MICROSOFT_TIMESTAMP_SIGNING:
|
||||
aux.MicrosoftTimestampSigning = true
|
||||
case OID_EKU_MICROSOFT_DOCUMENT_SIGNING:
|
||||
aux.MicrosoftDocumentSigning = true
|
||||
case OID_EKU_MICROSOFT_ENCRYPTED_FILE_SYSTEM:
|
||||
aux.MicrosoftEncryptedFileSystem = true
|
||||
case OID_EKU_MICROSOFT_WHQL_CRYPTO:
|
||||
aux.MicrosoftWhqlCrypto = true
|
||||
case OID_EKU_MICROSOFT_ROOT_LIST_SIGNER:
|
||||
aux.MicrosoftRootListSigner = true
|
||||
case OID_EKU_MICROSOFT_SYSTEM_HEALTH:
|
||||
aux.MicrosoftSystemHealth = true
|
||||
case OID_EKU_TIME_STAMPING:
|
||||
aux.TimeStamping = true
|
||||
case OID_EKU_APPLE_CODE_SIGNING_DEVELOPMENT:
|
||||
aux.AppleCodeSigningDevelopment = true
|
||||
case OID_EKU_APPLE_CRYPTO_QOS:
|
||||
aux.AppleCryptoQos = true
|
||||
case OID_EKU_NETSCAPE_SERVER_GATED_CRYPTO:
|
||||
aux.NetscapeServerGatedCrypto = true
|
||||
case OID_EKU_APPLE_CRYPTO_TIER3_QOS:
|
||||
aux.AppleCryptoTier3Qos = true
|
||||
case OID_EKU_MICROSOFT_SMART_DISPLAY:
|
||||
aux.MicrosoftSmartDisplay = true
|
||||
case OID_EKU_SERVER_AUTH:
|
||||
aux.ServerAuth = true
|
||||
case OID_EKU_IPSEC_END_SYSTEM:
|
||||
aux.IpsecEndSystem = true
|
||||
case OID_EKU_IPSEC_USER:
|
||||
aux.IpsecUser = true
|
||||
case OID_EKU_MICROSOFT_EFS_RECOVERY:
|
||||
aux.MicrosoftEfsRecovery = true
|
||||
case OID_EKU_MICROSOFT_KERNEL_MODE_CODE_SIGNING:
|
||||
aux.MicrosoftKernelModeCodeSigning = true
|
||||
case OID_EKU_MICROSOFT_QUALIFIED_SUBORDINATE:
|
||||
aux.MicrosoftQualifiedSubordinate = true
|
||||
case OID_EKU_MICROSOFT_SMARTCARD_LOGON:
|
||||
aux.MicrosoftSmartcardLogon = true
|
||||
case OID_EKU_EMAIL_PROTECTION:
|
||||
aux.EmailProtection = true
|
||||
case OID_EKU_APPLE_RESOURCE_SIGNING:
|
||||
aux.AppleResourceSigning = true
|
||||
case OID_EKU_MICROSOFT_OEM_WHQL_CRYPTO:
|
||||
aux.MicrosoftOemWhqlCrypto = true
|
||||
case OID_EKU_DVCS:
|
||||
aux.Dvcs = true
|
||||
case OID_EKU_MICROSOFT_SERVER_GATED_CRYPTO:
|
||||
aux.MicrosoftServerGatedCrypto = true
|
||||
case OID_EKU_MICROSOFT_CA_EXCHANGE:
|
||||
aux.MicrosoftCaExchange = true
|
||||
case OID_EKU_APPLE_ICHAT_SIGNING:
|
||||
aux.AppleIchatSigning = true
|
||||
case OID_EKU_APPLE_ICHAT_ENCRYPTION:
|
||||
aux.AppleIchatEncryption = true
|
||||
case OID_EKU_MICROSOFT_SYSTEM_HEALTH_LOOPHOLE:
|
||||
aux.MicrosoftSystemHealthLoophole = true
|
||||
case OID_EKU_ANY:
|
||||
aux.Any = true
|
||||
case OID_EKU_APPLE_CODE_SIGNING:
|
||||
aux.AppleCodeSigning = true
|
||||
case OID_EKU_APPLE_CRYPTO_ENV:
|
||||
aux.AppleCryptoEnv = true
|
||||
case OID_EKU_APPLE_CRYPTO_MAINTENANCE_ENV:
|
||||
aux.AppleCryptoMaintenanceEnv = true
|
||||
case OID_EKU_IPSEC_TUNNEL:
|
||||
aux.IpsecTunnel = true
|
||||
case OID_EKU_MICROSOFT_SGC_SERIALIZED:
|
||||
aux.MicrosoftSgcSerialized = true
|
||||
case OID_EKU_SBGP_CERT_AA_SERVICE_AUTH:
|
||||
aux.SbgpCertAaServiceAuth = true
|
||||
case OID_EKU_EAP_OVER_LAN:
|
||||
aux.EapOverLan = true
|
||||
case OID_EKU_MICROSOFT_LIFETIME_SIGNING:
|
||||
aux.MicrosoftLifetimeSigning = true
|
||||
case OID_EKU_MICROSOFT_CSP_SIGNATURE:
|
||||
aux.MicrosoftCspSignature = true
|
||||
case OID_EKU_MICROSOFT_CERT_TRUST_LIST_SIGNING:
|
||||
aux.MicrosoftCertTrustListSigning = true
|
||||
case OID_EKU_MICROSOFT_DRM:
|
||||
aux.MicrosoftDrm = true
|
||||
case OID_EKU_MICROSOFT_LICENSES:
|
||||
aux.MicrosoftLicenses = true
|
||||
case OID_EKU_MICROSOFT_LICENSE_SERVER:
|
||||
aux.MicrosoftLicenseServer = true
|
||||
case OID_EKU_MICROSOFT_ENROLLMENT_AGENT:
|
||||
aux.MicrosoftEnrollmentAgent = true
|
||||
case OID_EKU_APPLE_SYSTEM_IDENTITY:
|
||||
aux.AppleSystemIdentity = true
|
||||
case OID_EKU_MICROSOFT_KEY_RECOVERY_3:
|
||||
aux.MicrosoftKeyRecovery3 = true
|
||||
default:
|
||||
}
|
||||
return}
|
||||
|
||||
func (aux *auxExtendedKeyUsage) populateFromExtKeyUsage(eku ExtKeyUsage) {
|
||||
switch eku {
|
||||
case ExtKeyUsageAppleCryptoProductionEnv:
|
||||
aux.AppleCryptoProductionEnv = true
|
||||
case ExtKeyUsageAppleCryptoTier1Qos:
|
||||
aux.AppleCryptoTier1Qos = true
|
||||
case ExtKeyUsageClientAuth:
|
||||
aux.ClientAuth = true
|
||||
case ExtKeyUsageMicrosoftMobileDeviceSoftware:
|
||||
aux.MicrosoftMobileDeviceSoftware = true
|
||||
case ExtKeyUsageMicrosoftNt5Crypto:
|
||||
aux.MicrosoftNt5Crypto = true
|
||||
case ExtKeyUsageCodeSigning:
|
||||
aux.CodeSigning = true
|
||||
case ExtKeyUsageAppleCryptoDevelopmentEnv:
|
||||
aux.AppleCryptoDevelopmentEnv = true
|
||||
case ExtKeyUsageAppleCryptoTier0Qos:
|
||||
aux.AppleCryptoTier0Qos = true
|
||||
case ExtKeyUsageAppleCryptoTier2Qos:
|
||||
aux.AppleCryptoTier2Qos = true
|
||||
case ExtKeyUsageAppleCodeSigningThirdParty:
|
||||
aux.AppleCodeSigningThirdParty = true
|
||||
case ExtKeyUsageMicrosoftTimestampSigning:
|
||||
aux.MicrosoftTimestampSigning = true
|
||||
case ExtKeyUsageMicrosoftEncryptedFileSystem:
|
||||
aux.MicrosoftEncryptedFileSystem = true
|
||||
case ExtKeyUsageMicrosoftWhqlCrypto:
|
||||
aux.MicrosoftWhqlCrypto = true
|
||||
case ExtKeyUsageMicrosoftRootListSigner:
|
||||
aux.MicrosoftRootListSigner = true
|
||||
case ExtKeyUsageMicrosoftSystemHealth:
|
||||
aux.MicrosoftSystemHealth = true
|
||||
case ExtKeyUsageTimeStamping:
|
||||
aux.TimeStamping = true
|
||||
case ExtKeyUsageAppleCodeSigningDevelopment:
|
||||
aux.AppleCodeSigningDevelopment = true
|
||||
case ExtKeyUsageAppleCryptoQos:
|
||||
aux.AppleCryptoQos = true
|
||||
case ExtKeyUsageMicrosoftDocumentSigning:
|
||||
aux.MicrosoftDocumentSigning = true
|
||||
case ExtKeyUsageNetscapeServerGatedCrypto:
|
||||
aux.NetscapeServerGatedCrypto = true
|
||||
case ExtKeyUsageAppleCryptoTier3Qos:
|
||||
aux.AppleCryptoTier3Qos = true
|
||||
case ExtKeyUsageMicrosoftSmartDisplay:
|
||||
aux.MicrosoftSmartDisplay = true
|
||||
case ExtKeyUsageIpsecEndSystem:
|
||||
aux.IpsecEndSystem = true
|
||||
case ExtKeyUsageIpsecUser:
|
||||
aux.IpsecUser = true
|
||||
case ExtKeyUsageMicrosoftEfsRecovery:
|
||||
aux.MicrosoftEfsRecovery = true
|
||||
case ExtKeyUsageMicrosoftKernelModeCodeSigning:
|
||||
aux.MicrosoftKernelModeCodeSigning = true
|
||||
case ExtKeyUsageServerAuth:
|
||||
aux.ServerAuth = true
|
||||
case ExtKeyUsageMicrosoftQualifiedSubordinate:
|
||||
aux.MicrosoftQualifiedSubordinate = true
|
||||
case ExtKeyUsageEmailProtection:
|
||||
aux.EmailProtection = true
|
||||
case ExtKeyUsageAppleResourceSigning:
|
||||
aux.AppleResourceSigning = true
|
||||
case ExtKeyUsageMicrosoftOemWhqlCrypto:
|
||||
aux.MicrosoftOemWhqlCrypto = true
|
||||
case ExtKeyUsageMicrosoftSmartcardLogon:
|
||||
aux.MicrosoftSmartcardLogon = true
|
||||
case ExtKeyUsageMicrosoftServerGatedCrypto:
|
||||
aux.MicrosoftServerGatedCrypto = true
|
||||
case ExtKeyUsageMicrosoftCaExchange:
|
||||
aux.MicrosoftCaExchange = true
|
||||
case ExtKeyUsageDvcs:
|
||||
aux.Dvcs = true
|
||||
case ExtKeyUsageAppleIchatSigning:
|
||||
aux.AppleIchatSigning = true
|
||||
case ExtKeyUsageAppleIchatEncryption:
|
||||
aux.AppleIchatEncryption = true
|
||||
case ExtKeyUsageAny:
|
||||
aux.Any = true
|
||||
case ExtKeyUsageAppleCodeSigning:
|
||||
aux.AppleCodeSigning = true
|
||||
case ExtKeyUsageAppleCryptoEnv:
|
||||
aux.AppleCryptoEnv = true
|
||||
case ExtKeyUsageMicrosoftSystemHealthLoophole:
|
||||
aux.MicrosoftSystemHealthLoophole = true
|
||||
case ExtKeyUsageAppleCryptoMaintenanceEnv:
|
||||
aux.AppleCryptoMaintenanceEnv = true
|
||||
case ExtKeyUsageIpsecTunnel:
|
||||
aux.IpsecTunnel = true
|
||||
case ExtKeyUsageSbgpCertAaServiceAuth:
|
||||
aux.SbgpCertAaServiceAuth = true
|
||||
case ExtKeyUsageEapOverLan:
|
||||
aux.EapOverLan = true
|
||||
case ExtKeyUsageMicrosoftLifetimeSigning:
|
||||
aux.MicrosoftLifetimeSigning = true
|
||||
case ExtKeyUsageMicrosoftCspSignature:
|
||||
aux.MicrosoftCspSignature = true
|
||||
case ExtKeyUsageMicrosoftSgcSerialized:
|
||||
aux.MicrosoftSgcSerialized = true
|
||||
case ExtKeyUsageMicrosoftDrm:
|
||||
aux.MicrosoftDrm = true
|
||||
case ExtKeyUsageMicrosoftLicenses:
|
||||
aux.MicrosoftLicenses = true
|
||||
case ExtKeyUsageMicrosoftLicenseServer:
|
||||
aux.MicrosoftLicenseServer = true
|
||||
case ExtKeyUsageMicrosoftEnrollmentAgent:
|
||||
aux.MicrosoftEnrollmentAgent = true
|
||||
case ExtKeyUsageAppleSystemIdentity:
|
||||
aux.AppleSystemIdentity = true
|
||||
case ExtKeyUsageMicrosoftKeyRecovery3:
|
||||
aux.MicrosoftKeyRecovery3 = true
|
||||
case ExtKeyUsageMicrosoftCertTrustListSigning:
|
||||
aux.MicrosoftCertTrustListSigning = true
|
||||
case ExtKeyUsageMicrosoftDrmIndividualization:
|
||||
aux.MicrosoftDrmIndividualization = true
|
||||
case ExtKeyUsageMicrosoftKeyRecovery21:
|
||||
aux.MicrosoftKeyRecovery21 = true
|
||||
case ExtKeyUsageEapOverPpp:
|
||||
aux.EapOverPpp = true
|
||||
case ExtKeyUsageOcspSigning:
|
||||
aux.OcspSigning = true
|
||||
case ExtKeyUsageAppleSoftwareUpdateSigning:
|
||||
aux.AppleSoftwareUpdateSigning = true
|
||||
case ExtKeyUsageAppleCryptoTestEnv:
|
||||
aux.AppleCryptoTestEnv = true
|
||||
case ExtKeyUsageMicrosoftEmbeddedNtCrypto:
|
||||
aux.MicrosoftEmbeddedNtCrypto = true
|
||||
default:
|
||||
}
|
||||
return}
|
||||
|
||||
|
||||
var ekuOIDs map[string]asn1.ObjectIdentifier
|
||||
|
||||
|
||||
var ekuConstants map[string]ExtKeyUsage
|
||||
|
||||
func init() {
|
||||
ekuOIDs = make(map[string]asn1.ObjectIdentifier)
|
||||
ekuOIDs[OID_EKU_APPLE_CRYPTO_DEVELOPMENT_ENV] = oidExtKeyUsageAppleCryptoDevelopmentEnv
|
||||
ekuOIDs[OID_EKU_APPLE_CRYPTO_TIER0_QOS] = oidExtKeyUsageAppleCryptoTier0Qos
|
||||
ekuOIDs[OID_EKU_APPLE_CRYPTO_TIER2_QOS] = oidExtKeyUsageAppleCryptoTier2Qos
|
||||
ekuOIDs[OID_EKU_MICROSOFT_MOBILE_DEVICE_SOFTWARE] = oidExtKeyUsageMicrosoftMobileDeviceSoftware
|
||||
ekuOIDs[OID_EKU_MICROSOFT_NT5_CRYPTO] = oidExtKeyUsageMicrosoftNt5Crypto
|
||||
ekuOIDs[OID_EKU_CODE_SIGNING] = oidExtKeyUsageCodeSigning
|
||||
ekuOIDs[OID_EKU_APPLE_CODE_SIGNING_THIRD_PARTY] = oidExtKeyUsageAppleCodeSigningThirdParty
|
||||
ekuOIDs[OID_EKU_MICROSOFT_TIMESTAMP_SIGNING] = oidExtKeyUsageMicrosoftTimestampSigning
|
||||
ekuOIDs[OID_EKU_MICROSOFT_SYSTEM_HEALTH] = oidExtKeyUsageMicrosoftSystemHealth
|
||||
ekuOIDs[OID_EKU_TIME_STAMPING] = oidExtKeyUsageTimeStamping
|
||||
ekuOIDs[OID_EKU_APPLE_CODE_SIGNING_DEVELOPMENT] = oidExtKeyUsageAppleCodeSigningDevelopment
|
||||
ekuOIDs[OID_EKU_APPLE_CRYPTO_QOS] = oidExtKeyUsageAppleCryptoQos
|
||||
ekuOIDs[OID_EKU_MICROSOFT_DOCUMENT_SIGNING] = oidExtKeyUsageMicrosoftDocumentSigning
|
||||
ekuOIDs[OID_EKU_MICROSOFT_ENCRYPTED_FILE_SYSTEM] = oidExtKeyUsageMicrosoftEncryptedFileSystem
|
||||
ekuOIDs[OID_EKU_MICROSOFT_WHQL_CRYPTO] = oidExtKeyUsageMicrosoftWhqlCrypto
|
||||
ekuOIDs[OID_EKU_MICROSOFT_ROOT_LIST_SIGNER] = oidExtKeyUsageMicrosoftRootListSigner
|
||||
ekuOIDs[OID_EKU_NETSCAPE_SERVER_GATED_CRYPTO] = oidExtKeyUsageNetscapeServerGatedCrypto
|
||||
ekuOIDs[OID_EKU_APPLE_CRYPTO_TIER3_QOS] = oidExtKeyUsageAppleCryptoTier3Qos
|
||||
ekuOIDs[OID_EKU_MICROSOFT_SMART_DISPLAY] = oidExtKeyUsageMicrosoftSmartDisplay
|
||||
ekuOIDs[OID_EKU_MICROSOFT_EFS_RECOVERY] = oidExtKeyUsageMicrosoftEfsRecovery
|
||||
ekuOIDs[OID_EKU_MICROSOFT_KERNEL_MODE_CODE_SIGNING] = oidExtKeyUsageMicrosoftKernelModeCodeSigning
|
||||
ekuOIDs[OID_EKU_SERVER_AUTH] = oidExtKeyUsageServerAuth
|
||||
ekuOIDs[OID_EKU_IPSEC_END_SYSTEM] = oidExtKeyUsageIpsecEndSystem
|
||||
ekuOIDs[OID_EKU_IPSEC_USER] = oidExtKeyUsageIpsecUser
|
||||
ekuOIDs[OID_EKU_MICROSOFT_QUALIFIED_SUBORDINATE] = oidExtKeyUsageMicrosoftQualifiedSubordinate
|
||||
ekuOIDs[OID_EKU_APPLE_RESOURCE_SIGNING] = oidExtKeyUsageAppleResourceSigning
|
||||
ekuOIDs[OID_EKU_MICROSOFT_OEM_WHQL_CRYPTO] = oidExtKeyUsageMicrosoftOemWhqlCrypto
|
||||
ekuOIDs[OID_EKU_MICROSOFT_SMARTCARD_LOGON] = oidExtKeyUsageMicrosoftSmartcardLogon
|
||||
ekuOIDs[OID_EKU_EMAIL_PROTECTION] = oidExtKeyUsageEmailProtection
|
||||
ekuOIDs[OID_EKU_MICROSOFT_SERVER_GATED_CRYPTO] = oidExtKeyUsageMicrosoftServerGatedCrypto
|
||||
ekuOIDs[OID_EKU_MICROSOFT_CA_EXCHANGE] = oidExtKeyUsageMicrosoftCaExchange
|
||||
ekuOIDs[OID_EKU_DVCS] = oidExtKeyUsageDvcs
|
||||
ekuOIDs[OID_EKU_APPLE_ICHAT_SIGNING] = oidExtKeyUsageAppleIchatSigning
|
||||
ekuOIDs[OID_EKU_APPLE_ICHAT_ENCRYPTION] = oidExtKeyUsageAppleIchatEncryption
|
||||
ekuOIDs[OID_EKU_APPLE_CODE_SIGNING] = oidExtKeyUsageAppleCodeSigning
|
||||
ekuOIDs[OID_EKU_APPLE_CRYPTO_ENV] = oidExtKeyUsageAppleCryptoEnv
|
||||
ekuOIDs[OID_EKU_MICROSOFT_SYSTEM_HEALTH_LOOPHOLE] = oidExtKeyUsageMicrosoftSystemHealthLoophole
|
||||
ekuOIDs[OID_EKU_ANY] = oidExtKeyUsageAny
|
||||
ekuOIDs[OID_EKU_APPLE_CRYPTO_MAINTENANCE_ENV] = oidExtKeyUsageAppleCryptoMaintenanceEnv
|
||||
ekuOIDs[OID_EKU_IPSEC_TUNNEL] = oidExtKeyUsageIpsecTunnel
|
||||
ekuOIDs[OID_EKU_MICROSOFT_LIFETIME_SIGNING] = oidExtKeyUsageMicrosoftLifetimeSigning
|
||||
ekuOIDs[OID_EKU_MICROSOFT_CSP_SIGNATURE] = oidExtKeyUsageMicrosoftCspSignature
|
||||
ekuOIDs[OID_EKU_MICROSOFT_SGC_SERIALIZED] = oidExtKeyUsageMicrosoftSgcSerialized
|
||||
ekuOIDs[OID_EKU_SBGP_CERT_AA_SERVICE_AUTH] = oidExtKeyUsageSbgpCertAaServiceAuth
|
||||
ekuOIDs[OID_EKU_EAP_OVER_LAN] = oidExtKeyUsageEapOverLan
|
||||
ekuOIDs[OID_EKU_MICROSOFT_ENROLLMENT_AGENT] = oidExtKeyUsageMicrosoftEnrollmentAgent
|
||||
ekuOIDs[OID_EKU_APPLE_SYSTEM_IDENTITY] = oidExtKeyUsageAppleSystemIdentity
|
||||
ekuOIDs[OID_EKU_MICROSOFT_KEY_RECOVERY_3] = oidExtKeyUsageMicrosoftKeyRecovery3
|
||||
ekuOIDs[OID_EKU_MICROSOFT_CERT_TRUST_LIST_SIGNING] = oidExtKeyUsageMicrosoftCertTrustListSigning
|
||||
ekuOIDs[OID_EKU_MICROSOFT_DRM] = oidExtKeyUsageMicrosoftDrm
|
||||
ekuOIDs[OID_EKU_MICROSOFT_LICENSES] = oidExtKeyUsageMicrosoftLicenses
|
||||
ekuOIDs[OID_EKU_MICROSOFT_LICENSE_SERVER] = oidExtKeyUsageMicrosoftLicenseServer
|
||||
ekuOIDs[OID_EKU_OCSP_SIGNING] = oidExtKeyUsageOcspSigning
|
||||
ekuOIDs[OID_EKU_APPLE_SOFTWARE_UPDATE_SIGNING] = oidExtKeyUsageAppleSoftwareUpdateSigning
|
||||
ekuOIDs[OID_EKU_APPLE_CRYPTO_TEST_ENV] = oidExtKeyUsageAppleCryptoTestEnv
|
||||
ekuOIDs[OID_EKU_MICROSOFT_EMBEDDED_NT_CRYPTO] = oidExtKeyUsageMicrosoftEmbeddedNtCrypto
|
||||
ekuOIDs[OID_EKU_MICROSOFT_DRM_INDIVIDUALIZATION] = oidExtKeyUsageMicrosoftDrmIndividualization
|
||||
ekuOIDs[OID_EKU_MICROSOFT_KEY_RECOVERY_21] = oidExtKeyUsageMicrosoftKeyRecovery21
|
||||
ekuOIDs[OID_EKU_EAP_OVER_PPP] = oidExtKeyUsageEapOverPpp
|
||||
ekuOIDs[OID_EKU_APPLE_CRYPTO_PRODUCTION_ENV] = oidExtKeyUsageAppleCryptoProductionEnv
|
||||
ekuOIDs[OID_EKU_APPLE_CRYPTO_TIER1_QOS] = oidExtKeyUsageAppleCryptoTier1Qos
|
||||
ekuOIDs[OID_EKU_CLIENT_AUTH] = oidExtKeyUsageClientAuth
|
||||
|
||||
ekuConstants = make(map[string]ExtKeyUsage)
|
||||
ekuConstants[OID_EKU_APPLE_ICHAT_SIGNING] = ExtKeyUsageAppleIchatSigning
|
||||
ekuConstants[OID_EKU_APPLE_ICHAT_ENCRYPTION] = ExtKeyUsageAppleIchatEncryption
|
||||
ekuConstants[OID_EKU_MICROSOFT_SYSTEM_HEALTH_LOOPHOLE] = ExtKeyUsageMicrosoftSystemHealthLoophole
|
||||
ekuConstants[OID_EKU_ANY] = ExtKeyUsageAny
|
||||
ekuConstants[OID_EKU_APPLE_CODE_SIGNING] = ExtKeyUsageAppleCodeSigning
|
||||
ekuConstants[OID_EKU_APPLE_CRYPTO_ENV] = ExtKeyUsageAppleCryptoEnv
|
||||
ekuConstants[OID_EKU_APPLE_CRYPTO_MAINTENANCE_ENV] = ExtKeyUsageAppleCryptoMaintenanceEnv
|
||||
ekuConstants[OID_EKU_IPSEC_TUNNEL] = ExtKeyUsageIpsecTunnel
|
||||
ekuConstants[OID_EKU_MICROSOFT_SGC_SERIALIZED] = ExtKeyUsageMicrosoftSgcSerialized
|
||||
ekuConstants[OID_EKU_SBGP_CERT_AA_SERVICE_AUTH] = ExtKeyUsageSbgpCertAaServiceAuth
|
||||
ekuConstants[OID_EKU_EAP_OVER_LAN] = ExtKeyUsageEapOverLan
|
||||
ekuConstants[OID_EKU_MICROSOFT_LIFETIME_SIGNING] = ExtKeyUsageMicrosoftLifetimeSigning
|
||||
ekuConstants[OID_EKU_MICROSOFT_CSP_SIGNATURE] = ExtKeyUsageMicrosoftCspSignature
|
||||
ekuConstants[OID_EKU_MICROSOFT_CERT_TRUST_LIST_SIGNING] = ExtKeyUsageMicrosoftCertTrustListSigning
|
||||
ekuConstants[OID_EKU_MICROSOFT_DRM] = ExtKeyUsageMicrosoftDrm
|
||||
ekuConstants[OID_EKU_MICROSOFT_LICENSES] = ExtKeyUsageMicrosoftLicenses
|
||||
ekuConstants[OID_EKU_MICROSOFT_LICENSE_SERVER] = ExtKeyUsageMicrosoftLicenseServer
|
||||
ekuConstants[OID_EKU_MICROSOFT_ENROLLMENT_AGENT] = ExtKeyUsageMicrosoftEnrollmentAgent
|
||||
ekuConstants[OID_EKU_APPLE_SYSTEM_IDENTITY] = ExtKeyUsageAppleSystemIdentity
|
||||
ekuConstants[OID_EKU_MICROSOFT_KEY_RECOVERY_3] = ExtKeyUsageMicrosoftKeyRecovery3
|
||||
ekuConstants[OID_EKU_MICROSOFT_EMBEDDED_NT_CRYPTO] = ExtKeyUsageMicrosoftEmbeddedNtCrypto
|
||||
ekuConstants[OID_EKU_MICROSOFT_DRM_INDIVIDUALIZATION] = ExtKeyUsageMicrosoftDrmIndividualization
|
||||
ekuConstants[OID_EKU_MICROSOFT_KEY_RECOVERY_21] = ExtKeyUsageMicrosoftKeyRecovery21
|
||||
ekuConstants[OID_EKU_EAP_OVER_PPP] = ExtKeyUsageEapOverPpp
|
||||
ekuConstants[OID_EKU_OCSP_SIGNING] = ExtKeyUsageOcspSigning
|
||||
ekuConstants[OID_EKU_APPLE_SOFTWARE_UPDATE_SIGNING] = ExtKeyUsageAppleSoftwareUpdateSigning
|
||||
ekuConstants[OID_EKU_APPLE_CRYPTO_TEST_ENV] = ExtKeyUsageAppleCryptoTestEnv
|
||||
ekuConstants[OID_EKU_CLIENT_AUTH] = ExtKeyUsageClientAuth
|
||||
ekuConstants[OID_EKU_APPLE_CRYPTO_PRODUCTION_ENV] = ExtKeyUsageAppleCryptoProductionEnv
|
||||
ekuConstants[OID_EKU_APPLE_CRYPTO_TIER1_QOS] = ExtKeyUsageAppleCryptoTier1Qos
|
||||
ekuConstants[OID_EKU_APPLE_CRYPTO_TIER2_QOS] = ExtKeyUsageAppleCryptoTier2Qos
|
||||
ekuConstants[OID_EKU_MICROSOFT_MOBILE_DEVICE_SOFTWARE] = ExtKeyUsageMicrosoftMobileDeviceSoftware
|
||||
ekuConstants[OID_EKU_MICROSOFT_NT5_CRYPTO] = ExtKeyUsageMicrosoftNt5Crypto
|
||||
ekuConstants[OID_EKU_CODE_SIGNING] = ExtKeyUsageCodeSigning
|
||||
ekuConstants[OID_EKU_APPLE_CRYPTO_DEVELOPMENT_ENV] = ExtKeyUsageAppleCryptoDevelopmentEnv
|
||||
ekuConstants[OID_EKU_APPLE_CRYPTO_TIER0_QOS] = ExtKeyUsageAppleCryptoTier0Qos
|
||||
ekuConstants[OID_EKU_APPLE_CODE_SIGNING_THIRD_PARTY] = ExtKeyUsageAppleCodeSigningThirdParty
|
||||
ekuConstants[OID_EKU_MICROSOFT_TIMESTAMP_SIGNING] = ExtKeyUsageMicrosoftTimestampSigning
|
||||
ekuConstants[OID_EKU_MICROSOFT_DOCUMENT_SIGNING] = ExtKeyUsageMicrosoftDocumentSigning
|
||||
ekuConstants[OID_EKU_MICROSOFT_ENCRYPTED_FILE_SYSTEM] = ExtKeyUsageMicrosoftEncryptedFileSystem
|
||||
ekuConstants[OID_EKU_MICROSOFT_WHQL_CRYPTO] = ExtKeyUsageMicrosoftWhqlCrypto
|
||||
ekuConstants[OID_EKU_MICROSOFT_ROOT_LIST_SIGNER] = ExtKeyUsageMicrosoftRootListSigner
|
||||
ekuConstants[OID_EKU_MICROSOFT_SYSTEM_HEALTH] = ExtKeyUsageMicrosoftSystemHealth
|
||||
ekuConstants[OID_EKU_TIME_STAMPING] = ExtKeyUsageTimeStamping
|
||||
ekuConstants[OID_EKU_APPLE_CODE_SIGNING_DEVELOPMENT] = ExtKeyUsageAppleCodeSigningDevelopment
|
||||
ekuConstants[OID_EKU_APPLE_CRYPTO_QOS] = ExtKeyUsageAppleCryptoQos
|
||||
ekuConstants[OID_EKU_NETSCAPE_SERVER_GATED_CRYPTO] = ExtKeyUsageNetscapeServerGatedCrypto
|
||||
ekuConstants[OID_EKU_APPLE_CRYPTO_TIER3_QOS] = ExtKeyUsageAppleCryptoTier3Qos
|
||||
ekuConstants[OID_EKU_MICROSOFT_SMART_DISPLAY] = ExtKeyUsageMicrosoftSmartDisplay
|
||||
ekuConstants[OID_EKU_SERVER_AUTH] = ExtKeyUsageServerAuth
|
||||
ekuConstants[OID_EKU_IPSEC_END_SYSTEM] = ExtKeyUsageIpsecEndSystem
|
||||
ekuConstants[OID_EKU_IPSEC_USER] = ExtKeyUsageIpsecUser
|
||||
ekuConstants[OID_EKU_MICROSOFT_EFS_RECOVERY] = ExtKeyUsageMicrosoftEfsRecovery
|
||||
ekuConstants[OID_EKU_MICROSOFT_KERNEL_MODE_CODE_SIGNING] = ExtKeyUsageMicrosoftKernelModeCodeSigning
|
||||
ekuConstants[OID_EKU_MICROSOFT_QUALIFIED_SUBORDINATE] = ExtKeyUsageMicrosoftQualifiedSubordinate
|
||||
ekuConstants[OID_EKU_MICROSOFT_SMARTCARD_LOGON] = ExtKeyUsageMicrosoftSmartcardLogon
|
||||
ekuConstants[OID_EKU_EMAIL_PROTECTION] = ExtKeyUsageEmailProtection
|
||||
ekuConstants[OID_EKU_APPLE_RESOURCE_SIGNING] = ExtKeyUsageAppleResourceSigning
|
||||
ekuConstants[OID_EKU_MICROSOFT_OEM_WHQL_CRYPTO] = ExtKeyUsageMicrosoftOemWhqlCrypto
|
||||
ekuConstants[OID_EKU_DVCS] = ExtKeyUsageDvcs
|
||||
ekuConstants[OID_EKU_MICROSOFT_SERVER_GATED_CRYPTO] = ExtKeyUsageMicrosoftServerGatedCrypto
|
||||
ekuConstants[OID_EKU_MICROSOFT_CA_EXCHANGE] = ExtKeyUsageMicrosoftCaExchange
|
||||
}
|
||||
|
|
@ -0,0 +1,300 @@
|
|||
// The following directive is necessary to make the package coherent:
|
||||
|
||||
// +build ignore
|
||||
|
||||
// This program generates extended_key_usage.go. It can be invoked by running
|
||||
// `$ go generate`
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
COLUMN_IDX_OID = 0
|
||||
COLUMN_IDX_SHORT_NAME = 2
|
||||
)
|
||||
|
||||
const (
|
||||
GO_PREFIX = "oidExtKeyUsage"
|
||||
CONST_PREFIX = "OID_EKU"
|
||||
)
|
||||
|
||||
type OID struct {
|
||||
OID string
|
||||
ShortName string
|
||||
}
|
||||
|
||||
func (o *OID) OIDDecl() string {
|
||||
parts := strings.Split(o.OID, ".")
|
||||
buffer := bytes.Buffer{}
|
||||
buffer.WriteString("asn1.ObjectIdentifier{")
|
||||
for idx, p := range parts {
|
||||
buffer.WriteString(p)
|
||||
if idx != len(parts)-1 {
|
||||
buffer.WriteString(", ")
|
||||
}
|
||||
}
|
||||
buffer.WriteString("}")
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func (o *OID) GoName(prefix string) string {
|
||||
parts := strings.Split(o.ShortName, "-")
|
||||
for idx, p := range parts {
|
||||
if prefix == "" && idx == 0 {
|
||||
continue
|
||||
}
|
||||
parts[idx] = strings.Title(p)
|
||||
}
|
||||
return prefix + strings.Join(parts, "")
|
||||
}
|
||||
|
||||
func (o *OID) GoConstant(prefix string) string {
|
||||
parts := strings.Split(o.ShortName, "-")
|
||||
buffer := bytes.Buffer{}
|
||||
if prefix != "" {
|
||||
buffer.WriteString(strings.ToUpper(prefix))
|
||||
buffer.WriteString("_")
|
||||
}
|
||||
for idx, p := range parts {
|
||||
buffer.WriteString(strings.ToUpper(p))
|
||||
if idx != len(parts)-1 {
|
||||
buffer.WriteString("_")
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func (o *OID) JSONName(prefix string) string {
|
||||
parts := strings.Split(o.ShortName, "-")
|
||||
buffer := bytes.Buffer{}
|
||||
if prefix != "" {
|
||||
buffer.WriteString(strings.ToLower(prefix))
|
||||
buffer.WriteString("_")
|
||||
}
|
||||
for idx, p := range parts {
|
||||
buffer.WriteString(strings.ToLower(p))
|
||||
if idx != len(parts)-1 {
|
||||
buffer.WriteString("_")
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func (o *OID) StructFieldName() string {
|
||||
parts := strings.Split(o.ShortName, "-")
|
||||
buffer := bytes.Buffer{}
|
||||
for _, p := range parts {
|
||||
buffer.WriteString(strings.Title(p))
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
func writeHeader(out io.Writer) {
|
||||
s := `// Created by extended_key_usage_gen; DO NOT EDIT
|
||||
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package x509
|
||||
|
||||
import (
|
||||
"encoding/asn1"
|
||||
)
|
||||
|
||||
`
|
||||
out.Write([]byte(s))
|
||||
}
|
||||
|
||||
func generateASN1(oidToName map[string]OID) []byte {
|
||||
buffer := bytes.Buffer{}
|
||||
for _, oid := range oidToName {
|
||||
goName := oid.GoName(GO_PREFIX)
|
||||
oidDecl := oid.OIDDecl()
|
||||
buffer.WriteString(goName)
|
||||
buffer.WriteString(" = ")
|
||||
buffer.WriteString(oidDecl)
|
||||
buffer.WriteString("\n")
|
||||
}
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
func generateIntegerConstants(oidToName map[string]OID) []byte {
|
||||
buffer := bytes.Buffer{}
|
||||
buffer.WriteString("const (\n")
|
||||
first := true
|
||||
for _, oid := range oidToName {
|
||||
goName := oid.GoName("ExtKeyUsage")
|
||||
buffer.WriteString(goName)
|
||||
if first {
|
||||
buffer.WriteString(" ExtKeyUsage = iota")
|
||||
first = false
|
||||
}
|
||||
buffer.WriteString("\n")
|
||||
}
|
||||
buffer.WriteString(")\n")
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
func generateNameConstants(oidToName map[string]OID) []byte {
|
||||
buffer := bytes.Buffer{}
|
||||
for _, oid := range oidToName {
|
||||
constantName := oid.GoConstant(CONST_PREFIX)
|
||||
buffer.WriteString(constantName)
|
||||
buffer.WriteString(" = \"")
|
||||
buffer.WriteString(oid.OID)
|
||||
buffer.WriteString("\"\n")
|
||||
}
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
func generateOIDMap(oidToName map[string]OID, mapName string) []byte {
|
||||
buffer := bytes.Buffer{}
|
||||
buffer.WriteString(mapName)
|
||||
buffer.WriteString(" = make(map[string]asn1.ObjectIdentifier)\n")
|
||||
for _, oid := range oidToName {
|
||||
constantName := oid.GoConstant(CONST_PREFIX)
|
||||
goName := oid.GoName(GO_PREFIX)
|
||||
buffer.WriteString(mapName)
|
||||
buffer.WriteString("[")
|
||||
buffer.WriteString(constantName)
|
||||
buffer.WriteString("] = ")
|
||||
buffer.WriteString(goName)
|
||||
buffer.WriteString("\n")
|
||||
}
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
func generateIntegerMap(oidToName map[string]OID, mapName string) []byte {
|
||||
buffer := bytes.Buffer{}
|
||||
buffer.WriteString(mapName)
|
||||
buffer.WriteString(" = make(map[string]ExtKeyUsage)\n")
|
||||
for _, oid := range oidToName {
|
||||
constantName := oid.GoConstant(CONST_PREFIX)
|
||||
goName := oid.GoName("ExtKeyUsage")
|
||||
buffer.WriteString(mapName)
|
||||
buffer.WriteString("[")
|
||||
buffer.WriteString(constantName)
|
||||
buffer.WriteString("] = ")
|
||||
buffer.WriteString(goName)
|
||||
buffer.WriteString("\n")
|
||||
}
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
func generateEKUJSONStruct(oidToName map[string]OID) []byte {
|
||||
buffer := bytes.Buffer{}
|
||||
buffer.WriteString("type auxExtendedKeyUsage struct {\n")
|
||||
for _, oid := range oidToName {
|
||||
buffer.WriteString(oid.StructFieldName())
|
||||
buffer.WriteString(" bool `json:\"")
|
||||
buffer.WriteString(oid.JSONName(""))
|
||||
buffer.WriteString(",omitempty\"`\n")
|
||||
}
|
||||
buffer.WriteString("Unknown []string `json:\"unknown,omitempty\"`")
|
||||
buffer.WriteString("}\n\n")
|
||||
buffer.WriteString("func (aux *auxExtendedKeyUsage) populateFromASN1(oid asn1.ObjectIdentifier) {\n")
|
||||
buffer.WriteString("s := oid.String()\n")
|
||||
buffer.WriteString("switch s {\n")
|
||||
for _, oid := range oidToName {
|
||||
buffer.WriteString("case ")
|
||||
constantName := oid.GoConstant(CONST_PREFIX)
|
||||
buffer.WriteString(constantName)
|
||||
buffer.WriteString(":\n")
|
||||
buffer.WriteString("aux.")
|
||||
buffer.WriteString(oid.StructFieldName())
|
||||
buffer.WriteString(" = true\n")
|
||||
}
|
||||
buffer.WriteString("default:\n")
|
||||
buffer.WriteString("}\n")
|
||||
buffer.WriteString("return")
|
||||
buffer.WriteString("}\n\n")
|
||||
|
||||
buffer.WriteString("func (aux *auxExtendedKeyUsage) populateFromExtKeyUsage(eku ExtKeyUsage) {\n")
|
||||
buffer.WriteString("switch eku {\n")
|
||||
for _, oid := range oidToName {
|
||||
buffer.WriteString("case ")
|
||||
ekuName := oid.GoName("ExtKeyUsage")
|
||||
buffer.WriteString(ekuName)
|
||||
buffer.WriteString(":\n")
|
||||
buffer.WriteString("aux.")
|
||||
buffer.WriteString(oid.StructFieldName())
|
||||
buffer.WriteString(" = true\n")
|
||||
}
|
||||
buffer.WriteString("default:\n")
|
||||
buffer.WriteString("}\n")
|
||||
buffer.WriteString("return")
|
||||
buffer.WriteString("}\n\n")
|
||||
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
func main() {
|
||||
out, err := os.Create("extended_key_usage.go")
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
defer out.Close()
|
||||
writeHeader(out)
|
||||
|
||||
resp, err := http.Get("https://raw.githubusercontent.com/zmap/constants/master/x509/extended_key_usage.csv")
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
oidToName := make(map[string]OID)
|
||||
r := csv.NewReader(resp.Body)
|
||||
for lines := 0; ; lines++ {
|
||||
record, err := r.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
if lines == 0 {
|
||||
// Header row
|
||||
continue
|
||||
}
|
||||
oid := record[COLUMN_IDX_OID]
|
||||
shortName := record[COLUMN_IDX_SHORT_NAME]
|
||||
oidToName[oid] = OID{
|
||||
OID: oid,
|
||||
ShortName: shortName,
|
||||
}
|
||||
}
|
||||
|
||||
out.Write([]byte("const (\n"))
|
||||
constants := generateNameConstants(oidToName)
|
||||
out.Write(constants)
|
||||
out.Write([]byte(")\n"))
|
||||
|
||||
out.Write([]byte("var (\n"))
|
||||
oidDecls := generateASN1(oidToName)
|
||||
out.Write(oidDecls)
|
||||
out.Write([]byte(")\n"))
|
||||
|
||||
integersConstants := generateIntegerConstants(oidToName)
|
||||
out.Write(integersConstants)
|
||||
|
||||
out.Write(generateEKUJSONStruct(oidToName))
|
||||
|
||||
out.Write([]byte("\nvar ekuOIDs map[string]asn1.ObjectIdentifier\n\n"))
|
||||
out.Write([]byte("\nvar ekuConstants map[string]ExtKeyUsage\n\n"))
|
||||
|
||||
out.Write([]byte("func init() {\n"))
|
||||
mapEntries := generateOIDMap(oidToName, "ekuOIDs")
|
||||
out.Write(mapEntries)
|
||||
out.Write([]byte("\n"))
|
||||
intMapEntries := generateIntegerMap(oidToName, "ekuConstants")
|
||||
out.Write(intMapEntries)
|
||||
out.Write([]byte("}\n"))
|
||||
}
|
||||
21
vendor/github.com/zmap/zcrypto/x509/extended_key_usage_schema.sh
generated
vendored
Executable file
21
vendor/github.com/zmap/zcrypto/x509/extended_key_usage_schema.sh
generated
vendored
Executable file
|
|
@ -0,0 +1,21 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# TODO: This should really be generated by Go code as a subrecord, but
|
||||
# importing in Python is hard. This is quick and dirty.
|
||||
|
||||
FIELDS=$(\
|
||||
cat extended_key_usage.go |\
|
||||
grep json |\
|
||||
cut -d ':' -f 2 |\
|
||||
sed 's|,omitempty||g' |\
|
||||
tr -d '`')
|
||||
echo "extended_key_usage = SubRecord({"
|
||||
for f in $FIELDS; do
|
||||
if [ $f == "\"unknown\"" ]; then
|
||||
echo " $f: ListOf(OID())"
|
||||
else
|
||||
echo " $f: Boolean(),"
|
||||
fi
|
||||
done
|
||||
echo "})"
|
||||
|
|
@ -0,0 +1,765 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package x509
|
||||
|
||||
import (
|
||||
"encoding/asn1"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/zmap/zcrypto/x509/ct"
|
||||
"github.com/zmap/zcrypto/x509/pkix"
|
||||
)
|
||||
|
||||
var (
|
||||
oidExtKeyUsage = asn1.ObjectIdentifier{2, 5, 29, 15}
|
||||
oidExtBasicConstraints = asn1.ObjectIdentifier{2, 5, 29, 19}
|
||||
oidExtSubjectAltName = asn1.ObjectIdentifier{2, 5, 29, 17}
|
||||
oidExtIssuerAltName = asn1.ObjectIdentifier{2, 5, 29, 18}
|
||||
oidExtNameConstraints = asn1.ObjectIdentifier{2, 5, 29, 30}
|
||||
oidCRLDistributionPoints = asn1.ObjectIdentifier{2, 5, 29, 31}
|
||||
oidExtAuthKeyId = asn1.ObjectIdentifier{2, 5, 29, 35}
|
||||
oidExtSubjectKeyId = asn1.ObjectIdentifier{2, 5, 29, 14}
|
||||
oidExtExtendedKeyUsage = asn1.ObjectIdentifier{2, 5, 29, 37}
|
||||
oidExtCertificatePolicy = asn1.ObjectIdentifier{2, 5, 29, 32}
|
||||
|
||||
oidExtAuthorityInfoAccess = oidExtensionAuthorityInfoAccess
|
||||
oidExtensionCTPrecertificatePoison = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 11129, 2, 4, 3}
|
||||
oidExtSignedCertificateTimestampList = oidExtensionSignedCertificateTimestampList
|
||||
)
|
||||
|
||||
type encodedUnknownExtensions []encodedUnknownExtension
|
||||
|
||||
type CertificateExtensions struct {
|
||||
KeyUsage KeyUsage `json:"key_usage,omitempty"`
|
||||
BasicConstraints *BasicConstraints `json:"basic_constraints,omitempty"`
|
||||
SubjectAltName *GeneralNames `json:"subject_alt_name,omitempty"`
|
||||
IssuerAltName *GeneralNames `json:"issuer_alt_name,omitempty"`
|
||||
NameConstraints *NameConstraints `json:"name_constraints,omitempty"`
|
||||
CRLDistributionPoints CRLDistributionPoints `json:"crl_distribution_points,omitempty"`
|
||||
AuthKeyID SubjAuthKeyId `json:"authority_key_id,omitempty"`
|
||||
SubjectKeyID SubjAuthKeyId `json:"subject_key_id,omitempty"`
|
||||
ExtendedKeyUsage *ExtendedKeyUsageExtension `json:"extended_key_usage,omitempty"`
|
||||
CertificatePolicies *CertificatePoliciesData `json:"certificate_policies,omitempty"`
|
||||
AuthorityInfoAccess *AuthorityInfoAccess `json:"authority_info_access,omitempty"`
|
||||
IsPrecert IsPrecert `json:"ct_poison,omitempty"`
|
||||
SignedCertificateTimestampList []*ct.SignedCertificateTimestamp `json:"signed_certificate_timestamps,omitempty"`
|
||||
}
|
||||
|
||||
type UnknownCertificateExtensions []pkix.Extension
|
||||
|
||||
type encodedUnknownExtension struct {
|
||||
OID string `json:"oid"`
|
||||
Critical bool `json:"critical"`
|
||||
Value []byte `json:"raw,omitempty"`
|
||||
}
|
||||
|
||||
type IsPrecert bool
|
||||
|
||||
type BasicConstraints struct {
|
||||
IsCA bool `json:"is_ca"`
|
||||
MaxPathLen *int `json:"max_path_len,omitempty"`
|
||||
}
|
||||
|
||||
type NoticeReference struct {
|
||||
Organization string `json:"organization,omitempty"`
|
||||
NoticeNumbers NoticeNumber `json:"notice_numbers,omitempty"`
|
||||
}
|
||||
|
||||
type UserNoticeData struct {
|
||||
ExplicitText string `json:"explicit_text,omitempty"`
|
||||
NoticeReference []NoticeReference `json:"notice_reference,omitempty"`
|
||||
}
|
||||
|
||||
type CertificatePoliciesJSON struct {
|
||||
PolicyIdentifier string `json:"id,omitempty"`
|
||||
CPSUri []string `json:"cps,omitempty"`
|
||||
UserNotice []UserNoticeData `json:"user_notice,omitempty"`
|
||||
}
|
||||
|
||||
type CertificatePolicies []CertificatePoliciesJSON
|
||||
|
||||
type CertificatePoliciesData struct {
|
||||
PolicyIdentifiers []asn1.ObjectIdentifier
|
||||
QualifierId [][]asn1.ObjectIdentifier
|
||||
CPSUri [][]string
|
||||
ExplicitTexts [][]string
|
||||
NoticeRefOrganization [][]string
|
||||
NoticeRefNumbers [][]NoticeNumber
|
||||
}
|
||||
|
||||
func (cp *CertificatePoliciesData) MarshalJSON() ([]byte, error) {
|
||||
policies := CertificatePolicies{}
|
||||
for idx, oid := range cp.PolicyIdentifiers {
|
||||
cpsJSON := CertificatePoliciesJSON{}
|
||||
cpsJSON.PolicyIdentifier = oid.String()
|
||||
for _, uri := range cp.CPSUri[idx] {
|
||||
cpsJSON.CPSUri = append(cpsJSON.CPSUri, uri)
|
||||
}
|
||||
|
||||
for idx2, explicit_text := range cp.ExplicitTexts[idx] {
|
||||
uNoticeData := UserNoticeData{}
|
||||
uNoticeData.ExplicitText = explicit_text
|
||||
noticeRef := NoticeReference{}
|
||||
if len(cp.NoticeRefOrganization[idx]) > 0 {
|
||||
organization := cp.NoticeRefOrganization[idx][idx2]
|
||||
noticeRef.Organization = organization
|
||||
noticeRef.NoticeNumbers = cp.NoticeRefNumbers[idx][idx2]
|
||||
uNoticeData.NoticeReference = append(uNoticeData.NoticeReference, noticeRef)
|
||||
}
|
||||
cpsJSON.UserNotice = append(cpsJSON.UserNotice, uNoticeData)
|
||||
}
|
||||
|
||||
policies = append(policies, cpsJSON)
|
||||
}
|
||||
return json.Marshal(policies)
|
||||
}
|
||||
|
||||
// GeneralNames corresponds an X.509 GeneralName defined in
|
||||
// Section 4.2.1.6 of RFC 5280.
|
||||
//
|
||||
// GeneralName ::= CHOICE {
|
||||
// otherName [0] AnotherName,
|
||||
// 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 }
|
||||
type GeneralNames struct {
|
||||
DirectoryNames []pkix.Name
|
||||
DNSNames []string
|
||||
EDIPartyNames []pkix.EDIPartyName
|
||||
EmailAddresses []string
|
||||
IPAddresses []net.IP
|
||||
OtherNames []pkix.OtherName
|
||||
RegisteredIDs []asn1.ObjectIdentifier
|
||||
URIs []string
|
||||
}
|
||||
|
||||
type jsonGeneralNames struct {
|
||||
DirectoryNames []pkix.Name `json:"directory_names,omitempty"`
|
||||
DNSNames []string `json:"dns_names,omitempty"`
|
||||
EDIPartyNames []pkix.EDIPartyName `json:"edi_party_names,omitempty"`
|
||||
EmailAddresses []string `json:"email_addresses,omitempty"`
|
||||
IPAddresses []net.IP `json:"ip_addresses,omitempty"`
|
||||
OtherNames []pkix.OtherName `json:"other_names,omitempty"`
|
||||
RegisteredIDs []string `json:"registered_ids,omitempty"`
|
||||
URIs []string `json:"uniform_resource_identifiers,omitempty"`
|
||||
}
|
||||
|
||||
func (gn *GeneralNames) MarshalJSON() ([]byte, error) {
|
||||
jsan := jsonGeneralNames{
|
||||
DirectoryNames: gn.DirectoryNames,
|
||||
DNSNames: gn.DNSNames,
|
||||
EDIPartyNames: gn.EDIPartyNames,
|
||||
EmailAddresses: gn.EmailAddresses,
|
||||
IPAddresses: gn.IPAddresses,
|
||||
OtherNames: gn.OtherNames,
|
||||
RegisteredIDs: make([]string, 0, len(gn.RegisteredIDs)),
|
||||
URIs: gn.URIs,
|
||||
}
|
||||
for _, id := range gn.RegisteredIDs {
|
||||
jsan.RegisteredIDs = append(jsan.RegisteredIDs, id.String())
|
||||
}
|
||||
return json.Marshal(jsan)
|
||||
}
|
||||
|
||||
func (gn *GeneralNames) UnmarshalJSON(b []byte) error {
|
||||
var jsan jsonGeneralNames
|
||||
err := json.Unmarshal(b, &jsan)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gn.DirectoryNames = jsan.DirectoryNames
|
||||
gn.DNSNames = jsan.DNSNames
|
||||
gn.EDIPartyNames = jsan.EDIPartyNames
|
||||
gn.EmailAddresses = jsan.EmailAddresses
|
||||
gn.IPAddresses = jsan.IPAddresses
|
||||
gn.OtherNames = jsan.OtherNames
|
||||
gn.RegisteredIDs = make([]asn1.ObjectIdentifier, len(jsan.RegisteredIDs))
|
||||
gn.URIs = jsan.URIs
|
||||
|
||||
for i, rID := range jsan.RegisteredIDs {
|
||||
arcs := strings.Split(rID, ".")
|
||||
oid := make(asn1.ObjectIdentifier, len(arcs))
|
||||
|
||||
for j, s := range arcs {
|
||||
tmp, err := strconv.ParseInt(s, 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oid[j] = int(tmp)
|
||||
}
|
||||
gn.RegisteredIDs[i] = oid
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: Handle excluded names
|
||||
|
||||
type NameConstraints struct {
|
||||
Critical bool `json:"critical"`
|
||||
|
||||
PermittedDNSNames []GeneralSubtreeString
|
||||
PermittedEmailAddresses []GeneralSubtreeString
|
||||
PermittedIPAddresses []GeneralSubtreeIP
|
||||
PermittedDirectoryNames []GeneralSubtreeName
|
||||
PermittedEdiPartyNames []GeneralSubtreeEdi
|
||||
PermittedRegisteredIDs []GeneralSubtreeOid
|
||||
|
||||
ExcludedEmailAddresses []GeneralSubtreeString
|
||||
ExcludedDNSNames []GeneralSubtreeString
|
||||
ExcludedIPAddresses []GeneralSubtreeIP
|
||||
ExcludedDirectoryNames []GeneralSubtreeName
|
||||
ExcludedEdiPartyNames []GeneralSubtreeEdi
|
||||
ExcludedRegisteredIDs []GeneralSubtreeOid
|
||||
}
|
||||
|
||||
type NameConstraintsJSON struct {
|
||||
Critical bool `json:"critical"`
|
||||
|
||||
PermittedDNSNames []string `json:"permitted_names,omitempty"`
|
||||
PermittedEmailAddresses []string `json:"permitted_email_addresses,omitempty"`
|
||||
PermittedIPAddresses []GeneralSubtreeIP `json:"permitted_ip_addresses,omitempty"`
|
||||
PermittedDirectoryNames []pkix.Name `json:"permitted_directory_names,omitempty"`
|
||||
PermittedEdiPartyNames []pkix.EDIPartyName `json:"permitted_edi_party_names,omitempty"`
|
||||
PermittedRegisteredIDs []string `json:"permitted_registred_id,omitempty"`
|
||||
|
||||
ExcludedDNSNames []string `json:"excluded_names,omitempty"`
|
||||
ExcludedEmailAddresses []string `json:"excluded_email_addresses,omitempty"`
|
||||
ExcludedIPAddresses []GeneralSubtreeIP `json:"excluded_ip_addresses,omitempty"`
|
||||
ExcludedDirectoryNames []pkix.Name `json:"excluded_directory_names,omitempty"`
|
||||
ExcludedEdiPartyNames []pkix.EDIPartyName `json:"excluded_edi_party_names,omitempty"`
|
||||
ExcludedRegisteredIDs []string `json:"excluded_registred_id,omitempty"`
|
||||
}
|
||||
|
||||
func (nc *NameConstraints) UnmarshalJSON(b []byte) error {
|
||||
var ncJson NameConstraintsJSON
|
||||
err := json.Unmarshal(b, &ncJson)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, dns := range ncJson.PermittedDNSNames {
|
||||
nc.PermittedDNSNames = append(nc.PermittedDNSNames, GeneralSubtreeString{Data: dns})
|
||||
}
|
||||
for _, email := range ncJson.PermittedEmailAddresses {
|
||||
nc.PermittedEmailAddresses = append(nc.PermittedEmailAddresses, GeneralSubtreeString{Data: email})
|
||||
}
|
||||
for _, constraint := range ncJson.PermittedIPAddresses {
|
||||
nc.PermittedIPAddresses = append(nc.PermittedIPAddresses, constraint)
|
||||
}
|
||||
for _, directory := range ncJson.PermittedDirectoryNames {
|
||||
nc.PermittedDirectoryNames = append(nc.PermittedDirectoryNames, GeneralSubtreeName{Data: directory})
|
||||
}
|
||||
for _, edi := range ncJson.PermittedEdiPartyNames {
|
||||
nc.PermittedEdiPartyNames = append(nc.PermittedEdiPartyNames, GeneralSubtreeEdi{Data: edi})
|
||||
}
|
||||
for _, id := range ncJson.PermittedRegisteredIDs {
|
||||
arcs := strings.Split(id, ".")
|
||||
oid := make(asn1.ObjectIdentifier, len(arcs))
|
||||
|
||||
for j, s := range arcs {
|
||||
tmp, err := strconv.ParseInt(s, 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oid[j] = int(tmp)
|
||||
}
|
||||
nc.PermittedRegisteredIDs = append(nc.PermittedRegisteredIDs, GeneralSubtreeOid{Data: oid})
|
||||
}
|
||||
|
||||
for _, dns := range ncJson.ExcludedDNSNames {
|
||||
nc.ExcludedDNSNames = append(nc.ExcludedDNSNames, GeneralSubtreeString{Data: dns})
|
||||
}
|
||||
for _, email := range ncJson.ExcludedEmailAddresses {
|
||||
nc.ExcludedEmailAddresses = append(nc.ExcludedEmailAddresses, GeneralSubtreeString{Data: email})
|
||||
}
|
||||
for _, constraint := range ncJson.ExcludedIPAddresses {
|
||||
nc.ExcludedIPAddresses = append(nc.ExcludedIPAddresses, constraint)
|
||||
}
|
||||
for _, directory := range ncJson.ExcludedDirectoryNames {
|
||||
nc.ExcludedDirectoryNames = append(nc.ExcludedDirectoryNames, GeneralSubtreeName{Data: directory})
|
||||
}
|
||||
for _, edi := range ncJson.ExcludedEdiPartyNames {
|
||||
nc.ExcludedEdiPartyNames = append(nc.ExcludedEdiPartyNames, GeneralSubtreeEdi{Data: edi})
|
||||
}
|
||||
for _, id := range ncJson.ExcludedRegisteredIDs {
|
||||
arcs := strings.Split(id, ".")
|
||||
oid := make(asn1.ObjectIdentifier, len(arcs))
|
||||
|
||||
for j, s := range arcs {
|
||||
tmp, err := strconv.ParseInt(s, 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oid[j] = int(tmp)
|
||||
}
|
||||
nc.ExcludedRegisteredIDs = append(nc.ExcludedRegisteredIDs, GeneralSubtreeOid{Data: oid})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (nc NameConstraints) MarshalJSON() ([]byte, error) {
|
||||
var out NameConstraintsJSON
|
||||
for _, dns := range nc.PermittedDNSNames {
|
||||
out.PermittedDNSNames = append(out.PermittedDNSNames, dns.Data)
|
||||
}
|
||||
for _, email := range nc.PermittedEmailAddresses {
|
||||
out.PermittedEmailAddresses = append(out.PermittedEmailAddresses, email.Data)
|
||||
}
|
||||
out.PermittedIPAddresses = nc.PermittedIPAddresses
|
||||
for _, directory := range nc.PermittedDirectoryNames {
|
||||
out.PermittedDirectoryNames = append(out.PermittedDirectoryNames, directory.Data)
|
||||
}
|
||||
for _, edi := range nc.PermittedEdiPartyNames {
|
||||
out.PermittedEdiPartyNames = append(out.PermittedEdiPartyNames, edi.Data)
|
||||
}
|
||||
for _, id := range nc.PermittedRegisteredIDs {
|
||||
out.PermittedRegisteredIDs = append(out.PermittedRegisteredIDs, id.Data.String())
|
||||
}
|
||||
|
||||
for _, dns := range nc.ExcludedDNSNames {
|
||||
out.ExcludedDNSNames = append(out.ExcludedDNSNames, dns.Data)
|
||||
}
|
||||
for _, email := range nc.ExcludedEmailAddresses {
|
||||
out.ExcludedEmailAddresses = append(out.ExcludedEmailAddresses, email.Data)
|
||||
}
|
||||
for _, ip := range nc.ExcludedIPAddresses {
|
||||
out.ExcludedIPAddresses = append(out.ExcludedIPAddresses, ip)
|
||||
}
|
||||
for _, directory := range nc.ExcludedDirectoryNames {
|
||||
out.ExcludedDirectoryNames = append(out.ExcludedDirectoryNames, directory.Data)
|
||||
}
|
||||
for _, edi := range nc.ExcludedEdiPartyNames {
|
||||
out.ExcludedEdiPartyNames = append(out.ExcludedEdiPartyNames, edi.Data)
|
||||
}
|
||||
for _, id := range nc.ExcludedRegisteredIDs {
|
||||
out.ExcludedRegisteredIDs = append(out.ExcludedRegisteredIDs, id.Data.String())
|
||||
}
|
||||
return json.Marshal(out)
|
||||
}
|
||||
|
||||
type CRLDistributionPoints []string
|
||||
|
||||
type SubjAuthKeyId []byte
|
||||
|
||||
func (kid SubjAuthKeyId) MarshalJSON() ([]byte, error) {
|
||||
enc := hex.EncodeToString(kid)
|
||||
return json.Marshal(enc)
|
||||
}
|
||||
|
||||
type ExtendedKeyUsage []ExtKeyUsage
|
||||
|
||||
type ExtendedKeyUsageExtension struct {
|
||||
Known ExtendedKeyUsage
|
||||
Unknown []asn1.ObjectIdentifier
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshal interface. The output is a struct of
|
||||
// bools, with an additional `Value` field containing the actual OIDs.
|
||||
func (e *ExtendedKeyUsageExtension) MarshalJSON() ([]byte, error) {
|
||||
aux := new(auxExtendedKeyUsage)
|
||||
for _, e := range e.Known {
|
||||
aux.populateFromExtKeyUsage(e)
|
||||
}
|
||||
for _, oid := range e.Unknown {
|
||||
aux.Unknown = append(aux.Unknown, oid.String())
|
||||
}
|
||||
return json.Marshal(aux)
|
||||
}
|
||||
|
||||
func (e *ExtendedKeyUsageExtension) UnmarshalJSON(b []byte) error {
|
||||
aux := new(auxExtendedKeyUsage)
|
||||
if err := json.Unmarshal(b, aux); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: Generate the reverse functions.
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:generate go run extended_key_usage_gen.go
|
||||
|
||||
// The string functions for CertValidationLevel are auto-generated via
|
||||
// `go generate <full_path_to_x509_package>` or running `go generate` in the package directory
|
||||
//go:generate stringer -type=CertValidationLevel -output=generated_certvalidationlevel_string.go
|
||||
type CertValidationLevel int
|
||||
|
||||
const (
|
||||
UnknownValidationLevel CertValidationLevel = 0
|
||||
DV CertValidationLevel = 1
|
||||
OV CertValidationLevel = 2
|
||||
EV CertValidationLevel = 3
|
||||
)
|
||||
|
||||
func (c *CertValidationLevel) MarshalJSON() ([]byte, error) {
|
||||
if *c == UnknownValidationLevel || *c < 0 || *c > EV {
|
||||
return json.Marshal("unknown")
|
||||
}
|
||||
return json.Marshal(c.String())
|
||||
}
|
||||
|
||||
// TODO: All of validation-level maps should be auto-generated from
|
||||
// https://github.com/zmap/constants.
|
||||
|
||||
// ExtendedValidationOIDs contains the UNION of Chromium
|
||||
// (https://chromium.googlesource.com/chromium/src/net/+/master/cert/ev_root_ca_metadata.cc)
|
||||
// and Firefox
|
||||
// (http://hg.mozilla.org/mozilla-central/file/tip/security/certverifier/ExtendedValidation.cpp)
|
||||
// EV OID lists
|
||||
var ExtendedValidationOIDs = map[string]interface{}{
|
||||
// CA/Browser Forum EV OID standard
|
||||
// https://cabforum.org/object-registry/
|
||||
"2.23.140.1.1": nil,
|
||||
// CA/Browser Forum EV Code Signing
|
||||
"2.23.140.1.3": nil,
|
||||
// CA/Browser Forum .onion EV Certs
|
||||
"2.23.140.1.31": nil,
|
||||
// AC Camerfirma S.A. Chambers of Commerce Root - 2008
|
||||
// https://www.camerfirma.com
|
||||
// AC Camerfirma uses the last two arcs to track how the private key
|
||||
// is managed - the effective verification policy is the same.
|
||||
"1.3.6.1.4.1.17326.10.14.2.1.2": nil,
|
||||
"1.3.6.1.4.1.17326.10.14.2.2.2": nil,
|
||||
// AC Camerfirma S.A. Global Chambersign Root - 2008
|
||||
// https://server2.camerfirma.com:8082
|
||||
// AC Camerfirma uses the last two arcs to track how the private key
|
||||
// is managed - the effective verification policy is the same.
|
||||
"1.3.6.1.4.1.17326.10.8.12.1.2": nil,
|
||||
"1.3.6.1.4.1.17326.10.8.12.2.2": nil,
|
||||
// Actalis Authentication Root CA
|
||||
// https://ssltest-a.actalis.it:8443
|
||||
"1.3.159.1.17.1": nil,
|
||||
// AffirmTrust Commercial
|
||||
// https://commercial.affirmtrust.com/
|
||||
"1.3.6.1.4.1.34697.2.1": nil,
|
||||
// AffirmTrust Networking
|
||||
// https://networking.affirmtrust.com:4431
|
||||
"1.3.6.1.4.1.34697.2.2": nil,
|
||||
// AffirmTrust Premium
|
||||
// https://premium.affirmtrust.com:4432/
|
||||
"1.3.6.1.4.1.34697.2.3": nil,
|
||||
// AffirmTrust Premium ECC
|
||||
// https://premiumecc.affirmtrust.com:4433/
|
||||
"1.3.6.1.4.1.34697.2.4": nil,
|
||||
// Autoridad de Certificacion Firmaprofesional CIF A62634068
|
||||
// https://publifirma.firmaprofesional.com/
|
||||
"1.3.6.1.4.1.13177.10.1.3.10": nil,
|
||||
// Buypass Class 3 CA 1
|
||||
// https://valid.evident.ca13.ssl.buypass.no/
|
||||
"2.16.578.1.26.1.3.3": nil,
|
||||
// Certification Authority of WoSign
|
||||
// CA 沃通根证书
|
||||
// https://root2evtest.wosign.com/
|
||||
"1.3.6.1.4.1.36305.2": nil,
|
||||
// CertPlus Class 2 Primary CA (KEYNECTIS)
|
||||
// https://www.keynectis.com/
|
||||
"1.3.6.1.4.1.22234.2.5.2.3.1": nil,
|
||||
// Certum Trusted Network CA
|
||||
// https://juice.certum.pl/
|
||||
"1.2.616.1.113527.2.5.1.1": nil,
|
||||
// China Internet Network Information Center EV Certificates Root
|
||||
// https://evdemo.cnnic.cn/
|
||||
"1.3.6.1.4.1.29836.1.10": nil,
|
||||
// COMODO Certification Authority & USERTrust RSA Certification Authority & UTN-USERFirst-Hardware & AddTrust External CA Root
|
||||
// https://secure.comodo.com/
|
||||
// https://usertrustrsacertificationauthority-ev.comodoca.com/
|
||||
// https://addtrustexternalcaroot-ev.comodoca.com
|
||||
"1.3.6.1.4.1.6449.1.2.1.5.1": nil,
|
||||
// Cybertrust Global Root & GTE CyberTrust Global Root & Baltimore CyberTrust Root
|
||||
// https://evup.cybertrust.ne.jp/ctj-ev-upgrader/evseal.gif
|
||||
// https://www.cybertrust.ne.jp/
|
||||
// https://secure.omniroot.com/repository/
|
||||
"1.3.6.1.4.1.6334.1.100.1": nil,
|
||||
// DigiCert High Assurance EV Root CA
|
||||
// https://www.digicert.com
|
||||
"2.16.840.1.114412.2.1": nil,
|
||||
// D-TRUST Root Class 3 CA 2 EV 2009
|
||||
// https://certdemo-ev-valid.ssl.d-trust.net/
|
||||
"1.3.6.1.4.1.4788.2.202.1": nil,
|
||||
// Entrust.net Secure Server Certification Authority
|
||||
// https://www.entrust.net/
|
||||
"2.16.840.1.114028.10.1.2": nil,
|
||||
// E-Tugra Certification Authority
|
||||
// https://sslev.e-tugra.com.tr
|
||||
"2.16.792.3.0.4.1.1.4": nil,
|
||||
// GeoTrust Primary Certification Authority
|
||||
// https://www.geotrust.com/
|
||||
"1.3.6.1.4.1.14370.1.6": nil,
|
||||
// GlobalSign Root CA - R2
|
||||
// https://www.globalsign.com/
|
||||
"1.3.6.1.4.1.4146.1.1": nil,
|
||||
// Go Daddy Class 2 Certification Authority & Go Daddy Root Certificate Authority - G2
|
||||
// https://www.godaddy.com/
|
||||
// https://valid.gdig2.catest.godaddy.com/
|
||||
"2.16.840.1.114413.1.7.23.3": nil,
|
||||
// Izenpe.com - SHA256 root
|
||||
// The first OID is for businesses and the second for government entities.
|
||||
// These are the test sites, respectively:
|
||||
// https://servicios.izenpe.com
|
||||
// https://servicios1.izenpe.com
|
||||
// Windows XP finds this, SHA1, root instead. The policy OIDs are the same
|
||||
// as for the SHA256 root, above.
|
||||
"1.3.6.1.4.1.14777.6.1.1": nil,
|
||||
"1.3.6.1.4.1.14777.6.1.2": nil,
|
||||
// Network Solutions Certificate Authority
|
||||
// https://www.networksolutions.com/website-packages/index.jsp
|
||||
"1.3.6.1.4.1.782.1.2.1.8.1": nil,
|
||||
// QuoVadis Root CA 2
|
||||
// https://www.quovadis.bm/
|
||||
"1.3.6.1.4.1.8024.0.2.100.1.2": nil,
|
||||
// SecureTrust CA, SecureTrust Corporation
|
||||
// https://www.securetrust.com
|
||||
// https://www.trustwave.com/
|
||||
"2.16.840.1.114404.1.1.2.4.1": nil,
|
||||
// Security Communication RootCA1
|
||||
// https://www.secomtrust.net/contact/form.html
|
||||
"1.2.392.200091.100.721.1": nil,
|
||||
// Staat der Nederlanden EV Root CA
|
||||
// https://pkioevssl-v.quovadisglobal.com/
|
||||
"2.16.528.1.1003.1.2.7": nil,
|
||||
// StartCom Certification Authority
|
||||
// https://www.startssl.com/
|
||||
"1.3.6.1.4.1.23223.1.1.1": nil,
|
||||
// Starfield Class 2 Certification Authority
|
||||
// https://www.starfieldtech.com/
|
||||
"2.16.840.1.114414.1.7.23.3": nil,
|
||||
// Starfield Services Root Certificate Authority - G2
|
||||
// https://valid.sfsg2.catest.starfieldtech.com/
|
||||
"2.16.840.1.114414.1.7.24.3": nil,
|
||||
// SwissSign Gold CA - G2
|
||||
// https://testevg2.swisssign.net/
|
||||
"2.16.756.1.89.1.2.1.1": nil,
|
||||
// Swisscom Root EV CA 2
|
||||
// https://test-quarz-ev-ca-2.pre.swissdigicert.ch
|
||||
"2.16.756.1.83.21.0": nil,
|
||||
// thawte Primary Root CA
|
||||
// https://www.thawte.com/
|
||||
"2.16.840.1.113733.1.7.48.1": nil,
|
||||
// TWCA Global Root CA
|
||||
// https://evssldemo3.twca.com.tw/index.html
|
||||
"1.3.6.1.4.1.40869.1.1.22.3": nil,
|
||||
// T-TeleSec GlobalRoot Class 3
|
||||
// http://www.telesec.de/ / https://root-class3.test.telesec.de/
|
||||
"1.3.6.1.4.1.7879.13.24.1": nil,
|
||||
// VeriSign Class 3 Public Primary Certification Authority - G5
|
||||
// https://www.verisign.com/
|
||||
"2.16.840.1.113733.1.7.23.6": nil,
|
||||
// Wells Fargo WellsSecure Public Root Certificate Authority
|
||||
// https://nerys.wellsfargo.com/test.html
|
||||
"2.16.840.1.114171.500.9": nil,
|
||||
// CN=CFCA EV ROOT,O=China Financial Certification Authority,C=CN
|
||||
// https://www.cfca.com.cn/
|
||||
"2.16.156.112554.3": nil,
|
||||
// CN=OISTE WISeKey Global Root GB CA,OU=OISTE Foundation Endorsed,O=WISeKey,C=CH
|
||||
// https://www.wisekey.com/repository/cacertificates/
|
||||
"2.16.756.5.14.7.4.8": nil,
|
||||
// CN=TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H6,O=TÜRKTRUST Bilgi İletişim ve Bilişim Güvenliği Hizmetleri A...,L=Ankara,C=TR
|
||||
// https://www.turktrust.com.tr/
|
||||
"2.16.792.3.0.3.1.1.5": nil,
|
||||
}
|
||||
|
||||
// OrganizationValidationOIDs contains CA specific OV OIDs from
|
||||
// https://cabforum.org/object-registry/
|
||||
var OrganizationValidationOIDs = map[string]interface{}{
|
||||
// CA/Browser Forum OV OID standard
|
||||
// https://cabforum.org/object-registry/
|
||||
"2.23.140.1.2.2": nil,
|
||||
// CA/Browser Forum individually validated
|
||||
"2.23.140.1.2.3": nil,
|
||||
// Digicert
|
||||
"2.16.840.1.114412.1.1": nil,
|
||||
// D-Trust
|
||||
"1.3.6.1.4.1.4788.2.200.1": nil,
|
||||
// GoDaddy
|
||||
"2.16.840.1.114413.1.7.23.2": nil,
|
||||
// Logius
|
||||
"2.16.528.1.1003.1.2.5.6": nil,
|
||||
// QuoVadis
|
||||
"1.3.6.1.4.1.8024.0.2.100.1.1": nil,
|
||||
// Starfield
|
||||
"2.16.840.1.114414.1.7.23.2": nil,
|
||||
// TurkTrust
|
||||
"2.16.792.3.0.3.1.1.2": nil,
|
||||
}
|
||||
|
||||
// DomainValidationOIDs contain OIDs that identify DV certs.
|
||||
var DomainValidationOIDs = map[string]interface{}{
|
||||
// Globalsign
|
||||
"1.3.6.1.4.1.4146.1.10.10": nil,
|
||||
// Let's Encrypt
|
||||
"1.3.6.1.4.1.44947.1.1.1": nil,
|
||||
// Comodo (eNom)
|
||||
"1.3.6.1.4.1.6449.1.2.2.10": nil,
|
||||
// Comodo (WoTrust)
|
||||
"1.3.6.1.4.1.6449.1.2.2.15": nil,
|
||||
// Comodo (RBC SOFT)
|
||||
"1.3.6.1.4.1.6449.1.2.2.16": nil,
|
||||
// Comodo (RegisterFly)
|
||||
"1.3.6.1.4.1.6449.1.2.2.17": nil,
|
||||
// Comodo (Central Security Patrols)
|
||||
"1.3.6.1.4.1.6449.1.2.2.18": nil,
|
||||
// Comodo (eBiz Networks)
|
||||
"1.3.6.1.4.1.6449.1.2.2.19": nil,
|
||||
// Comodo (OptimumSSL)
|
||||
"1.3.6.1.4.1.6449.1.2.2.21": nil,
|
||||
// Comodo (WoSign)
|
||||
"1.3.6.1.4.1.6449.1.2.2.22": nil,
|
||||
// Comodo (Register.com)
|
||||
"1.3.6.1.4.1.6449.1.2.2.24": nil,
|
||||
// Comodo (The Code Project)
|
||||
"1.3.6.1.4.1.6449.1.2.2.25": nil,
|
||||
// Comodo (Gandi)
|
||||
"1.3.6.1.4.1.6449.1.2.2.26": nil,
|
||||
// Comodo (GlobeSSL)
|
||||
"1.3.6.1.4.1.6449.1.2.2.27": nil,
|
||||
// Comodo (DreamHost)
|
||||
"1.3.6.1.4.1.6449.1.2.2.28": nil,
|
||||
// Comodo (TERENA)
|
||||
"1.3.6.1.4.1.6449.1.2.2.29": nil,
|
||||
// Comodo (GlobalSSL)
|
||||
"1.3.6.1.4.1.6449.1.2.2.31": nil,
|
||||
// Comodo (IceWarp)
|
||||
"1.3.6.1.4.1.6449.1.2.2.35": nil,
|
||||
// Comodo (Dotname Korea)
|
||||
"1.3.6.1.4.1.6449.1.2.2.37": nil,
|
||||
// Comodo (TrustSign)
|
||||
"1.3.6.1.4.1.6449.1.2.2.38": nil,
|
||||
// Comodo (Formidable)
|
||||
"1.3.6.1.4.1.6449.1.2.2.39": nil,
|
||||
// Comodo (SSL Blindado)
|
||||
"1.3.6.1.4.1.6449.1.2.2.40": nil,
|
||||
// Comodo (Dreamscape Networks)
|
||||
"1.3.6.1.4.1.6449.1.2.2.41": nil,
|
||||
// Comodo (K Software)
|
||||
"1.3.6.1.4.1.6449.1.2.2.42": nil,
|
||||
// Comodo (FBS)
|
||||
"1.3.6.1.4.1.6449.1.2.2.44": nil,
|
||||
// Comodo (ReliaSite)
|
||||
"1.3.6.1.4.1.6449.1.2.2.45": nil,
|
||||
// Comodo (CertAssure)
|
||||
"1.3.6.1.4.1.6449.1.2.2.47": nil,
|
||||
// Comodo (TrustAsia)
|
||||
"1.3.6.1.4.1.6449.1.2.2.49": nil,
|
||||
// Comodo (SecureCore)
|
||||
"1.3.6.1.4.1.6449.1.2.2.50": nil,
|
||||
// Comodo (Western Digital)
|
||||
"1.3.6.1.4.1.6449.1.2.2.51": nil,
|
||||
// Comodo (cPanel)
|
||||
"1.3.6.1.4.1.6449.1.2.2.52": nil,
|
||||
// Comodo (BlackCert)
|
||||
"1.3.6.1.4.1.6449.1.2.2.53": nil,
|
||||
// Comodo (KeyNet Systems)
|
||||
"1.3.6.1.4.1.6449.1.2.2.54": nil,
|
||||
// Comodo
|
||||
"1.3.6.1.4.1.6449.1.2.2.7": nil,
|
||||
// Comodo (CSC)
|
||||
"1.3.6.1.4.1.6449.1.2.2.8": nil,
|
||||
// Digicert
|
||||
"2.16.840.1.114412.1.2": nil,
|
||||
// GoDaddy
|
||||
"2.16.840.1.114413.1.7.23.1": nil,
|
||||
// Starfield
|
||||
"2.16.840.1.114414.1.7.23.1": nil,
|
||||
// CA/B Forum
|
||||
"2.23.140.1.2.1": nil,
|
||||
}
|
||||
|
||||
// TODO pull out other types
|
||||
type AuthorityInfoAccess struct {
|
||||
OCSPServer []string `json:"ocsp_urls,omitempty"`
|
||||
IssuingCertificateURL []string `json:"issuer_urls,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Certificate) jsonifyExtensions() (*CertificateExtensions, UnknownCertificateExtensions) {
|
||||
exts := new(CertificateExtensions)
|
||||
unk := make([]pkix.Extension, 0, 2)
|
||||
for _, e := range c.Extensions {
|
||||
if e.Id.Equal(oidExtKeyUsage) {
|
||||
exts.KeyUsage = c.KeyUsage
|
||||
} else if e.Id.Equal(oidExtBasicConstraints) {
|
||||
exts.BasicConstraints = new(BasicConstraints)
|
||||
exts.BasicConstraints.IsCA = c.IsCA
|
||||
if c.MaxPathLen > 0 || c.MaxPathLenZero {
|
||||
exts.BasicConstraints.MaxPathLen = new(int)
|
||||
*exts.BasicConstraints.MaxPathLen = c.MaxPathLen
|
||||
}
|
||||
} else if e.Id.Equal(oidExtSubjectAltName) {
|
||||
exts.SubjectAltName = new(GeneralNames)
|
||||
exts.SubjectAltName.DirectoryNames = c.DirectoryNames
|
||||
exts.SubjectAltName.DNSNames = c.DNSNames
|
||||
exts.SubjectAltName.EDIPartyNames = c.EDIPartyNames
|
||||
exts.SubjectAltName.EmailAddresses = c.EmailAddresses
|
||||
exts.SubjectAltName.IPAddresses = c.IPAddresses
|
||||
exts.SubjectAltName.OtherNames = c.OtherNames
|
||||
exts.SubjectAltName.RegisteredIDs = c.RegisteredIDs
|
||||
exts.SubjectAltName.URIs = c.URIs
|
||||
} else if e.Id.Equal(oidExtIssuerAltName) {
|
||||
exts.IssuerAltName = new(GeneralNames)
|
||||
exts.IssuerAltName.DirectoryNames = c.IANDirectoryNames
|
||||
exts.IssuerAltName.DNSNames = c.IANDNSNames
|
||||
exts.IssuerAltName.EDIPartyNames = c.IANEDIPartyNames
|
||||
exts.IssuerAltName.EmailAddresses = c.IANEmailAddresses
|
||||
exts.IssuerAltName.IPAddresses = c.IANIPAddresses
|
||||
exts.IssuerAltName.OtherNames = c.IANOtherNames
|
||||
exts.IssuerAltName.RegisteredIDs = c.IANRegisteredIDs
|
||||
exts.IssuerAltName.URIs = c.IANURIs
|
||||
} else if e.Id.Equal(oidExtNameConstraints) {
|
||||
exts.NameConstraints = new(NameConstraints)
|
||||
exts.NameConstraints.Critical = c.NameConstraintsCritical
|
||||
|
||||
exts.NameConstraints.PermittedDNSNames = c.PermittedDNSNames
|
||||
exts.NameConstraints.PermittedEmailAddresses = c.PermittedEmailAddresses
|
||||
exts.NameConstraints.PermittedIPAddresses = c.PermittedIPAddresses
|
||||
exts.NameConstraints.PermittedDirectoryNames = c.PermittedDirectoryNames
|
||||
exts.NameConstraints.PermittedEdiPartyNames = c.PermittedEdiPartyNames
|
||||
exts.NameConstraints.PermittedRegisteredIDs = c.PermittedRegisteredIDs
|
||||
|
||||
exts.NameConstraints.ExcludedEmailAddresses = c.ExcludedEmailAddresses
|
||||
exts.NameConstraints.ExcludedDNSNames = c.ExcludedDNSNames
|
||||
exts.NameConstraints.ExcludedIPAddresses = c.ExcludedIPAddresses
|
||||
exts.NameConstraints.ExcludedDirectoryNames = c.ExcludedDirectoryNames
|
||||
exts.NameConstraints.ExcludedEdiPartyNames = c.ExcludedEdiPartyNames
|
||||
exts.NameConstraints.ExcludedRegisteredIDs = c.ExcludedRegisteredIDs
|
||||
} else if e.Id.Equal(oidCRLDistributionPoints) {
|
||||
exts.CRLDistributionPoints = c.CRLDistributionPoints
|
||||
} else if e.Id.Equal(oidExtAuthKeyId) {
|
||||
exts.AuthKeyID = c.AuthorityKeyId
|
||||
} else if e.Id.Equal(oidExtExtendedKeyUsage) {
|
||||
exts.ExtendedKeyUsage = new(ExtendedKeyUsageExtension)
|
||||
exts.ExtendedKeyUsage.Known = c.ExtKeyUsage
|
||||
exts.ExtendedKeyUsage.Unknown = c.UnknownExtKeyUsage
|
||||
} else if e.Id.Equal(oidExtCertificatePolicy) {
|
||||
exts.CertificatePolicies = new(CertificatePoliciesData)
|
||||
exts.CertificatePolicies.PolicyIdentifiers = c.PolicyIdentifiers
|
||||
exts.CertificatePolicies.NoticeRefNumbers = c.NoticeRefNumbers
|
||||
exts.CertificatePolicies.NoticeRefOrganization = c.ParsedNoticeRefOrganization
|
||||
exts.CertificatePolicies.ExplicitTexts = c.ParsedExplicitTexts
|
||||
exts.CertificatePolicies.QualifierId = c.QualifierId
|
||||
exts.CertificatePolicies.CPSUri = c.CPSuri
|
||||
|
||||
} else if e.Id.Equal(oidExtAuthorityInfoAccess) {
|
||||
exts.AuthorityInfoAccess = new(AuthorityInfoAccess)
|
||||
exts.AuthorityInfoAccess.OCSPServer = c.OCSPServer
|
||||
exts.AuthorityInfoAccess.IssuingCertificateURL = c.IssuingCertificateURL
|
||||
} else if e.Id.Equal(oidExtSubjectKeyId) {
|
||||
exts.SubjectKeyID = c.SubjectKeyId
|
||||
} else if e.Id.Equal(oidExtSignedCertificateTimestampList) {
|
||||
exts.SignedCertificateTimestampList = c.SignedCertificateTimestampList
|
||||
} else if e.Id.Equal(oidExtensionCTPrecertificatePoison) {
|
||||
exts.IsPrecert = true
|
||||
} else {
|
||||
// Unknown extension
|
||||
unk = append(unk, e)
|
||||
}
|
||||
}
|
||||
return exts, unk
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package x509
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// CertificateFingerprint represents a digest/fingerprint of some data. It can
|
||||
// easily be encoded to hex and JSON (as a hex string).
|
||||
type CertificateFingerprint []byte
|
||||
|
||||
// MD5Fingerprint creates a fingerprint of data using the MD5 hash algorithm.
|
||||
func MD5Fingerprint(data []byte) CertificateFingerprint {
|
||||
sum := md5.Sum(data)
|
||||
return sum[:]
|
||||
}
|
||||
|
||||
// SHA1Fingerprint creates a fingerprint of data using the SHA1 hash algorithm.
|
||||
func SHA1Fingerprint(data []byte) CertificateFingerprint {
|
||||
sum := sha1.Sum(data)
|
||||
return sum[:]
|
||||
}
|
||||
|
||||
// SHA256Fingerprint creates a fingerprint of data using the SHA256 hash
|
||||
// algorithm.
|
||||
func SHA256Fingerprint(data []byte) CertificateFingerprint {
|
||||
sum := sha256.Sum256(data)
|
||||
return sum[:]
|
||||
}
|
||||
|
||||
// SHA512Fingerprint creates a fingerprint of data using the SHA256 hash
|
||||
// algorithm.
|
||||
func SHA512Fingerprint(data []byte) CertificateFingerprint {
|
||||
sum := sha512.Sum512(data)
|
||||
return sum[:]
|
||||
}
|
||||
|
||||
// Equal returns true if the fingerprints are bytewise-equal.
|
||||
func (f CertificateFingerprint) Equal(other CertificateFingerprint) bool {
|
||||
return bytes.Equal(f, other)
|
||||
}
|
||||
|
||||
// Hex returns the given fingerprint encoded as a hex string.
|
||||
func (f CertificateFingerprint) Hex() string {
|
||||
return hex.EncodeToString(f)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface, and marshals the
|
||||
// fingerprint as a hex string.
|
||||
func (f *CertificateFingerprint) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(f.Hex())
|
||||
}
|
||||
16
vendor/github.com/zmap/zcrypto/x509/generated_certvalidationlevel_string.go
generated
vendored
Normal file
16
vendor/github.com/zmap/zcrypto/x509/generated_certvalidationlevel_string.go
generated
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// Code generated by "stringer -type=CertValidationLevel -output=generated_certvalidationlevel_string.go"; DO NOT EDIT.
|
||||
|
||||
package x509
|
||||
|
||||
import "fmt"
|
||||
|
||||
const _CertValidationLevel_name = "UnknownValidationLevelDVOVEV"
|
||||
|
||||
var _CertValidationLevel_index = [...]uint8{0, 22, 24, 26, 28}
|
||||
|
||||
func (i CertValidationLevel) String() string {
|
||||
if i < 0 || i >= CertValidationLevel(len(_CertValidationLevel_index)-1) {
|
||||
return fmt.Sprintf("CertValidationLevel(%d)", i)
|
||||
}
|
||||
return _CertValidationLevel_name[_CertValidationLevel_index[i]:_CertValidationLevel_index[i+1]]
|
||||
}
|
||||
|
|
@ -0,0 +1,468 @@
|
|||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package x509
|
||||
|
||||
import (
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"encoding/asn1"
|
||||
"encoding/json"
|
||||
"net"
|
||||
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/asaskevich/govalidator"
|
||||
jsonKeys "github.com/zmap/zcrypto/json"
|
||||
"github.com/zmap/zcrypto/x509/pkix"
|
||||
)
|
||||
|
||||
var kMinTime, kMaxTime time.Time
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
kMinTime, err = time.Parse(time.RFC3339, "0001-01-01T00:00:00Z")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
kMaxTime, err = time.Parse(time.RFC3339, "9999-12-31T23:59:59Z")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
type auxKeyUsage struct {
|
||||
DigitalSignature bool `json:"digital_signature,omitempty"`
|
||||
ContentCommitment bool `json:"content_commitment,omitempty"`
|
||||
KeyEncipherment bool `json:"key_encipherment,omitempty"`
|
||||
DataEncipherment bool `json:"data_encipherment,omitempty"`
|
||||
KeyAgreement bool `json:"key_agreement,omitempty"`
|
||||
CertificateSign bool `json:"certificate_sign,omitempty"`
|
||||
CRLSign bool `json:"crl_sign,omitempty"`
|
||||
EncipherOnly bool `json:"encipher_only,omitempty"`
|
||||
DecipherOnly bool `json:"decipher_only,omitempty"`
|
||||
Value uint32 `json:"value"`
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface
|
||||
func (k KeyUsage) MarshalJSON() ([]byte, error) {
|
||||
var enc auxKeyUsage
|
||||
enc.Value = uint32(k)
|
||||
if k&KeyUsageDigitalSignature > 0 {
|
||||
enc.DigitalSignature = true
|
||||
}
|
||||
if k&KeyUsageContentCommitment > 0 {
|
||||
enc.ContentCommitment = true
|
||||
}
|
||||
if k&KeyUsageKeyEncipherment > 0 {
|
||||
enc.KeyEncipherment = true
|
||||
}
|
||||
if k&KeyUsageDataEncipherment > 0 {
|
||||
enc.DataEncipherment = true
|
||||
}
|
||||
if k&KeyUsageKeyAgreement > 0 {
|
||||
enc.KeyAgreement = true
|
||||
}
|
||||
if k&KeyUsageCertSign > 0 {
|
||||
enc.CertificateSign = true
|
||||
}
|
||||
if k&KeyUsageCRLSign > 0 {
|
||||
enc.CRLSign = true
|
||||
}
|
||||
if k&KeyUsageEncipherOnly > 0 {
|
||||
enc.EncipherOnly = true
|
||||
}
|
||||
if k&KeyUsageDecipherOnly > 0 {
|
||||
enc.DecipherOnly = true
|
||||
}
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshler interface
|
||||
func (k *KeyUsage) UnmarshalJSON(b []byte) error {
|
||||
var aux auxKeyUsage
|
||||
if err := json.Unmarshal(b, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: validate the flags match
|
||||
v := int(aux.Value)
|
||||
*k = KeyUsage(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
type auxSignatureAlgorithm struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
OID pkix.AuxOID `json:"oid"`
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface
|
||||
func (s *SignatureAlgorithm) MarshalJSON() ([]byte, error) {
|
||||
aux := auxSignatureAlgorithm{
|
||||
Name: s.String(),
|
||||
}
|
||||
for _, val := range signatureAlgorithmDetails {
|
||||
if val.algo == *s {
|
||||
aux.OID = make([]int, len(val.oid))
|
||||
for idx := range val.oid {
|
||||
aux.OID[idx] = val.oid[idx]
|
||||
}
|
||||
}
|
||||
}
|
||||
return json.Marshal(&aux)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshler interface
|
||||
func (s *SignatureAlgorithm) UnmarshalJSON(b []byte) error {
|
||||
var aux auxSignatureAlgorithm
|
||||
if err := json.Unmarshal(b, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
*s = UnknownSignatureAlgorithm
|
||||
oid := asn1.ObjectIdentifier(aux.OID.AsSlice())
|
||||
for _, val := range signatureAlgorithmDetails {
|
||||
if val.oid.Equal(oid) {
|
||||
*s = val.algo
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type auxPublicKeyAlgorithm struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
OID *pkix.AuxOID `json:"oid,omitempty"`
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface
|
||||
func (p *PublicKeyAlgorithm) MarshalJSON() ([]byte, error) {
|
||||
aux := auxPublicKeyAlgorithm{
|
||||
Name: p.String(),
|
||||
}
|
||||
return json.Marshal(&aux)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface
|
||||
func (p *PublicKeyAlgorithm) UnmarshalJSON(b []byte) error {
|
||||
var aux auxPublicKeyAlgorithm
|
||||
if err := json.Unmarshal(b, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func clampTime(t time.Time) time.Time {
|
||||
if t.Before(kMinTime) {
|
||||
return kMinTime
|
||||
}
|
||||
if t.After(kMaxTime) {
|
||||
return kMaxTime
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
type auxValidity struct {
|
||||
Start string `json:"start"`
|
||||
End string `json:"end"`
|
||||
ValidityPeriod int `json:"length"`
|
||||
}
|
||||
|
||||
func (v *validity) MarshalJSON() ([]byte, error) {
|
||||
aux := auxValidity{
|
||||
Start: clampTime(v.NotBefore.UTC()).Format(time.RFC3339),
|
||||
End: clampTime(v.NotAfter.UTC()).Format(time.RFC3339),
|
||||
ValidityPeriod: int(v.NotAfter.Sub(v.NotBefore).Seconds()),
|
||||
}
|
||||
return json.Marshal(&aux)
|
||||
}
|
||||
|
||||
func (v *validity) UnmarshalJSON(b []byte) error {
|
||||
var aux auxValidity
|
||||
if err := json.Unmarshal(b, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
var err error
|
||||
if v.NotBefore, err = time.Parse(time.RFC3339, aux.Start); err != nil {
|
||||
return err
|
||||
}
|
||||
if v.NotAfter, err = time.Parse(time.RFC3339, aux.End); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type jsonSubjectKeyInfo struct {
|
||||
KeyAlgorithm PublicKeyAlgorithm `json:"key_algorithm"`
|
||||
RSAPublicKey *jsonKeys.RSAPublicKey `json:"rsa_public_key,omitempty"`
|
||||
DSAPublicKey interface{} `json:"dsa_public_key,omitempty"`
|
||||
ECDSAPublicKey interface{} `json:"ecdsa_public_key,omitempty"`
|
||||
SPKIFingerprint CertificateFingerprint `json:"fingerprint_sha256"`
|
||||
}
|
||||
|
||||
type jsonSignature struct {
|
||||
SignatureAlgorithm SignatureAlgorithm `json:"signature_algorithm"`
|
||||
Value []byte `json:"value"`
|
||||
Valid bool `json:"valid"`
|
||||
SelfSigned bool `json:"self_signed"`
|
||||
}
|
||||
|
||||
type fullValidity struct {
|
||||
validity
|
||||
ValidityPeriod int
|
||||
}
|
||||
|
||||
type jsonCertificate struct {
|
||||
Version int `json:"version"`
|
||||
SerialNumber string `json:"serial_number"`
|
||||
SignatureAlgorithm SignatureAlgorithm `json:"signature_algorithm"`
|
||||
Issuer pkix.Name `json:"issuer"`
|
||||
IssuerDN string `json:"issuer_dn,omitempty"`
|
||||
Validity fullValidity `json:"validity"`
|
||||
Subject pkix.Name `json:"subject"`
|
||||
SubjectDN string `json:"subject_dn,omitempty"`
|
||||
SubjectKeyInfo jsonSubjectKeyInfo `json:"subject_key_info"`
|
||||
Extensions *CertificateExtensions `json:"extensions,omitempty"`
|
||||
UnknownExtensions UnknownCertificateExtensions `json:"unknown_extensions,omitempty"`
|
||||
Signature jsonSignature `json:"signature"`
|
||||
FingerprintMD5 CertificateFingerprint `json:"fingerprint_md5"`
|
||||
FingerprintSHA1 CertificateFingerprint `json:"fingerprint_sha1"`
|
||||
FingerprintSHA256 CertificateFingerprint `json:"fingerprint_sha256"`
|
||||
FingerprintNoCT CertificateFingerprint `json:"tbs_noct_fingerprint"`
|
||||
SPKISubjectFingerprint CertificateFingerprint `json:"spki_subject_fingerprint"`
|
||||
TBSCertificateFingerprint CertificateFingerprint `json:"tbs_fingerprint"`
|
||||
ValidationLevel CertValidationLevel `json:"validation_level"`
|
||||
Names []string `json:"names,omitempty"`
|
||||
Redacted bool `json:"redacted"`
|
||||
}
|
||||
|
||||
func AddECDSAPublicKeyToKeyMap(keyMap map[string]interface{}, key *ecdsa.PublicKey) {
|
||||
params := key.Params()
|
||||
keyMap["p"] = params.P.Bytes()
|
||||
keyMap["n"] = params.N.Bytes()
|
||||
keyMap["b"] = params.B.Bytes()
|
||||
keyMap["gx"] = params.Gx.Bytes()
|
||||
keyMap["gy"] = params.Gy.Bytes()
|
||||
keyMap["x"] = key.X.Bytes()
|
||||
keyMap["y"] = key.Y.Bytes()
|
||||
keyMap["curve"] = key.Curve.Params().Name
|
||||
keyMap["length"] = key.Curve.Params().BitSize
|
||||
}
|
||||
|
||||
func AddDSAPublicKeyToKeyMap(keyMap map[string]interface{}, key *dsa.PublicKey) {
|
||||
keyMap["p"] = key.P.Bytes()
|
||||
keyMap["q"] = key.Q.Bytes()
|
||||
keyMap["g"] = key.G.Bytes()
|
||||
keyMap["y"] = key.Y.Bytes()
|
||||
}
|
||||
|
||||
func (c *Certificate) MarshalJSON() ([]byte, error) {
|
||||
// Fill out the certificate
|
||||
jc := new(jsonCertificate)
|
||||
jc.Version = c.Version
|
||||
jc.SerialNumber = c.SerialNumber.String()
|
||||
jc.SignatureAlgorithm = c.SignatureAlgorithm
|
||||
jc.Issuer = c.Issuer
|
||||
jc.IssuerDN = c.Issuer.String()
|
||||
|
||||
jc.Validity.NotBefore = c.NotBefore
|
||||
jc.Validity.NotAfter = c.NotAfter
|
||||
jc.Validity.ValidityPeriod = c.ValidityPeriod
|
||||
jc.Subject = c.Subject
|
||||
jc.SubjectDN = c.Subject.String()
|
||||
jc.SubjectKeyInfo.KeyAlgorithm = c.PublicKeyAlgorithm
|
||||
|
||||
if isValidName(c.Subject.CommonName) {
|
||||
jc.Names = append(jc.Names, c.Subject.CommonName)
|
||||
}
|
||||
|
||||
for _, name := range c.DNSNames {
|
||||
if isValidName(name) {
|
||||
jc.Names = append(jc.Names, name)
|
||||
} else if !strings.Contains(name, ".") { //just a TLD
|
||||
jc.Names = append(jc.Names, name)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for _, name := range c.URIs {
|
||||
if govalidator.IsURL(name) {
|
||||
jc.Names = append(jc.Names, name)
|
||||
}
|
||||
}
|
||||
|
||||
for _, name := range c.IPAddresses {
|
||||
str := name.String()
|
||||
if govalidator.IsURL(str) {
|
||||
jc.Names = append(jc.Names, str)
|
||||
}
|
||||
}
|
||||
|
||||
jc.Names = purgeNameDuplicates(jc.Names)
|
||||
jc.Redacted = false
|
||||
for _, name := range jc.Names {
|
||||
if strings.HasPrefix(name, "?") {
|
||||
jc.Redacted = true
|
||||
}
|
||||
}
|
||||
|
||||
// Pull out the key
|
||||
keyMap := make(map[string]interface{})
|
||||
|
||||
jc.SubjectKeyInfo.SPKIFingerprint = c.SPKIFingerprint
|
||||
switch key := c.PublicKey.(type) {
|
||||
case *rsa.PublicKey:
|
||||
rsaKey := new(jsonKeys.RSAPublicKey)
|
||||
rsaKey.PublicKey = key
|
||||
jc.SubjectKeyInfo.RSAPublicKey = rsaKey
|
||||
case *dsa.PublicKey:
|
||||
AddDSAPublicKeyToKeyMap(keyMap, key)
|
||||
jc.SubjectKeyInfo.DSAPublicKey = keyMap
|
||||
case *ecdsa.PublicKey:
|
||||
AddECDSAPublicKeyToKeyMap(keyMap, key)
|
||||
jc.SubjectKeyInfo.ECDSAPublicKey = keyMap
|
||||
case *AugmentedECDSA:
|
||||
pub := key.Pub
|
||||
keyMap["pub"] = key.Raw.Bytes
|
||||
params := pub.Params()
|
||||
keyMap["p"] = params.P.Bytes()
|
||||
keyMap["n"] = params.N.Bytes()
|
||||
keyMap["b"] = params.B.Bytes()
|
||||
keyMap["gx"] = params.Gx.Bytes()
|
||||
keyMap["gy"] = params.Gy.Bytes()
|
||||
keyMap["x"] = pub.X.Bytes()
|
||||
keyMap["y"] = pub.Y.Bytes()
|
||||
keyMap["curve"] = pub.Curve.Params().Name
|
||||
keyMap["length"] = pub.Curve.Params().BitSize
|
||||
|
||||
//keyMap["asn1_oid"] = c.SignatureAlgorithmOID.String()
|
||||
|
||||
jc.SubjectKeyInfo.ECDSAPublicKey = keyMap
|
||||
}
|
||||
|
||||
jc.Extensions, jc.UnknownExtensions = c.jsonifyExtensions()
|
||||
|
||||
// TODO: Handle the fact this might not match
|
||||
jc.Signature.SignatureAlgorithm = jc.SignatureAlgorithm
|
||||
jc.Signature.Value = c.Signature
|
||||
jc.Signature.Valid = c.validSignature
|
||||
jc.Signature.SelfSigned = c.SelfSigned
|
||||
jc.FingerprintMD5 = c.FingerprintMD5
|
||||
jc.FingerprintSHA1 = c.FingerprintSHA1
|
||||
jc.FingerprintSHA256 = c.FingerprintSHA256
|
||||
jc.FingerprintNoCT = c.FingerprintNoCT
|
||||
jc.SPKISubjectFingerprint = c.SPKISubjectFingerprint
|
||||
jc.TBSCertificateFingerprint = c.TBSCertificateFingerprint
|
||||
jc.ValidationLevel = c.ValidationLevel
|
||||
|
||||
return json.Marshal(jc)
|
||||
}
|
||||
|
||||
func purgeNameDuplicates(names []string) (out []string) {
|
||||
hashset := make(map[string]bool, len(names))
|
||||
for _, name := range names {
|
||||
if _, inc := hashset[name]; !inc {
|
||||
hashset[name] = true
|
||||
}
|
||||
}
|
||||
|
||||
out = make([]string, 0, len(hashset))
|
||||
for key := range hashset {
|
||||
out = append(out, key)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func isValidName(name string) (ret bool) {
|
||||
|
||||
// Check for wildcards and redacts, ignore malformed urls
|
||||
if strings.HasPrefix(name, "?.") || strings.HasPrefix(name, "*.") {
|
||||
ret = isValidName(name[2:])
|
||||
} else {
|
||||
ret = govalidator.IsURL(name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func orMask(ip net.IP, mask net.IPMask) net.IP {
|
||||
if len(ip) == 0 || len(mask) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(ip) != net.IPv4len && len(ip) != net.IPv6len {
|
||||
return nil
|
||||
}
|
||||
if len(ip) != len(mask) {
|
||||
return nil
|
||||
}
|
||||
out := make([]byte, len(ip))
|
||||
for idx := range ip {
|
||||
out[idx] = ip[idx] | mask[idx]
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func invertMask(mask net.IPMask) net.IPMask {
|
||||
if mask == nil {
|
||||
return nil
|
||||
}
|
||||
out := make([]byte, len(mask))
|
||||
for idx := range mask {
|
||||
out[idx] = ^mask[idx]
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
type auxGeneralSubtreeIP struct {
|
||||
CIDR string `json:"cidr,omitempty"`
|
||||
Begin string `json:"begin,omitempty"`
|
||||
End string `json:"end,omitempty"`
|
||||
Mask string `json:"mask,omitempty"`
|
||||
}
|
||||
|
||||
func (g *GeneralSubtreeIP) MarshalJSON() ([]byte, error) {
|
||||
aux := auxGeneralSubtreeIP{}
|
||||
aux.CIDR = g.Data.String()
|
||||
// Check to see if the subnet is valid. An invalid subnet will return 0,0
|
||||
// from Size(). If the subnet is invalid, only output the CIDR.
|
||||
ones, bits := g.Data.Mask.Size()
|
||||
if ones == 0 && bits == 0 {
|
||||
return json.Marshal(&aux)
|
||||
}
|
||||
// The first IP in the range should be `ip & mask`.
|
||||
begin := g.Data.IP.Mask(g.Data.Mask)
|
||||
if begin != nil {
|
||||
aux.Begin = begin.String()
|
||||
}
|
||||
// The last IP (inclusive) is `ip & (^mask)`.
|
||||
inverseMask := invertMask(g.Data.Mask)
|
||||
end := orMask(g.Data.IP, inverseMask)
|
||||
if end != nil {
|
||||
aux.End = end.String()
|
||||
}
|
||||
// Output the mask as an IP, but enforce it can be formatted correctly.
|
||||
// net.IP.String() only works on byte arrays of the correct length.
|
||||
maskLen := len(g.Data.Mask)
|
||||
if maskLen == net.IPv4len || maskLen == net.IPv6len {
|
||||
maskAsIP := net.IP(g.Data.Mask)
|
||||
aux.Mask = maskAsIP.String()
|
||||
}
|
||||
return json.Marshal(&aux)
|
||||
}
|
||||
|
||||
func (g *GeneralSubtreeIP) UnmarshalJSON(b []byte) error {
|
||||
aux := auxGeneralSubtreeIP{}
|
||||
if err := json.Unmarshal(b, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
ip, ipNet, err := net.ParseCIDR(aux.CIDR)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Data.IP = ip
|
||||
g.Data.Mask = ipNet.Mask
|
||||
g.Min = 0
|
||||
g.Max = 0
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// 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
|
||||
|
||||
|
||||
func (p PublicKeyAlgorithm) String() string {
|
||||
if p >= total_key_algorithms || p < 0 {
|
||||
p = UnknownPublicKeyAlgorithm
|
||||
}
|
||||
return keyAlgorithmNames[p]
|
||||
}
|
||||
|
||||
func (c *Certificate) SignatureAlgorithmName() string {
|
||||
switch c.SignatureAlgorithm {
|
||||
case UnknownSignatureAlgorithm:
|
||||
return c.SignatureAlgorithmOID.String()
|
||||
default:
|
||||
return c.SignatureAlgorithm.String()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Certificate) PublicKeyAlgorithmName() string {
|
||||
switch c.PublicKeyAlgorithm {
|
||||
case UnknownPublicKeyAlgorithm:
|
||||
return c.PublicKeyAlgorithmOID.String()
|
||||
default:
|
||||
return c.PublicKeyAlgorithm.String()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,240 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package x509
|
||||
|
||||
// RFC 1423 describes the encryption of PEM blocks. The algorithm used to
|
||||
// generate a key from the password was derived by looking at the OpenSSL
|
||||
// implementation.
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type PEMCipher int
|
||||
|
||||
// Possible values for the EncryptPEMBlock encryption algorithm.
|
||||
const (
|
||||
_ PEMCipher = iota
|
||||
PEMCipherDES
|
||||
PEMCipher3DES
|
||||
PEMCipherAES128
|
||||
PEMCipherAES192
|
||||
PEMCipherAES256
|
||||
)
|
||||
|
||||
// rfc1423Algo holds a method for enciphering a PEM block.
|
||||
type rfc1423Algo struct {
|
||||
cipher PEMCipher
|
||||
name string
|
||||
cipherFunc func(key []byte) (cipher.Block, error)
|
||||
keySize int
|
||||
blockSize int
|
||||
}
|
||||
|
||||
// rfc1423Algos holds a slice of the possible ways to encrypt a PEM
|
||||
// block. The ivSize numbers were taken from the OpenSSL source.
|
||||
var rfc1423Algos = []rfc1423Algo{{
|
||||
cipher: PEMCipherDES,
|
||||
name: "DES-CBC",
|
||||
cipherFunc: des.NewCipher,
|
||||
keySize: 8,
|
||||
blockSize: des.BlockSize,
|
||||
}, {
|
||||
cipher: PEMCipher3DES,
|
||||
name: "DES-EDE3-CBC",
|
||||
cipherFunc: des.NewTripleDESCipher,
|
||||
keySize: 24,
|
||||
blockSize: des.BlockSize,
|
||||
}, {
|
||||
cipher: PEMCipherAES128,
|
||||
name: "AES-128-CBC",
|
||||
cipherFunc: aes.NewCipher,
|
||||
keySize: 16,
|
||||
blockSize: aes.BlockSize,
|
||||
}, {
|
||||
cipher: PEMCipherAES192,
|
||||
name: "AES-192-CBC",
|
||||
cipherFunc: aes.NewCipher,
|
||||
keySize: 24,
|
||||
blockSize: aes.BlockSize,
|
||||
}, {
|
||||
cipher: PEMCipherAES256,
|
||||
name: "AES-256-CBC",
|
||||
cipherFunc: aes.NewCipher,
|
||||
keySize: 32,
|
||||
blockSize: aes.BlockSize,
|
||||
},
|
||||
}
|
||||
|
||||
// deriveKey uses a key derivation function to stretch the password into a key
|
||||
// with the number of bits our cipher requires. This algorithm was derived from
|
||||
// the OpenSSL source.
|
||||
func (c rfc1423Algo) deriveKey(password, salt []byte) []byte {
|
||||
hash := md5.New()
|
||||
out := make([]byte, c.keySize)
|
||||
var digest []byte
|
||||
|
||||
for i := 0; i < len(out); i += len(digest) {
|
||||
hash.Reset()
|
||||
hash.Write(digest)
|
||||
hash.Write(password)
|
||||
hash.Write(salt)
|
||||
digest = hash.Sum(digest[:0])
|
||||
copy(out[i:], digest)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// IsEncryptedPEMBlock returns if the PEM block is password encrypted.
|
||||
func IsEncryptedPEMBlock(b *pem.Block) bool {
|
||||
_, ok := b.Headers["DEK-Info"]
|
||||
return ok
|
||||
}
|
||||
|
||||
// IncorrectPasswordError is returned when an incorrect password is detected.
|
||||
var IncorrectPasswordError = errors.New("x509: decryption password incorrect")
|
||||
|
||||
// DecryptPEMBlock takes a password encrypted PEM block and the password used to
|
||||
// encrypt it and returns a slice of decrypted DER encoded bytes. It inspects
|
||||
// the DEK-Info header to determine the algorithm used for decryption. If no
|
||||
// DEK-Info header is present, an error is returned. If an incorrect password
|
||||
// is detected an IncorrectPasswordError is returned. Because of deficiencies
|
||||
// in the encrypted-PEM format, it's not always possible to detect an incorrect
|
||||
// password. In these cases no error will be returned but the decrypted DER
|
||||
// bytes will be random noise.
|
||||
func DecryptPEMBlock(b *pem.Block, password []byte) ([]byte, error) {
|
||||
dek, ok := b.Headers["DEK-Info"]
|
||||
if !ok {
|
||||
return nil, errors.New("x509: no DEK-Info header in block")
|
||||
}
|
||||
|
||||
idx := strings.Index(dek, ",")
|
||||
if idx == -1 {
|
||||
return nil, errors.New("x509: malformed DEK-Info header")
|
||||
}
|
||||
|
||||
mode, hexIV := dek[:idx], dek[idx+1:]
|
||||
ciph := cipherByName(mode)
|
||||
if ciph == nil {
|
||||
return nil, errors.New("x509: unknown encryption mode")
|
||||
}
|
||||
iv, err := hex.DecodeString(hexIV)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(iv) != ciph.blockSize {
|
||||
return nil, errors.New("x509: incorrect IV size")
|
||||
}
|
||||
|
||||
// Based on the OpenSSL implementation. The salt is the first 8 bytes
|
||||
// of the initialization vector.
|
||||
key := ciph.deriveKey(password, iv[:8])
|
||||
block, err := ciph.cipherFunc(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(b.Bytes)%block.BlockSize() != 0 {
|
||||
return nil, errors.New("x509: encrypted PEM data is not a multiple of the block size")
|
||||
}
|
||||
|
||||
data := make([]byte, len(b.Bytes))
|
||||
dec := cipher.NewCBCDecrypter(block, iv)
|
||||
dec.CryptBlocks(data, b.Bytes)
|
||||
|
||||
// Blocks are padded using a scheme where the last n bytes of padding are all
|
||||
// equal to n. It can pad from 1 to blocksize bytes inclusive. See RFC 1423.
|
||||
// For example:
|
||||
// [x y z 2 2]
|
||||
// [x y 7 7 7 7 7 7 7]
|
||||
// If we detect a bad padding, we assume it is an invalid password.
|
||||
dlen := len(data)
|
||||
if dlen == 0 || dlen%ciph.blockSize != 0 {
|
||||
return nil, errors.New("x509: invalid padding")
|
||||
}
|
||||
last := int(data[dlen-1])
|
||||
if dlen < last {
|
||||
return nil, IncorrectPasswordError
|
||||
}
|
||||
if last == 0 || last > ciph.blockSize {
|
||||
return nil, IncorrectPasswordError
|
||||
}
|
||||
for _, val := range data[dlen-last:] {
|
||||
if int(val) != last {
|
||||
return nil, IncorrectPasswordError
|
||||
}
|
||||
}
|
||||
return data[:dlen-last], nil
|
||||
}
|
||||
|
||||
// EncryptPEMBlock returns a PEM block of the specified type holding the
|
||||
// given DER-encoded data encrypted with the specified algorithm and
|
||||
// password.
|
||||
func EncryptPEMBlock(rand io.Reader, blockType string, data, password []byte, alg PEMCipher) (*pem.Block, error) {
|
||||
ciph := cipherByKey(alg)
|
||||
if ciph == nil {
|
||||
return nil, errors.New("x509: unknown encryption mode")
|
||||
}
|
||||
iv := make([]byte, ciph.blockSize)
|
||||
if _, err := io.ReadFull(rand, iv); err != nil {
|
||||
return nil, errors.New("x509: cannot generate IV: " + err.Error())
|
||||
}
|
||||
// The salt is the first 8 bytes of the initialization vector,
|
||||
// matching the key derivation in DecryptPEMBlock.
|
||||
key := ciph.deriveKey(password, iv[:8])
|
||||
block, err := ciph.cipherFunc(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
enc := cipher.NewCBCEncrypter(block, iv)
|
||||
pad := ciph.blockSize - len(data)%ciph.blockSize
|
||||
encrypted := make([]byte, len(data), len(data)+pad)
|
||||
// We could save this copy by encrypting all the whole blocks in
|
||||
// the data separately, but it doesn't seem worth the additional
|
||||
// code.
|
||||
copy(encrypted, data)
|
||||
// See RFC 1423, section 1.1
|
||||
for i := 0; i < pad; i++ {
|
||||
encrypted = append(encrypted, byte(pad))
|
||||
}
|
||||
enc.CryptBlocks(encrypted, encrypted)
|
||||
|
||||
return &pem.Block{
|
||||
Type: blockType,
|
||||
Headers: map[string]string{
|
||||
"Proc-Type": "4,ENCRYPTED",
|
||||
"DEK-Info": ciph.name + "," + hex.EncodeToString(iv),
|
||||
},
|
||||
Bytes: encrypted,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func cipherByName(name string) *rfc1423Algo {
|
||||
for i := range rfc1423Algos {
|
||||
alg := &rfc1423Algos[i]
|
||||
if alg.name == name {
|
||||
return alg
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cipherByKey(key PEMCipher) *rfc1423Algo {
|
||||
for i := range rfc1423Algos {
|
||||
alg := &rfc1423Algos[i]
|
||||
if alg.cipher == key {
|
||||
return alg
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package x509
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// pkcs1PrivateKey is a structure which mirrors the PKCS#1 ASN.1 for an RSA private key.
|
||||
type pkcs1PrivateKey struct {
|
||||
Version int
|
||||
N *big.Int
|
||||
E int
|
||||
D *big.Int
|
||||
P *big.Int
|
||||
Q *big.Int
|
||||
// We ignore these values, if present, because rsa will calculate them.
|
||||
Dp *big.Int `asn1:"optional"`
|
||||
Dq *big.Int `asn1:"optional"`
|
||||
Qinv *big.Int `asn1:"optional"`
|
||||
|
||||
AdditionalPrimes []pkcs1AdditionalRSAPrime `asn1:"optional,omitempty"`
|
||||
}
|
||||
|
||||
type pkcs1AdditionalRSAPrime struct {
|
||||
Prime *big.Int
|
||||
|
||||
// We ignore these values because rsa will calculate them.
|
||||
Exp *big.Int
|
||||
Coeff *big.Int
|
||||
}
|
||||
|
||||
// pkcs1PublicKey reflects the ASN.1 structure of a PKCS#1 public key.
|
||||
type pkcs1PublicKey struct {
|
||||
N *big.Int
|
||||
E int
|
||||
}
|
||||
|
||||
// ParsePKCS1PrivateKey returns an RSA private key from its ASN.1 PKCS#1 DER encoded form.
|
||||
func ParsePKCS1PrivateKey(der []byte) (*rsa.PrivateKey, error) {
|
||||
var priv pkcs1PrivateKey
|
||||
rest, err := asn1.Unmarshal(der, &priv)
|
||||
if len(rest) > 0 {
|
||||
return nil, asn1.SyntaxError{Msg: "trailing data"}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if priv.Version > 1 {
|
||||
return nil, errors.New("x509: unsupported private key version")
|
||||
}
|
||||
|
||||
if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 {
|
||||
return nil, errors.New("x509: private key contains zero or negative value")
|
||||
}
|
||||
|
||||
key := new(rsa.PrivateKey)
|
||||
key.PublicKey = rsa.PublicKey{
|
||||
E: priv.E,
|
||||
N: priv.N,
|
||||
}
|
||||
|
||||
key.D = priv.D
|
||||
key.Primes = make([]*big.Int, 2+len(priv.AdditionalPrimes))
|
||||
key.Primes[0] = priv.P
|
||||
key.Primes[1] = priv.Q
|
||||
for i, a := range priv.AdditionalPrimes {
|
||||
if a.Prime.Sign() <= 0 {
|
||||
return nil, errors.New("x509: private key contains zero or negative prime")
|
||||
}
|
||||
key.Primes[i+2] = a.Prime
|
||||
// We ignore the other two values because rsa will calculate
|
||||
// them as needed.
|
||||
}
|
||||
|
||||
err = key.Validate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key.Precompute()
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// MarshalPKCS1PrivateKey converts a private key to ASN.1 DER encoded form.
|
||||
func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte {
|
||||
key.Precompute()
|
||||
|
||||
version := 0
|
||||
if len(key.Primes) > 2 {
|
||||
version = 1
|
||||
}
|
||||
|
||||
priv := pkcs1PrivateKey{
|
||||
Version: version,
|
||||
N: key.N,
|
||||
E: key.PublicKey.E,
|
||||
D: key.D,
|
||||
P: key.Primes[0],
|
||||
Q: key.Primes[1],
|
||||
Dp: key.Precomputed.Dp,
|
||||
Dq: key.Precomputed.Dq,
|
||||
Qinv: key.Precomputed.Qinv,
|
||||
}
|
||||
|
||||
priv.AdditionalPrimes = make([]pkcs1AdditionalRSAPrime, len(key.Precomputed.CRTValues))
|
||||
for i, values := range key.Precomputed.CRTValues {
|
||||
priv.AdditionalPrimes[i].Prime = key.Primes[2+i]
|
||||
priv.AdditionalPrimes[i].Exp = values.Exp
|
||||
priv.AdditionalPrimes[i].Coeff = values.Coeff
|
||||
}
|
||||
|
||||
b, _ := asn1.Marshal(priv)
|
||||
return b
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// 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 (
|
||||
"github.com/zmap/zcrypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,274 @@
|
|||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package pkix
|
||||
|
||||
import (
|
||||
"encoding/asn1"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type auxAttributeTypeAndValue struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (a *AttributeTypeAndValue) MarshalJSON() ([]byte, error) {
|
||||
aux := auxAttributeTypeAndValue{}
|
||||
aux.Type = a.Type.String()
|
||||
if s, ok := a.Value.(string); ok {
|
||||
aux.Value = s
|
||||
}
|
||||
return json.Marshal(&aux)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (a *AttributeTypeAndValue) UnmarshalJSON(b []byte) error {
|
||||
aux := auxAttributeTypeAndValue{}
|
||||
if err := json.Unmarshal(b, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
a.Type = nil
|
||||
if len(aux.Type) > 0 {
|
||||
parts := strings.Split(aux.Type, ".")
|
||||
for _, part := range parts {
|
||||
i, err := strconv.Atoi(part)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.Type = append(a.Type, i)
|
||||
}
|
||||
}
|
||||
a.Value = aux.Value
|
||||
return nil
|
||||
}
|
||||
|
||||
type auxOtherName struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Value []byte `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (o *OtherName) MarshalJSON() ([]byte, error) {
|
||||
aux := auxOtherName{
|
||||
ID: o.TypeID.String(),
|
||||
Value: o.Value.Bytes,
|
||||
}
|
||||
return json.Marshal(&aux)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (o *OtherName) UnmarshalJSON(b []byte) (err error) {
|
||||
aux := auxOtherName{}
|
||||
if err = json.Unmarshal(b, &aux); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Turn dot-notation back into an OID
|
||||
if len(aux.ID) == 0 {
|
||||
return errors.New("empty type ID")
|
||||
}
|
||||
parts := strings.Split(aux.ID, ".")
|
||||
o.TypeID = nil
|
||||
for _, part := range parts {
|
||||
i, err := strconv.Atoi(part)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
o.TypeID = append(o.TypeID, i)
|
||||
}
|
||||
|
||||
// Build the ASN.1 value
|
||||
o.Value = asn1.RawValue{
|
||||
Tag: 0,
|
||||
Class: asn1.ClassContextSpecific,
|
||||
IsCompound: true,
|
||||
Bytes: aux.Value,
|
||||
}
|
||||
o.Value.FullBytes, err = asn1.Marshal(o.Value)
|
||||
return
|
||||
}
|
||||
|
||||
type auxExtension struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Critical bool `json:"critical"`
|
||||
Value []byte `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (ext *Extension) MarshalJSON() ([]byte, error) {
|
||||
aux := auxExtension{
|
||||
ID: ext.Id.String(),
|
||||
Critical: ext.Critical,
|
||||
Value: ext.Value,
|
||||
}
|
||||
return json.Marshal(&aux)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (ext *Extension) UnmarshalJSON(b []byte) (err error) {
|
||||
aux := auxExtension{}
|
||||
if err = json.Unmarshal(b, &aux); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
parts := strings.Split(aux.ID, ".")
|
||||
for _, part := range parts {
|
||||
i, err := strconv.Atoi(part)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ext.Id = append(ext.Id, i)
|
||||
}
|
||||
ext.Critical = aux.Critical
|
||||
ext.Value = aux.Value
|
||||
return
|
||||
}
|
||||
|
||||
type auxName struct {
|
||||
CommonName []string `json:"common_name,omitempty"`
|
||||
SerialNumber []string `json:"serial_number,omitempty"`
|
||||
Country []string `json:"country,omitempty"`
|
||||
Locality []string `json:"locality,omitempty"`
|
||||
Province []string `json:"province,omitempty"`
|
||||
StreetAddress []string `json:"street_address,omitempty"`
|
||||
Organization []string `json:"organization,omitempty"`
|
||||
OrganizationalUnit []string `json:"organizational_unit,omitempty"`
|
||||
PostalCode []string `json:"postal_code,omitempty"`
|
||||
DomainComponent []string `json:"domain_component,omitempty"`
|
||||
EmailAddress []string `json:"email_address,omitempty"`
|
||||
GivenName []string `json:"given_name,omitempty"`
|
||||
Surname []string `json:"surname,omitempty"`
|
||||
// EV
|
||||
JurisdictionCountry []string `json:"jurisdiction_country,omitempty"`
|
||||
JurisdictionLocality []string `json:"jurisdiction_locality,omitempty"`
|
||||
JurisdictionProvince []string `json:"jurisdiction_province,omitempty"`
|
||||
|
||||
UnknownAttributes []AttributeTypeAndValue `json:"-"`
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (n *Name) MarshalJSON() ([]byte, error) {
|
||||
aux := auxName{}
|
||||
attrs := n.ToRDNSequence()
|
||||
for _, attrSet := range attrs {
|
||||
for _, a := range attrSet {
|
||||
s, ok := a.Value.(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if a.Type.Equal(oidCommonName) {
|
||||
aux.CommonName = append(aux.CommonName, s)
|
||||
} else if a.Type.Equal(oidSurname) {
|
||||
aux.Surname = append(aux.Surname, s)
|
||||
} else if a.Type.Equal(oidSerialNumber) {
|
||||
aux.SerialNumber = append(aux.SerialNumber, s)
|
||||
} else if a.Type.Equal(oidCountry) {
|
||||
aux.Country = append(aux.Country, s)
|
||||
} else if a.Type.Equal(oidLocality) {
|
||||
aux.Locality = append(aux.Locality, s)
|
||||
} else if a.Type.Equal(oidProvince) {
|
||||
aux.Province = append(aux.Province, s)
|
||||
} else if a.Type.Equal(oidStreetAddress) {
|
||||
aux.StreetAddress = append(aux.StreetAddress, s)
|
||||
} else if a.Type.Equal(oidOrganization) {
|
||||
aux.Organization = append(aux.Organization, s)
|
||||
} else if a.Type.Equal(oidGivenName) {
|
||||
aux.GivenName = append(aux.GivenName, s)
|
||||
} else if a.Type.Equal(oidOrganizationalUnit) {
|
||||
aux.OrganizationalUnit = append(aux.OrganizationalUnit, s)
|
||||
} else if a.Type.Equal(oidPostalCode) {
|
||||
aux.PostalCode = append(aux.PostalCode, s)
|
||||
} else if a.Type.Equal(oidDomainComponent) {
|
||||
aux.DomainComponent = append(aux.DomainComponent, s)
|
||||
} else if a.Type.Equal(oidDNEmailAddress) {
|
||||
aux.EmailAddress = append(aux.EmailAddress, s)
|
||||
// EV
|
||||
} else if a.Type.Equal(oidJurisdictionCountry) {
|
||||
aux.JurisdictionCountry = append(aux.JurisdictionCountry, s)
|
||||
} else if a.Type.Equal(oidJurisdictionLocality) {
|
||||
aux.JurisdictionLocality = append(aux.JurisdictionLocality, s)
|
||||
} else if a.Type.Equal(oidJurisdictionProvince) {
|
||||
aux.JurisdictionProvince = append(aux.JurisdictionProvince, s)
|
||||
} else {
|
||||
aux.UnknownAttributes = append(aux.UnknownAttributes, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
return json.Marshal(&aux)
|
||||
}
|
||||
|
||||
func appendATV(names []AttributeTypeAndValue, fieldVals []string, asn1Id asn1.ObjectIdentifier) []AttributeTypeAndValue {
|
||||
if len(fieldVals) == 0 {
|
||||
return names
|
||||
}
|
||||
|
||||
for _, val := range fieldVals {
|
||||
names = append(names, AttributeTypeAndValue{Type: asn1Id, Value: val})
|
||||
}
|
||||
|
||||
return names
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface.
|
||||
func (n *Name) UnmarshalJSON(b []byte) error {
|
||||
aux := auxName{}
|
||||
if err := json.Unmarshal(b, &aux); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Populate Names as []AttributeTypeAndValue
|
||||
n.Names = appendATV(n.Names, aux.Country, oidCountry)
|
||||
n.Names = appendATV(n.Names, aux.Organization, oidOrganization)
|
||||
n.Names = appendATV(n.Names, aux.OrganizationalUnit, oidOrganizationalUnit)
|
||||
n.Names = appendATV(n.Names, aux.Locality, oidLocality)
|
||||
n.Names = appendATV(n.Names, aux.Province, oidProvince)
|
||||
n.Names = appendATV(n.Names, aux.StreetAddress, oidStreetAddress)
|
||||
n.Names = appendATV(n.Names, aux.PostalCode, oidPostalCode)
|
||||
n.Names = appendATV(n.Names, aux.DomainComponent, oidDomainComponent)
|
||||
n.Names = appendATV(n.Names, aux.EmailAddress, oidDNEmailAddress)
|
||||
// EV
|
||||
n.Names = appendATV(n.Names, aux.JurisdictionCountry, oidJurisdictionCountry)
|
||||
n.Names = appendATV(n.Names, aux.JurisdictionLocality, oidJurisdictionLocality)
|
||||
n.Names = appendATV(n.Names, aux.JurisdictionProvince, oidJurisdictionProvince)
|
||||
|
||||
n.Names = appendATV(n.Names, aux.CommonName, oidCommonName)
|
||||
n.Names = appendATV(n.Names, aux.SerialNumber, oidSerialNumber)
|
||||
|
||||
// Populate specific fields as []string
|
||||
n.Country = aux.Country
|
||||
n.Organization = aux.Organization
|
||||
n.OrganizationalUnit = aux.OrganizationalUnit
|
||||
n.Locality = aux.Locality
|
||||
n.Province = aux.Province
|
||||
n.StreetAddress = aux.StreetAddress
|
||||
n.PostalCode = aux.PostalCode
|
||||
n.DomainComponent = aux.DomainComponent
|
||||
// EV
|
||||
n.JurisdictionCountry = aux.JurisdictionCountry
|
||||
n.JurisdictionLocality = aux.JurisdictionLocality
|
||||
n.JurisdictionProvince = aux.JurisdictionProvince
|
||||
|
||||
// CommonName and SerialNumber are not arrays.
|
||||
if len(aux.CommonName) > 0 {
|
||||
n.CommonName = aux.CommonName[0]
|
||||
}
|
||||
if len(aux.SerialNumber) > 0 {
|
||||
n.SerialNumber = aux.SerialNumber[0]
|
||||
}
|
||||
|
||||
// Add "extra" commonNames and serialNumbers to ExtraNames.
|
||||
if len(aux.CommonName) > 1 {
|
||||
n.ExtraNames = appendATV(n.ExtraNames, aux.CommonName[1:], oidCommonName)
|
||||
}
|
||||
if len(aux.SerialNumber) > 1 {
|
||||
n.ExtraNames = appendATV(n.ExtraNames, aux.SerialNumber[1:], oidSerialNumber)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
// 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 pkix
|
||||
|
||||
import (
|
||||
"encoding/asn1"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// AuxOID behaves similar to asn1.ObjectIdentifier, except encodes to JSON as a
|
||||
// string in dot notation. It is a type synonym for []int, and can be converted
|
||||
// to an asn1.ObjectIdentifier by going through []int and back.
|
||||
type AuxOID []int
|
||||
|
||||
// AsSlice returns a slice over the inner-representation
|
||||
func (aux *AuxOID) AsSlice() []int {
|
||||
return *aux
|
||||
}
|
||||
|
||||
// CopyAsSlice returns a copy of the inter-representation as a slice
|
||||
func (aux *AuxOID) CopyAsSlice() []int {
|
||||
out := make([]int, len(*aux))
|
||||
copy(out, *aux)
|
||||
return out
|
||||
}
|
||||
|
||||
// Equal tests (deep) equality of two AuxOIDs
|
||||
func (aux *AuxOID) Equal(other *AuxOID) bool {
|
||||
var a []int = *aux
|
||||
var b []int = *other
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for idx := range a {
|
||||
if a[idx] != b[idx] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface
|
||||
func (aux *AuxOID) MarshalJSON() ([]byte, error) {
|
||||
var oid asn1.ObjectIdentifier
|
||||
oid = []int(*aux)
|
||||
return json.Marshal(oid.String())
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the json.Unmarshaler interface
|
||||
func (aux *AuxOID) UnmarshalJSON(b []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(b, &s); err != nil {
|
||||
return err
|
||||
}
|
||||
parts := strings.Split(s, ".")
|
||||
if len(parts) < 1 {
|
||||
return fmt.Errorf("Invalid OID string %s", s)
|
||||
}
|
||||
slice := make([]int, len(parts))
|
||||
for idx := range parts {
|
||||
n, err := strconv.Atoi(parts[idx])
|
||||
if err != nil || n < 0 {
|
||||
return fmt.Errorf("Invalid OID integer %s", parts[idx])
|
||||
}
|
||||
slice[idx] = n
|
||||
}
|
||||
*aux = slice
|
||||
return nil
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,197 @@
|
|||
// 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 (
|
||||
"encoding/asn1"
|
||||
"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{}
|
||||
//}
|
||||
|
||||
// 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] == 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 (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
|
||||
//}
|
||||
|
||||
// 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 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: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 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"`
|
||||
}
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
// 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 (
|
||||
"encoding/asn1"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 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 `json:"type"`
|
||||
Value interface{} `json:"value"`
|
||||
}
|
||||
|
||||
// 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, DomainComponent []string
|
||||
EmailAddress []string
|
||||
SerialNumber, CommonName string
|
||||
GivenName, Surname []string
|
||||
// EV Components
|
||||
JurisdictionLocality, JurisdictionProvince, JurisdictionCountry []string
|
||||
|
||||
Names []AttributeTypeAndValue
|
||||
ExtraNames []AttributeTypeAndValue
|
||||
|
||||
// OriginalRDNS is saved if the name is populated using FillFromRDNSequence.
|
||||
// Additionally, if OriginalRDNS is non-nil, the String and ToRDNSequence
|
||||
// methods will simply use this.
|
||||
OriginalRDNS RDNSequence
|
||||
}
|
||||
|
||||
// FillFromRDNSequence populates n based on the AttributeTypeAndValueSETs in the
|
||||
// RDNSequence. It save the sequence as OriginalRDNS.
|
||||
func (n *Name) FillFromRDNSequence(rdns *RDNSequence) {
|
||||
n.OriginalRDNS = *rdns
|
||||
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 4:
|
||||
n.Surname = append(n.Surname, 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)
|
||||
case 42:
|
||||
n.GivenName = append(n.GivenName, value)
|
||||
}
|
||||
} else if t.Equal(oidDomainComponent) {
|
||||
n.DomainComponent = append(n.DomainComponent, value)
|
||||
} else if t.Equal(oidDNEmailAddress) {
|
||||
// Deprecated, see RFC 5280 Section 4.1.2.6
|
||||
n.EmailAddress = append(n.EmailAddress, value)
|
||||
} else if t.Equal(oidJurisdictionLocality) {
|
||||
n.JurisdictionLocality = append(n.JurisdictionLocality, value)
|
||||
} else if t.Equal(oidJurisdictionProvince) {
|
||||
n.JurisdictionProvince = append(n.JurisdictionProvince, value)
|
||||
} else if t.Equal(oidJurisdictionCountry) {
|
||||
n.JurisdictionCountry = append(n.JurisdictionCountry, 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}
|
||||
oidSurname = []int{2, 5, 4, 4}
|
||||
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}
|
||||
oidGivenName = []int{2, 5, 4, 42}
|
||||
oidDomainComponent = []int{0, 9, 2342, 19200300, 100, 1, 25}
|
||||
oidDNEmailAddress = []int{1, 2, 840, 113549, 1, 9, 1}
|
||||
// EV
|
||||
oidJurisdictionLocality = []int{1, 3, 6, 1, 4, 1, 311, 60, 2, 1, 1}
|
||||
oidJurisdictionProvince = []int{1, 3, 6, 1, 4, 1, 311, 60, 2, 1, 2}
|
||||
oidJurisdictionCountry = []int{1, 3, 6, 1, 4, 1, 311, 60, 2, 1, 3}
|
||||
)
|
||||
|
||||
// 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 {
|
||||
// NOTE: stdlib prevents adding if the oid is already present in n.ExtraNames
|
||||
//if len(values) == 0 || oidInAttributeTypeAndValue(oid, n.ExtraNames) {
|
||||
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)
|
||||
}
|
||||
|
||||
// String returns an RDNSequence as comma seperated list of
|
||||
// AttributeTypeAndValues in canonical form.
|
||||
func (seq RDNSequence) String() string {
|
||||
out := make([]string, 0, len(seq))
|
||||
// An RDNSequence is effectively an [][]AttributeTypeAndValue
|
||||
for _, atvSet := range seq {
|
||||
for _, atv := range atvSet {
|
||||
// Convert each individual AttributeTypeAndValue to X=Y
|
||||
attrParts := make([]string, 0, 2)
|
||||
oidString := atv.Type.String()
|
||||
oidName, ok := oidDotNotationToNames[oidString]
|
||||
if ok {
|
||||
attrParts = append(attrParts, oidName.ShortName)
|
||||
} else {
|
||||
attrParts = append(attrParts, oidString)
|
||||
}
|
||||
switch value := atv.Value.(type) {
|
||||
case string:
|
||||
attrParts = append(attrParts, value)
|
||||
case []byte:
|
||||
attrParts = append(attrParts, string(value))
|
||||
default:
|
||||
continue
|
||||
}
|
||||
attrString := strings.Join(attrParts, "=")
|
||||
out = append(out, attrString)
|
||||
}
|
||||
}
|
||||
return strings.Join(out, ", ")
|
||||
}
|
||||
|
||||
// ToRDNSequence returns OriginalRDNS is populated. Otherwise, it builds an
|
||||
// RDNSequence in canonical order.
|
||||
func (n Name) ToRDNSequence() (ret RDNSequence) {
|
||||
if n.OriginalRDNS != nil {
|
||||
return n.OriginalRDNS
|
||||
}
|
||||
if len(n.CommonName) > 0 {
|
||||
ret = n.appendRDNs(ret, []string{n.CommonName}, oidCommonName)
|
||||
}
|
||||
ret = n.appendRDNs(ret, n.OrganizationalUnit, oidOrganizationalUnit)
|
||||
ret = n.appendRDNs(ret, n.Organization, oidOrganization)
|
||||
ret = n.appendRDNs(ret, n.StreetAddress, oidStreetAddress)
|
||||
ret = n.appendRDNs(ret, n.Locality, oidLocality)
|
||||
ret = n.appendRDNs(ret, n.Province, oidProvince)
|
||||
ret = n.appendRDNs(ret, n.PostalCode, oidPostalCode)
|
||||
ret = n.appendRDNs(ret, n.Country, oidCountry)
|
||||
ret = n.appendRDNs(ret, n.DomainComponent, oidDomainComponent)
|
||||
// EV Components
|
||||
ret = n.appendRDNs(ret, n.JurisdictionLocality, oidJurisdictionLocality)
|
||||
ret = n.appendRDNs(ret, n.JurisdictionProvince, oidJurisdictionProvince)
|
||||
ret = n.appendRDNs(ret, n.JurisdictionCountry, oidJurisdictionCountry)
|
||||
if len(n.SerialNumber) > 0 {
|
||||
ret = n.appendRDNs(ret, []string{n.SerialNumber}, oidSerialNumber)
|
||||
}
|
||||
ret = append(ret, n.ExtraNames)
|
||||
return ret
|
||||
}
|
||||
|
||||
// String returns a canonical representation of a DistinguishedName
|
||||
func (n *Name) String() string {
|
||||
seq := n.ToRDNSequence()
|
||||
return seq.String()
|
||||
}
|
||||
|
||||
// OtherName represents the ASN.1 structure of the same name. See RFC
|
||||
// 5280, section 4.2.1.6.
|
||||
type OtherName struct {
|
||||
TypeID asn1.ObjectIdentifier
|
||||
Value asn1.RawValue `asn1:"explicit"`
|
||||
}
|
||||
|
||||
// EDIPartyName represents the ASN.1 structure of the same name. See RFC
|
||||
// 5280, section 4.2.1.6.
|
||||
type EDIPartyName struct {
|
||||
NameAssigner string `asn1:"tag:0,optional,explicit" json:"name_assigner,omitempty"`
|
||||
PartyName string `asn1:"tag:1,explicit" json:"party_name"`
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package x509
|
||||
|
||||
import "sync"
|
||||
|
||||
var (
|
||||
once sync.Once
|
||||
systemRoots *CertPool
|
||||
systemRootsErr error
|
||||
)
|
||||
|
||||
func systemRootsPool() *CertPool {
|
||||
once.Do(initSystemRoots)
|
||||
return systemRoots
|
||||
}
|
||||
|
||||
func initSystemRoots() {
|
||||
systemRoots, systemRootsErr = loadSystemRoots()
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
// 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
|
||||
// Use non-cgo on Darwin to prevent duplicate symbols on cgo
|
||||
|
||||
//
|
||||
///*
|
||||
//#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>
|
||||
//
|
||||
//// FetchPEMRoots_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 FetchPEMRoots_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;
|
||||
//}
|
||||
//
|
||||
//// useOldCode 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 useOldCode() {
|
||||
// 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;
|
||||
//}
|
||||
//
|
||||
//// FetchPEMRoots 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 FetchPEMRoots(CFDataRef *pemRoots, CFDataRef *untrustedPemRoots) {
|
||||
// if (useOldCode()) {
|
||||
// return FetchPEMRoots_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;
|
||||
// if (i != 0) {
|
||||
// // 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 = 1; 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;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// CFRelease(trustSettings);
|
||||
// }
|
||||
// // 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) {
|
||||
// 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 = nil
|
||||
// var untrustedData C.CFDataRef = nil
|
||||
// err := C.FetchPEMRoots(&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 untrustedData == nil {
|
||||
// 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
|
||||
//}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
// 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/x509"
|
||||
"encoding/pem"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
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, "// Created 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 {
|
||||
sn, ok := big.NewInt(0).SetString(id.serialNumber, 0) // 0x prefix selects hex
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid serial number: %q", id.serialNumber)
|
||||
}
|
||||
ski, ok := big.NewInt(0).SetString(id.subjectKeyID, 0)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid Subject Key ID: %q", id.subjectKeyID)
|
||||
}
|
||||
|
||||
for _, cert := range scerts {
|
||||
if sn.Cmp(cert.SerialNumber) != 0 {
|
||||
continue
|
||||
}
|
||||
cski := big.NewInt(0).SetBytes(cert.SubjectKeyId)
|
||||
if ski.Cmp(cski) != 0 {
|
||||
continue
|
||||
}
|
||||
certs = append(certs, cert)
|
||||
break
|
||||
}
|
||||
}
|
||||
return certs, nil
|
||||
}
|
||||
|
||||
func sysCerts() (certs []*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
|
||||
}
|
||||
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
|
||||
}
|
||||
certs = append(certs, cert)
|
||||
}
|
||||
return certs, nil
|
||||
}
|
||||
|
||||
type certID struct {
|
||||
serialNumber string
|
||||
subjectKeyID string
|
||||
}
|
||||
|
||||
// fetchCertIDs fetches IDs of iOS X509 certificates from apple.com.
|
||||
func fetchCertIDs() ([]certID, error) {
|
||||
resp, err := http.Get("https://support.apple.com/en-us/HT204132")
|
||||
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, "<section id=trusted"):]
|
||||
text = text[:strings.Index(text, "</section>")]
|
||||
|
||||
lines := strings.Split(text, "\n")
|
||||
var ids []certID
|
||||
var id certID
|
||||
for i, ln := range lines {
|
||||
if i == len(lines)-1 {
|
||||
break
|
||||
}
|
||||
const sn = "Serial Number:"
|
||||
if ln == sn {
|
||||
id.serialNumber = "0x" + strings.Replace(strings.TrimSpace(lines[i+1]), ":", "", -1)
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(ln, sn) {
|
||||
// extract hex value from parentheses.
|
||||
id.serialNumber = ln[strings.Index(ln, "(")+1 : len(ln)-1]
|
||||
continue
|
||||
}
|
||||
if strings.TrimSpace(ln) == "X509v3 Subject Key Identifier:" {
|
||||
id.subjectKeyID = "0x" + strings.Replace(strings.TrimSpace(lines[i+1]), ":", "", -1)
|
||||
ids = append(ids, id)
|
||||
id = certID{}
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
package x509
|
||||
|
||||
func loadSystemRoots() (*CertPool, error) {
|
||||
p := NewCertPool()
|
||||
p.AppendCertsFromPEM([]byte(systemRootsPEM))
|
||||
return p, nil
|
||||
}
|
||||
`
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue