Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 44 additions & 20 deletions cmd/boulder-mtpublisher/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import (
"fmt"
"os"

"github.com/letsencrypt/boulder/bs3"
"github.com/letsencrypt/boulder/cmd"
"github.com/letsencrypt/boulder/config"
"github.com/letsencrypt/boulder/issuance"
"github.com/letsencrypt/boulder/mtpublisher"
"github.com/letsencrypt/boulder/privatekey"
"github.com/letsencrypt/boulder/sa"
"github.com/letsencrypt/boulder/trees/issuancelog"
)
Expand All @@ -25,25 +26,39 @@ type Config struct {

DebugAddr string `validate:"omitempty,hostname_port"`

// PollInterval is how often the stub scans for checkpoints that still
// lack a mirror cosignature.
// PollInterval is how often the publisher scans for checkpoints that
// still lack a mirror cosignature.
PollInterval config.Duration `validate:"required"`

// LogID identifies the issuance log this publisher operates on. It must
// match the mtca's.
LogID issuancelog.ID `validate:"required"`

// MirrorID identifies the cosigner this publisher writes alongside each
// cosignature (e.g. "32473.9").
MirrorID string `validate:"required"`

// MirrorPublicKeyFile holds the PEM-encoded ML-DSA-44 public key used
// to verify cosignatures.
MirrorPublicKeyFile string `validate:"required"`

// MirrorKeyFile holds the PEM-encoded ML-DSA-44 private key used to
// cosign checkpoints.
MirrorKeyFile string `validate:"required"`
// MTCACertFile holds the PEM-encoded certificate of the mtca, whose
// ML-DSA-44 public key is used to reconstruct each checkpoint's signed
// note from the database.
MTCACertFile string `validate:"required"`

// Mirror identifies the mirror this publisher submits to. Note: this is
// temporary until we start loading support multiple mirrors sourced from
// https://www.gstatic.com/mtcs/cosigners/v1/cosigners.json (schema:
// https://www.gstatic.com/mtcs/cosigners/v1/cosigners_schema.json).
Mirror struct {
// ID is the mirror's ID (e.g. "32473.9").
ID string `validate:"required"`

// PublicKeyFile holds the mirror's PEM-encoded ML-DSA-44 public
// key.
PublicKeyFile string `validate:"required"`

// BaseURL is the base URL of the mirror's tlog-mirror submission
// endpoints (e.g. "http://localhost:4700").
BaseURL string `validate:"required,url"`
}

// S3 locates the source log's tile storage, which the publisher reads
// entries and proof hashes from when submitting to the mirror.
S3 bs3.Config `validate:"required"`
}
Syslog cmd.SyslogConfig
OpenTelemetry cmd.OpenTelemetryConfig
Expand Down Expand Up @@ -94,13 +109,22 @@ func main() {
dbMap, err := sa.InitWrappedDb(c.MTPublisher.DB, scope, logger)
cmd.FailOnError(err, "While initializing dbMap")

signer, _, err := privatekey.Load(c.MTPublisher.MirrorKeyFile)
cmd.FailOnError(err, "Loading cosigner key")
pubKey, err := loadMLDSAPublicKey(c.MTPublisher.MirrorPublicKeyFile)
cmd.FailOnError(err, "Loading cosigner public key")
pubKey, err := loadMLDSAPublicKey(c.MTPublisher.Mirror.PublicKeyFile)
cmd.FailOnError(err, "Loading mirror public key")
Comment on lines +112 to +113

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: move this down to line 122. This pubkey isn't used until we construct the mirror, so all the MTCA-loading code shouldn't be between those two operations.

caCert, err := issuance.LoadCertificate(c.MTPublisher.MTCACertFile)
cmd.FailOnError(err, "Loading MTCA certificate")
caPubKey, ok := caCert.PublicKey.(*mldsa.PublicKey)
if !ok {
cmd.Fail(fmt.Sprintf("MTCA certificate public key is %T, must be ML-DSA-44", caCert.PublicKey))
}
s3c, err := bs3.FromConfig(c.MTPublisher.S3, logger)
cmd.FailOnError(err, "Loading S3 config")

mirror, err := mtpublisher.NewMirrorClient(c.MTPublisher.Mirror.BaseURL, mtpublisher.NewSource(s3c, c.MTPublisher.LogID.TilePrefix()), c.MTPublisher.Mirror.ID, pubKey)
cmd.FailOnError(err, "Creating mirror client")

publisher, err := mtpublisher.New(dbMap, c.MTPublisher.PollInterval.Duration, c.MTPublisher.LogID, c.MTPublisher.MirrorID, signer, pubKey, logger)
cmd.FailOnError(err, "Failed to create MTPublisher stub")
publisher, err := mtpublisher.New(dbMap, c.MTPublisher.PollInterval.Duration, c.MTPublisher.LogID, caPubKey, mirror, logger)
cmd.FailOnError(err, "Failed to create MTPublisher")

ctx, cancel := context.WithCancel(context.Background())
go cmd.CatchSignals(cancel)
Expand Down
8 changes: 8 additions & 0 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,14 @@ type HostnamePolicyConfig struct {
HostnamePolicyFile string `validate:"required"`
}

// MirrorConfig identifies an MTC mirror cosigner.
Comment thread
beautifulentropy marked this conversation as resolved.
type MirrorConfig struct {
// ID is the mirror's ID (e.g. "32473.9").
ID string `validate:"required"`
// PublicKeyFile holds the mirror's PEM-encoded ML-DSA-44 public key.
PublicKeyFile string `validate:"required"`
}

// TLSConfig represents certificates and a key for authenticated TLS.
type TLSConfig struct {
CertFile string `validate:"required"`
Expand Down
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ services:
condition: service_started
bminio:
condition: service_healthy
bsunlight:
condition: service_started
entrypoint: test/entrypoint.sh
working_dir: &boulder_working_dir /boulder

Expand Down
16 changes: 15 additions & 1 deletion mtca/mtca_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ import (
blog "github.com/letsencrypt/boulder/log"
"github.com/letsencrypt/boulder/mtca/proto"
"github.com/letsencrypt/boulder/mtpublisher"
"github.com/letsencrypt/boulder/mtpublisher/mtpublishertest"
"github.com/letsencrypt/boulder/privatekey"
"github.com/letsencrypt/boulder/sa"
"github.com/letsencrypt/boulder/test/vars"
"github.com/letsencrypt/boulder/trees/cosigned"
"github.com/letsencrypt/boulder/trees/entry"
Expand Down Expand Up @@ -285,11 +287,23 @@ func (e *errorS3) PutObject(ctx context.Context, params *s3.PutObjectInput, optF
// in for the daemon, so sequencing can proceed.
func mirrorCosign(t *testing.T, m *mtca) {
t.Helper()
caPub, ok := m.issuer.Signer.Public().(*mldsa.PublicKey)
if !ok {
t.Fatalf("issuer public key is %T, must be ML-DSA-44", m.issuer.Signer.Public())
}
key, err := mldsa.NewPrivateKey(mldsa.MLDSA44(), make([]byte, 32))
if err != nil {
t.Fatalf("NewPrivateKey: %s", err)
}
p, err := mtpublisher.New(m.db, time.Second, m.logID, "32473.9", privatekey.NewDeterministicSigner(key), key.PublicKey(), blog.NewMock())
mirror, err := mtpublishertest.NewTestMirror("32473.9", m.logID.Origin(), privatekey.NewDeterministicSigner(key))
if err != nil {
t.Fatalf("mtpublishertest.NewTestMirror: %s", err)
}
dbMap, err := sa.DBMapForTest(vars.DBConnMTCMeta_44947_4_1_0_44FullPerms)
if err != nil {
t.Fatalf("opening mtcmeta dbMap: %s", err)
}
p, err := mtpublisher.New(dbMap, time.Second, m.logID, caPub, mirror, blog.NewMock())
if err != nil {
t.Fatalf("mtpublisher.New: %s", err)
}
Expand Down
Loading