From 717f444fb5270eb2f19b7ab267e9030dcef5232d Mon Sep 17 00:00:00 2001 From: kayrus Date: Mon, 18 Jul 2016 15:34:29 +0200 Subject: [PATCH 01/12] vendor: added cfssl/revoke, crypto/ocsp and crypto/pkcs12 packages --- .../cloudflare/cfssl/crypto/pkcs7/pkcs7.go | 188 +++++ .../github.com/cloudflare/cfssl/errors/doc.go | 46 ++ .../cloudflare/cfssl/errors/error.go | 420 +++++++++++ .../cloudflare/cfssl/errors/error_test.go | 338 +++++++++ .../cloudflare/cfssl/errors/http.go | 47 ++ .../cfssl/helpers/derhelpers/derhelpers.go | 42 ++ .../cloudflare/cfssl/helpers/helpers.go | 518 ++++++++++++++ .../cloudflare/cfssl/helpers/helpers_test.go | 496 +++++++++++++ .../cfssl/helpers/testdata/bundle.pem | 53 ++ .../cfssl/helpers/testdata/bundle_pkcs7.pem | 52 ++ .../testdata/bundle_with_whitespace.pem | 56 ++ .../cloudflare/cfssl/helpers/testdata/ca.pem | 27 + .../cfssl/helpers/testdata/ca_key.pem | 28 + .../cfssl/helpers/testdata/cert.der | Bin 0 -> 498 bytes .../cfssl/helpers/testdata/cert.pem | 13 + .../cfssl/helpers/testdata/cert_pkcs7.pem | 14 + .../helpers/testdata/cert_with_whitespace.pem | 15 + .../cfssl/helpers/testdata/ecdsa256.csr | 11 + .../cfssl/helpers/testdata/empty.pem | 1 + .../cfssl/helpers/testdata/empty_pkcs7.der | Bin 0 -> 41 bytes .../cfssl/helpers/testdata/empty_pkcs7.pem | 3 + .../cfssl/helpers/testdata/emptycert.pem | 2 + .../helpers/testdata/emptypasswordpkcs12.p12 | Bin 0 -> 2557 bytes .../cfssl/helpers/testdata/enc_priv_key.pem | 30 + .../helpers/testdata/messed_up_bundle.pem | 48 ++ .../helpers/testdata/messed_up_priv_key.pem | 20 + .../cfssl/helpers/testdata/messedupcert.pem | 11 + .../cfssl/helpers/testdata/multiplecerts.p12 | Bin 0 -> 3101 bytes .../cfssl/helpers/testdata/noheadercert.pem | 9 + .../cfssl/helpers/testdata/passwordpkcs12.p12 | Bin 0 -> 2557 bytes .../cfssl/helpers/testdata/priv_rsa_key.pem | 28 + .../helpers/testdata/private_ecdsa_key.pem | 5 + .../cfssl/helpers/testdata/rsa-old.csr | 19 + .../cfssl/helpers/testdata/secp256k1-key.pem | 5 + .../cfssl/helpers/testdata/test.bad.csr.pem | 15 + .../cfssl/helpers/testdata/test.csr.pem | 18 + .../helpers/testsuite/testdata/cert_csr.json | 18 + .../testsuite/testdata/initCA/ca_csr.json | 23 + .../testdata/initCA/cfssl_output.pem | 1 + .../helpers/testsuite/testing_helpers.go | 423 +++++++++++ .../helpers/testsuite/testing_helpers_test.go | 479 +++++++++++++ .../github.com/cloudflare/cfssl/log/log.go | 162 +++++ .../cloudflare/cfssl/log/log_test.go | 186 +++++ .../cloudflare/cfssl/revoke/revoke.go | 357 ++++++++++ .../cloudflare/cfssl/revoke/revoke_test.go | 231 ++++++ cmd/vendor/golang.org/x/crypto/ocsp/ocsp.go | 673 ++++++++++++++++++ .../golang.org/x/crypto/ocsp/ocsp_test.go | 584 +++++++++++++++ .../golang.org/x/crypto/pkcs12/bmp-string.go | 50 ++ .../x/crypto/pkcs12/bmp-string_test.go | 63 ++ .../golang.org/x/crypto/pkcs12/crypto.go | 131 ++++ .../golang.org/x/crypto/pkcs12/crypto_test.go | 125 ++++ .../golang.org/x/crypto/pkcs12/errors.go | 23 + .../crypto/pkcs12/internal/rc2/bench_test.go | 27 + .../x/crypto/pkcs12/internal/rc2/rc2.go | 274 +++++++ .../x/crypto/pkcs12/internal/rc2/rc2_test.go | 93 +++ cmd/vendor/golang.org/x/crypto/pkcs12/mac.go | 45 ++ .../golang.org/x/crypto/pkcs12/mac_test.go | 42 ++ .../golang.org/x/crypto/pkcs12/pbkdf.go | 170 +++++ .../golang.org/x/crypto/pkcs12/pbkdf_test.go | 34 + .../golang.org/x/crypto/pkcs12/pkcs12.go | 342 +++++++++ .../golang.org/x/crypto/pkcs12/pkcs12_test.go | 138 ++++ .../golang.org/x/crypto/pkcs12/safebags.go | 57 ++ 62 files changed, 7329 insertions(+) create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/crypto/pkcs7/pkcs7.go create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/errors/doc.go create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/errors/error.go create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/errors/error_test.go create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/errors/http.go create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/derhelpers/derhelpers.go create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/helpers.go create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/helpers_test.go create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/bundle.pem create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/bundle_pkcs7.pem create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/bundle_with_whitespace.pem create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/ca.pem create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/ca_key.pem create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/cert.der create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/cert.pem create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/cert_pkcs7.pem create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/cert_with_whitespace.pem create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/ecdsa256.csr create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/empty.pem create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/empty_pkcs7.der create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/empty_pkcs7.pem create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/emptycert.pem create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/emptypasswordpkcs12.p12 create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/enc_priv_key.pem create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/messed_up_bundle.pem create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/messed_up_priv_key.pem create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/messedupcert.pem create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/multiplecerts.p12 create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/noheadercert.pem create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/passwordpkcs12.p12 create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/priv_rsa_key.pem create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/private_ecdsa_key.pem create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/rsa-old.csr create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/secp256k1-key.pem create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/test.bad.csr.pem create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/test.csr.pem create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testsuite/testdata/cert_csr.json create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testsuite/testdata/initCA/ca_csr.json create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testsuite/testdata/initCA/cfssl_output.pem create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testsuite/testing_helpers.go create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/helpers/testsuite/testing_helpers_test.go create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/log/log.go create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/log/log_test.go create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go create mode 100644 cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke_test.go create mode 100644 cmd/vendor/golang.org/x/crypto/ocsp/ocsp.go create mode 100644 cmd/vendor/golang.org/x/crypto/ocsp/ocsp_test.go create mode 100644 cmd/vendor/golang.org/x/crypto/pkcs12/bmp-string.go create mode 100644 cmd/vendor/golang.org/x/crypto/pkcs12/bmp-string_test.go create mode 100644 cmd/vendor/golang.org/x/crypto/pkcs12/crypto.go create mode 100644 cmd/vendor/golang.org/x/crypto/pkcs12/crypto_test.go create mode 100644 cmd/vendor/golang.org/x/crypto/pkcs12/errors.go create mode 100644 cmd/vendor/golang.org/x/crypto/pkcs12/internal/rc2/bench_test.go create mode 100644 cmd/vendor/golang.org/x/crypto/pkcs12/internal/rc2/rc2.go create mode 100644 cmd/vendor/golang.org/x/crypto/pkcs12/internal/rc2/rc2_test.go create mode 100644 cmd/vendor/golang.org/x/crypto/pkcs12/mac.go create mode 100644 cmd/vendor/golang.org/x/crypto/pkcs12/mac_test.go create mode 100644 cmd/vendor/golang.org/x/crypto/pkcs12/pbkdf.go create mode 100644 cmd/vendor/golang.org/x/crypto/pkcs12/pbkdf_test.go create mode 100644 cmd/vendor/golang.org/x/crypto/pkcs12/pkcs12.go create mode 100644 cmd/vendor/golang.org/x/crypto/pkcs12/pkcs12_test.go create mode 100644 cmd/vendor/golang.org/x/crypto/pkcs12/safebags.go diff --git a/cmd/vendor/github.com/cloudflare/cfssl/crypto/pkcs7/pkcs7.go b/cmd/vendor/github.com/cloudflare/cfssl/crypto/pkcs7/pkcs7.go new file mode 100644 index 00000000000..8db547fce59 --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/crypto/pkcs7/pkcs7.go @@ -0,0 +1,188 @@ +// Package pkcs7 implements the subset of the CMS PKCS #7 datatype that is typically +// used to package certificates and CRLs. Using openssl, every certificate converted +// to PKCS #7 format from another encoding such as PEM conforms to this implementation. +// reference: https://www.openssl.org/docs/apps/crl2pkcs7.html +// +// PKCS #7 Data type, reference: https://tools.ietf.org/html/rfc2315 +// +// The full pkcs#7 cryptographic message syntax allows for cryptographic enhancements, +// for example data can be encrypted and signed and then packaged through pkcs#7 to be +// sent over a network and then verified and decrypted. It is asn1, and the type of +// PKCS #7 ContentInfo, which comprises the PKCS #7 structure, is: +// +// ContentInfo ::= SEQUENCE { +// contentType ContentType, +// content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL +// } +// +// There are 6 possible ContentTypes, data, signedData, envelopedData, +// signedAndEnvelopedData, digestedData, and encryptedData. Here signedData, Data, and encrypted +// Data are implemented, as the degenerate case of signedData without a signature is the typical +// format for transferring certificates and CRLS, and Data and encryptedData are used in PKCS #12 +// formats. +// The ContentType signedData has the form: +// +// +// signedData ::= SEQUENCE { +// version Version, +// digestAlgorithms DigestAlgorithmIdentifiers, +// contentInfo ContentInfo, +// certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL +// crls [1] IMPLICIT CertificateRevocationLists OPTIONAL, +// signerInfos SignerInfos +// } +// +// As of yet signerInfos and digestAlgorithms are not parsed, as they are not relevant to +// this system's use of PKCS #7 data. Version is an integer type, note that PKCS #7 is +// recursive, this second layer of ContentInfo is similar ignored for our degenerate +// usage. The ExtendedCertificatesAndCertificates type consists of a sequence of choices +// between PKCS #6 extended certificates and x509 certificates. Any sequence consisting +// of any number of extended certificates is not yet supported in this implementation. +// +// The ContentType Data is simply a raw octet string and is parsed directly into a Go []byte slice. +// +// The ContentType encryptedData is the most complicated and its form can be gathered by +// the go type below. It essentially contains a raw octet string of encrypted data and an +// algorithm identifier for use in decrypting this data. +package pkcs7 + +import ( + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + + cferr "github.com/cloudflare/cfssl/errors" +) + +// Types used for asn1 Unmarshaling. + +type signedData struct { + Version int + DigestAlgorithms asn1.RawValue + ContentInfo asn1.RawValue + Certificates asn1.RawValue `asn1:"optional" asn1:"tag:0"` + Crls asn1.RawValue `asn1:"optional"` + SignerInfos asn1.RawValue +} + +type initPKCS7 struct { + Raw asn1.RawContent + ContentType asn1.ObjectIdentifier + Content asn1.RawValue `asn1:"tag:0,explicit,optional"` +} + +// Object identifier strings of the three implemented PKCS7 types. +const ( + ObjIDData = "1.2.840.113549.1.7.1" + ObjIDSignedData = "1.2.840.113549.1.7.2" + ObjIDEncryptedData = "1.2.840.113549.1.7.6" +) + +// PKCS7 represents the ASN1 PKCS #7 Content type. It contains one of three +// possible types of Content objects, as denoted by the object identifier in +// the ContentInfo field, the other two being nil. SignedData +// is the degenerate SignedData Content info without signature used +// to hold certificates and crls. Data is raw bytes, and EncryptedData +// is as defined in PKCS #7 standard. +type PKCS7 struct { + Raw asn1.RawContent + ContentInfo string + Content Content +} + +// Content implements three of the six possible PKCS7 data types. Only one is non-nil. +type Content struct { + Data []byte + SignedData SignedData + EncryptedData EncryptedData +} + +// SignedData defines the typical carrier of certificates and crls. +type SignedData struct { + Raw asn1.RawContent + Version int + Certificates []*x509.Certificate + Crl *pkix.CertificateList +} + +// Data contains raw bytes. Used as a subtype in PKCS12. +type Data struct { + Bytes []byte +} + +// EncryptedData contains encrypted data. Used as a subtype in PKCS12. +type EncryptedData struct { + Raw asn1.RawContent + Version int + EncryptedContentInfo EncryptedContentInfo +} + +// EncryptedContentInfo is a subtype of PKCS7EncryptedData. +type EncryptedContentInfo struct { + Raw asn1.RawContent + ContentType asn1.ObjectIdentifier + ContentEncryptionAlgorithm pkix.AlgorithmIdentifier + EncryptedContent []byte `asn1:"tag:0,optional"` +} + +// ParsePKCS7 attempts to parse the DER encoded bytes of a +// PKCS7 structure. +func ParsePKCS7(raw []byte) (msg *PKCS7, err error) { + + var pkcs7 initPKCS7 + _, err = asn1.Unmarshal(raw, &pkcs7) + if err != nil { + return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err) + } + + msg = new(PKCS7) + msg.Raw = pkcs7.Raw + msg.ContentInfo = pkcs7.ContentType.String() + switch { + case msg.ContentInfo == ObjIDData: + msg.ContentInfo = "Data" + _, err = asn1.Unmarshal(pkcs7.Content.Bytes, &msg.Content.Data) + if err != nil { + return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err) + } + case msg.ContentInfo == ObjIDSignedData: + msg.ContentInfo = "SignedData" + var signedData signedData + _, err = asn1.Unmarshal(pkcs7.Content.Bytes, &signedData) + if err != nil { + return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err) + } + if len(signedData.Certificates.Bytes) != 0 { + msg.Content.SignedData.Certificates, err = x509.ParseCertificates(signedData.Certificates.Bytes) + if err != nil { + return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err) + } + } + if len(signedData.Crls.Bytes) != 0 { + msg.Content.SignedData.Crl, err = x509.ParseDERCRL(signedData.Crls.Bytes) + if err != nil { + return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err) + } + } + msg.Content.SignedData.Version = signedData.Version + msg.Content.SignedData.Raw = pkcs7.Content.Bytes + case msg.ContentInfo == ObjIDEncryptedData: + msg.ContentInfo = "EncryptedData" + var encryptedData EncryptedData + _, err = asn1.Unmarshal(pkcs7.Content.Bytes, &encryptedData) + if err != nil { + return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, err) + } + if encryptedData.Version != 0 { + return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, errors.New("Only support for PKCS #7 encryptedData version 0")) + } + msg.Content.EncryptedData = encryptedData + + default: + return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, errors.New("Attempt to parse PKCS# 7 Content not of type data, signed data or encrypted data")) + } + + return msg, nil + +} diff --git a/cmd/vendor/github.com/cloudflare/cfssl/errors/doc.go b/cmd/vendor/github.com/cloudflare/cfssl/errors/doc.go new file mode 100644 index 00000000000..1910e2662f9 --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/errors/doc.go @@ -0,0 +1,46 @@ +/* +Package errors provides error types returned in CF SSL. + +1. Type Error is intended for errors produced by CF SSL packages. +It formats to a json object that consists of an error message and a 4-digit code for error reasoning. + +Example: {"code":1002, "message": "Failed to decode certificate"} + +The index of codes are listed below: + 1XXX: CertificateError + 1000: Unknown + 1001: ReadFailed + 1002: DecodeFailed + 1003: ParseFailed + 1100: SelfSigned + 12XX: VerifyFailed + 121X: CertificateInvalid + 1210: NotAuthorizedToSign + 1211: Expired + 1212: CANotAuthorizedForThisName + 1213: TooManyIntermediates + 1214: IncompatibleUsage + 1220: UnknownAuthority + 2XXX: PrivatekeyError + 2000: Unknown + 2001: ReadFailed + 2002: DecodeFailed + 2003: ParseFailed + 2100: Encrypted + 2200: NotRSA + 2300: KeyMismatch + 2400: GenerationFailed + 2500: Unavailable + 3XXX: IntermediatesError + 4XXX: RootError + 5XXX: PolicyError + 5100: NoKeyUsages + 5200: InvalidPolicy + 5300: InvalidRequest + 5400: UnknownProfile + 6XXX: DialError + +2. Type HttpError is intended for CF SSL API to consume. It contains a HTTP status code that will be read and returned +by the API server. +*/ +package errors diff --git a/cmd/vendor/github.com/cloudflare/cfssl/errors/error.go b/cmd/vendor/github.com/cloudflare/cfssl/errors/error.go new file mode 100644 index 00000000000..88663b2c67e --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/errors/error.go @@ -0,0 +1,420 @@ +package errors + +import ( + "crypto/x509" + "encoding/json" + "fmt" +) + +// Error is the error type usually returned by functions in CF SSL package. +// It contains a 4-digit error code where the most significant digit +// describes the category where the error occurred and the rest 3 digits +// describe the specific error reason. +type Error struct { + ErrorCode int `json:"code"` + Message string `json:"message"` +} + +// Category is the most significant digit of the error code. +type Category int + +// Reason is the last 3 digits of the error code. +type Reason int + +const ( + // Success indicates no error occurred. + Success Category = 1000 * iota // 0XXX + + // CertificateError indicates a fault in a certificate. + CertificateError // 1XXX + + // PrivateKeyError indicates a fault in a private key. + PrivateKeyError // 2XXX + + // IntermediatesError indicates a fault in an intermediate. + IntermediatesError // 3XXX + + // RootError indicates a fault in a root. + RootError // 4XXX + + // PolicyError indicates an error arising from a malformed or + // non-existent policy, or a breach of policy. + PolicyError // 5XXX + + // DialError indicates a network fault. + DialError // 6XXX + + // APIClientError indicates a problem with the API client. + APIClientError // 7XXX + + // OCSPError indicates a problem with OCSP signing + OCSPError // 8XXX + + // CSRError indicates a problem with CSR parsing + CSRError // 9XXX + + // CTError indicates a problem with the certificate transparency process + CTError // 10XXX + + // CertStoreError indicates a problem with the certificate store + CertStoreError // 11XXX +) + +// None is a non-specified error. +const ( + None Reason = iota +) + +// Warning code for a success +const ( + BundleExpiringBit int = 1 << iota // 0x01 + BundleNotUbiquitousBit // 0x02 +) + +// Parsing errors +const ( + Unknown Reason = iota // X000 + ReadFailed // X001 + DecodeFailed // X002 + ParseFailed // X003 +) + +// The following represent certificate non-parsing errors, and must be +// specified along with CertificateError. +const ( + // SelfSigned indicates that a certificate is self-signed and + // cannot be used in the manner being attempted. + SelfSigned Reason = 100 * (iota + 1) // Code 11XX + + // VerifyFailed is an X.509 verification failure. The least two + // significant digits of 12XX is determined as the actual x509 + // error is examined. + VerifyFailed // Code 12XX + + // BadRequest indicates that the certificate request is invalid. + BadRequest // Code 13XX + + // MissingSerial indicates that the profile specified + // 'ClientProvidesSerialNumbers', but the SignRequest did not include a serial + // number. + MissingSerial // Code 14XX +) + +const ( + certificateInvalid = 10 * (iota + 1) //121X + unknownAuthority //122x +) + +// The following represent private-key non-parsing errors, and must be +// specified with PrivateKeyError. +const ( + // Encrypted indicates that the private key is a PKCS #8 encrypted + // private key. At this time, CFSSL does not support decrypting + // these keys. + Encrypted Reason = 100 * (iota + 1) //21XX + + // NotRSAOrECC indicates that they key is not an RSA or ECC + // private key; these are the only two private key types supported + // at this time by CFSSL. + NotRSAOrECC //22XX + + // KeyMismatch indicates that the private key does not match + // the public key or certificate being presented with the key. + KeyMismatch //23XX + + // GenerationFailed indicates that a private key could not + // be generated. + GenerationFailed //24XX + + // Unavailable indicates that a private key mechanism (such as + // PKCS #11) was requested but support for that mechanism is + // not available. + Unavailable +) + +// The following are policy-related non-parsing errors, and must be +// specified along with PolicyError. +const ( + // NoKeyUsages indicates that the profile does not permit any + // key usages for the certificate. + NoKeyUsages Reason = 100 * (iota + 1) // 51XX + + // InvalidPolicy indicates that policy being requested is not + // a valid policy or does not exist. + InvalidPolicy // 52XX + + // InvalidRequest indicates a certificate request violated the + // constraints of the policy being applied to the request. + InvalidRequest // 53XX + + // UnknownProfile indicates that the profile does not exist. + UnknownProfile // 54XX +) + +// The following are API client related errors, and should be +// specified with APIClientError. +const ( + // AuthenticationFailure occurs when the client is unable + // to obtain an authentication token for the request. + AuthenticationFailure Reason = 100 * (iota + 1) + + // JSONError wraps an encoding/json error. + JSONError + + // IOError wraps an io/ioutil error. + IOError + + // ClientHTTPError wraps a net/http error. + ClientHTTPError + + // ServerRequestFailed covers any other failures from the API + // client. + ServerRequestFailed +) + +// The following are OCSP related errors, and should be +// specified with OCSPError +const ( + // IssuerMismatch ocurs when the certificate in the OCSP signing + // request was not issued by the CA that this responder responds for. + IssuerMismatch Reason = 100 * (iota + 1) // 81XX + + // InvalidStatus occurs when the OCSP signing requests includes an + // invalid value for the certificate status. + InvalidStatus +) + +// Certificate transparency related errors specified with CTError +const ( + // PrecertSubmissionFailed occurs when submitting a precertificate to + // a log server fails + PrecertSubmissionFailed = 100 * (iota + 1) +) + +// Certificate persistence related errors specified with CertStoreError +const ( + // InsertionFailed occurs when a SQL insert query failes to complete. + InsertionFailed = 100 * (iota + 1) + // RecordNotFound occurs when a SQL query targeting on one unique + // record failes to update the specified row in the table. + RecordNotFound +) + +// The error interface implementation, which formats to a JSON object string. +func (e *Error) Error() string { + marshaled, err := json.Marshal(e) + if err != nil { + panic(err) + } + return string(marshaled) + +} + +// New returns an error that contains an error code and message derived from +// the given category, reason. Currently, to avoid confusion, it is not +// allowed to create an error of category Success +func New(category Category, reason Reason) *Error { + errorCode := int(category) + int(reason) + var msg string + switch category { + case OCSPError: + switch reason { + case ReadFailed: + msg = "No certificate provided" + case IssuerMismatch: + msg = "Certificate not issued by this issuer" + case InvalidStatus: + msg = "Invalid revocation status" + } + case CertificateError: + switch reason { + case Unknown: + msg = "Unknown certificate error" + case ReadFailed: + msg = "Failed to read certificate" + case DecodeFailed: + msg = "Failed to decode certificate" + case ParseFailed: + msg = "Failed to parse certificate" + case SelfSigned: + msg = "Certificate is self signed" + case VerifyFailed: + msg = "Unable to verify certificate" + case BadRequest: + msg = "Invalid certificate request" + case MissingSerial: + msg = "Missing serial number in request" + default: + panic(fmt.Sprintf("Unsupported CFSSL error reason %d under category CertificateError.", + reason)) + + } + case PrivateKeyError: + switch reason { + case Unknown: + msg = "Unknown private key error" + case ReadFailed: + msg = "Failed to read private key" + case DecodeFailed: + msg = "Failed to decode private key" + case ParseFailed: + msg = "Failed to parse private key" + case Encrypted: + msg = "Private key is encrypted." + case NotRSAOrECC: + msg = "Private key algorithm is not RSA or ECC" + case KeyMismatch: + msg = "Private key does not match public key" + case GenerationFailed: + msg = "Failed to new private key" + case Unavailable: + msg = "Private key is unavailable" + default: + panic(fmt.Sprintf("Unsupported CFSSL error reason %d under category PrivateKeyError.", + reason)) + } + case IntermediatesError: + switch reason { + case Unknown: + msg = "Unknown intermediate certificate error" + case ReadFailed: + msg = "Failed to read intermediate certificate" + case DecodeFailed: + msg = "Failed to decode intermediate certificate" + case ParseFailed: + msg = "Failed to parse intermediate certificate" + default: + panic(fmt.Sprintf("Unsupported CFSSL error reason %d under category IntermediatesError.", + reason)) + } + case RootError: + switch reason { + case Unknown: + msg = "Unknown root certificate error" + case ReadFailed: + msg = "Failed to read root certificate" + case DecodeFailed: + msg = "Failed to decode root certificate" + case ParseFailed: + msg = "Failed to parse root certificate" + default: + panic(fmt.Sprintf("Unsupported CFSSL error reason %d under category RootError.", + reason)) + } + case PolicyError: + switch reason { + case Unknown: + msg = "Unknown policy error" + case NoKeyUsages: + msg = "Invalid policy: no key usage available" + case InvalidPolicy: + msg = "Invalid or unknown policy" + case InvalidRequest: + msg = "Policy violation request" + case UnknownProfile: + msg = "Unknown policy profile" + default: + panic(fmt.Sprintf("Unsupported CFSSL error reason %d under category PolicyError.", + reason)) + } + case DialError: + switch reason { + case Unknown: + msg = "Failed to dial remote server" + default: + panic(fmt.Sprintf("Unsupported CFSSL error reason %d under category DialError.", + reason)) + } + case APIClientError: + switch reason { + case AuthenticationFailure: + msg = "API client authentication failure" + case JSONError: + msg = "API client JSON config error" + case ClientHTTPError: + msg = "API client HTTP error" + case IOError: + msg = "API client IO error" + case ServerRequestFailed: + msg = "API client error: Server request failed" + default: + panic(fmt.Sprintf("Unsupported CFSSL error reason %d under category APIClientError.", + reason)) + } + case CSRError: + switch reason { + case Unknown: + msg = "CSR parsing failed due to unknown error" + case ReadFailed: + msg = "CSR file read failed" + case ParseFailed: + msg = "CSR Parsing failed" + case DecodeFailed: + msg = "CSR Decode failed" + case BadRequest: + msg = "CSR Bad request" + default: + panic(fmt.Sprintf("Unsupported CF-SSL error reason %d under category APIClientError.", reason)) + } + case CTError: + switch reason { + case Unknown: + msg = "Certificate transparency parsing failed due to unknown error" + case PrecertSubmissionFailed: + msg = "Certificate transparency precertificate submission failed" + default: + panic(fmt.Sprintf("Unsupported CF-SSL error reason %d under category CTError.", reason)) + } + case CertStoreError: + switch reason { + case Unknown: + msg = "Certificate store action failed due to unknown error" + default: + panic(fmt.Sprintf("Unsupported CF-SSL error reason %d under category CertStoreError.", reason)) + } + + default: + panic(fmt.Sprintf("Unsupported CFSSL error type: %d.", + category)) + } + return &Error{ErrorCode: errorCode, Message: msg} +} + +// Wrap returns an error that contains the given error and an error code derived from +// the given category, reason and the error. Currently, to avoid confusion, it is not +// allowed to create an error of category Success +func Wrap(category Category, reason Reason, err error) *Error { + errorCode := int(category) + int(reason) + if err == nil { + panic("Wrap needs a supplied error to initialize.") + } + + // do not double wrap a error + switch err.(type) { + case *Error: + panic("Unable to wrap a wrapped error.") + } + + switch category { + case CertificateError: + // given VerifyFailed , report the status with more detailed status code + // for some certificate errors we care. + if reason == VerifyFailed { + switch errorType := err.(type) { + case x509.CertificateInvalidError: + errorCode += certificateInvalid + int(errorType.Reason) + case x509.UnknownAuthorityError: + errorCode += unknownAuthority + } + } + case PrivateKeyError, IntermediatesError, RootError, PolicyError, DialError, + APIClientError, CSRError, CTError, CertStoreError: + // no-op, just use the error + default: + panic(fmt.Sprintf("Unsupported CFSSL error type: %d.", + category)) + } + + return &Error{ErrorCode: errorCode, Message: err.Error()} + +} diff --git a/cmd/vendor/github.com/cloudflare/cfssl/errors/error_test.go b/cmd/vendor/github.com/cloudflare/cfssl/errors/error_test.go new file mode 100644 index 00000000000..7b04b1d98ee --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/errors/error_test.go @@ -0,0 +1,338 @@ +package errors + +import ( + "crypto/x509" + "encoding/json" + "errors" + "testing" +) + +func TestNew(t *testing.T) { + err := New(CertificateError, Unknown) + if err == nil { + t.Fatal("Error creation failed.") + } + if err.ErrorCode != int(CertificateError)+int(Unknown) { + t.Fatal("Error code construction failed.") + } + if err.Message != "Unknown certificate error" { + t.Fatal("Error message construction failed.") + } + + code := New(OCSPError, ReadFailed).ErrorCode + if code != 8001 { + t.Fatal("Improper error code") + } + + code = New(OCSPError, IssuerMismatch).ErrorCode + if code != 8100 { + t.Fatal("Improper error code") + } + + code = New(OCSPError, InvalidStatus).ErrorCode + if code != 8200 { + t.Fatal("Improper error code") + } + + code = New(CertificateError, Unknown).ErrorCode + if code != 1000 { + t.Fatal("Improper error code") + } + code = New(CertificateError, ReadFailed).ErrorCode + if code != 1001 { + t.Fatal("Improper error code") + } + code = New(CertificateError, DecodeFailed).ErrorCode + if code != 1002 { + t.Fatal("Improper error code") + } + code = New(CertificateError, ParseFailed).ErrorCode + if code != 1003 { + t.Fatal("Improper error code") + } + code = New(CertificateError, SelfSigned).ErrorCode + if code != 1100 { + t.Fatal("Improper error code") + } + code = New(CertificateError, VerifyFailed).ErrorCode + if code != 1200 { + t.Fatal("Improper error code") + } + code = New(CertificateError, BadRequest).ErrorCode + if code != 1300 { + t.Fatal("Improper error code") + } + code = New(CertificateError, MissingSerial).ErrorCode + if code != 1400 { + t.Fatal("Improper error code") + } + + code = New(PrivateKeyError, Unknown).ErrorCode + if code != 2000 { + t.Fatal("Improper error code") + } + code = New(PrivateKeyError, ReadFailed).ErrorCode + if code != 2001 { + t.Fatal("Improper error code") + } + code = New(PrivateKeyError, DecodeFailed).ErrorCode + if code != 2002 { + t.Fatal("Improper error code") + } + code = New(PrivateKeyError, ParseFailed).ErrorCode + if code != 2003 { + t.Fatal("Improper error code") + } + code = New(PrivateKeyError, Encrypted).ErrorCode + if code != 2100 { + t.Fatal("Improper error code") + } + code = New(PrivateKeyError, NotRSAOrECC).ErrorCode + if code != 2200 { + t.Fatal("Improper error code") + } + code = New(PrivateKeyError, KeyMismatch).ErrorCode + if code != 2300 { + t.Fatal("Improper error code") + } + code = New(PrivateKeyError, GenerationFailed).ErrorCode + if code != 2400 { + t.Fatal("Improper error code") + } + code = New(PrivateKeyError, Unavailable).ErrorCode + if code != 2500 { + t.Fatal("Improper error code") + } + + code = New(IntermediatesError, Unknown).ErrorCode + if code != 3000 { + t.Fatal("Improper error code") + } + code = New(IntermediatesError, ReadFailed).ErrorCode + if code != 3001 { + t.Fatal("Improper error code") + } + code = New(IntermediatesError, DecodeFailed).ErrorCode + if code != 3002 { + t.Fatal("Improper error code") + } + code = New(IntermediatesError, ParseFailed).ErrorCode + if code != 3003 { + t.Fatal("Improper error code") + } + + code = New(RootError, Unknown).ErrorCode + if code != 4000 { + t.Fatal("Improper error code") + } + code = New(RootError, ReadFailed).ErrorCode + if code != 4001 { + t.Fatal("Improper error code") + } + code = New(RootError, DecodeFailed).ErrorCode + if code != 4002 { + t.Fatal("Improper error code") + } + code = New(RootError, ParseFailed).ErrorCode + if code != 4003 { + t.Fatal("Improper error code") + } + + code = New(PolicyError, Unknown).ErrorCode + if code != 5000 { + t.Fatal("Improper error code") + } + code = New(PolicyError, NoKeyUsages).ErrorCode + if code != 5100 { + t.Fatal("Improper error code") + } + code = New(PolicyError, InvalidPolicy).ErrorCode + if code != 5200 { + t.Fatal("Improper error code") + } + code = New(PolicyError, InvalidRequest).ErrorCode + if code != 5300 { + t.Fatal("Improper error code") + } + code = New(PolicyError, UnknownProfile).ErrorCode + if code != 5400 { + t.Fatal("Improper error code") + } + + code = New(DialError, Unknown).ErrorCode + if code != 6000 { + t.Fatal("Improper error code") + } + + code = New(APIClientError, AuthenticationFailure).ErrorCode + if code != 7100 { + t.Fatal("Improper error code") + } + code = New(APIClientError, JSONError).ErrorCode + if code != 7200 { + t.Fatal("Improper error code") + } + code = New(APIClientError, ClientHTTPError).ErrorCode + if code != 7400 { + t.Fatal("Improper error code") + } + code = New(APIClientError, IOError).ErrorCode + if code != 7300 { + t.Fatal("Improper error code") + } + code = New(APIClientError, ServerRequestFailed).ErrorCode + if code != 7500 { + t.Fatal("Improper error code") + } + + code = New(CSRError, Unknown).ErrorCode + if code != 9000 { + t.Fatal("Improper error code") + } + code = New(CSRError, ReadFailed).ErrorCode + if code != 9001 { + t.Fatal("Improper error code") + } + code = New(CSRError, DecodeFailed).ErrorCode + if code != 9002 { + t.Fatal("Improper error code") + } + code = New(CSRError, ParseFailed).ErrorCode + if code != 9003 { + t.Fatal("Improper error code") + } + code = New(CSRError, KeyMismatch).ErrorCode + if code != 9300 { + t.Fatal("Improper error code") + } + code = New(CSRError, BadRequest).ErrorCode + if code != 9300 { + t.Fatal("Improper error code") + } + + code = New(CTError, Unknown).ErrorCode + if code != 10000 { + t.Fatal("Improper error code") + } + code = New(CTError, PrecertSubmissionFailed).ErrorCode + if code != 10100 { + t.Fatal("Improper error code") + } +} + +func TestWrap(t *testing.T) { + msg := "Arbitrary error message" + err := Wrap(CertificateError, Unknown, errors.New(msg)) + if err == nil { + t.Fatal("Error creation failed.") + } + if err.ErrorCode != int(CertificateError)+int(Unknown) { + t.Fatal("Error code construction failed.") + } + if err.Message != msg { + t.Fatal("Error message construction failed.") + } + + err = Wrap(CertificateError, VerifyFailed, x509.CertificateInvalidError{Reason: x509.Expired}) + if err == nil { + t.Fatal("Error creation failed.") + } + if err.ErrorCode != int(CertificateError)+int(VerifyFailed)+certificateInvalid+int(x509.Expired) { + t.Fatal("Error code construction failed.") + } + if err.Message != "x509: certificate has expired or is not yet valid" { + t.Fatal("Error message construction failed.") + } + + err = Wrap(CertificateError, VerifyFailed, x509.UnknownAuthorityError{}) + if err == nil { + t.Fatal("Error creation failed.") + } + + err = Wrap(RootError, Unknown, errors.New(msg)) + if err == nil { + t.Fatal("Error creation failed.") + } + if err.ErrorCode != int(RootError)+int(Unknown) { + t.Fatal("Error code construction failed.") + } + if err.Message != msg { + t.Fatal("Error message construction failed.") + } +} + +func TestMarshal(t *testing.T) { + msg := "Arbitrary error message" + err := Wrap(CertificateError, Unknown, errors.New(msg)) + bytes, _ := json.Marshal(err) + var received Error + json.Unmarshal(bytes, &received) + if received.ErrorCode != int(CertificateError)+int(Unknown) { + t.Fatal("Error code construction failed.") + } + if received.Message != msg { + t.Fatal("Error message construction failed.") + } +} + +func TestErrorString(t *testing.T) { + msg := "Arbitrary error message" + err := Wrap(CertificateError, Unknown, errors.New(msg)) + str := err.Error() + if str != `{"code":1000,"message":"`+msg+`"}` { + t.Fatal("Incorrect Error():", str) + } +} + +func TestHTTP(t *testing.T) { + err := NewMethodNotAllowed("GET") + if err == nil { + t.Fatal("New Mathod Check failed") + } + + err = NewBadRequest(errors.New("Bad Request")) + if err == nil { + t.Fatal("New Bad Request Check failed") + } + + if err.StatusCode != 400 { + t.Fatal("New Bad Request error code construction failed") + } + + err = NewBadRequestString("Bad Request String") + if err == nil { + t.Fatal("New Bad Request String Check failed") + } + + if err.StatusCode != 400 { + t.Fatal("New Bad Request String error code construction failed") + } + + err = NewBadRequestMissingParameter("Request Missing Parameter") + if err == nil { + t.Fatal("New Bad Request Missing Parameter Check failed") + } + + if err.StatusCode != 400 { + t.Fatal("New Bad Request Missing Parameter error code construction failed") + } + + err = NewBadRequestUnwantedParameter("Unwanted Parameter Present In Request") + if err == nil { + t.Fatal("New Bad Request Unwanted Parameter Check failed") + } + + if err.StatusCode != 400 { + t.Fatal("New Bad Request Unwanted Parameter error code construction failed") + } + +} + +func TestHTTPErrorString(t *testing.T) { + method := "GET" + err := NewMethodNotAllowed(method) + str := err.Error() + if str != `Method is not allowed:"`+method+`"` { + t.Fatal("Incorrect Error():", str) + } +} diff --git a/cmd/vendor/github.com/cloudflare/cfssl/errors/http.go b/cmd/vendor/github.com/cloudflare/cfssl/errors/http.go new file mode 100644 index 00000000000..c9c0a39c703 --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/errors/http.go @@ -0,0 +1,47 @@ +package errors + +import ( + "errors" + "net/http" +) + +// HTTPError is an augmented error with a HTTP status code. +type HTTPError struct { + StatusCode int + error +} + +// Error implements the error interface. +func (e *HTTPError) Error() string { + return e.error.Error() +} + +// NewMethodNotAllowed returns an appropriate error in the case that +// an HTTP client uses an invalid method (i.e. a GET in place of a POST) +// on an API endpoint. +func NewMethodNotAllowed(method string) *HTTPError { + return &HTTPError{http.StatusMethodNotAllowed, errors.New(`Method is not allowed:"` + method + `"`)} +} + +// NewBadRequest creates a HttpError with the given error and error code 400. +func NewBadRequest(err error) *HTTPError { + return &HTTPError{http.StatusBadRequest, err} +} + +// NewBadRequestString returns a HttpError with the supplied message +// and error code 400. +func NewBadRequestString(s string) *HTTPError { + return NewBadRequest(errors.New(s)) +} + +// NewBadRequestMissingParameter returns a 400 HttpError as a required +// parameter is missing in the HTTP request. +func NewBadRequestMissingParameter(s string) *HTTPError { + return NewBadRequestString(`Missing parameter "` + s + `"`) +} + +// NewBadRequestUnwantedParameter returns a 400 HttpError as a unnecessary +// parameter is present in the HTTP request. +func NewBadRequestUnwantedParameter(s string) *HTTPError { + return NewBadRequestString(`Unwanted parameter "` + s + `"`) +} diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/derhelpers/derhelpers.go b/cmd/vendor/github.com/cloudflare/cfssl/helpers/derhelpers/derhelpers.go new file mode 100644 index 00000000000..bcc74185085 --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/helpers/derhelpers/derhelpers.go @@ -0,0 +1,42 @@ +// Package derhelpers implements common functionality +// on DER encoded data +package derhelpers + +import ( + "crypto" + "crypto/ecdsa" + "crypto/rsa" + "crypto/x509" + + cferr "github.com/cloudflare/cfssl/errors" +) + +// ParsePrivateKeyDER parses a PKCS #1, PKCS #8, or elliptic curve +// DER-encoded private key. The key must not be in PEM format. +func ParsePrivateKeyDER(keyDER []byte) (key crypto.Signer, err error) { + generalKey, err := x509.ParsePKCS8PrivateKey(keyDER) + if err != nil { + generalKey, err = x509.ParsePKCS1PrivateKey(keyDER) + if err != nil { + generalKey, err = x509.ParseECPrivateKey(keyDER) + if err != nil { + // We don't include the actual error into + // the final error. The reason might be + // we don't want to leak any info about + // the private key. + return nil, cferr.New(cferr.PrivateKeyError, + cferr.ParseFailed) + } + } + } + + switch generalKey.(type) { + case *rsa.PrivateKey: + return generalKey.(*rsa.PrivateKey), nil + case *ecdsa.PrivateKey: + return generalKey.(*ecdsa.PrivateKey), nil + } + + // should never reach here + return nil, cferr.New(cferr.PrivateKeyError, cferr.ParseFailed) +} diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/helpers.go b/cmd/vendor/github.com/cloudflare/cfssl/helpers/helpers.go new file mode 100644 index 00000000000..48d096a9877 --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/helpers/helpers.go @@ -0,0 +1,518 @@ +// Package helpers implements utility functionality common to many +// CFSSL packages. +package helpers + +import ( + "bytes" + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "encoding/asn1" + "encoding/pem" + "errors" + "io/ioutil" + "math/big" + + "strings" + "time" + + "github.com/cloudflare/cfssl/crypto/pkcs7" + cferr "github.com/cloudflare/cfssl/errors" + "github.com/cloudflare/cfssl/helpers/derhelpers" + "github.com/cloudflare/cfssl/log" + "golang.org/x/crypto/pkcs12" +) + +// OneYear is a time.Duration representing a year's worth of seconds. +const OneYear = 8760 * time.Hour + +// OneDay is a time.Duration representing a day's worth of seconds. +const OneDay = 24 * time.Hour + +// InclusiveDate returns the time.Time representation of a date - 1 +// nanosecond. This allows time.After to be used inclusively. +func InclusiveDate(year int, month time.Month, day int) time.Time { + return time.Date(year, month, day, 0, 0, 0, 0, time.UTC).Add(-1 * time.Nanosecond) +} + +// Jul2012 is the July 2012 CAB Forum deadline for when CAs must stop +// issuing certificates valid for more than 5 years. +var Jul2012 = InclusiveDate(2012, time.July, 01) + +// Apr2015 is the April 2015 CAB Forum deadline for when CAs must stop +// issuing certificates valid for more than 39 months. +var Apr2015 = InclusiveDate(2015, time.April, 01) + +// KeyLength returns the bit size of ECDSA or RSA PublicKey +func KeyLength(key interface{}) int { + if key == nil { + return 0 + } + if ecdsaKey, ok := key.(*ecdsa.PublicKey); ok { + return ecdsaKey.Curve.Params().BitSize + } else if rsaKey, ok := key.(*rsa.PublicKey); ok { + return rsaKey.N.BitLen() + } + + return 0 +} + +// ExpiryTime returns the time when the certificate chain is expired. +func ExpiryTime(chain []*x509.Certificate) (notAfter time.Time) { + if len(chain) == 0 { + return + } + + notAfter = chain[0].NotAfter + for _, cert := range chain { + if notAfter.After(cert.NotAfter) { + notAfter = cert.NotAfter + } + } + return +} + +// MonthsValid returns the number of months for which a certificate is valid. +func MonthsValid(c *x509.Certificate) int { + issued := c.NotBefore + expiry := c.NotAfter + years := (expiry.Year() - issued.Year()) + months := years*12 + int(expiry.Month()) - int(issued.Month()) + + // Round up if valid for less than a full month + if expiry.Day() > issued.Day() { + months++ + } + return months +} + +// ValidExpiry determines if a certificate is valid for an acceptable +// length of time per the CA/Browser Forum baseline requirements. +// See https://cabforum.org/wp-content/uploads/CAB-Forum-BR-1.3.0.pdf +func ValidExpiry(c *x509.Certificate) bool { + issued := c.NotBefore + + var maxMonths int + switch { + case issued.After(Apr2015): + maxMonths = 39 + case issued.After(Jul2012): + maxMonths = 60 + case issued.Before(Jul2012): + maxMonths = 120 + } + + if MonthsValid(c) > maxMonths { + return false + } + return true +} + +// SignatureString returns the TLS signature string corresponding to +// an X509 signature algorithm. +func SignatureString(alg x509.SignatureAlgorithm) string { + switch alg { + case x509.MD2WithRSA: + return "MD2WithRSA" + case x509.MD5WithRSA: + return "MD5WithRSA" + case x509.SHA1WithRSA: + return "SHA1WithRSA" + case x509.SHA256WithRSA: + return "SHA256WithRSA" + case x509.SHA384WithRSA: + return "SHA384WithRSA" + case x509.SHA512WithRSA: + return "SHA512WithRSA" + case x509.DSAWithSHA1: + return "DSAWithSHA1" + case x509.DSAWithSHA256: + return "DSAWithSHA256" + case x509.ECDSAWithSHA1: + return "ECDSAWithSHA1" + case x509.ECDSAWithSHA256: + return "ECDSAWithSHA256" + case x509.ECDSAWithSHA384: + return "ECDSAWithSHA384" + case x509.ECDSAWithSHA512: + return "ECDSAWithSHA512" + default: + return "Unknown Signature" + } +} + +// HashAlgoString returns the hash algorithm name contains in the signature +// method. +func HashAlgoString(alg x509.SignatureAlgorithm) string { + switch alg { + case x509.MD2WithRSA: + return "MD2" + case x509.MD5WithRSA: + return "MD5" + case x509.SHA1WithRSA: + return "SHA1" + case x509.SHA256WithRSA: + return "SHA256" + case x509.SHA384WithRSA: + return "SHA384" + case x509.SHA512WithRSA: + return "SHA512" + case x509.DSAWithSHA1: + return "SHA1" + case x509.DSAWithSHA256: + return "SHA256" + case x509.ECDSAWithSHA1: + return "SHA1" + case x509.ECDSAWithSHA256: + return "SHA256" + case x509.ECDSAWithSHA384: + return "SHA384" + case x509.ECDSAWithSHA512: + return "SHA512" + default: + return "Unknown Hash Algorithm" + } +} + +// EncodeCertificatesPEM encodes a number of x509 certficates to PEM +func EncodeCertificatesPEM(certs []*x509.Certificate) []byte { + var buffer bytes.Buffer + for _, cert := range certs { + pem.Encode(&buffer, &pem.Block{ + Type: "CERTIFICATE", + Bytes: cert.Raw, + }) + } + + return buffer.Bytes() +} + +// EncodeCertificatePEM encodes a single x509 certficates to PEM +func EncodeCertificatePEM(cert *x509.Certificate) []byte { + return EncodeCertificatesPEM([]*x509.Certificate{cert}) +} + +// ParseCertificatesPEM parses a sequence of PEM-encoded certificate and returns them, +// can handle PEM encoded PKCS #7 structures. +func ParseCertificatesPEM(certsPEM []byte) ([]*x509.Certificate, error) { + var certs []*x509.Certificate + var err error + certsPEM = bytes.TrimSpace(certsPEM) + for len(certsPEM) > 0 { + var cert []*x509.Certificate + cert, certsPEM, err = ParseOneCertificateFromPEM(certsPEM) + if err != nil { + + return nil, cferr.New(cferr.CertificateError, cferr.ParseFailed) + } else if cert == nil { + break + } + + certs = append(certs, cert...) + } + if len(certsPEM) > 0 { + return nil, cferr.New(cferr.CertificateError, cferr.DecodeFailed) + } + return certs, nil +} + +// ParseCertificatesDER parses a DER encoding of a certificate object and possibly private key, +// either PKCS #7, PKCS #12, or raw x509. +func ParseCertificatesDER(certsDER []byte, password string) (certs []*x509.Certificate, key crypto.Signer, err error) { + certsDER = bytes.TrimSpace(certsDER) + pkcs7data, err := pkcs7.ParsePKCS7(certsDER) + if err != nil { + var pkcs12data interface{} + certs = make([]*x509.Certificate, 1) + pkcs12data, certs[0], err = pkcs12.Decode(certsDER, password) + if err != nil { + certs, err = x509.ParseCertificates(certsDER) + if err != nil { + return nil, nil, cferr.New(cferr.CertificateError, cferr.DecodeFailed) + } + } else { + key = pkcs12data.(crypto.Signer) + } + } else { + if pkcs7data.ContentInfo != "SignedData" { + return nil, nil, cferr.Wrap(cferr.CertificateError, cferr.DecodeFailed, errors.New("can only extract certificates from signed data content info")) + } + certs = pkcs7data.Content.SignedData.Certificates + } + if certs == nil { + return nil, key, cferr.New(cferr.CertificateError, cferr.DecodeFailed) + } + return certs, key, nil +} + +// ParseSelfSignedCertificatePEM parses a PEM-encoded certificate and check if it is self-signed. +func ParseSelfSignedCertificatePEM(certPEM []byte) (*x509.Certificate, error) { + cert, err := ParseCertificatePEM(certPEM) + if err != nil { + return nil, err + } + + if err := cert.CheckSignature(cert.SignatureAlgorithm, cert.RawTBSCertificate, cert.Signature); err != nil { + return nil, cferr.Wrap(cferr.CertificateError, cferr.VerifyFailed, err) + } + return cert, nil +} + +// ParseCertificatePEM parses and returns a PEM-encoded certificate, +// can handle PEM encoded PKCS #7 structures. +func ParseCertificatePEM(certPEM []byte) (*x509.Certificate, error) { + certPEM = bytes.TrimSpace(certPEM) + cert, rest, err := ParseOneCertificateFromPEM(certPEM) + if err != nil { + // Log the actual parsing error but throw a default parse error message. + log.Debugf("Certificate parsing error: %v", err) + return nil, cferr.New(cferr.CertificateError, cferr.ParseFailed) + } else if cert == nil { + return nil, cferr.New(cferr.CertificateError, cferr.DecodeFailed) + } else if len(rest) > 0 { + return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, errors.New("the PEM file should contain only one object")) + } else if len(cert) > 1 { + return nil, cferr.Wrap(cferr.CertificateError, cferr.ParseFailed, errors.New("the PKCS7 object in the PEM file should contain only one certificate")) + } + return cert[0], nil +} + +// ParseOneCertificateFromPEM attempts to parse one PEM encoded certificate object, +// either a raw x509 certificate or a PKCS #7 structure possibly containing +// multiple certificates, from the top of certsPEM, which itself may +// contain multiple PEM encoded certificate objects. +func ParseOneCertificateFromPEM(certsPEM []byte) ([]*x509.Certificate, []byte, error) { + + block, rest := pem.Decode(certsPEM) + if block == nil { + return nil, rest, nil + } + + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + pkcs7data, err := pkcs7.ParsePKCS7(block.Bytes) + if err != nil { + return nil, rest, err + } + if pkcs7data.ContentInfo != "SignedData" { + return nil, rest, errors.New("only PKCS #7 Signed Data Content Info supported for certificate parsing") + } + certs := pkcs7data.Content.SignedData.Certificates + if certs == nil { + return nil, rest, errors.New("PKCS #7 structure contains no certificates") + } + return certs, rest, nil + } + var certs = []*x509.Certificate{cert} + return certs, rest, nil +} + +// LoadPEMCertPool loads a pool of PEM certificates from file. +func LoadPEMCertPool(certsFile string) (*x509.CertPool, error) { + if certsFile == "" { + return nil, nil + } + pemCerts, err := ioutil.ReadFile(certsFile) + if err != nil { + return nil, err + } + + return PEMToCertPool(pemCerts) +} + +// PEMToCertPool concerts PEM certificates to a CertPool. +func PEMToCertPool(pemCerts []byte) (*x509.CertPool, error) { + if len(pemCerts) == 0 { + return nil, nil + } + + certPool := x509.NewCertPool() + if !certPool.AppendCertsFromPEM(pemCerts) { + return nil, errors.New("failed to load cert pool") + } + + return certPool, nil +} + +// ParsePrivateKeyPEM parses and returns a PEM-encoded private +// key. The private key may be either an unencrypted PKCS#8, PKCS#1, +// or elliptic private key. +func ParsePrivateKeyPEM(keyPEM []byte) (key crypto.Signer, err error) { + return ParsePrivateKeyPEMWithPassword(keyPEM, nil) +} + +// ParsePrivateKeyPEMWithPassword parses and returns a PEM-encoded private +// key. The private key may be a potentially encrypted PKCS#8, PKCS#1, +// or elliptic private key. +func ParsePrivateKeyPEMWithPassword(keyPEM []byte, password []byte) (key crypto.Signer, err error) { + keyDER, err := GetKeyDERFromPEM(keyPEM, password) + if err != nil { + return nil, err + } + + return derhelpers.ParsePrivateKeyDER(keyDER) +} + +// GetKeyDERFromPEM parses a PEM-encoded private key and returns DER-format key bytes. +func GetKeyDERFromPEM(in []byte, password []byte) ([]byte, error) { + keyDER, _ := pem.Decode(in) + if keyDER != nil { + if procType, ok := keyDER.Headers["Proc-Type"]; ok { + if strings.Contains(procType, "ENCRYPTED") { + if password != nil { + return x509.DecryptPEMBlock(keyDER, password) + } + return nil, cferr.New(cferr.PrivateKeyError, cferr.Encrypted) + } + } + return keyDER.Bytes, nil + } + + return nil, cferr.New(cferr.PrivateKeyError, cferr.DecodeFailed) +} + +// CheckSignature verifies a signature made by the key on a CSR, such +// as on the CSR itself. +func CheckSignature(csr *x509.CertificateRequest, algo x509.SignatureAlgorithm, signed, signature []byte) error { + var hashType crypto.Hash + + switch algo { + case x509.SHA1WithRSA, x509.ECDSAWithSHA1: + hashType = crypto.SHA1 + case x509.SHA256WithRSA, x509.ECDSAWithSHA256: + hashType = crypto.SHA256 + case x509.SHA384WithRSA, x509.ECDSAWithSHA384: + hashType = crypto.SHA384 + case x509.SHA512WithRSA, x509.ECDSAWithSHA512: + hashType = crypto.SHA512 + default: + return x509.ErrUnsupportedAlgorithm + } + + if !hashType.Available() { + return x509.ErrUnsupportedAlgorithm + } + h := hashType.New() + + h.Write(signed) + digest := h.Sum(nil) + + switch pub := csr.PublicKey.(type) { + case *rsa.PublicKey: + return rsa.VerifyPKCS1v15(pub, hashType, digest, signature) + case *ecdsa.PublicKey: + ecdsaSig := new(struct{ R, S *big.Int }) + if _, err := asn1.Unmarshal(signature, ecdsaSig); err != nil { + return err + } + if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 { + return errors.New("x509: ECDSA signature contained zero or negative values") + } + if !ecdsa.Verify(pub, digest, ecdsaSig.R, ecdsaSig.S) { + return errors.New("x509: ECDSA verification failure") + } + return nil + } + return x509.ErrUnsupportedAlgorithm +} + +// ParseCSR parses a PEM- or DER-encoded PKCS #10 certificate signing request. +func ParseCSR(in []byte) (csr *x509.CertificateRequest, rest []byte, err error) { + in = bytes.TrimSpace(in) + p, rest := pem.Decode(in) + if p != nil { + if p.Type != "NEW CERTIFICATE REQUEST" && p.Type != "CERTIFICATE REQUEST" { + return nil, rest, cferr.New(cferr.CSRError, cferr.BadRequest) + } + + csr, err = x509.ParseCertificateRequest(p.Bytes) + } else { + csr, err = x509.ParseCertificateRequest(in) + } + + if err != nil { + return nil, rest, err + } + + err = CheckSignature(csr, csr.SignatureAlgorithm, csr.RawTBSCertificateRequest, csr.Signature) + if err != nil { + return nil, rest, err + } + + return csr, rest, nil +} + +// ParseCSRPEM parses a PEM-encoded certificiate signing request. +// It does not check the signature. This is useful for dumping data from a CSR +// locally. +func ParseCSRPEM(csrPEM []byte) (*x509.CertificateRequest, error) { + block, _ := pem.Decode([]byte(csrPEM)) + der := block.Bytes + csrObject, err := x509.ParseCertificateRequest(der) + + if err != nil { + return nil, err + } + + return csrObject, nil +} + +// SignerAlgo returns an X.509 signature algorithm from a crypto.Signer. +func SignerAlgo(priv crypto.Signer) x509.SignatureAlgorithm { + switch pub := priv.Public().(type) { + case *rsa.PublicKey: + bitLength := pub.N.BitLen() + switch { + case bitLength >= 4096: + return x509.SHA512WithRSA + case bitLength >= 3072: + return x509.SHA384WithRSA + case bitLength >= 2048: + return x509.SHA256WithRSA + default: + return x509.SHA1WithRSA + } + case *ecdsa.PublicKey: + switch pub.Curve { + case elliptic.P521(): + return x509.ECDSAWithSHA512 + case elliptic.P384(): + return x509.ECDSAWithSHA384 + case elliptic.P256(): + return x509.ECDSAWithSHA256 + default: + return x509.ECDSAWithSHA1 + } + default: + return x509.UnknownSignatureAlgorithm + } +} + +// LoadClientCertificate load key/certificate from pem files +func LoadClientCertificate(certFile string, keyFile string) (*tls.Certificate, error) { + if certFile != "" && keyFile != "" { + cert, err := tls.LoadX509KeyPair(certFile, keyFile) + if err != nil { + log.Critical("Unable to read client certificate from file: %s or key from file: %s", certFile, keyFile) + return nil, err + } + log.Debug("Client certificate loaded ") + return &cert, nil + } + return nil, nil +} + +// CreateTLSConfig creates a tls.Config object from certs and roots +func CreateTLSConfig(remoteCAs *x509.CertPool, cert *tls.Certificate) *tls.Config { + var certs []tls.Certificate + if cert != nil { + certs = []tls.Certificate{*cert} + } + return &tls.Config{ + Certificates: certs, + RootCAs: remoteCAs, + } +} diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/helpers_test.go b/cmd/vendor/github.com/cloudflare/cfssl/helpers/helpers_test.go new file mode 100644 index 00000000000..9f1d6b1465f --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/helpers/helpers_test.go @@ -0,0 +1,496 @@ +package helpers + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "io/ioutil" + "math" + "testing" + "time" +) + +const ( + testCertFile = "testdata/cert.pem" + testCertDERFile = "testdata/cert.der" + testBundleFile = "testdata/bundle.pem" + testExtraWSCertFile = "testdata/cert_with_whitespace.pem" + testExtraWSBundleFile = "testdata/bundle_with_whitespace.pem" + testMessedUpBundleFile = "testdata/messed_up_bundle.pem" + testMessedUpCertFile = "testdata/messedupcert.pem" + testEmptyCertFile = "testdata/emptycert.pem" + testPrivateRSAKey = "testdata/priv_rsa_key.pem" + testPrivateECDSAKey = "testdata/private_ecdsa_key.pem" + testUnsupportedECDSAKey = "testdata/secp256k1-key.pem" + testMessedUpPrivateKey = "testdata/messed_up_priv_key.pem" + testEncryptedPrivateKey = "testdata/enc_priv_key.pem" + testEmptyPem = "testdata/empty.pem" + testNoHeaderCert = "testdata/noheadercert.pem" + testSinglePKCS7 = "testdata/cert_pkcs7.pem" + testEmptyPKCS7DER = "testdata/empty_pkcs7.der" // openssl crl2pkcs7 -nocrl -out empty_pkcs7.der -outform der + testEmptyPKCS7PEM = "testdata/empty_pkcs7.pem" // openssl crl2pkcs7 -nocrl -out empty_pkcs7.pem -outform pem + testMultiplePKCS7 = "testdata/bundle_pkcs7.pem" + testPKCS12EmptyPswd = "testdata/emptypasswordpkcs12.p12" + testPKCS12Passwordispassword = "testdata/passwordpkcs12.p12" + testPKCS12MultipleCerts = "testdata/multiplecerts.p12" + testCSRPEM = "testdata/test.csr.pem" + testCSRPEMBad = "testdata/test.bad.csr.pem" +) + +func TestParseCertificatesDER(t *testing.T) { + var password = []string{"password", "", ""} + for i, testFile := range []string{testPKCS12Passwordispassword, testPKCS12EmptyPswd, testCertDERFile} { + testDER, err := ioutil.ReadFile(testFile) + if err != nil { + t.Fatal(err) + } + if _, _, err := ParseCertificatesDER(testDER, password[i]); err != nil { + t.Fatal(err) + } + // Incorrect Password for PKCS12 formatted files + if _, _, err := ParseCertificatesDER(testDER, "incorrectpassword"); err == nil && i != 2 { + t.Fatal(err) + } + } + + testDER, err := ioutil.ReadFile(testEmptyPKCS7DER) + if err != nil { + t.Fatal(err) + } + // PKCS7 with no certificates + if _, _, err := ParseCertificatesDER(testDER, ""); err == nil { + t.Fatal(err) + } +} + +func TestKeyLength(t *testing.T) { + expNil := 0 + recNil := KeyLength(nil) + if expNil != recNil { + t.Fatal("KeyLength on nil did not return 0") + } + + expNonsense := 0 + inNonsense := "string?" + outNonsense := KeyLength(inNonsense) + if expNonsense != outNonsense { + t.Fatal("KeyLength malfunctioning on nonsense input") + } + + //test the ecdsa branch + ecdsaPriv, _ := ecdsa.GenerateKey(elliptic.P224(), rand.Reader) + ecdsaIn, _ := ecdsaPriv.Public().(*ecdsa.PublicKey) + expEcdsa := ecdsaIn.Curve.Params().BitSize + outEcdsa := KeyLength(ecdsaIn) + if expEcdsa != outEcdsa { + t.Fatal("KeyLength malfunctioning on ecdsa input") + } + + //test the rsa branch + rsaPriv, _ := rsa.GenerateKey(rand.Reader, 256) + rsaIn, _ := rsaPriv.Public().(*rsa.PublicKey) + expRsa := rsaIn.N.BitLen() + outRsa := KeyLength(rsaIn) + + if expRsa != outRsa { + t.Fatal("KeyLength malfunctioning on rsa input") + } +} + +func TestExpiryTime(t *testing.T) { + // nil case + var expNil time.Time + inNil := []*x509.Certificate{} + outNil := ExpiryTime(inNil) + if expNil != outNil { + t.Fatal("Expiry time is malfunctioning on empty input") + } + + //read a pem file and use that expiry date + bytes, _ := ioutil.ReadFile(testBundleFile) + certs, err := ParseCertificatesPEM(bytes) + if err != nil { + t.Fatalf("%v", err) + } + expected := time.Date(2014, time.April, 15, 0, 0, 0, 0, time.UTC) + out := ExpiryTime(certs) + if out != expected { + t.Fatalf("Expected %v, got %v", expected, out) + } +} + +func TestMonthsValid(t *testing.T) { + var cert = &x509.Certificate{ + NotBefore: time.Date(2015, time.April, 01, 0, 0, 0, 0, time.UTC), + NotAfter: time.Date(2015, time.April, 01, 0, 0, 0, 0, time.UTC), + } + + if MonthsValid(cert) != 0 { + t.Fail() + } + + cert.NotAfter = time.Date(2016, time.April, 01, 0, 0, 0, 0, time.UTC) + if MonthsValid(cert) != 12 { + t.Fail() + } + + // extra days should be rounded up to 1 month + cert.NotAfter = time.Date(2016, time.April, 02, 0, 0, 0, 0, time.UTC) + if MonthsValid(cert) != 13 { + t.Fail() + } +} + +func TestHasValidExpiry(t *testing.T) { + // Issue period > April 1, 2015 + var cert = &x509.Certificate{ + NotBefore: time.Date(2015, time.April, 01, 0, 0, 0, 0, time.UTC), + NotAfter: time.Date(2016, time.April, 01, 0, 0, 0, 0, time.UTC), + } + + if !ValidExpiry(cert) { + t.Fail() + } + + cert.NotAfter = time.Date(2019, time.April, 01, 01, 0, 0, 0, time.UTC) + if ValidExpiry(cert) { + t.Fail() + } + + // Issue period < July 1, 2012 + cert.NotBefore = time.Date(2009, time.March, 01, 0, 0, 0, 0, time.UTC) + if ValidExpiry(cert) { + t.Fail() + } + + // Issue period July 1, 2012 - April 1, 2015 + cert.NotBefore = time.Date(2012, time.July, 01, 0, 0, 0, 0, time.UTC) + cert.NotAfter = time.Date(2017, time.July, 01, 0, 0, 0, 0, time.UTC) + if !ValidExpiry(cert) { + t.Fail() + } +} + +func TestHashAlgoString(t *testing.T) { + if HashAlgoString(x509.MD2WithRSA) != "MD2" { + t.Fatal("standin") + } + if HashAlgoString(x509.MD5WithRSA) != "MD5" { + t.Fatal("standin") + } + if HashAlgoString(x509.SHA1WithRSA) != "SHA1" { + t.Fatal("standin") + } + if HashAlgoString(x509.SHA256WithRSA) != "SHA256" { + t.Fatal("standin") + } + if HashAlgoString(x509.SHA384WithRSA) != "SHA384" { + t.Fatal("standin") + } + if HashAlgoString(x509.SHA512WithRSA) != "SHA512" { + t.Fatal("standin") + } + if HashAlgoString(x509.DSAWithSHA1) != "SHA1" { + t.Fatal("standin") + } + if HashAlgoString(x509.DSAWithSHA256) != "SHA256" { + t.Fatal("standin") + } + if HashAlgoString(x509.ECDSAWithSHA1) != "SHA1" { + t.Fatal("standin") + } + if HashAlgoString(x509.ECDSAWithSHA256) != "SHA256" { + t.Fatal("standin") + } + if HashAlgoString(x509.ECDSAWithSHA384) != "SHA384" { + t.Fatal("standin") + } + if HashAlgoString(x509.ECDSAWithSHA512) != "SHA512" { + t.Fatal("standin") + } + if HashAlgoString(math.MaxInt32) != "Unknown Hash Algorithm" { + t.Fatal("standin") + } +} + +func TestSignatureString(t *testing.T) { + if SignatureString(x509.MD2WithRSA) != "MD2WithRSA" { + t.Fatal("Signature String functioning improperly") + } + if SignatureString(x509.MD5WithRSA) != "MD5WithRSA" { + t.Fatal("Signature String functioning improperly") + } + if SignatureString(x509.SHA1WithRSA) != "SHA1WithRSA" { + t.Fatal("Signature String functioning improperly") + } + if SignatureString(x509.SHA256WithRSA) != "SHA256WithRSA" { + t.Fatal("Signature String functioning improperly") + } + if SignatureString(x509.SHA384WithRSA) != "SHA384WithRSA" { + t.Fatal("Signature String functioning improperly") + } + if SignatureString(x509.SHA512WithRSA) != "SHA512WithRSA" { + t.Fatal("Signature String functioning improperly") + } + if SignatureString(x509.DSAWithSHA1) != "DSAWithSHA1" { + t.Fatal("Signature String functioning improperly") + } + if SignatureString(x509.DSAWithSHA256) != "DSAWithSHA256" { + t.Fatal("Signature String functioning improperly") + } + if SignatureString(x509.ECDSAWithSHA1) != "ECDSAWithSHA1" { + t.Fatal("Signature String functioning improperly") + } + if SignatureString(x509.ECDSAWithSHA256) != "ECDSAWithSHA256" { + t.Fatal("Signature String functioning improperly") + } + if SignatureString(x509.ECDSAWithSHA384) != "ECDSAWithSHA384" { + t.Fatal("Signature String functioning improperly") + } + if SignatureString(x509.ECDSAWithSHA512) != "ECDSAWithSHA512" { + t.Fatal("Signature String functioning improperly") + } + if SignatureString(math.MaxInt32) != "Unknown Signature" { + t.Fatal("Signature String functioning improperly") + } +} + +func TestParseCertificatePEM(t *testing.T) { + for _, testFile := range []string{testCertFile, testExtraWSCertFile, testSinglePKCS7} { + certPEM, err := ioutil.ReadFile(testFile) + if err != nil { + t.Fatal(err) + } + + if _, err := ParseCertificatePEM(certPEM); err != nil { + t.Fatal(err) + } + } + for _, testFile := range []string{testBundleFile, testMessedUpCertFile, testEmptyPKCS7PEM, testEmptyCertFile, testMultiplePKCS7} { + certPEM, err := ioutil.ReadFile(testFile) + if err != nil { + t.Fatal(err) + } + + if _, err := ParseCertificatePEM(certPEM); err == nil { + t.Fatal("Incorrect cert failed to raise error") + } + } +} + +func TestParseCertificatesPEM(t *testing.T) { + // expected cases + for _, testFile := range []string{testBundleFile, testExtraWSBundleFile, testSinglePKCS7, testMultiplePKCS7} { + bundlePEM, err := ioutil.ReadFile(testFile) + if err != nil { + t.Fatal(err) + } + + if _, err := ParseCertificatesPEM(bundlePEM); err != nil { + t.Fatal(err) + } + } + + // test failure cases + // few lines deleted, then headers removed + for _, testFile := range []string{testMessedUpBundleFile, testEmptyPKCS7PEM, testNoHeaderCert} { + bundlePEM, err := ioutil.ReadFile(testFile) + if err != nil { + t.Fatal(err) + } + + if _, err := ParseCertificatesPEM(bundlePEM); err == nil { + t.Fatal("Incorrectly-formatted file failed to produce an error") + } + } +} + +func TestSelfSignedCertificatePEM(t *testing.T) { + testPEM, _ := ioutil.ReadFile(testCertFile) + _, err := ParseSelfSignedCertificatePEM(testPEM) + if err != nil { + t.Fatalf("%v", err) + } + + // a few lines deleted from the pem file + wrongPEM, _ := ioutil.ReadFile(testMessedUpCertFile) + _, err2 := ParseSelfSignedCertificatePEM(wrongPEM) + if err2 == nil { + t.Fatal("Improper pem file failed to raise an error") + } + + // alter the signature of a valid certificate + blk, _ := pem.Decode(testPEM) + blk.Bytes[len(blk.Bytes)-10]++ // some hacking to get to the sig + alteredBytes := pem.EncodeToMemory(blk) + _, err = ParseSelfSignedCertificatePEM(alteredBytes) + if err == nil { + t.Fatal("Incorrect cert failed to produce an error") + } + +} + +func TestParsePrivateKeyPEM(t *testing.T) { + + // expected cases + testRSAPEM, _ := ioutil.ReadFile(testPrivateRSAKey) + _, err := ParsePrivateKeyPEM(testRSAPEM) + if err != nil { + t.Fatal(err) + } + + testECDSAPEM, _ := ioutil.ReadFile(testPrivateECDSAKey) + _, err = ParsePrivateKeyPEM(testECDSAPEM) + if err != nil { + t.Fatal(err) + } + + // error cases + errCases := []string{ + testMessedUpPrivateKey, // a few lines deleted + testEmptyPem, // empty file + testEncryptedPrivateKey, // encrypted key + testUnsupportedECDSAKey, // ECDSA curve not currently supported by Go standard library + } + + for _, fname := range errCases { + testPEM, _ := ioutil.ReadFile(fname) + _, err = ParsePrivateKeyPEM(testPEM) + if err == nil { + t.Fatal("Incorrect private key failed to produce an error") + } + } + +} + +// Imported from signers/local/testdata/ +const ecdsaTestCSR = "testdata/ecdsa256.csr" + +func TestParseCSRPEM(t *testing.T) { + in, err := ioutil.ReadFile(ecdsaTestCSR) + if err != nil { + t.Fatalf("%v", err) + } + + _, _, err = ParseCSR(in) + if err != nil { + t.Fatalf("%v", err) + } + + in[12]++ + _, _, err = ParseCSR(in) + if err == nil { + t.Fatalf("Expected an invalid CSR.") + } + in[12]-- +} + +func TestParseCSRPEMMore(t *testing.T) { + csrPEM, err := ioutil.ReadFile(testCSRPEM) + if err != nil { + t.Fatal(err) + } + + if _, err := ParseCSRPEM(csrPEM); err != nil { + t.Fatal(err) + } + + csrPEM, err = ioutil.ReadFile(testCSRPEMBad) + if err != nil { + t.Fatal(err) + } + + if _, err := ParseCSRPEM(csrPEM); err == nil { + t.Fatal(err) + } +} + +// Imported from signers/local/testdata/ +const rsaOldTestCSR = "testdata/rsa-old.csr" + +func TestParseOldCSR(t *testing.T) { + in, err := ioutil.ReadFile(rsaOldTestCSR) + if err != nil { + t.Fatalf("%v", err) + } + + _, _, err = ParseCSR(in) + if err != nil { + t.Fatalf("%v", err) + } +} + +// Imported from signers/local/testdata/ +const clientCertFile = "testdata/ca.pem" +const clientKeyFile = "testdata/ca_key.pem" + +func TestClientCertParams(t *testing.T) { + cert, err := LoadClientCertificate(testCertFile, testPrivateRSAKey) + if err == nil { + t.Fatal("Unmatched cert/key should generate error") + } + + cert, err = LoadClientCertificate("", "") + if err != nil || cert != nil { + t.Fatal("Certificate atempted to loaded with missing key and cert") + } + cert, err = LoadClientCertificate(clientCertFile, "") + if err != nil || cert != nil { + t.Fatal("Certificate atempted to loaded with missing key") + } + cert, err = LoadClientCertificate("", clientKeyFile) + if err != nil || cert != nil { + t.Fatal("Certificate atempted to loaded with missing cert") + } + + cert, err = LoadClientCertificate(clientCertFile, clientKeyFile) + if err != nil { + t.Fatal(err) + } + if cert == nil { + t.Fatal("cert not created") + } +} + +func TestLoadPEMCertPool(t *testing.T) { + certPool, err := PEMToCertPool([]byte{}) + if certPool != nil || err != nil { + t.Fatal("Empty file name should not generate error or a cert pool") + } + + in, err := ioutil.ReadFile(testEmptyPem) + if err != nil { + t.Fatalf("%v", err) + } + certPool, err = PEMToCertPool(in) + if certPool != nil { + t.Fatal("Empty file should not generate a cert pool") + } else if err == nil { + t.Fatal("Expected error for empty file") + } + + in, err = ioutil.ReadFile(testEmptyCertFile) + if err != nil { + t.Fatalf("%v", err) + } + certPool, err = PEMToCertPool(in) + if certPool != nil { + t.Fatal("Empty cert should not generate a cert pool") + } else if err == nil { + t.Fatal("Expected error for empty cert") + } + + in, err = ioutil.ReadFile(clientCertFile) + if err != nil { + t.Fatalf("%v", err) + } + certPool, err = PEMToCertPool(in) + if err != nil { + t.Fatalf("%v", err) + } else if certPool == nil { + t.Fatal("cert pool not created") + } +} diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/bundle.pem b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/bundle.pem new file mode 100644 index 00000000000..2f4ca682388 --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/bundle.pem @@ -0,0 +1,53 @@ +-----BEGIN CERTIFICATE----- +MIIEczCCAl2gAwIBAgIIDARj8BWNsscwCwYJKoZIhvcNAQELMIGMMQswCQYDVQQG +EwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdp +bmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZv +cm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5jb20wHhcNMTQwMzAyMDAw +MDAwWhcNMTkwNDAxMDAwMDAwWjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkNs +b3VkRmxhcmUxHDAaBgNVBAsTE1N5c3RlbXMgRW5naW5lZXJpbmcxFjAUBgNVBAcT +DVNhbiBGcmFuY2lzY28xEzARBgNVBAgTCkNhbGlmb3JuaWExHTAbBgNVBAMTFGNs +b3VkZmxhcmUtaW50ZXIuY29tMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEIVkjNJGw +f3F0XWJH7yQSVtxuoBidi5JNsQ7FhxEQcZEl3b+/1iF60TBY2Yi6KwJuA6nIE73P +IXGyfNhThw4D8CiZbackQ/ufgz2DyvxyWFDPzLr7TXeM/0wSp/imoxWeo4GIMIGF +MA4GA1UdDwEB/wQEAwIApDASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBRB ++YoiUjIm34/wBwHdJGE4Wufs/DAfBgNVHSMEGDAWgBTXXUgpaSwO9HOrQBxGqOOS +FHsHEDAfBgNVHREEGDAWghRjbG91ZGZsYXJlLWludGVyLmNvbTALBgkqhkiG9w0B +AQsDggIBACRqAC5EJEe+8ihv1WzCUMEMb7KtS0BqoNbdXE32ia66PgJSQmHcmeJd +FI1UjL0DlljTM2tc+8KxR/1/qnKiI+W/D4wFTWOY/JWFOd15q7lXuKGl+8PMkAHF +A145JCr6oZoO9G9wUwVUrbmXAbyPCOfzsEQ2+mD9F1ZpoEjzVhtGf0R+vnYrRw8j +4WCv5AIcYRAf7HZxbhMILF1bccNlqyUtdH+/MTHXpjkjJjA5KbsHBrAEfjAXkD7c +WWOay6m7mVWb3PPFmGorP6t29baEETK9ZTZSrfD9rnExjjUCftWJEn0M4Pp98DvT +br6+bg8jwtq73qdyOfNsC/Sod18UuHH7MTQA22yqAF5jIlcYtAHGlNnl+sDPZACs +369/Z9rOL9vPFL+Z3F/uJtqZzvN1QiCkj8jWzR0u9fh3eQwZADM2RwgwS4Gs2Ygh +PsypDo33sFOwfX93KqKBsTHssn8SSDDaSnZ8bu1ATEdshbVieecuQx40UadPuJpw +EPVqTR5AhviXQ9bKrTnU5T7EgkW9vNydkpLQQlMg3QE8hsndv4loGZbZGfNtqQHS +/mg1t07S+7OEa4YaMW+wVOBOqTdW7OXlZFLfCcF5SYLM0SnlTMklRMxiqI4JqZXH +0thnUGD0JjfLX4rTaZUzT3lrXXWzpS2jzutXQkjGv4nhGGprIDuT +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEkDCCA/ugAwIBAgIIWnP9jF/2nogwCwYJKoZIhvcNAQELMH0xCzAJBgNVBAYT +AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2Nv +MRMwEQYDVQQKDApDbG91ZEZsYXJlMRQwEgYDVQQLDAtERVZfVEVTVElORzEWMBQG +A1UEAwwNQ0ZTU0xfVEVTVF9DQTAeFw0xNDAzMDEwMDAwMDBaFw0xNDA0MTUwMDAw +MDBaMIGMMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UE +CxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzET +MBEGA1UECBMKQ2FsaWZvcm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5j +b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDlCnV+vj0sVPy8SqHL +AlI+xwnPhWgzj2VevD6Nz1Zu1BeQ5m5y4CWCf+GmRGTP7+a/C510Fw6rpmInB0Ng +xxwQ2rC08fJtCnijlGH/VjEPIHY5lRaAomcM8Rgx6JOuv9BpZJKpr9pyUMV53JeW +RbWuLH5nEMdyk9NpetS2gWxt4/D20QlhK/tHkROrcLmEUddwIGdwE8JzI88c77Fu +u6pgMtHKvl4GGH0yvb4T7PvCdH8V2tCH7bt8roXd9MSyFVy7uORkfouip7EsVREU +mlcY5EvpR141KXbZqiOQiusJ+u76mEUQNk8wCR1/CW/ii9v1BKOVjXwCfEtIXjg0 +APJx1VNSSH6XoDpUETL+eQ4J0FL9XNbsDuYar7+zD0N1/5vSo3HLNRQR9f0lbsys +sWBEN+CxK19xyPumr21Z0bU0f1B5H52VSF0q3I1Ju9wRo994a7YipdGcmZ2lChmT +7r3mzlBTYl3poU26q34v8wG9U7Jv4fsZJ+RGebDI+TR3QG6Yod06l9oEYZxWXBY7 +STOs8wuTu3huSnan/IpWnV017Vsc61D5G+QrqcxZdXckt3anZKCF75JpUnJ7vuow +TmmHlb8KIMa9mOvcuGX4P6mz8gTi2arl/aL27kj9Q0Jgv/y1ebe2Bx2P9TF6+VND +DL3J/vSVlFeqLt2reAIBKnytLwIDAQABo4GIMIGFMA4GA1UdDwEB/wQEAwIApDAS +BgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBTXXUgpaSwO9HOrQBxGqOOSFHsH +EDAfBgNVHSMEGDAWgBS4Xu+uZ1C31vMH5Wq+VbNnOg2SPjAfBgNVHREEGDAWghRj +bG91ZGZsYXJlLWludGVyLmNvbTALBgkqhkiG9w0BAQsDgYEAfPLKCAHnPzgMYLX/ +fWznVvOEFAAYZByPFx4QdMBbDZUtxHyvJIBs6PdxrdSuDwSiMqE7qQIi+jzzwGl9 +fC7vf45B2zCX0OW51QL2oWNBdKlGgB+b2pwyME82lX/Pr7V1GY10u+ep1xdZDnch +DaMsXjQQTJu0iuG3qKEuCmUwOmc= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/bundle_pkcs7.pem b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/bundle_pkcs7.pem new file mode 100644 index 00000000000..85a41605fb6 --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/bundle_pkcs7.pem @@ -0,0 +1,52 @@ +-----BEGIN PKCS7----- +MIIJOAYJKoZIhvcNAQcCoIIJKTCCCSUCAQExADALBgkqhkiG9w0BBwGgggkLMIIE +czCCAl2gAwIBAgIIDARj8BWNsscwCwYJKoZIhvcNAQELMIGMMQswCQYDVQQGEwJV +UzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdpbmVl +cmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZvcm5p +YTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5jb20wHhcNMTQwMzAyMDAwMDAw +WhcNMTkwNDAxMDAwMDAwWjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkNsb3Vk +RmxhcmUxHDAaBgNVBAsTE1N5c3RlbXMgRW5naW5lZXJpbmcxFjAUBgNVBAcTDVNh +biBGcmFuY2lzY28xEzARBgNVBAgTCkNhbGlmb3JuaWExHTAbBgNVBAMTFGNsb3Vk +ZmxhcmUtaW50ZXIuY29tMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEIVkjNJGwf3F0 +XWJH7yQSVtxuoBidi5JNsQ7FhxEQcZEl3b+/1iF60TBY2Yi6KwJuA6nIE73PIXGy +fNhThw4D8CiZbackQ/ufgz2DyvxyWFDPzLr7TXeM/0wSp/imoxWeo4GIMIGFMA4G +A1UdDwEB/wQEAwIApDASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBRB+Yoi +UjIm34/wBwHdJGE4Wufs/DAfBgNVHSMEGDAWgBTXXUgpaSwO9HOrQBxGqOOSFHsH +EDAfBgNVHREEGDAWghRjbG91ZGZsYXJlLWludGVyLmNvbTALBgkqhkiG9w0BAQsD +ggIBACRqAC5EJEe+8ihv1WzCUMEMb7KtS0BqoNbdXE32ia66PgJSQmHcmeJdFI1U +jL0DlljTM2tc+8KxR/1/qnKiI+W/D4wFTWOY/JWFOd15q7lXuKGl+8PMkAHFA145 +JCr6oZoO9G9wUwVUrbmXAbyPCOfzsEQ2+mD9F1ZpoEjzVhtGf0R+vnYrRw8j4WCv +5AIcYRAf7HZxbhMILF1bccNlqyUtdH+/MTHXpjkjJjA5KbsHBrAEfjAXkD7cWWOa +y6m7mVWb3PPFmGorP6t29baEETK9ZTZSrfD9rnExjjUCftWJEn0M4Pp98DvTbr6+ +bg8jwtq73qdyOfNsC/Sod18UuHH7MTQA22yqAF5jIlcYtAHGlNnl+sDPZACs369/ +Z9rOL9vPFL+Z3F/uJtqZzvN1QiCkj8jWzR0u9fh3eQwZADM2RwgwS4Gs2YghPsyp +Do33sFOwfX93KqKBsTHssn8SSDDaSnZ8bu1ATEdshbVieecuQx40UadPuJpwEPVq +TR5AhviXQ9bKrTnU5T7EgkW9vNydkpLQQlMg3QE8hsndv4loGZbZGfNtqQHS/mg1 +t07S+7OEa4YaMW+wVOBOqTdW7OXlZFLfCcF5SYLM0SnlTMklRMxiqI4JqZXH0thn +UGD0JjfLX4rTaZUzT3lrXXWzpS2jzutXQkjGv4nhGGprIDuTMIIEkDCCA/ugAwIB +AgIIWnP9jF/2nogwCwYJKoZIhvcNAQELMH0xCzAJBgNVBAYTAlVTMRMwEQYDVQQI +DApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRMwEQYDVQQKDApD +bG91ZEZsYXJlMRQwEgYDVQQLDAtERVZfVEVTVElORzEWMBQGA1UEAwwNQ0ZTU0xf +VEVTVF9DQTAeFw0xNDAzMDEwMDAwMDBaFw0xNDA0MTUwMDAwMDBaMIGMMQswCQYD +VQQGEwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBF +bmdpbmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2Fs +aWZvcm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5jb20wggIiMA0GCSqG +SIb3DQEBAQUAA4ICDwAwggIKAoICAQDlCnV+vj0sVPy8SqHLAlI+xwnPhWgzj2Ve +vD6Nz1Zu1BeQ5m5y4CWCf+GmRGTP7+a/C510Fw6rpmInB0NgxxwQ2rC08fJtCnij +lGH/VjEPIHY5lRaAomcM8Rgx6JOuv9BpZJKpr9pyUMV53JeWRbWuLH5nEMdyk9Np +etS2gWxt4/D20QlhK/tHkROrcLmEUddwIGdwE8JzI88c77Fuu6pgMtHKvl4GGH0y +vb4T7PvCdH8V2tCH7bt8roXd9MSyFVy7uORkfouip7EsVREUmlcY5EvpR141KXbZ +qiOQiusJ+u76mEUQNk8wCR1/CW/ii9v1BKOVjXwCfEtIXjg0APJx1VNSSH6XoDpU +ETL+eQ4J0FL9XNbsDuYar7+zD0N1/5vSo3HLNRQR9f0lbsyssWBEN+CxK19xyPum +r21Z0bU0f1B5H52VSF0q3I1Ju9wRo994a7YipdGcmZ2lChmT7r3mzlBTYl3poU26 +q34v8wG9U7Jv4fsZJ+RGebDI+TR3QG6Yod06l9oEYZxWXBY7STOs8wuTu3huSnan +/IpWnV017Vsc61D5G+QrqcxZdXckt3anZKCF75JpUnJ7vuowTmmHlb8KIMa9mOvc +uGX4P6mz8gTi2arl/aL27kj9Q0Jgv/y1ebe2Bx2P9TF6+VNDDL3J/vSVlFeqLt2r +eAIBKnytLwIDAQABo4GIMIGFMA4GA1UdDwEB/wQEAwIApDASBgNVHRMBAf8ECDAG +AQH/AgEBMB0GA1UdDgQWBBTXXUgpaSwO9HOrQBxGqOOSFHsHEDAfBgNVHSMEGDAW +gBS4Xu+uZ1C31vMH5Wq+VbNnOg2SPjAfBgNVHREEGDAWghRjbG91ZGZsYXJlLWlu +dGVyLmNvbTALBgkqhkiG9w0BAQsDgYEAfPLKCAHnPzgMYLX/fWznVvOEFAAYZByP +Fx4QdMBbDZUtxHyvJIBs6PdxrdSuDwSiMqE7qQIi+jzzwGl9fC7vf45B2zCX0OW5 +1QL2oWNBdKlGgB+b2pwyME82lX/Pr7V1GY10u+ep1xdZDnchDaMsXjQQTJu0iuG3 +qKEuCmUwOmehADEA +-----END PKCS7----- diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/bundle_with_whitespace.pem b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/bundle_with_whitespace.pem new file mode 100644 index 00000000000..dce0d8f6f27 --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/bundle_with_whitespace.pem @@ -0,0 +1,56 @@ + +-----BEGIN CERTIFICATE----- +MIIEczCCAl2gAwIBAgIIDARj8BWNsscwCwYJKoZIhvcNAQELMIGMMQswCQYDVQQG +EwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdp +bmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZv +cm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5jb20wHhcNMTQwMzAyMDAw +MDAwWhcNMTkwNDAxMDAwMDAwWjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkNs +b3VkRmxhcmUxHDAaBgNVBAsTE1N5c3RlbXMgRW5naW5lZXJpbmcxFjAUBgNVBAcT +DVNhbiBGcmFuY2lzY28xEzARBgNVBAgTCkNhbGlmb3JuaWExHTAbBgNVBAMTFGNs +b3VkZmxhcmUtaW50ZXIuY29tMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEIVkjNJGw +f3F0XWJH7yQSVtxuoBidi5JNsQ7FhxEQcZEl3b+/1iF60TBY2Yi6KwJuA6nIE73P +IXGyfNhThw4D8CiZbackQ/ufgz2DyvxyWFDPzLr7TXeM/0wSp/imoxWeo4GIMIGF +MA4GA1UdDwEB/wQEAwIApDASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBRB ++YoiUjIm34/wBwHdJGE4Wufs/DAfBgNVHSMEGDAWgBTXXUgpaSwO9HOrQBxGqOOS +FHsHEDAfBgNVHREEGDAWghRjbG91ZGZsYXJlLWludGVyLmNvbTALBgkqhkiG9w0B +AQsDggIBACRqAC5EJEe+8ihv1WzCUMEMb7KtS0BqoNbdXE32ia66PgJSQmHcmeJd +FI1UjL0DlljTM2tc+8KxR/1/qnKiI+W/D4wFTWOY/JWFOd15q7lXuKGl+8PMkAHF +A145JCr6oZoO9G9wUwVUrbmXAbyPCOfzsEQ2+mD9F1ZpoEjzVhtGf0R+vnYrRw8j +4WCv5AIcYRAf7HZxbhMILF1bccNlqyUtdH+/MTHXpjkjJjA5KbsHBrAEfjAXkD7c +WWOay6m7mVWb3PPFmGorP6t29baEETK9ZTZSrfD9rnExjjUCftWJEn0M4Pp98DvT +br6+bg8jwtq73qdyOfNsC/Sod18UuHH7MTQA22yqAF5jIlcYtAHGlNnl+sDPZACs +369/Z9rOL9vPFL+Z3F/uJtqZzvN1QiCkj8jWzR0u9fh3eQwZADM2RwgwS4Gs2Ygh +PsypDo33sFOwfX93KqKBsTHssn8SSDDaSnZ8bu1ATEdshbVieecuQx40UadPuJpw +EPVqTR5AhviXQ9bKrTnU5T7EgkW9vNydkpLQQlMg3QE8hsndv4loGZbZGfNtqQHS +/mg1t07S+7OEa4YaMW+wVOBOqTdW7OXlZFLfCcF5SYLM0SnlTMklRMxiqI4JqZXH +0thnUGD0JjfLX4rTaZUzT3lrXXWzpS2jzutXQkjGv4nhGGprIDuT +-----END CERTIFICATE----- + +-----BEGIN CERTIFICATE----- +MIIEkDCCA/ugAwIBAgIIWnP9jF/2nogwCwYJKoZIhvcNAQELMH0xCzAJBgNVBAYT +AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2Nv +MRMwEQYDVQQKDApDbG91ZEZsYXJlMRQwEgYDVQQLDAtERVZfVEVTVElORzEWMBQG +A1UEAwwNQ0ZTU0xfVEVTVF9DQTAeFw0xNDAzMDEwMDAwMDBaFw0xNDA0MTUwMDAw +MDBaMIGMMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UE +CxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzET +MBEGA1UECBMKQ2FsaWZvcm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5j +b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDlCnV+vj0sVPy8SqHL +AlI+xwnPhWgzj2VevD6Nz1Zu1BeQ5m5y4CWCf+GmRGTP7+a/C510Fw6rpmInB0Ng +xxwQ2rC08fJtCnijlGH/VjEPIHY5lRaAomcM8Rgx6JOuv9BpZJKpr9pyUMV53JeW +RbWuLH5nEMdyk9NpetS2gWxt4/D20QlhK/tHkROrcLmEUddwIGdwE8JzI88c77Fu +u6pgMtHKvl4GGH0yvb4T7PvCdH8V2tCH7bt8roXd9MSyFVy7uORkfouip7EsVREU +mlcY5EvpR141KXbZqiOQiusJ+u76mEUQNk8wCR1/CW/ii9v1BKOVjXwCfEtIXjg0 +APJx1VNSSH6XoDpUETL+eQ4J0FL9XNbsDuYar7+zD0N1/5vSo3HLNRQR9f0lbsys +sWBEN+CxK19xyPumr21Z0bU0f1B5H52VSF0q3I1Ju9wRo994a7YipdGcmZ2lChmT +7r3mzlBTYl3poU26q34v8wG9U7Jv4fsZJ+RGebDI+TR3QG6Yod06l9oEYZxWXBY7 +STOs8wuTu3huSnan/IpWnV017Vsc61D5G+QrqcxZdXckt3anZKCF75JpUnJ7vuow +TmmHlb8KIMa9mOvcuGX4P6mz8gTi2arl/aL27kj9Q0Jgv/y1ebe2Bx2P9TF6+VND +DL3J/vSVlFeqLt2reAIBKnytLwIDAQABo4GIMIGFMA4GA1UdDwEB/wQEAwIApDAS +BgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBTXXUgpaSwO9HOrQBxGqOOSFHsH +EDAfBgNVHSMEGDAWgBS4Xu+uZ1C31vMH5Wq+VbNnOg2SPjAfBgNVHREEGDAWghRj +bG91ZGZsYXJlLWludGVyLmNvbTALBgkqhkiG9w0BAQsDgYEAfPLKCAHnPzgMYLX/ +fWznVvOEFAAYZByPFx4QdMBbDZUtxHyvJIBs6PdxrdSuDwSiMqE7qQIi+jzzwGl9 +fC7vf45B2zCX0OW51QL2oWNBdKlGgB+b2pwyME82lX/Pr7V1GY10u+ep1xdZDnch +DaMsXjQQTJu0iuG3qKEuCmUwOmc= +-----END CERTIFICATE----- + diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/ca.pem b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/ca.pem new file mode 100644 index 00000000000..1a1f5a93b70 --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/ca.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEmzCCA4OgAwIBAgIMAMSvNBgypwaaSQ5iMA0GCSqGSIb3DQEBBQUAMIGMMQsw +CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy +YW5jaXNjbzETMBEGA1UEChMKQ0ZTU0wgVEVTVDEbMBkGA1UEAxMSQ0ZTU0wgVEVT +VCBSb290IENBMR4wHAYJKoZIhvcNAQkBFg90ZXN0QHRlc3QubG9jYWwwHhcNMTIx +MjEyMDIxMDMxWhcNMjIxMDIxMDIxMDMxWjCBjDELMAkGA1UEBhMCVVMxEzARBgNV +BAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzARBgNVBAoT +CkNGU1NMIFRFU1QxGzAZBgNVBAMTEkNGU1NMIFRFU1QgUm9vdCBDQTEeMBwGCSqG +SIb3DQEJARYPdGVzdEB0ZXN0LmxvY2FsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEAsRp1xSfIDoD/40Bo4Hls3sFn4dav5NgxbZGpVyGF7dJI9u0eEnL4 +BUGssPaUFLWC83CZxujUEiEfE0oKX+uOhhGv3+j5xSTNM764m2eSiN53cdZtK05d +hwq9uS8LtjKOQeN1mQ5qmiqxBMdjkKgMsVw5lMCgoYKo57kaKFyXzdpNVDzqw+pt +HWmuNtDQjK3qT5Ma06mYPmIGYhIZYLY7oJGg9ZEaNR0GIw4zIT5JRsNiaSb5wTLw +aa0n/4vLJyVjLJcYmJBvZWj8g+taK+C4INu/jGux+bmsC9hq14tbOaTNAn/NE0qN +8oHwcRBEqfOdEYdZkxI5NWPiKNW/Q+AeXQIDAQABo4H6MIH3MB0GA1UdDgQWBBS3 +0veEuqg51fusEM4p/YuWpBPsvTCBxAYDVR0jBIG8MIG5gBS30veEuqg51fusEM4p +/YuWpBPsvaGBkqSBjzCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3Ju +aWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzARBgNVBAoTCkNGU1NMIFRFU1Qx +GzAZBgNVBAMTEkNGU1NMIFRFU1QgUm9vdCBDQTEeMBwGCSqGSIb3DQEJARYPdGVz +dEB0ZXN0LmxvY2FsggwAxK80GDKnBppJDmIwDwYDVR0TBAgwBgEB/wIBADANBgkq +hkiG9w0BAQUFAAOCAQEAJ7r1EZYDwed6rS0+YKHdkRGRQ5Rz6A9DIVBPXrSMAGj3 +F5EF2m/GJbhpVbnNJTVlgP9DDyabOZNxzdrCr4cHMkYYnocDdgAodnkw6GZ/GJTc +depbVTR4TpihFNzeDEGJePrEwM1DouGswpu97jyuCYZ3z1a60+a+3C1GwWaJ7Aet +Uqm+yLTUrMISsfnDPqJdM1NeqW3jiZ4IgcqJkieCCSpag9Xuzrp9q6rjmePvlQkv +qz020JGg6VijJ+c6Tf5y0XqbAhkBTqYtVamu9gEth9utn12EhdNjTZMPKMjjgFUd +H0N6yOEuQMl4ky7RxZBM0iPyeob6i4z2LEQilgv9MQ== +-----END CERTIFICATE----- diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/ca_key.pem b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/ca_key.pem new file mode 100644 index 00000000000..d2de6bfba1a --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/ca_key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCxGnXFJ8gOgP/j +QGjgeWzewWfh1q/k2DFtkalXIYXt0kj27R4ScvgFQayw9pQUtYLzcJnG6NQSIR8T +Sgpf646GEa/f6PnFJM0zvribZ5KI3ndx1m0rTl2HCr25Lwu2Mo5B43WZDmqaKrEE +x2OQqAyxXDmUwKChgqjnuRooXJfN2k1UPOrD6m0daa420NCMrepPkxrTqZg+YgZi +EhlgtjugkaD1kRo1HQYjDjMhPklGw2JpJvnBMvBprSf/i8snJWMslxiYkG9laPyD +61or4Lgg27+Ma7H5uawL2GrXi1s5pM0Cf80TSo3ygfBxEESp850Rh1mTEjk1Y+Io +1b9D4B5dAgMBAAECggEAKHhjcSomDSptTwDo9mLI/h40HudwSlsc8GzYxZBjinUD +N2n39T9QbeMUE1xFenX/9qFEgq+xxnLLJx1EQacSapCgIAqdCO/f9HMgvGJumdg8 +c0cMq1i9Bp7tu+OESZ5D48qWlOM2eQRIb08g8W11eRIaFmPuUPoKnuktkQuXpPJc +YbS/+JuA8SDwe6sV0cMCQuS+iHFfeGwWCKrDUkhLwcL3waW3od2XFyOeFFWFhl0h +HmM/mWKRuRdqR7hrmArTwFZVkB+o/1ywVYXIv+JQm0eNZ5PKLNJGL2f5oxbMR/JI +AoK0bAlJmYaFp96h1KpbPwLEL/0hHSWA7sAyJIgQAQKBgQDaEAZor/w4ZUTekT1+ +cbId0yA+ikDXQOfXaNCSh9Pex+Psjd5zVVOqyVFJ29daRju3d7rmpN4Cm5V4h0l1 +/2ad207rjCAnpCHtaddJWNyJzF2IL2IaoCZQRp0k7zOjBGQpoWDTwBaEin5CCv3P +kkdQkKz6FDP1xskHSLZr21/QCQKBgQDP6jXutEgGjf3yKpMFk/69EamJdon8clbt +hl7cOyWtobnZhdOWVZPe00Oo3Jag2aWgFFsm3EtwnUCnR4d4+fXRKS2LkhfIUZcz +cKy17Ileggdd8UGhL4RDrF/En9tJL86WcVkcoOrqLcGB2FLWrVhVpHFK74eLMCH/ +uc/+ioPItQKBgHYoDsD08s7AGMQcoNx90MyWVLduhFnegoFW+wUa8jOZzieka6/E +wVQeR5yksZjpy3vLNYu6M83n7eLkM2rrm/fXGHlLcTTpm7SgEBZfPwivotKjEh5p +PrlqucWEk082lutz1RqHz+u7e1Rfzk2F7nx6GDBdeBYpw03eGXJx6QW5AoGBAIJq +4puyAEAET1fZNtHX7IGCk7sDXTi6LCbgE57HhzHr8V0t4fQ6CABMuvMwM1gATjEk +s6yjoLqqGUUUzDipanViBAy5fiuManC868lN7zkWDTLzQ3ytBqVAee4na/DziP27 +ae9YTSLJwskE/alloLRP6zTbHUXE0n7LelmrX1DFAoGBAMFLl+Lu+WFgCHxBjn43 +rHpJbQZQmsFhAMhkN4hsj6dJfAGn2gRLRiVRAika+8QF65xMZiVQWUVSUZADWERi +0SXGjzN1wYxO3Qzy3LYwws6fxFAq5lo79eb38yFT2lHdqK3x/QgiDSRVl+R6cExV +xQB518/lp2eIeMpglWByDwJX +-----END PRIVATE KEY----- diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/cert.der b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/cert.der new file mode 100644 index 0000000000000000000000000000000000000000..fa90568bffc85dc44fe9a0209c818bf24b311ff0 GIT binary patch literal 498 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

;MvmDG&_acN{3|?=zOPgfi z??HSXdPWrbk#s1lWc*Eyc9jvRJKTzW{TR(CnJmHIatqMUad5OIoN=vpbh}sKh0z`< zy#v!)s7X3VNCEdBD&I3K24w$pg(p}f77$EiQTAujre?dP$~=K1AC!HSNh6)k-!b4L z|2lIea1rCaSolrrD6gktS12EfCtLOXdxFp3oWn6CFe3&DDuzgg_YDCF6)_eB6t5Qn zu)0$2|J)VxRa^QzE<3&p`!F#uAutIB1uG5%0vZJX1QfW+o}fU7ZMcNCswNS%Np04F T>E;9ouY>3iVWTE;0s;sC1D?Nf literal 0 HcmV?d00001 diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/enc_priv_key.pem b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/enc_priv_key.pem new file mode 100644 index 00000000000..ac61047e8c5 --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/enc_priv_key.pem @@ -0,0 +1,30 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: AES-128-CBC,90B8A5792FA2FE75B2053582F3DF394F + +yVY2xuth5fdJBg9gg+6eP3qTsr0CJ2mGEDW6rvYmYuATSRF1hVERrsznxJYjYLaw +JHec8FVr78y4aXxI/aFzlxLkS8f12WjTtIhzHwhzgSJDwVOXSRphnLAeHWnhEKLe +7kO+vzoTPIc3ECwdvtr6//z2tP1/sac+yIhL6C+x2rS5hFHhmDUXtILPxxfHJCiM +qtKiiOZz3W6008CeJMC9ZPlKHDvpq7aIL4rfVP/GkZ+/teQkgWNpMxac7+gWLKuK +109v6pu+8KT49D6SMsaZPvAb5PXcIB79ZCPI1JX0V26CKcswba4RHG/h1xifwyAF +OIvmK29mmFqbx5GPlUefRUuPwRJKCXFiK6LTdhCwLYodtXde4ibvOFYy4onGoVax +I5WVaOhQMqp+mxA6z7odrIvuFcQGixIA+peaaSbpNZSZGuxRvVefcdxPbJ+26Ijs +wq8uyalbwhKtjPTPNkMaaYzJdWS7wd2DS4RM9JT8Y1h6NTftCY3c+/txOlt5pQzW +T8n+NTd4o+PFOHzMnmEnrtf9Y/SSzXDB2OPCD95YdIXItQDdKcjK0NmnY8GNfkWL +G30NJNy3/DR7Sa5u4xuqNgcgTFhgZaOQ1IVB3p5VjknqAX3gWFu2DrqzbH45071A +He7VbdbzBpMHI2EdiCVOuK9fD/5sv25u9vVC2NHtG/YcoEQv+RB52TNHn9kdiMj1 +gLaywPqGjFmaPxI0xX07BrL+D9RruUT1GAEyw4JAHuJZIyq3+V98wmV/pEqwc7hp +8WuSi6YddetfF4NPA5cGWt8qZ1it+wD/1ydQEAQsxdANqi0XVudYpYox02EoRS02 +up0sd9zqz83pN9RyOOKtGcHdt85gb9DYRVeff1UszMaoVULxqxYetwtzpiHn6grL +DmnSk+DYgvXKOVt8tmSJysDTumhK1VN3xb34TYYJxeBOQJLzWFjGSELEpphZAQSj +rS4OM1FwoP48wvASGiWD4VUJ6v+6F+NDvJr01S+zWGLg1EeUZJmXGHW5GrGd4Kgx +3rdeOsrED9oXKp2cpgx9avXJ9upixja9MbAPp7RkSyeHMPvsuaI44xvOP3f0crmG +d/5CdBKVT7nFaeTGSx/78kHb3VJyopAMm9k0V3CheKwBXXSbXmV1+0muBxMHsEI3 +aEKaI0y5cDfTewzo/U0l0kGtxF6kUPN1pdjFpAvssRlkGttFOC2nWxHwaNHpn7Kq +gFAlN6P4cyB6kb+LvckIYTZ/tV39dx7PfL0KG5TWjJ4a9GSoL1IrAhQq+Qv6oUEt +1vlejZoKyZ/35fni0fmeYNho+pCPimm6l+sHTuXkrWgGLr0S9O00HFLz11D7R4o9 +7mF4JkMNztT+ENOdT4xQBi3OGjRGMwtE6PsQPfDeu13Vq6eDtdEGUdhW1kAsGnBi +eJRuysnGpnoWofJ7yS0+DhnS4GAVi907TMrQWwmez9V4CXl4NBc8X9T69TFL2LsW +2KU9NUXdiCRZqZHD41gd3+RuRA/oXh50V9oaow+uepwYKTFyzde5IH1/DgBd7tOd +Fa2fM5/zSA0uFPRb3yCVhRg5d6J9t5yaPAz7Jp0D1mDDGsMBD1O/FYJvWoANEwUX +-----END RSA PRIVATE KEY----- diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/messed_up_bundle.pem b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/messed_up_bundle.pem new file mode 100644 index 00000000000..f18ee97b6c5 --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/messed_up_bundle.pem @@ -0,0 +1,48 @@ +-----BEGIN CERTIFICATE----- +MIIEczCCAl2gAwIBAgIIDARj8BWNsscwCwYJKoZIhvcNAQELMIGMMQswCQYDVQQG +EwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UECxMTU3lzdGVtcyBFbmdp +bmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZv +cm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5jb20wHhcNMTQwMzAyMDAw +MDAwWhcNMTkwNDAxMDAwMDAwWjCBjDELMAkGA1UEBhMCVVMxEzARBgNVBAoTCkNs +b3VkRmxhcmUxHDAaBgNVBAsTE1N5c3RlbXMgRW5naW5lZXJpbmcxFjAUBgNVBAcT +FHsHEDAfBgNVHREEGDAWghRjbG91ZGZsYXJlLWludGVyLmNvbTALBgkqhkiG9w0B +AQsDggIBACRqAC5EJEe+8ihv1WzCUMEMb7KtS0BqoNbdXE32ia66PgJSQmHcmeJd +FI1UjL0DlljTM2tc+8KxR/1/qnKiI+W/D4wFTWOY/JWFOd15q7lXuKGl+8PMkAHF +A145JCr6oZoO9G9wUwVUrbmXAbyPCOfzsEQ2+mD9F1ZpoEjzVhtGf0R+vnYrRw8j +4WCv5AIcYRAf7HZxbhMILF1bccNlqyUtdH+/MTHXpjkjJjA5KbsHBrAEfjAXkD7c +WWOay6m7mVWb3PPFmGorP6t29baEETK9ZTZSrfD9rnExjjUCftWJEn0M4Pp98DvT +br6+bg8jwtq73qdyOfNsC/Sod18UuHH7MTQA22yqAF5jIlcYtAHGlNnl+sDPZACs +369/Z9rOL9vPFL+Z3F/uJtqZzvN1QiCkj8jWzR0u9fh3eQwZADM2RwgwS4Gs2Ygh +PsypDo33sFOwfX93KqKBsTHssn8SSDDaSnZ8bu1ATEdshbVieecuQx40UadPuJpw +EPVqTR5AhviXQ9bKrTnU5T7EgkW9vNydkpLQQlMg3QE8hsndv4loGZbZGfNtqQHS +/mg1t07S+7OEa4YaMW+wVOBOqTdW7OXlZFLfCcF5SYLM0SnlTMklRMxiqI4JqZXH +0thnUGD0JjfLX4rTaZUzT3lrXXWzpS2jzutXQkjGv4nhGGprIDuT +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIEkDCCA/ugAwIBAgIIWnP9jF/2nogwCwYJKoZIhvcNAQELMH0xCzAJBgNVBAYT +AlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2Nv +MRMwEQYDVQQKDApDbG91ZEZsYXJlMRQwEgYDVQQLDAtERVZfVEVTVElORzEWMBQG +A1UEAwwNQ0ZTU0xfVEVTVF9DQTAeFw0xNDAzMDEwMDAwMDBaFw0xNDA0MTUwMDAw +MDBaMIGMMQswCQYDVQQGEwJVUzETMBEGA1UEChMKQ2xvdWRGbGFyZTEcMBoGA1UE +CxMTU3lzdGVtcyBFbmdpbmVlcmluZzEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzET +MBEGA1UECBMKQ2FsaWZvcm5pYTEdMBsGA1UEAxMUY2xvdWRmbGFyZS1pbnRlci5j +b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDlCnV+vj0sVPy8SqHL +AlI+xwnPhWgzj2VevD6Nz1Zu1BeQ5m5y4CWCf+GmRGTP7+a/C510Fw6rpmInB0Ng +xxwQ2rC08fJtCnijlGH/VjEPIHY5lRaAomcM8Rgx6JOuv9BpZJKpr9pyUMV53JeW +RbWuLH5nEMdyk9NpetS2gWxt4/D20QlhK/tHkROrcLmEUddwIGdwE8JzI88c77Fu +u6pgMtHKvl4GGH0yvb4T7PvCdH8V2tCH7bt8roXd9MSyFVy7uORkfouip7EsVREU +mlcY5EvpR141KXbZqiOQiusJ+u76mEUQNk8wCR1/CW/ii9v1BKOVjXwCfEtIXjg0 +APJx1VNSSH6XoDpUETL+eQ4J0FL9XNbsDuYar7+zD0N1/5vSo3HLNRQR9f0lbsys +sWBEN+CxK19xyPumr21Z0bU0f1B5H52VSF0q3I1Ju9wRo994a7YipdGcmZ2lChmT +7r3mzlBTYl3poU26q34v8wG9U7Jv4fsZJ+RGebDI+TR3QG6Yod06l9oEYZxWXBY7 +STOs8wuTu3huSnan/IpWnV017Vsc61D5G+QrqcxZdXckt3anZKCF75JpUnJ7vuow +TmmHlb8KIMa9mOvcuGX4P6mz8gTi2arl/aL27kj9Q0Jgv/y1ebe2Bx2P9TF6+VND +DL3J/vSVlFeqLt2reAIBKnytLwIDAQABo4GIMIGFMA4GA1UdDwEB/wQEAwIApDAS +BgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQWBBTXXUgpaSwO9HOrQBxGqOOSFHsH +EDAfBgNVHSMEGDAWgBS4Xu+uZ1C31vMH5Wq+VbNnOg2SPjAfBgNVHREEGDAWghRj +bG91ZGZsYXJlLWludGVyLmNvbTALBgkqhkiG9w0BAQsDgYEAfPLKCAHnPzgMYLX/ +fWznVvOEFAAYZByPFx4QdMBbDZUtxHyvJIBs6PdxrdSuDwSiMqE7qQIi+jzzwGl9 +fC7vf45B2zCX0OW51QL2oWNBdKlGgB+b2pwyME82lX/Pr7V1GY10u+ep1xdZDnch +DaMsXjQQTJu0iuG3qKEuCmUwOmc= +-----END CERTIFICATE----- +SDFSDKjkfdlsdfj diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/messed_up_priv_key.pem b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/messed_up_priv_key.pem new file mode 100644 index 00000000000..778273ca649 --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/messed_up_priv_key.pem @@ -0,0 +1,20 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAvGKyz9ZzIXI/BFrtqbVQMmKQkPZGndyfV3AzeSb2ulbS/s5k +yNJMH/jKZiSCvZiJNnW+JNlJrgLxORMmPStPz/N/0L0vCTotQKZUiaBttFgHgobQ +LFsbMnumt9It5W/uOwgWI9binuzvqyPXywLlYwOq3jkOmA22ymhflzRrl6a3jzcY +hT9evxHl0gV4bN7KZ5p4wK/UUuG1uMEQLw87lUwRRHeW3ZG52VL38+redka+f5pa +SGKyG5j0oe1NPLqAjckNgqvDdPMY2gicmCq0VSLzTNpHRsURTUSJvC/iv34vVfba +gIYgTvm8BvGbJSlZqP4kEVlOfd3vmB0ttUeoDwIDAQABAoIBAHZdpXCFlA1d1U6N +O2s4a01dNOyAcVpa9xtfelgTLU9jomtLj3PG/uHP1oxbQHKUVxKK5JAOnwbg/mQY +LhydDCbjHlovpFAt56UJXXCkBoocDYvr3P0huXL80oIJY6EXtR4ONKsMJ5Qn12c2 +vC3ogey2rzO1sf/EDigbcIR3AWtk1Tx8ZDUooktOFypIsDQgjjxXiURGssAlMPSh +6GVgO4JRRG6oRxEna7yDe7izmh/hC5sxSYLsEikCgYEAsBHhb/Qef5obRCSrfFuQ +41P7MCtGrXVxKD3iCDGQCzVbEbYGpmZnGsXSaHljp2FtnamaGGEudYziozGKPHjs +pbTbsLIDbmNwxz1WcaZ1iyIjtOxcAEqDod8hY4hL6SaxypwTHn4Ydbw2NGzp11Eg +Di4SVL82utjycATdKFvBzdsCgYB/3M+GMrt0Sh87rKcQLdL709Kzjcfhvm4HjIbJ +GSXGPCZaYMKaXRTdNAjtRKxMawc9qcf0xSBEHL0GkB158TzusDQtjP1anTcYOnl6 +GsO4bRivp314iNlP4r3S3bIXqBxCGH3HbrvpdPFAN//qjYmAki2lFQZywfvbQOE8 +oFQHwQKBgHqJkTck2DGlXQIwA7jirLggISXjSPlsG4w4LuhY9ivyNKLUi4x5k1cE +bX7SrRtJErQ1WaDN4TFG25xnysi5h+aPinuySatd0XmA5+dE1YjTqqShMO+lUpzi +PrOQl6Eva/uw5BDAcUH4AaXTNRvvtXQptUil9qXyOh6fszikA9Mm +-----END RSA PRIVATE KEY----- diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/messedupcert.pem b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/messedupcert.pem new file mode 100644 index 00000000000..577c375e8c6 --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/messedupcert.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIB7jCCAVmgAwIBAgIBADALBgkqhkiG9w0BAQUwJjEQMA4GA1UEChMHQWNtZSBD +bzESMBAGA1UEAxMJMTI3LjAuMC4xMB4XDTEyMDkwNzIyMDAwNFoXDTEzMDkwNzIy +MDUwNFowJjEQMA4GA1UEChMHQWNtZSBDbzESMBAGA1UEAxMJMTI3LjAuMC4xMIGd +MAsGCSqGSIb3DQEBAQOBjQAwgYkCgYEAm6f+jkP2t5q/vM0YAUZZkhq/EAYD+L1C +cqhEvLFbu3MCAwEAAaMyMDAwDgYDVR0PAQH/BAQDAgCgMA0GA1UdDgQGBAQBAgME +MA8GA1UdIwQIMAaABAECAwQwCwYJKoZIhvcNAQEFA4GBABndWRIcfi+QB9Sakr+m +dYnXTgYCnFio53L2Z+6EHTGG+rEhWtUEGhL4p4pzXX4siAnjWvwcgXTo92cafcfi +uB7wRfK+NL9CTJdpN6cdL+fiNHzH8hsl3bj1nL0CSmdn2hkUWVLbLhSgWlib/I8O +aq+K7aVrgHkPnWeRiG6tl+ZA +-----END CERTIFICATE----- diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/multiplecerts.p12 b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/multiplecerts.p12 new file mode 100644 index 0000000000000000000000000000000000000000..bef8ca44f08b3fd5e660f103f2a90756f10ab288 GIT binary patch literal 3101 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 literal 0 HcmV?d00001 diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/priv_rsa_key.pem b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/priv_rsa_key.pem new file mode 100644 index 00000000000..e67f2f7139a --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/priv_rsa_key.pem @@ -0,0 +1,28 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAvGKyz9ZzIXI/BFrtqbVQMmKQkPZGndyfV3AzeSb2ulbS/s5k +yNJMH/jKZiSCvZiJNnW+JNlJrgLxORMmPStPz/N/0L0vCTotQKZUiaBttFgHgobQ +LFsbMnumt9It5W/uOwgWI9binuzvqyPXywLlYwOq3jkOmA22ymhflzRrl6a3jzcY +hT9evxHl0gV4bN7KZ5p4wK/UUuG1uMEQLw87lUwRRHeW3ZG52VL38+redka+f5pa +SGKyG5j0oe1NPLqAjckNgqvDdPMY2gicmCq0VSLzTNpHRsURTUSJvC/iv34vVfba +gIYgTvm8BvGbJSlZqP4kEVlOfd3vmB0ttUeoDwIDAQABAoIBAHZdpXCFlA1d1U6N +O2s4a01dNOyAcVpa9xtfelgTLU9jomtLj3PG/uHP1oxbQHKUVxKK5JAOnwbg/mQY +LhydDCbjHlovpFAt56UJXXCkBoocDYvr3P0huXL80oIJY6EXtR4ONKsMJ5Qn12c2 +vC3ogey2rzO1sf/EDigbcIR3AWtk1Tx8ZDUooktOFypIsDQgjjxXiURGssAlMPSh +1BFz4StRUK4bESaja0GiHwbuxHa+XYEBlK5OqMo/fpWqpgHhV/42+7wdcBMJsMr8 +rFBe6m+r6TTbLSGJNowyd05XrjoAI35qduckpJ3Voun90i4ynTudjdJ/vHpIqB74 +qQLFW2ECgYEA+GSRVqobaKKakNUFGmK0I5T5Tikz5f137YXXER6aLtDQNiSrlXNi +0aphkC/EfRO3oNvamq5+55bmmgDVoNNPDfpajKz+LZyG8GC2EXlTKO0hZS64KRRl +C+bd+ZsYiUDImNVRbIHN82f+BQgsgXlTaWpBOrEqmoePO/J44O4eX3cCgYEAwieq +amY4UaY+MhWPJFRK1y9M3hM8+N9N/35CFewQUdFJosC6vVQ4t8jNkSOxVQdgbNwE +i/bTBgIwg82JJYbBUPuCVeTT3i6zxymf/FLumrI73URD81IN6FiH1skg0hSFrvs0 +6GVgO4JRRG6oRxEna7yDe7izmh/hC5sxSYLsEikCgYEAsBHhb/Qef5obRCSrfFuQ +41P7MCtGrXVxKD3iCDGQCzVbEbYGpmZnGsXSaHljp2FtnamaGGEudYziozGKPHjs +pbTbsLIDbmNwxz1WcaZ1iyIjtOxcAEqDod8hY4hL6SaxypwTHn4Ydbw2NGzp11Eg +Di4SVL82utjycATdKFvBzdsCgYB/3M+GMrt0Sh87rKcQLdL709Kzjcfhvm4HjIbJ +GSXGPCZaYMKaXRTdNAjtRKxMawc9qcf0xSBEHL0GkB158TzusDQtjP1anTcYOnl6 +GsO4bRivp314iNlP4r3S3bIXqBxCGH3HbrvpdPFAN//qjYmAki2lFQZywfvbQOE8 +oFQHwQKBgHqJkTck2DGlXQIwA7jirLggISXjSPlsG4w4LuhY9ivyNKLUi4x5k1cE +bX7SrRtJErQ1WaDN4TFG25xnysi5h+aPinuySatd0XmA5+dE1YjTqqShMO+lUpzi +PrOQl6Eva/uw5BDAcUH4AaXTNRvvtXQptUil9qXyOh6fszikA9Mm +-----END RSA PRIVATE KEY----- + diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/private_ecdsa_key.pem b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/private_ecdsa_key.pem new file mode 100644 index 00000000000..46a8602e2d0 --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/private_ecdsa_key.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MGgCAQEEHCGXsrNo2xfy8+zd4Pzj8rcQ5KqQO43au1t/7nugBwYFK4EEACGhPAM6 +AASJodCTtj5aYXnWxMiYhwjEgNQJJbNzJFEbsGJX9pCWZC673ammTWFHMjnMPkS/ +9eU5YeW40BHqfw== +-----END EC PRIVATE KEY----- diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/rsa-old.csr b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/rsa-old.csr new file mode 100644 index 00000000000..80d67c2894e --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/rsa-old.csr @@ -0,0 +1,19 @@ +-----BEGIN NEW CERTIFICATE REQUEST----- +MIIDCTCCAfMCAQAwgYYxCzAJBgNVBAYTAlVTMRMwEQYDVQQKEwpDbG91ZEZsYXJl +MRwwGgYDVQQLExNTeXN0ZW1zIEVuZ2luZWVyaW5nMRYwFAYDVQQHEw1TYW4gRnJh +bmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRcwFQYDVQQDEw5jbG91ZGZsYXJl +LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALTWdoYxX4KN51fP +WxQAyGH++VsPbfpAoXIbCPXSmU04BvIxyjzpHQ0ChMKkT/2VNcUeFJwk2fCf+ZwU +f0raTQTplofwkckE0gEYA3WcEfJp+hbvbTb/2recsf+JE6JACYJe2Uu5wsjtrE5j +A+7aT2BEU9RWzBdSy/5281ZfW3PArqcWaf8+RUyA3WRxVWmjmhFsVB+mdNLhCpW0 +C0QNMYR1ppEZiKVnEdao8gcI5sOvSd+35t8g82aPXcNSPU6jKcx1YNUPX5wgPEmu ++anfc9RliQbYqqJYVODgBmV8IR5grw93yTsODoWKtFQ4PKVlnt9CD8AS/iSMQYm3 +OUogqgMCAwEAAaA/MD0GCSqGSIb3DQEJDjEwMC4wLAYDVR0RBCUwI4IOY2xvdWRm +bGFyZS5jb22CEXd3d2Nsb3VkZmxhcmUuY29tMAsGCSqGSIb3DQEBCwOCAQEAl809 +gk9uZkRK+MJVYDSLjgGR2xqk5qOwnhovnispA7N3Z1GshodJRQa6ngNCKuXIm2/6 +AxB9kDGK14n186Qq4odXqHSHs8FG9i0zUcBXeLv1rPAKtwKTas/SLmsOpPgWPZFa +iYiHHeu4HjOQoF987d7uGRYwc3xfstKwJsEXc12eCw2NH8TM1tJgSc/o6CzIpA91 +QnZKhx6uGM4xI2gnOaJA1YikNhyFGBuOGMZgd0k2+/IcR2pg0z4pc5oQw1bXLANx +anqlA/MDrCM9v9019bRJ73zK8LQ3k/FW61PA9nL7RZ8ku65R+uYcVEdLa8pUeqnH +cJZNboDRsItpccZuRQ== +-----END NEW CERTIFICATE REQUEST----- diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/secp256k1-key.pem b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/secp256k1-key.pem new file mode 100644 index 00000000000..e1cfce93129 --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/secp256k1-key.pem @@ -0,0 +1,5 @@ +-----BEGIN EC PRIVATE KEY----- +MHQCAQEEIJLKycmoCAk4HqlJGdsuFyHsxfIheKsLH91tS/TNP5OOoAcGBSuBBAAK +oUQDQgAEBkmL7cvC2cgchzfSuUZPGnzH0FqBtf3kGhSllQiIzGDn4envPXNqp+93 +V2NZ8VT+Aba4ln2Vbp9gYrKquut5Zg== +-----END EC PRIVATE KEY----- diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/test.bad.csr.pem b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/test.bad.csr.pem new file mode 100644 index 00000000000..c58a5810c0a --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/test.bad.csr.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICzDCCAbQCAQAwgYYxCzAJBgNVBAYTAkVOMQ0wCwYDVQQIDARub25lMQ0wCwYD +VQQHDARub25lMRIwEAYDVQQKDAlXaWtpcGVkaWExDTALBgNVBAsMBG5vbmUxGDAW +BgNVBAMMDyoud2lraXBlZGlhLm9yZzEcMBoGCSqGSIb3DQEJARYNbm9uZUBub25l +LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMP/U8RlcCD6E8AL +PT8LLUR9ygyygPCaSmIEC8zXGJung3ykElXFRz/Jc/bu0hxCxi2YDz5IjxBBOpB/ +kieG83HsSmZZtR+drZIQ6vOsr/ucvpnB9z4XzKuabNGZ5ZiTSQ9L7Mx8FzvUTq5y +57HhA7ECAwEAAaAAMA0GCSqGSIb3DQEBBAUAA4IBAQBn8OCVOIx+n0AS6WbEmYDR +SspR9xOCoOwYfamB+2Bpmt82R01zJ/kaqzUtZUjaGvQvAaz5lUwoMdaO0X7I5Xfl +sllMFDaYoGD4Rru4s8gz2qG/QHWA8uPXzJVAj6X0olbIdLTEqTKsnBj4Zr1AJCNy +/YcG4ouLJr140o26MhwBpoCRpPjAgdYMH60BYfnc4/DILxMVqR9xqK1s98d6Ob/+ +3wHFK+S7BRWrJQXcM8veAexXuk9lHQ+FgGfD0eSYGz0kyP26Qa2pLTwumjt+nBPl +rfJxaLHwTQ/1988G0H35ED0f9Md5fzoKi5evU1wG5WRxdEUPyt3QUXxdQ69i0C+7 +-----END CERTIFICATE REQUEST----- + diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/test.csr.pem b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/test.csr.pem new file mode 100644 index 00000000000..3cb67971895 --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testdata/test.csr.pem @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICzDCCAbQCAQAwgYYxCzAJBgNVBAYTAkVOMQ0wCwYDVQQIDARub25lMQ0wCwYD +VQQHDARub25lMRIwEAYDVQQKDAlXaWtpcGVkaWExDTALBgNVBAsMBG5vbmUxGDAW +BgNVBAMMDyoud2lraXBlZGlhLm9yZzEcMBoGCSqGSIb3DQEJARYNbm9uZUBub25l +LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMP/U8RlcCD6E8AL +PT8LLUR9ygyygPCaSmIEC8zXGJung3ykElXFRz/Jc/bu0hxCxi2YDz5IjxBBOpB/ +kieG83HsSmZZtR+drZIQ6vOsr/ucvpnB9z4XzKuabNGZ5ZiTSQ9L7Mx8FzvUTq5y +/ArIuM+FBeuno/IV8zvwAe/VRa8i0QjFXT9vBBp35aeatdnJ2ds50yKCsHHcjvtr +9/8zPVqqmhl2XFS3Qdqlsprzbgksom67OobJGjaV+fNHNQ0o/rzP//Pl3i7vvaEG +7Ff8tQhEwR9nJUR1T6Z7ln7S6cOr23YozgWVkEJ/dSr6LAopb+cZ88FzW5NszU6i +57HhA7ECAwEAAaAAMA0GCSqGSIb3DQEBBAUAA4IBAQBn8OCVOIx+n0AS6WbEmYDR +SspR9xOCoOwYfamB+2Bpmt82R01zJ/kaqzUtZUjaGvQvAaz5lUwoMdaO0X7I5Xfl +sllMFDaYoGD4Rru4s8gz2qG/QHWA8uPXzJVAj6X0olbIdLTEqTKsnBj4Zr1AJCNy +/YcG4ouLJr140o26MhwBpoCRpPjAgdYMH60BYfnc4/DILxMVqR9xqK1s98d6Ob/+ +3wHFK+S7BRWrJQXcM8veAexXuk9lHQ+FgGfD0eSYGz0kyP26Qa2pLTwumjt+nBPl +rfJxaLHwTQ/1988G0H35ED0f9Md5fzoKi5evU1wG5WRxdEUPyt3QUXxdQ69i0C+7 +-----END CERTIFICATE REQUEST----- + diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/testsuite/testdata/cert_csr.json b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testsuite/testdata/cert_csr.json new file mode 100644 index 00000000000..0d3bb65cd49 --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testsuite/testdata/cert_csr.json @@ -0,0 +1,18 @@ +{ + "hosts": [ + "ca.example2.com" + ], + "names": [ + { + "C": "US", + "ST": "California", + "L": "San Francisco", + "O": "Internet Widgets, LLC", + "OU": "Certificate Authority" + } + ], + "key": { + "algo": "rsa", + "size": 2048 + } +} \ No newline at end of file diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/testsuite/testdata/initCA/ca_csr.json b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testsuite/testdata/initCA/ca_csr.json new file mode 100644 index 00000000000..861ec167e2b --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testsuite/testdata/initCA/ca_csr.json @@ -0,0 +1,23 @@ +{ + "cn": "example.com", + "hosts": [ + "ca.example.com" + ], + "names": [ + { + "C": "US", + "ST": "California", + "L": "San Francisco", + "O": "Internet Widgets, LLC", + "OU": "Certificate Authority" + } + ], + "key": { + "algo": "rsa", + "size": 2048 + }, + "ca": { + "pathlen": 1, + "expiry": "1/1/2015" + } +} \ No newline at end of file diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/testsuite/testdata/initCA/cfssl_output.pem b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testsuite/testdata/initCA/cfssl_output.pem new file mode 100644 index 00000000000..0f9d9db4999 --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testsuite/testdata/initCA/cfssl_output.pem @@ -0,0 +1 @@ +{"cert":"-----BEGIN CERTIFICATE-----\nMIIECjCCAvKgAwIBAgIIIrWBQ2reGCQwDQYJKoZIhvcNAQELBQAwgZAxCzAJBgNV\nBAYTAlVTMR4wHAYDVQQKExVJbnRlcm5ldCBXaWRnZXRzLCBMTEMxHjAcBgNVBAsT\nFUNlcnRpZmljYXRlIEF1dGhvcml0eTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzET\nMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMTUw\nOTA5MjExNjAwWhcNMTUwOTA5MjExNjAwWjCBkDELMAkGA1UEBhMCVVMxHjAcBgNV\nBAoTFUludGVybmV0IFdpZGdldHMsIExMQzEeMBwGA1UECxMVQ2VydGlmaWNhdGUg\nQXV0aG9yaXR5MRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRMwEQYDVQQIEwpDYWxp\nZm9ybmlhMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQAD\nggEPADCCAQoCggEBALOFN1p4aaMQKqInRanzmf05CD4kYKKVygggOHhD3ZgwFgIG\n20Z0dlTHStY/UvY+2qkpB1Bp9trmys9YvPzt5ElX2szZ2deZc0TWvzZeuZDQVsbx\n0Ea6RGxRnEEoMrpaRFoe2TOKSVKW+SzLC/eoNUoxsZVo6cNsU4BeGqExOWCDFzjd\nCRLJeXqafYeL1dUiXZ028tOZVWLIjaLu3FKENHeDB36gr99KckFdeDnqaAVvu66V\nbF5QGvIv2RmK9y3cq7rIizYfayi/dBS8/AY31OOHcEGsVTZYjqB/s9aIocCOzfvQ\nV5++QdkDiYhIwokB3fT5b4wIb8CLr0GUnHnslnMCAwEAAaNmMGQwDgYDVR0PAQH/\nBAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFCL83Uiwa3VGSXwD\nf8XKgu1l+nX8MB8GA1UdIwQYMBaAFCL83Uiwa3VGSXwDf8XKgu1l+nX8MA0GCSqG\nSIb3DQEBCwUAA4IBAQCRP7HK4JHxY4uYE3VBkx5SSQouZrnpQCOnUhSof3vi5nk0\nlhfIU7nbS6HVyQC4mY47gDrBlKwTr++Kw9BKyK9EgfVWhct8vszcRexZk5mmO3yi\n3s4kqD158SP0t5AMahHGK/VviD/Id4kaX1PIPHW3TfU3Ly4LP6d/NlloaXc8qrsg\nKJO+AXTy9xnEkjtq7lvnP3JLwA1Y+nozqkp46kQv0K+Nz+MBqN5De12I9J7T0k6h\nij4MsiraPdeij6d1cp+OEW8DPe+MCIBYAvbJXtQY5zYPB2/F6JJHMeQ9AmluvUFn\nD3ipCSVbw1Tgpp8xcx+4rt9Vq6Oo8pGi4JR3C7TN\n-----END CERTIFICATE-----\n","csr":"-----BEGIN CERTIFICATE REQUEST-----\nMIIDAjCCAeoCAQAwgZAxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVJbnRlcm5ldCBX\naWRnZXRzLCBMTEMxHjAcBgNVBAsTFUNlcnRpZmljYXRlIEF1dGhvcml0eTEWMBQG\nA1UEBxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UE\nAxMLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCz\nhTdaeGmjECqiJ0Wp85n9OQg+JGCilcoIIDh4Q92YMBYCBttGdHZUx0rWP1L2Ptqp\nKQdQafba5srPWLz87eRJV9rM2dnXmXNE1r82XrmQ0FbG8dBGukRsUZxBKDK6WkRa\nHtkziklSlvksywv3qDVKMbGVaOnDbFOAXhqhMTlggxc43QkSyXl6mn2Hi9XVIl2d\nNvLTmVViyI2i7txShDR3gwd+oK/fSnJBXXg56mgFb7uulWxeUBryL9kZivct3Ku6\nyIs2H2sov3QUvPwGN9Tjh3BBrFU2WI6gf7PWiKHAjs370FefvkHZA4mISMKJAd30\n+W+MCG/Ai69BlJx57JZzAgMBAAGgLDAqBgkqhkiG9w0BCQ4xHTAbMBkGA1UdEQQS\nMBCCDmNhLmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQA2E/lSvOtUZjOj\ncOAcALpCVLLTSRdqF7745jSqIrHV8RuB+V6HFi55qoFzCKfJbc81SUqX7DhzoUtP\nNjdq5RfhEh/kWw3GeIN90Y5thVDm0i19A7GY+maJXMjw3LVWlKIo9a7rCOQvCi3O\nkA0QGO1Rc1d6/MneoWUR2tUHjJ83FvO089RaDUQhYpInBXV3GQByJGJeF8USJ/wS\nXy+SLDkaG9mC0TJwWNpldGoZQ57h/QeLtl2wfK3sLga5eVuQO0Lnzt6W3tYvvSAO\nckN/hrTMFMJ/vXHFUPXk8AYUBt0FqgYYHR5JxA9Wl2+Fr+Duw7Qee7/kcbwhbo1M\nLe9GgT/c\n-----END CERTIFICATE REQUEST-----\n","key":"-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAs4U3WnhpoxAqoidFqfOZ/TkIPiRgopXKCCA4eEPdmDAWAgbb\nRnR2VMdK1j9S9j7aqSkHUGn22ubKz1i8/O3kSVfazNnZ15lzRNa/Nl65kNBWxvHQ\nRrpEbFGcQSgyulpEWh7ZM4pJUpb5LMsL96g1SjGxlWjpw2xTgF4aoTE5YIMXON0J\nEsl5epp9h4vV1SJdnTby05lVYsiNou7cUoQ0d4MHfqCv30pyQV14OepoBW+7rpVs\nXlAa8i/ZGYr3LdyrusiLNh9rKL90FLz8BjfU44dwQaxVNliOoH+z1oihwI7N+9BX\nn75B2QOJiEjCiQHd9PlvjAhvwIuvQZSceeyWcwIDAQABAoIBAQCYD0cLwuL31EHI\niCtiAY12CFELEhUoomg26dPrStDwkAqUPOqPiyaQOR8SyyCipCrjDnW7j4YhdUxk\nxW2PcJHl7hzPV9hF3xzcOmpMy3+zQkW+ziT1Q+GhPp90MXCGmN881tRz67af1nHJ\nl1PTuw8ogV9Ch1M6zQ8NtKgp0WAgdn2YlkVrYv9/JaOZdh7ewNQPs+V68bC8tmmz\nEj0kuH06Lzr3zxfxOgAq8Xrv8b5GmFNjcRPOu5WWCRKt26FSwAcI/U5AXRuqVr8o\nthMiXKPA+RMSSy7bDF1EOFYdNyrWaLEzyrKLSxTXTvOT5kIfwVWKTTzdNug/BaYy\neesqkgtBAoGBAOafPTvJHYLHuyb11X2FYblvkLaw+orWvdy1E2sV62M3YiloOp5R\neWteEptvsEjo0ZXZyYqOK5WHsbkdTbPE7QPctKIYdVdztPb+3wWHM9hP1kGWuohW\n+S/kVIwf+ijH10Q3wQPgtsIGrj0Q/rkizD6qqmKz8gNizRwX2k3kkp/1AoGBAMdG\nabBuC9XHxFtVIEzHSXbVn38Z6tjxURcUK7hob8iRfl4JXbHtEKkoM2iIpY/a4YrA\nvjLBy0tNWnurTdT/Md7L3pqU4wVlMY0i5ysae7/vm2WV9ePdcHM1Oa4gFTSL3dW1\nLI3TLwxDD3vlqhP/Js3CMq7wtvTE1T1sWxjiLOPHAoGBAI9t49Bl8SOgAoZliARL\nyw5gE3l7siZdDbHRMCV+eWm4TSWBfEtwUnpzBFGFcfa2TXrL+ytf5j1WKjL9xZCT\nXfDz96eEXJ4qCRdARgYTIyxZ4t/h1Vrr9IhTkj2fuZ5ZQ0la/4Dg5ejf7Mdv5WvQ\n94PV8qf2UALJFNVvBEdDa+ltAoGAVjSYvhEtH4M254fQ3EYN+tF5GSPeG5FxUfmQ\n3EqZqPt/3jBRDwqN/Y99hcgvTycSENNGtHBvgJjq/rrhhbYMHeS13Mtx1kCrifHC\nbTwcsrB12iFgaP2/iqdI15HbeorTIYMpzgTAwp40EZYN2G61m6daA6Hwk7yevt6k\nHgSiBLsCgYEAoou8wIMi0+zyUgIo8MXYhexJFuYvEdHeyYvWN/FA4j/DZelixgW9\no4a0M9yOXStvjUUpG/TpaceJGqtwXk9bMBZML05fT7Im/3Qmh+44fF/a0q0Ard2N\nzdlIuaKlvcK5a6jD+mcN9vzOi4iuGUm1OhfTvHD/IS/rG4r6XhahHm4=\n-----END RSA PRIVATE KEY-----\n"} diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/testsuite/testing_helpers.go b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testsuite/testing_helpers.go new file mode 100644 index 00000000000..f937540e98b --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testsuite/testing_helpers.go @@ -0,0 +1,423 @@ +// These functions are designed for use in testing other parts of the code. + +package testsuite + +import ( + "bufio" + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "os" + "os/exec" + "strconv" + "strings" + "testing" + "time" + + "github.com/cloudflare/cfssl/config" + "github.com/cloudflare/cfssl/csr" +) + +// CFSSLServerData is the data with which a server is initialized. These fields +// can be left empty if desired. Any empty fields passed in to StartServer will +// lead to the server being initialized with the default values defined by the +// 'cfssl serve' command. +type CFSSLServerData struct { + CA []byte + CABundle []byte + CAKey []byte + IntBundle []byte +} + +// CFSSLServer is the type returned by StartCFSSLServer. It serves as a handle +// to a running CFSSL server. +type CFSSLServer struct { + process *os.Process + tempFiles []string +} + +// StartCFSSLServer creates a local server listening on the given address and +// port number. Both the address and port number are assumed to be valid. +func StartCFSSLServer(address string, portNumber int, serverData CFSSLServerData) (*CFSSLServer, error) { + // This value is explained below. + startupTime := time.Second + + // We return this when an error occurs. + nilServer := &CFSSLServer{nil, nil} + + args := []string{"serve", "-address", address, "-port", strconv.Itoa(portNumber)} + var tempCAFile, tempCABundleFile, tempCAKeyFile, tempIntBundleFile string + var err error + var tempFiles []string + if len(serverData.CA) > 0 { + tempCAFile, err = createTempFile(serverData.CA) + tempFiles = append(tempFiles, tempCAFile) + args = append(args, "-ca") + args = append(args, tempCAFile) + } + if len(serverData.CABundle) > 0 { + tempCABundleFile, err = createTempFile(serverData.CABundle) + tempFiles = append(tempFiles, tempCABundleFile) + args = append(args, "-ca-bundle") + args = append(args, tempCABundleFile) + } + if len(serverData.CAKey) > 0 { + tempCAKeyFile, err = createTempFile(serverData.CAKey) + tempFiles = append(tempFiles, tempCAKeyFile) + args = append(args, "-ca-key") + args = append(args, tempCAKeyFile) + } + if len(serverData.IntBundle) > 0 { + tempIntBundleFile, err = createTempFile(serverData.IntBundle) + tempFiles = append(tempFiles, tempIntBundleFile) + args = append(args, "-int-bundle") + args = append(args, tempIntBundleFile) + } + // If an error occurred in the creation of any file, return an error. + if err != nil { + for _, file := range tempFiles { + os.Remove(file) + } + return nilServer, err + } + + command := exec.Command("cfssl", args...) + + stdErrPipe, err := command.StderrPipe() + if err != nil { + for _, file := range tempFiles { + os.Remove(file) + } + return nilServer, err + } + + err = command.Start() + if err != nil { + for _, file := range tempFiles { + os.Remove(file) + } + return nilServer, err + } + + // We check to see if the address given is already in use. There is no way + // to do this other than to just wait and see if an error message pops up. + // Therefore we wait for startupTime, and if we don't see an error message + // by then, we deem the server ready and return. + + errorOccurred := make(chan bool) + go func() { + scanner := bufio.NewScanner(stdErrPipe) + for scanner.Scan() { + line := scanner.Text() + if strings.Contains(line, "address already in use") { + errorOccurred <- true + } + } + }() + + select { + case <-errorOccurred: + for _, file := range tempFiles { + os.Remove(file) + } + return nilServer, errors.New( + "Error occurred on server: address " + address + ":" + + strconv.Itoa(portNumber) + " already in use.") + case <-time.After(startupTime): + return &CFSSLServer{command.Process, tempFiles}, nil + } +} + +// Kill a running CFSSL server. +func (server *CFSSLServer) Kill() error { + for _, file := range server.tempFiles { + os.Remove(file) + } + return server.process.Kill() +} + +// CreateCertificateChain creates a chain of certificates from a slice of +// requests. The first request is the root certificate and the last is the +// leaf. The chain is returned as a slice of PEM-encoded bytes. +func CreateCertificateChain(requests []csr.CertificateRequest) (certChain []byte, key []byte, err error) { + // Create the root certificate using the first request. This will be + // self-signed. + certChain = make([]byte, 0) + rootCert, prevKey, err := CreateSelfSignedCert(requests[0]) + if err != nil { + return nil, nil, err + } + certChain = append(certChain, rootCert...) + + // For each of the next requests, create a certificate signed by the + // previous certificate. + prevCert := rootCert + for _, request := range requests[1:] { + cert, key, err := SignCertificate(request, prevCert, prevKey) + if err != nil { + return nil, nil, err + } + certChain = append(certChain, byte('\n')) + certChain = append(certChain, cert...) + prevCert = cert + prevKey = key + } + + return certChain, key, nil +} + +// CreateSelfSignedCert creates a self-signed certificate from a certificate +// request. This function just calls the CLI "gencert" command. +func CreateSelfSignedCert(request csr.CertificateRequest) (encodedCert, encodedKey []byte, err error) { + // Marshall the request into JSON format and write it to a temporary file. + jsonBytes, err := json.Marshal(request) + if err != nil { + return nil, nil, err + } + tempFile, err := createTempFile(jsonBytes) + if err != nil { + os.Remove(tempFile) + return nil, nil, err + } + + // Create the certificate with the CLI tools. + command := exec.Command("cfssl", "gencert", "-initca", tempFile) + CLIOutput, err := command.CombinedOutput() + if err != nil { + os.Remove(tempFile) + return nil, nil, fmt.Errorf("%v - CLI output: %s", err, string(CLIOutput)) + } + err = checkCLIOutput(CLIOutput) + if err != nil { + os.Remove(tempFile) + return nil, nil, err + } + + encodedCert, err = cleanCLIOutput(CLIOutput, "cert") + if err != nil { + os.Remove(tempFile) + return nil, nil, err + } + encodedKey, err = cleanCLIOutput(CLIOutput, "key") + if err != nil { + os.Remove(tempFile) + return nil, nil, err + } + + os.Remove(tempFile) + + return encodedCert, encodedKey, nil +} + +// SignCertificate uses a certificate (input as signerCert) to create a signed +// certificate for the input request. +func SignCertificate(request csr.CertificateRequest, signerCert, signerKey []byte) (encodedCert, encodedKey []byte, err error) { + // Marshall the request into JSON format and write it to a temporary file. + jsonBytes, err := json.Marshal(request) + if err != nil { + return nil, nil, err + } + tempJSONFile, err := createTempFile(jsonBytes) + if err != nil { + os.Remove(tempJSONFile) + return nil, nil, err + } + + // Create a CSR file with the CLI tools. + command := exec.Command("cfssl", "genkey", tempJSONFile) + CLIOutput, err := command.CombinedOutput() + if err != nil { + os.Remove(tempJSONFile) + return nil, nil, fmt.Errorf("%v - CLI output: %s", err, string(CLIOutput)) + } + err = checkCLIOutput(CLIOutput) + if err != nil { + os.Remove(tempJSONFile) + return nil, nil, err + } + encodedCSR, err := cleanCLIOutput(CLIOutput, "csr") + if err != nil { + os.Remove(tempJSONFile) + return nil, nil, err + } + encodedCSRKey, err := cleanCLIOutput(CLIOutput, "key") + if err != nil { + os.Remove(tempJSONFile) + return nil, nil, err + } + + // Now we write this encoded CSR and its key to file. + tempCSRFile, err := createTempFile(encodedCSR) + if err != nil { + os.Remove(tempJSONFile) + os.Remove(tempCSRFile) + return nil, nil, err + } + + // We also need to write the signer's certficate and key to temporary files. + tempSignerCertFile, err := createTempFile(signerCert) + if err != nil { + os.Remove(tempJSONFile) + os.Remove(tempCSRFile) + os.Remove(tempSignerCertFile) + return nil, nil, err + } + tempSignerKeyFile, err := createTempFile(signerKey) + if err != nil { + os.Remove(tempJSONFile) + os.Remove(tempCSRFile) + os.Remove(tempSignerCertFile) + os.Remove(tempSignerKeyFile) + return nil, nil, err + } + + // Now we use the signer's certificate and key file along with the CSR file + // to sign a certificate for the input request. We use the CLI tools to do + // this. + command = exec.Command( + "cfssl", + "sign", + "-ca", tempSignerCertFile, + "-ca-key", tempSignerKeyFile, + "-hostname", request.CN, + tempCSRFile, + ) + CLIOutput, err = command.CombinedOutput() + err = checkCLIOutput(CLIOutput) + if err != nil { + return nil, nil, fmt.Errorf("%v - CLI output: %s", err, string(CLIOutput)) + } + encodedCert, err = cleanCLIOutput(CLIOutput, "cert") + if err != nil { + return nil, nil, err + } + + // Clean up. + os.Remove(tempJSONFile) + os.Remove(tempCSRFile) + os.Remove(tempSignerCertFile) + os.Remove(tempSignerKeyFile) + + return encodedCert, encodedCSRKey, nil +} + +// Creates a temporary file with the given data. Returns the file name. +func createTempFile(data []byte) (fileName string, err error) { + // Avoid overwriting a file in the currect directory by choosing an unused + // file name. + baseName := "temp" + tempFileName := baseName + tryIndex := 0 + for { + if _, err := os.Stat(tempFileName); err == nil { + tempFileName = baseName + strconv.Itoa(tryIndex) + tryIndex++ + } else { + break + } + } + + readWritePermissions := os.FileMode(0664) + err = ioutil.WriteFile(tempFileName, data, readWritePermissions) + if err != nil { + return "", err + } + + return tempFileName, nil +} + +// Checks the CLI Output for failure. +func checkCLIOutput(CLIOutput []byte) error { + outputString := string(CLIOutput) + // Proper output will contain the substring "---BEGIN" somewhere + failureOccurred := !strings.Contains(outputString, "---BEGIN") + if failureOccurred { + return errors.New("Failure occurred during CLI execution: " + outputString) + } + return nil +} + +// Returns the cleaned up PEM encoding for the item specified (for example, +// 'cert' or 'key'). +func cleanCLIOutput(CLIOutput []byte, item string) (cleanedOutput []byte, err error) { + outputString := string(CLIOutput) + // The keyword will be surrounded by quotes. + itemString := "\"" + item + "\"" + // We should only search for the keyword beyond this point. + eligibleSearchIndex := strings.Index(outputString, "{") + outputString = outputString[eligibleSearchIndex:] + // Make sure the item is present in the output. + if strings.Index(outputString, itemString) == -1 { + return nil, errors.New("Item " + item + " not found in CLI Output") + } + // We add 2 for the [:"] that follows the item + startIndex := strings.Index(outputString, itemString) + len(itemString) + 2 + outputString = outputString[startIndex:] + endIndex := strings.Index(outputString, "\\n\"") + outputString = outputString[:endIndex] + outputString = strings.Replace(outputString, "\\n", "\n", -1) + + return []byte(outputString), nil +} + +// NewConfig returns a config object from the data passed. +func NewConfig(t *testing.T, configBytes []byte) *config.Config { + conf, err := config.LoadConfig([]byte(configBytes)) + if err != nil { + t.Fatal("config loading error:", err) + } + if !conf.Valid() { + t.Fatal("config is not valid") + } + return conf +} + +// CSRTest holds information about CSR test files. +type CSRTest struct { + File string + KeyAlgo string + KeyLen int + // Error checking function + ErrorCallback func(*testing.T, error) +} + +// CSRTests define a set of CSR files for testing. +var CSRTests = []CSRTest{ + { + File: "../../signer/local/testdata/rsa2048.csr", + KeyAlgo: "rsa", + KeyLen: 2048, + ErrorCallback: nil, + }, + { + File: "../../signer/local/testdata/rsa3072.csr", + KeyAlgo: "rsa", + KeyLen: 3072, + ErrorCallback: nil, + }, + { + File: "../../signer/local/testdata/rsa4096.csr", + KeyAlgo: "rsa", + KeyLen: 4096, + ErrorCallback: nil, + }, + { + File: "../../signer/local/testdata/ecdsa256.csr", + KeyAlgo: "ecdsa", + KeyLen: 256, + ErrorCallback: nil, + }, + { + File: "../../signer/local/testdata/ecdsa384.csr", + KeyAlgo: "ecdsa", + KeyLen: 384, + ErrorCallback: nil, + }, + { + File: "../../signer/local/testdata/ecdsa521.csr", + KeyAlgo: "ecdsa", + KeyLen: 521, + ErrorCallback: nil, + }, +} diff --git a/cmd/vendor/github.com/cloudflare/cfssl/helpers/testsuite/testing_helpers_test.go b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testsuite/testing_helpers_test.go new file mode 100644 index 00000000000..f077e782349 --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/helpers/testsuite/testing_helpers_test.go @@ -0,0 +1,479 @@ +package testsuite + +import ( + "crypto/x509" + "encoding/json" + "io/ioutil" + "math" + "math/rand" + "os" + "os/exec" + "reflect" + "strconv" + "strings" + "testing" + "time" + + "github.com/cloudflare/cfssl/csr" + "github.com/cloudflare/cfssl/helpers" +) + +const ( + testDataDirectory = "testdata" + initCADirectory = testDataDirectory + string(os.PathSeparator) + "initCA" + preMadeOutput = initCADirectory + string(os.PathSeparator) + "cfssl_output.pem" + csrFile = testDataDirectory + string(os.PathSeparator) + "cert_csr.json" +) + +var ( + keyRequest = csr.BasicKeyRequest{ + A: "rsa", + S: 2048, + } + CAConfig = csr.CAConfig{ + PathLength: 1, + Expiry: "1h", // issue a CA certificate only valid for 1 hour + } + baseRequest = csr.CertificateRequest{ + CN: "example.com", + Names: []csr.Name{ + { + C: "US", + ST: "California", + L: "San Francisco", + O: "Internet Widgets, LLC", + OU: "Certificate Authority", + }, + }, + Hosts: []string{"ca.example.com"}, + KeyRequest: &keyRequest, + } + CARequest = csr.CertificateRequest{ + CN: "example.com", + Names: []csr.Name{ + { + C: "US", + ST: "California", + L: "San Francisco", + O: "Internet Widgets, LLC", + OU: "Certificate Authority", + }, + }, + Hosts: []string{"ca.example.com"}, + KeyRequest: &keyRequest, + CA: &CAConfig, + } +) + +func TestStartCFSSLServer(t *testing.T) { + // We will test on this address and port. Be sure that these are free or + // the test will fail. + addressToTest := "127.0.0.1" + portToTest := 9775 + + CACert, CAKey, err := CreateSelfSignedCert(CARequest) + if err != nil { + t.Fatal(err.Error()) + } + + // Set up a test server using our CA certificate and key. + serverData := CFSSLServerData{CA: CACert, CAKey: CAKey} + server, err := StartCFSSLServer(addressToTest, portToTest, serverData) + if err != nil { + t.Fatal(err.Error()) + } + + // Try to start up a second server at the same address and port number. We + // should get an 'address in use' error. + _, err = StartCFSSLServer(addressToTest, portToTest, serverData) + if err == nil || !strings.Contains(err.Error(), "Error occurred on server: address") { + t.Fatal("Two servers allowed on same address and port.") + } + + // Now make a request of our server and check that no error occurred. + + // First we need a request to send to our server. We marshall the request + // into JSON format and write it to a temporary file. + jsonBytes, err := json.Marshal(baseRequest) + if err != nil { + t.Fatal(err.Error()) + } + tempFile, err := createTempFile(jsonBytes) + if err != nil { + os.Remove(tempFile) + panic(err) + } + + // Now we make the request and check the output. + remoteServerString := "-remote=" + addressToTest + ":" + strconv.Itoa(portToTest) + command := exec.Command( + "cfssl", "gencert", remoteServerString, "-hostname="+baseRequest.CN, tempFile) + CLIOutput, err := command.CombinedOutput() + os.Remove(tempFile) + if err != nil { + t.Fatalf("%v: %s", err.Error(), string(CLIOutput)) + } + err = checkCLIOutput(CLIOutput) + if err != nil { + t.Fatal(err.Error()) + } + // The output should contain the certificate, request, and private key. + _, err = cleanCLIOutput(CLIOutput, "cert") + if err != nil { + t.Fatal(err.Error()) + } + _, err = cleanCLIOutput(CLIOutput, "csr") + if err != nil { + t.Fatal(err.Error()) + } + _, err = cleanCLIOutput(CLIOutput, "key") + if err != nil { + t.Fatal(err.Error()) + } + + // Finally, kill the server. + err = server.Kill() + if err != nil { + t.Fatal(err.Error()) + } +} + +func TestCreateCertificateChain(t *testing.T) { + + // N is the number of certificates that will be chained together. + N := 10 + + // --- TEST: Create a chain of one certificate. --- // + + encodedChainFromCode, _, err := CreateCertificateChain([]csr.CertificateRequest{CARequest}) + if err != nil { + t.Fatal(err.Error()) + } + + // Now compare to a pre-made certificate chain using a JSON file containing + // the same request data. + + CLIOutputFile := preMadeOutput + CLIOutput, err := ioutil.ReadFile(CLIOutputFile) + if err != nil { + t.Fatal(err.Error()) + } + encodedChainFromCLI, err := cleanCLIOutput(CLIOutput, "cert") + if err != nil { + t.Fatal(err.Error()) + } + + chainFromCode, err := helpers.ParseCertificatesPEM(encodedChainFromCode) + if err != nil { + t.Fatal(err.Error()) + } + chainFromCLI, err := helpers.ParseCertificatesPEM(encodedChainFromCLI) + if err != nil { + t.Fatal(err.Error()) + } + + if !chainsEqual(chainFromCode, chainFromCLI) { + unequalFieldSlices := checkFieldsOfChains(chainFromCode, chainFromCLI) + for i, unequalFields := range unequalFieldSlices { + if len(unequalFields) > 0 { + t.Log("The certificate chains held unequal fields for chain " + strconv.Itoa(i)) + t.Log("The following fields were unequal:") + for _, field := range unequalFields { + t.Log("\t" + field) + } + } + } + t.Fatal("Certificate chains unequal.") + } + + // --- TEST: Create a chain of N certificates. --- // + + // First we make a slice of N requests. We make each slightly different. + + cnGrabBag := []string{"example", "invalid", "test"} + topLevelDomains := []string{".com", ".net", ".org"} + subDomains := []string{"www.", "secure.", "ca.", ""} + countryGrabBag := []string{"USA", "China", "England", "Vanuatu"} + stateGrabBag := []string{"California", "Texas", "Alaska", "London"} + localityGrabBag := []string{"San Francisco", "Houston", "London", "Oslo"} + orgGrabBag := []string{"Internet Widgets, LLC", "CloudFlare, Inc."} + orgUnitGrabBag := []string{"Certificate Authority", "Systems Engineering"} + + requests := make([]csr.CertificateRequest, N) + requests[0] = CARequest + for i := 1; i < N; i++ { + requests[i] = baseRequest + + cn := randomElement(cnGrabBag) + tld := randomElement(topLevelDomains) + subDomain1 := randomElement(subDomains) + subDomain2 := randomElement(subDomains) + country := randomElement(countryGrabBag) + state := randomElement(stateGrabBag) + locality := randomElement(localityGrabBag) + org := randomElement(orgGrabBag) + orgUnit := randomElement(orgUnitGrabBag) + + requests[i].CN = cn + "." + tld + requests[i].Names = []csr.Name{ + {C: country, + ST: state, + L: locality, + O: org, + OU: orgUnit, + }, + } + hosts := []string{subDomain1 + requests[i].CN} + if subDomain2 != subDomain1 { + hosts = append(hosts, subDomain2+requests[i].CN) + } + requests[i].Hosts = hosts + } + + // Now we make a certificate chain out of these requests. + encodedCertChain, _, err := CreateCertificateChain(requests) + if err != nil { + t.Fatal(err.Error()) + } + + // To test this chain, we compare the data encoded in each certificate to + // each request we used to generate the chain. + chain, err := helpers.ParseCertificatesPEM(encodedCertChain) + if err != nil { + t.Fatal(err.Error()) + } + + if len(chain) != len(requests) { + t.Log("Length of chain: " + strconv.Itoa(len(chain))) + t.Log("Length of requests: " + strconv.Itoa(len(requests))) + t.Fatal("Length of chain not equal to length of requests.") + } + + mismatchOccurred := false + for i := 0; i < len(chain); i++ { + certEqualsRequest, unequalFields := certEqualsRequest(chain[i], requests[i]) + if !certEqualsRequest { + mismatchOccurred = true + t.Log( + "Certificate " + strconv.Itoa(i) + " and request " + + strconv.Itoa(i) + " unequal.", + ) + t.Log("Unequal fields for index " + strconv.Itoa(i) + ":") + for _, field := range unequalFields { + t.Log("\t" + field) + } + } + } + + // TODO: check that each certificate is actually signed by the previous one + + if mismatchOccurred { + t.Fatal("Unequal certificate(s) and request(s) found.") + } + + // --- TEST: Create a chain of certificates with invalid path lengths. --- // + + // Other invalid chains? +} + +func TestCreateSelfSignedCert(t *testing.T) { + + // --- TEST: Create a self-signed certificate from a CSR. --- // + + // Generate a self-signed certificate from the request. + encodedCertFromCode, _, err := CreateSelfSignedCert(CARequest) + if err != nil { + t.Fatal(err.Error()) + } + + // Now compare to a pre-made certificate made using a JSON file with the + // same request information. This JSON file is located in testdata/initCA + // and is called ca_csr.json. + + CLIOutputFile := preMadeOutput + CLIOutput, err := ioutil.ReadFile(CLIOutputFile) + if err != nil { + t.Fatal(err.Error()) + } + encodedCertFromCLI, err := cleanCLIOutput(CLIOutput, "cert") + if err != nil { + t.Fatal(err.Error()) + } + + certFromCode, err := helpers.ParseSelfSignedCertificatePEM(encodedCertFromCode) + if err != nil { + t.Fatal(err.Error()) + } + certFromCLI, err := helpers.ParseSelfSignedCertificatePEM(encodedCertFromCLI) + if err != nil { + t.Fatal(err.Error()) + } + + // Nullify any fields of the certificates which are dependent upon the time + // of the certificate's creation. + nullifyTimeDependency(certFromCode) + nullifyTimeDependency(certFromCLI) + + if !reflect.DeepEqual(certFromCode, certFromCLI) { + unequalFields := checkFields( + *certFromCode, *certFromCLI, reflect.TypeOf(*certFromCode)) + t.Log("The following fields were unequal:") + for _, field := range unequalFields { + t.Log(field) + } + t.Fatal("Certificates unequal.") + } + +} + +// Compare two x509 certificate chains. We only compare relevant data to +// determine equality. +func chainsEqual(chain1, chain2 []*x509.Certificate) bool { + if len(chain1) != len(chain2) { + return false + } + + for i := 0; i < len(chain1); i++ { + cert1 := nullifyTimeDependency(chain1[i]) + cert2 := nullifyTimeDependency(chain2[i]) + if !reflect.DeepEqual(cert1, cert2) { + return false + } + } + return true +} + +// When comparing certificates created at different times for equality, we do +// not want to worry about fields which are dependent on the time of creation. +// Thus we nullify these fields before comparing the certificates. +func nullifyTimeDependency(cert *x509.Certificate) *x509.Certificate { + cert.Raw = nil + cert.RawTBSCertificate = nil + cert.RawSubject = nil + cert.RawIssuer = nil + cert.RawSubjectPublicKeyInfo = nil + cert.Signature = nil + cert.PublicKey = nil + cert.SerialNumber = nil + cert.NotBefore = time.Time{} + cert.NotAfter = time.Time{} + cert.Extensions = nil + cert.SubjectKeyId = nil + cert.AuthorityKeyId = nil + + cert.Subject.Names = nil + cert.Subject.ExtraNames = nil + cert.Issuer.Names = nil + cert.Issuer.ExtraNames = nil + + return cert +} + +// Compares two structs and returns a list containing the names of all fields +// for which the two structs hold different values. +func checkFields(struct1, struct2 interface{}, typeOfStructs reflect.Type) []string { + v1 := reflect.ValueOf(struct1) + v2 := reflect.ValueOf(struct2) + + var unequalFields []string + for i := 0; i < v1.NumField(); i++ { + if !reflect.DeepEqual(v1.Field(i).Interface(), v2.Field(i).Interface()) { + unequalFields = append(unequalFields, typeOfStructs.Field(i).Name) + } + } + + return unequalFields +} + +// Runs checkFields on the corresponding elements of chain1 and chain2. Element +// i of the returned slice contains a slice of the fields for which certificate +// i in chain1 had different values than certificate i of chain2. +func checkFieldsOfChains(chain1, chain2 []*x509.Certificate) [][]string { + minLen := math.Min(float64(len(chain1)), float64(len(chain2))) + typeOfCert := reflect.TypeOf(*chain1[0]) + + var unequalFields [][]string + for i := 0; i < int(minLen); i++ { + unequalFields = append(unequalFields, checkFields( + *chain1[i], *chain2[i], typeOfCert)) + } + + return unequalFields +} + +// Compares a certificate to a request. Returns (true, []) if both items +// contain matching data (for the things that can match). Otherwise, returns +// (false, unequalFields) where unequalFields contains the names of all fields +// which did not match. +func certEqualsRequest(cert *x509.Certificate, request csr.CertificateRequest) (bool, []string) { + equal := true + var unequalFields []string + + if cert.Subject.CommonName != request.CN { + equal = false + unequalFields = append(unequalFields, "Common Name") + } + + nameData := make(map[string]map[string]bool) + nameData["Country"] = make(map[string]bool) + nameData["Organization"] = make(map[string]bool) + nameData["OrganizationalUnit"] = make(map[string]bool) + nameData["Locality"] = make(map[string]bool) + nameData["Province"] = make(map[string]bool) + for _, name := range request.Names { + nameData["Country"][name.C] = true + nameData["Organization"][name.O] = true + nameData["OrganizationalUnit"][name.OU] = true + nameData["Locality"][name.L] = true + nameData["Province"][name.ST] = true + } + for _, country := range cert.Subject.Country { + if _, exists := nameData["Country"][country]; !exists { + equal = false + unequalFields = append(unequalFields, "Country") + } + } + for _, organization := range cert.Subject.Organization { + if _, exists := nameData["Organization"][organization]; !exists { + equal = false + unequalFields = append(unequalFields, "Organization") + } + } + for _, organizationalUnit := range cert.Subject.OrganizationalUnit { + if _, exists := nameData["OrganizationalUnit"][organizationalUnit]; !exists { + equal = false + unequalFields = append(unequalFields, "OrganizationalUnit") + } + } + for _, locality := range cert.Subject.Locality { + if _, exists := nameData["Locality"][locality]; !exists { + equal = false + unequalFields = append(unequalFields, "Locality") + } + } + for _, province := range cert.Subject.Province { + if _, exists := nameData["Province"][province]; !exists { + equal = false + unequalFields = append(unequalFields, "Province") + } + } + + // TODO: check hosts + + if cert.BasicConstraintsValid && request.CA != nil { + if cert.MaxPathLen != request.CA.PathLength { + equal = false + unequalFields = append(unequalFields, "Max Path Length") + } + // TODO: check expiry + } + + // TODO: check isCA + + return equal, unequalFields +} + +// Returns a random element of the input slice. +func randomElement(set []string) string { + return set[rand.Intn(len(set))] +} diff --git a/cmd/vendor/github.com/cloudflare/cfssl/log/log.go b/cmd/vendor/github.com/cloudflare/cfssl/log/log.go new file mode 100644 index 00000000000..956c9d4604a --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/log/log.go @@ -0,0 +1,162 @@ +// Package log implements a wrapper around the Go standard library's +// logging package. Clients should set the current log level; only +// messages below that level will actually be logged. For example, if +// Level is set to LevelWarning, only log messages at the Warning, +// Error, and Critical levels will be logged. +package log + +import ( + "fmt" + "log" + "os" +) + +// The following constants represent logging levels in increasing levels of seriousness. +const ( + // LevelDebug is the log level for Debug statements. + LevelDebug = iota + // LevelInfo is the log level for Info statements. + LevelInfo + // LevelWarning is the log level for Warning statements. + LevelWarning + // LevelError is the log level for Error statements. + LevelError + // LevelCritical is the log level for Critical statements. + LevelCritical + // LevelFatal is the log level for Fatal statements. + LevelFatal +) + +var levelPrefix = [...]string{ + LevelDebug: "DEBUG", + LevelInfo: "INFO", + LevelWarning: "WARNING", + LevelError: "ERROR", + LevelCritical: "CRITICAL", + LevelFatal: "FATAL", +} + +// Level stores the current logging level. +var Level = LevelInfo + +// SyslogWriter specifies the necessary methods for an alternate output +// destination passed in via SetLogger. +// +// SyslogWriter is satisfied by *syslog.Writer. +type SyslogWriter interface { + Debug(string) + Info(string) + Warning(string) + Err(string) + Crit(string) + Emerg(string) +} + +// syslogWriter stores the SetLogger() parameter. +var syslogWriter SyslogWriter + +// SetLogger sets the output used for output by this package. +// A *syslog.Writer is a good choice for the logger parameter. +// Call with a nil parameter to revert to default behavior. +func SetLogger(logger SyslogWriter) { + syslogWriter = logger +} + +func print(l int, msg string) { + if l >= Level { + if syslogWriter != nil { + switch l { + case LevelDebug: + syslogWriter.Debug(msg) + case LevelInfo: + syslogWriter.Info(msg) + case LevelWarning: + syslogWriter.Warning(msg) + case LevelError: + syslogWriter.Err(msg) + case LevelCritical: + syslogWriter.Crit(msg) + case LevelFatal: + syslogWriter.Emerg(msg) + } + } else { + log.Printf("[%s] %s", levelPrefix[l], msg) + } + } +} + +func outputf(l int, format string, v []interface{}) { + print(l, fmt.Sprintf(format, v...)) +} + +func output(l int, v []interface{}) { + print(l, fmt.Sprint(v...)) +} + +// Fatalf logs a formatted message at the "fatal" level and then exits. The +// arguments are handled in the same manner as fmt.Printf. +func Fatalf(format string, v ...interface{}) { + outputf(LevelFatal, format, v) + os.Exit(1) +} + +// Fatal logs its arguments at the "fatal" level and then exits. +func Fatal(v ...interface{}) { + output(LevelFatal, v) + os.Exit(1) +} + +// Criticalf logs a formatted message at the "critical" level. The +// arguments are handled in the same manner as fmt.Printf. +func Criticalf(format string, v ...interface{}) { + outputf(LevelCritical, format, v) +} + +// Critical logs its arguments at the "critical" level. +func Critical(v ...interface{}) { + output(LevelCritical, v) +} + +// Errorf logs a formatted message at the "error" level. The arguments +// are handled in the same manner as fmt.Printf. +func Errorf(format string, v ...interface{}) { + outputf(LevelError, format, v) +} + +// Error logs its arguments at the "error" level. +func Error(v ...interface{}) { + output(LevelError, v) +} + +// Warningf logs a formatted message at the "warning" level. The +// arguments are handled in the same manner as fmt.Printf. +func Warningf(format string, v ...interface{}) { + outputf(LevelWarning, format, v) +} + +// Warning logs its arguments at the "warning" level. +func Warning(v ...interface{}) { + output(LevelWarning, v) +} + +// Infof logs a formatted message at the "info" level. The arguments +// are handled in the same manner as fmt.Printf. +func Infof(format string, v ...interface{}) { + outputf(LevelInfo, format, v) +} + +// Info logs its arguments at the "info" level. +func Info(v ...interface{}) { + output(LevelInfo, v) +} + +// Debugf logs a formatted message at the "debug" level. The arguments +// are handled in the same manner as fmt.Printf. +func Debugf(format string, v ...interface{}) { + outputf(LevelDebug, format, v) +} + +// Debug logs its arguments at the "debug" level. +func Debug(v ...interface{}) { + output(LevelDebug, v) +} diff --git a/cmd/vendor/github.com/cloudflare/cfssl/log/log_test.go b/cmd/vendor/github.com/cloudflare/cfssl/log/log_test.go new file mode 100644 index 00000000000..2a08694510f --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/log/log_test.go @@ -0,0 +1,186 @@ +package log + +import ( + "bytes" + "log" + "strings" + "testing" +) + +const teststring = "asdf123" + +func TestOutputf(t *testing.T) { + buf := new(bytes.Buffer) + log.SetOutput(buf) + Level = LevelDebug + outputf(LevelDebug, teststring, nil) + + // outputf correctly prints string + if !strings.Contains(buf.String(), teststring) { + t.Fail() + } + return +} + +func TestOutput(t *testing.T) { + buf := new(bytes.Buffer) + log.SetOutput(buf) + Level = LevelDebug + output(LevelDebug, nil) + + // outputf correctly prints string with proper Debug prefix + if !strings.Contains(buf.String(), levelPrefix[LevelDebug]) { + t.Fail() + } + return +} + +func TestCriticalf(t *testing.T) { + buf := new(bytes.Buffer) + log.SetOutput(buf) + Criticalf(teststring, nil) + + // outputf correctly prints string + // should never fail because critical > debug + if !strings.Contains(buf.String(), teststring) { + t.Fail() + } + return +} + +func TestCritical(t *testing.T) { + buf := new(bytes.Buffer) + log.SetOutput(buf) + Critical(nil) + + // outputf correctly prints string + if !strings.Contains(buf.String(), levelPrefix[LevelCritical]) { + t.Fail() + } + return +} + +func TestWarningf(t *testing.T) { + buf := new(bytes.Buffer) + log.SetOutput(buf) + Warningf(teststring, nil) + + // outputf correctly prints string + // should never fail because fatal critical > debug + if !strings.Contains(buf.String(), teststring) { + t.Fail() + } + return +} + +func TestWarning(t *testing.T) { + buf := new(bytes.Buffer) + log.SetOutput(buf) + Warning(nil) + + // outputf correctly prints string + if !strings.Contains(buf.String(), levelPrefix[LevelWarning]) { + t.Fail() + } + return +} + +func TestInfof(t *testing.T) { + buf := new(bytes.Buffer) + log.SetOutput(buf) + Infof(teststring, nil) + + // outputf correctly prints string + // should never fail because fatal info > debug + if !strings.Contains(buf.String(), teststring) { + t.Fail() + } + return +} + +func TestInfo(t *testing.T) { + buf := new(bytes.Buffer) + log.SetOutput(buf) + Info(nil) + + // outputf correctly prints string + if !strings.Contains(buf.String(), levelPrefix[LevelInfo]) { + t.Fail() + } + return +} + +func TestDebugf(t *testing.T) { + buf := new(bytes.Buffer) + log.SetOutput(buf) + Level = LevelDebug + Debugf(teststring, nil) + + // outputf correctly prints string + // should never fail because fatal debug >= debug + if !strings.Contains(buf.String(), teststring) { + t.Fail() + } + return +} + +func TestDebug(t *testing.T) { + buf := new(bytes.Buffer) + log.SetOutput(buf) + Level = LevelDebug + Debug(nil) + + // outputf correctly prints string + if !strings.Contains(buf.String(), levelPrefix[LevelDebug]) { + t.Fail() + } + return +} + +type testSyslogger struct { + *bytes.Buffer +} + +func (l testSyslogger) Debug(s string) { + l.WriteString("[DEBUG] ") + _, _ = l.WriteString(s) +} + +func (l testSyslogger) Info(s string) { + l.WriteString("[INFO] ") + _, _ = l.WriteString(s) +} + +func (l testSyslogger) Warning(s string) { + l.WriteString("[WARN] ") + _, _ = l.WriteString(s) +} + +func (l testSyslogger) Err(s string) { + l.WriteString("[ERROR] ") + _, _ = l.WriteString(s) +} + +func (l testSyslogger) Crit(s string) { + l.WriteString("[CRIT] ") + _, _ = l.WriteString(s) +} + +func (l testSyslogger) Emerg(s string) { + l.WriteString("[FATAL] ") + _, _ = l.WriteString(s) +} + +func TestSetLogger(t *testing.T) { + buf := new(bytes.Buffer) + SetLogger(testSyslogger{buf}) + Level = LevelDebug + outputf(LevelDebug, teststring, nil) + + // outputf correctly prints string + if !strings.Contains(buf.String(), teststring) { + t.Fail() + } + SetLogger(nil) + return +} diff --git a/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go b/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go new file mode 100644 index 00000000000..a9e4267ba8f --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go @@ -0,0 +1,357 @@ +// Package revoke provides functionality for checking the validity of +// a cert. Specifically, the temporal validity of the certificate is +// checked first, then any CRL and OCSP url in the cert is checked. +package revoke + +import ( + "bytes" + "crypto" + "crypto/x509" + "crypto/x509/pkix" + "encoding/base64" + "encoding/pem" + "errors" + "io/ioutil" + "net/http" + neturl "net/url" + "os" + "time" + + "golang.org/x/crypto/ocsp" + + "github.com/cloudflare/cfssl/helpers" + "github.com/cloudflare/cfssl/log" +) + +// HardFail determines whether the failure to check the revocation +// status of a certificate (i.e. due to network failure) causes +// verification to fail (a hard failure). +var HardFail = false + +// CRLSet associates a PKIX certificate list with the URL the CRL is +// fetched from. +var CRLSet = map[string]*pkix.CertificateList{} + +// We can't handle LDAP certificates, so this checks to see if the +// URL string points to an LDAP resource so that we can ignore it. +func ldapURL(url string) bool { + u, err := neturl.Parse(url) + if err != nil { + log.Warningf("error parsing url %s: %v", url, err) + return false + } + if u.Scheme == "ldap" { + return true + } + return false +} + +// revCheck should check the certificate for any revocations. It +// returns a pair of booleans: the first indicates whether the certificate +// is revoked, the second indicates whether the revocations were +// successfully checked.. This leads to the following combinations: +// +// false, false: an error was encountered while checking revocations. +// +// false, true: the certificate was checked successfully and +// it is not revoked. +// +// true, true: the certificate was checked successfully and +// it is revoked. +// +// true, false: failure to check revocation status causes +// verification to fail +func revCheck(cert *x509.Certificate) (revoked, ok bool) { + for _, url := range cert.CRLDistributionPoints { + if ldapURL(url) { + log.Infof("skipping LDAP CRL: %s", url) + continue + } + + if revoked, ok := certIsRevokedCRL(cert, url); !ok { + log.Warning("error checking revocation via CRL") + if HardFail { + return true, false + } + return false, false + } else if revoked { + log.Info("certificate is revoked via CRL") + return true, true + } + + if revoked, ok := certIsRevokedOCSP(cert, HardFail); !ok { + log.Warning("error checking revocation via OCSP") + if HardFail { + return true, false + } + return false, false + } else if revoked { + log.Info("certificate is revoked via OCSP") + return true, true + } + } + + return false, true +} + +// fetchCRL fetches and parses a CRL. +func fetchCRL(url string) (*pkix.CertificateList, error) { + resp, err := http.Get(url) + if err != nil { + return nil, err + } else if resp.StatusCode >= 300 { + return nil, errors.New("failed to retrieve CRL") + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + resp.Body.Close() + + return x509.ParseCRL(body) +} + +func revLocalCheck(cert *x509.Certificate, path string) (revoked, ok bool) { + if revoked, ok := certIsRevokedCRL(cert, path); !ok { + log.Warning("error checking revocation via CRL") + if HardFail { + return true, false + } + return false, false + } else if revoked { + log.Info("certificate is revoked via CRL") + return true, true + } + + return false, true +} + +func getIssuer(cert *x509.Certificate) *x509.Certificate { + var issuer *x509.Certificate + var err error + for _, issuingCert := range cert.IssuingCertificateURL { + issuer, err = fetchRemote(issuingCert) + if err != nil { + continue + } + break + } + + return issuer + +} + +// check a cert against a specific CRL. Returns the same bool pair +// as revCheck. +func certIsRevokedCRL(cert *x509.Certificate, url string) (revoked, ok bool) { + crl, ok := CRLSet[url] + if ok && crl == nil { + ok = false + delete(CRLSet, url) + } + + var shouldFetchCRL = true + if ok { + if !crl.HasExpired(time.Now()) { + shouldFetchCRL = false + } + } + + u, err := neturl.Parse(url) + if err != nil { + log.Warningf("failed to parse CRL url: %v", err) + return false, false + } + if u.Scheme == "" && shouldFetchCRL { + if _, err := os.Stat(url); err == nil { + tmp, err := ioutil.ReadFile(url) + if err != nil { + log.Warningf("failed to read local CRL path: %v", err) + return false, false + } + crl, err = x509.ParseCRL(tmp) + if err != nil { + log.Warningf("failed to parse local CRL file: %v", err) + return false, false + } + CRLSet[url] = crl + shouldFetchCRL = false + } else { + log.Warningf("failed to read local CRL path: %v", err) + return false, false + } + } + + issuer := getIssuer(cert) + + if shouldFetchCRL { + var err error + crl, err = fetchCRL(url) + if err != nil { + log.Warningf("failed to fetch CRL: %v", err) + return false, false + } + + // check CRL signature + if issuer != nil { + err = issuer.CheckCRLSignature(crl) + if err != nil { + log.Warningf("failed to verify CRL: %v", err) + return false, false + } + } + + CRLSet[url] = crl + } + + for _, revoked := range crl.TBSCertList.RevokedCertificates { + if cert.SerialNumber.Cmp(revoked.SerialNumber) == 0 { + log.Info("Serial number match: intermediate is revoked.") + return true, true + } + } + + return false, true +} + +// VerifyCertificate ensures that the certificate passed in hasn't +// expired and checks the CRL for the server. +func VerifyCertificate(cert *x509.Certificate) (revoked, ok bool) { + if !time.Now().Before(cert.NotAfter) { + log.Infof("Certificate expired %s\n", cert.NotAfter) + return true, true + } else if !time.Now().After(cert.NotBefore) { + log.Infof("Certificate isn't valid until %s\n", cert.NotBefore) + return true, true + } + + return revCheck(cert) +} + +// VerifyCertificateByCRLPath ensures that the certificate passed in hasn't +// expired and checks the local CRL path. +func VerifyCertificateByCRLPath(cert *x509.Certificate, crlPath string) (revoked, ok bool) { + if !time.Now().Before(cert.NotAfter) { + log.Infof("Certificate expired %s\n", cert.NotAfter) + return true, true + } else if !time.Now().After(cert.NotBefore) { + log.Infof("Certificate isn't valid until %s\n", cert.NotBefore) + return true, true + } + + return revLocalCheck(cert, crlPath) +} + +func fetchRemote(url string) (*x509.Certificate, error) { + resp, err := http.Get(url) + if err != nil { + return nil, err + } + + in, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + resp.Body.Close() + + p, _ := pem.Decode(in) + if p != nil { + return helpers.ParseCertificatePEM(in) + } + + return x509.ParseCertificate(in) +} + +var ocspOpts = ocsp.RequestOptions{ + Hash: crypto.SHA1, +} + +func certIsRevokedOCSP(leaf *x509.Certificate, strict bool) (revoked, ok bool) { + var err error + + ocspURLs := leaf.OCSPServer + if len(ocspURLs) == 0 { + // OCSP not enabled for this certificate. + return false, true + } + + issuer := getIssuer(leaf) + + if issuer == nil { + return false, false + } + + ocspRequest, err := ocsp.CreateRequest(leaf, issuer, &ocspOpts) + if err != nil { + return + } + + for _, server := range ocspURLs { + resp, err := sendOCSPRequest(server, ocspRequest, issuer) + if err != nil { + if strict { + return + } + continue + } + if err = resp.CheckSignatureFrom(issuer); err != nil { + return false, false + } + + // There wasn't an error fetching the OCSP status. + ok = true + + if resp.Status != ocsp.Good { + // The certificate was revoked. + revoked = true + } + + return + } + return +} + +// sendOCSPRequest attempts to request an OCSP response from the +// server. The error only indicates a failure to *fetch* the +// certificate, and *does not* mean the certificate is valid. +func sendOCSPRequest(server string, req []byte, issuer *x509.Certificate) (*ocsp.Response, error) { + var resp *http.Response + var err error + if len(req) > 256 { + buf := bytes.NewBuffer(req) + resp, err = http.Post(server, "application/ocsp-request", buf) + } else { + reqURL := server + "/" + base64.StdEncoding.EncodeToString(req) + resp, err = http.Get(reqURL) + } + + if err != nil { + return nil, err + } + + if resp.StatusCode != http.StatusOK { + return nil, errors.New("failed to retrieve OSCP") + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + resp.Body.Close() + + switch { + case bytes.Equal(body, ocsp.UnauthorizedErrorResponse): + return nil, errors.New("OSCP unauthorized") + case bytes.Equal(body, ocsp.MalformedRequestErrorResponse): + return nil, errors.New("OSCP malformed") + case bytes.Equal(body, ocsp.InternalErrorErrorResponse): + return nil, errors.New("OSCP internal error") + case bytes.Equal(body, ocsp.TryLaterErrorResponse): + return nil, errors.New("OSCP try later") + case bytes.Equal(body, ocsp.SigRequredErrorResponse): + return nil, errors.New("OSCP signature required") + } + + return ocsp.ParseResponse(body, issuer) +} diff --git a/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke_test.go b/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke_test.go new file mode 100644 index 00000000000..03dbee8fdc5 --- /dev/null +++ b/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke_test.go @@ -0,0 +1,231 @@ +package revoke + +import ( + "crypto/x509" + //"crypto/x509/pkix" + "encoding/pem" + "fmt" + "os" + "testing" + "time" +) + +// The first three test cases represent known revoked, expired, and good +// certificates that were checked on the date listed in the log. The +// good certificate will eventually need to be replaced in year 2029. + +// If there is a soft-fail, the test will pass to mimic the default +// behaviour used in this software. However, it will print a warning +// to indicate that this is the case. + +// 2014/05/22 14:18:17 Certificate expired 2014-04-04 14:14:20 +0000 UTC +// 2014/05/22 14:18:17 Revoked certificate: misc/intermediate_ca/ActalisServerAuthenticationCA.crt +var expiredCert = mustParse(`-----BEGIN CERTIFICATE----- +MIIEXTCCA8agAwIBAgIEBycURTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV +UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU +cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds +b2JhbCBSb290MB4XDTA3MDQwNDE0MTUxNFoXDTE0MDQwNDE0MTQyMFowejELMAkG +A1UEBhMCSVQxFzAVBgNVBAoTDkFjdGFsaXMgUy5wLkEuMScwJQYDVQQLEx5DZXJ0 +aWZpY2F0aW9uIFNlcnZpY2UgUHJvdmlkZXIxKTAnBgNVBAMTIEFjdGFsaXMgU2Vy +dmVyIEF1dGhlbnRpY2F0aW9uIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAv6P0bhXbUQkVW8ox0HJ+sP5+j6pTwS7yg/wGEUektB/G1duQiT1v21fo +LANr6F353jILQDCpHIfal3MhbSsHEMKU7XaqsyLWV93bcIKbIloS/eXDfkog6KB3 +u0JHgrtNz584Jg/OLm9feffNbCJ38TiLo0/UWkAQ6PQWaOwZEgyKjVI5F3swoTB3 +g0LZAzegvkU00Kfp13cSg+cJeU4SajwtfQ+g6s6dlaekaHy/0ef46PfiHHRuhEhE +JWIpDtUN2ywTT33MSSUe5glDIiXYfcamJQrebzGsHEwyqI195Yaxb+FLNND4n3HM +e7EI2OrLyT+r/WMvQbl+xNihwtv+HwIDAQABo4IBbzCCAWswEgYDVR0TAQH/BAgw +BgEB/wIBADBTBgNVHSAETDBKMEgGCSsGAQQBsT4BADA7MDkGCCsGAQUFBwIBFi1o +dHRwOi8vd3d3LnB1YmxpYy10cnVzdC5jb20vQ1BTL09tbmlSb290Lmh0bWwwDgYD +VR0PAQH/BAQDAgEGMIGJBgNVHSMEgYEwf6F5pHcwdTELMAkGA1UEBhMCVVMxGDAW +BgNVBAoTD0dURSBDb3Jwb3JhdGlvbjEnMCUGA1UECxMeR1RFIEN5YmVyVHJ1c3Qg +U29sdXRpb25zLCBJbmMuMSMwIQYDVQQDExpHVEUgQ3liZXJUcnVzdCBHbG9iYWwg +Um9vdIICAaUwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL3d3dy5wdWJsaWMtdHJ1 +c3QuY29tL2NnaS1iaW4vQ1JMLzIwMTgvY2RwLmNybDAdBgNVHQ4EFgQUpi6OuXYt +oxHC3cTezVLuraWpAFEwDQYJKoZIhvcNAQEFBQADgYEAAtjJBwjsvw7DBs+v7BQz +gSGeg6nbYUuPL7+1driT5XsUKJ7WZjiwW2zW/WHZ+zGo1Ev8Dc574RpSrg/EIlfH +TpBiBuFgiKtJksKdoxPZGSI8FitwcgeW+y8wotmm0CtDzWN27g2kfSqHb5eHfZY5 +sESPRwHkcMUNdAp37FLweUw= +-----END CERTIFICATE-----`) + +// 2014/05/22 14:18:31 Serial number match: intermediate is revoked. +// 2014/05/22 14:18:31 certificate is revoked via CRL +// 2014/05/22 14:18:31 Revoked certificate: misc/intermediate_ca/MobileArmorEnterpriseCA.crt +var revokedCert = mustParse(`-----BEGIN CERTIFICATE----- +MIIEEzCCAvugAwIBAgILBAAAAAABGMGjftYwDQYJKoZIhvcNAQEFBQAwcTEoMCYG +A1UEAxMfR2xvYmFsU2lnbiBSb290U2lnbiBQYXJ0bmVycyBDQTEdMBsGA1UECxMU +Um9vdFNpZ24gUGFydG5lcnMgQ0ExGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2Ex +CzAJBgNVBAYTAkJFMB4XDTA4MDMxODEyMDAwMFoXDTE4MDMxODEyMDAwMFowJTEj +MCEGA1UEAxMaTW9iaWxlIEFybW9yIEVudGVycHJpc2UgQ0EwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCaEjeDR73jSZVlacRn5bc5VIPdyouHvGIBUxyS +C6483HgoDlWrWlkEndUYFjRPiQqJFthdJxfglykXD+btHixMIYbz/6eb7hRTdT9w +HKsfH+wTBIdb5AZiNjkg3QcCET5HfanJhpREjZWP513jM/GSrG3VwD6X5yttCIH1 +NFTDAr7aqpW/UPw4gcPfkwS92HPdIkb2DYnsqRrnKyNValVItkxJiotQ1HOO3YfX +ivGrHIbJdWYg0rZnkPOgYF0d+aIA4ZfwvdW48+r/cxvLevieuKj5CTBZZ8XrFt8r +JTZhZljbZvnvq/t6ZIzlwOj082f+lTssr1fJ3JsIPnG2lmgTAgMBAAGjgfcwgfQw +DgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFIZw +ns4uzXdLX6xDRXUzFgZxWM7oME0GA1UdIARGMEQwQgYJKwYBBAGgMgE8MDUwMwYI +KwYBBQUHAgIwJxolaHR0cDovL3d3dy5nbG9iYWxzaWduLmNvbS9yZXBvc2l0b3J5 +LzA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmdsb2JhbHNpZ24ubmV0L1Jv +b3RTaWduUGFydG5lcnMuY3JsMB8GA1UdIwQYMBaAFFaE7LVxpedj2NtRBNb65vBI +UknOMA0GCSqGSIb3DQEBBQUAA4IBAQBZvf+2xUJE0ekxuNk30kPDj+5u9oI3jZyM +wvhKcs7AuRAbcxPtSOnVGNYl8By7DPvPun+U3Yci8540y143RgD+kz3jxIBaoW/o +c4+X61v6DBUtcBPEt+KkV6HIsZ61SZmc/Y1I2eoeEt6JYoLjEZMDLLvc1cK/+wpg +dUZSK4O9kjvIXqvsqIOlkmh/6puSugTNao2A7EIQr8ut0ZmzKzMyZ0BuQhJDnAPd +Kz5vh+5tmytUPKA8hUgmLWe94lMb7Uqq2wgZKsqun5DAWleKu81w7wEcOrjiiB+x +jeBHq7OnpWm+ccTOPCE6H4ZN4wWVS7biEBUdop/8HgXBPQHWAdjL +-----END CERTIFICATE-----`) + +// A Comodo intermediate CA certificate with issuer url, CRL url and OCSP url +var goodComodoCA = (`-----BEGIN CERTIFICATE----- +MIIGCDCCA/CgAwIBAgIQKy5u6tl1NmwUim7bo3yMBzANBgkqhkiG9w0BAQwFADCB +hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G +A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV +BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQwMjEy +MDAwMDAwWhcNMjkwMjExMjM1OTU5WjCBkDELMAkGA1UEBhMCR0IxGzAZBgNVBAgT +EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR +Q09NT0RPIENBIExpbWl0ZWQxNjA0BgNVBAMTLUNPTU9ETyBSU0EgRG9tYWluIFZh +bGlkYXRpb24gU2VjdXJlIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP +ADCCAQoCggEBAI7CAhnhoFmk6zg1jSz9AdDTScBkxwtiBUUWOqigwAwCfx3M28Sh +bXcDow+G+eMGnD4LgYqbSRutA776S9uMIO3Vzl5ljj4Nr0zCsLdFXlIvNN5IJGS0 +Qa4Al/e+Z96e0HqnU4A7fK31llVvl0cKfIWLIpeNs4TgllfQcBhglo/uLQeTnaG6 +ytHNe+nEKpooIZFNb5JPJaXyejXdJtxGpdCsWTWM/06RQ1A/WZMebFEh7lgUq/51 +UHg+TLAchhP6a5i84DuUHoVS3AOTJBhuyydRReZw3iVDpA3hSqXttn7IzW3uLh0n +c13cRTCAquOyQQuvvUSH2rnlG51/ruWFgqUCAwEAAaOCAWUwggFhMB8GA1UdIwQY +MBaAFLuvfgI9+qbxPISOre44mOzZMjLUMB0GA1UdDgQWBBSQr2o6lFoL2JDqElZz +30O0Oija5zAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNV +HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwGwYDVR0gBBQwEjAGBgRVHSAAMAgG +BmeBDAECATBMBgNVHR8ERTBDMEGgP6A9hjtodHRwOi8vY3JsLmNvbW9kb2NhLmNv +bS9DT01PRE9SU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDBxBggrBgEFBQcB +AQRlMGMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NPTU9E +T1JTQUFkZFRydXN0Q0EuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21v +ZG9jYS5jb20wDQYJKoZIhvcNAQEMBQADggIBAE4rdk+SHGI2ibp3wScF9BzWRJ2p +mj6q1WZmAT7qSeaiNbz69t2Vjpk1mA42GHWx3d1Qcnyu3HeIzg/3kCDKo2cuH1Z/ +e+FE6kKVxF0NAVBGFfKBiVlsit2M8RKhjTpCipj4SzR7JzsItG8kO3KdY3RYPBps +P0/HEZrIqPW1N+8QRcZs2eBelSaz662jue5/DJpmNXMyYE7l3YphLG5SEXdoltMY +dVEVABt0iN3hxzgEQyjpFv3ZBdRdRydg1vs4O2xyopT4Qhrf7W8GjEXCBgCq5Ojc +2bXhc3js9iPc0d1sjhqPpepUfJa3w/5Vjo1JXvxku88+vZbrac2/4EjxYoIQ5QxG +V/Iz2tDIY+3GH5QFlkoakdH368+PUq4NCNk+qKBR6cGHdNXJ93SrLlP7u3r7l+L4 +HyaPs9Kg4DdbKDsx5Q5XLVq4rXmsXiBmGqW5prU5wfWYQ//u+aen/e7KJD2AFsQX +j4rBYKEMrltDR5FL1ZoXX/nUh8HCjLfn4g8wGTeGrODcQgPmlKidrv0PJFGUzpII +0fxQ8ANAe4hZ7Q7drNJ3gjTcBpUC2JD5Leo31Rpg0Gcg19hCC0Wvgmje3WYkN5Ap +lBlGGSW4gNfL1IYoakRwJiNiqZ+Gb7+6kHDSVneFeO/qJakXzlByjAA6quPbYzSf ++AZxAeKCINT+b72x +-----END CERTIFICATE-----`) + +var goodCert = mustParse(goodComodoCA) + +func mustParse(pemData string) *x509.Certificate { + block, _ := pem.Decode([]byte(pemData)) + if block == nil { + panic("Invalid PEM data.") + } else if block.Type != "CERTIFICATE" { + panic("Invalid PEM type.") + } + + cert, err := x509.ParseCertificate([]byte(block.Bytes)) + if err != nil { + panic(err.Error()) + } + return cert +} + +func TestRevoked(t *testing.T) { + if revoked, ok := VerifyCertificate(revokedCert); !ok { + fmt.Fprintf(os.Stderr, "Warning: soft fail checking revocation") + } else if !revoked { + t.Fatalf("revoked certificate should have been marked as revoked") + } +} + +func TestExpired(t *testing.T) { + if revoked, ok := VerifyCertificate(expiredCert); !ok { + fmt.Fprintf(os.Stderr, "Warning: soft fail checking revocation") + } else if !revoked { + t.Fatalf("expired certificate should have been marked as revoked") + } +} + +func TestGood(t *testing.T) { + if revoked, ok := VerifyCertificate(goodCert); !ok { + fmt.Fprintf(os.Stderr, "Warning: soft fail checking revocation") + } else if revoked { + t.Fatalf("good certificate should not have been marked as revoked") + } + +} + +func TestLdap(t *testing.T) { + ldapCert := mustParse(goodComodoCA) + ldapCert.CRLDistributionPoints = append(ldapCert.CRLDistributionPoints, "ldap://myldap.example.com") + if revoked, ok := VerifyCertificate(ldapCert); revoked || !ok { + t.Fatalf("ldap certificate should have been recognized") + } +} + +func TestLdapURLErr(t *testing.T) { + if ldapURL(":") { + t.Fatalf("bad url does not cause error") + } +} + +func TestCertNotYetValid(t *testing.T) { + notReadyCert := expiredCert + notReadyCert.NotBefore = time.Date(3000, time.January, 1, 1, 1, 1, 1, time.Local) + notReadyCert.NotAfter = time.Date(3005, time.January, 1, 1, 1, 1, 1, time.Local) + if revoked, _ := VerifyCertificate(expiredCert); !revoked { + t.Fatalf("not yet verified certificate should have been marked as revoked") + } +} + +func TestCRLFetchError(t *testing.T) { + ldapCert := mustParse(goodComodoCA) + ldapCert.CRLDistributionPoints[0] = "" + if revoked, ok := VerifyCertificate(ldapCert); ok || revoked { + t.Fatalf("Fetching error not encountered") + } + HardFail = true + if revoked, ok := VerifyCertificate(ldapCert); ok || !revoked { + t.Fatalf("Fetching error not encountered, hardfail not registered") + } + HardFail = false +} + +func TestBadCRLSet(t *testing.T) { + ldapCert := mustParse(goodComodoCA) + ldapCert.CRLDistributionPoints[0] = "" + CRLSet[""] = nil + certIsRevokedCRL(ldapCert, "") + if _, ok := CRLSet[""]; ok { + t.Fatalf("key emptystring should be deleted from CRLSet") + } + delete(CRLSet, "") + +} + +func TestCachedCRLSet(t *testing.T) { + VerifyCertificate(goodCert) + if revoked, ok := VerifyCertificate(goodCert); !ok || revoked { + t.Fatalf("Previously fetched CRL's should be read smoothly and unrevoked") + } +} + +func TestRemoteFetchError(t *testing.T) { + + badurl := ":" + + if _, err := fetchRemote(badurl); err == nil { + t.Fatalf("fetching bad url should result in non-nil error") + } + +} + +func TestNoOCSPServers(t *testing.T) { + badIssuer := goodCert + badIssuer.IssuingCertificateURL = []string{" "} + certIsRevokedOCSP(badIssuer, true) + noOCSPCert := goodCert + noOCSPCert.OCSPServer = make([]string, 0) + if revoked, ok := certIsRevokedOCSP(noOCSPCert, true); revoked || !ok { + t.Fatalf("OCSP falsely registered as enabled for this certificate") + } +} diff --git a/cmd/vendor/golang.org/x/crypto/ocsp/ocsp.go b/cmd/vendor/golang.org/x/crypto/ocsp/ocsp.go new file mode 100644 index 00000000000..ea61cf49852 --- /dev/null +++ b/cmd/vendor/golang.org/x/crypto/ocsp/ocsp.go @@ -0,0 +1,673 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package ocsp parses OCSP responses as specified in RFC 2560. OCSP responses +// are signed messages attesting to the validity of a certificate for a small +// period of time. This is used to manage revocation for X.509 certificates. +package ocsp // import "golang.org/x/crypto/ocsp" + +import ( + "crypto" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + "crypto/sha1" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + "math/big" + "strconv" + "time" +) + +var idPKIXOCSPBasic = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 5, 5, 7, 48, 1, 1}) + +// ResponseStatus contains the result of an OCSP request. See +// https://tools.ietf.org/html/rfc6960#section-2.3 +type ResponseStatus int + +const ( + Success ResponseStatus = 0 + Malformed ResponseStatus = 1 + InternalError ResponseStatus = 2 + TryLater ResponseStatus = 3 + // Status code four is ununsed in OCSP. See + // https://tools.ietf.org/html/rfc6960#section-4.2.1 + SignatureRequired ResponseStatus = 5 + Unauthorized ResponseStatus = 6 +) + +func (r ResponseStatus) String() string { + switch r { + case Success: + return "success" + case Malformed: + return "malformed" + case InternalError: + return "internal error" + case TryLater: + return "try later" + case SignatureRequired: + return "signature required" + case Unauthorized: + return "unauthorized" + default: + return "unknown OCSP status: " + strconv.Itoa(int(r)) + } +} + +// ResponseError is an error that may be returned by ParseResponse to indicate +// that the response itself is an error, not just that its indicating that a +// certificate is revoked, unknown, etc. +type ResponseError struct { + Status ResponseStatus +} + +func (r ResponseError) Error() string { + return "ocsp: error from server: " + r.Status.String() +} + +// These are internal structures that reflect the ASN.1 structure of an OCSP +// response. See RFC 2560, section 4.2. + +type certID struct { + HashAlgorithm pkix.AlgorithmIdentifier + NameHash []byte + IssuerKeyHash []byte + SerialNumber *big.Int +} + +// https://tools.ietf.org/html/rfc2560#section-4.1.1 +type ocspRequest struct { + TBSRequest tbsRequest +} + +type tbsRequest struct { + Version int `asn1:"explicit,tag:0,default:0,optional"` + RequestorName pkix.RDNSequence `asn1:"explicit,tag:1,optional"` + RequestList []request +} + +type request struct { + Cert certID +} + +type responseASN1 struct { + Status asn1.Enumerated + Response responseBytes `asn1:"explicit,tag:0,optional"` +} + +type responseBytes struct { + ResponseType asn1.ObjectIdentifier + Response []byte +} + +type basicResponse struct { + TBSResponseData responseData + SignatureAlgorithm pkix.AlgorithmIdentifier + Signature asn1.BitString + Certificates []asn1.RawValue `asn1:"explicit,tag:0,optional"` +} + +type responseData struct { + Raw asn1.RawContent + Version int `asn1:"optional,default:1,explicit,tag:0"` + RawResponderName asn1.RawValue `asn1:"optional,explicit,tag:1"` + KeyHash []byte `asn1:"optional,explicit,tag:2"` + ProducedAt time.Time `asn1:"generalized"` + Responses []singleResponse +} + +type singleResponse struct { + CertID certID + Good asn1.Flag `asn1:"tag:0,optional"` + Revoked revokedInfo `asn1:"tag:1,optional"` + Unknown asn1.Flag `asn1:"tag:2,optional"` + ThisUpdate time.Time `asn1:"generalized"` + NextUpdate time.Time `asn1:"generalized,explicit,tag:0,optional"` + SingleExtensions []pkix.Extension `asn1:"explicit,tag:1,optional"` +} + +type revokedInfo struct { + RevocationTime time.Time `asn1:"generalized"` + Reason asn1.Enumerated `asn1:"explicit,tag:0,optional"` +} + +var ( + oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2} + oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4} + oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5} + oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11} + oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12} + oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13} + oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3} + oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 4, 3, 2} + oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1} + oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2} + oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3} + oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4} +) + +var hashOIDs = map[crypto.Hash]asn1.ObjectIdentifier{ + crypto.SHA1: asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}), + crypto.SHA256: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 1}), + crypto.SHA384: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 2}), + crypto.SHA512: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 3}), +} + +// TODO(rlb): This is also from crypto/x509, so same comment as AGL's below +var signatureAlgorithmDetails = []struct { + algo x509.SignatureAlgorithm + oid asn1.ObjectIdentifier + pubKeyAlgo x509.PublicKeyAlgorithm + hash crypto.Hash +}{ + {x509.MD2WithRSA, oidSignatureMD2WithRSA, x509.RSA, crypto.Hash(0) /* no value for MD2 */}, + {x509.MD5WithRSA, oidSignatureMD5WithRSA, x509.RSA, crypto.MD5}, + {x509.SHA1WithRSA, oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1}, + {x509.SHA256WithRSA, oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256}, + {x509.SHA384WithRSA, oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384}, + {x509.SHA512WithRSA, oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512}, + {x509.DSAWithSHA1, oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1}, + {x509.DSAWithSHA256, oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256}, + {x509.ECDSAWithSHA1, oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1}, + {x509.ECDSAWithSHA256, oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256}, + {x509.ECDSAWithSHA384, oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384}, + {x509.ECDSAWithSHA512, oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512}, +} + +// TODO(rlb): This is also from crypto/x509, so same comment as AGL's below +func signingParamsForPublicKey(pub interface{}, requestedSigAlgo x509.SignatureAlgorithm) (hashFunc crypto.Hash, sigAlgo pkix.AlgorithmIdentifier, err error) { + var pubType x509.PublicKeyAlgorithm + + switch pub := pub.(type) { + case *rsa.PublicKey: + pubType = x509.RSA + hashFunc = crypto.SHA256 + sigAlgo.Algorithm = oidSignatureSHA256WithRSA + sigAlgo.Parameters = asn1.RawValue{ + Tag: 5, + } + + case *ecdsa.PublicKey: + pubType = x509.ECDSA + + switch pub.Curve { + case elliptic.P224(), elliptic.P256(): + hashFunc = crypto.SHA256 + sigAlgo.Algorithm = oidSignatureECDSAWithSHA256 + case elliptic.P384(): + hashFunc = crypto.SHA384 + sigAlgo.Algorithm = oidSignatureECDSAWithSHA384 + case elliptic.P521(): + hashFunc = crypto.SHA512 + sigAlgo.Algorithm = oidSignatureECDSAWithSHA512 + default: + err = errors.New("x509: unknown elliptic curve") + } + + default: + err = errors.New("x509: only RSA and ECDSA keys supported") + } + + if err != nil { + return + } + + if requestedSigAlgo == 0 { + return + } + + found := false + for _, details := range signatureAlgorithmDetails { + if details.algo == requestedSigAlgo { + if details.pubKeyAlgo != pubType { + err = errors.New("x509: requested SignatureAlgorithm does not match private key type") + return + } + sigAlgo.Algorithm, hashFunc = details.oid, details.hash + if hashFunc == 0 { + err = errors.New("x509: cannot sign with hash function requested") + return + } + found = true + break + } + } + + if !found { + err = errors.New("x509: unknown SignatureAlgorithm") + } + + return +} + +// TODO(agl): this is taken from crypto/x509 and so should probably be exported +// from crypto/x509 or crypto/x509/pkix. +func getSignatureAlgorithmFromOID(oid asn1.ObjectIdentifier) x509.SignatureAlgorithm { + for _, details := range signatureAlgorithmDetails { + if oid.Equal(details.oid) { + return details.algo + } + } + return x509.UnknownSignatureAlgorithm +} + +// TODO(rlb): This is not taken from crypto/x509, but it's of the same general form. +func getHashAlgorithmFromOID(target asn1.ObjectIdentifier) crypto.Hash { + for hash, oid := range hashOIDs { + if oid.Equal(target) { + return hash + } + } + return crypto.Hash(0) +} + +// This is the exposed reflection of the internal OCSP structures. + +// The status values that can be expressed in OCSP. See RFC 6960. +const ( + // Good means that the certificate is valid. + Good = iota + // Revoked means that the certificate has been deliberately revoked. + Revoked + // Unknown means that the OCSP responder doesn't know about the certificate. + Unknown + // ServerFailed is unused and was never used (see + // https://go-review.googlesource.com/#/c/18944). ParseResponse will + // return a ResponseError when an error response is parsed. + ServerFailed +) + +// The enumerated reasons for revoking a certificate. See RFC 5280. +const ( + Unspecified = iota + KeyCompromise = iota + CACompromise = iota + AffiliationChanged = iota + Superseded = iota + CessationOfOperation = iota + CertificateHold = iota + _ = iota + RemoveFromCRL = iota + PrivilegeWithdrawn = iota + AACompromise = iota +) + +// Request represents an OCSP request. See RFC 6960. +type Request struct { + HashAlgorithm crypto.Hash + IssuerNameHash []byte + IssuerKeyHash []byte + SerialNumber *big.Int +} + +// Response represents an OCSP response containing a single SingleResponse. See +// RFC 6960. +type Response struct { + // Status is one of {Good, Revoked, Unknown} + Status int + SerialNumber *big.Int + ProducedAt, ThisUpdate, NextUpdate, RevokedAt time.Time + RevocationReason int + Certificate *x509.Certificate + // TBSResponseData contains the raw bytes of the signed response. If + // Certificate is nil then this can be used to verify Signature. + TBSResponseData []byte + Signature []byte + SignatureAlgorithm x509.SignatureAlgorithm + + // Extensions contains raw X.509 extensions from the singleExtensions field + // of the OCSP response. When parsing certificates, this can be used to + // extract non-critical extensions that are not parsed by this package. When + // marshaling OCSP responses, the Extensions field is ignored, see + // ExtraExtensions. + Extensions []pkix.Extension + + // ExtraExtensions contains extensions to be copied, raw, into any marshaled + // OCSP response (in the singleExtensions field). Values override any + // extensions that would otherwise be produced based on the other fields. The + // ExtraExtensions field is not populated when parsing certificates, see + // Extensions. + ExtraExtensions []pkix.Extension +} + +// These are pre-serialized error responses for the various non-success codes +// defined by OCSP. The Unauthorized code in particular can be used by an OCSP +// responder that supports only pre-signed responses as a response to requests +// for certificates with unknown status. See RFC 5019. +var ( + MalformedRequestErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x01} + InternalErrorErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x02} + TryLaterErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x03} + SigRequredErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x05} + UnauthorizedErrorResponse = []byte{0x30, 0x03, 0x0A, 0x01, 0x06} +) + +// CheckSignatureFrom checks that the signature in resp is a valid signature +// from issuer. This should only be used if resp.Certificate is nil. Otherwise, +// the OCSP response contained an intermediate certificate that created the +// signature. That signature is checked by ParseResponse and only +// resp.Certificate remains to be validated. +func (resp *Response) CheckSignatureFrom(issuer *x509.Certificate) error { + return issuer.CheckSignature(resp.SignatureAlgorithm, resp.TBSResponseData, resp.Signature) +} + +// ParseError results from an invalid OCSP response. +type ParseError string + +func (p ParseError) Error() string { + return string(p) +} + +// ParseRequest parses an OCSP request in DER form. It only supports +// requests for a single certificate. Signed requests are not supported. +// If a request includes a signature, it will result in a ParseError. +func ParseRequest(bytes []byte) (*Request, error) { + var req ocspRequest + rest, err := asn1.Unmarshal(bytes, &req) + if err != nil { + return nil, err + } + if len(rest) > 0 { + return nil, ParseError("trailing data in OCSP request") + } + + if len(req.TBSRequest.RequestList) == 0 { + return nil, ParseError("OCSP request contains no request body") + } + innerRequest := req.TBSRequest.RequestList[0] + + hashFunc := getHashAlgorithmFromOID(innerRequest.Cert.HashAlgorithm.Algorithm) + if hashFunc == crypto.Hash(0) { + return nil, ParseError("OCSP request uses unknown hash function") + } + + return &Request{ + HashAlgorithm: hashFunc, + IssuerNameHash: innerRequest.Cert.NameHash, + IssuerKeyHash: innerRequest.Cert.IssuerKeyHash, + SerialNumber: innerRequest.Cert.SerialNumber, + }, nil +} + +// ParseResponse parses an OCSP response in DER form. It only supports +// responses for a single certificate. If the response contains a certificate +// then the signature over the response is checked. If issuer is not nil then +// it will be used to validate the signature or embedded certificate. +// +// Invalid signatures or parse failures will result in a ParseError. Error +// responses will result in a ResponseError. +func ParseResponse(bytes []byte, issuer *x509.Certificate) (*Response, error) { + var resp responseASN1 + rest, err := asn1.Unmarshal(bytes, &resp) + if err != nil { + return nil, err + } + if len(rest) > 0 { + return nil, ParseError("trailing data in OCSP response") + } + + if status := ResponseStatus(resp.Status); status != Success { + return nil, ResponseError{status} + } + + if !resp.Response.ResponseType.Equal(idPKIXOCSPBasic) { + return nil, ParseError("bad OCSP response type") + } + + var basicResp basicResponse + rest, err = asn1.Unmarshal(resp.Response.Response, &basicResp) + if err != nil { + return nil, err + } + + if len(basicResp.Certificates) > 1 { + return nil, ParseError("OCSP response contains bad number of certificates") + } + + if len(basicResp.TBSResponseData.Responses) != 1 { + return nil, ParseError("OCSP response contains bad number of responses") + } + + ret := &Response{ + TBSResponseData: basicResp.TBSResponseData.Raw, + Signature: basicResp.Signature.RightAlign(), + SignatureAlgorithm: getSignatureAlgorithmFromOID(basicResp.SignatureAlgorithm.Algorithm), + } + + if len(basicResp.Certificates) > 0 { + ret.Certificate, err = x509.ParseCertificate(basicResp.Certificates[0].FullBytes) + if err != nil { + return nil, err + } + + if err := ret.CheckSignatureFrom(ret.Certificate); err != nil { + return nil, ParseError("bad OCSP signature") + } + + if issuer != nil { + if err := issuer.CheckSignature(ret.Certificate.SignatureAlgorithm, ret.Certificate.RawTBSCertificate, ret.Certificate.Signature); err != nil { + return nil, ParseError("bad signature on embedded certificate") + } + } + } else if issuer != nil { + if err := ret.CheckSignatureFrom(issuer); err != nil { + return nil, ParseError("bad OCSP signature") + } + } + + r := basicResp.TBSResponseData.Responses[0] + + for _, ext := range r.SingleExtensions { + if ext.Critical { + return nil, ParseError("unsupported critical extension") + } + } + ret.Extensions = r.SingleExtensions + + ret.SerialNumber = r.CertID.SerialNumber + + switch { + case bool(r.Good): + ret.Status = Good + case bool(r.Unknown): + ret.Status = Unknown + default: + ret.Status = Revoked + ret.RevokedAt = r.Revoked.RevocationTime + ret.RevocationReason = int(r.Revoked.Reason) + } + + ret.ProducedAt = basicResp.TBSResponseData.ProducedAt + ret.ThisUpdate = r.ThisUpdate + ret.NextUpdate = r.NextUpdate + + return ret, nil +} + +// RequestOptions contains options for constructing OCSP requests. +type RequestOptions struct { + // Hash contains the hash function that should be used when + // constructing the OCSP request. If zero, SHA-1 will be used. + Hash crypto.Hash +} + +func (opts *RequestOptions) hash() crypto.Hash { + if opts == nil || opts.Hash == 0 { + // SHA-1 is nearly universally used in OCSP. + return crypto.SHA1 + } + return opts.Hash +} + +// CreateRequest returns a DER-encoded, OCSP request for the status of cert. If +// opts is nil then sensible defaults are used. +func CreateRequest(cert, issuer *x509.Certificate, opts *RequestOptions) ([]byte, error) { + hashFunc := opts.hash() + + // OCSP seems to be the only place where these raw hash identifiers are + // used. I took the following from + // http://msdn.microsoft.com/en-us/library/ff635603.aspx + var hashOID asn1.ObjectIdentifier + hashOID, ok := hashOIDs[hashFunc] + if !ok { + return nil, x509.ErrUnsupportedAlgorithm + } + + if !hashFunc.Available() { + return nil, x509.ErrUnsupportedAlgorithm + } + h := opts.hash().New() + + var publicKeyInfo struct { + Algorithm pkix.AlgorithmIdentifier + PublicKey asn1.BitString + } + if _, err := asn1.Unmarshal(issuer.RawSubjectPublicKeyInfo, &publicKeyInfo); err != nil { + return nil, err + } + + h.Write(publicKeyInfo.PublicKey.RightAlign()) + issuerKeyHash := h.Sum(nil) + + h.Reset() + h.Write(issuer.RawSubject) + issuerNameHash := h.Sum(nil) + + return asn1.Marshal(ocspRequest{ + tbsRequest{ + Version: 0, + RequestList: []request{ + { + Cert: certID{ + pkix.AlgorithmIdentifier{ + Algorithm: hashOID, + Parameters: asn1.RawValue{Tag: 5 /* ASN.1 NULL */}, + }, + issuerNameHash, + issuerKeyHash, + cert.SerialNumber, + }, + }, + }, + }, + }) +} + +// CreateResponse returns a DER-encoded OCSP response with the specified contents. +// The fields in the response are populated as follows: +// +// The responder cert is used to populate the ResponderName field, and the certificate +// itself is provided alongside the OCSP response signature. +// +// The issuer cert is used to puplate the IssuerNameHash and IssuerKeyHash fields. +// (SHA-1 is used for the hash function; this is not configurable.) +// +// The template is used to populate the SerialNumber, RevocationStatus, RevokedAt, +// RevocationReason, ThisUpdate, and NextUpdate fields. +// +// The ProducedAt date is automatically set to the current date, to the nearest minute. +func CreateResponse(issuer, responderCert *x509.Certificate, template Response, priv crypto.Signer) ([]byte, error) { + var publicKeyInfo struct { + Algorithm pkix.AlgorithmIdentifier + PublicKey asn1.BitString + } + if _, err := asn1.Unmarshal(issuer.RawSubjectPublicKeyInfo, &publicKeyInfo); err != nil { + return nil, err + } + + h := sha1.New() + h.Write(publicKeyInfo.PublicKey.RightAlign()) + issuerKeyHash := h.Sum(nil) + + h.Reset() + h.Write(issuer.RawSubject) + issuerNameHash := h.Sum(nil) + + innerResponse := singleResponse{ + CertID: certID{ + HashAlgorithm: pkix.AlgorithmIdentifier{ + Algorithm: hashOIDs[crypto.SHA1], + Parameters: asn1.RawValue{Tag: 5 /* ASN.1 NULL */}, + }, + NameHash: issuerNameHash, + IssuerKeyHash: issuerKeyHash, + SerialNumber: template.SerialNumber, + }, + ThisUpdate: template.ThisUpdate.UTC(), + NextUpdate: template.NextUpdate.UTC(), + SingleExtensions: template.ExtraExtensions, + } + + switch template.Status { + case Good: + innerResponse.Good = true + case Unknown: + innerResponse.Unknown = true + case Revoked: + innerResponse.Revoked = revokedInfo{ + RevocationTime: template.RevokedAt.UTC(), + Reason: asn1.Enumerated(template.RevocationReason), + } + } + + responderName := asn1.RawValue{ + Class: 2, // context-specific + Tag: 1, // explicit tag + IsCompound: true, + Bytes: responderCert.RawSubject, + } + tbsResponseData := responseData{ + Version: 0, + RawResponderName: responderName, + ProducedAt: time.Now().Truncate(time.Minute).UTC(), + Responses: []singleResponse{innerResponse}, + } + + tbsResponseDataDER, err := asn1.Marshal(tbsResponseData) + if err != nil { + return nil, err + } + + hashFunc, signatureAlgorithm, err := signingParamsForPublicKey(priv.Public(), template.SignatureAlgorithm) + if err != nil { + return nil, err + } + + responseHash := hashFunc.New() + responseHash.Write(tbsResponseDataDER) + signature, err := priv.Sign(rand.Reader, responseHash.Sum(nil), hashFunc) + if err != nil { + return nil, err + } + + response := basicResponse{ + TBSResponseData: tbsResponseData, + SignatureAlgorithm: signatureAlgorithm, + Signature: asn1.BitString{ + Bytes: signature, + BitLength: 8 * len(signature), + }, + } + if template.Certificate != nil { + response.Certificates = []asn1.RawValue{ + asn1.RawValue{FullBytes: template.Certificate.Raw}, + } + } + responseDER, err := asn1.Marshal(response) + if err != nil { + return nil, err + } + + return asn1.Marshal(responseASN1{ + Status: asn1.Enumerated(Success), + Response: responseBytes{ + ResponseType: idPKIXOCSPBasic, + Response: responseDER, + }, + }) +} diff --git a/cmd/vendor/golang.org/x/crypto/ocsp/ocsp_test.go b/cmd/vendor/golang.org/x/crypto/ocsp/ocsp_test.go new file mode 100644 index 00000000000..33868497381 --- /dev/null +++ b/cmd/vendor/golang.org/x/crypto/ocsp/ocsp_test.go @@ -0,0 +1,584 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package ocsp + +import ( + "bytes" + "crypto" + "crypto/sha1" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "encoding/hex" + "math/big" + "reflect" + "testing" + "time" +) + +func TestOCSPDecode(t *testing.T) { + responseBytes, _ := hex.DecodeString(ocspResponseHex) + resp, err := ParseResponse(responseBytes, nil) + if err != nil { + t.Error(err) + } + + expected := Response{ + Status: Good, + SerialNumber: big.NewInt(0x1d0fa), + RevocationReason: Unspecified, + ThisUpdate: time.Date(2010, 7, 7, 15, 1, 5, 0, time.UTC), + NextUpdate: time.Date(2010, 7, 7, 18, 35, 17, 0, time.UTC), + } + + if !reflect.DeepEqual(resp.ThisUpdate, expected.ThisUpdate) { + t.Errorf("resp.ThisUpdate: got %d, want %d", resp.ThisUpdate, expected.ThisUpdate) + } + + if !reflect.DeepEqual(resp.NextUpdate, expected.NextUpdate) { + t.Errorf("resp.NextUpdate: got %d, want %d", resp.NextUpdate, expected.NextUpdate) + } + + if resp.Status != expected.Status { + t.Errorf("resp.Status: got %d, want %d", resp.Status, expected.Status) + } + + if resp.SerialNumber.Cmp(expected.SerialNumber) != 0 { + t.Errorf("resp.SerialNumber: got %x, want %x", resp.SerialNumber, expected.SerialNumber) + } + + if resp.RevocationReason != expected.RevocationReason { + t.Errorf("resp.RevocationReason: got %d, want %d", resp.RevocationReason, expected.RevocationReason) + } +} + +func TestOCSPDecodeWithoutCert(t *testing.T) { + responseBytes, _ := hex.DecodeString(ocspResponseWithoutCertHex) + _, err := ParseResponse(responseBytes, nil) + if err != nil { + t.Error(err) + } +} + +func TestOCSPDecodeWithExtensions(t *testing.T) { + responseBytes, _ := hex.DecodeString(ocspResponseWithCriticalExtensionHex) + _, err := ParseResponse(responseBytes, nil) + if err == nil { + t.Error(err) + } + + responseBytes, _ = hex.DecodeString(ocspResponseWithExtensionHex) + response, err := ParseResponse(responseBytes, nil) + if err != nil { + t.Fatal(err) + } + + if len(response.Extensions) != 1 { + t.Errorf("len(response.Extensions): got %v, want %v", len(response.Extensions), 1) + } + + extensionBytes := response.Extensions[0].Value + expectedBytes, _ := hex.DecodeString(ocspExtensionValueHex) + if !bytes.Equal(extensionBytes, expectedBytes) { + t.Errorf("response.Extensions[0]: got %x, want %x", extensionBytes, expectedBytes) + } +} + +func TestOCSPSignature(t *testing.T) { + issuerCert, _ := hex.DecodeString(startComHex) + issuer, err := x509.ParseCertificate(issuerCert) + if err != nil { + t.Fatal(err) + } + + response, _ := hex.DecodeString(ocspResponseHex) + if _, err := ParseResponse(response, issuer); err != nil { + t.Error(err) + } +} + +func TestOCSPRequest(t *testing.T) { + leafCert, _ := hex.DecodeString(leafCertHex) + cert, err := x509.ParseCertificate(leafCert) + if err != nil { + t.Fatal(err) + } + + issuerCert, _ := hex.DecodeString(issuerCertHex) + issuer, err := x509.ParseCertificate(issuerCert) + if err != nil { + t.Fatal(err) + } + + request, err := CreateRequest(cert, issuer, nil) + if err != nil { + t.Fatal(err) + } + + expectedBytes, _ := hex.DecodeString(ocspRequestHex) + if !bytes.Equal(request, expectedBytes) { + t.Errorf("request: got %x, wanted %x", request, expectedBytes) + } + + decodedRequest, err := ParseRequest(expectedBytes) + if err != nil { + t.Fatal(err) + } + + if decodedRequest.HashAlgorithm != crypto.SHA1 { + t.Errorf("request.HashAlgorithm: got %v, want %v", decodedRequest.HashAlgorithm, crypto.SHA1) + } + + var publicKeyInfo struct { + Algorithm pkix.AlgorithmIdentifier + PublicKey asn1.BitString + } + _, err = asn1.Unmarshal(issuer.RawSubjectPublicKeyInfo, &publicKeyInfo) + if err != nil { + t.Fatal(err) + } + + h := sha1.New() + h.Write(publicKeyInfo.PublicKey.RightAlign()) + issuerKeyHash := h.Sum(nil) + + h.Reset() + h.Write(issuer.RawSubject) + issuerNameHash := h.Sum(nil) + + if got := decodedRequest.IssuerKeyHash; !bytes.Equal(got, issuerKeyHash) { + t.Errorf("request.IssuerKeyHash: got %x, want %x", got, issuerKeyHash) + } + + if got := decodedRequest.IssuerNameHash; !bytes.Equal(got, issuerNameHash) { + t.Errorf("request.IssuerKeyHash: got %x, want %x", got, issuerNameHash) + } + + if got := decodedRequest.SerialNumber; got.Cmp(cert.SerialNumber) != 0 { + t.Errorf("request.SerialNumber: got %x, want %x", got, cert.SerialNumber) + } +} + +func TestOCSPResponse(t *testing.T) { + leafCert, _ := hex.DecodeString(leafCertHex) + leaf, err := x509.ParseCertificate(leafCert) + if err != nil { + t.Fatal(err) + } + + issuerCert, _ := hex.DecodeString(issuerCertHex) + issuer, err := x509.ParseCertificate(issuerCert) + if err != nil { + t.Fatal(err) + } + + responderCert, _ := hex.DecodeString(responderCertHex) + responder, err := x509.ParseCertificate(responderCert) + if err != nil { + t.Fatal(err) + } + + responderPrivateKeyDER, _ := hex.DecodeString(responderPrivateKeyHex) + responderPrivateKey, err := x509.ParsePKCS1PrivateKey(responderPrivateKeyDER) + if err != nil { + t.Fatal(err) + } + + extensionBytes, _ := hex.DecodeString(ocspExtensionValueHex) + extensions := []pkix.Extension{ + pkix.Extension{ + Id: ocspExtensionOID, + Critical: false, + Value: extensionBytes, + }, + } + + producedAt := time.Now().Truncate(time.Minute) + thisUpdate := time.Date(2010, 7, 7, 15, 1, 5, 0, time.UTC) + nextUpdate := time.Date(2010, 7, 7, 18, 35, 17, 0, time.UTC) + template := Response{ + Status: Revoked, + SerialNumber: leaf.SerialNumber, + ThisUpdate: thisUpdate, + NextUpdate: nextUpdate, + RevokedAt: thisUpdate, + RevocationReason: KeyCompromise, + Certificate: responder, + ExtraExtensions: extensions, + } + + responseBytes, err := CreateResponse(issuer, responder, template, responderPrivateKey) + if err != nil { + t.Fatal(err) + } + + resp, err := ParseResponse(responseBytes, nil) + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(resp.ThisUpdate, template.ThisUpdate) { + t.Errorf("resp.ThisUpdate: got %d, want %d", resp.ThisUpdate, template.ThisUpdate) + } + + if !reflect.DeepEqual(resp.NextUpdate, template.NextUpdate) { + t.Errorf("resp.NextUpdate: got %d, want %d", resp.NextUpdate, template.NextUpdate) + } + + if !reflect.DeepEqual(resp.RevokedAt, template.RevokedAt) { + t.Errorf("resp.RevokedAt: got %d, want %d", resp.RevokedAt, template.RevokedAt) + } + + if !reflect.DeepEqual(resp.Extensions, template.ExtraExtensions) { + t.Errorf("resp.Extensions: got %v, want %v", resp.Extensions, template.ExtraExtensions) + } + + if !resp.ProducedAt.Equal(producedAt) { + t.Errorf("resp.ProducedAt: got %d, want %d", resp.ProducedAt, producedAt) + } + + if resp.Status != template.Status { + t.Errorf("resp.Status: got %d, want %d", resp.Status, template.Status) + } + + if resp.SerialNumber.Cmp(template.SerialNumber) != 0 { + t.Errorf("resp.SerialNumber: got %x, want %x", resp.SerialNumber, template.SerialNumber) + } + + if resp.RevocationReason != template.RevocationReason { + t.Errorf("resp.RevocationReason: got %d, want %d", resp.RevocationReason, template.RevocationReason) + } +} + +func TestErrorResponse(t *testing.T) { + responseBytes, _ := hex.DecodeString(errorResponseHex) + _, err := ParseResponse(responseBytes, nil) + + respErr, ok := err.(ResponseError) + if !ok { + t.Fatalf("expected ResponseError from ParseResponse but got %#v", err) + } + if respErr.Status != Malformed { + t.Fatalf("expected Malformed status from ParseResponse but got %d", respErr.Status) + } +} + +// This OCSP response was taken from Thawte's public OCSP responder. +// To recreate: +// $ openssl s_client -tls1 -showcerts -servername www.google.com -connect www.google.com:443 +// Copy and paste the first certificate into /tmp/cert.crt and the second into +// /tmp/intermediate.crt +// $ openssl ocsp -issuer /tmp/intermediate.crt -cert /tmp/cert.crt -url http://ocsp.thawte.com -resp_text -respout /tmp/ocsp.der +// Then hex encode the result: +// $ python -c 'print file("/tmp/ocsp.der", "r").read().encode("hex")' + +const ocspResponseHex = "308206bc0a0100a08206b5308206b106092b0601050507300101048206a23082069e3081" + + "c9a14e304c310b300906035504061302494c31163014060355040a130d5374617274436f" + + "6d204c74642e312530230603550403131c5374617274436f6d20436c6173732031204f43" + + "5350205369676e6572180f32303130303730373137333531375a30663064303c30090605" + + "2b0e03021a050004146568874f40750f016a3475625e1f5c93e5a26d580414eb4234d098" + + "b0ab9ff41b6b08f7cc642eef0e2c45020301d0fa8000180f323031303037303731353031" + + "30355aa011180f32303130303730373138333531375a300d06092a864886f70d01010505" + + "000382010100ab557ff070d1d7cebbb5f0ec91a15c3fed22eb2e1b8244f1b84545f013a4" + + "fb46214c5e3fbfbebb8a56acc2b9db19f68fd3c3201046b3824d5ba689f99864328710cb" + + "467195eb37d84f539e49f859316b32964dc3e47e36814ce94d6c56dd02733b1d0802f7ff" + + "4eebdbbd2927dcf580f16cbc290f91e81b53cb365e7223f1d6e20a88ea064104875e0145" + + "672b20fc14829d51ca122f5f5d77d3ad6c83889c55c7dc43680ba2fe3cef8b05dbcabdc0" + + "d3e09aaf9725597f8c858c2fa38c0d6aed2e6318194420dd1a1137445d13e1c97ab47896" + + "17a4e08925f46f867b72e3a4dc1f08cb870b2b0717f7207faa0ac512e628a029aba7457a" + + "e63dcf3281e2162d9349a08204ba308204b6308204b23082039aa003020102020101300d" + + "06092a864886f70d010105050030818c310b300906035504061302494c31163014060355" + + "040a130d5374617274436f6d204c74642e312b3029060355040b13225365637572652044" + + "69676974616c204365727469666963617465205369676e696e6731383036060355040313" + + "2f5374617274436f6d20436c6173732031205072696d61727920496e7465726d65646961" + + "746520536572766572204341301e170d3037313032353030323330365a170d3132313032" + + "333030323330365a304c310b300906035504061302494c31163014060355040a130d5374" + + "617274436f6d204c74642e312530230603550403131c5374617274436f6d20436c617373" + + "2031204f435350205369676e657230820122300d06092a864886f70d0101010500038201" + + "0f003082010a0282010100b9561b4c45318717178084e96e178df2255e18ed8d8ecc7c2b" + + "7b51a6c1c2e6bf0aa3603066f132fe10ae97b50e99fa24b83fc53dd2777496387d14e1c3" + + "a9b6a4933e2ac12413d085570a95b8147414a0bc007c7bcf222446ef7f1a156d7ea1c577" + + "fc5f0facdfd42eb0f5974990cb2f5cefebceef4d1bdc7ae5c1075c5a99a93171f2b0845b" + + "4ff0864e973fcfe32f9d7511ff87a3e943410c90a4493a306b6944359340a9ca96f02b66" + + "ce67f028df2980a6aaee8d5d5d452b8b0eb93f923cc1e23fcccbdbe7ffcb114d08fa7a6a" + + "3c404f825d1a0e715935cf623a8c7b59670014ed0622f6089a9447a7a19010f7fe58f841" + + "29a2765ea367824d1c3bb2fda308530203010001a382015c30820158300c0603551d1301" + + "01ff04023000300b0603551d0f0404030203a8301e0603551d250417301506082b060105" + + "0507030906092b0601050507300105301d0603551d0e0416041445e0a36695414c5dd449" + + "bc00e33cdcdbd2343e173081a80603551d230481a030819d8014eb4234d098b0ab9ff41b" + + "6b08f7cc642eef0e2c45a18181a47f307d310b300906035504061302494c311630140603" + + "55040a130d5374617274436f6d204c74642e312b3029060355040b132253656375726520" + + "4469676974616c204365727469666963617465205369676e696e67312930270603550403" + + "13205374617274436f6d2043657274696669636174696f6e20417574686f726974798201" + + "0a30230603551d12041c301a8618687474703a2f2f7777772e737461727473736c2e636f" + + "6d2f302c06096086480186f842010d041f161d5374617274436f6d205265766f63617469" + + "6f6e20417574686f72697479300d06092a864886f70d01010505000382010100182d2215" + + "8f0fc0291324fa8574c49bb8ff2835085adcbf7b7fc4191c397ab6951328253fffe1e5ec" + + "2a7da0d50fca1a404e6968481366939e666c0a6209073eca57973e2fefa9ed1718e8176f" + + "1d85527ff522c08db702e3b2b180f1cbff05d98128252cf0f450f7dd2772f4188047f19d" + + "c85317366f94bc52d60f453a550af58e308aaab00ced33040b62bf37f5b1ab2a4f7f0f80" + + "f763bf4d707bc8841d7ad9385ee2a4244469260b6f2bf085977af9074796048ecc2f9d48" + + "a1d24ce16e41a9941568fec5b42771e118f16c106a54ccc339a4b02166445a167902e75e" + + "6d8620b0825dcd18a069b90fd851d10fa8effd409deec02860d26d8d833f304b10669b42" + +const startComHex = "308206343082041ca003020102020118300d06092a864886f70d0101050500307d310b30" + + "0906035504061302494c31163014060355040a130d5374617274436f6d204c74642e312b" + + "3029060355040b1322536563757265204469676974616c20436572746966696361746520" + + "5369676e696e6731293027060355040313205374617274436f6d20436572746966696361" + + "74696f6e20417574686f72697479301e170d3037313032343230353431375a170d313731" + + "3032343230353431375a30818c310b300906035504061302494c31163014060355040a13" + + "0d5374617274436f6d204c74642e312b3029060355040b13225365637572652044696769" + + "74616c204365727469666963617465205369676e696e67313830360603550403132f5374" + + "617274436f6d20436c6173732031205072696d61727920496e7465726d65646961746520" + + "53657276657220434130820122300d06092a864886f70d01010105000382010f00308201" + + "0a0282010100b689c6acef09527807ac9263d0f44418188480561f91aee187fa3250b4d3" + + "4706f0e6075f700e10f71dc0ce103634855a0f92ac83c6ac58523fba38e8fce7a724e240" + + "a60876c0926e9e2a6d4d3f6e61200adb59ded27d63b33e46fefa215118d7cd30a6ed076e" + + "3b7087b4f9faebee823c056f92f7a4dc0a301e9373fe07cad75f809d225852ae06da8b87" + + "2369b0e42ad8ea83d2bdf371db705a280faf5a387045123f304dcd3baf17e50fcba0a95d" + + "48aab16150cb34cd3c5cc30be810c08c9bf0030362feb26c3e720eee1c432ac9480e5739" + + "c43121c810c12c87fe5495521f523c31129b7fe7c0a0a559d5e28f3ef0d5a8e1d77031a9" + + "c4b3cfaf6d532f06f4a70203010001a38201ad308201a9300f0603551d130101ff040530" + + "030101ff300e0603551d0f0101ff040403020106301d0603551d0e04160414eb4234d098" + + "b0ab9ff41b6b08f7cc642eef0e2c45301f0603551d230418301680144e0bef1aa4405ba5" + + "17698730ca346843d041aef2306606082b06010505070101045a3058302706082b060105" + + "05073001861b687474703a2f2f6f6373702e737461727473736c2e636f6d2f6361302d06" + + "082b060105050730028621687474703a2f2f7777772e737461727473736c2e636f6d2f73" + + "667363612e637274305b0603551d1f045430523027a025a0238621687474703a2f2f7777" + + "772e737461727473736c2e636f6d2f73667363612e63726c3027a025a023862168747470" + + "3a2f2f63726c2e737461727473736c2e636f6d2f73667363612e63726c3081800603551d" + + "20047930773075060b2b0601040181b5370102013066302e06082b060105050702011622" + + "687474703a2f2f7777772e737461727473736c2e636f6d2f706f6c6963792e7064663034" + + "06082b060105050702011628687474703a2f2f7777772e737461727473736c2e636f6d2f" + + "696e7465726d6564696174652e706466300d06092a864886f70d01010505000382020100" + + "2109493ea5886ee00b8b48da314d8ff75657a2e1d36257e9b556f38545753be5501f048b" + + "e6a05a3ee700ae85d0fbff200364cbad02e1c69172f8a34dd6dee8cc3fa18aa2e37c37a7" + + "c64f8f35d6f4d66e067bdd21d9cf56ffcb302249fe8904f385e5aaf1e71fe875904dddf9" + + "46f74234f745580c110d84b0c6da5d3ef9019ee7e1da5595be741c7bfc4d144fac7e5547" + + "7d7bf4a50d491e95e8f712c1ccff76a62547d0f37535be97b75816ebaa5c786fec5330af" + + "ea044dcca902e3f0b60412f630b1113d904e5664d7dc3c435f7339ef4baf87ebf6fe6888" + + "4472ead207c669b0c1a18bef1749d761b145485f3b2021e95bb2ccf4d7e931f50b15613b" + + "7a94e3ebd9bc7f94ae6ae3626296a8647cb887f399327e92a252bebbf865cfc9f230fc8b" + + "c1c2a696d75f89e15c3480f58f47072fb491bfb1a27e5f4b5ad05b9f248605515a690365" + + "434971c5e06f94346bf61bd8a9b04c7e53eb8f48dfca33b548fa364a1a53a6330cd089cd" + + "4915cd89313c90c072d7654b52358a461144b93d8e2865a63e799e5c084429adb035112e" + + "214eb8d2e7103e5d8483b3c3c2e4d2c6fd094b7409ddf1b3d3193e800da20b19f038e7c5" + + "c2afe223db61e29d5c6e2089492e236ab262c145b49faf8ba7f1223bf87de290d07a19fb" + + "4a4ce3d27d5f4a8303ed27d6239e6b8db459a2d9ef6c8229dd75193c3f4c108defbb7527" + + "d2ae83a7a8ce5ba7" + +const ocspResponseWithoutCertHex = "308201d40a0100a08201cd308201c906092b0601050507300101048201ba3082" + + "01b630819fa2160414884451ff502a695e2d88f421bad90cf2cecbea7c180f3230313330" + + "3631383037323434335a30743072304a300906052b0e03021a0500041448b60d38238df8" + + "456e4ee5843ea394111802979f0414884451ff502a695e2d88f421bad90cf2cecbea7c02" + + "1100f78b13b946fc9635d8ab49de9d2148218000180f3230313330363138303732343433" + + "5aa011180f32303133303632323037323434335a300d06092a864886f70d010105050003" + + "82010100103e18b3d297a5e7a6c07a4fc52ac46a15c0eba96f3be17f0ffe84de5b8c8e05" + + "5a8f577586a849dc4abd6440eb6fedde4622451e2823c1cbf3558b4e8184959c9fe96eff" + + "8bc5f95866c58c6d087519faabfdae37e11d9874f1bc0db292208f645dd848185e4dd38b" + + "6a8547dfa7b74d514a8470015719064d35476b95bebb03d4d2845c5ca15202d2784878f2" + + "0f904c24f09736f044609e9c271381713400e563023d212db422236440c6f377bbf24b2b" + + "9e7dec8698e36a8df68b7592ad3489fb2937afb90eb85d2aa96b81c94c25057dbd4759d9" + + "20a1a65c7f0b6427a224b3c98edd96b9b61f706099951188b0289555ad30a216fb774651" + + "5a35fca2e054dfa8" + +// PKIX nonce extension +var ocspExtensionOID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 48, 1, 2} +var ocspExtensionValueHex = "0403000000" + +const ocspResponseWithCriticalExtensionHex = "308204fe0a0100a08204f7308204f306092b0601050507300101048204e4308204e03081" + + "dba003020100a11b3019311730150603550403130e4f43535020526573706f6e64657218" + + "0f32303136303130343137303130305a3081a53081a23049300906052b0e03021a050004" + + "14c0fe0278fc99188891b3f212e9c7e1b21ab7bfc004140dfc1df0a9e0f01ce7f2b21317" + + "7e6f8d157cd4f60210017f77deb3bcbb235d44ccc7dba62e72a116180f32303130303730" + + "373135303130355aa0030a0101180f32303130303730373135303130355aa011180f3230" + + "3130303730373138333531375aa1193017301506092b06010505073001020101ff040504" + + "03000000300d06092a864886f70d01010b0500038201010031c730ca60a7a0d92d8e4010" + + "911b469de95b4d27e89de6537552436237967694f76f701cf6b45c932bd308bca4a8d092" + + "5c604ba94796903091d9e6c000178e72c1f0a24a277dd262835af5d17d3f9d7869606c9f" + + "e7c8e708a41645699895beee38bfa63bb46296683761c5d1d65439b8ab868dc3017c9eeb" + + "b70b82dbf3a31c55b457d48bb9e82b335ed49f445042eaf606b06a3e0639824924c89c63" + + "eccddfe85e6694314138b2536f5e15e07085d0f6e26d4b2f8244bab0d70de07283ac6384" + + "a0501fc3dea7cf0adfd4c7f34871080900e252ddc403e3f0265f2a704af905d3727504ed" + + "28f3214a219d898a022463c78439799ca81c8cbafdbcec34ea937cd6a08202ea308202e6" + + "308202e2308201caa003020102020101300d06092a864886f70d01010b05003019311730" + + "150603550403130e4f43535020526573706f6e646572301e170d31353031333031353530" + + "33335a170d3136303133303135353033335a3019311730150603550403130e4f43535020" + + "526573706f6e64657230820122300d06092a864886f70d01010105000382010f00308201" + + "0a0282010100e8155f2d3e6f2e8d14c62a788bd462f9f844e7a6977c83ef1099f0f6616e" + + "c5265b56f356e62c5400f0b06a2e7945a82752c636df32a895152d6074df1701dc6ccfbc" + + "bec75a70bd2b55ae2be7e6cad3b5fd4cd5b7790ab401a436d3f5f346074ffde8a99d5b72" + + "3350f0a112076614b12ef79c78991b119453445acf2416ab0046b540db14c9fc0f27b898" + + "9ad0f63aa4b8aefc91aa8a72160c36307c60fec78a93d3fddf4259902aa77e7332971c7d" + + "285b6a04f648993c6922a3e9da9adf5f81508c3228791843e5d49f24db2f1290bafd97e6" + + "55b1049a199f652cd603c4fafa330c390b0da78fbbc67e8fa021cbd74eb96222b12ace31" + + "a77dcf920334dc94581b0203010001a3353033300e0603551d0f0101ff04040302078030" + + "130603551d25040c300a06082b06010505070309300c0603551d130101ff04023000300d" + + "06092a864886f70d01010b05000382010100718012761b5063e18f0dc44644d8e6ab8612" + + "31c15fd5357805425d82aec1de85bf6d3e30fce205e3e3b8b795bbe52e40a439286d2288" + + "9064f4aeeb150359b9425f1da51b3a5c939018555d13ac42c565a0603786a919328f3267" + + "09dce52c22ad958ecb7873b9771d1148b1c4be2efe80ba868919fc9f68b6090c2f33c156" + + "d67156e42766a50b5d51e79637b7e58af74c2a951b1e642fa7741fec982cc937de37eff5" + + "9e2005d5939bfc031589ca143e6e8ab83f40ee08cc20a6b4a95a318352c28d18528dcaf9" + + "66705de17afa19d6e8ae91ddf33179d16ebb6ac2c69cae8373d408ebf8c55308be6c04d9" + + "3a25439a94299a65a709756c7a3e568be049d5c38839" + +const ocspResponseWithExtensionHex = "308204fb0a0100a08204f4308204f006092b0601050507300101048204e1308204dd3081" + + "d8a003020100a11b3019311730150603550403130e4f43535020526573706f6e64657218" + + "0f32303136303130343136353930305a3081a230819f3049300906052b0e03021a050004" + + "14c0fe0278fc99188891b3f212e9c7e1b21ab7bfc004140dfc1df0a9e0f01ce7f2b21317" + + "7e6f8d157cd4f60210017f77deb3bcbb235d44ccc7dba62e72a116180f32303130303730" + + "373135303130355aa0030a0101180f32303130303730373135303130355aa011180f3230" + + "3130303730373138333531375aa1163014301206092b0601050507300102040504030000" + + "00300d06092a864886f70d01010b05000382010100c09a33e0b2324c852421bb83f85ac9" + + "9113f5426012bd2d2279a8166e9241d18a33c870894250622ffc7ed0c4601b16d624f90b" + + "779265442cdb6868cf40ab304ab4b66e7315ed02cf663b1601d1d4751772b31bc299db23" + + "9aebac78ed6797c06ed815a7a8d18d63cfbb609cafb47ec2e89e37db255216eb09307848" + + "d01be0a3e943653c78212b96ff524b74c9ec456b17cdfb950cc97645c577b2e09ff41dde" + + "b03afb3adaa381cc0f7c1d95663ef22a0f72f2c45613ae8e2b2d1efc96e8463c7d1d8a1d" + + "7e3b35df8fe73a301fc3f804b942b2b3afa337ff105fc1462b7b1c1d75eb4566c8665e59" + + "f80393b0adbf8004ff6c3327ed34f007cb4a3348a7d55e06e3a08202ea308202e6308202" + + "e2308201caa003020102020101300d06092a864886f70d01010b05003019311730150603" + + "550403130e4f43535020526573706f6e646572301e170d3135303133303135353033335a" + + "170d3136303133303135353033335a3019311730150603550403130e4f43535020526573" + + "706f6e64657230820122300d06092a864886f70d01010105000382010f003082010a0282" + + "010100e8155f2d3e6f2e8d14c62a788bd462f9f844e7a6977c83ef1099f0f6616ec5265b" + + "56f356e62c5400f0b06a2e7945a82752c636df32a895152d6074df1701dc6ccfbcbec75a" + + "70bd2b55ae2be7e6cad3b5fd4cd5b7790ab401a436d3f5f346074ffde8a99d5b723350f0" + + "a112076614b12ef79c78991b119453445acf2416ab0046b540db14c9fc0f27b8989ad0f6" + + "3aa4b8aefc91aa8a72160c36307c60fec78a93d3fddf4259902aa77e7332971c7d285b6a" + + "04f648993c6922a3e9da9adf5f81508c3228791843e5d49f24db2f1290bafd97e655b104" + + "9a199f652cd603c4fafa330c390b0da78fbbc67e8fa021cbd74eb96222b12ace31a77dcf" + + "920334dc94581b0203010001a3353033300e0603551d0f0101ff04040302078030130603" + + "551d25040c300a06082b06010505070309300c0603551d130101ff04023000300d06092a" + + "864886f70d01010b05000382010100718012761b5063e18f0dc44644d8e6ab861231c15f" + + "d5357805425d82aec1de85bf6d3e30fce205e3e3b8b795bbe52e40a439286d22889064f4" + + "aeeb150359b9425f1da51b3a5c939018555d13ac42c565a0603786a919328f326709dce5" + + "2c22ad958ecb7873b9771d1148b1c4be2efe80ba868919fc9f68b6090c2f33c156d67156" + + "e42766a50b5d51e79637b7e58af74c2a951b1e642fa7741fec982cc937de37eff59e2005" + + "d5939bfc031589ca143e6e8ab83f40ee08cc20a6b4a95a318352c28d18528dcaf966705d" + + "e17afa19d6e8ae91ddf33179d16ebb6ac2c69cae8373d408ebf8c55308be6c04d93a2543" + + "9a94299a65a709756c7a3e568be049d5c38839" + +const ocspRequestHex = "3051304f304d304b3049300906052b0e03021a05000414c0fe0278fc99188891b3f212e9" + + "c7e1b21ab7bfc004140dfc1df0a9e0f01ce7f2b213177e6f8d157cd4f60210017f77deb3" + + "bcbb235d44ccc7dba62e72" + +const leafCertHex = "308203c830820331a0030201020210017f77deb3bcbb235d44ccc7dba62e72300d06092a" + + "864886f70d01010505003081ba311f301d060355040a1316566572695369676e20547275" + + "7374204e6574776f726b31173015060355040b130e566572695369676e2c20496e632e31" + + "333031060355040b132a566572695369676e20496e7465726e6174696f6e616c20536572" + + "766572204341202d20436c617373203331493047060355040b13407777772e7665726973" + + "69676e2e636f6d2f43505320496e636f72702e6279205265662e204c494142494c495459" + + "204c54442e286329393720566572695369676e301e170d3132303632313030303030305a" + + "170d3133313233313233353935395a3068310b3009060355040613025553311330110603" + + "550408130a43616c69666f726e6961311230100603550407130950616c6f20416c746f31" + + "173015060355040a130e46616365626f6f6b2c20496e632e311730150603550403140e2a" + + "2e66616365626f6f6b2e636f6d30819f300d06092a864886f70d010101050003818d0030" + + "818902818100ae94b171e2deccc1693e051063240102e0689ae83c39b6b3e74b97d48d7b" + + "23689100b0b496ee62f0e6d356bcf4aa0f50643402f5d1766aa972835a7564723f39bbef" + + "5290ded9bcdbf9d3d55dfad23aa03dc604c54d29cf1d4b3bdbd1a809cfae47b44c7eae17" + + "c5109bee24a9cf4a8d911bb0fd0415ae4c3f430aa12a557e2ae10203010001a382011e30" + + "82011a30090603551d130402300030440603551d20043d303b3039060b6086480186f845" + + "01071703302a302806082b06010505070201161c68747470733a2f2f7777772e76657269" + + "7369676e2e636f6d2f727061303c0603551d1f043530333031a02fa02d862b687474703a" + + "2f2f535652496e746c2d63726c2e766572697369676e2e636f6d2f535652496e746c2e63" + + "726c301d0603551d250416301406082b0601050507030106082b06010505070302300b06" + + "03551d0f0404030205a0303406082b0601050507010104283026302406082b0601050507" + + "30018618687474703a2f2f6f6373702e766572697369676e2e636f6d30270603551d1104" + + "20301e820e2a2e66616365626f6f6b2e636f6d820c66616365626f6f6b2e636f6d300d06" + + "092a864886f70d0101050500038181005b6c2b75f8ed30aa51aad36aba595e555141951f" + + "81a53b447910ac1f76ff78fc2781616b58f3122afc1c87010425e9ed43df1a7ba6498060" + + "67e2688af03db58c7df4ee03309a6afc247ccb134dc33e54c6bc1d5133a532a73273b1d7" + + "9cadc08e7e1a83116d34523340b0305427a21742827c98916698ee7eaf8c3bdd71700817" + +const issuerCertHex = "30820383308202eca003020102021046fcebbab4d02f0f926098233f93078f300d06092a" + + "864886f70d0101050500305f310b300906035504061302555331173015060355040a130e" + + "566572695369676e2c20496e632e31373035060355040b132e436c617373203320507562" + + "6c6963205072696d6172792043657274696669636174696f6e20417574686f7269747930" + + "1e170d3937303431373030303030305a170d3136313032343233353935395a3081ba311f" + + "301d060355040a1316566572695369676e205472757374204e6574776f726b3117301506" + + "0355040b130e566572695369676e2c20496e632e31333031060355040b132a5665726953" + + "69676e20496e7465726e6174696f6e616c20536572766572204341202d20436c61737320" + + "3331493047060355040b13407777772e766572697369676e2e636f6d2f43505320496e63" + + "6f72702e6279205265662e204c494142494c495459204c54442e28632939372056657269" + + "5369676e30819f300d06092a864886f70d010101050003818d0030818902818100d88280" + + "e8d619027d1f85183925a2652be1bfd405d3bce6363baaf04c6c5bb6e7aa3c734555b2f1" + + "bdea9742ed9a340a15d4a95cf54025ddd907c132b2756cc4cabba3fe56277143aa63f530" + + "3e9328e5faf1093bf3b74d4e39f75c495ab8c11dd3b28afe70309542cbfe2b518b5a3c3a" + + "f9224f90b202a7539c4f34e7ab04b27b6f0203010001a381e33081e0300f0603551d1304" + + "0830060101ff02010030440603551d20043d303b3039060b6086480186f8450107010130" + + "2a302806082b06010505070201161c68747470733a2f2f7777772e766572697369676e2e" + + "636f6d2f43505330340603551d25042d302b06082b0601050507030106082b0601050507" + + "030206096086480186f8420401060a6086480186f845010801300b0603551d0f04040302" + + "0106301106096086480186f842010104040302010630310603551d1f042a30283026a024" + + "a0228620687474703a2f2f63726c2e766572697369676e2e636f6d2f706361332e63726c" + + "300d06092a864886f70d010105050003818100408e4997968a73dd8e4def3e61b7caa062" + + "adf40e0abb753de26ed82cc7bff4b98c369bcaa2d09c724639f6a682036511c4bcbf2da6" + + "f5d93b0ab598fab378b91ef22b4c62d5fdb27a1ddf33fd73f9a5d82d8c2aead1fcb028b6" + + "e94948134b838a1b487b24f738de6f4154b8ab576b06dfc7a2d4a9f6f136628088f28b75" + + "d68071" + +// Key and certificate for the OCSP responder were not taken from the Thawte +// responder, since CreateResponse requires that we have the private key. +// Instead, they were generated randomly. +const responderPrivateKeyHex = "308204a40201000282010100e8155f2d3e6f2e8d14c62a788bd462f9f844e7a6977c83ef" + + "1099f0f6616ec5265b56f356e62c5400f0b06a2e7945a82752c636df32a895152d6074df" + + "1701dc6ccfbcbec75a70bd2b55ae2be7e6cad3b5fd4cd5b7790ab401a436d3f5f346074f" + + "fde8a99d5b723350f0a112076614b12ef79c78991b119453445acf2416ab0046b540db14" + + "c9fc0f27b8989ad0f63aa4b8aefc91aa8a72160c36307c60fec78a93d3fddf4259902aa7" + + "7e7332971c7d285b6a04f648993c6922a3e9da9adf5f81508c3228791843e5d49f24db2f" + + "1290bafd97e655b1049a199f652cd603c4fafa330c390b0da78fbbc67e8fa021cbd74eb9" + + "6222b12ace31a77dcf920334dc94581b02030100010282010100bcf0b93d7238bda329a8" + + "72e7149f61bcb37c154330ccb3f42a85c9002c2e2bdea039d77d8581cd19bed94078794e" + + "56293d601547fc4bf6a2f9002fe5772b92b21b254403b403585e3130cc99ccf08f0ef81a" + + "575b38f597ba4660448b54f44bfbb97072b5a2bf043bfeca828cf7741d13698e3f38162b" + + "679faa646b82abd9a72c5c7d722c5fc577a76d2c2daac588accad18516d1bbad10b0dfa2" + + "05cfe246b59e28608a43942e1b71b0c80498075121de5b900d727c31c42c78cf1db5c0aa" + + "5b491e10ea4ed5c0962aaf2ae025dd81fa4ce490d9d6b4a4465411d8e542fc88617e5695" + + "1aa4fc8ea166f2b4d0eb89ef17f2b206bd5f1014bf8fe0e71fe62f2cccf102818100f2dc" + + "ddf878d553286daad68bac4070a82ffec3dc4666a2750f47879eec913f91836f1d976b60" + + "daf9356e078446dafab5bd2e489e5d64f8572ba24a4ba4f3729b5e106c4dd831cc2497a7" + + "e6c7507df05cb64aeb1bbc81c1e340d58b5964cf39cff84ea30c29ec5d3f005ee1362698" + + "07395037955955655292c3e85f6187fa1f9502818100f4a33c102630840705f8c778a47b" + + "87e8da31e68809af981ac5e5999cf1551685d761cdf0d6520361b99aebd5777a940fa64d" + + "327c09fa63746fbb3247ec73a86edf115f1fe5c83598db803881ade71c33c6e956118345" + + "497b98b5e07bb5be75971465ec78f2f9467e1b74956ca9d4c7c3e314e742a72d8b33889c" + + "6c093a466cef0281801d3df0d02124766dd0be98349b19eb36a508c4e679e793ba0a8bef" + + "4d786888c1e9947078b1ea28938716677b4ad8c5052af12eb73ac194915264a913709a0b" + + "7b9f98d4a18edd781a13d49899f91c20dbd8eb2e61d991ba19b5cdc08893f5cb9d39e5a6" + + "0629ea16d426244673b1b3ee72bd30e41fac8395acac40077403de5efd028180050731dd" + + "d71b1a2b96c8d538ba90bb6b62c8b1c74c03aae9a9f59d21a7a82b0d572ef06fa9c807bf" + + "c373d6b30d809c7871df96510c577421d9860c7383fda0919ece19996b3ca13562159193" + + "c0c246471e287f975e8e57034e5136aaf44254e2650def3d51292474c515b1588969112e" + + "0a85cc77073e9d64d2c2fc497844284b02818100d71d63eabf416cf677401ebf965f8314" + + "120b568a57dd3bd9116c629c40dc0c6948bab3a13cc544c31c7da40e76132ef5dd3f7534" + + "45a635930c74326ae3df0edd1bfb1523e3aa259873ac7cf1ac31151ec8f37b528c275622" + + "48f99b8bed59fd4da2576aa6ee20d93a684900bf907e80c66d6e2261ae15e55284b4ed9d" + + "6bdaa059" + +const responderCertHex = "308202e2308201caa003020102020101300d06092a864886f70d01010b05003019311730" + + "150603550403130e4f43535020526573706f6e646572301e170d31353031333031353530" + + "33335a170d3136303133303135353033335a3019311730150603550403130e4f43535020" + + "526573706f6e64657230820122300d06092a864886f70d01010105000382010f00308201" + + "0a0282010100e8155f2d3e6f2e8d14c62a788bd462f9f844e7a6977c83ef1099f0f6616e" + + "c5265b56f356e62c5400f0b06a2e7945a82752c636df32a895152d6074df1701dc6ccfbc" + + "bec75a70bd2b55ae2be7e6cad3b5fd4cd5b7790ab401a436d3f5f346074ffde8a99d5b72" + + "3350f0a112076614b12ef79c78991b119453445acf2416ab0046b540db14c9fc0f27b898" + + "9ad0f63aa4b8aefc91aa8a72160c36307c60fec78a93d3fddf4259902aa77e7332971c7d" + + "285b6a04f648993c6922a3e9da9adf5f81508c3228791843e5d49f24db2f1290bafd97e6" + + "55b1049a199f652cd603c4fafa330c390b0da78fbbc67e8fa021cbd74eb96222b12ace31" + + "a77dcf920334dc94581b0203010001a3353033300e0603551d0f0101ff04040302078030" + + "130603551d25040c300a06082b06010505070309300c0603551d130101ff04023000300d" + + "06092a864886f70d01010b05000382010100718012761b5063e18f0dc44644d8e6ab8612" + + "31c15fd5357805425d82aec1de85bf6d3e30fce205e3e3b8b795bbe52e40a439286d2288" + + "9064f4aeeb150359b9425f1da51b3a5c939018555d13ac42c565a0603786a919328f3267" + + "09dce52c22ad958ecb7873b9771d1148b1c4be2efe80ba868919fc9f68b6090c2f33c156" + + "d67156e42766a50b5d51e79637b7e58af74c2a951b1e642fa7741fec982cc937de37eff5" + + "9e2005d5939bfc031589ca143e6e8ab83f40ee08cc20a6b4a95a318352c28d18528dcaf9" + + "66705de17afa19d6e8ae91ddf33179d16ebb6ac2c69cae8373d408ebf8c55308be6c04d9" + + "3a25439a94299a65a709756c7a3e568be049d5c38839" + +const errorResponseHex = "30030a0101" diff --git a/cmd/vendor/golang.org/x/crypto/pkcs12/bmp-string.go b/cmd/vendor/golang.org/x/crypto/pkcs12/bmp-string.go new file mode 100644 index 00000000000..284d2a68f1e --- /dev/null +++ b/cmd/vendor/golang.org/x/crypto/pkcs12/bmp-string.go @@ -0,0 +1,50 @@ +// 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 pkcs12 + +import ( + "errors" + "unicode/utf16" +) + +// bmpString returns s encoded in UCS-2 with a zero terminator. +func bmpString(s string) ([]byte, error) { + // References: + // https://tools.ietf.org/html/rfc7292#appendix-B.1 + // http://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane + // - non-BMP characters are encoded in UTF 16 by using a surrogate pair of 16-bit codes + // EncodeRune returns 0xfffd if the rune does not need special encoding + // - the above RFC provides the info that BMPStrings are NULL terminated. + + ret := make([]byte, 0, 2*len(s)+2) + + for _, r := range s { + if t, _ := utf16.EncodeRune(r); t != 0xfffd { + return nil, errors.New("pkcs12: string contains characters that cannot be encoded in UCS-2") + } + ret = append(ret, byte(r/256), byte(r%256)) + } + + return append(ret, 0, 0), nil +} + +func decodeBMPString(bmpString []byte) (string, error) { + if len(bmpString)%2 != 0 { + return "", errors.New("pkcs12: odd-length BMP string") + } + + // strip terminator if present + if l := len(bmpString); l >= 2 && bmpString[l-1] == 0 && bmpString[l-2] == 0 { + bmpString = bmpString[:l-2] + } + + s := make([]uint16, 0, len(bmpString)/2) + for len(bmpString) > 0 { + s = append(s, uint16(bmpString[0])<<8+uint16(bmpString[1])) + bmpString = bmpString[2:] + } + + return string(utf16.Decode(s)), nil +} diff --git a/cmd/vendor/golang.org/x/crypto/pkcs12/bmp-string_test.go b/cmd/vendor/golang.org/x/crypto/pkcs12/bmp-string_test.go new file mode 100644 index 00000000000..7fca55f4e8b --- /dev/null +++ b/cmd/vendor/golang.org/x/crypto/pkcs12/bmp-string_test.go @@ -0,0 +1,63 @@ +// 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 pkcs12 + +import ( + "bytes" + "encoding/hex" + "testing" +) + +var bmpStringTests = []struct { + in string + expectedHex string + shouldFail bool +}{ + {"", "0000", false}, + // Example from https://tools.ietf.org/html/rfc7292#appendix-B. + {"Beavis", "0042006500610076006900730000", false}, + // Some characters from the "Letterlike Symbols Unicode block". + {"\u2115 - Double-struck N", "21150020002d00200044006f00750062006c0065002d00730074007200750063006b0020004e0000", false}, + // any character outside the BMP should trigger an error. + {"\U0001f000 East wind (Mahjong)", "", true}, +} + +func TestBMPString(t *testing.T) { + for i, test := range bmpStringTests { + expected, err := hex.DecodeString(test.expectedHex) + if err != nil { + t.Fatalf("#%d: failed to decode expectation", i) + } + + out, err := bmpString(test.in) + if err == nil && test.shouldFail { + t.Errorf("#%d: expected to fail, but produced %x", i, out) + continue + } + + if err != nil && !test.shouldFail { + t.Errorf("#%d: failed unexpectedly: %s", i, err) + continue + } + + if !test.shouldFail { + if !bytes.Equal(out, expected) { + t.Errorf("#%d: expected %s, got %x", i, test.expectedHex, out) + continue + } + + roundTrip, err := decodeBMPString(out) + if err != nil { + t.Errorf("#%d: decoding output gave an error: %s", i, err) + continue + } + + if roundTrip != test.in { + t.Errorf("#%d: decoding output resulted in %q, but it should have been %q", i, roundTrip, test.in) + continue + } + } + } +} diff --git a/cmd/vendor/golang.org/x/crypto/pkcs12/crypto.go b/cmd/vendor/golang.org/x/crypto/pkcs12/crypto.go new file mode 100644 index 00000000000..4bd4470ec04 --- /dev/null +++ b/cmd/vendor/golang.org/x/crypto/pkcs12/crypto.go @@ -0,0 +1,131 @@ +// 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 pkcs12 + +import ( + "bytes" + "crypto/cipher" + "crypto/des" + "crypto/x509/pkix" + "encoding/asn1" + "errors" + + "golang.org/x/crypto/pkcs12/internal/rc2" +) + +var ( + oidPBEWithSHAAnd3KeyTripleDESCBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3}) + oidPBEWithSHAAnd40BitRC2CBC = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 6}) +) + +// pbeCipher is an abstraction of a PKCS#12 cipher. +type pbeCipher interface { + // create returns a cipher.Block given a key. + create(key []byte) (cipher.Block, error) + // deriveKey returns a key derived from the given password and salt. + deriveKey(salt, password []byte, iterations int) []byte + // deriveKey returns an IV derived from the given password and salt. + deriveIV(salt, password []byte, iterations int) []byte +} + +type shaWithTripleDESCBC struct{} + +func (shaWithTripleDESCBC) create(key []byte) (cipher.Block, error) { + return des.NewTripleDESCipher(key) +} + +func (shaWithTripleDESCBC) deriveKey(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 24) +} + +func (shaWithTripleDESCBC) deriveIV(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8) +} + +type shaWith40BitRC2CBC struct{} + +func (shaWith40BitRC2CBC) create(key []byte) (cipher.Block, error) { + return rc2.New(key, len(key)*8) +} + +func (shaWith40BitRC2CBC) deriveKey(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 1, 5) +} + +func (shaWith40BitRC2CBC) deriveIV(salt, password []byte, iterations int) []byte { + return pbkdf(sha1Sum, 20, 64, salt, password, iterations, 2, 8) +} + +type pbeParams struct { + Salt []byte + Iterations int +} + +func pbDecrypterFor(algorithm pkix.AlgorithmIdentifier, password []byte) (cipher.BlockMode, int, error) { + var cipherType pbeCipher + + switch { + case algorithm.Algorithm.Equal(oidPBEWithSHAAnd3KeyTripleDESCBC): + cipherType = shaWithTripleDESCBC{} + case algorithm.Algorithm.Equal(oidPBEWithSHAAnd40BitRC2CBC): + cipherType = shaWith40BitRC2CBC{} + default: + return nil, 0, NotImplementedError("algorithm " + algorithm.Algorithm.String() + " is not supported") + } + + var params pbeParams + if err := unmarshal(algorithm.Parameters.FullBytes, ¶ms); err != nil { + return nil, 0, err + } + + key := cipherType.deriveKey(params.Salt, password, params.Iterations) + iv := cipherType.deriveIV(params.Salt, password, params.Iterations) + + block, err := cipherType.create(key) + if err != nil { + return nil, 0, err + } + + return cipher.NewCBCDecrypter(block, iv), block.BlockSize(), nil +} + +func pbDecrypt(info decryptable, password []byte) (decrypted []byte, err error) { + cbc, blockSize, err := pbDecrypterFor(info.Algorithm(), password) + if err != nil { + return nil, err + } + + encrypted := info.Data() + if len(encrypted) == 0 { + return nil, errors.New("pkcs12: empty encrypted data") + } + if len(encrypted)%blockSize != 0 { + return nil, errors.New("pkcs12: input is not a multiple of the block size") + } + decrypted = make([]byte, len(encrypted)) + cbc.CryptBlocks(decrypted, encrypted) + + psLen := int(decrypted[len(decrypted)-1]) + if psLen == 0 || psLen > blockSize { + return nil, ErrDecryption + } + + if len(decrypted) < psLen { + return nil, ErrDecryption + } + ps := decrypted[len(decrypted)-psLen:] + decrypted = decrypted[:len(decrypted)-psLen] + if bytes.Compare(ps, bytes.Repeat([]byte{byte(psLen)}, psLen)) != 0 { + return nil, ErrDecryption + } + + return +} + +// decryptable abstracts a object that contains ciphertext. +type decryptable interface { + Algorithm() pkix.AlgorithmIdentifier + Data() []byte +} diff --git a/cmd/vendor/golang.org/x/crypto/pkcs12/crypto_test.go b/cmd/vendor/golang.org/x/crypto/pkcs12/crypto_test.go new file mode 100644 index 00000000000..eb4dae8fceb --- /dev/null +++ b/cmd/vendor/golang.org/x/crypto/pkcs12/crypto_test.go @@ -0,0 +1,125 @@ +// 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 pkcs12 + +import ( + "bytes" + "crypto/x509/pkix" + "encoding/asn1" + "testing" +) + +var sha1WithTripleDES = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 1, 3}) + +func TestPbDecrypterFor(t *testing.T) { + params, _ := asn1.Marshal(pbeParams{ + Salt: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + Iterations: 2048, + }) + alg := pkix.AlgorithmIdentifier{ + Algorithm: asn1.ObjectIdentifier([]int{1, 2, 3}), + Parameters: asn1.RawValue{ + FullBytes: params, + }, + } + + pass, _ := bmpString("Sesame open") + + _, _, err := pbDecrypterFor(alg, pass) + if _, ok := err.(NotImplementedError); !ok { + t.Errorf("expected not implemented error, got: %T %s", err, err) + } + + alg.Algorithm = sha1WithTripleDES + cbc, blockSize, err := pbDecrypterFor(alg, pass) + if err != nil { + t.Errorf("unexpected error from pbDecrypterFor %v", err) + } + if blockSize != 8 { + t.Errorf("unexpected block size %d, wanted 8", blockSize) + } + + plaintext := []byte{1, 2, 3, 4, 5, 6, 7, 8} + expectedCiphertext := []byte{185, 73, 135, 249, 137, 1, 122, 247} + ciphertext := make([]byte, len(plaintext)) + cbc.CryptBlocks(ciphertext, plaintext) + + if bytes.Compare(ciphertext, expectedCiphertext) != 0 { + t.Errorf("bad ciphertext, got %x but wanted %x", ciphertext, expectedCiphertext) + } +} + +var pbDecryptTests = []struct { + in []byte + expected []byte + expectedError error +}{ + { + []byte("\x33\x73\xf3\x9f\xda\x49\xae\xfc\xa0\x9a\xdf\x5a\x58\xa0\xea\x46"), // 7 padding bytes + []byte("A secret!"), + nil, + }, + { + []byte("\x33\x73\xf3\x9f\xda\x49\xae\xfc\x96\x24\x2f\x71\x7e\x32\x3f\xe7"), // 8 padding bytes + []byte("A secret"), + nil, + }, + { + []byte("\x35\x0c\xc0\x8d\xab\xa9\x5d\x30\x7f\x9a\xec\x6a\xd8\x9b\x9c\xd9"), // 9 padding bytes, incorrect + nil, + ErrDecryption, + }, + { + []byte("\xb2\xf9\x6e\x06\x60\xae\x20\xcf\x08\xa0\x7b\xd9\x6b\x20\xef\x41"), // incorrect padding bytes: [ ... 0x04 0x02 ] + nil, + ErrDecryption, + }, +} + +func TestPbDecrypt(t *testing.T) { + for i, test := range pbDecryptTests { + decryptable := testDecryptable{ + data: test.in, + algorithm: pkix.AlgorithmIdentifier{ + Algorithm: sha1WithTripleDES, + Parameters: pbeParams{ + Salt: []byte("\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8"), + Iterations: 4096, + }.RawASN1(), + }, + } + password, _ := bmpString("sesame") + + plaintext, err := pbDecrypt(decryptable, password) + if err != test.expectedError { + t.Errorf("#%d: got error %q, but wanted %q", i, err, test.expectedError) + continue + } + + if !bytes.Equal(plaintext, test.expected) { + t.Errorf("#%d: got %x, but wanted %x", i, plaintext, test.expected) + } + } +} + +type testDecryptable struct { + data []byte + algorithm pkix.AlgorithmIdentifier +} + +func (d testDecryptable) Algorithm() pkix.AlgorithmIdentifier { return d.algorithm } +func (d testDecryptable) Data() []byte { return d.data } + +func (params pbeParams) RawASN1() (raw asn1.RawValue) { + asn1Bytes, err := asn1.Marshal(params) + if err != nil { + panic(err) + } + _, err = asn1.Unmarshal(asn1Bytes, &raw) + if err != nil { + panic(err) + } + return +} diff --git a/cmd/vendor/golang.org/x/crypto/pkcs12/errors.go b/cmd/vendor/golang.org/x/crypto/pkcs12/errors.go new file mode 100644 index 00000000000..7377ce6fb2b --- /dev/null +++ b/cmd/vendor/golang.org/x/crypto/pkcs12/errors.go @@ -0,0 +1,23 @@ +// 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 pkcs12 + +import "errors" + +var ( + // ErrDecryption represents a failure to decrypt the input. + ErrDecryption = errors.New("pkcs12: decryption error, incorrect padding") + + // ErrIncorrectPassword is returned when an incorrect password is detected. + // Usually, P12/PFX data is signed to be able to verify the password. + ErrIncorrectPassword = errors.New("pkcs12: decryption password incorrect") +) + +// NotImplementedError indicates that the input is not currently supported. +type NotImplementedError string + +func (e NotImplementedError) Error() string { + return "pkcs12: " + string(e) +} diff --git a/cmd/vendor/golang.org/x/crypto/pkcs12/internal/rc2/bench_test.go b/cmd/vendor/golang.org/x/crypto/pkcs12/internal/rc2/bench_test.go new file mode 100644 index 00000000000..3347f338c18 --- /dev/null +++ b/cmd/vendor/golang.org/x/crypto/pkcs12/internal/rc2/bench_test.go @@ -0,0 +1,27 @@ +// 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 rc2 + +import ( + "testing" +) + +func BenchmarkEncrypt(b *testing.B) { + r, _ := New([]byte{0, 0, 0, 0, 0, 0, 0, 0}, 64) + b.ResetTimer() + var src [8]byte + for i := 0; i < b.N; i++ { + r.Encrypt(src[:], src[:]) + } +} + +func BenchmarkDecrypt(b *testing.B) { + r, _ := New([]byte{0, 0, 0, 0, 0, 0, 0, 0}, 64) + b.ResetTimer() + var src [8]byte + for i := 0; i < b.N; i++ { + r.Decrypt(src[:], src[:]) + } +} diff --git a/cmd/vendor/golang.org/x/crypto/pkcs12/internal/rc2/rc2.go b/cmd/vendor/golang.org/x/crypto/pkcs12/internal/rc2/rc2.go new file mode 100644 index 00000000000..8c7090258c5 --- /dev/null +++ b/cmd/vendor/golang.org/x/crypto/pkcs12/internal/rc2/rc2.go @@ -0,0 +1,274 @@ +// 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 rc2 implements the RC2 cipher +/* +https://www.ietf.org/rfc/rfc2268.txt +http://people.csail.mit.edu/rivest/pubs/KRRR98.pdf + +This code is licensed under the MIT license. +*/ +package rc2 + +import ( + "crypto/cipher" + "encoding/binary" +) + +// The rc2 block size in bytes +const BlockSize = 8 + +type rc2Cipher struct { + k [64]uint16 +} + +// New returns a new rc2 cipher with the given key and effective key length t1 +func New(key []byte, t1 int) (cipher.Block, error) { + // TODO(dgryski): error checking for key length + return &rc2Cipher{ + k: expandKey(key, t1), + }, nil +} + +func (*rc2Cipher) BlockSize() int { return BlockSize } + +var piTable = [256]byte{ + 0xd9, 0x78, 0xf9, 0xc4, 0x19, 0xdd, 0xb5, 0xed, 0x28, 0xe9, 0xfd, 0x79, 0x4a, 0xa0, 0xd8, 0x9d, + 0xc6, 0x7e, 0x37, 0x83, 0x2b, 0x76, 0x53, 0x8e, 0x62, 0x4c, 0x64, 0x88, 0x44, 0x8b, 0xfb, 0xa2, + 0x17, 0x9a, 0x59, 0xf5, 0x87, 0xb3, 0x4f, 0x13, 0x61, 0x45, 0x6d, 0x8d, 0x09, 0x81, 0x7d, 0x32, + 0xbd, 0x8f, 0x40, 0xeb, 0x86, 0xb7, 0x7b, 0x0b, 0xf0, 0x95, 0x21, 0x22, 0x5c, 0x6b, 0x4e, 0x82, + 0x54, 0xd6, 0x65, 0x93, 0xce, 0x60, 0xb2, 0x1c, 0x73, 0x56, 0xc0, 0x14, 0xa7, 0x8c, 0xf1, 0xdc, + 0x12, 0x75, 0xca, 0x1f, 0x3b, 0xbe, 0xe4, 0xd1, 0x42, 0x3d, 0xd4, 0x30, 0xa3, 0x3c, 0xb6, 0x26, + 0x6f, 0xbf, 0x0e, 0xda, 0x46, 0x69, 0x07, 0x57, 0x27, 0xf2, 0x1d, 0x9b, 0xbc, 0x94, 0x43, 0x03, + 0xf8, 0x11, 0xc7, 0xf6, 0x90, 0xef, 0x3e, 0xe7, 0x06, 0xc3, 0xd5, 0x2f, 0xc8, 0x66, 0x1e, 0xd7, + 0x08, 0xe8, 0xea, 0xde, 0x80, 0x52, 0xee, 0xf7, 0x84, 0xaa, 0x72, 0xac, 0x35, 0x4d, 0x6a, 0x2a, + 0x96, 0x1a, 0xd2, 0x71, 0x5a, 0x15, 0x49, 0x74, 0x4b, 0x9f, 0xd0, 0x5e, 0x04, 0x18, 0xa4, 0xec, + 0xc2, 0xe0, 0x41, 0x6e, 0x0f, 0x51, 0xcb, 0xcc, 0x24, 0x91, 0xaf, 0x50, 0xa1, 0xf4, 0x70, 0x39, + 0x99, 0x7c, 0x3a, 0x85, 0x23, 0xb8, 0xb4, 0x7a, 0xfc, 0x02, 0x36, 0x5b, 0x25, 0x55, 0x97, 0x31, + 0x2d, 0x5d, 0xfa, 0x98, 0xe3, 0x8a, 0x92, 0xae, 0x05, 0xdf, 0x29, 0x10, 0x67, 0x6c, 0xba, 0xc9, + 0xd3, 0x00, 0xe6, 0xcf, 0xe1, 0x9e, 0xa8, 0x2c, 0x63, 0x16, 0x01, 0x3f, 0x58, 0xe2, 0x89, 0xa9, + 0x0d, 0x38, 0x34, 0x1b, 0xab, 0x33, 0xff, 0xb0, 0xbb, 0x48, 0x0c, 0x5f, 0xb9, 0xb1, 0xcd, 0x2e, + 0xc5, 0xf3, 0xdb, 0x47, 0xe5, 0xa5, 0x9c, 0x77, 0x0a, 0xa6, 0x20, 0x68, 0xfe, 0x7f, 0xc1, 0xad, +} + +func expandKey(key []byte, t1 int) [64]uint16 { + + l := make([]byte, 128) + copy(l, key) + + var t = len(key) + var t8 = (t1 + 7) / 8 + var tm = byte(255 % uint(1<<(8+uint(t1)-8*uint(t8)))) + + for i := len(key); i < 128; i++ { + l[i] = piTable[l[i-1]+l[uint8(i-t)]] + } + + l[128-t8] = piTable[l[128-t8]&tm] + + for i := 127 - t8; i >= 0; i-- { + l[i] = piTable[l[i+1]^l[i+t8]] + } + + var k [64]uint16 + + for i := range k { + k[i] = uint16(l[2*i]) + uint16(l[2*i+1])*256 + } + + return k +} + +func rotl16(x uint16, b uint) uint16 { + return (x >> (16 - b)) | (x << b) +} + +func (c *rc2Cipher) Encrypt(dst, src []byte) { + + r0 := binary.LittleEndian.Uint16(src[0:]) + r1 := binary.LittleEndian.Uint16(src[2:]) + r2 := binary.LittleEndian.Uint16(src[4:]) + r3 := binary.LittleEndian.Uint16(src[6:]) + + var j int + + for j <= 16 { + // mix r0 + r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) + r0 = rotl16(r0, 1) + j++ + + // mix r1 + r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2) + r1 = rotl16(r1, 2) + j++ + + // mix r2 + r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3) + r2 = rotl16(r2, 3) + j++ + + // mix r3 + r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0) + r3 = rotl16(r3, 5) + j++ + + } + + r0 = r0 + c.k[r3&63] + r1 = r1 + c.k[r0&63] + r2 = r2 + c.k[r1&63] + r3 = r3 + c.k[r2&63] + + for j <= 40 { + + // mix r0 + r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) + r0 = rotl16(r0, 1) + j++ + + // mix r1 + r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2) + r1 = rotl16(r1, 2) + j++ + + // mix r2 + r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3) + r2 = rotl16(r2, 3) + j++ + + // mix r3 + r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0) + r3 = rotl16(r3, 5) + j++ + + } + + r0 = r0 + c.k[r3&63] + r1 = r1 + c.k[r0&63] + r2 = r2 + c.k[r1&63] + r3 = r3 + c.k[r2&63] + + for j <= 60 { + + // mix r0 + r0 = r0 + c.k[j] + (r3 & r2) + ((^r3) & r1) + r0 = rotl16(r0, 1) + j++ + + // mix r1 + r1 = r1 + c.k[j] + (r0 & r3) + ((^r0) & r2) + r1 = rotl16(r1, 2) + j++ + + // mix r2 + r2 = r2 + c.k[j] + (r1 & r0) + ((^r1) & r3) + r2 = rotl16(r2, 3) + j++ + + // mix r3 + r3 = r3 + c.k[j] + (r2 & r1) + ((^r2) & r0) + r3 = rotl16(r3, 5) + j++ + } + + binary.LittleEndian.PutUint16(dst[0:], r0) + binary.LittleEndian.PutUint16(dst[2:], r1) + binary.LittleEndian.PutUint16(dst[4:], r2) + binary.LittleEndian.PutUint16(dst[6:], r3) +} + +func (c *rc2Cipher) Decrypt(dst, src []byte) { + + r0 := binary.LittleEndian.Uint16(src[0:]) + r1 := binary.LittleEndian.Uint16(src[2:]) + r2 := binary.LittleEndian.Uint16(src[4:]) + r3 := binary.LittleEndian.Uint16(src[6:]) + + j := 63 + + for j >= 44 { + // unmix r3 + r3 = rotl16(r3, 16-5) + r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0) + j-- + + // unmix r2 + r2 = rotl16(r2, 16-3) + r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3) + j-- + + // unmix r1 + r1 = rotl16(r1, 16-2) + r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2) + j-- + + // unmix r0 + r0 = rotl16(r0, 16-1) + r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1) + j-- + } + + r3 = r3 - c.k[r2&63] + r2 = r2 - c.k[r1&63] + r1 = r1 - c.k[r0&63] + r0 = r0 - c.k[r3&63] + + for j >= 20 { + // unmix r3 + r3 = rotl16(r3, 16-5) + r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0) + j-- + + // unmix r2 + r2 = rotl16(r2, 16-3) + r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3) + j-- + + // unmix r1 + r1 = rotl16(r1, 16-2) + r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2) + j-- + + // unmix r0 + r0 = rotl16(r0, 16-1) + r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1) + j-- + + } + + r3 = r3 - c.k[r2&63] + r2 = r2 - c.k[r1&63] + r1 = r1 - c.k[r0&63] + r0 = r0 - c.k[r3&63] + + for j >= 0 { + + // unmix r3 + r3 = rotl16(r3, 16-5) + r3 = r3 - c.k[j] - (r2 & r1) - ((^r2) & r0) + j-- + + // unmix r2 + r2 = rotl16(r2, 16-3) + r2 = r2 - c.k[j] - (r1 & r0) - ((^r1) & r3) + j-- + + // unmix r1 + r1 = rotl16(r1, 16-2) + r1 = r1 - c.k[j] - (r0 & r3) - ((^r0) & r2) + j-- + + // unmix r0 + r0 = rotl16(r0, 16-1) + r0 = r0 - c.k[j] - (r3 & r2) - ((^r3) & r1) + j-- + + } + + binary.LittleEndian.PutUint16(dst[0:], r0) + binary.LittleEndian.PutUint16(dst[2:], r1) + binary.LittleEndian.PutUint16(dst[4:], r2) + binary.LittleEndian.PutUint16(dst[6:], r3) +} diff --git a/cmd/vendor/golang.org/x/crypto/pkcs12/internal/rc2/rc2_test.go b/cmd/vendor/golang.org/x/crypto/pkcs12/internal/rc2/rc2_test.go new file mode 100644 index 00000000000..8a49dfaf3c6 --- /dev/null +++ b/cmd/vendor/golang.org/x/crypto/pkcs12/internal/rc2/rc2_test.go @@ -0,0 +1,93 @@ +// 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 rc2 + +import ( + "bytes" + "encoding/hex" + "testing" +) + +func TestEncryptDecrypt(t *testing.T) { + + // TODO(dgryski): add the rest of the test vectors from the RFC + var tests = []struct { + key string + plain string + cipher string + t1 int + }{ + { + "0000000000000000", + "0000000000000000", + "ebb773f993278eff", + 63, + }, + { + "ffffffffffffffff", + "ffffffffffffffff", + "278b27e42e2f0d49", + 64, + }, + { + "3000000000000000", + "1000000000000001", + "30649edf9be7d2c2", + 64, + }, + { + "88", + "0000000000000000", + "61a8a244adacccf0", + 64, + }, + { + "88bca90e90875a", + "0000000000000000", + "6ccf4308974c267f", + 64, + }, + { + "88bca90e90875a7f0f79c384627bafb2", + "0000000000000000", + "1a807d272bbe5db1", + 64, + }, + { + "88bca90e90875a7f0f79c384627bafb2", + "0000000000000000", + "2269552ab0f85ca6", + 128, + }, + { + "88bca90e90875a7f0f79c384627bafb216f80a6f85920584c42fceb0be255daf1e", + "0000000000000000", + "5b78d3a43dfff1f1", + 129, + }, + } + + for _, tt := range tests { + k, _ := hex.DecodeString(tt.key) + p, _ := hex.DecodeString(tt.plain) + c, _ := hex.DecodeString(tt.cipher) + + b, _ := New(k, tt.t1) + + var dst [8]byte + + b.Encrypt(dst[:], p) + + if !bytes.Equal(dst[:], c) { + t.Errorf("encrypt failed: got % 2x wanted % 2x\n", dst, c) + } + + b.Decrypt(dst[:], c) + + if !bytes.Equal(dst[:], p) { + t.Errorf("decrypt failed: got % 2x wanted % 2x\n", dst, p) + } + } +} diff --git a/cmd/vendor/golang.org/x/crypto/pkcs12/mac.go b/cmd/vendor/golang.org/x/crypto/pkcs12/mac.go new file mode 100644 index 00000000000..5f38aa7de83 --- /dev/null +++ b/cmd/vendor/golang.org/x/crypto/pkcs12/mac.go @@ -0,0 +1,45 @@ +// 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 pkcs12 + +import ( + "crypto/hmac" + "crypto/sha1" + "crypto/x509/pkix" + "encoding/asn1" +) + +type macData struct { + Mac digestInfo + MacSalt []byte + Iterations int `asn1:"optional,default:1"` +} + +// from PKCS#7: +type digestInfo struct { + Algorithm pkix.AlgorithmIdentifier + Digest []byte +} + +var ( + oidSHA1 = asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}) +) + +func verifyMac(macData *macData, message, password []byte) error { + if !macData.Mac.Algorithm.Algorithm.Equal(oidSHA1) { + return NotImplementedError("unknown digest algorithm: " + macData.Mac.Algorithm.Algorithm.String()) + } + + key := pbkdf(sha1Sum, 20, 64, macData.MacSalt, password, macData.Iterations, 3, 20) + + mac := hmac.New(sha1.New, key) + mac.Write(message) + expectedMAC := mac.Sum(nil) + + if !hmac.Equal(macData.Mac.Digest, expectedMAC) { + return ErrIncorrectPassword + } + return nil +} diff --git a/cmd/vendor/golang.org/x/crypto/pkcs12/mac_test.go b/cmd/vendor/golang.org/x/crypto/pkcs12/mac_test.go new file mode 100644 index 00000000000..1ed4ff21e14 --- /dev/null +++ b/cmd/vendor/golang.org/x/crypto/pkcs12/mac_test.go @@ -0,0 +1,42 @@ +// 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 pkcs12 + +import ( + "encoding/asn1" + "testing" +) + +func TestVerifyMac(t *testing.T) { + td := macData{ + Mac: digestInfo{ + Digest: []byte{0x18, 0x20, 0x3d, 0xff, 0x1e, 0x16, 0xf4, 0x92, 0xf2, 0xaf, 0xc8, 0x91, 0xa9, 0xba, 0xd6, 0xca, 0x9d, 0xee, 0x51, 0x93}, + }, + MacSalt: []byte{1, 2, 3, 4, 5, 6, 7, 8}, + Iterations: 2048, + } + + message := []byte{11, 12, 13, 14, 15} + password, _ := bmpString("") + + td.Mac.Algorithm.Algorithm = asn1.ObjectIdentifier([]int{1, 2, 3}) + err := verifyMac(&td, message, password) + if _, ok := err.(NotImplementedError); !ok { + t.Errorf("err: %v", err) + } + + td.Mac.Algorithm.Algorithm = asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}) + err = verifyMac(&td, message, password) + if err != ErrIncorrectPassword { + t.Errorf("Expected incorrect password, got err: %v", err) + } + + password, _ = bmpString("Sesame open") + err = verifyMac(&td, message, password) + if err != nil { + t.Errorf("err: %v", err) + } + +} diff --git a/cmd/vendor/golang.org/x/crypto/pkcs12/pbkdf.go b/cmd/vendor/golang.org/x/crypto/pkcs12/pbkdf.go new file mode 100644 index 00000000000..5c419d41e32 --- /dev/null +++ b/cmd/vendor/golang.org/x/crypto/pkcs12/pbkdf.go @@ -0,0 +1,170 @@ +// 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 pkcs12 + +import ( + "bytes" + "crypto/sha1" + "math/big" +) + +var ( + one = big.NewInt(1) +) + +// sha1Sum returns the SHA-1 hash of in. +func sha1Sum(in []byte) []byte { + sum := sha1.Sum(in) + return sum[:] +} + +// fillWithRepeats returns v*ceiling(len(pattern) / v) bytes consisting of +// repeats of pattern. +func fillWithRepeats(pattern []byte, v int) []byte { + if len(pattern) == 0 { + return nil + } + outputLen := v * ((len(pattern) + v - 1) / v) + return bytes.Repeat(pattern, (outputLen+len(pattern)-1)/len(pattern))[:outputLen] +} + +func pbkdf(hash func([]byte) []byte, u, v int, salt, password []byte, r int, ID byte, size int) (key []byte) { + // implementation of https://tools.ietf.org/html/rfc7292#appendix-B.2 , RFC text verbatim in comments + + // Let H be a hash function built around a compression function f: + + // Z_2^u x Z_2^v -> Z_2^u + + // (that is, H has a chaining variable and output of length u bits, and + // the message input to the compression function of H is v bits). The + // values for u and v are as follows: + + // HASH FUNCTION VALUE u VALUE v + // MD2, MD5 128 512 + // SHA-1 160 512 + // SHA-224 224 512 + // SHA-256 256 512 + // SHA-384 384 1024 + // SHA-512 512 1024 + // SHA-512/224 224 1024 + // SHA-512/256 256 1024 + + // Furthermore, let r be the iteration count. + + // We assume here that u and v are both multiples of 8, as are the + // lengths of the password and salt strings (which we denote by p and s, + // respectively) and the number n of pseudorandom bits required. In + // addition, u and v are of course non-zero. + + // For information on security considerations for MD5 [19], see [25] and + // [1], and on those for MD2, see [18]. + + // The following procedure can be used to produce pseudorandom bits for + // a particular "purpose" that is identified by a byte called "ID". + // This standard specifies 3 different values for the ID byte: + + // 1. If ID=1, then the pseudorandom bits being produced are to be used + // as key material for performing encryption or decryption. + + // 2. If ID=2, then the pseudorandom bits being produced are to be used + // as an IV (Initial Value) for encryption or decryption. + + // 3. If ID=3, then the pseudorandom bits being produced are to be used + // as an integrity key for MACing. + + // 1. Construct a string, D (the "diversifier"), by concatenating v/8 + // copies of ID. + var D []byte + for i := 0; i < v; i++ { + D = append(D, ID) + } + + // 2. Concatenate copies of the salt together to create a string S of + // length v(ceiling(s/v)) bits (the final copy of the salt may be + // truncated to create S). Note that if the salt is the empty + // string, then so is S. + + S := fillWithRepeats(salt, v) + + // 3. Concatenate copies of the password together to create a string P + // of length v(ceiling(p/v)) bits (the final copy of the password + // may be truncated to create P). Note that if the password is the + // empty string, then so is P. + + P := fillWithRepeats(password, v) + + // 4. Set I=S||P to be the concatenation of S and P. + I := append(S, P...) + + // 5. Set c=ceiling(n/u). + c := (size + u - 1) / u + + // 6. For i=1, 2, ..., c, do the following: + A := make([]byte, c*20) + var IjBuf []byte + for i := 0; i < c; i++ { + // A. Set A2=H^r(D||I). (i.e., the r-th hash of D||1, + // H(H(H(... H(D||I)))) + Ai := hash(append(D, I...)) + for j := 1; j < r; j++ { + Ai = hash(Ai) + } + copy(A[i*20:], Ai[:]) + + if i < c-1 { // skip on last iteration + // B. Concatenate copies of Ai to create a string B of length v + // bits (the final copy of Ai may be truncated to create B). + var B []byte + for len(B) < v { + B = append(B, Ai[:]...) + } + B = B[:v] + + // C. Treating I as a concatenation I_0, I_1, ..., I_(k-1) of v-bit + // blocks, where k=ceiling(s/v)+ceiling(p/v), modify I by + // setting I_j=(I_j+B+1) mod 2^v for each j. + { + Bbi := new(big.Int).SetBytes(B) + Ij := new(big.Int) + + for j := 0; j < len(I)/v; j++ { + Ij.SetBytes(I[j*v : (j+1)*v]) + Ij.Add(Ij, Bbi) + Ij.Add(Ij, one) + Ijb := Ij.Bytes() + // We expect Ijb to be exactly v bytes, + // if it is longer or shorter we must + // adjust it accordingly. + if len(Ijb) > v { + Ijb = Ijb[len(Ijb)-v:] + } + if len(Ijb) < v { + if IjBuf == nil { + IjBuf = make([]byte, v) + } + bytesShort := v - len(Ijb) + for i := 0; i < bytesShort; i++ { + IjBuf[i] = 0 + } + copy(IjBuf[bytesShort:], Ijb) + Ijb = IjBuf + } + copy(I[j*v:(j+1)*v], Ijb) + } + } + } + } + // 7. Concatenate A_1, A_2, ..., A_c together to form a pseudorandom + // bit string, A. + + // 8. Use the first n bits of A as the output of this entire process. + return A[:size] + + // If the above process is being used to generate a DES key, the process + // should be used to create 64 random bits, and the key's parity bits + // should be set after the 64 bits have been produced. Similar concerns + // hold for 2-key and 3-key triple-DES keys, for CDMF keys, and for any + // similar keys with parity bits "built into them". +} diff --git a/cmd/vendor/golang.org/x/crypto/pkcs12/pbkdf_test.go b/cmd/vendor/golang.org/x/crypto/pkcs12/pbkdf_test.go new file mode 100644 index 00000000000..262037d7eba --- /dev/null +++ b/cmd/vendor/golang.org/x/crypto/pkcs12/pbkdf_test.go @@ -0,0 +1,34 @@ +// 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 pkcs12 + +import ( + "bytes" + "testing" +) + +func TestThatPBKDFWorksCorrectlyForLongKeys(t *testing.T) { + cipherInfo := shaWithTripleDESCBC{} + + salt := []byte("\xff\xff\xff\xff\xff\xff\xff\xff") + password, _ := bmpString("sesame") + key := cipherInfo.deriveKey(salt, password, 2048) + + if expected := []byte("\x7c\xd9\xfd\x3e\x2b\x3b\xe7\x69\x1a\x44\xe3\xbe\xf0\xf9\xea\x0f\xb9\xb8\x97\xd4\xe3\x25\xd9\xd1"); bytes.Compare(key, expected) != 0 { + t.Fatalf("expected key '%x', but found '%x'", expected, key) + } +} + +func TestThatPBKDFHandlesLeadingZeros(t *testing.T) { + // This test triggers a case where I_j (in step 6C) ends up with leading zero + // byte, meaning that len(Ijb) < v (leading zeros get stripped by big.Int). + // This was previously causing bug whereby certain inputs would break the + // derivation and produce the wrong output. + key := pbkdf(sha1Sum, 20, 64, []byte("\xf3\x7e\x05\xb5\x18\x32\x4b\x4b"), []byte("\x00\x00"), 2048, 1, 24) + expected := []byte("\x00\xf7\x59\xff\x47\xd1\x4d\xd0\x36\x65\xd5\x94\x3c\xb3\xc4\xa3\x9a\x25\x55\xc0\x2a\xed\x66\xe1") + if bytes.Compare(key, expected) != 0 { + t.Fatalf("expected key '%x', but found '%x'", expected, key) + } +} diff --git a/cmd/vendor/golang.org/x/crypto/pkcs12/pkcs12.go b/cmd/vendor/golang.org/x/crypto/pkcs12/pkcs12.go new file mode 100644 index 00000000000..ad6341e60fa --- /dev/null +++ b/cmd/vendor/golang.org/x/crypto/pkcs12/pkcs12.go @@ -0,0 +1,342 @@ +// 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 pkcs12 implements some of PKCS#12. +// +// This implementation is distilled from https://tools.ietf.org/html/rfc7292 +// and referenced documents. It is intended for decoding P12/PFX-stored +// certificates and keys for use with the crypto/tls package. +package pkcs12 + +import ( + "crypto/ecdsa" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "encoding/hex" + "encoding/pem" + "errors" +) + +var ( + oidDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 1}) + oidEncryptedDataContentType = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 7, 6}) + + oidFriendlyName = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 20}) + oidLocalKeyID = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 21}) + oidMicrosoftCSPName = asn1.ObjectIdentifier([]int{1, 3, 6, 1, 4, 1, 311, 17, 1}) +) + +type pfxPdu struct { + Version int + AuthSafe contentInfo + MacData macData `asn1:"optional"` +} + +type contentInfo struct { + ContentType asn1.ObjectIdentifier + Content asn1.RawValue `asn1:"tag:0,explicit,optional"` +} + +type encryptedData struct { + Version int + EncryptedContentInfo encryptedContentInfo +} + +type encryptedContentInfo struct { + ContentType asn1.ObjectIdentifier + ContentEncryptionAlgorithm pkix.AlgorithmIdentifier + EncryptedContent []byte `asn1:"tag:0,optional"` +} + +func (i encryptedContentInfo) Algorithm() pkix.AlgorithmIdentifier { + return i.ContentEncryptionAlgorithm +} + +func (i encryptedContentInfo) Data() []byte { return i.EncryptedContent } + +type safeBag struct { + Id asn1.ObjectIdentifier + Value asn1.RawValue `asn1:"tag:0,explicit"` + Attributes []pkcs12Attribute `asn1:"set,optional"` +} + +type pkcs12Attribute struct { + Id asn1.ObjectIdentifier + Value asn1.RawValue `asn1:"set"` +} + +type encryptedPrivateKeyInfo struct { + AlgorithmIdentifier pkix.AlgorithmIdentifier + EncryptedData []byte +} + +func (i encryptedPrivateKeyInfo) Algorithm() pkix.AlgorithmIdentifier { + return i.AlgorithmIdentifier +} + +func (i encryptedPrivateKeyInfo) Data() []byte { + return i.EncryptedData +} + +// PEM block types +const ( + certificateType = "CERTIFICATE" + privateKeyType = "PRIVATE KEY" +) + +// unmarshal calls asn1.Unmarshal, but also returns an error if there is any +// trailing data after unmarshaling. +func unmarshal(in []byte, out interface{}) error { + trailing, err := asn1.Unmarshal(in, out) + if err != nil { + return err + } + if len(trailing) != 0 { + return errors.New("pkcs12: trailing data found") + } + return nil +} + +// ConvertToPEM converts all "safe bags" contained in pfxData to PEM blocks. +func ToPEM(pfxData []byte, password string) ([]*pem.Block, error) { + encodedPassword, err := bmpString(password) + if err != nil { + return nil, ErrIncorrectPassword + } + + bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword) + + blocks := make([]*pem.Block, 0, len(bags)) + for _, bag := range bags { + block, err := convertBag(&bag, encodedPassword) + if err != nil { + return nil, err + } + blocks = append(blocks, block) + } + + return blocks, nil +} + +func convertBag(bag *safeBag, password []byte) (*pem.Block, error) { + block := &pem.Block{ + Headers: make(map[string]string), + } + + for _, attribute := range bag.Attributes { + k, v, err := convertAttribute(&attribute) + if err != nil { + return nil, err + } + block.Headers[k] = v + } + + switch { + case bag.Id.Equal(oidCertBag): + block.Type = certificateType + certsData, err := decodeCertBag(bag.Value.Bytes) + if err != nil { + return nil, err + } + block.Bytes = certsData + case bag.Id.Equal(oidPKCS8ShroundedKeyBag): + block.Type = privateKeyType + + key, err := decodePkcs8ShroudedKeyBag(bag.Value.Bytes, password) + if err != nil { + return nil, err + } + + switch key := key.(type) { + case *rsa.PrivateKey: + block.Bytes = x509.MarshalPKCS1PrivateKey(key) + case *ecdsa.PrivateKey: + block.Bytes, err = x509.MarshalECPrivateKey(key) + if err != nil { + return nil, err + } + default: + return nil, errors.New("found unknown private key type in PKCS#8 wrapping") + } + default: + return nil, errors.New("don't know how to convert a safe bag of type " + bag.Id.String()) + } + return block, nil +} + +func convertAttribute(attribute *pkcs12Attribute) (key, value string, err error) { + isString := false + + switch { + case attribute.Id.Equal(oidFriendlyName): + key = "friendlyName" + isString = true + case attribute.Id.Equal(oidLocalKeyID): + key = "localKeyId" + case attribute.Id.Equal(oidMicrosoftCSPName): + // This key is chosen to match OpenSSL. + key = "Microsoft CSP Name" + isString = true + default: + return "", "", errors.New("pkcs12: unknown attribute with OID " + attribute.Id.String()) + } + + if isString { + if err := unmarshal(attribute.Value.Bytes, &attribute.Value); err != nil { + return "", "", err + } + if value, err = decodeBMPString(attribute.Value.Bytes); err != nil { + return "", "", err + } + } else { + var id []byte + if err := unmarshal(attribute.Value.Bytes, &id); err != nil { + return "", "", err + } + value = hex.EncodeToString(id) + } + + return key, value, nil +} + +// Decode extracts a certificate and private key from pfxData. This function +// assumes that there is only one certificate and only one private key in the +// pfxData. +func Decode(pfxData []byte, password string) (privateKey interface{}, certificate *x509.Certificate, err error) { + encodedPassword, err := bmpString(password) + if err != nil { + return nil, nil, err + } + + bags, encodedPassword, err := getSafeContents(pfxData, encodedPassword) + if err != nil { + return nil, nil, err + } + + if len(bags) != 2 { + err = errors.New("pkcs12: expected exactly two safe bags in the PFX PDU") + return + } + + for _, bag := range bags { + switch { + case bag.Id.Equal(oidCertBag): + if certificate != nil { + err = errors.New("pkcs12: expected exactly one certificate bag") + } + + certsData, err := decodeCertBag(bag.Value.Bytes) + if err != nil { + return nil, nil, err + } + certs, err := x509.ParseCertificates(certsData) + if err != nil { + return nil, nil, err + } + if len(certs) != 1 { + err = errors.New("pkcs12: expected exactly one certificate in the certBag") + return nil, nil, err + } + certificate = certs[0] + + case bag.Id.Equal(oidPKCS8ShroundedKeyBag): + if privateKey != nil { + err = errors.New("pkcs12: expected exactly one key bag") + } + + if privateKey, err = decodePkcs8ShroudedKeyBag(bag.Value.Bytes, encodedPassword); err != nil { + return nil, nil, err + } + } + } + + if certificate == nil { + return nil, nil, errors.New("pkcs12: certificate missing") + } + if privateKey == nil { + return nil, nil, errors.New("pkcs12: private key missing") + } + + return +} + +func getSafeContents(p12Data, password []byte) (bags []safeBag, updatedPassword []byte, err error) { + pfx := new(pfxPdu) + if err := unmarshal(p12Data, pfx); err != nil { + return nil, nil, errors.New("pkcs12: error reading P12 data: " + err.Error()) + } + + if pfx.Version != 3 { + return nil, nil, NotImplementedError("can only decode v3 PFX PDU's") + } + + if !pfx.AuthSafe.ContentType.Equal(oidDataContentType) { + return nil, nil, NotImplementedError("only password-protected PFX is implemented") + } + + // unmarshal the explicit bytes in the content for type 'data' + if err := unmarshal(pfx.AuthSafe.Content.Bytes, &pfx.AuthSafe.Content); err != nil { + return nil, nil, err + } + + if len(pfx.MacData.Mac.Algorithm.Algorithm) == 0 { + return nil, nil, errors.New("pkcs12: no MAC in data") + } + + if err := verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password); err != nil { + if err == ErrIncorrectPassword && len(password) == 2 && password[0] == 0 && password[1] == 0 { + // some implementations use an empty byte array + // for the empty string password try one more + // time with empty-empty password + password = nil + err = verifyMac(&pfx.MacData, pfx.AuthSafe.Content.Bytes, password) + } + if err != nil { + return nil, nil, err + } + } + + var authenticatedSafe []contentInfo + if err := unmarshal(pfx.AuthSafe.Content.Bytes, &authenticatedSafe); err != nil { + return nil, nil, err + } + + if len(authenticatedSafe) != 2 { + return nil, nil, NotImplementedError("expected exactly two items in the authenticated safe") + } + + for _, ci := range authenticatedSafe { + var data []byte + + switch { + case ci.ContentType.Equal(oidDataContentType): + if err := unmarshal(ci.Content.Bytes, &data); err != nil { + return nil, nil, err + } + case ci.ContentType.Equal(oidEncryptedDataContentType): + var encryptedData encryptedData + if err := unmarshal(ci.Content.Bytes, &encryptedData); err != nil { + return nil, nil, err + } + if encryptedData.Version != 0 { + return nil, nil, NotImplementedError("only version 0 of EncryptedData is supported") + } + if data, err = pbDecrypt(encryptedData.EncryptedContentInfo, password); err != nil { + return nil, nil, err + } + default: + return nil, nil, NotImplementedError("only data and encryptedData content types are supported in authenticated safe") + } + + var safeContents []safeBag + if err := unmarshal(data, &safeContents); err != nil { + return nil, nil, err + } + bags = append(bags, safeContents...) + } + + return bags, password, nil +} diff --git a/cmd/vendor/golang.org/x/crypto/pkcs12/pkcs12_test.go b/cmd/vendor/golang.org/x/crypto/pkcs12/pkcs12_test.go new file mode 100644 index 00000000000..14dd2a6c5d6 --- /dev/null +++ b/cmd/vendor/golang.org/x/crypto/pkcs12/pkcs12_test.go @@ -0,0 +1,138 @@ +// 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 pkcs12 + +import ( + "crypto/rsa" + "crypto/tls" + "encoding/base64" + "encoding/pem" + "testing" +) + +func TestPfx(t *testing.T) { + for commonName, base64P12 := range testdata { + p12, _ := base64.StdEncoding.DecodeString(base64P12) + + priv, cert, err := Decode(p12, "") + if err != nil { + t.Fatal(err) + } + + if err := priv.(*rsa.PrivateKey).Validate(); err != nil { + t.Errorf("error while validating private key: %v", err) + } + + if cert.Subject.CommonName != commonName { + t.Errorf("expected common name to be %q, but found %q", commonName, cert.Subject.CommonName) + } + } +} + +func TestPEM(t *testing.T) { + for commonName, base64P12 := range testdata { + p12, _ := base64.StdEncoding.DecodeString(base64P12) + + blocks, err := ToPEM(p12, "") + if err != nil { + t.Fatalf("error while converting to PEM: %s", err) + } + + var pemData []byte + for _, b := range blocks { + pemData = append(pemData, pem.EncodeToMemory(b)...) + } + + cert, err := tls.X509KeyPair(pemData, pemData) + if err != nil { + t.Errorf("err while converting to key pair: %v", err) + } + config := tls.Config{ + Certificates: []tls.Certificate{cert}, + } + config.BuildNameToCertificate() + + if _, exists := config.NameToCertificate[commonName]; !exists { + t.Errorf("did not find our cert in PEM?: %v", config.NameToCertificate) + } + } +} + +func ExampleToPEM() { + p12, _ := base64.StdEncoding.DecodeString(`MIIJzgIBAzCCCZQGCS ... CA+gwggPk==`) + + blocks, err := ToPEM(p12, "password") + if err != nil { + panic(err) + } + + var pemData []byte + for _, b := range blocks { + pemData = append(pemData, pem.EncodeToMemory(b)...) + } + + // then use PEM data for tls to construct tls certificate: + cert, err := tls.X509KeyPair(pemData, pemData) + if err != nil { + panic(err) + } + + config := &tls.Config{ + Certificates: []tls.Certificate{cert}, + } + + _ = config +} + +var testdata = map[string]string{ + // 'null' password test case + "Windows Azure Tools": `MIIKDAIBAzCCCcwGCSqGSIb3DQEHAaCCCb0Eggm5MIIJtTCCBe4GCSqGSIb3DQEHAaCCBd8EggXbMIIF1zCCBdMGCyqGSIb3DQEMCgECoIIE7jCCBOowHAYKKoZIhvcNAQwBAzAOBAhStUNnlTGV+gICB9AEggTIJ81JIossF6boFWpPtkiQRPtI6DW6e9QD4/WvHAVrM2bKdpMzSMsCML5NyuddANTKHBVq00Jc9keqGNAqJPKkjhSUebzQFyhe0E1oI9T4zY5UKr/I8JclOeccH4QQnsySzYUG2SnniXnQ+JrG3juetli7EKth9h6jLc6xbubPadY5HMB3wL/eG/kJymiXwU2KQ9Mgd4X6jbcV+NNCE/8jbZHvSTCPeYTJIjxfeX61Sj5kFKUCzERbsnpyevhY3X0eYtEDezZQarvGmXtMMdzf8HJHkWRdk9VLDLgjk8uiJif/+X4FohZ37ig0CpgC2+dP4DGugaZZ51hb8tN9GeCKIsrmWogMXDIVd0OACBp/EjJVmFB6y0kUCXxUE0TZt0XA1tjAGJcjDUpBvTntZjPsnH/4ZySy+s2d9OOhJ6pzRQBRm360TzkFdSwk9DLiLdGfv4pwMMu/vNGBlqjP/1sQtj+jprJiD1sDbCl4AdQZVoMBQHadF2uSD4/o17XG/Ci0r2h6Htc2yvZMAbEY4zMjjIn2a+vqIxD6onexaek1R3zbkS9j19D6EN9EWn8xgz80YRCyW65znZk8xaIhhvlU/mg7sTxeyuqroBZNcq6uDaQTehDpyH7bY2l4zWRpoj10a6JfH2q5shYz8Y6UZC/kOTfuGqbZDNZWro/9pYquvNNW0M847E5t9bsf9VkAAMHRGBbWoVoU9VpI0UnoXSfvpOo+aXa2DSq5sHHUTVY7A9eov3z5IqT+pligx11xcs+YhDWcU8di3BTJisohKvv5Y8WSkm/rloiZd4ig269k0jTRk1olP/vCksPli4wKG2wdsd5o42nX1yL7mFfXocOANZbB+5qMkiwdyoQSk+Vq+C8nAZx2bbKhUq2MbrORGMzOe0Hh0x2a0PeObycN1Bpyv7Mp3ZI9h5hBnONKCnqMhtyQHUj/nNvbJUnDVYNfoOEqDiEqqEwB7YqWzAKz8KW0OIqdlM8uiQ4JqZZlFllnWJUfaiDrdFM3lYSnFQBkzeVlts6GpDOOBjCYd7dcCNS6kq6pZC6p6HN60Twu0JnurZD6RT7rrPkIGE8vAenFt4iGe/yF52fahCSY8Ws4K0UTwN7bAS+4xRHVCWvE8sMRZsRCHizb5laYsVrPZJhE6+hux6OBb6w8kwPYXc+ud5v6UxawUWgt6uPwl8mlAtU9Z7Miw4Nn/wtBkiLL/ke1UI1gqJtcQXgHxx6mzsjh41+nAgTvdbsSEyU6vfOmxGj3Rwc1eOrIhJUqn5YjOWfzzsz/D5DzWKmwXIwdspt1p+u+kol1N3f2wT9fKPnd/RGCb4g/1hc3Aju4DQYgGY782l89CEEdalpQ/35bQczMFk6Fje12HykakWEXd/bGm9Unh82gH84USiRpeOfQvBDYoqEyrY3zkFZzBjhDqa+jEcAj41tcGx47oSfDq3iVYCdL7HSIjtnyEktVXd7mISZLoMt20JACFcMw+mrbjlug+eU7o2GR7T+LwtOp/p4LZqyLa7oQJDwde1BNZtm3TCK2P1mW94QDL0nDUps5KLtr1DaZXEkRbjSJub2ZE9WqDHyU3KA8G84Tq/rN1IoNu/if45jacyPje1Npj9IftUZSP22nV7HMwZtwQ4P4MYHRMBMGCSqGSIb3DQEJFTEGBAQBAAAAMFsGCSqGSIb3DQEJFDFOHkwAewBCADQAQQA0AEYARQBCADAALQBBADEAOABBAC0ANAA0AEIAQgAtAEIANQBGADIALQA0ADkAMQBFAEYAMQA1ADIAQgBBADEANgB9MF0GCSsGAQQBgjcRATFQHk4ATQBpAGMAcgBvAHMAbwBmAHQAIABTAG8AZgB0AHcAYQByAGUAIABLAGUAeQAgAFMAdABvAHIAYQBnAGUAIABQAHIAbwB2AGkAZABlAHIwggO/BgkqhkiG9w0BBwagggOwMIIDrAIBADCCA6UGCSqGSIb3DQEHATAcBgoqhkiG9w0BDAEGMA4ECEBk5ZAYpu0WAgIH0ICCA3hik4mQFGpw9Ha8TQPtk+j2jwWdxfF0+sTk6S8PTsEfIhB7wPltjiCK92Uv2tCBQnodBUmatIfkpnRDEySmgmdglmOCzj204lWAMRs94PoALGn3JVBXbO1vIDCbAPOZ7Z0Hd0/1t2hmk8v3//QJGUg+qr59/4y/MuVfIg4qfkPcC2QSvYWcK3oTf6SFi5rv9B1IOWFgN5D0+C+x/9Lb/myPYX+rbOHrwtJ4W1fWKoz9g7wwmGFA9IJ2DYGuH8ifVFbDFT1Vcgsvs8arSX7oBsJVW0qrP7XkuDRe3EqCmKW7rBEwYrFznhxZcRDEpMwbFoSvgSIZ4XhFY9VKYglT+JpNH5iDceYEBOQL4vBLpxNUk3l5jKaBNxVa14AIBxq18bVHJ+STInhLhad4u10v/Xbx7wIL3f9DX1yLAkPrpBYbNHS2/ew6H/ySDJnoIDxkw2zZ4qJ+qUJZ1S0lbZVG+VT0OP5uF6tyOSpbMlcGkdl3z254n6MlCrTifcwkzscysDsgKXaYQw06rzrPW6RDub+t+hXzGny799fS9jhQMLDmOggaQ7+LA4oEZsfT89HLMWxJYDqjo3gIfjciV2mV54R684qLDS+AO09U49e6yEbwGlq8lpmO/pbXCbpGbB1b3EomcQbxdWxW2WEkkEd/VBn81K4M3obmywwXJkw+tPXDXfBmzzaqqCR+onMQ5ME1nMkY8ybnfoCc1bDIupjVWsEL2Wvq752RgI6KqzVNr1ew1IdqV5AWN2fOfek+0vi3Jd9FHF3hx8JMwjJL9dZsETV5kHtYJtE7wJ23J68BnCt2eI0GEuwXcCf5EdSKN/xXCTlIokc4Qk/gzRdIZsvcEJ6B1lGovKG54X4IohikqTjiepjbsMWj38yxDmK3mtENZ9ci8FPfbbvIEcOCZIinuY3qFUlRSbx7VUerEoV1IP3clUwexVQo4lHFee2jd7ocWsdSqSapW7OWUupBtDzRkqVhE7tGria+i1W2d6YLlJ21QTjyapWJehAMO637OdbJCCzDs1cXbodRRE7bsP492ocJy8OX66rKdhYbg8srSFNKdb3pF3UDNbN9jhI/t8iagRhNBhlQtTr1me2E/c86Q18qcRXl4bcXTt6acgCeffK6Y26LcVlrgjlD33AEYRRUeyC+rpxbT0aMjdFderlndKRIyG23mSp0HaUwNzAfMAcGBSsOAwIaBBRlviCbIyRrhIysg2dc/KbLFTc2vQQUg4rfwHMM4IKYRD/fsd1x6dda+wQ=`, + // empty string password test case + "testing@example.com": `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 +HQ8CAggA`, +} diff --git a/cmd/vendor/golang.org/x/crypto/pkcs12/safebags.go b/cmd/vendor/golang.org/x/crypto/pkcs12/safebags.go new file mode 100644 index 00000000000..def1f7b98d7 --- /dev/null +++ b/cmd/vendor/golang.org/x/crypto/pkcs12/safebags.go @@ -0,0 +1,57 @@ +// 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 pkcs12 + +import ( + "crypto/x509" + "encoding/asn1" + "errors" +) + +var ( + // see https://tools.ietf.org/html/rfc7292#appendix-D + oidCertTypeX509Certificate = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 9, 22, 1}) + oidPKCS8ShroundedKeyBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 2}) + oidCertBag = asn1.ObjectIdentifier([]int{1, 2, 840, 113549, 1, 12, 10, 1, 3}) +) + +type certBag struct { + Id asn1.ObjectIdentifier + Data []byte `asn1:"tag:0,explicit"` +} + +func decodePkcs8ShroudedKeyBag(asn1Data, password []byte) (privateKey interface{}, err error) { + pkinfo := new(encryptedPrivateKeyInfo) + if err = unmarshal(asn1Data, pkinfo); err != nil { + return nil, errors.New("pkcs12: error decoding PKCS#8 shrouded key bag: " + err.Error()) + } + + pkData, err := pbDecrypt(pkinfo, password) + if err != nil { + return nil, errors.New("pkcs12: error decrypting PKCS#8 shrouded key bag: " + err.Error()) + } + + ret := new(asn1.RawValue) + if err = unmarshal(pkData, ret); err != nil { + return nil, errors.New("pkcs12: error unmarshaling decrypted private key: " + err.Error()) + } + + if privateKey, err = x509.ParsePKCS8PrivateKey(pkData); err != nil { + return nil, errors.New("pkcs12: error parsing PKCS#8 private key: " + err.Error()) + } + + return privateKey, nil +} + +func decodeCertBag(asn1Data []byte) (x509Certificates []byte, err error) { + bag := new(certBag) + if err := unmarshal(asn1Data, bag); err != nil { + return nil, errors.New("pkcs12: error decoding cert bag: " + err.Error()) + } + if !bag.Id.Equal(oidCertTypeX509Certificate) { + return nil, NotImplementedError("only X509 certificates are supported") + } + return bag.Data, nil +} From 461cece5ad64033f68b3a022c29cfb9efa7bc0f2 Mon Sep 17 00:00:00 2001 From: kayrus Date: Sun, 17 Jul 2016 18:18:05 +0200 Subject: [PATCH 02/12] security: Implemented certificate revocation list --- embed/config.go | 2 ++ embed/etcd.go | 35 +++++++++++++++++++++++++++++++++-- etcdmain/config.go | 2 ++ etcdmain/help.go | 4 ++++ pkg/tlsutil/tlsutil.go | 17 +++++++++++++++++ pkg/transport/listener.go | 3 ++- 6 files changed, 60 insertions(+), 3 deletions(-) diff --git a/embed/config.go b/embed/config.go index 8d4ce54a6f5..47c94971eac 100644 --- a/embed/config.go +++ b/embed/config.go @@ -123,6 +123,7 @@ type securityConfig struct { KeyFile string `json:"key-file"` CertAuth bool `json:"client-cert-auth"` TrustedCAFile string `json:"trusted-ca-file"` + CRLFile string `json:"crl-file"` AutoTLS bool `json:"auto-tls"` } @@ -214,6 +215,7 @@ func (cfg *configYAML) configFromFile(path string) error { tls.KeyFile = ysc.KeyFile tls.ClientCertAuth = ysc.CertAuth tls.TrustedCAFile = ysc.TrustedCAFile + tls.CRLFile = ysc.CRLFile } copySecurityDetails(&cfg.ClientTLSInfo, &cfg.ClientSecurityJSON) copySecurityDetails(&cfg.PeerTLSInfo, &cfg.PeerSecurityJSON) diff --git a/embed/etcd.go b/embed/etcd.go index b6f3cc19e56..c4b3eb6f891 100644 --- a/embed/etcd.go +++ b/embed/etcd.go @@ -16,15 +16,18 @@ package embed import ( "crypto/tls" + "errors" "fmt" "net" "net/http" "path" + "github.com/cloudflare/cfssl/revoke" "github.com/coreos/etcd/etcdserver" "github.com/coreos/etcd/etcdserver/api/v2http" "github.com/coreos/etcd/pkg/cors" runtimeutil "github.com/coreos/etcd/pkg/runtime" + "github.com/coreos/etcd/pkg/tlsutil" "github.com/coreos/etcd/pkg/transport" "github.com/coreos/etcd/pkg/types" "github.com/coreos/etcd/rafthttp" @@ -280,6 +283,27 @@ func startClientListeners(cfg *Config) (sctxs map[string]*serveCtx, err error) { return sctxs, nil } +func revokeCheckHandler(req *http.Request, CRLpath string) error { + if req.TLS == nil { + return nil + } + for _, cert := range req.TLS.PeerCertificates { + var revoked, ok bool + if CRLpath != "" { + revoked, ok = revoke.VerifyCertificateByCRLPath(cert, CRLpath) + } else { + revoked, ok = revoke.VerifyCertificate(cert) + } + if !ok { + return errors.New("Cert check failed") + } + if revoked { + return errors.New("Cert if revoked") + } + } + return nil +} + func (e *Etcd) serve() (err error) { var ctlscfg *tls.Config if !e.cfg.ClientTLSInfo.Empty() { @@ -294,16 +318,23 @@ func (e *Etcd) serve() (err error) { } // Start the peer server in a goroutine - ph := v2http.NewPeerHandler(e.Server) + ph := tlsutil.RevocationCheck( + v2http.NewPeerHandler(e.Server), + revokeCheckHandler, + e.cfg.PeerTLSInfo.CRLFile) for _, l := range e.Peers { go func(l net.Listener) { e.errc <- servePeerHTTP(l, ph) }(l) } + clientHandler := tlsutil.RevocationCheck( + v2http.NewClientHandler(e.Server, e.Server.Cfg.ReqTimeout()), + revokeCheckHandler, + e.cfg.ClientTLSInfo.CRLFile) // Start a client server goroutine for each listen address ch := http.Handler(&cors.CORSHandler{ - Handler: v2http.NewClientHandler(e.Server, e.Server.Cfg.ReqTimeout()), + Handler: clientHandler, Info: e.cfg.CorsInfo, }) for _, sctx := range e.sctxs { diff --git a/etcdmain/config.go b/etcdmain/config.go index 5fb4dea4934..40fc472b53d 100644 --- a/etcdmain/config.go +++ b/etcdmain/config.go @@ -173,12 +173,14 @@ func newConfig() *config { fs.StringVar(&cfg.ClientTLSInfo.KeyFile, "key-file", "", "Path to the client server TLS key file.") fs.BoolVar(&cfg.ClientTLSInfo.ClientCertAuth, "client-cert-auth", false, "Enable client cert authentication.") fs.StringVar(&cfg.ClientTLSInfo.TrustedCAFile, "trusted-ca-file", "", "Path to the client server TLS trusted CA key file.") + fs.StringVar(&cfg.ClientTLSInfo.CRLFile, "crl-file", "", "Path to the client server certificate revocation list file.") fs.BoolVar(&cfg.ClientAutoTLS, "auto-tls", false, "Client TLS using generated certificates") fs.StringVar(&cfg.PeerTLSInfo.CAFile, "peer-ca-file", "", "DEPRECATED: Path to the peer server TLS CA file.") fs.StringVar(&cfg.PeerTLSInfo.CertFile, "peer-cert-file", "", "Path to the peer server TLS cert file.") fs.StringVar(&cfg.PeerTLSInfo.KeyFile, "peer-key-file", "", "Path to the peer server TLS key file.") fs.BoolVar(&cfg.PeerTLSInfo.ClientCertAuth, "peer-client-cert-auth", false, "Enable peer client cert authentication.") fs.StringVar(&cfg.PeerTLSInfo.TrustedCAFile, "peer-trusted-ca-file", "", "Path to the peer server TLS trusted CA file.") + fs.StringVar(&cfg.PeerTLSInfo.CRLFile, "peer-crl-file", "", "Path to the peer server certificate revocation list file.") fs.BoolVar(&cfg.PeerAutoTLS, "peer-auto-tls", false, "Peer TLS using generated certificates") // logging diff --git a/etcdmain/help.go b/etcdmain/help.go index e425a90cd36..b3fd8ef8ee1 100644 --- a/etcdmain/help.go +++ b/etcdmain/help.go @@ -118,6 +118,8 @@ security flags: enable client cert authentication. --trusted-ca-file '' path to the client server TLS trusted CA key file. + --crl-file '' + path to the client server certificate revocation list file. --auto-tls 'false' client TLS using generated certificates. --peer-ca-file '' [DEPRECATED] @@ -130,6 +132,8 @@ security flags: enable peer client cert authentication. --peer-trusted-ca-file '' path to the peer server TLS trusted CA file. + --peer-crl-file '' + path to the peer server certificate revocation list file. --peer-auto-tls 'false' peer TLS using self-generated certificates if --peer-key-file and --peer-cert-file are not provided. diff --git a/pkg/tlsutil/tlsutil.go b/pkg/tlsutil/tlsutil.go index 79b1f632ed5..1cca0ae0c22 100644 --- a/pkg/tlsutil/tlsutil.go +++ b/pkg/tlsutil/tlsutil.go @@ -18,7 +18,11 @@ import ( "crypto/tls" "crypto/x509" "encoding/pem" + "fmt" "io/ioutil" + "net/http" + + etcdErr "github.com/coreos/etcd/error" ) // NewCertPool creates x509 certPool with provided CA files. @@ -70,3 +74,16 @@ func NewCert(certfile, keyfile string, parseFunc func([]byte, []byte) (tls.Certi } return &tlsCert, nil } + +func RevocationCheck(handler http.Handler, checker func(*http.Request, string) error, CRLpath string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + err := checker(req, CRLpath) + if err == nil { + handler.ServeHTTP(w, req) + return + } + w.WriteHeader(http.StatusForbidden) + e := etcdErr.NewError(etcdErr.EcodeUnauthorized, fmt.Sprint(err), 0) + e.WriteTo(w) + }) +} diff --git a/pkg/transport/listener.go b/pkg/transport/listener.go index 78407ad8b85..153640fecd8 100644 --- a/pkg/transport/listener.go +++ b/pkg/transport/listener.go @@ -65,6 +65,7 @@ type TLSInfo struct { KeyFile string CAFile string TrustedCAFile string + CRLFile string ClientCertAuth bool selfCert bool @@ -75,7 +76,7 @@ type TLSInfo struct { } func (info TLSInfo) String() string { - return fmt.Sprintf("cert = %s, key = %s, ca = %s, trusted-ca = %s, client-cert-auth = %v", info.CertFile, info.KeyFile, info.CAFile, info.TrustedCAFile, info.ClientCertAuth) + return fmt.Sprintf("cert = %s, key = %s, ca = %s, trusted-ca = %s, cert-auth = %v, crl-file = %s", info.CertFile, info.KeyFile, info.CAFile, info.TrustedCAFile, info.ClientCertAuth, info.CRLFile) } func (info TLSInfo) Empty() bool { From f08dfd87ddb9c6735e71a33dcb2d7598efa923ff Mon Sep 17 00:00:00 2001 From: kayrus Date: Tue, 19 Jul 2016 14:08:02 +0200 Subject: [PATCH 03/12] CRL: Update cfssl dependency and fixed remarks --- .../cloudflare/cfssl/revoke/revoke.go | 179 +++++++++++------- embed/etcd.go | 29 +-- pkg/tlsutil/tlsutil.go | 28 ++- 3 files changed, 136 insertions(+), 100 deletions(-) diff --git a/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go b/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go index a9e4267ba8f..1ed5f3907c4 100644 --- a/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go +++ b/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go @@ -11,6 +11,7 @@ import ( "encoding/base64" "encoding/pem" "errors" + "fmt" "io/ioutil" "net/http" neturl "net/url" @@ -23,14 +24,21 @@ import ( "github.com/cloudflare/cfssl/log" ) -// HardFail determines whether the failure to check the revocation -// status of a certificate (i.e. due to network failure) causes -// verification to fail (a hard failure). -var HardFail = false +// Revoke type contains configuration for each new revoke instance +type Revoke struct { + // HardFail determines whether the failure to check the revocation + // status of a certificate (i.e. due to network failure) causes + // verification to fail (a hard failure). + HardFail bool + // CRLSet associates a PKIX certificate list with the URL the CRL is + // fetched from. + CRLSet map[string]*pkix.CertificateList +} -// CRLSet associates a PKIX certificate list with the URL the CRL is -// fetched from. -var CRLSet = map[string]*pkix.CertificateList{} +// NewRevokeChecker creates Revoke config structure +func NewRevokeChecker() *Revoke { + return &Revoke{false, map[string]*pkix.CertificateList{}} +} // We can't handle LDAP certificates, so this checks to see if the // URL string points to an LDAP resource so that we can ignore it. @@ -61,16 +69,29 @@ func ldapURL(url string) bool { // // true, false: failure to check revocation status causes // verification to fail -func revCheck(cert *x509.Certificate) (revoked, ok bool) { +func (r *Revoke) revCheck(cert *x509.Certificate, localCRLPath string) (revoked, ok bool) { + if localCRLPath != "" { + if revoked, ok := r.certIsRevokedCRL(cert, localCRLPath, true); !ok { + log.Warning("error checking revocation via CRL") + if r.HardFail { + return true, false + } + return false, false + } else if revoked { + log.Info("certificate is revoked via CRL") + return true, true + } + } + for _, url := range cert.CRLDistributionPoints { if ldapURL(url) { log.Infof("skipping LDAP CRL: %s", url) continue } - if revoked, ok := certIsRevokedCRL(cert, url); !ok { + if revoked, ok := r.certIsRevokedCRL(cert, url, false); !ok { log.Warning("error checking revocation via CRL") - if HardFail { + if r.HardFail { return true, false } return false, false @@ -79,9 +100,9 @@ func revCheck(cert *x509.Certificate) (revoked, ok bool) { return true, true } - if revoked, ok := certIsRevokedOCSP(cert, HardFail); !ok { + if revoked, ok := certIsRevokedOCSP(cert, r.HardFail); !ok { log.Warning("error checking revocation via OCSP") - if HardFail { + if r.HardFail { return true, false } return false, false @@ -112,21 +133,6 @@ func fetchCRL(url string) (*pkix.CertificateList, error) { return x509.ParseCRL(body) } -func revLocalCheck(cert *x509.Certificate, path string) (revoked, ok bool) { - if revoked, ok := certIsRevokedCRL(cert, path); !ok { - log.Warning("error checking revocation via CRL") - if HardFail { - return true, false - } - return false, false - } else if revoked { - log.Info("certificate is revoked via CRL") - return true, true - } - - return false, true -} - func getIssuer(cert *x509.Certificate) *x509.Certificate { var issuer *x509.Certificate var err error @@ -139,73 +145,99 @@ func getIssuer(cert *x509.Certificate) *x509.Certificate { } return issuer - } -// check a cert against a specific CRL. Returns the same bool pair -// as revCheck. -func certIsRevokedCRL(cert *x509.Certificate, url string) (revoked, ok bool) { - crl, ok := CRLSet[url] +// checks whether CRL in memory is valid +func (r *Revoke) isInMemoryCRLValid(key string) bool { + crl, ok := r.CRLSet[key] if ok && crl == nil { ok = false - delete(CRLSet, url) + delete(r.CRLSet, key) } - var shouldFetchCRL = true if ok { if !crl.HasExpired(time.Now()) { - shouldFetchCRL = false + return true } } - u, err := neturl.Parse(url) + return false +} + +// FetchLocalCRL reads CRL from the local filesystem +// force flag allows you to update the CRL +func (r *Revoke) FetchLocalCRL(path string, force bool) error { + shouldFetchCRL := !r.isInMemoryCRLValid(path) + + u, err := neturl.Parse(path) if err != nil { log.Warningf("failed to parse CRL url: %v", err) - return false, false + return err } - if u.Scheme == "" && shouldFetchCRL { - if _, err := os.Stat(url); err == nil { - tmp, err := ioutil.ReadFile(url) + + if u.Scheme == "" && (shouldFetchCRL || force) { + if _, err := os.Stat(path); err == nil { + tmp, err := ioutil.ReadFile(path) if err != nil { - log.Warningf("failed to read local CRL path: %v", err) - return false, false + return fmt.Errorf("failed to read local CRL path: %v", err) } - crl, err = x509.ParseCRL(tmp) + crl, err := x509.ParseCRL(tmp) if err != nil { - log.Warningf("failed to parse local CRL file: %v", err) - return false, false + return fmt.Errorf("failed to parse local CRL file: %v", err) } - CRLSet[url] = crl - shouldFetchCRL = false + r.CRLSet[path] = crl } else { - log.Warningf("failed to read local CRL path: %v", err) - return false, false + return fmt.Errorf("failed to read local CRL path: %v", err) } } + return nil +} + +// FetchRemoteCRL fetches remote CRL into internal map, +// force overwrites previously read CRL +func (r *Revoke) FetchRemoteCRL(url string, cert *x509.Certificate, force bool) error { + shouldFetchCRL := !r.isInMemoryCRLValid(url) + issuer := getIssuer(cert) - if shouldFetchCRL { + if force || shouldFetchCRL { var err error - crl, err = fetchCRL(url) + crl, err := fetchCRL(url) if err != nil { - log.Warningf("failed to fetch CRL: %v", err) - return false, false + return fmt.Errorf("failed to fetch CRL: %v", err) } // check CRL signature if issuer != nil { err = issuer.CheckCRLSignature(crl) if err != nil { - log.Warningf("failed to verify CRL: %v", err) - return false, false + return fmt.Errorf("failed to verify CRL: %v", err) } } - CRLSet[url] = crl + r.CRLSet[url] = crl + } + + return nil +} + +// check a cert against a specific CRL. Returns the same bool pair +// as revCheck. +func (r *Revoke) certIsRevokedCRL(cert *x509.Certificate, url string, fetchLocal bool) (revoked, ok bool) { + var err error + if fetchLocal { + err = r.FetchLocalCRL(url, false) + } else { + err = r.FetchRemoteCRL(url, cert, false) + } + + if err != nil { + log.Warningf("%v", err) + return false, false } - for _, revoked := range crl.TBSCertList.RevokedCertificates { + for _, revoked := range r.CRLSet[url].TBSCertList.RevokedCertificates { if cert.SerialNumber.Cmp(revoked.SerialNumber) == 0 { log.Info("Serial number match: intermediate is revoked.") return true, true @@ -215,32 +247,37 @@ func certIsRevokedCRL(cert *x509.Certificate, url string) (revoked, ok bool) { return false, true } -// VerifyCertificate ensures that the certificate passed in hasn't -// expired and checks the CRL for the server. -func VerifyCertificate(cert *x509.Certificate) (revoked, ok bool) { +// verifyCertTime verifies whether certificate time frames are valid +func verifyCertTime(cert *x509.Certificate) bool { if !time.Now().Before(cert.NotAfter) { - log.Infof("Certificate expired %s\n", cert.NotAfter) - return true, true + log.Infof("Certificate expired %s", cert.NotAfter) + return false } else if !time.Now().After(cert.NotBefore) { - log.Infof("Certificate isn't valid until %s\n", cert.NotBefore) + log.Infof("Certificate isn't valid until %s", cert.NotBefore) + return false + } + + return true +} + +// VerifyCertificate ensures that the certificate passed in hasn't +// expired and checks the CRL for the server. +func (r *Revoke) VerifyCertificate(cert *x509.Certificate) (revoked, ok bool) { + if !verifyCertTime(cert) { return true, true } - return revCheck(cert) + return r.revCheck(cert, "") } // VerifyCertificateByCRLPath ensures that the certificate passed in hasn't // expired and checks the local CRL path. -func VerifyCertificateByCRLPath(cert *x509.Certificate, crlPath string) (revoked, ok bool) { - if !time.Now().Before(cert.NotAfter) { - log.Infof("Certificate expired %s\n", cert.NotAfter) - return true, true - } else if !time.Now().After(cert.NotBefore) { - log.Infof("Certificate isn't valid until %s\n", cert.NotBefore) +func (r *Revoke) VerifyCertificateByCRLPath(cert *x509.Certificate, crlPath string) (revoked, ok bool) { + if !verifyCertTime(cert) { return true, true } - return revLocalCheck(cert, crlPath) + return r.revCheck(cert, crlPath) } func fetchRemote(url string) (*x509.Certificate, error) { diff --git a/embed/etcd.go b/embed/etcd.go index c4b3eb6f891..dd741a7ab8e 100644 --- a/embed/etcd.go +++ b/embed/etcd.go @@ -16,13 +16,11 @@ package embed import ( "crypto/tls" - "errors" "fmt" "net" "net/http" "path" - "github.com/cloudflare/cfssl/revoke" "github.com/coreos/etcd/etcdserver" "github.com/coreos/etcd/etcdserver/api/v2http" "github.com/coreos/etcd/pkg/cors" @@ -283,27 +281,6 @@ func startClientListeners(cfg *Config) (sctxs map[string]*serveCtx, err error) { return sctxs, nil } -func revokeCheckHandler(req *http.Request, CRLpath string) error { - if req.TLS == nil { - return nil - } - for _, cert := range req.TLS.PeerCertificates { - var revoked, ok bool - if CRLpath != "" { - revoked, ok = revoke.VerifyCertificateByCRLPath(cert, CRLpath) - } else { - revoked, ok = revoke.VerifyCertificate(cert) - } - if !ok { - return errors.New("Cert check failed") - } - if revoked { - return errors.New("Cert if revoked") - } - } - return nil -} - func (e *Etcd) serve() (err error) { var ctlscfg *tls.Config if !e.cfg.ClientTLSInfo.Empty() { @@ -318,9 +295,8 @@ func (e *Etcd) serve() (err error) { } // Start the peer server in a goroutine - ph := tlsutil.RevocationCheck( + ph := tlsutil.NewRevokeHandler( v2http.NewPeerHandler(e.Server), - revokeCheckHandler, e.cfg.PeerTLSInfo.CRLFile) for _, l := range e.Peers { go func(l net.Listener) { @@ -328,9 +304,8 @@ func (e *Etcd) serve() (err error) { }(l) } - clientHandler := tlsutil.RevocationCheck( + clientHandler := tlsutil.NewRevokeHandler( v2http.NewClientHandler(e.Server, e.Server.Cfg.ReqTimeout()), - revokeCheckHandler, e.cfg.ClientTLSInfo.CRLFile) // Start a client server goroutine for each listen address ch := http.Handler(&cors.CORSHandler{ diff --git a/pkg/tlsutil/tlsutil.go b/pkg/tlsutil/tlsutil.go index 1cca0ae0c22..39c75437051 100644 --- a/pkg/tlsutil/tlsutil.go +++ b/pkg/tlsutil/tlsutil.go @@ -22,6 +22,8 @@ import ( "io/ioutil" "net/http" + "github.com/cloudflare/cfssl/revoke" + etcdErr "github.com/coreos/etcd/error" ) @@ -75,9 +77,31 @@ func NewCert(certfile, keyfile string, parseFunc func([]byte, []byte) (tls.Certi return &tlsCert, nil } -func RevocationCheck(handler http.Handler, checker func(*http.Request, string) error, CRLpath string) http.Handler { +func revokeCheckHandler(req *http.Request, CRLpath string, revokeChecker *revoke.Revoke) error { + if req.TLS == nil { + return nil + } + for _, cert := range req.TLS.PeerCertificates { + var revoked, ok bool + if CRLpath != "" { + revoked, ok = revokeChecker.VerifyCertificateByCRLPath(cert, CRLpath) + } else { + revoked, ok = revokeChecker.VerifyCertificate(cert) + } + if !ok { + return fmt.Errorf("cert check failed (CN=%s, Serial: %s)", cert.Subject.CommonName, cert.SerialNumber.String()) + } + if revoked { + return fmt.Errorf("Cert is revoked (CN=%s, Serial: %s)", cert.Subject.CommonName, cert.SerialNumber.String()) + } + } + return nil +} + +func NewRevokeHandler(handler http.Handler, CRLpath string) http.Handler { + revokeChecker := revoke.NewRevokeChecker() return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - err := checker(req, CRLpath) + err := revokeCheckHandler(req, CRLpath, revokeChecker) if err == nil { handler.ServeHTTP(w, req) return From 2b066b8877782ad7b8d19f97243c099db64e3eb5 Mon Sep 17 00:00:00 2001 From: kayrus Date: Wed, 20 Jul 2016 02:01:32 +0200 Subject: [PATCH 04/12] Updated deps --- .../cloudflare/cfssl/revoke/revoke.go | 25 ++++++++++++++----- pkg/tlsutil/tlsutil.go | 2 +- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go b/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go index 1ed5f3907c4..021c9c3e53f 100644 --- a/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go +++ b/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go @@ -16,6 +16,7 @@ import ( "net/http" neturl "net/url" "os" + "sync" "time" "golang.org/x/crypto/ocsp" @@ -33,11 +34,16 @@ type Revoke struct { // CRLSet associates a PKIX certificate list with the URL the CRL is // fetched from. CRLSet map[string]*pkix.CertificateList + // CRLSetLck is a Mutex lokcer for the CRLSet + CRLSetLck sync.Mutex } -// NewRevokeChecker creates Revoke config structure -func NewRevokeChecker() *Revoke { - return &Revoke{false, map[string]*pkix.CertificateList{}} +// New creates Revoke config structure +func New() *Revoke { + return &Revoke{ + HardFail: false, + CRLSet: map[string]*pkix.CertificateList{}, + } } // We can't handle LDAP certificates, so this checks to see if the @@ -152,7 +158,9 @@ func (r *Revoke) isInMemoryCRLValid(key string) bool { crl, ok := r.CRLSet[key] if ok && crl == nil { ok = false + r.CRLSetLck.Lock() delete(r.CRLSet, key) + r.CRLSetLck.Unlock() } if ok { @@ -176,7 +184,9 @@ func (r *Revoke) FetchLocalCRL(path string, force bool) error { } if u.Scheme == "" && (shouldFetchCRL || force) { - if _, err := os.Stat(path); err == nil { + if _, err := os.Stat(path); err != nil { + return fmt.Errorf("failed to read local CRL path: %v", err) + } else { tmp, err := ioutil.ReadFile(path) if err != nil { return fmt.Errorf("failed to read local CRL path: %v", err) @@ -185,9 +195,10 @@ func (r *Revoke) FetchLocalCRL(path string, force bool) error { if err != nil { return fmt.Errorf("failed to parse local CRL file: %v", err) } + + r.CRLSetLck.Lock() r.CRLSet[path] = crl - } else { - return fmt.Errorf("failed to read local CRL path: %v", err) + r.CRLSetLck.Unlock() } } @@ -216,7 +227,9 @@ func (r *Revoke) FetchRemoteCRL(url string, cert *x509.Certificate, force bool) } } + r.CRLSetLck.Lock() r.CRLSet[url] = crl + r.CRLSetLck.Unlock() } return nil diff --git a/pkg/tlsutil/tlsutil.go b/pkg/tlsutil/tlsutil.go index 39c75437051..8d350d4810e 100644 --- a/pkg/tlsutil/tlsutil.go +++ b/pkg/tlsutil/tlsutil.go @@ -99,7 +99,7 @@ func revokeCheckHandler(req *http.Request, CRLpath string, revokeChecker *revoke } func NewRevokeHandler(handler http.Handler, CRLpath string) http.Handler { - revokeChecker := revoke.NewRevokeChecker() + revokeChecker := revoke.New() return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { err := revokeCheckHandler(req, CRLpath, revokeChecker) if err == nil { From ecb04ea0eea8bdbd3258418260d84b9a1d3879c9 Mon Sep 17 00:00:00 2001 From: kayrus Date: Wed, 20 Jul 2016 10:18:09 +0200 Subject: [PATCH 05/12] Added --crl-check flag for the certs with built-in CRL info --- embed/config.go | 4 +++- embed/etcd.go | 24 ++++++++++++++++++------ etcdmain/config.go | 14 ++++++++++++-- etcdmain/help.go | 8 ++++++-- pkg/transport/listener.go | 5 +++-- 5 files changed, 42 insertions(+), 13 deletions(-) diff --git a/embed/config.go b/embed/config.go index 47c94971eac..c87011dce32 100644 --- a/embed/config.go +++ b/embed/config.go @@ -123,6 +123,7 @@ type securityConfig struct { KeyFile string `json:"key-file"` CertAuth bool `json:"client-cert-auth"` TrustedCAFile string `json:"trusted-ca-file"` + CRLCheck bool `json:"crl-check"` CRLFile string `json:"crl-file"` AutoTLS bool `json:"auto-tls"` } @@ -213,8 +214,9 @@ func (cfg *configYAML) configFromFile(path string) error { tls.CAFile = ysc.CAFile tls.CertFile = ysc.CertFile tls.KeyFile = ysc.KeyFile - tls.ClientCertAuth = ysc.CertAuth tls.TrustedCAFile = ysc.TrustedCAFile + tls.ClientCertAuth = ysc.CertAuth + tls.CRLCheck = (ysc.CRLCheck || ysc.CRLFile != "") && ysc.CertAuth tls.CRLFile = ysc.CRLFile } copySecurityDetails(&cfg.ClientTLSInfo, &cfg.ClientSecurityJSON) diff --git a/embed/etcd.go b/embed/etcd.go index dd741a7ab8e..be4f9589047 100644 --- a/embed/etcd.go +++ b/embed/etcd.go @@ -295,18 +295,30 @@ func (e *Etcd) serve() (err error) { } // Start the peer server in a goroutine - ph := tlsutil.NewRevokeHandler( - v2http.NewPeerHandler(e.Server), - e.cfg.PeerTLSInfo.CRLFile) + var ph, clientHandler http.Handler + if e.cfg.PeerTLSInfo.CRLCheck { + // Enable CRL checker handler for the peer server + ph = tlsutil.NewRevokeHandler( + v2http.NewPeerHandler(e.Server), + e.cfg.PeerTLSInfo.CRLFile) + } else { + ph = v2http.NewPeerHandler(e.Server) + } + // Start the peer server in a goroutine for _, l := range e.Peers { go func(l net.Listener) { e.errc <- servePeerHTTP(l, ph) }(l) } - clientHandler := tlsutil.NewRevokeHandler( - v2http.NewClientHandler(e.Server, e.Server.Cfg.ReqTimeout()), - e.cfg.ClientTLSInfo.CRLFile) + if e.cfg.ClientTLSInfo.CRLCheck { + // Enable CRL checker handler for the client server + clientHandler = tlsutil.NewRevokeHandler( + v2http.NewClientHandler(e.Server, e.Server.Cfg.ReqTimeout()), + e.cfg.ClientTLSInfo.CRLFile) + } else { + clientHandler = v2http.NewClientHandler(e.Server, e.Server.Cfg.ReqTimeout()) + } // Start a client server goroutine for each listen address ch := http.Handler(&cors.CORSHandler{ Handler: clientHandler, diff --git a/etcdmain/config.go b/etcdmain/config.go index 40fc472b53d..37b89be38af 100644 --- a/etcdmain/config.go +++ b/etcdmain/config.go @@ -173,14 +173,16 @@ func newConfig() *config { fs.StringVar(&cfg.ClientTLSInfo.KeyFile, "key-file", "", "Path to the client server TLS key file.") fs.BoolVar(&cfg.ClientTLSInfo.ClientCertAuth, "client-cert-auth", false, "Enable client cert authentication.") fs.StringVar(&cfg.ClientTLSInfo.TrustedCAFile, "trusted-ca-file", "", "Path to the client server TLS trusted CA key file.") - fs.StringVar(&cfg.ClientTLSInfo.CRLFile, "crl-file", "", "Path to the client server certificate revocation list file.") + fs.BoolVar(&cfg.ClientTLSInfo.CRLCheck, "crl-check", false, "Enable CRL check for the client server. Works only when --client-cert-auth flag is set.") + fs.StringVar(&cfg.ClientTLSInfo.CRLFile, "crl-file", "", "Path to the client server certificate revocation list file. If set, automatically enables --crl-check flag.") fs.BoolVar(&cfg.ClientAutoTLS, "auto-tls", false, "Client TLS using generated certificates") fs.StringVar(&cfg.PeerTLSInfo.CAFile, "peer-ca-file", "", "DEPRECATED: Path to the peer server TLS CA file.") fs.StringVar(&cfg.PeerTLSInfo.CertFile, "peer-cert-file", "", "Path to the peer server TLS cert file.") fs.StringVar(&cfg.PeerTLSInfo.KeyFile, "peer-key-file", "", "Path to the peer server TLS key file.") fs.BoolVar(&cfg.PeerTLSInfo.ClientCertAuth, "peer-client-cert-auth", false, "Enable peer client cert authentication.") fs.StringVar(&cfg.PeerTLSInfo.TrustedCAFile, "peer-trusted-ca-file", "", "Path to the peer server TLS trusted CA file.") - fs.StringVar(&cfg.PeerTLSInfo.CRLFile, "peer-crl-file", "", "Path to the peer server certificate revocation list file.") + fs.BoolVar(&cfg.PeerTLSInfo.CRLCheck, "peer-crl-check", false, "Enable CRL check for the peer server. Works only when --peer-client-cert-auth flag is set.") + fs.StringVar(&cfg.PeerTLSInfo.CRLFile, "peer-crl-file", "", "Path to the peer server certificate revocation list file. If set, automatically enables --peer-crl-check flag.") fs.BoolVar(&cfg.PeerAutoTLS, "peer-auto-tls", false, "Peer TLS using generated certificates") // logging @@ -234,6 +236,14 @@ func (cfg *config) parse(arguments []string) error { } else { err = cfg.configFromCmdLine() } + + if cfg.ClientTLSInfo.CRLFile != "" && cfg.ClientTLSInfo.ClientCertAuth { + cfg.ClientTLSInfo.CRLCheck = true + } + + if cfg.PeerTLSInfo.CRLFile != "" && cfg.PeerTLSInfo.ClientCertAuth { + cfg.PeerTLSInfo.CRLCheck = true + } return err } diff --git a/etcdmain/help.go b/etcdmain/help.go index b3fd8ef8ee1..c83b8e650d2 100644 --- a/etcdmain/help.go +++ b/etcdmain/help.go @@ -118,8 +118,10 @@ security flags: enable client cert authentication. --trusted-ca-file '' path to the client server TLS trusted CA key file. + --crl-check 'false' + enable CRL check for the client server. Works only when --client-cert-auth flag is set. --crl-file '' - path to the client server certificate revocation list file. + path to the client server certificate revocation list file. If set, automatically enables --crl-check flag. --auto-tls 'false' client TLS using generated certificates. --peer-ca-file '' [DEPRECATED] @@ -132,8 +134,10 @@ security flags: enable peer client cert authentication. --peer-trusted-ca-file '' path to the peer server TLS trusted CA file. + --peer-crl-check 'false' + enable CRL check for the peer server. Works only when --peer-client-cert-auth flag is set. --peer-crl-file '' - path to the peer server certificate revocation list file. + path to the peer server certificate revocation list file. If set, automatically enables --peer-crl-check flag. --peer-auto-tls 'false' peer TLS using self-generated certificates if --peer-key-file and --peer-cert-file are not provided. diff --git a/pkg/transport/listener.go b/pkg/transport/listener.go index 153640fecd8..efab71d261e 100644 --- a/pkg/transport/listener.go +++ b/pkg/transport/listener.go @@ -65,8 +65,9 @@ type TLSInfo struct { KeyFile string CAFile string TrustedCAFile string - CRLFile string ClientCertAuth bool + CRLCheck bool + CRLFile string selfCert bool @@ -76,7 +77,7 @@ type TLSInfo struct { } func (info TLSInfo) String() string { - return fmt.Sprintf("cert = %s, key = %s, ca = %s, trusted-ca = %s, cert-auth = %v, crl-file = %s", info.CertFile, info.KeyFile, info.CAFile, info.TrustedCAFile, info.ClientCertAuth, info.CRLFile) + return fmt.Sprintf("cert = %s, key = %s, ca = %s, trusted-ca = %s, cert-auth = %v, crl-check = %v, crl-file = %s", info.CertFile, info.KeyFile, info.CAFile, info.TrustedCAFile, info.ClientCertAuth, info.CRLCheck, info.CRLFile) } func (info TLSInfo) Empty() bool { From b7dea951d3a65a9abd2e1858b09ec6b0ce2e0c13 Mon Sep 17 00:00:00 2001 From: kayrus Date: Wed, 20 Jul 2016 10:47:10 +0200 Subject: [PATCH 06/12] Updated deps --- .../cloudflare/cfssl/revoke/revoke.go | 29 +++++++++---------- pkg/tlsutil/tlsutil.go | 4 +-- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go b/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go index 021c9c3e53f..aa90f27ed2c 100644 --- a/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go +++ b/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go @@ -84,7 +84,7 @@ func (r *Revoke) revCheck(cert *x509.Certificate, localCRLPath string) (revoked, } return false, false } else if revoked { - log.Info("certificate is revoked via CRL") + log.Infof("certificate is revoked via local CRL file (CN=%s, Serial: %s)", cert.Subject.CommonName, cert.SerialNumber) return true, true } } @@ -102,7 +102,7 @@ func (r *Revoke) revCheck(cert *x509.Certificate, localCRLPath string) (revoked, } return false, false } else if revoked { - log.Info("certificate is revoked via CRL") + log.Info("certificate is revoked via CRL (CN=%s, Serial: %s)", cert.Subject.CommonName, cert.SerialNumber) return true, true } @@ -186,20 +186,20 @@ func (r *Revoke) FetchLocalCRL(path string, force bool) error { if u.Scheme == "" && (shouldFetchCRL || force) { if _, err := os.Stat(path); err != nil { return fmt.Errorf("failed to read local CRL path: %v", err) - } else { - tmp, err := ioutil.ReadFile(path) - if err != nil { - return fmt.Errorf("failed to read local CRL path: %v", err) - } - crl, err := x509.ParseCRL(tmp) - if err != nil { - return fmt.Errorf("failed to parse local CRL file: %v", err) - } + } - r.CRLSetLck.Lock() - r.CRLSet[path] = crl - r.CRLSetLck.Unlock() + tmp, err := ioutil.ReadFile(path) + if err != nil { + return fmt.Errorf("failed to read local CRL path: %v", err) + } + crl, err := x509.ParseCRL(tmp) + if err != nil { + return fmt.Errorf("failed to parse local CRL file: %v", err) } + + r.CRLSetLck.Lock() + r.CRLSet[path] = crl + r.CRLSetLck.Unlock() } return nil @@ -252,7 +252,6 @@ func (r *Revoke) certIsRevokedCRL(cert *x509.Certificate, url string, fetchLocal for _, revoked := range r.CRLSet[url].TBSCertList.RevokedCertificates { if cert.SerialNumber.Cmp(revoked.SerialNumber) == 0 { - log.Info("Serial number match: intermediate is revoked.") return true, true } } diff --git a/pkg/tlsutil/tlsutil.go b/pkg/tlsutil/tlsutil.go index 8d350d4810e..c9bedfa5c96 100644 --- a/pkg/tlsutil/tlsutil.go +++ b/pkg/tlsutil/tlsutil.go @@ -89,10 +89,10 @@ func revokeCheckHandler(req *http.Request, CRLpath string, revokeChecker *revoke revoked, ok = revokeChecker.VerifyCertificate(cert) } if !ok { - return fmt.Errorf("cert check failed (CN=%s, Serial: %s)", cert.Subject.CommonName, cert.SerialNumber.String()) + return fmt.Errorf("cert check failed") } if revoked { - return fmt.Errorf("Cert is revoked (CN=%s, Serial: %s)", cert.Subject.CommonName, cert.SerialNumber.String()) + return fmt.Errorf("Cert is revoked") } } return nil From 829fb81b2292cb93e4efab02c7989b3b6b36a72b Mon Sep 17 00:00:00 2001 From: kayrus Date: Wed, 20 Jul 2016 11:41:05 +0200 Subject: [PATCH 07/12] Added a hardfail option --- .../cloudflare/cfssl/revoke/revoke.go | 9 ++++--- embed/config.go | 2 ++ embed/etcd.go | 6 +++-- etcdmain/config.go | 2 ++ etcdmain/help.go | 4 ++++ pkg/tlsutil/tlsutil.go | 24 +++++++------------ pkg/transport/listener.go | 3 ++- 7 files changed, 29 insertions(+), 21 deletions(-) diff --git a/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go b/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go index aa90f27ed2c..90dd5e3129d 100644 --- a/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go +++ b/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go @@ -31,6 +31,8 @@ type Revoke struct { // status of a certificate (i.e. due to network failure) causes // verification to fail (a hard failure). HardFail bool + // HardFailLck is a Mutex locker for the HardFail + HardFailLck sync.Mutex // CRLSet associates a PKIX certificate list with the URL the CRL is // fetched from. CRLSet map[string]*pkix.CertificateList @@ -38,10 +40,11 @@ type Revoke struct { CRLSetLck sync.Mutex } -// New creates Revoke config structure -func New() *Revoke { +// New creates Revoke config structure. +// Accepts hardfail bool variableas an option +func New(hardfail bool) *Revoke { return &Revoke{ - HardFail: false, + HardFail: hardfail, CRLSet: map[string]*pkix.CertificateList{}, } } diff --git a/embed/config.go b/embed/config.go index c87011dce32..599cfe4bc4b 100644 --- a/embed/config.go +++ b/embed/config.go @@ -124,6 +124,7 @@ type securityConfig struct { CertAuth bool `json:"client-cert-auth"` TrustedCAFile string `json:"trusted-ca-file"` CRLCheck bool `json:"crl-check"` + CRLHardFail bool `json:"crl-hard-fail"` CRLFile string `json:"crl-file"` AutoTLS bool `json:"auto-tls"` } @@ -217,6 +218,7 @@ func (cfg *configYAML) configFromFile(path string) error { tls.TrustedCAFile = ysc.TrustedCAFile tls.ClientCertAuth = ysc.CertAuth tls.CRLCheck = (ysc.CRLCheck || ysc.CRLFile != "") && ysc.CertAuth + tls.CRLHardFail = ysc.CRLHardFail tls.CRLFile = ysc.CRLFile } copySecurityDetails(&cfg.ClientTLSInfo, &cfg.ClientSecurityJSON) diff --git a/embed/etcd.go b/embed/etcd.go index be4f9589047..dd44e572a51 100644 --- a/embed/etcd.go +++ b/embed/etcd.go @@ -300,7 +300,8 @@ func (e *Etcd) serve() (err error) { // Enable CRL checker handler for the peer server ph = tlsutil.NewRevokeHandler( v2http.NewPeerHandler(e.Server), - e.cfg.PeerTLSInfo.CRLFile) + e.cfg.PeerTLSInfo.CRLFile, + e.cfg.PeerTLSInfo.CRLHardFail) } else { ph = v2http.NewPeerHandler(e.Server) } @@ -315,7 +316,8 @@ func (e *Etcd) serve() (err error) { // Enable CRL checker handler for the client server clientHandler = tlsutil.NewRevokeHandler( v2http.NewClientHandler(e.Server, e.Server.Cfg.ReqTimeout()), - e.cfg.ClientTLSInfo.CRLFile) + e.cfg.ClientTLSInfo.CRLFile, + e.cfg.ClientTLSInfo.CRLHardFail) } else { clientHandler = v2http.NewClientHandler(e.Server, e.Server.Cfg.ReqTimeout()) } diff --git a/etcdmain/config.go b/etcdmain/config.go index 37b89be38af..3ce90adc1ef 100644 --- a/etcdmain/config.go +++ b/etcdmain/config.go @@ -174,6 +174,7 @@ func newConfig() *config { fs.BoolVar(&cfg.ClientTLSInfo.ClientCertAuth, "client-cert-auth", false, "Enable client cert authentication.") fs.StringVar(&cfg.ClientTLSInfo.TrustedCAFile, "trusted-ca-file", "", "Path to the client server TLS trusted CA key file.") fs.BoolVar(&cfg.ClientTLSInfo.CRLCheck, "crl-check", false, "Enable CRL check for the client server. Works only when --client-cert-auth flag is set.") + fs.BoolVar(&cfg.ClientTLSInfo.CRLHardFail, "crl-hard-fail", true, "Enable hard fail revocation plan for the the client server. Fail CRL check if CRL is unavailable.") fs.StringVar(&cfg.ClientTLSInfo.CRLFile, "crl-file", "", "Path to the client server certificate revocation list file. If set, automatically enables --crl-check flag.") fs.BoolVar(&cfg.ClientAutoTLS, "auto-tls", false, "Client TLS using generated certificates") fs.StringVar(&cfg.PeerTLSInfo.CAFile, "peer-ca-file", "", "DEPRECATED: Path to the peer server TLS CA file.") @@ -182,6 +183,7 @@ func newConfig() *config { fs.BoolVar(&cfg.PeerTLSInfo.ClientCertAuth, "peer-client-cert-auth", false, "Enable peer client cert authentication.") fs.StringVar(&cfg.PeerTLSInfo.TrustedCAFile, "peer-trusted-ca-file", "", "Path to the peer server TLS trusted CA file.") fs.BoolVar(&cfg.PeerTLSInfo.CRLCheck, "peer-crl-check", false, "Enable CRL check for the peer server. Works only when --peer-client-cert-auth flag is set.") + fs.BoolVar(&cfg.PeerTLSInfo.CRLHardFail, "peer-crl-hard-fail", true, "Enable hard fail revocation plan for the the peer server. Fail CRL check if CRL is unavailable.") fs.StringVar(&cfg.PeerTLSInfo.CRLFile, "peer-crl-file", "", "Path to the peer server certificate revocation list file. If set, automatically enables --peer-crl-check flag.") fs.BoolVar(&cfg.PeerAutoTLS, "peer-auto-tls", false, "Peer TLS using generated certificates") diff --git a/etcdmain/help.go b/etcdmain/help.go index c83b8e650d2..b964ded2481 100644 --- a/etcdmain/help.go +++ b/etcdmain/help.go @@ -120,6 +120,8 @@ security flags: path to the client server TLS trusted CA key file. --crl-check 'false' enable CRL check for the client server. Works only when --client-cert-auth flag is set. + --crl-hard-fail 'true' + enable hard fail revocation plan for the the client server. Fail CRL check if CRL is unavailable. --crl-file '' path to the client server certificate revocation list file. If set, automatically enables --crl-check flag. --auto-tls 'false' @@ -136,6 +138,8 @@ security flags: path to the peer server TLS trusted CA file. --peer-crl-check 'false' enable CRL check for the peer server. Works only when --peer-client-cert-auth flag is set. + --peer-crl-hard-fail 'true' + enable hard fail revocation plan for the the peer server. Fail CRL check if CRL is unavailable. --peer-crl-file '' path to the peer server certificate revocation list file. If set, automatically enables --peer-crl-check flag. --peer-auto-tls 'false' diff --git a/pkg/tlsutil/tlsutil.go b/pkg/tlsutil/tlsutil.go index c9bedfa5c96..d8a545fa900 100644 --- a/pkg/tlsutil/tlsutil.go +++ b/pkg/tlsutil/tlsutil.go @@ -18,13 +18,10 @@ import ( "crypto/tls" "crypto/x509" "encoding/pem" - "fmt" "io/ioutil" "net/http" "github.com/cloudflare/cfssl/revoke" - - etcdErr "github.com/coreos/etcd/error" ) // NewCertPool creates x509 certPool with provided CA files. @@ -77,9 +74,9 @@ func NewCert(certfile, keyfile string, parseFunc func([]byte, []byte) (tls.Certi return &tlsCert, nil } -func revokeCheckHandler(req *http.Request, CRLpath string, revokeChecker *revoke.Revoke) error { +func isReqCertValid(req *http.Request, CRLpath string, revokeChecker *revoke.Revoke) bool { if req.TLS == nil { - return nil + return true } for _, cert := range req.TLS.PeerCertificates { var revoked, ok bool @@ -88,26 +85,23 @@ func revokeCheckHandler(req *http.Request, CRLpath string, revokeChecker *revoke } else { revoked, ok = revokeChecker.VerifyCertificate(cert) } - if !ok { - return fmt.Errorf("cert check failed") + if !ok && revokeChecker.HardFail { + return false } if revoked { - return fmt.Errorf("Cert is revoked") + return false } } - return nil + return true } -func NewRevokeHandler(handler http.Handler, CRLpath string) http.Handler { - revokeChecker := revoke.New() +func NewRevokeHandler(handler http.Handler, CRLpath string, hardfail bool) http.Handler { + revokeChecker := revoke.New(hardfail) return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - err := revokeCheckHandler(req, CRLpath, revokeChecker) - if err == nil { + if isReqCertValid(req, CRLpath, revokeChecker) { handler.ServeHTTP(w, req) return } w.WriteHeader(http.StatusForbidden) - e := etcdErr.NewError(etcdErr.EcodeUnauthorized, fmt.Sprint(err), 0) - e.WriteTo(w) }) } diff --git a/pkg/transport/listener.go b/pkg/transport/listener.go index efab71d261e..ebb29081816 100644 --- a/pkg/transport/listener.go +++ b/pkg/transport/listener.go @@ -67,6 +67,7 @@ type TLSInfo struct { TrustedCAFile string ClientCertAuth bool CRLCheck bool + CRLHardFail bool CRLFile string selfCert bool @@ -77,7 +78,7 @@ type TLSInfo struct { } func (info TLSInfo) String() string { - return fmt.Sprintf("cert = %s, key = %s, ca = %s, trusted-ca = %s, cert-auth = %v, crl-check = %v, crl-file = %s", info.CertFile, info.KeyFile, info.CAFile, info.TrustedCAFile, info.ClientCertAuth, info.CRLCheck, info.CRLFile) + return fmt.Sprintf("cert = %s, key = %s, ca = %s, trusted-ca = %s, cert-auth = %v, crl-check = %v, crl-hard-fail = %v, crl-file = %s", info.CertFile, info.KeyFile, info.CAFile, info.TrustedCAFile, info.ClientCertAuth, info.CRLCheck, info.CRLHardFail, info.CRLFile) } func (info TLSInfo) Empty() bool { From 23c6d2a7e7976bdfd0074c927260c6d4c7d6fdff Mon Sep 17 00:00:00 2001 From: kayrus Date: Wed, 20 Jul 2016 19:20:38 +0200 Subject: [PATCH 08/12] Updated cfssl/revoke --- .../cloudflare/cfssl/revoke/revoke.go | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go b/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go index 90dd5e3129d..36da6eb6c1a 100644 --- a/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go +++ b/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go @@ -31,13 +31,11 @@ type Revoke struct { // status of a certificate (i.e. due to network failure) causes // verification to fail (a hard failure). HardFail bool - // HardFailLck is a Mutex locker for the HardFail - HardFailLck sync.Mutex // CRLSet associates a PKIX certificate list with the URL the CRL is // fetched from. CRLSet map[string]*pkix.CertificateList - // CRLSetLck is a Mutex lokcer for the CRLSet - CRLSetLck sync.Mutex + // Lck is a Mutex locker for the struct + Lck sync.Mutex } // New creates Revoke config structure. @@ -161,9 +159,9 @@ func (r *Revoke) isInMemoryCRLValid(key string) bool { crl, ok := r.CRLSet[key] if ok && crl == nil { ok = false - r.CRLSetLck.Lock() + r.Lck.Lock() delete(r.CRLSet, key) - r.CRLSetLck.Unlock() + r.Lck.Unlock() } if ok { @@ -200,9 +198,9 @@ func (r *Revoke) FetchLocalCRL(path string, force bool) error { return fmt.Errorf("failed to parse local CRL file: %v", err) } - r.CRLSetLck.Lock() + r.Lck.Lock() r.CRLSet[path] = crl - r.CRLSetLck.Unlock() + r.Lck.Unlock() } return nil @@ -230,9 +228,9 @@ func (r *Revoke) FetchRemoteCRL(url string, cert *x509.Certificate, force bool) } } - r.CRLSetLck.Lock() + r.Lck.Lock() r.CRLSet[url] = crl - r.CRLSetLck.Unlock() + r.Lck.Unlock() } return nil From e016f6d30c56770a17fa1ffce01506aceeb4b761 Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 21 Jul 2016 17:30:14 +0200 Subject: [PATCH 09/12] Updated PR --- .../cloudflare/cfssl/revoke/revoke.go | 157 +++++++++++------- embed/etcd.go | 21 ++- pkg/tlsutil/tlsutil.go | 16 +- 3 files changed, 113 insertions(+), 81 deletions(-) diff --git a/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go b/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go index 36da6eb6c1a..38436cda350 100644 --- a/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go +++ b/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go @@ -10,7 +10,6 @@ import ( "crypto/x509/pkix" "encoding/base64" "encoding/pem" - "errors" "fmt" "io/ioutil" "net/http" @@ -27,26 +26,68 @@ import ( // Revoke type contains configuration for each new revoke instance type Revoke struct { + // LocalCRL contains path to the local CRL file. When set, certificate + // will be checked only using local CRL script, remote methods will be + // skipped. + localCRL string // HardFail determines whether the failure to check the revocation // status of a certificate (i.e. due to network failure) causes // verification to fail (a hard failure). - HardFail bool - // CRLSet associates a PKIX certificate list with the URL the CRL is + hardFail bool + // crlSet associates a PKIX certificate list with the URL the CRL is // fetched from. - CRLSet map[string]*pkix.CertificateList - // Lck is a Mutex locker for the struct - Lck sync.Mutex + crlSet map[string]*pkix.CertificateList + // lck is a Mutex locker for the struct + lck sync.Mutex } // New creates Revoke config structure. -// Accepts hardfail bool variableas an option +// Accepts hardfail bool variable as an option func New(hardfail bool) *Revoke { return &Revoke{ - HardFail: hardfail, - CRLSet: map[string]*pkix.CertificateList{}, + localCRL: "", + hardFail: hardfail, + crlSet: map[string]*pkix.CertificateList{}, } } +// SetLocalCRL sets localCRL path into the Revoke struct +func (r *Revoke) SetLocalCRL(localCRLpath string) error { + if localCRLpath == "" { + r.lck.Lock() + if _, ok := r.crlSet[r.localCRL]; ok { + delete(r.crlSet, r.localCRL) + } + r.localCRL = "" + r.lck.Unlock() + return nil + } + if u, err := neturl.Parse(r.localCRL); err != nil { + return err + } else if u.Scheme != "" { + return fmt.Errorf("Path is not valid: %s", localCRLpath) + } + + if err := r.fetchLocalCRL(localCRLpath, true); err != nil { + return err + } + + return nil +} + +// SetHardFail allows to dynamically set hardfail bool into the +// Revoke struct +func (r *Revoke) SetHardFail(hardfail bool) { + r.lck.Lock() + r.hardFail = hardfail + r.lck.Unlock() +} + +// HardFail returns hardfail bool from the Revoke struct +func (r *Revoke) HardFail() bool { + return r.hardFail +} + // We can't handle LDAP certificates, so this checks to see if the // URL string points to an LDAP resource so that we can ignore it. func ldapURL(url string) bool { @@ -76,11 +117,11 @@ func ldapURL(url string) bool { // // true, false: failure to check revocation status causes // verification to fail -func (r *Revoke) revCheck(cert *x509.Certificate, localCRLPath string) (revoked, ok bool) { - if localCRLPath != "" { - if revoked, ok := r.certIsRevokedCRL(cert, localCRLPath, true); !ok { - log.Warning("error checking revocation via CRL") - if r.HardFail { +func (r *Revoke) revCheck(cert *x509.Certificate) (revoked, ok bool) { + if r.localCRL != "" { + if revoked, ok := r.certIsRevokedCRL(cert, r.localCRL); !ok { + log.Warning("error checking revocation via local CRL file") + if r.hardFail { return true, false } return false, false @@ -96,9 +137,9 @@ func (r *Revoke) revCheck(cert *x509.Certificate, localCRLPath string) (revoked, continue } - if revoked, ok := r.certIsRevokedCRL(cert, url, false); !ok { + if revoked, ok := r.certIsRevokedCRL(cert, url); !ok { log.Warning("error checking revocation via CRL") - if r.HardFail { + if r.hardFail { return true, false } return false, false @@ -107,14 +148,14 @@ func (r *Revoke) revCheck(cert *x509.Certificate, localCRLPath string) (revoked, return true, true } - if revoked, ok := certIsRevokedOCSP(cert, r.HardFail); !ok { + if revoked, ok := certIsRevokedOCSP(cert, r.hardFail); !ok { log.Warning("error checking revocation via OCSP") - if r.HardFail { + if r.hardFail { return true, false } return false, false } else if revoked { - log.Info("certificate is revoked via OCSP") + log.Info("certificate is revoked via OCSP (CN=%s, Serial: %s)", cert.Subject.CommonName, cert.SerialNumber) return true, true } } @@ -128,7 +169,7 @@ func fetchCRL(url string) (*pkix.CertificateList, error) { if err != nil { return nil, err } else if resp.StatusCode >= 300 { - return nil, errors.New("failed to retrieve CRL") + return nil, fmt.Errorf("failed to retrieve CRL") } body, err := ioutil.ReadAll(resp.Body) @@ -156,12 +197,12 @@ func getIssuer(cert *x509.Certificate) *x509.Certificate { // checks whether CRL in memory is valid func (r *Revoke) isInMemoryCRLValid(key string) bool { - crl, ok := r.CRLSet[key] + crl, ok := r.crlSet[key] if ok && crl == nil { ok = false - r.Lck.Lock() - delete(r.CRLSet, key) - r.Lck.Unlock() + r.lck.Lock() + delete(r.crlSet, key) + r.lck.Unlock() } if ok { @@ -173,23 +214,15 @@ func (r *Revoke) isInMemoryCRLValid(key string) bool { return false } -// FetchLocalCRL reads CRL from the local filesystem +// fetchLocalCRL reads CRL from the local filesystem // force flag allows you to update the CRL -func (r *Revoke) FetchLocalCRL(path string, force bool) error { - shouldFetchCRL := !r.isInMemoryCRLValid(path) - - u, err := neturl.Parse(path) - if err != nil { - log.Warningf("failed to parse CRL url: %v", err) - return err - } - - if u.Scheme == "" && (shouldFetchCRL || force) { - if _, err := os.Stat(path); err != nil { +func (r *Revoke) fetchLocalCRL(newLocalCRL string, force bool) error { + if force || !r.isInMemoryCRLValid(newLocalCRL) { + if _, err := os.Stat(newLocalCRL); err != nil { return fmt.Errorf("failed to read local CRL path: %v", err) } - tmp, err := ioutil.ReadFile(path) + tmp, err := ioutil.ReadFile(newLocalCRL) if err != nil { return fmt.Errorf("failed to read local CRL path: %v", err) } @@ -198,9 +231,15 @@ func (r *Revoke) FetchLocalCRL(path string, force bool) error { return fmt.Errorf("failed to parse local CRL file: %v", err) } - r.Lck.Lock() - r.CRLSet[path] = crl - r.Lck.Unlock() + r.lck.Lock() + r.crlSet[newLocalCRL] = crl + if r.localCRL != newLocalCRL { + if _, ok := r.crlSet[r.localCRL]; ok { + delete(r.crlSet, r.localCRL) + } + r.localCRL = newLocalCRL + } + r.lck.Unlock() } return nil @@ -228,9 +267,9 @@ func (r *Revoke) FetchRemoteCRL(url string, cert *x509.Certificate, force bool) } } - r.Lck.Lock() - r.CRLSet[url] = crl - r.Lck.Unlock() + r.lck.Lock() + r.crlSet[url] = crl + r.lck.Unlock() } return nil @@ -238,10 +277,10 @@ func (r *Revoke) FetchRemoteCRL(url string, cert *x509.Certificate, force bool) // check a cert against a specific CRL. Returns the same bool pair // as revCheck. -func (r *Revoke) certIsRevokedCRL(cert *x509.Certificate, url string, fetchLocal bool) (revoked, ok bool) { +func (r *Revoke) certIsRevokedCRL(cert *x509.Certificate, url string) (revoked, ok bool) { var err error - if fetchLocal { - err = r.FetchLocalCRL(url, false) + if url == r.localCRL { + err = r.fetchLocalCRL(r.localCRL, false) } else { err = r.FetchRemoteCRL(url, cert, false) } @@ -251,7 +290,7 @@ func (r *Revoke) certIsRevokedCRL(cert *x509.Certificate, url string, fetchLocal return false, false } - for _, revoked := range r.CRLSet[url].TBSCertList.RevokedCertificates { + for _, revoked := range r.crlSet[url].TBSCertList.RevokedCertificates { if cert.SerialNumber.Cmp(revoked.SerialNumber) == 0 { return true, true } @@ -280,17 +319,7 @@ func (r *Revoke) VerifyCertificate(cert *x509.Certificate) (revoked, ok bool) { return true, true } - return r.revCheck(cert, "") -} - -// VerifyCertificateByCRLPath ensures that the certificate passed in hasn't -// expired and checks the local CRL path. -func (r *Revoke) VerifyCertificateByCRLPath(cert *x509.Certificate, crlPath string) (revoked, ok bool) { - if !verifyCertTime(cert) { - return true, true - } - - return r.revCheck(cert, crlPath) + return r.revCheck(cert) } func fetchRemote(url string) (*x509.Certificate, error) { @@ -381,7 +410,7 @@ func sendOCSPRequest(server string, req []byte, issuer *x509.Certificate) (*ocsp } if resp.StatusCode != http.StatusOK { - return nil, errors.New("failed to retrieve OSCP") + return nil, fmt.Errorf("failed to retrieve OSCP") } body, err := ioutil.ReadAll(resp.Body) @@ -392,15 +421,15 @@ func sendOCSPRequest(server string, req []byte, issuer *x509.Certificate) (*ocsp switch { case bytes.Equal(body, ocsp.UnauthorizedErrorResponse): - return nil, errors.New("OSCP unauthorized") + return nil, fmt.Errorf("OSCP unauthorized") case bytes.Equal(body, ocsp.MalformedRequestErrorResponse): - return nil, errors.New("OSCP malformed") + return nil, fmt.Errorf("OSCP malformed") case bytes.Equal(body, ocsp.InternalErrorErrorResponse): - return nil, errors.New("OSCP internal error") + return nil, fmt.Errorf("OSCP internal error") case bytes.Equal(body, ocsp.TryLaterErrorResponse): - return nil, errors.New("OSCP try later") + return nil, fmt.Errorf("OSCP try later") case bytes.Equal(body, ocsp.SigRequredErrorResponse): - return nil, errors.New("OSCP signature required") + return nil, fmt.Errorf("OSCP signature required") } return ocsp.ParseResponse(body, issuer) diff --git a/embed/etcd.go b/embed/etcd.go index dd44e572a51..ee92358fa0c 100644 --- a/embed/etcd.go +++ b/embed/etcd.go @@ -21,6 +21,7 @@ import ( "net/http" "path" + "github.com/cloudflare/cfssl/revoke" "github.com/coreos/etcd/etcdserver" "github.com/coreos/etcd/etcdserver/api/v2http" "github.com/coreos/etcd/pkg/cors" @@ -294,14 +295,17 @@ func (e *Etcd) serve() (err error) { plog.Infof("cors = %s", e.cfg.CorsInfo) } - // Start the peer server in a goroutine - var ph, clientHandler http.Handler + // Define the peer server handler + var ph http.Handler if e.cfg.PeerTLSInfo.CRLCheck { // Enable CRL checker handler for the peer server + peerRH := revoke.New(e.cfg.PeerTLSInfo.CRLHardFail) + if err = peerRH.SetLocalCRL(e.cfg.PeerTLSInfo.CRLFile); err != nil { + return err + } ph = tlsutil.NewRevokeHandler( v2http.NewPeerHandler(e.Server), - e.cfg.PeerTLSInfo.CRLFile, - e.cfg.PeerTLSInfo.CRLHardFail) + peerRH) } else { ph = v2http.NewPeerHandler(e.Server) } @@ -312,12 +316,17 @@ func (e *Etcd) serve() (err error) { }(l) } + // Define the client server handler + var clientHandler http.Handler if e.cfg.ClientTLSInfo.CRLCheck { // Enable CRL checker handler for the client server + clientRH := revoke.New(e.cfg.ClientTLSInfo.CRLHardFail) + if err = clientRH.SetLocalCRL(e.cfg.ClientTLSInfo.CRLFile); err != nil { + return err + } clientHandler = tlsutil.NewRevokeHandler( v2http.NewClientHandler(e.Server, e.Server.Cfg.ReqTimeout()), - e.cfg.ClientTLSInfo.CRLFile, - e.cfg.ClientTLSInfo.CRLHardFail) + clientRH) } else { clientHandler = v2http.NewClientHandler(e.Server, e.Server.Cfg.ReqTimeout()) } diff --git a/pkg/tlsutil/tlsutil.go b/pkg/tlsutil/tlsutil.go index d8a545fa900..375378bd35e 100644 --- a/pkg/tlsutil/tlsutil.go +++ b/pkg/tlsutil/tlsutil.go @@ -74,18 +74,13 @@ func NewCert(certfile, keyfile string, parseFunc func([]byte, []byte) (tls.Certi return &tlsCert, nil } -func isReqCertValid(req *http.Request, CRLpath string, revokeChecker *revoke.Revoke) bool { +func isReqCertValid(req *http.Request, rc *revoke.Revoke) bool { if req.TLS == nil { return true } for _, cert := range req.TLS.PeerCertificates { - var revoked, ok bool - if CRLpath != "" { - revoked, ok = revokeChecker.VerifyCertificateByCRLPath(cert, CRLpath) - } else { - revoked, ok = revokeChecker.VerifyCertificate(cert) - } - if !ok && revokeChecker.HardFail { + revoked, ok := rc.VerifyCertificate(cert) + if !ok && rc.HardFail() { return false } if revoked { @@ -95,10 +90,9 @@ func isReqCertValid(req *http.Request, CRLpath string, revokeChecker *revoke.Rev return true } -func NewRevokeHandler(handler http.Handler, CRLpath string, hardfail bool) http.Handler { - revokeChecker := revoke.New(hardfail) +func NewRevokeHandler(handler http.Handler, rc *revoke.Revoke) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - if isReqCertValid(req, CRLpath, revokeChecker) { + if isReqCertValid(req, rc) { handler.ServeHTTP(w, req) return } From c6baf706cf7a1406075ef4023bc8839245bb18fc Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 21 Jul 2016 17:45:07 +0200 Subject: [PATCH 10/12] embed: Fixed serve() err return --- embed/etcd.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embed/etcd.go b/embed/etcd.go index ee92358fa0c..8d5548415a0 100644 --- a/embed/etcd.go +++ b/embed/etcd.go @@ -129,7 +129,9 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) { e.errc = make(chan error, len(e.Peers)+len(e.Clients)+2*len(e.sctxs)) e.Server.Start() - e.serve() + if err = e.serve(); err != nil { + return + } <-e.Server.ReadyNotify() return } From 9e0dba8af99e91aea90aba7845076c8e4de62e0e Mon Sep 17 00:00:00 2001 From: kayrus Date: Thu, 21 Jul 2016 18:35:25 +0200 Subject: [PATCH 11/12] Updated cfssl/revoke --- .../cloudflare/cfssl/revoke/revoke.go | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go b/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go index 38436cda350..9456791271a 100644 --- a/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go +++ b/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go @@ -41,6 +41,10 @@ type Revoke struct { lck sync.Mutex } +// DefaultChecker is a default config for regular apps which don't need to +// use custom options. +var DefaultChecker = New(false) + // New creates Revoke config structure. // Accepts hardfail bool variable as an option func New(hardfail bool) *Revoke { @@ -126,7 +130,7 @@ func (r *Revoke) revCheck(cert *x509.Certificate) (revoked, ok bool) { } return false, false } else if revoked { - log.Infof("certificate is revoked via local CRL file (CN=%s, Serial: %s)", cert.Subject.CommonName, cert.SerialNumber) + log.Infof("certificate is revoked by '%s' CRL file (CN=%s, Serial: %s)", r.localCRL, cert.Subject.CommonName, cert.SerialNumber) return true, true } } @@ -144,7 +148,7 @@ func (r *Revoke) revCheck(cert *x509.Certificate) (revoked, ok bool) { } return false, false } else if revoked { - log.Info("certificate is revoked via CRL (CN=%s, Serial: %s)", cert.Subject.CommonName, cert.SerialNumber) + log.Info("certificate is revoked by '%s' CRL (CN=%s, Serial: %s)", url, cert.Subject.CommonName, cert.SerialNumber) return true, true } @@ -155,7 +159,7 @@ func (r *Revoke) revCheck(cert *x509.Certificate) (revoked, ok bool) { } return false, false } else if revoked { - log.Info("certificate is revoked via OCSP (CN=%s, Serial: %s)", cert.Subject.CommonName, cert.SerialNumber) + log.Info("certificate is revoked by '%s' OCSP (CN=%s, Serial: %s)", url, cert.Subject.CommonName, cert.SerialNumber) return true, true } } @@ -312,6 +316,18 @@ func verifyCertTime(cert *x509.Certificate) bool { return true } +// VerifyCertificate ensures that the certificate passed in hasn't +// expired and checks the CRL for the server. +// Comparing to the next public method, this function uses +// DefaultChecker variable. +func VerifyCertificate(cert *x509.Certificate) (revoked, ok bool) { + if !verifyCertTime(cert) { + return true, true + } + + return DefaultChecker.revCheck(cert) +} + // VerifyCertificate ensures that the certificate passed in hasn't // expired and checks the CRL for the server. func (r *Revoke) VerifyCertificate(cert *x509.Certificate) (revoked, ok bool) { From a8b7511fd1f9403a531cb0944028137190c17fbf Mon Sep 17 00:00:00 2001 From: kayrus Date: Fri, 5 Aug 2016 12:48:41 +0200 Subject: [PATCH 12/12] Updated cfssl/revoke pkg --- .../cloudflare/cfssl/revoke/revoke.go | 250 +++++++++++------- pkg/tlsutil/tlsutil.go | 2 +- 2 files changed, 159 insertions(+), 93 deletions(-) diff --git a/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go b/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go index 9456791271a..7aeaec853a0 100644 --- a/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go +++ b/cmd/vendor/github.com/cloudflare/cfssl/revoke/revoke.go @@ -24,72 +24,133 @@ import ( "github.com/cloudflare/cfssl/log" ) +type localCRL struct { + // embedded Mutex locker for the localCRL + sync.RWMutex + path string + crl *pkix.CertificateList +} + +type crlSet struct { + // embedded Mutex locker for the crlSet + sync.RWMutex + // crlSet associates a PKIX certificate list with the URL the CRL is + // fetched from. + set map[string]*pkix.CertificateList +} + +type hardFail struct { + // embedded Mutex locker for the hardFail + sync.RWMutex + // hardFail value + val bool +} + // Revoke type contains configuration for each new revoke instance type Revoke struct { // LocalCRL contains path to the local CRL file. When set, certificate // will be checked only using local CRL script, remote methods will be // skipped. - localCRL string + localCRL localCRL // HardFail determines whether the failure to check the revocation // status of a certificate (i.e. due to network failure) causes // verification to fail (a hard failure). - hardFail bool + hardFail hardFail // crlSet associates a PKIX certificate list with the URL the CRL is // fetched from. - crlSet map[string]*pkix.CertificateList - // lck is a Mutex locker for the struct - lck sync.Mutex + crl crlSet } -// DefaultChecker is a default config for regular apps which don't need to +// defaultChecker is a default config for regular apps which don't need to // use custom options. -var DefaultChecker = New(false) +var defaultChecker = New(false) // New creates Revoke config structure. // Accepts hardfail bool variable as an option func New(hardfail bool) *Revoke { return &Revoke{ - localCRL: "", - hardFail: hardfail, - crlSet: map[string]*pkix.CertificateList{}, + localCRL: localCRL{ + path: "", + crl: nil, + }, + hardFail: hardFail{ + val: hardfail, + }, + crl: crlSet{ + set: map[string]*pkix.CertificateList{}, + }, } } +func (r *Revoke) unsetLocalCRL() { + r.localCRL.path = "" + r.localCRL.crl = nil +} + // SetLocalCRL sets localCRL path into the Revoke struct func (r *Revoke) SetLocalCRL(localCRLpath string) error { + r.localCRL.Lock() + defer r.localCRL.Unlock() + + return r.setLocalCRL(localCRLpath) +} + +func (r *Revoke) setLocalCRL(localCRLpath string) error { if localCRLpath == "" { - r.lck.Lock() - if _, ok := r.crlSet[r.localCRL]; ok { - delete(r.crlSet, r.localCRL) - } - r.localCRL = "" - r.lck.Unlock() + r.unsetLocalCRL() return nil } - if u, err := neturl.Parse(r.localCRL); err != nil { - return err - } else if u.Scheme != "" { - return fmt.Errorf("Path is not valid: %s", localCRLpath) - } - if err := r.fetchLocalCRL(localCRLpath, true); err != nil { + if u, err := neturl.Parse(localCRLpath); err != nil { + r.unsetLocalCRL() return err + } else if u.Scheme == "" { + crl, err := r.fetchLocalCRL(localCRLpath) + if err != nil { + r.unsetLocalCRL() + return err + } + r.localCRL.crl = crl + r.localCRL.path = localCRLpath + return nil + } else if u.Scheme == "file" { + crl, err := r.fetchLocalCRL(u.Path) + if err != nil { + r.unsetLocalCRL() + return err + } + r.localCRL.crl = crl + r.localCRL.path = u.Path + return nil } - return nil + return fmt.Errorf("Path is not valid: %s", localCRLpath) +} + +// SetHardFail allows to dynamically set hardfail bool into the +// default var struct +func SetHardFail(hardfail bool) { + defaultChecker.SetHardFail(hardfail) +} + +// IsHardFail returns hardfail bool from the default var struct +func IsHardFail() bool { + return defaultChecker.IsHardFail() } // SetHardFail allows to dynamically set hardfail bool into the // Revoke struct func (r *Revoke) SetHardFail(hardfail bool) { - r.lck.Lock() - r.hardFail = hardfail - r.lck.Unlock() + defer r.hardFail.Unlock() + r.hardFail.Lock() + r.hardFail.val = hardfail } -// HardFail returns hardfail bool from the Revoke struct -func (r *Revoke) HardFail() bool { - return r.hardFail +// IsHardFail returns hardfail bool from the Revoke struct +func (r *Revoke) IsHardFail() bool { + defer r.hardFail.RUnlock() + r.hardFail.RLock() + return r.hardFail.val } // We can't handle LDAP certificates, so this checks to see if the @@ -122,15 +183,15 @@ func ldapURL(url string) bool { // true, false: failure to check revocation status causes // verification to fail func (r *Revoke) revCheck(cert *x509.Certificate) (revoked, ok bool) { - if r.localCRL != "" { - if revoked, ok := r.certIsRevokedCRL(cert, r.localCRL); !ok { + if r.localCRL.path != "" && r.localCRL.crl != nil { + if revoked, ok := r.certIsRevokedByLocalCRL(cert); !ok { log.Warning("error checking revocation via local CRL file") - if r.hardFail { + if r.hardFail.val { return true, false } return false, false } else if revoked { - log.Infof("certificate is revoked by '%s' CRL file (CN=%s, Serial: %s)", r.localCRL, cert.Subject.CommonName, cert.SerialNumber) + log.Infof("certificate is revoked by '%s' CRL file (CN=%s, Serial: %s)", r.localCRL.path, cert.Subject.CommonName, cert.SerialNumber) return true, true } } @@ -143,23 +204,23 @@ func (r *Revoke) revCheck(cert *x509.Certificate) (revoked, ok bool) { if revoked, ok := r.certIsRevokedCRL(cert, url); !ok { log.Warning("error checking revocation via CRL") - if r.hardFail { + if r.hardFail.val { return true, false } return false, false } else if revoked { - log.Info("certificate is revoked by '%s' CRL (CN=%s, Serial: %s)", url, cert.Subject.CommonName, cert.SerialNumber) + log.Infof("certificate is revoked by '%s' CRL (CN=%s, Serial: %s)", url, cert.Subject.CommonName, cert.SerialNumber) return true, true } - if revoked, ok := certIsRevokedOCSP(cert, r.hardFail); !ok { + if revoked, ok := certIsRevokedOCSP(cert, r.hardFail.val); !ok { log.Warning("error checking revocation via OCSP") - if r.hardFail { + if r.hardFail.val { return true, false } return false, false } else if revoked { - log.Info("certificate is revoked by '%s' OCSP (CN=%s, Serial: %s)", url, cert.Subject.CommonName, cert.SerialNumber) + log.Infof("certificate is revoked by '%s' OCSP (CN=%s, Serial: %s)", url, cert.Subject.CommonName, cert.SerialNumber) return true, true } } @@ -201,18 +262,17 @@ func getIssuer(cert *x509.Certificate) *x509.Certificate { // checks whether CRL in memory is valid func (r *Revoke) isInMemoryCRLValid(key string) bool { - crl, ok := r.crlSet[key] + crl, ok := r.crl.set[key] if ok && crl == nil { ok = false - r.lck.Lock() - delete(r.crlSet, key) - r.lck.Unlock() + delete(r.crl.set, key) + } else if crl == nil { + delete(r.crl.set, key) + return false } - if ok { - if !crl.HasExpired(time.Now()) { - return true - } + if ok && !crl.HasExpired(time.Now()) { + return true } return false @@ -220,44 +280,37 @@ func (r *Revoke) isInMemoryCRLValid(key string) bool { // fetchLocalCRL reads CRL from the local filesystem // force flag allows you to update the CRL -func (r *Revoke) fetchLocalCRL(newLocalCRL string, force bool) error { - if force || !r.isInMemoryCRLValid(newLocalCRL) { - if _, err := os.Stat(newLocalCRL); err != nil { - return fmt.Errorf("failed to read local CRL path: %v", err) - } +func (r *Revoke) fetchLocalCRL(newLocalCRL string) (*pkix.CertificateList, error) { + if _, err := os.Stat(newLocalCRL); err != nil { + return nil, fmt.Errorf("failed to read local CRL path: %v", err) + } - tmp, err := ioutil.ReadFile(newLocalCRL) - if err != nil { - return fmt.Errorf("failed to read local CRL path: %v", err) - } - crl, err := x509.ParseCRL(tmp) - if err != nil { - return fmt.Errorf("failed to parse local CRL file: %v", err) - } + tmp, err := ioutil.ReadFile(newLocalCRL) + if err != nil { + return nil, fmt.Errorf("failed to read local CRL path: %v", err) + } + crl, err := x509.ParseCRL(tmp) + if err != nil { + return nil, fmt.Errorf("failed to parse local CRL file: %v", err) + } - r.lck.Lock() - r.crlSet[newLocalCRL] = crl - if r.localCRL != newLocalCRL { - if _, ok := r.crlSet[r.localCRL]; ok { - delete(r.crlSet, r.localCRL) - } - r.localCRL = newLocalCRL - } - r.lck.Unlock() + if crl == nil { + return nil, fmt.Errorf("Local CRL is nil") } - return nil + if crl.HasExpired(time.Now()) { + return nil, fmt.Errorf("Local CRL (%s) has expired", newLocalCRL) + } + + return crl, nil } // FetchRemoteCRL fetches remote CRL into internal map, // force overwrites previously read CRL -func (r *Revoke) FetchRemoteCRL(url string, cert *x509.Certificate, force bool) error { +func (r *Revoke) fetchRemoteCRL(url string, issuer *x509.Certificate) error { shouldFetchCRL := !r.isInMemoryCRLValid(url) - issuer := getIssuer(cert) - - if force || shouldFetchCRL { - var err error + if shouldFetchCRL { crl, err := fetchCRL(url) if err != nil { return fmt.Errorf("failed to fetch CRL: %v", err) @@ -271,31 +324,43 @@ func (r *Revoke) FetchRemoteCRL(url string, cert *x509.Certificate, force bool) } } - r.lck.Lock() - r.crlSet[url] = crl - r.lck.Unlock() + r.crl.set[url] = crl } return nil } // check a cert against a specific CRL. Returns the same bool pair -// as revCheck. +// as revCheck. If remote is false - will assume that url is a file. func (r *Revoke) certIsRevokedCRL(cert *x509.Certificate, url string) (revoked, ok bool) { - var err error - if url == r.localCRL { - err = r.fetchLocalCRL(r.localCRL, false) - } else { - err = r.FetchRemoteCRL(url, cert, false) - } + err := r.fetchRemoteCRL(url, getIssuer(cert)) if err != nil { log.Warningf("%v", err) return false, false } - for _, revoked := range r.crlSet[url].TBSCertList.RevokedCertificates { + for _, revoked := range r.crl.set[url].TBSCertList.RevokedCertificates { if cert.SerialNumber.Cmp(revoked.SerialNumber) == 0 { + log.Info("Serial number match: intermediate is revoked.") + return true, true + } + } + + return false, true +} + +// check a cert against a specific localCRL. Returns the same bool pair +// as revCheck. +func (r *Revoke) certIsRevokedByLocalCRL(cert *x509.Certificate) (revoked, ok bool) { + if r.localCRL.crl.HasExpired(time.Now()) { + log.Info("Local CRL (%s) has expired", r.localCRL.path) + return false, false + } + + for _, revoked := range r.localCRL.crl.TBSCertList.RevokedCertificates { + if cert.SerialNumber.Cmp(revoked.SerialNumber) == 0 { + log.Info("Serial number match: intermediate is revoked.") return true, true } } @@ -318,14 +383,8 @@ func verifyCertTime(cert *x509.Certificate) bool { // VerifyCertificate ensures that the certificate passed in hasn't // expired and checks the CRL for the server. -// Comparing to the next public method, this function uses -// DefaultChecker variable. func VerifyCertificate(cert *x509.Certificate) (revoked, ok bool) { - if !verifyCertTime(cert) { - return true, true - } - - return DefaultChecker.revCheck(cert) + return defaultChecker.VerifyCertificate(cert) } // VerifyCertificate ensures that the certificate passed in hasn't @@ -335,6 +394,13 @@ func (r *Revoke) VerifyCertificate(cert *x509.Certificate) (revoked, ok bool) { return true, true } + defer r.hardFail.RUnlock() + defer r.localCRL.RUnlock() + defer r.crl.Unlock() + r.hardFail.RLock() + r.localCRL.RLock() + r.crl.Lock() + return r.revCheck(cert) } diff --git a/pkg/tlsutil/tlsutil.go b/pkg/tlsutil/tlsutil.go index 375378bd35e..0f55350e0bc 100644 --- a/pkg/tlsutil/tlsutil.go +++ b/pkg/tlsutil/tlsutil.go @@ -80,7 +80,7 @@ func isReqCertValid(req *http.Request, rc *revoke.Revoke) bool { } for _, cert := range req.TLS.PeerCertificates { revoked, ok := rc.VerifyCertificate(cert) - if !ok && rc.HardFail() { + if !ok && rc.IsHardFail() { return false } if revoked {