mirror of
https://github.com/moby/moby.git
synced 2026-08-09 01:21:37 +00:00
full diff: https://github.com/zmap/zlint/compare/v3.1.0...v3.5.0 Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
650 lines
20 KiB
Go
650 lines
20 KiB
Go
// Copyright 2015 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package x509
|
|
|
|
import (
|
|
"crypto/ecdsa"
|
|
"crypto/rsa"
|
|
"encoding/json"
|
|
"errors"
|
|
"net"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/zmap/zcrypto/dsa"
|
|
"github.com/zmap/zcrypto/encoding/asn1"
|
|
jsonKeys "github.com/zmap/zcrypto/json"
|
|
"github.com/zmap/zcrypto/util"
|
|
"github.com/zmap/zcrypto/x509/pkix"
|
|
)
|
|
|
|
var kMinTime, kMaxTime time.Time
|
|
|
|
func init() {
|
|
var err error
|
|
kMinTime, err = time.Parse(time.RFC3339, "0001-01-01T00:00:00Z")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
kMaxTime, err = time.Parse(time.RFC3339, "9999-12-31T23:59:59Z")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
type auxKeyUsage struct {
|
|
DigitalSignature bool `json:"digital_signature,omitempty"`
|
|
ContentCommitment bool `json:"content_commitment,omitempty"`
|
|
KeyEncipherment bool `json:"key_encipherment,omitempty"`
|
|
DataEncipherment bool `json:"data_encipherment,omitempty"`
|
|
KeyAgreement bool `json:"key_agreement,omitempty"`
|
|
CertificateSign bool `json:"certificate_sign,omitempty"`
|
|
CRLSign bool `json:"crl_sign,omitempty"`
|
|
EncipherOnly bool `json:"encipher_only,omitempty"`
|
|
DecipherOnly bool `json:"decipher_only,omitempty"`
|
|
Value uint32 `json:"value"`
|
|
}
|
|
|
|
// MarshalJSON implements the json.Marshaler interface
|
|
func (k KeyUsage) MarshalJSON() ([]byte, error) {
|
|
var enc auxKeyUsage
|
|
enc.Value = uint32(k)
|
|
if k&KeyUsageDigitalSignature > 0 {
|
|
enc.DigitalSignature = true
|
|
}
|
|
if k&KeyUsageContentCommitment > 0 {
|
|
enc.ContentCommitment = true
|
|
}
|
|
if k&KeyUsageKeyEncipherment > 0 {
|
|
enc.KeyEncipherment = true
|
|
}
|
|
if k&KeyUsageDataEncipherment > 0 {
|
|
enc.DataEncipherment = true
|
|
}
|
|
if k&KeyUsageKeyAgreement > 0 {
|
|
enc.KeyAgreement = true
|
|
}
|
|
if k&KeyUsageCertSign > 0 {
|
|
enc.CertificateSign = true
|
|
}
|
|
if k&KeyUsageCRLSign > 0 {
|
|
enc.CRLSign = true
|
|
}
|
|
if k&KeyUsageEncipherOnly > 0 {
|
|
enc.EncipherOnly = true
|
|
}
|
|
if k&KeyUsageDecipherOnly > 0 {
|
|
enc.DecipherOnly = true
|
|
}
|
|
return json.Marshal(&enc)
|
|
}
|
|
|
|
// UnmarshalJSON implements the json.Unmarshler interface
|
|
func (k *KeyUsage) UnmarshalJSON(b []byte) error {
|
|
var aux auxKeyUsage
|
|
if err := json.Unmarshal(b, &aux); err != nil {
|
|
return err
|
|
}
|
|
// TODO: validate the flags match
|
|
v := int(aux.Value)
|
|
*k = KeyUsage(v)
|
|
return nil
|
|
}
|
|
|
|
// JSONSignatureAlgorithm is the intermediate type
|
|
// used when marshaling a PublicKeyAlgorithm out to JSON.
|
|
type JSONSignatureAlgorithm struct {
|
|
Name string `json:"name,omitempty"`
|
|
OID pkix.AuxOID `json:"oid"`
|
|
}
|
|
|
|
// MarshalJSON implements the json.Marshaler interface
|
|
// MAY NOT PRESERVE ORIGINAL OID FROM CERTIFICATE -
|
|
// CONSIDER USING jsonifySignatureAlgorithm INSTEAD!
|
|
func (s *SignatureAlgorithm) MarshalJSON() ([]byte, error) {
|
|
aux := JSONSignatureAlgorithm{
|
|
Name: s.String(),
|
|
}
|
|
for _, val := range signatureAlgorithmDetails {
|
|
if val.algo == *s {
|
|
aux.OID = make([]int, len(val.oid))
|
|
for idx := range val.oid {
|
|
aux.OID[idx] = val.oid[idx]
|
|
}
|
|
}
|
|
}
|
|
return json.Marshal(&aux)
|
|
}
|
|
|
|
// UnmarshalJSON implements the json.Unmarshler interface
|
|
func (s *SignatureAlgorithm) UnmarshalJSON(b []byte) error {
|
|
var aux JSONSignatureAlgorithm
|
|
if err := json.Unmarshal(b, &aux); err != nil {
|
|
return err
|
|
}
|
|
*s = UnknownSignatureAlgorithm
|
|
oid := asn1.ObjectIdentifier(aux.OID.AsSlice())
|
|
if oid.Equal(oidSignatureRSAPSS) {
|
|
pssAlgs := []SignatureAlgorithm{SHA256WithRSAPSS, SHA384WithRSAPSS, SHA512WithRSAPSS}
|
|
for _, alg := range pssAlgs {
|
|
if strings.Compare(alg.String(), aux.Name) == 0 {
|
|
*s = alg
|
|
break
|
|
}
|
|
}
|
|
} else {
|
|
for _, val := range signatureAlgorithmDetails {
|
|
if val.oid.Equal(oid) {
|
|
*s = val.algo
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// jsonifySignatureAlgorithm gathers the necessary fields in a Certificate
|
|
// into a JSONSignatureAlgorithm, which can then use the default
|
|
// JSON marhsalers and unmarshalers. THIS FUNCTION IS PREFERED OVER
|
|
// THE CUSTOM JSON MARSHALER PRESENTED ABOVE FOR SIGNATUREALGORITHM
|
|
// BECAUSE THIS METHOD PRESERVES THE OID ORIGINALLY IN THE CERTIFICATE!
|
|
// This reason also explains why we need this function -
|
|
// the OID is unfortunately stored outside the scope of a
|
|
// SignatureAlgorithm struct and cannot be recovered without access to the
|
|
// entire Certificate if we do not know the signature algorithm.
|
|
func (c *Certificate) jsonifySignatureAlgorithm() JSONSignatureAlgorithm {
|
|
aux := JSONSignatureAlgorithm{}
|
|
if c.SignatureAlgorithm == 0 {
|
|
aux.Name = "unknown_algorithm"
|
|
} else {
|
|
aux.Name = c.SignatureAlgorithm.String()
|
|
}
|
|
aux.OID = make([]int, len(c.SignatureAlgorithmOID))
|
|
for idx := range c.SignatureAlgorithmOID {
|
|
aux.OID[idx] = c.SignatureAlgorithmOID[idx]
|
|
}
|
|
return aux
|
|
}
|
|
|
|
type auxPublicKeyAlgorithm struct {
|
|
Name string `json:"name,omitempty"`
|
|
OID *pkix.AuxOID `json:"oid,omitempty"`
|
|
}
|
|
|
|
var publicKeyNameToAlgorithm = map[string]PublicKeyAlgorithm{
|
|
"RSA": RSA,
|
|
"DSA": DSA,
|
|
"ECDSA": ECDSA,
|
|
}
|
|
|
|
// MarshalJSON implements the json.Marshaler interface
|
|
func (p *PublicKeyAlgorithm) MarshalJSON() ([]byte, error) {
|
|
aux := auxPublicKeyAlgorithm{
|
|
Name: p.String(),
|
|
}
|
|
return json.Marshal(&aux)
|
|
}
|
|
|
|
// UnmarshalJSON implements the json.Unmarshaler interface
|
|
func (p *PublicKeyAlgorithm) UnmarshalJSON(b []byte) error {
|
|
var aux auxPublicKeyAlgorithm
|
|
if err := json.Unmarshal(b, &aux); err != nil {
|
|
return err
|
|
}
|
|
*p = publicKeyNameToAlgorithm[aux.Name]
|
|
return nil
|
|
}
|
|
|
|
func clampTime(t time.Time) time.Time {
|
|
if t.Before(kMinTime) {
|
|
return kMinTime
|
|
}
|
|
if t.After(kMaxTime) {
|
|
return kMaxTime
|
|
}
|
|
return t
|
|
}
|
|
|
|
type auxValidity struct {
|
|
Start string `json:"start"`
|
|
End string `json:"end"`
|
|
ValidityPeriod int `json:"length"`
|
|
}
|
|
|
|
func (v *validity) MarshalJSON() ([]byte, error) {
|
|
aux := auxValidity{
|
|
Start: clampTime(v.NotBefore.UTC()).Format(time.RFC3339),
|
|
End: clampTime(v.NotAfter.UTC()).Format(time.RFC3339),
|
|
ValidityPeriod: int(v.NotAfter.Unix() - v.NotBefore.Unix()),
|
|
}
|
|
return json.Marshal(&aux)
|
|
}
|
|
|
|
func (v *validity) UnmarshalJSON(b []byte) error {
|
|
var aux auxValidity
|
|
if err := json.Unmarshal(b, &aux); err != nil {
|
|
return err
|
|
}
|
|
var err error
|
|
if v.NotBefore, err = time.Parse(time.RFC3339, aux.Start); err != nil {
|
|
return err
|
|
}
|
|
if v.NotAfter, err = time.Parse(time.RFC3339, aux.End); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ECDSAPublicKeyJSON - used to condense several fields from a
|
|
// ECDSA public key into one field for use in JSONCertificate.
|
|
// Uses default JSON marshal and unmarshal methods
|
|
type ECDSAPublicKeyJSON struct {
|
|
B []byte `json:"b"`
|
|
Curve string `json:"curve"`
|
|
Gx []byte `json:"gx"`
|
|
Gy []byte `json:"gy"`
|
|
Length int `json:"length"`
|
|
N []byte `json:"n"`
|
|
P []byte `json:"p"`
|
|
Pub []byte `json:"pub,omitempty"`
|
|
X []byte `json:"x"`
|
|
Y []byte `json:"y"`
|
|
}
|
|
|
|
// DSAPublicKeyJSON - used to condense several fields from a
|
|
// DSA public key into one field for use in JSONCertificate.
|
|
// Uses default JSON marshal and unmarshal methods
|
|
type DSAPublicKeyJSON struct {
|
|
G []byte `json:"g"`
|
|
P []byte `json:"p"`
|
|
Q []byte `json:"q"`
|
|
Y []byte `json:"y"`
|
|
}
|
|
|
|
// GetDSAPublicKeyJSON - get the DSAPublicKeyJSON for the given standard DSA PublicKey.
|
|
func GetDSAPublicKeyJSON(key *dsa.PublicKey) *DSAPublicKeyJSON {
|
|
return &DSAPublicKeyJSON{
|
|
P: key.P.Bytes(),
|
|
Q: key.Q.Bytes(),
|
|
G: key.G.Bytes(),
|
|
Y: key.Y.Bytes(),
|
|
}
|
|
}
|
|
|
|
// GetRSAPublicKeyJSON - get the jsonKeys.RSAPublicKey for the given standard RSA PublicKey.
|
|
func GetRSAPublicKeyJSON(key *rsa.PublicKey) *jsonKeys.RSAPublicKey {
|
|
rsaKey := new(jsonKeys.RSAPublicKey)
|
|
rsaKey.PublicKey = key
|
|
return rsaKey
|
|
}
|
|
|
|
// GetECDSAPublicKeyJSON - get the GetECDSAPublicKeyJSON for the given standard ECDSA PublicKey.
|
|
func GetECDSAPublicKeyJSON(key *ecdsa.PublicKey) *ECDSAPublicKeyJSON {
|
|
params := key.Params()
|
|
return &ECDSAPublicKeyJSON{
|
|
P: params.P.Bytes(),
|
|
N: params.N.Bytes(),
|
|
B: params.B.Bytes(),
|
|
Gx: params.Gx.Bytes(),
|
|
Gy: params.Gy.Bytes(),
|
|
X: key.X.Bytes(),
|
|
Y: key.Y.Bytes(),
|
|
Curve: key.Curve.Params().Name,
|
|
Length: key.Curve.Params().BitSize,
|
|
}
|
|
}
|
|
|
|
// GetAugmentedECDSAPublicKeyJSON - get the GetECDSAPublicKeyJSON for the given "augmented"
|
|
// ECDSA PublicKey.
|
|
func GetAugmentedECDSAPublicKeyJSON(key *AugmentedECDSA) *ECDSAPublicKeyJSON {
|
|
params := key.Pub.Params()
|
|
return &ECDSAPublicKeyJSON{
|
|
P: params.P.Bytes(),
|
|
N: params.N.Bytes(),
|
|
B: params.B.Bytes(),
|
|
Gx: params.Gx.Bytes(),
|
|
Gy: params.Gy.Bytes(),
|
|
X: key.Pub.X.Bytes(),
|
|
Y: key.Pub.Y.Bytes(),
|
|
Curve: key.Pub.Curve.Params().Name,
|
|
Length: key.Pub.Curve.Params().BitSize,
|
|
Pub: key.Raw.Bytes,
|
|
}
|
|
}
|
|
|
|
// jsonifySubjectKey - Convert public key data in a Certificate
|
|
// into json output format for JSONCertificate
|
|
func (c *Certificate) jsonifySubjectKey() JSONSubjectKeyInfo {
|
|
j := JSONSubjectKeyInfo{
|
|
KeyAlgorithm: c.PublicKeyAlgorithm,
|
|
SPKIFingerprint: c.SPKIFingerprint,
|
|
}
|
|
|
|
switch key := c.PublicKey.(type) {
|
|
case *rsa.PublicKey:
|
|
rsaKey := new(jsonKeys.RSAPublicKey)
|
|
rsaKey.PublicKey = key
|
|
j.RSAPublicKey = rsaKey
|
|
case *dsa.PublicKey:
|
|
j.DSAPublicKey = &DSAPublicKeyJSON{
|
|
P: key.P.Bytes(),
|
|
Q: key.Q.Bytes(),
|
|
G: key.G.Bytes(),
|
|
Y: key.Y.Bytes(),
|
|
}
|
|
case *ecdsa.PublicKey:
|
|
params := key.Params()
|
|
j.ECDSAPublicKey = &ECDSAPublicKeyJSON{
|
|
P: params.P.Bytes(),
|
|
N: params.N.Bytes(),
|
|
B: params.B.Bytes(),
|
|
Gx: params.Gx.Bytes(),
|
|
Gy: params.Gy.Bytes(),
|
|
X: key.X.Bytes(),
|
|
Y: key.Y.Bytes(),
|
|
Curve: key.Curve.Params().Name,
|
|
Length: key.Curve.Params().BitSize,
|
|
}
|
|
case *AugmentedECDSA:
|
|
params := key.Pub.Params()
|
|
j.ECDSAPublicKey = &ECDSAPublicKeyJSON{
|
|
P: params.P.Bytes(),
|
|
N: params.N.Bytes(),
|
|
B: params.B.Bytes(),
|
|
Gx: params.Gx.Bytes(),
|
|
Gy: params.Gy.Bytes(),
|
|
X: key.Pub.X.Bytes(),
|
|
Y: key.Pub.Y.Bytes(),
|
|
Curve: key.Pub.Curve.Params().Name,
|
|
Length: key.Pub.Curve.Params().BitSize,
|
|
Pub: key.Raw.Bytes,
|
|
}
|
|
}
|
|
return j
|
|
}
|
|
|
|
// JSONSubjectKeyInfo - used to condense several fields from x509.Certificate
|
|
// related to the subject public key into one field within JSONCertificate
|
|
// Unfortunately, this struct cannot have its own Marshal method since it
|
|
// needs information from multiple fields in x509.Certificate
|
|
type JSONSubjectKeyInfo struct {
|
|
KeyAlgorithm PublicKeyAlgorithm `json:"key_algorithm"`
|
|
RSAPublicKey *jsonKeys.RSAPublicKey `json:"rsa_public_key,omitempty"`
|
|
DSAPublicKey *DSAPublicKeyJSON `json:"dsa_public_key,omitempty"`
|
|
ECDSAPublicKey *ECDSAPublicKeyJSON `json:"ecdsa_public_key,omitempty"`
|
|
SPKIFingerprint CertificateFingerprint `json:"fingerprint_sha256"`
|
|
}
|
|
|
|
// JSONSignature - used to condense several fields from x509.Certificate
|
|
// related to the signature into one field within JSONCertificate
|
|
// Unfortunately, this struct cannot have its own Marshal method since it
|
|
// needs information from multiple fields in x509.Certificate
|
|
type JSONSignature struct {
|
|
SignatureAlgorithm JSONSignatureAlgorithm `json:"signature_algorithm"`
|
|
Value []byte `json:"value"`
|
|
Valid bool `json:"valid"`
|
|
SelfSigned bool `json:"self_signed"`
|
|
}
|
|
|
|
// JSONValidity - used to condense several fields related
|
|
// to validity in x509.Certificate into one field within JSONCertificate
|
|
// Unfortunately, this struct cannot have its own Marshal method since it
|
|
// needs information from multiple fields in x509.Certificate
|
|
type JSONValidity struct {
|
|
validity
|
|
ValidityPeriod int
|
|
}
|
|
|
|
// JSONCertificate - used to condense data from x509.Certificate when marhsaling
|
|
// into JSON. This struct has a distinct and independent layout from
|
|
// x509.Certificate, mostly for condensing data across repetitive
|
|
// fields and making it more presentable.
|
|
type JSONCertificate struct {
|
|
Version int `json:"version"`
|
|
SerialNumber string `json:"serial_number"`
|
|
SignatureAlgorithm JSONSignatureAlgorithm `json:"signature_algorithm"`
|
|
Issuer pkix.Name `json:"issuer"`
|
|
IssuerDN string `json:"issuer_dn,omitempty"`
|
|
Validity JSONValidity `json:"validity"`
|
|
Subject pkix.Name `json:"subject"`
|
|
SubjectDN string `json:"subject_dn,omitempty"`
|
|
SubjectKeyInfo JSONSubjectKeyInfo `json:"subject_key_info"`
|
|
Extensions *CertificateExtensions `json:"extensions,omitempty"`
|
|
UnknownExtensions UnknownCertificateExtensions `json:"unknown_extensions,omitempty"`
|
|
Signature JSONSignature `json:"signature"`
|
|
FingerprintMD5 CertificateFingerprint `json:"fingerprint_md5"`
|
|
FingerprintSHA1 CertificateFingerprint `json:"fingerprint_sha1"`
|
|
FingerprintSHA256 CertificateFingerprint `json:"fingerprint_sha256"`
|
|
FingerprintNoCT CertificateFingerprint `json:"tbs_noct_fingerprint"`
|
|
SPKISubjectFingerprint CertificateFingerprint `json:"spki_subject_fingerprint"`
|
|
TBSCertificateFingerprint CertificateFingerprint `json:"tbs_fingerprint"`
|
|
ValidationLevel CertValidationLevel `json:"validation_level"`
|
|
Names []string `json:"names,omitempty"`
|
|
Redacted bool `json:"redacted"`
|
|
}
|
|
|
|
// CollectAllNames - Collect and validate all DNS / URI / IP Address names for a given certificate
|
|
func (c *Certificate) CollectAllNames() []string {
|
|
var names []string
|
|
|
|
if isValidName(c.Subject.CommonName) {
|
|
names = append(names, c.Subject.CommonName)
|
|
}
|
|
|
|
for _, name := range c.DNSNames {
|
|
if isValidName(name) {
|
|
names = append(names, name)
|
|
} else if !strings.Contains(name, ".") { //just a TLD
|
|
names = append(names, name)
|
|
}
|
|
|
|
}
|
|
|
|
for _, name := range c.URIs {
|
|
if util.IsURL(name) {
|
|
names = append(names, name)
|
|
}
|
|
}
|
|
|
|
for _, name := range c.IPAddresses {
|
|
str := name.String()
|
|
if util.IsURL(str) {
|
|
names = append(names, str)
|
|
}
|
|
}
|
|
|
|
return purgeNameDuplicates(names)
|
|
}
|
|
|
|
func (c *Certificate) MarshalJSON() ([]byte, error) {
|
|
// Fill out the certificate
|
|
jc := new(JSONCertificate)
|
|
jc.Version = c.Version
|
|
jc.SerialNumber = c.SerialNumber.String()
|
|
jc.Issuer = c.Issuer
|
|
jc.IssuerDN = c.Issuer.String()
|
|
|
|
jc.Validity.NotBefore = c.NotBefore
|
|
jc.Validity.NotAfter = c.NotAfter
|
|
jc.Validity.ValidityPeriod = c.ValidityPeriod
|
|
jc.Subject = c.Subject
|
|
jc.SubjectDN = c.Subject.String()
|
|
jc.Names = c.CollectAllNames()
|
|
jc.Redacted = false
|
|
for _, name := range jc.Names {
|
|
if strings.HasPrefix(name, "?") {
|
|
jc.Redacted = true
|
|
}
|
|
}
|
|
|
|
jc.SubjectKeyInfo = c.jsonifySubjectKey()
|
|
jc.Extensions, jc.UnknownExtensions = c.jsonifyExtensions()
|
|
|
|
// TODO: Handle the fact this might not match
|
|
jc.SignatureAlgorithm = c.jsonifySignatureAlgorithm()
|
|
jc.Signature.SignatureAlgorithm = jc.SignatureAlgorithm
|
|
jc.Signature.Value = c.Signature
|
|
jc.Signature.Valid = c.validSignature
|
|
jc.Signature.SelfSigned = c.SelfSigned
|
|
if c.SelfSigned {
|
|
jc.Signature.Valid = true
|
|
}
|
|
jc.FingerprintMD5 = c.FingerprintMD5
|
|
jc.FingerprintSHA1 = c.FingerprintSHA1
|
|
jc.FingerprintSHA256 = c.FingerprintSHA256
|
|
jc.FingerprintNoCT = c.FingerprintNoCT
|
|
jc.SPKISubjectFingerprint = c.SPKISubjectFingerprint
|
|
jc.TBSCertificateFingerprint = c.TBSCertificateFingerprint
|
|
jc.ValidationLevel = c.ValidationLevel
|
|
|
|
return json.Marshal(jc)
|
|
}
|
|
|
|
// UnmarshalJSON - intentionally implimented to always error,
|
|
// as this method should not be used. The MarshalJSON method
|
|
// on Certificate condenses data in a way that is not recoverable.
|
|
// Use the x509.ParseCertificate function instead or
|
|
// JSONCertificateWithRaw Marshal method
|
|
func (jc *JSONCertificate) UnmarshalJSON(b []byte) error {
|
|
return errors.New("Do not unmarshal cert JSON directly, use JSONCertificateWithRaw or x509.ParseCertificate function")
|
|
}
|
|
|
|
// UnmarshalJSON - intentionally implimented to always error,
|
|
// as this method should not be used. The MarshalJSON method
|
|
// on Certificate condenses data in a way that is not recoverable.
|
|
// Use the x509.ParseCertificate function instead or
|
|
// JSONCertificateWithRaw Marshal method
|
|
func (c *Certificate) UnmarshalJSON(b []byte) error {
|
|
return errors.New("Do not unmarshal cert JSON directly, use JSONCertificateWithRaw or x509.ParseCertificate function")
|
|
}
|
|
|
|
// JSONCertificateWithRaw - intermediate struct for unmarshaling json
|
|
// of a certificate - the raw is require since the
|
|
// MarshalJSON method on Certificate condenses data in a way that
|
|
// makes extraction to the original in Unmarshal impossible.
|
|
// The JSON output of Marshal is not even used to construct
|
|
// a certificate, all we need is raw
|
|
type JSONCertificateWithRaw struct {
|
|
Raw []byte `json:"raw,omitempty"`
|
|
}
|
|
|
|
// ParseRaw - for converting the intermediate object
|
|
// JSONCertificateWithRaw into a parsed Certificate
|
|
// see description of JSONCertificateWithRaw for
|
|
// why this is used instead of UnmarshalJSON methods
|
|
func (c *JSONCertificateWithRaw) ParseRaw() (*Certificate, error) {
|
|
return ParseCertificate(c.Raw)
|
|
}
|
|
|
|
func purgeNameDuplicates(names []string) (out []string) {
|
|
hashset := make(map[string]bool, len(names))
|
|
for _, name := range names {
|
|
if _, inc := hashset[name]; !inc {
|
|
hashset[name] = true
|
|
}
|
|
}
|
|
|
|
out = make([]string, 0, len(hashset))
|
|
for key := range hashset {
|
|
out = append(out, key)
|
|
}
|
|
|
|
sort.Strings(out) // must sort to ensure output is deterministic!
|
|
return
|
|
}
|
|
|
|
func isValidName(name string) (ret bool) {
|
|
|
|
// Check for wildcards and redacts, ignore malformed urls
|
|
if strings.HasPrefix(name, "?.") || strings.HasPrefix(name, "*.") {
|
|
ret = isValidName(name[2:])
|
|
} else {
|
|
ret = util.IsURL(name)
|
|
}
|
|
return
|
|
}
|
|
|
|
func orMask(ip net.IP, mask net.IPMask) net.IP {
|
|
if len(ip) == 0 || len(mask) == 0 {
|
|
return nil
|
|
}
|
|
if len(ip) != net.IPv4len && len(ip) != net.IPv6len {
|
|
return nil
|
|
}
|
|
if len(ip) != len(mask) {
|
|
return nil
|
|
}
|
|
out := make([]byte, len(ip))
|
|
for idx := range ip {
|
|
out[idx] = ip[idx] | mask[idx]
|
|
}
|
|
return out
|
|
}
|
|
|
|
func invertMask(mask net.IPMask) net.IPMask {
|
|
if mask == nil {
|
|
return nil
|
|
}
|
|
out := make([]byte, len(mask))
|
|
for idx := range mask {
|
|
out[idx] = ^mask[idx]
|
|
}
|
|
return out
|
|
}
|
|
|
|
type auxGeneralSubtreeIP struct {
|
|
CIDR string `json:"cidr,omitempty"`
|
|
Begin string `json:"begin,omitempty"`
|
|
End string `json:"end,omitempty"`
|
|
Mask string `json:"mask,omitempty"`
|
|
}
|
|
|
|
func (g *GeneralSubtreeIP) MarshalJSON() ([]byte, error) {
|
|
aux := auxGeneralSubtreeIP{}
|
|
aux.CIDR = g.Data.String()
|
|
// Check to see if the subnet is valid. An invalid subnet will return 0,0
|
|
// from Size(). If the subnet is invalid, only output the CIDR.
|
|
ones, bits := g.Data.Mask.Size()
|
|
if ones == 0 && bits == 0 {
|
|
return json.Marshal(&aux)
|
|
}
|
|
// The first IP in the range should be `ip & mask`.
|
|
begin := g.Data.IP.Mask(g.Data.Mask)
|
|
if begin != nil {
|
|
aux.Begin = begin.String()
|
|
}
|
|
// The last IP (inclusive) is `ip & (^mask)`.
|
|
inverseMask := invertMask(g.Data.Mask)
|
|
end := orMask(g.Data.IP, inverseMask)
|
|
if end != nil {
|
|
aux.End = end.String()
|
|
}
|
|
// Output the mask as an IP, but enforce it can be formatted correctly.
|
|
// net.IP.String() only works on byte arrays of the correct length.
|
|
maskLen := len(g.Data.Mask)
|
|
if maskLen == net.IPv4len || maskLen == net.IPv6len {
|
|
maskAsIP := net.IP(g.Data.Mask)
|
|
aux.Mask = maskAsIP.String()
|
|
}
|
|
return json.Marshal(&aux)
|
|
}
|
|
|
|
func (g *GeneralSubtreeIP) UnmarshalJSON(b []byte) error {
|
|
aux := auxGeneralSubtreeIP{}
|
|
if err := json.Unmarshal(b, &aux); err != nil {
|
|
return err
|
|
}
|
|
ip, ipNet, err := net.ParseCIDR(aux.CIDR)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
g.Data.IP = ip
|
|
g.Data.Mask = ipNet.Mask
|
|
g.Min = 0
|
|
g.Max = 0
|
|
return nil
|
|
}
|