...

Source file src/github.com/in-toto/in-toto-golang/in_toto/certconstraint_test.go

Documentation: github.com/in-toto/in-toto-golang/in_toto

     1  package in_toto
     2  
     3  import (
     4  	"crypto"
     5  	"crypto/ed25519"
     6  	"crypto/rand"
     7  	"crypto/rsa"
     8  	"crypto/x509"
     9  	"crypto/x509/pkix"
    10  	"encoding/pem"
    11  	"fmt"
    12  	"math/big"
    13  	"net/url"
    14  	"testing"
    15  	"time"
    16  )
    17  
    18  type checkConstraintAttributeCase struct {
    19  	Constraints []string
    20  	Values      []string
    21  	Expected    bool
    22  }
    23  
    24  func TestCheckCertConstraint(t *testing.T) {
    25  	cases := []checkConstraintAttributeCase{
    26  		{
    27  			Constraints: []string{"test1", "test2"},
    28  			Values:      []string{"test2", "test1"},
    29  			Expected:    true,
    30  		},
    31  		{
    32  			Constraints: []string{"test1", "test2"},
    33  			Values:      []string{"test2"},
    34  			Expected:    false,
    35  		},
    36  		{
    37  			Constraints: []string{AllowAllConstraint},
    38  			Values:      []string{"any", "thing", "goes"},
    39  			Expected:    true,
    40  		},
    41  		{
    42  			Constraints: []string{},
    43  			Values:      []string{},
    44  			Expected:    true,
    45  		},
    46  		{
    47  			Constraints: []string{},
    48  			Values:      []string{"test1"},
    49  			Expected:    false,
    50  		},
    51  		{
    52  			Constraints: []string{""},
    53  			Values:      []string{""},
    54  			Expected:    true,
    55  		},
    56  		{
    57  			Constraints: []string{""},
    58  			Values:      []string{"test1"},
    59  			Expected:    false,
    60  		},
    61  		{
    62  			Constraints: []string{"test1", "test2"},
    63  			Values:      []string{"test1", "test2", "test3"},
    64  			Expected:    false,
    65  		},
    66  	}
    67  
    68  	for _, c := range cases {
    69  		err := checkCertConstraint("constraint", c.Constraints, c.Values)
    70  		actual := err == nil
    71  		if actual != c.Expected {
    72  			t.Errorf("got %v when expected %v. Constraints: %v, Values: %v", actual, c.Expected, c.Constraints, c.Values)
    73  		}
    74  	}
    75  }
    76  
    77  type constraintCheckCase struct {
    78  	Constraint CertificateConstraint
    79  	Cert       *x509.Certificate
    80  	Expected   bool
    81  }
    82  
    83  func TestConstraintCheck(t *testing.T) {
    84  	testCertSubject := pkix.Name{
    85  		CommonName:   "step1.example.com",
    86  		Organization: []string{"example"},
    87  	}
    88  	testCertEmails := []string{"example@example.com"}
    89  	testCertDNSNames := []string{"example.com"}
    90  	testCertURI, _ := url.Parse("spiffe://example.com/step1")
    91  	testCertURIs := []*url.URL{testCertURI}
    92  	testertValidity := 1 * time.Hour
    93  	testCertPublicKeyAlgorithm := x509.Ed25519
    94  	testCertTemplate := &x509.Certificate{
    95  		Subject:        testCertSubject,
    96  		EmailAddresses: testCertEmails,
    97  		DNSNames:       testCertDNSNames,
    98  		URIs:           testCertURIs,
    99  	}
   100  
   101  	testCert, testIntermediateCert, testRootCert, err := createTestCert(testCertTemplate, testCertPublicKeyAlgorithm, testertValidity)
   102  	if err != nil {
   103  		t.Fatalf("failed to create test cert: %v", err)
   104  	}
   105  
   106  	rootCertPool := x509.NewCertPool()
   107  	rootCertPool.AddCert(testRootCert)
   108  	intermediateCertPool := x509.NewCertPool()
   109  	intermediateCertPool.AddCert(testIntermediateCert)
   110  
   111  	roots := []string{"example"}
   112  
   113  	cases := []constraintCheckCase{
   114  		{
   115  			Cert: testCert,
   116  			Constraint: CertificateConstraint{
   117  				CommonName:    "step1.example.com",
   118  				DNSNames:      []string{"example.com"},
   119  				Emails:        []string{"example@example.com"},
   120  				Organizations: []string{"example"},
   121  				Roots:         []string{"example"},
   122  				URIs:          []string{"spiffe://example.com/step1"},
   123  			},
   124  			Expected: true,
   125  		},
   126  		{
   127  			Cert: testCert,
   128  			Constraint: CertificateConstraint{
   129  				CommonName:    "*",
   130  				DNSNames:      []string{"*"},
   131  				Emails:        []string{"*"},
   132  				Organizations: []string{"*"},
   133  				Roots:         []string{"*"},
   134  				URIs:          []string{"*"},
   135  			},
   136  			Expected: true,
   137  		},
   138  		{
   139  			Cert: testCert,
   140  			Constraint: CertificateConstraint{
   141  				CommonName:    "",
   142  				DNSNames:      []string{},
   143  				Emails:        []string{},
   144  				Organizations: []string{},
   145  				Roots:         []string{},
   146  				URIs:          []string{},
   147  			},
   148  			Expected: false,
   149  		},
   150  		{
   151  			Cert: testCert,
   152  			Constraint: CertificateConstraint{
   153  				CommonName:    "",
   154  				DNSNames:      []string{""},
   155  				Emails:        []string{""},
   156  				Organizations: []string{""},
   157  				Roots:         []string{""},
   158  				URIs:          []string{""},
   159  			},
   160  			Expected: false,
   161  		},
   162  		{
   163  			Cert: testCert,
   164  			Constraint: CertificateConstraint{
   165  				CommonName:    "",
   166  				DNSNames:      []string{"example.com"},
   167  				Emails:        []string{"example@example.com"},
   168  				Organizations: []string{"example"},
   169  				Roots:         []string{"example"},
   170  				URIs:          []string{"spiffe://example.com/step1"},
   171  			},
   172  			Expected: false,
   173  		},
   174  		{
   175  			Cert: testCert,
   176  			Constraint: CertificateConstraint{
   177  				CommonName:    "step1.example.com",
   178  				DNSNames:      []string{},
   179  				Emails:        []string{"example@example.com"},
   180  				Organizations: []string{"example"},
   181  				Roots:         []string{"example"},
   182  				URIs:          []string{"spiffe://example.com/step1"},
   183  			},
   184  			Expected: false,
   185  		},
   186  		{
   187  			Cert: testCert,
   188  			Constraint: CertificateConstraint{
   189  				CommonName:    "step1.example.com",
   190  				DNSNames:      []string{"example.com"},
   191  				Emails:        []string{},
   192  				Organizations: []string{"example"},
   193  				Roots:         []string{"example"},
   194  				URIs:          []string{"spiffe://example.com/step1"},
   195  			},
   196  			Expected: false,
   197  		},
   198  		{
   199  			Cert: testCert,
   200  			Constraint: CertificateConstraint{
   201  				CommonName:    "step1.example.com",
   202  				DNSNames:      []string{"example.com"},
   203  				Emails:        []string{"example@example.com"},
   204  				Organizations: []string{},
   205  				Roots:         []string{"example"},
   206  				URIs:          []string{"spiffe://example.com/step1"},
   207  			},
   208  			Expected: false,
   209  		},
   210  		{
   211  			Cert: testCert,
   212  			Constraint: CertificateConstraint{
   213  				CommonName:    "step1.example.com",
   214  				DNSNames:      []string{"example.com"},
   215  				Emails:        []string{"example@example.com"},
   216  				Organizations: []string{"example"},
   217  				Roots:         []string{},
   218  				URIs:          []string{"spiffe://example.com/step1"},
   219  			},
   220  			Expected: false,
   221  		},
   222  		{
   223  			Cert: testCert,
   224  			Constraint: CertificateConstraint{
   225  				CommonName:    "*",
   226  				DNSNames:      []string{"*"},
   227  				Emails:        []string{"*"},
   228  				Organizations: []string{"*"},
   229  				Roots:         []string{"example2"},
   230  				URIs:          []string{"*"},
   231  			},
   232  			Expected: false,
   233  		},
   234  		{
   235  			Cert: testCert,
   236  			Constraint: CertificateConstraint{
   237  				CommonName:    "step1.example.com",
   238  				DNSNames:      []string{"example.com"},
   239  				Emails:        []string{"example@example.com"},
   240  				Organizations: []string{"example"},
   241  				Roots:         []string{"example"},
   242  				URIs:          []string{},
   243  			},
   244  			Expected: false,
   245  		},
   246  		{
   247  			Cert: testCert,
   248  			Constraint: CertificateConstraint{
   249  				CommonName:    "*",
   250  				DNSNames:      []string{"*"},
   251  				Emails:        []string{"*"},
   252  				Organizations: []string{"*"},
   253  				Roots:         []string{"*"},
   254  				URIs:          []string{"spiffe://example.com/step1", "step1.example.com"},
   255  			},
   256  			Expected: false,
   257  		},
   258  		{
   259  			Cert: testCert,
   260  			Constraint: CertificateConstraint{
   261  				CommonName:    "*",
   262  				DNSNames:      []string{"*"},
   263  				Emails:        []string{"*"},
   264  				Organizations: []string{"*"},
   265  				Roots:         []string{"*"},
   266  				URIs:          []string{"spiffe://example.com/step2"},
   267  			},
   268  			Expected: false,
   269  		},
   270  	}
   271  
   272  	for _, c := range cases {
   273  		err := c.Constraint.Check(c.Cert, roots, rootCertPool, intermediateCertPool)
   274  		actual := err == nil
   275  		if actual != c.Expected {
   276  			t.Errorf("got %v when expected %v. Constraint: %+v, Errors: %s", actual, c.Expected, c.Constraint, err)
   277  		}
   278  	}
   279  }
   280  
   281  func createTestCert(template *x509.Certificate, publicKeyAlgorithm x509.PublicKeyAlgorithm, validity time.Duration) (*x509.Certificate, *x509.Certificate, *x509.Certificate, error) {
   282  	rootCertSubject := pkix.Name{
   283  		CommonName: "Root CA",
   284  	}
   285  	rootCertMaxPathLen := 1
   286  	rootCertValidity := 10 * 365 * 24 * time.Hour // 10 years
   287  	rootCertPublicKeyAlgorithm := x509.Ed25519
   288  	rootCertTemplate := &x509.Certificate{
   289  		Subject:    rootCertSubject,
   290  		MaxPathLen: rootCertMaxPathLen,
   291  	}
   292  	rootCert, _, rootKey, err := createSelfSignedCA(rootCertTemplate, rootCertPublicKeyAlgorithm, rootCertValidity)
   293  	if err != nil {
   294  		return nil, nil, nil, err
   295  	}
   296  
   297  	intermediateCertSubject := pkix.Name{
   298  		CommonName: "Intermediate CA",
   299  	}
   300  	intermediateCertMaxPathLen := 0
   301  	intermediateCertValidity := 10 * 365 * 24 * time.Hour
   302  	intermediateCertPublicKeyAlgorithm := x509.Ed25519
   303  	intermediateCertTemplate := &x509.Certificate{
   304  		Subject:    intermediateCertSubject,
   305  		MaxPathLen: intermediateCertMaxPathLen,
   306  	}
   307  	intermediateCert, _, intermediateKey, err := createCA(intermediateCertTemplate, rootCert, rootKey, intermediateCertPublicKeyAlgorithm, intermediateCertValidity)
   308  	if err != nil {
   309  		return nil, nil, nil, err
   310  	}
   311  
   312  	endEntityCert, _, _, err := createEndEntityCert(template, intermediateCert, intermediateKey, publicKeyAlgorithm, validity)
   313  	if err != nil {
   314  		return nil, nil, nil, err
   315  	}
   316  
   317  	return endEntityCert, intermediateCert, rootCert, nil
   318  }
   319  
   320  func createSelfSignedCA(template *x509.Certificate, publicKeyAlgorithm x509.PublicKeyAlgorithm, validity time.Duration) (*x509.Certificate, []byte, crypto.PrivateKey, error) {
   321  	if template.Subject.CommonName == "" {
   322  		return nil, nil, nil, fmt.Errorf("subject common name must be set")
   323  	}
   324  
   325  	if template.MaxPathLen <= 0 {
   326  		return nil, nil, nil, fmt.Errorf("maxPathLen must be set and greater than 0")
   327  	}
   328  
   329  	serialNumber, err := createSerialNumber()
   330  	if err != nil {
   331  		return nil, nil, nil, fmt.Errorf("failed to create certificate serial number: %w", err)
   332  	}
   333  
   334  	template.SerialNumber = serialNumber
   335  	template.NotBefore = time.Now()
   336  	template.NotAfter = time.Now().Add(validity)
   337  	template.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature
   338  	template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}
   339  	template.BasicConstraintsValid = true
   340  	template.IsCA = true
   341  	template.MaxPathLenZero = false
   342  
   343  	publicKey, privateKey, err := createKeyPair(publicKeyAlgorithm)
   344  	if err != nil {
   345  		return nil, nil, nil, fmt.Errorf("failed to create key pair: %w", err)
   346  	}
   347  
   348  	cert, certPEM, err := createCert(template, template, publicKey, privateKey)
   349  	if err != nil {
   350  		return nil, nil, nil, fmt.Errorf("failed to create cert: %w", err)
   351  	}
   352  
   353  	return cert, certPEM, privateKey, nil
   354  }
   355  
   356  func createCA(template, issuerCert *x509.Certificate, issuerPrivateKey crypto.PrivateKey, publicKeyAlgorithm x509.PublicKeyAlgorithm, validity time.Duration) (*x509.Certificate, []byte, crypto.PrivateKey, error) {
   357  	if template.Subject.CommonName == "" {
   358  		return nil, nil, nil, fmt.Errorf("subject common name must be set")
   359  	}
   360  
   361  	serialNumber, err := createSerialNumber()
   362  	if err != nil {
   363  		return nil, nil, nil, fmt.Errorf("failed to create certificate serial number: %w", err)
   364  	}
   365  
   366  	var maxPathLenZero bool
   367  	if template.MaxPathLen > 0 {
   368  		maxPathLenZero = false
   369  	} else {
   370  		maxPathLenZero = true
   371  	}
   372  
   373  	template.SerialNumber = serialNumber
   374  	template.NotBefore = time.Now()
   375  	template.NotAfter = time.Now().Add(validity)
   376  	template.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageDigitalSignature
   377  	template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}
   378  	template.BasicConstraintsValid = true
   379  	template.IsCA = true
   380  	template.MaxPathLenZero = maxPathLenZero
   381  
   382  	publicKey, privateKey, err := createKeyPair(publicKeyAlgorithm)
   383  	if err != nil {
   384  		return nil, nil, nil, fmt.Errorf("failed to create key pair: %w", err)
   385  	}
   386  
   387  	cert, certPEM, err := createCert(template, issuerCert, publicKey, issuerPrivateKey)
   388  	if err != nil {
   389  		return nil, nil, nil, fmt.Errorf("failed to create cert: %w", err)
   390  	}
   391  
   392  	return cert, certPEM, privateKey, nil
   393  }
   394  
   395  func createEndEntityCert(template, issuerCert *x509.Certificate, issuerPrivateKey crypto.PrivateKey, publicKeyAlgorithm x509.PublicKeyAlgorithm, validity time.Duration) (*x509.Certificate, []byte, crypto.PrivateKey, error) {
   396  	if template.Subject.CommonName == "" {
   397  		return nil, nil, nil, fmt.Errorf("subject common name must be set")
   398  	}
   399  
   400  	serialNumber, err := createSerialNumber()
   401  	if err != nil {
   402  		return nil, nil, nil, fmt.Errorf("failed to create certificate serial number: %w", err)
   403  	}
   404  
   405  	template.SerialNumber = serialNumber
   406  	template.NotBefore = time.Now()
   407  	template.NotAfter = time.Now().Add(validity)
   408  	template.KeyUsage = x509.KeyUsageDigitalSignature
   409  	template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}
   410  	template.IsCA = false
   411  	template.MaxPathLenZero = true
   412  
   413  	publicKey, privateKey, err := createKeyPair(publicKeyAlgorithm)
   414  	if err != nil {
   415  		return nil, nil, nil, fmt.Errorf("failed to create key pair: %w", err)
   416  	}
   417  
   418  	cert, certPEM, err := createCert(template, issuerCert, publicKey, issuerPrivateKey)
   419  	if err != nil {
   420  		return nil, nil, nil, fmt.Errorf("failed to create cert: %w", err)
   421  	}
   422  
   423  	return cert, certPEM, privateKey, nil
   424  }
   425  
   426  func createKeyPair(publicKeyAlgorithm x509.PublicKeyAlgorithm) (crypto.PublicKey, crypto.PrivateKey, error) {
   427  	var publicKey crypto.PublicKey
   428  	var privateKey crypto.PrivateKey
   429  
   430  	switch publicKeyAlgorithm {
   431  
   432  	case x509.RSA:
   433  		rsaPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048)
   434  		if err != nil {
   435  			return nil, nil, fmt.Errorf("failed to generate rsa private key: %w", err)
   436  		}
   437  		publicKey = &rsaPrivateKey.PublicKey
   438  		privateKey = rsaPrivateKey
   439  
   440  	case x509.Ed25519:
   441  		ed25519PublicKey, ed25519PrivateKey, err := ed25519.GenerateKey(rand.Reader)
   442  		if err != nil {
   443  			return nil, nil, fmt.Errorf("failed to generate ed25519 key pair: %w", err)
   444  		}
   445  		publicKey = ed25519PublicKey
   446  		privateKey = ed25519PrivateKey
   447  
   448  	default:
   449  		return nil, nil, fmt.Errorf("unsupported public key algorithm")
   450  	}
   451  
   452  	return publicKey, privateKey, nil
   453  }
   454  
   455  func createCert(template, issuer *x509.Certificate, subjectPublicKey crypto.PublicKey, issuerPrivateKey crypto.PrivateKey) (*x509.Certificate, []byte, error) {
   456  	certBytes, err := x509.CreateCertificate(rand.Reader, template, issuer, subjectPublicKey, issuerPrivateKey)
   457  	if err != nil {
   458  		return nil, nil, fmt.Errorf("failed to create certificate: %w", err)
   459  	}
   460  
   461  	cert, err := x509.ParseCertificate(certBytes)
   462  	if err != nil {
   463  		return nil, nil, fmt.Errorf("failed to parse certificate: %w", err)
   464  	}
   465  
   466  	b := pem.Block{Type: "CERTIFICATE", Bytes: certBytes}
   467  	certPEM := pem.EncodeToMemory(&b)
   468  
   469  	return cert, certPEM, err
   470  }
   471  
   472  func createSerialNumber() (*big.Int, error) {
   473  	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 256)
   474  	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
   475  	if err != nil {
   476  		return nil, fmt.Errorf("failed to generate serial number: %w", err)
   477  	}
   478  	return serialNumber, nil
   479  }
   480  

View as plain text