27bb03bbc0
* adding copyright header * fix fmt and a test
280 lines
8.5 KiB
Go
280 lines
8.5 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package pki
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/vault/sdk/framework"
|
|
"github.com/hashicorp/vault/sdk/helper/certutil"
|
|
"github.com/hashicorp/vault/sdk/logical"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
var ctx = context.Background()
|
|
|
|
func Test_ConfigsRoundTrip(t *testing.T) {
|
|
t.Parallel()
|
|
b, s := CreateBackendWithStorage(t)
|
|
sc := b.makeStorageContext(ctx, s)
|
|
|
|
// Create an empty key, issuer for testing.
|
|
key := keyEntry{ID: genKeyId()}
|
|
err := sc.writeKey(key)
|
|
require.NoError(t, err)
|
|
issuer := &issuerEntry{ID: genIssuerId()}
|
|
err = sc.writeIssuer(issuer)
|
|
require.NoError(t, err)
|
|
|
|
// Verify we handle nothing stored properly
|
|
keyConfigEmpty, err := sc.getKeysConfig()
|
|
require.NoError(t, err)
|
|
require.Equal(t, &keyConfigEntry{}, keyConfigEmpty)
|
|
|
|
issuerConfigEmpty, err := sc.getIssuersConfig()
|
|
require.NoError(t, err)
|
|
require.Equal(t, &issuerConfigEntry{}, issuerConfigEmpty)
|
|
|
|
// Now attempt to store and reload properly
|
|
origKeyConfig := &keyConfigEntry{
|
|
DefaultKeyId: key.ID,
|
|
}
|
|
origIssuerConfig := &issuerConfigEntry{
|
|
DefaultIssuerId: issuer.ID,
|
|
}
|
|
|
|
err = sc.setKeysConfig(origKeyConfig)
|
|
require.NoError(t, err)
|
|
err = sc.setIssuersConfig(origIssuerConfig)
|
|
require.NoError(t, err)
|
|
|
|
keyConfig, err := sc.getKeysConfig()
|
|
require.NoError(t, err)
|
|
require.Equal(t, origKeyConfig, keyConfig)
|
|
|
|
issuerConfig, err := sc.getIssuersConfig()
|
|
require.NoError(t, err)
|
|
require.Equal(t, origIssuerConfig.DefaultIssuerId, issuerConfig.DefaultIssuerId)
|
|
}
|
|
|
|
func Test_IssuerRoundTrip(t *testing.T) {
|
|
t.Parallel()
|
|
b, s := CreateBackendWithStorage(t)
|
|
sc := b.makeStorageContext(ctx, s)
|
|
issuer1, key1 := genIssuerAndKey(t, b, s)
|
|
issuer2, key2 := genIssuerAndKey(t, b, s)
|
|
|
|
// We get an error when issuer id not found
|
|
_, err := sc.fetchIssuerById(issuer1.ID)
|
|
require.Error(t, err)
|
|
|
|
// We get an error when key id not found
|
|
_, err = sc.fetchKeyById(key1.ID)
|
|
require.Error(t, err)
|
|
|
|
// Now write out our issuers and keys
|
|
err = sc.writeKey(key1)
|
|
require.NoError(t, err)
|
|
err = sc.writeIssuer(&issuer1)
|
|
require.NoError(t, err)
|
|
|
|
err = sc.writeKey(key2)
|
|
require.NoError(t, err)
|
|
err = sc.writeIssuer(&issuer2)
|
|
require.NoError(t, err)
|
|
|
|
fetchedKey1, err := sc.fetchKeyById(key1.ID)
|
|
require.NoError(t, err)
|
|
|
|
fetchedIssuer1, err := sc.fetchIssuerById(issuer1.ID)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, &key1, fetchedKey1)
|
|
require.Equal(t, &issuer1, fetchedIssuer1)
|
|
|
|
keys, err := sc.listKeys()
|
|
require.NoError(t, err)
|
|
|
|
require.ElementsMatch(t, []keyID{key1.ID, key2.ID}, keys)
|
|
|
|
issuers, err := sc.listIssuers()
|
|
require.NoError(t, err)
|
|
|
|
require.ElementsMatch(t, []issuerID{issuer1.ID, issuer2.ID}, issuers)
|
|
}
|
|
|
|
func Test_KeysIssuerImport(t *testing.T) {
|
|
t.Parallel()
|
|
b, s := CreateBackendWithStorage(t)
|
|
sc := b.makeStorageContext(ctx, s)
|
|
|
|
issuer1, key1 := genIssuerAndKey(t, b, s)
|
|
issuer2, key2 := genIssuerAndKey(t, b, s)
|
|
|
|
// Key 1 before Issuer 1; Issuer 2 before Key 2.
|
|
// Remove KeyIDs from non-written entities before beginning.
|
|
key1.ID = ""
|
|
issuer1.ID = ""
|
|
issuer1.KeyID = ""
|
|
|
|
key1Ref1, existing, err := sc.importKey(key1.PrivateKey, "key1", key1.PrivateKeyType)
|
|
require.NoError(t, err)
|
|
require.False(t, existing)
|
|
require.Equal(t, strings.TrimSpace(key1.PrivateKey), strings.TrimSpace(key1Ref1.PrivateKey))
|
|
|
|
// Make sure if we attempt to re-import the same private key, no import/updates occur.
|
|
// So the existing flag should be set to true, and we do not update the existing Name field.
|
|
key1Ref2, existing, err := sc.importKey(key1.PrivateKey, "ignore-me", key1.PrivateKeyType)
|
|
require.NoError(t, err)
|
|
require.True(t, existing)
|
|
require.Equal(t, key1.PrivateKey, key1Ref1.PrivateKey)
|
|
require.Equal(t, key1Ref1.ID, key1Ref2.ID)
|
|
require.Equal(t, key1Ref1.Name, key1Ref2.Name)
|
|
|
|
issuer1Ref1, existing, err := sc.importIssuer(issuer1.Certificate, "issuer1")
|
|
require.NoError(t, err)
|
|
require.False(t, existing)
|
|
require.Equal(t, strings.TrimSpace(issuer1.Certificate), strings.TrimSpace(issuer1Ref1.Certificate))
|
|
require.Equal(t, key1Ref1.ID, issuer1Ref1.KeyID)
|
|
require.Equal(t, "issuer1", issuer1Ref1.Name)
|
|
|
|
// Make sure if we attempt to re-import the same issuer, no import/updates occur.
|
|
// So the existing flag should be set to true, and we do not update the existing Name field.
|
|
issuer1Ref2, existing, err := sc.importIssuer(issuer1.Certificate, "ignore-me")
|
|
require.NoError(t, err)
|
|
require.True(t, existing)
|
|
require.Equal(t, strings.TrimSpace(issuer1.Certificate), strings.TrimSpace(issuer1Ref1.Certificate))
|
|
require.Equal(t, issuer1Ref1.ID, issuer1Ref2.ID)
|
|
require.Equal(t, key1Ref1.ID, issuer1Ref2.KeyID)
|
|
require.Equal(t, issuer1Ref1.Name, issuer1Ref2.Name)
|
|
|
|
err = sc.writeIssuer(&issuer2)
|
|
require.NoError(t, err)
|
|
|
|
err = sc.writeKey(key2)
|
|
require.NoError(t, err)
|
|
|
|
// Same double import tests as above, but make sure if the previous was created through writeIssuer not importIssuer.
|
|
issuer2Ref, existing, err := sc.importIssuer(issuer2.Certificate, "ignore-me")
|
|
require.NoError(t, err)
|
|
require.True(t, existing)
|
|
require.Equal(t, strings.TrimSpace(issuer2.Certificate), strings.TrimSpace(issuer2Ref.Certificate))
|
|
require.Equal(t, issuer2.ID, issuer2Ref.ID)
|
|
require.Equal(t, "", issuer2Ref.Name)
|
|
require.Equal(t, issuer2.KeyID, issuer2Ref.KeyID)
|
|
|
|
// Same double import tests as above, but make sure if the previous was created through writeKey not importKey.
|
|
key2Ref, existing, err := sc.importKey(key2.PrivateKey, "ignore-me", key2.PrivateKeyType)
|
|
require.NoError(t, err)
|
|
require.True(t, existing)
|
|
require.Equal(t, key2.PrivateKey, key2Ref.PrivateKey)
|
|
require.Equal(t, key2.ID, key2Ref.ID)
|
|
require.Equal(t, "", key2Ref.Name)
|
|
}
|
|
|
|
func Test_IssuerUpgrade(t *testing.T) {
|
|
t.Parallel()
|
|
b, s := CreateBackendWithStorage(t)
|
|
sc := b.makeStorageContext(ctx, s)
|
|
|
|
// Make sure that we add OCSP signing to v0 issuers if CRLSigning is enabled
|
|
issuer, _ := genIssuerAndKey(t, b, s)
|
|
issuer.Version = 0
|
|
issuer.Usage.ToggleUsage(OCSPSigningUsage)
|
|
|
|
err := sc.writeIssuer(&issuer)
|
|
require.NoError(t, err, "failed writing out issuer")
|
|
|
|
newIssuer, err := sc.fetchIssuerById(issuer.ID)
|
|
require.NoError(t, err, "failed fetching issuer")
|
|
|
|
require.Equal(t, uint(1), newIssuer.Version)
|
|
require.True(t, newIssuer.Usage.HasUsage(OCSPSigningUsage))
|
|
|
|
// If CRLSigning is not present on a v0, we should not have OCSP signing after upgrade.
|
|
issuer, _ = genIssuerAndKey(t, b, s)
|
|
issuer.Version = 0
|
|
issuer.Usage.ToggleUsage(OCSPSigningUsage)
|
|
issuer.Usage.ToggleUsage(CRLSigningUsage)
|
|
|
|
err = sc.writeIssuer(&issuer)
|
|
require.NoError(t, err, "failed writing out issuer")
|
|
|
|
newIssuer, err = sc.fetchIssuerById(issuer.ID)
|
|
require.NoError(t, err, "failed fetching issuer")
|
|
|
|
require.Equal(t, uint(1), newIssuer.Version)
|
|
require.False(t, newIssuer.Usage.HasUsage(OCSPSigningUsage))
|
|
}
|
|
|
|
func genIssuerAndKey(t *testing.T, b *backend, s logical.Storage) (issuerEntry, keyEntry) {
|
|
certBundle := genCertBundle(t, b, s)
|
|
|
|
keyId := genKeyId()
|
|
|
|
pkiKey := keyEntry{
|
|
ID: keyId,
|
|
PrivateKeyType: certBundle.PrivateKeyType,
|
|
PrivateKey: strings.TrimSpace(certBundle.PrivateKey) + "\n",
|
|
}
|
|
|
|
issuerId := genIssuerId()
|
|
|
|
pkiIssuer := issuerEntry{
|
|
ID: issuerId,
|
|
KeyID: keyId,
|
|
Certificate: strings.TrimSpace(certBundle.Certificate) + "\n",
|
|
CAChain: certBundle.CAChain,
|
|
SerialNumber: certBundle.SerialNumber,
|
|
Usage: AllIssuerUsages,
|
|
Version: latestIssuerVersion,
|
|
}
|
|
|
|
return pkiIssuer, pkiKey
|
|
}
|
|
|
|
func genCertBundle(t *testing.T, b *backend, s logical.Storage) *certutil.CertBundle {
|
|
// Pretty gross just to generate a cert bundle, but
|
|
fields := addCACommonFields(map[string]*framework.FieldSchema{})
|
|
fields = addCAKeyGenerationFields(fields)
|
|
fields = addCAIssueFields(fields)
|
|
apiData := &framework.FieldData{
|
|
Schema: fields,
|
|
Raw: map[string]interface{}{
|
|
"exported": "internal",
|
|
"cn": "example.com",
|
|
"ttl": 3600,
|
|
},
|
|
}
|
|
sc := b.makeStorageContext(ctx, s)
|
|
_, _, role, respErr := getGenerationParams(sc, apiData)
|
|
require.Nil(t, respErr)
|
|
|
|
input := &inputBundle{
|
|
req: &logical.Request{
|
|
Operation: logical.UpdateOperation,
|
|
Path: "issue/testrole",
|
|
Storage: s,
|
|
},
|
|
apiData: apiData,
|
|
role: role,
|
|
}
|
|
parsedCertBundle, _, err := generateCert(sc, input, nil, true, b.GetRandomReader())
|
|
|
|
require.NoError(t, err)
|
|
certBundle, err := parsedCertBundle.ToCertBundle()
|
|
require.NoError(t, err)
|
|
return certBundle
|
|
}
|
|
|
|
func writeLegacyBundle(t *testing.T, b *backend, s logical.Storage, bundle *certutil.CertBundle) {
|
|
entry, err := logical.StorageEntryJSON(legacyCertBundlePath, bundle)
|
|
require.NoError(t, err)
|
|
|
|
err = s.Put(context.Background(), entry)
|
|
require.NoError(t, err)
|
|
}
|