...

Source file src/github.com/sigstore/rekor/pkg/pki/x509/x509_test.go

Documentation: github.com/sigstore/rekor/pkg/pki/x509

     1  //
     2  // Copyright 2021 The Sigstore Authors.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package x509
    17  
    18  import (
    19  	"bytes"
    20  	"crypto"
    21  	"crypto/ecdsa"
    22  	"crypto/ed25519"
    23  	"crypto/rsa"
    24  	"crypto/sha256"
    25  	"crypto/x509"
    26  	"encoding/hex"
    27  	"net"
    28  	"net/url"
    29  	"reflect"
    30  	"strings"
    31  	"testing"
    32  
    33  	"github.com/sigstore/rekor/pkg/pki/identity"
    34  	"github.com/sigstore/rekor/pkg/pki/x509/testutils"
    35  	"github.com/sigstore/sigstore/pkg/cryptoutils"
    36  	"github.com/sigstore/sigstore/pkg/signature"
    37  )
    38  
    39  // Generated with:
    40  // openssl genrsa -out myprivate.pem 512
    41  // openssl pkcs8 -topk8 -in myprivate.pem  -nocrypt'
    42  const pkcs1v15Priv = `-----BEGIN PRIVATE KEY-----
    43  MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAoLEL57Kd5w8b5LCl
    44  SM+5mJbVYj4GoFXP/Gynfk6mDj7aANYWAkU74xkjz0BX2Nq0IT9DyxWI8aXZ8B6R
    45  YtbsPwIDAQABAkA2WgwTz5eXKsYdgR421YQKN6JvO1mUa9IQqFOy5jlGgbR+W5HG
    46  JfQVJKhCGMYYmByHgR0QDk/6gvJjhuszTHuJAiEA0siY/vE20zC1UHpPgDXXVSNN
    47  dKtM6YKBKSo47oTKQHsCIQDDKZgal50Cd3W+lOWpNO23QGZgBhJrJ70TpcPWGEsS
    48  DQIhAIDIMLnq1G1Z4B2IbRRPUP3icMtscbRlmNZ2xovsM8oLAiBluZh+w+gjEQFe
    49  hV3wBJajnf2+r2uKTvxO8WhSf/chQQIhAKzYjX2chfvPN6hRqeGeoPpRLXS8cdxC
    50  A4hZJRvZgkO3
    51  -----END PRIVATE KEY-----
    52  `
    53  
    54  // Extracted from above with:
    55  // openssl rsa -in myprivate.pem -pubout
    56  const pkcs1v15Pub = `-----BEGIN PUBLIC KEY-----
    57  MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKCxC+eynecPG+SwpUjPuZiW1WI+BqBV
    58  z/xsp35Opg4+2gDWFgJFO+MZI89AV9jatCE/Q8sViPGl2fAekWLW7D8CAwEAAQ==
    59  -----END PUBLIC KEY-----
    60  `
    61  
    62  // Generated with:
    63  // openssl ecparam -genkey -name prime256v1 > ec_private.pem
    64  // openssl pkcs8 -topk8 -in ec_private.pem  -nocrypt
    65  const priv = `-----BEGIN PRIVATE KEY-----
    66  MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgmrLtCpBdXgXLUr7o
    67  nSUPfo3oXMjmvuwTOjpTulIBKlKhRANCAATH6KSpTFe6uXFmW1qNEFXaO7fWPfZt
    68  pPZrHZ1cFykidZoURKoYXfkohJ+U/USYy8Sd8b4DMd5xDRZCnlDM0h37
    69  -----END PRIVATE KEY-----
    70  `
    71  
    72  // Extracted from above with:
    73  // openssl ec -in ec_private.pem -pubout
    74  const pubStr = `-----BEGIN PUBLIC KEY-----
    75  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEx+ikqUxXurlxZltajRBV2ju31j32
    76  baT2ax2dXBcpInWaFESqGF35KISflP1EmMvEnfG+AzHecQ0WQp5QzNId+w==
    77  -----END PUBLIC KEY-----
    78  `
    79  
    80  // Generated with:
    81  // openssl genpkey -algorithm ED25519 -out edprivate.pem
    82  const ed25519Priv = `-----BEGIN PRIVATE KEY-----
    83  MC4CAQAwBQYDK2VwBCIEIKjlXfR/VFvO9qM9+CG2qbuSM54k8ciKWHhgNwKTgqpG
    84  -----END PRIVATE KEY-----
    85  `
    86  
    87  // Extracted from above with:
    88  // openssl pkey -in edprivate.pem -pubout
    89  const ed25519Pub = `-----BEGIN PUBLIC KEY-----
    90  MCowBQYDK2VwAyEAizWek2gKgMM+bad4rVJ5nc9NsbNOba0A0BNfzOgklRs=
    91  -----END PUBLIC KEY-----
    92  `
    93  
    94  const pubWithTrailingNewLine = `-----BEGIN PUBLIC KEY-----
    95  MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEx+ikqUxXurlxZltajRBV2ju31j32
    96  baT2ax2dXBcpInWaFESqGF35KISflP1EmMvEnfG+AzHecQ0WQp5QzNId+w==
    97  -----END PUBLIC KEY-----
    98  
    99  `
   100  
   101  func signData(t *testing.T, b []byte, pkey string) []byte {
   102  
   103  	priv, err := cryptoutils.UnmarshalPEMToPrivateKey([]byte(pkey), cryptoutils.SkipPassword)
   104  	if err != nil {
   105  		t.Fatal(err)
   106  	}
   107  	signer, err := signature.LoadSigner(priv, crypto.SHA256)
   108  	if err != nil {
   109  		t.Fatal(err)
   110  	}
   111  
   112  	signature, err := signer.SignMessage(bytes.NewReader(b))
   113  	if err != nil {
   114  		t.Fatal(err)
   115  	}
   116  	return signature
   117  }
   118  
   119  func TestSignature_Verify(t *testing.T) {
   120  	tests := []struct {
   121  		name string
   122  		priv string
   123  		pub  string
   124  	}{
   125  		{
   126  			name: "rsa",
   127  			priv: pkcs1v15Priv,
   128  			pub:  pkcs1v15Pub,
   129  		},
   130  		{
   131  			name: "ec",
   132  			priv: priv,
   133  			pub:  pubStr,
   134  		},
   135  		{
   136  			name: "ed25519",
   137  			priv: ed25519Priv,
   138  			pub:  ed25519Pub,
   139  		},
   140  	}
   141  	for _, tt := range tests {
   142  		t.Run(tt.name, func(t *testing.T) {
   143  			data := []byte("hey! this is my test data")
   144  			sigBytes := signData(t, data, tt.priv)
   145  			s, err := NewSignature(bytes.NewReader(sigBytes))
   146  			if err != nil {
   147  				t.Fatal(err)
   148  			}
   149  
   150  			pub, err := NewPublicKey(strings.NewReader(tt.pub))
   151  			if err != nil {
   152  				t.Fatal(err)
   153  			}
   154  
   155  			if err := s.Verify(bytes.NewReader(data), pub); err != nil {
   156  				t.Errorf("Signature.Verify() error = %v", err)
   157  			}
   158  
   159  			// Now try with the canonical value
   160  			cb, err := s.CanonicalValue()
   161  			if err != nil {
   162  				t.Error(err)
   163  			}
   164  			canonicalSig, err := NewSignature(bytes.NewReader(cb))
   165  			if err != nil {
   166  				t.Error(err)
   167  			}
   168  			if err := canonicalSig.Verify(bytes.NewReader(data), pub); err != nil {
   169  				t.Errorf("Signature.Verify() error = %v", err)
   170  			}
   171  
   172  			pubKey, _ := cryptoutils.UnmarshalPEMToPublicKey([]byte(tt.pub))
   173  			derKey, _ := cryptoutils.MarshalPublicKeyToDER(pubKey)
   174  			digest := sha256.Sum256(derKey)
   175  			expectedID := identity.Identity{Crypto: pubKey, Raw: derKey, Fingerprint: hex.EncodeToString(digest[:])}
   176  			ids, err := pub.Identities()
   177  			if err != nil {
   178  				t.Fatal(err)
   179  			}
   180  			if len(ids) != 1 {
   181  				t.Errorf("%v: too many identities, expected 1, got %v", tt.name, len(ids))
   182  			}
   183  			switch v := ids[0].Crypto.(type) {
   184  			case *rsa.PublicKey:
   185  				if tt.name != "rsa" {
   186  					t.Fatalf("unexpected key, expected RSA, got %v", reflect.TypeOf(v))
   187  				}
   188  			case *ecdsa.PublicKey:
   189  				if tt.name != "ec" {
   190  					t.Fatalf("unexpected key, expected RSA, got %v", reflect.TypeOf(v))
   191  				}
   192  			case ed25519.PublicKey:
   193  				if tt.name != "ed25519" {
   194  					t.Fatalf("unexpected key, expected RSA, got %v", reflect.TypeOf(v))
   195  				}
   196  			default:
   197  				t.Fatalf("unexpected key type, got %v", reflect.TypeOf(v))
   198  			}
   199  			if err := cryptoutils.EqualKeys(expectedID.Crypto, ids[0].Crypto); err != nil {
   200  				t.Errorf("%v: public keys did not match: %v", tt.name, err)
   201  			}
   202  			if !reflect.DeepEqual(expectedID.Raw, ids[0].Raw) {
   203  				t.Errorf("%v: raw identities did not match, expected %v, got %v", tt.name, expectedID.Raw, ids[0].Raw)
   204  			}
   205  			if expectedID.Fingerprint != ids[0].Fingerprint {
   206  				t.Errorf("%v: fingerprints did not match, expected %v, got %v", tt.name, expectedID.Fingerprint, ids[0].Fingerprint)
   207  			}
   208  		})
   209  	}
   210  }
   211  
   212  func TestSignature_VerifyFail(t *testing.T) {
   213  	tests := []struct {
   214  		name string
   215  		priv string
   216  		pub  string
   217  	}{
   218  		{
   219  			name: "rsa",
   220  			priv: pkcs1v15Priv,
   221  			pub:  pkcs1v15Pub,
   222  		},
   223  		{
   224  			name: "ec",
   225  			priv: priv,
   226  			pub:  pubStr,
   227  		},
   228  		{
   229  			name: "ed25519",
   230  			priv: ed25519Priv,
   231  			pub:  ed25519Pub,
   232  		},
   233  	}
   234  	for _, tt := range tests {
   235  		t.Run(tt.name, func(t *testing.T) {
   236  			// Make some fake data, and tamper with the signature
   237  			data := []byte("hey! this is my test data")
   238  			sigBytes := signData(t, data, tt.priv)
   239  			sigBytes[0]--
   240  			s, err := NewSignature(bytes.NewReader(sigBytes))
   241  			if err != nil {
   242  				t.Fatal(err)
   243  			}
   244  
   245  			pub, err := NewPublicKey(strings.NewReader(tt.pub))
   246  			if err != nil {
   247  				t.Fatal(err)
   248  			}
   249  
   250  			if err := s.Verify(bytes.NewReader(data), pub); err == nil {
   251  				t.Error("Signature.Verify() expected error!")
   252  			}
   253  		})
   254  	}
   255  }
   256  
   257  func TestPublicKeyWithCertChain(t *testing.T) {
   258  	rootCert, rootKey, _ := testutils.GenerateRootCa()
   259  	subCert, subKey, _ := testutils.GenerateSubordinateCa(rootCert, rootKey)
   260  	subjectURL, _ := url.Parse("https://github.com/slsa-framework/slsa-github-generator/.github/workflows/builder_go_slsa3.yml@refs/tags/v1.1.1")
   261  	leafCert, leafKey, _ := testutils.GenerateLeafCertWithSubjectAlternateNames(
   262  		[]string{"example.com"}, []string{"subject@example.com"}, []net.IP{{1, 1, 1, 1}}, []*url.URL{subjectURL}, "oidc-issuer", subCert, subKey)
   263  
   264  	pemCertChain, err := cryptoutils.MarshalCertificatesToPEM([]*x509.Certificate{leafCert, subCert, rootCert})
   265  	if err != nil {
   266  		t.Fatalf("unexpected error marshalling certificate chain: %v", err)
   267  	}
   268  
   269  	pub, err := NewPublicKey(bytes.NewReader(pemCertChain))
   270  	if err != nil {
   271  		t.Fatalf("unexpected error generating public key: %v", err)
   272  	}
   273  	if pub.certs == nil || !pub.certs[0].Equal(leafCert) || !pub.certs[1].Equal(subCert) || !pub.certs[2].Equal(rootCert) {
   274  		t.Fatal("expected certificate chain to match provided certificate chain")
   275  	}
   276  
   277  	if !pub.CryptoPubKey().(*ecdsa.PublicKey).Equal(leafKey.Public()) {
   278  		t.Fatal("expected public keys to match")
   279  	}
   280  
   281  	if !reflect.DeepEqual(pub.EmailAddresses(), leafCert.EmailAddresses) {
   282  		t.Fatalf("expected matching subjects, expected %v, got %v", leafCert.EmailAddresses, pub.EmailAddresses())
   283  	}
   284  
   285  	var expectedSubjects []string
   286  	expectedSubjects = append(expectedSubjects, leafCert.DNSNames...)
   287  	expectedSubjects = append(expectedSubjects, leafCert.EmailAddresses...)
   288  	expectedSubjects = append(expectedSubjects, leafCert.IPAddresses[0].String())
   289  	expectedSubjects = append(expectedSubjects, leafCert.URIs[0].String())
   290  	if !reflect.DeepEqual(pub.Subjects(), expectedSubjects) {
   291  		t.Fatalf("expected matching subjects, expected %v, got %v", expectedSubjects, pub.Subjects())
   292  	}
   293  
   294  	digest := sha256.Sum256(leafCert.Raw)
   295  	expectedID := identity.Identity{Crypto: leafCert, Raw: leafCert.Raw, Fingerprint: hex.EncodeToString((digest[:]))}
   296  	ids, err := pub.Identities()
   297  	if err != nil {
   298  		t.Fatal(err)
   299  	}
   300  	if len(ids) != 1 {
   301  		t.Errorf("too many identities, expected 1, got %v", len(ids))
   302  	}
   303  	if !ids[0].Crypto.(*x509.Certificate).Equal(expectedID.Crypto.(*x509.Certificate)) {
   304  		t.Errorf("certificates did not match")
   305  	}
   306  	if !reflect.DeepEqual(expectedID.Raw, ids[0].Raw) {
   307  		t.Errorf("raw identities did not match, expected %v, got %v", expectedID.Raw, ids[0].Raw)
   308  	}
   309  	if expectedID.Fingerprint != ids[0].Fingerprint {
   310  		t.Errorf("fingerprints did not match, expected %v, got %v", expectedID.Fingerprint, ids[0].Fingerprint)
   311  	}
   312  
   313  	canonicalValue, err := pub.CanonicalValue()
   314  	if err != nil {
   315  		t.Fatalf("unexpected error fetching canonical value: %v", err)
   316  	}
   317  	if !reflect.DeepEqual(canonicalValue, pemCertChain) {
   318  		t.Fatalf("expected canonical value %v, got %v", pemCertChain, canonicalValue)
   319  	}
   320  
   321  	// Generate signature to verify
   322  	data := []byte("test")
   323  	signer, err := signature.LoadSigner(leafKey, crypto.SHA256)
   324  	if err != nil {
   325  		t.Fatal(err)
   326  	}
   327  	sigBytes, err := signer.SignMessage(bytes.NewReader(data))
   328  	if err != nil {
   329  		t.Fatal(err)
   330  	}
   331  	s, err := NewSignature(bytes.NewReader(sigBytes))
   332  	if err != nil {
   333  		t.Fatalf("unexpected error generating signature: %v", err)
   334  	}
   335  	err = s.Verify(bytes.NewReader(data), pub)
   336  	if err != nil {
   337  		t.Fatalf("unexpected error verifying signature, %v", err)
   338  	}
   339  
   340  	// Verify works with expired certificate
   341  	leafCert, leafKey, _ = testutils.GenerateExpiredLeafCert("subject@example.com", "oidc-issuer", subCert, subKey)
   342  	pemCertChain, _ = cryptoutils.MarshalCertificatesToPEM([]*x509.Certificate{leafCert, subCert, rootCert})
   343  	pub, _ = NewPublicKey(bytes.NewReader(pemCertChain))
   344  	signer, _ = signature.LoadSigner(leafKey, crypto.SHA256)
   345  	sigBytes, _ = signer.SignMessage(bytes.NewReader(data))
   346  	s, _ = NewSignature(bytes.NewReader(sigBytes))
   347  	err = s.Verify(bytes.NewReader(data), pub)
   348  	if err != nil {
   349  		t.Fatalf("unexpected error verifying signature with expired certificate: %v", err)
   350  	}
   351  
   352  	// Verify error with invalid chain
   353  	pemCertChain, _ = cryptoutils.MarshalCertificatesToPEM([]*x509.Certificate{leafCert, rootCert})
   354  	pub, _ = NewPublicKey(bytes.NewReader(pemCertChain))
   355  	signer, _ = signature.LoadSigner(leafKey, crypto.SHA256)
   356  	sigBytes, _ = signer.SignMessage(bytes.NewReader(data))
   357  	s, _ = NewSignature(bytes.NewReader(sigBytes))
   358  	err = s.Verify(bytes.NewReader(data), pub)
   359  	if err == nil || !strings.Contains(err.Error(), "x509: certificate signed by unknown authority") {
   360  		t.Fatalf("expected error verifying signature, got %v", err)
   361  	}
   362  
   363  	// Verify works with chain without intermediate
   364  	leafCert, leafKey, _ = testutils.GenerateLeafCert("subject@example.com", "oidc-issuer", nil, rootCert, rootKey)
   365  	pemCertChain, _ = cryptoutils.MarshalCertificatesToPEM([]*x509.Certificate{leafCert, rootCert})
   366  	pub, _ = NewPublicKey(bytes.NewReader(pemCertChain))
   367  	signer, _ = signature.LoadSigner(leafKey, crypto.SHA256)
   368  	sigBytes, _ = signer.SignMessage(bytes.NewReader(data))
   369  	s, _ = NewSignature(bytes.NewReader(sigBytes))
   370  	err = s.Verify(bytes.NewReader(data), pub)
   371  	if err != nil {
   372  		t.Fatalf("unexpected error verifying signature, %v", err)
   373  	}
   374  
   375  	// Verify error with long chain
   376  	chain := []*x509.Certificate{}
   377  	for i := 0; i < 11; i++ {
   378  		chain = append(chain, leafCert)
   379  	}
   380  	pemCertChain, _ = cryptoutils.MarshalCertificatesToPEM(chain)
   381  	_, err = NewPublicKey(bytes.NewReader(pemCertChain))
   382  	if err == nil || !strings.Contains(err.Error(), "too many certificates specified in PEM block") {
   383  		t.Fatalf("expected error with long certificate chain, got %v", err)
   384  	}
   385  
   386  	// Verify public key with extra trailing newline is parsed OK
   387  	key, err := NewPublicKey(strings.NewReader(pubWithTrailingNewLine))
   388  	if err != nil {
   389  		t.Fatalf("unexpected error parsing public key with extra trailing newline: %v", err)
   390  	}
   391  	canonicalKeyBytes, err := key.CanonicalValue()
   392  	if err != nil {
   393  		t.Fatalf("unexpected error canonicalizing public key with extra trailing newline: %v", err)
   394  	}
   395  
   396  	if !bytes.Equal([]byte(pubStr), canonicalKeyBytes) {
   397  		t.Fatalf("expected canonical value to match original without extra trailing new line")
   398  	}
   399  }
   400  

View as plain text