diff --git a/go/tools/scion-pki/internal/v2/tmpl/BUILD.bazel b/go/tools/scion-pki/internal/v2/tmpl/BUILD.bazel index 067e76cba6..ce4de11da1 100644 --- a/go/tools/scion-pki/internal/v2/tmpl/BUILD.bazel +++ b/go/tools/scion-pki/internal/v2/tmpl/BUILD.bazel @@ -29,6 +29,7 @@ go_test( deps = [ "//go/lib/addr:go_default_library", "//go/lib/scrypto:go_default_library", + "//go/lib/scrypto/cert/v2:go_default_library", "//go/lib/scrypto/trc/v2:go_default_library", "//go/lib/util:go_default_library", "//go/lib/xtest:go_default_library", diff --git a/go/tools/scion-pki/internal/v2/tmpl/topo.go b/go/tools/scion-pki/internal/v2/tmpl/topo.go index 0a5d80779c..1452b8903e 100644 --- a/go/tools/scion-pki/internal/v2/tmpl/topo.go +++ b/go/tools/scion-pki/internal/v2/tmpl/topo.go @@ -50,7 +50,7 @@ func (g topoGen) Run(topo topoFile) error { if err := g.genKeys(topo, trcs); err != nil { return serrors.WrapStr("unable to generate key configs", err) } - if err := g.genCerts(topo, trcs); err != nil { + if err := g.genCerts(topo); err != nil { return serrors.WrapStr("unable to generate certificate configs", err) } pkicmn.QuietPrint("Generated all configuration files\n") @@ -163,11 +163,83 @@ func (g topoGen) genASKeys(as addr.AS, cfg conf.TRC2) conf.Keys { return keys } -func (g topoGen) genCerts(topo topoFile, cfg map[addr.ISD]conf.TRC2) error { - // TODO(roosd): implement. +func (g topoGen) genCerts(topo topoFile) error { + if err := g.genIssuerCerts(topo); err != nil { + return serrors.WrapStr("unable to generate issuer certificates", err) + } + if err := g.genASCerts(topo); err != nil { + return serrors.WrapStr("unable to generate AS certificates", err) + } + return nil +} + +func (g topoGen) genIssuerCerts(topo topoFile) error { + for ia, entry := range topo.ASes { + if !entry.Core { + continue + } + cfg := g.genIssuerCert(ia) + var buf bytes.Buffer + if err := cfg.Encode(&buf); err != nil { + return serrors.WithCtx(err, "ia", ia) + } + file := conf.IssuerFile(g.Dirs.Root, ia, cfg.Version) + if err := pkicmn.WriteToFile(buf.Bytes(), file, 0644); err != nil { + return serrors.WrapStr("unable to write issuer config", err, "ia", ia, "file", file) + } + } + return nil +} + +func (g topoGen) genIssuerCert(ia addr.IA) conf.Issuer { + issKey := scrypto.KeyVersion(1) + cfg := conf.Issuer{ + Description: fmt.Sprintf("Issuer certificate %s", ia), + Version: 1, + IssuingKeyVersion: &issKey, + RevocationKeyVersion: nil, + TRCVersion: 1, + OptDistPoints: []addr.IA{}, + Validity: g.Validity, + } + return cfg +} + +func (g topoGen) genASCerts(topo topoFile) error { + for ia, entry := range topo.ASes { + issuer := entry.Issuer + if entry.Core { + issuer = ia + } + cfg := g.genASCert(ia, issuer) + var buf bytes.Buffer + if err := cfg.Encode(&buf); err != nil { + return serrors.WithCtx(err, "ia", ia) + } + file := conf.ASFile(g.Dirs.Root, ia, cfg.Version) + if err := pkicmn.WriteToFile(buf.Bytes(), file, 0644); err != nil { + return serrors.WrapStr("unable to write AS config", err, "ia", ia, "file", file) + } + } return nil } +func (g topoGen) genASCert(ia, issuer addr.IA) conf.AS { + sigKey, encKey, revKey := scrypto.KeyVersion(1), scrypto.KeyVersion(1), scrypto.KeyVersion(1) + cfg := conf.AS{ + Description: fmt.Sprintf("Issuer certificate %s", ia), + Version: 1, + SigningKeyVersion: &sigKey, + EncryptionKeyVersion: &encKey, + RevocationKeyVersion: &revKey, + IssuerIA: issuer, + IssuerCertVersion: 1, + OptDistPoints: []addr.IA{}, + Validity: g.Validity, + } + return cfg +} + // topoFile is used to parse the topology description. type topoFile struct { ASes map[addr.IA]asEntry `yaml:"ASes"` diff --git a/go/tools/scion-pki/internal/v2/tmpl/topo_test.go b/go/tools/scion-pki/internal/v2/tmpl/topo_test.go index cb6c739362..2621592de3 100644 --- a/go/tools/scion-pki/internal/v2/tmpl/topo_test.go +++ b/go/tools/scion-pki/internal/v2/tmpl/topo_test.go @@ -23,6 +23,7 @@ import ( "github.com/scionproto/scion/go/lib/addr" "github.com/scionproto/scion/go/lib/scrypto" + "github.com/scionproto/scion/go/lib/scrypto/cert/v2" "github.com/scionproto/scion/go/lib/scrypto/trc/v2" "github.com/scionproto/scion/go/lib/util" "github.com/scionproto/scion/go/lib/xtest" @@ -86,4 +87,66 @@ func TestTopoGen(t *testing.T) { assert.Equal(t, exp, cfg.PrimaryASes) }) + for ia, entry := range topo.ASes { + t.Run("Keys config "+ia.String(), func(t *testing.T) { + cfg, err := conf.LoadKeys(conf.KeysFile(tmpDir, ia)) + require.NoError(t, err) + + checkMeta := func(t *testing.T, meta conf.KeyMeta, algo string) { + t.Helper() + assert.Equal(t, algo, meta.Algorithm) + assert.Equal(t, g.Validity, meta.Validity) + } + + checkMeta(t, cfg.AS[cert.SigningKey][1], scrypto.Ed25519) + checkMeta(t, cfg.AS[cert.RevocationKey][1], scrypto.Ed25519) + checkMeta(t, cfg.AS[cert.EncryptionKey][1], scrypto.Curve25519xSalsa20Poly1305) + if !entry.Core { + return + } + checkMeta(t, cfg.Issuer[cert.IssuingKey][1], scrypto.Ed25519) + checkMeta(t, cfg.Primary[trc.IssuingKey][1], scrypto.Ed25519) + checkMeta(t, cfg.Primary[trc.OnlineKey][1], scrypto.Ed25519) + checkMeta(t, cfg.Primary[trc.OfflineKey][1], scrypto.Ed25519) + }) + } + + for ia, entry := range topo.ASes { + if !entry.Core { + continue + } + t.Run("Issuer config "+ia.String(), func(t *testing.T) { + cfg, err := conf.LoadIssuer(conf.IssuerFile(tmpDir, ia, 1)) + require.NoError(t, err) + + assert.Contains(t, cfg.Description, "Issuer certificate") + assert.Equal(t, scrypto.Version(1), cfg.Version) + assert.Equal(t, scrypto.KeyVersion(1), *cfg.IssuingKeyVersion) + assert.Nil(t, cfg.RevocationKeyVersion) + assert.Equal(t, scrypto.Version(1), cfg.TRCVersion) + assert.Empty(t, cfg.OptDistPoints) + assert.Equal(t, g.Validity, cfg.Validity) + }) + } + + for ia, entry := range topo.ASes { + issuer := entry.Issuer + if entry.Core { + issuer = ia + } + t.Run("AS config "+ia.String(), func(t *testing.T) { + cfg, err := conf.LoadAS(conf.ASFile(tmpDir, ia, 1)) + require.NoError(t, err) + + assert.Contains(t, cfg.Description, "Issuer certificate") + assert.Equal(t, scrypto.Version(1), cfg.Version) + assert.Equal(t, scrypto.KeyVersion(1), *cfg.SigningKeyVersion) + assert.Equal(t, scrypto.KeyVersion(1), *cfg.EncryptionKeyVersion) + assert.Equal(t, scrypto.KeyVersion(1), *cfg.RevocationKeyVersion) + assert.Equal(t, issuer, cfg.IssuerIA) + assert.Equal(t, scrypto.Version(1), cfg.IssuerCertVersion) + assert.Empty(t, cfg.OptDistPoints) + assert.Equal(t, g.Validity, cfg.Validity) + }) + } }