Add support for storing or sending attestation data to linkedca

This commit is contained in:
Mariano Cano 2022-10-06 12:22:19 -07:00
parent a258ea3e2d
commit bd1938b0da
No known key found for this signature in database
6 changed files with 139 additions and 49 deletions

View file

@ -278,6 +278,7 @@ func (c *linkedCaClient) StoreCertificateChain(p provisioner.Interface, fullchai
PemCertificate: serializeCertificateChain(fullchain[0]),
PemCertificateChain: serializeCertificateChain(fullchain[1:]...),
Provisioner: createProvisionerIdentity(p),
AttestationData: createAttestationData(p),
RaProvisioner: raProvisioner,
EndpointId: endpointID,
})
@ -395,26 +396,34 @@ func createProvisionerIdentity(p provisioner.Interface) *linkedca.ProvisionerIde
}
}
type raProvisioner interface {
RAInfo() *provisioner.RAInfo
}
func createRegistrationAuthorityProvisioner(p provisioner.Interface) (*linkedca.RegistrationAuthorityProvisioner, string) {
if rap, ok := p.(raProvisioner); ok {
info := rap.RAInfo()
typ := linkedca.Provisioner_Type_value[strings.ToUpper(info.ProvisionerType)]
return &linkedca.RegistrationAuthorityProvisioner{
AuthorityId: info.AuthorityID,
Provisioner: &linkedca.ProvisionerIdentity{
Id: info.ProvisionerID,
Type: linkedca.Provisioner_Type(typ),
Name: info.ProvisionerName,
},
}, info.EndpointID
if info := rap.RAInfo(); info != nil {
typ := linkedca.Provisioner_Type_value[strings.ToUpper(info.ProvisionerType)]
return &linkedca.RegistrationAuthorityProvisioner{
AuthorityId: info.AuthorityID,
Provisioner: &linkedca.ProvisionerIdentity{
Id: info.ProvisionerID,
Type: linkedca.Provisioner_Type(typ),
Name: info.ProvisionerName,
},
}, info.EndpointID
}
}
return nil, ""
}
func createAttestationData(p provisioner.Interface) *linkedca.AttestationData {
if ap, ok := p.(attProvisioner); ok {
if data := ap.AttestationData(); data != nil {
return &linkedca.AttestationData{
PermanentIdentifier: data.PermanentIdentifier,
}
}
}
return nil
}
func serializeCertificate(crt *x509.Certificate) string {
if crt == nil {
return ""

View file

@ -25,6 +25,44 @@ import (
"github.com/smallstep/certificates/errs"
)
type raProvisioner interface {
RAInfo() *provisioner.RAInfo
}
type attProvisioner interface {
AttestationData() *provisioner.AttestationData
}
// wrapProvisioner wraps the given provisioner with RA information and
// attestation data.
func wrapProvisioner(p provisioner.Interface, attData *provisioner.AttestationData) *wrappedProvisioner {
var raInfo *provisioner.RAInfo
if rap, ok := p.(raProvisioner); ok {
raInfo = rap.RAInfo()
}
return &wrappedProvisioner{
Interface: p,
attestationData: attData,
raInfo: raInfo,
}
}
// wrappedProvisioner implements raProvisioner and attProvisioner.
type wrappedProvisioner struct {
provisioner.Interface
attestationData *provisioner.AttestationData
raInfo *provisioner.RAInfo
}
func (p *wrappedProvisioner) AttestationData() *provisioner.AttestationData {
return p.attestationData
}
func (p *wrappedProvisioner) RAInfo() *provisioner.RAInfo {
return p.raInfo
}
// GetEncryptedKey returns the JWE key corresponding to the given kid argument.
func (a *Authority) GetEncryptedKey(kid string) (string, error) {
a.adminMutex.RLock()

View file

@ -258,6 +258,11 @@ func (a *Authority) Sign(csr *x509.CertificateRequest, signOpts provisioner.Sign
}
fullchain := append([]*x509.Certificate{resp.Certificate}, resp.CertificateChain...)
// Wrap provisioner with extra information.
prov = wrapProvisioner(prov, attData)
// Store certificate in the db.
if err = a.storeCertificate(prov, fullchain); err != nil {
if !errors.Is(err, db.ErrNotImplemented) {
return nil, errs.Wrap(http.StatusInternalServerError, err,

View file

@ -18,8 +18,6 @@ import (
"testing"
"time"
"gopkg.in/square/go-jose.v2/jwt"
"go.step.sm/crypto/jose"
"go.step.sm/crypto/keyutil"
"go.step.sm/crypto/minica"
@ -59,6 +57,15 @@ func (m *certificateDurationEnforcer) Enforce(cert *x509.Certificate) error {
return nil
}
type certificateChainDB struct {
db.MockAuthDB
MStoreCertificateChain func(provisioner.Interface, ...*x509.Certificate) error
}
func (d *certificateChainDB) StoreCertificateChain(p provisioner.Interface, certs ...*x509.Certificate) error {
return d.MStoreCertificateChain(p, certs...)
}
func getDefaultIssuer(a *Authority) *x509.Certificate {
return a.x509CAService.(*softcas.SoftCAS).CertificateChain[len(a.x509CAService.(*softcas.SoftCAS).CertificateChain)-1]
}
@ -767,7 +774,6 @@ ZYtQ9Ot36qc=
aa.config.AuthorityConfig.Template = a.config.AuthorityConfig.Template
aa.db = &db.MockAuthDB{
MStoreCertificate: func(crt *x509.Certificate) error {
fmt.Println(crt.Subject)
assert.Equals(t, crt.Subject.CommonName, "smallstep test")
return nil
},
@ -793,6 +799,38 @@ ZYtQ9Ot36qc=
extensionsCount: 6,
}
},
"ok with attestation data": func(t *testing.T) *signTest {
csr := getCSR(t, priv)
aa := testAuthority(t)
aa.config.AuthorityConfig.Template = a.config.AuthorityConfig.Template
aa.db = &certificateChainDB{
MStoreCertificateChain: func(prov provisioner.Interface, certs ...*x509.Certificate) error {
p, ok := prov.(attProvisioner)
if assert.True(t, ok) {
assert.Equals(t, &provisioner.AttestationData{
PermanentIdentifier: "1234567890",
}, p.AttestationData())
}
if assert.Len(t, 2, certs) {
assert.Equals(t, certs[0].Subject.CommonName, "smallstep test")
assert.Equals(t, certs[1].Subject.CommonName, "smallstep Intermediate CA")
}
return nil
},
}
return &signTest{
auth: aa,
csr: csr,
extraOpts: append(extraOpts, provisioner.AttestationData{
PermanentIdentifier: "1234567890",
}),
signOpts: signOpts,
notBefore: signOpts.NotBefore.Time().Truncate(time.Second),
notAfter: signOpts.NotAfter.Time().Truncate(time.Second),
extensionsCount: 6,
}
},
}
for name, genTestCase := range tests {
@ -1401,15 +1439,15 @@ func TestAuthority_Revoke(t *testing.T) {
}
},
"fail/nil-db": func() test {
cl := jwt.Claims{
cl := jose.Claims{
Subject: "sn",
Issuer: validIssuer,
NotBefore: jwt.NewNumericDate(now),
Expiry: jwt.NewNumericDate(now.Add(time.Minute)),
NotBefore: jose.NewNumericDate(now),
Expiry: jose.NewNumericDate(now.Add(time.Minute)),
Audience: validAudience,
ID: "44",
}
raw, err := jwt.Signed(sig).Claims(cl).CompactSerialize()
raw, err := jose.Signed(sig).Claims(cl).CompactSerialize()
assert.FatalError(t, err)
return test{
@ -1441,15 +1479,15 @@ func TestAuthority_Revoke(t *testing.T) {
Err: errors.New("force"),
}))
cl := jwt.Claims{
cl := jose.Claims{
Subject: "sn",
Issuer: validIssuer,
NotBefore: jwt.NewNumericDate(now),
Expiry: jwt.NewNumericDate(now.Add(time.Minute)),
NotBefore: jose.NewNumericDate(now),
Expiry: jose.NewNumericDate(now.Add(time.Minute)),
Audience: validAudience,
ID: "44",
}
raw, err := jwt.Signed(sig).Claims(cl).CompactSerialize()
raw, err := jose.Signed(sig).Claims(cl).CompactSerialize()
assert.FatalError(t, err)
return test{
@ -1481,15 +1519,15 @@ func TestAuthority_Revoke(t *testing.T) {
Err: db.ErrAlreadyExists,
}))
cl := jwt.Claims{
cl := jose.Claims{
Subject: "sn",
Issuer: validIssuer,
NotBefore: jwt.NewNumericDate(now),
Expiry: jwt.NewNumericDate(now.Add(time.Minute)),
NotBefore: jose.NewNumericDate(now),
Expiry: jose.NewNumericDate(now.Add(time.Minute)),
Audience: validAudience,
ID: "44",
}
raw, err := jwt.Signed(sig).Claims(cl).CompactSerialize()
raw, err := jose.Signed(sig).Claims(cl).CompactSerialize()
assert.FatalError(t, err)
return test{
@ -1520,15 +1558,15 @@ func TestAuthority_Revoke(t *testing.T) {
},
}))
cl := jwt.Claims{
cl := jose.Claims{
Subject: "sn",
Issuer: validIssuer,
NotBefore: jwt.NewNumericDate(now),
Expiry: jwt.NewNumericDate(now.Add(time.Minute)),
NotBefore: jose.NewNumericDate(now),
Expiry: jose.NewNumericDate(now.Add(time.Minute)),
Audience: validAudience,
ID: "44",
}
raw, err := jwt.Signed(sig).Claims(cl).CompactSerialize()
raw, err := jose.Signed(sig).Claims(cl).CompactSerialize()
assert.FatalError(t, err)
return test{
auth: _a,
@ -1612,15 +1650,15 @@ func TestAuthority_Revoke(t *testing.T) {
},
}))
cl := jwt.Claims{
cl := jose.Claims{
Subject: "sn",
Issuer: validIssuer,
NotBefore: jwt.NewNumericDate(now),
Expiry: jwt.NewNumericDate(now.Add(time.Minute)),
NotBefore: jose.NewNumericDate(now),
Expiry: jose.NewNumericDate(now.Add(time.Minute)),
Audience: validAudience,
ID: "44",
}
raw, err := jwt.Signed(sig).Claims(cl).CompactSerialize()
raw, err := jose.Signed(sig).Claims(cl).CompactSerialize()
assert.FatalError(t, err)
return test{
auth: a,

8
go.mod
View file

@ -11,7 +11,7 @@ require (
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Masterminds/sprig/v3 v3.2.2
github.com/ThalesIgnite/crypto11 v1.2.5 // indirect
github.com/aws/aws-sdk-go v1.44.37 // indirect
github.com/aws/aws-sdk-go v1.44.111 // indirect
github.com/dgraph-io/ristretto v0.0.4-0.20200906165740-41ebdbffecfd // indirect
github.com/fatih/color v1.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.4.0
@ -42,8 +42,8 @@ require (
github.com/urfave/cli v1.22.10
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352
go.step.sm/cli-utils v0.7.5
go.step.sm/crypto v0.19.1-0.20220929182301-ae99d3fe3185
go.step.sm/linkedca v0.19.0-rc.2
go.step.sm/crypto v0.20.0
go.step.sm/linkedca v0.19.0-rc.3
golang.org/x/crypto v0.0.0-20220919173607-35f4265a4bc0
golang.org/x/net v0.0.0-20220927171203-f486391704dc
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect
@ -59,7 +59,7 @@ require (
cloud.google.com/go/compute v1.7.0 // indirect
cloud.google.com/go/iam v0.3.0 // indirect
cloud.google.com/go/kms v1.4.0 // indirect
filippo.io/edwards25519 v1.0.0-rc.1 // indirect
filippo.io/edwards25519 v1.0.0 // indirect
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect

16
go.sum
View file

@ -64,8 +64,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/Azure/azure-sdk-for-go v65.0.0+incompatible h1:HzKLt3kIwMm4KeJYTdx9EbjRYTySD/t8i1Ee/W5EGXw=
@ -133,8 +133,8 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.44.37 h1:KvDxCX6dfJeEDC77U5GPGSP0ErecmNnhDHFxw+NIvlI=
github.com/aws/aws-sdk-go v1.44.37/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go v1.44.111 h1:AcWfOgeedSQ4gQVwcIe6aLxpQNJMloZQyqnr7Dzki+s=
github.com/aws/aws-sdk-go v1.44.111/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@ -788,10 +788,10 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe
go.step.sm/cli-utils v0.7.5 h1:jyp6X8k8mN1B0uWJydTid0C++8tQhm2kaaAdXKQQzdk=
go.step.sm/cli-utils v0.7.5/go.mod h1:taSsY8haLmXoXM3ZkywIyRmVij/4Aj0fQbNTlJvv71I=
go.step.sm/crypto v0.9.0/go.mod h1:+CYG05Mek1YDqi5WK0ERc6cOpKly2i/a5aZmU1sfGj0=
go.step.sm/crypto v0.19.1-0.20220929182301-ae99d3fe3185 h1:W+UhojTrFZngWTudpP3n9vPs4UNLudVSkKrWZuZg/RU=
go.step.sm/crypto v0.19.1-0.20220929182301-ae99d3fe3185/go.mod h1:972LarNeN9dgx4+zkF3fHCnTWLXzuQSIOdMaGeIslUY=
go.step.sm/linkedca v0.19.0-rc.2 h1:IcPqZ5y7MZNq1+VbYQcKoQEvX80NKRncU1WFCDyY+So=
go.step.sm/linkedca v0.19.0-rc.2/go.mod h1:MCZmPIdzElEofZbiw4eyUHayXgFTwa94cNAV34aJ5ew=
go.step.sm/crypto v0.20.0 h1:IMZUjTdol70IxOLZYHt8AHJy9uIHz/PvDOi2S+urpOs=
go.step.sm/crypto v0.20.0/go.mod h1:diT2XWIHQy0397UI3i78qCKeLLLp2wu0/DIJI66u/MU=
go.step.sm/linkedca v0.19.0-rc.3 h1:3Uu8j187wm7mby+/pz/aQ0wHKRm7w/2AsVPpvcAn4v8=
go.step.sm/linkedca v0.19.0-rc.3/go.mod h1:MCZmPIdzElEofZbiw4eyUHayXgFTwa94cNAV34aJ5ew=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=