1  
     2  
     3  
     4  
     5  
     6  
     7  
     8  
     9  
    10  package nss
    11  
    12  import (
    13  	"bufio"
    14  	"bytes"
    15  	"crypto/sha1"
    16  	"crypto/x509"
    17  	"errors"
    18  	"fmt"
    19  	"io"
    20  	"strconv"
    21  	"strings"
    22  	"time"
    23  )
    24  
    25  
    26  
    27  type Constraint interface {
    28  	Kind() Kind
    29  }
    30  
    31  
    32  type Kind int
    33  
    34  const (
    35  	CKA_NSS_SERVER_DISTRUST_AFTER Kind = iota
    36  )
    37  
    38  
    39  
    40  
    41  
    42  type DistrustAfter time.Time
    43  
    44  func (DistrustAfter) Kind() Kind {
    45  	return CKA_NSS_SERVER_DISTRUST_AFTER
    46  }
    47  
    48  
    49  
    50  
    51  type Certificate struct {
    52  	
    53  	X509 *x509.Certificate
    54  	
    55  	
    56  	
    57  	
    58  	Constraints []Constraint
    59  }
    60  
    61  func parseMulitLineOctal(s *bufio.Scanner) ([]byte, error) {
    62  	buf := bytes.NewBuffer(nil)
    63  	for s.Scan() {
    64  		if s.Text() == "END" {
    65  			break
    66  		}
    67  		b, err := strconv.Unquote(fmt.Sprintf("\"%s\"", s.Text()))
    68  		if err != nil {
    69  			return nil, err
    70  		}
    71  		buf.Write([]byte(b))
    72  	}
    73  	return buf.Bytes(), nil
    74  }
    75  
    76  type certObj struct {
    77  	c             *x509.Certificate
    78  	DistrustAfter *time.Time
    79  }
    80  
    81  func parseCertClass(s *bufio.Scanner) ([sha1.Size]byte, *certObj, error) {
    82  	var h [sha1.Size]byte
    83  	co := &certObj{}
    84  	for s.Scan() {
    85  		l := s.Text()
    86  		if l == "" {
    87  			
    88  			break
    89  		}
    90  		if strings.HasPrefix(l, "CKA_VALUE") {
    91  			b, err := parseMulitLineOctal(s)
    92  			if err != nil {
    93  				return h, nil, err
    94  			}
    95  			co.c, err = x509.ParseCertificate(b)
    96  			if err != nil {
    97  				return h, nil, err
    98  			}
    99  			h = sha1.Sum(b)
   100  		} else if strings.HasPrefix(l, "CKA_NSS_MOZILLA_CA_POLICY CK_BBOOL CK_FALSE") {
   101  			
   102  			return h, nil, nil
   103  		} else if l == "CKA_NSS_SERVER_DISTRUST_AFTER MULTILINE_OCTAL" {
   104  			dateStr, err := parseMulitLineOctal(s)
   105  			if err != nil {
   106  				return h, nil, err
   107  			}
   108  			t, err := time.Parse("060102150405Z0700", string(dateStr))
   109  			if err != nil {
   110  				return h, nil, err
   111  			}
   112  			co.DistrustAfter = &t
   113  		}
   114  	}
   115  	if co.c == nil {
   116  		return h, nil, errors.New("malformed CKO_CERTIFICATE object")
   117  	}
   118  	return h, co, nil
   119  }
   120  
   121  type trustObj struct {
   122  	trusted bool
   123  }
   124  
   125  func parseTrustClass(s *bufio.Scanner) ([sha1.Size]byte, *trustObj, error) {
   126  	var h [sha1.Size]byte
   127  	to := &trustObj{trusted: false} 
   128  
   129  	for s.Scan() {
   130  		l := s.Text()
   131  		if l == "" {
   132  			
   133  			break
   134  		}
   135  		if l == "CKA_CERT_SHA1_HASH MULTILINE_OCTAL" {
   136  			hash, err := parseMulitLineOctal(s)
   137  			if err != nil {
   138  				return h, nil, err
   139  			}
   140  			copy(h[:], hash)
   141  		} else if l == "CKA_TRUST_SERVER_AUTH CK_TRUST CKT_NSS_TRUSTED_DELEGATOR" {
   142  			
   143  			to.trusted = true
   144  		}
   145  	}
   146  
   147  	return h, to, nil
   148  }
   149  
   150  
   151  
   152  
   153  
   154  
   155  
   156  
   157  
   158  
   159  
   160  
   161  
   162  
   163  func Parse(r io.Reader) ([]*Certificate, error) {
   164  	
   165  	
   166  	
   167  	
   168  	
   169  	
   170  	
   171  	
   172  	
   173  	
   174  	
   175  	
   176  	
   177  	
   178  	
   179  	
   180  	
   181  	
   182  	
   183  	
   184  
   185  	scanner := bufio.NewScanner(r)
   186  
   187  	type nssEntry struct {
   188  		cert  *certObj
   189  		trust *trustObj
   190  	}
   191  	entries := map[[sha1.Size]byte]*nssEntry{}
   192  
   193  	for scanner.Scan() {
   194  		
   195  		if !strings.HasPrefix(scanner.Text(), "CKA_CLASS") {
   196  			continue
   197  		}
   198  
   199  		f := strings.Fields(scanner.Text())
   200  		if len(f) != 3 {
   201  			return nil, errors.New("malformed CKA_CLASS")
   202  		}
   203  		switch f[2] {
   204  		case "CKO_CERTIFICATE":
   205  			h, co, err := parseCertClass(scanner)
   206  			if err != nil {
   207  				return nil, err
   208  			}
   209  			if co != nil {
   210  				e, ok := entries[h]
   211  				if !ok {
   212  					e = &nssEntry{}
   213  					entries[h] = e
   214  				}
   215  				e.cert = co
   216  			}
   217  
   218  		case "CKO_NSS_TRUST":
   219  			h, to, err := parseTrustClass(scanner)
   220  			if err != nil {
   221  				return nil, err
   222  			}
   223  			if to != nil {
   224  				e, ok := entries[h]
   225  				if !ok {
   226  					e = &nssEntry{}
   227  					entries[h] = e
   228  				}
   229  				e.trust = to
   230  			}
   231  		}
   232  	}
   233  	if err := scanner.Err(); err != nil {
   234  		return nil, err
   235  	}
   236  
   237  	var certs []*Certificate
   238  	for h, e := range entries {
   239  		if e.cert == nil && e.trust != nil {
   240  			
   241  			
   242  			
   243  			
   244  			continue
   245  		} else if e.cert != nil && e.trust == nil {
   246  			return nil, fmt.Errorf("missing trust object for certificate with SHA1 hash: %x", h)
   247  		}
   248  		if !e.trust.trusted {
   249  			continue
   250  		}
   251  		nssCert := &Certificate{X509: e.cert.c}
   252  		if e.cert.DistrustAfter != nil {
   253  			nssCert.Constraints = append(nssCert.Constraints, DistrustAfter(*e.cert.DistrustAfter))
   254  		}
   255  		certs = append(certs, nssCert)
   256  	}
   257  
   258  	return certs, nil
   259  }
   260  
View as plain text