@@ -29,6 +29,7 @@ import (
2929 mtcapb "github.com/letsencrypt/boulder/mtca/proto"
3030 "github.com/letsencrypt/boulder/trees/cosignature"
3131 "github.com/letsencrypt/boulder/trees/entry"
32+ "github.com/letsencrypt/boulder/trees/issuancelog"
3233 "github.com/letsencrypt/boulder/trees/tiles"
3334 "golang.org/x/mod/sumdb/tlog"
3435)
@@ -42,16 +43,20 @@ var _ mtcapb.MTCAServer = &mtca{}
4243func New (
4344 issuer * issuance.Issuer ,
4445 profiles map [string ]* issuance.Profile ,
46+ logID issuancelog.ID ,
4547 sequencingPeriod time.Duration ,
4648 dbMap * borp.DbMap ,
4749 s3c simpleS3 ,
4850 logger blog.Logger ,
4951 clk clock.Clock ,
5052) (* mtca , error ) {
51- mtcaID , err := getMTCAID (issuer .Cert .Certificate )
53+ certCAID , err := getCAID (issuer .Cert .Certificate )
5254 if err != nil {
5355 return nil , err
5456 }
57+ if certCAID != logID .CAID {
58+ return nil , fmt .Errorf ("configured CA ID %q does not match issuer certificate CA ID %q" , logID .CAID , certCAID )
59+ }
5560
5661 if sequencingPeriod == 0 {
5762 return nil , errors .New ("sequencingPeriod must be non-zero" )
@@ -60,10 +65,8 @@ func New(
6065 m := & mtca {
6166 issuer : issuer ,
6267 profiles : profiles ,
63- mtcaID : mtcaID ,
64- // TODO: collect this from config
65- logNumber : 44 ,
66- pool : & pool {maxSize : 100 },
68+ logID : logID ,
69+ pool : & pool {maxSize : 100 },
6770
6871 sequencingPeriod : sequencingPeriod ,
6972
@@ -73,7 +76,7 @@ func New(
7376 clk : clk ,
7477 }
7578
76- cosigner , err := cosignature .NewCosigner (mtcaID , m . mtcLogID (), issuer .Signer )
79+ cosigner , err := cosignature .NewCosigner (logID . CAID , logID . Origin (), issuer .Signer )
7780 if err != nil {
7881 return nil , fmt .Errorf ("creating CA cosigner: %s" , err )
7982 }
@@ -83,7 +86,7 @@ func New(
8386 if ! ok {
8487 return nil , fmt .Errorf ("issuer public key is %T, must be ML-DSA-44" , issuer .Signer .Public ())
8588 }
86- verifier , err := cosignature .NewVerifier (mtcaID , pubKey )
89+ verifier , err := cosignature .NewVerifier (logID . CAID , pubKey )
8790 if err != nil {
8891 return nil , fmt .Errorf ("creating CA verifier: %s" , err )
8992 }
@@ -97,12 +100,11 @@ type mtca struct {
97100
98101 issuer * issuance.Issuer
99102 profiles map [string ]* issuance.Profile
100- mtcaID string
103+ logID issuancelog. ID
101104 cosigner * cosignature.Cosigner
102105 verifier * cosignature.Verifier
103106
104- logNumber uint16
105- pool * pool
107+ pool * pool
106108
107109 // frontier contains all the tiles on the right edge of the tree.
108110 // It will be used to accumulate entries for writing to storage.
@@ -128,15 +130,15 @@ type simpleS3 interface {
128130 Bucket () string
129131}
130132
131- func getMTCAID (issuerCert * x509.Certificate ) (string , error ) {
133+ func getCAID (issuerCert * x509.Certificate ) (string , error ) {
132134 testingTrustAnchorIDOID := asn1.ObjectIdentifier {1 , 3 , 6 , 1 , 4 , 1 , 44363 , 47 , 1 }
133135 for _ , attribute := range issuerCert .Subject .Names {
134136 if attribute .Type .Equal (testingTrustAnchorIDOID ) {
135- mtcaID , ok := attribute .Value .(string )
137+ caID , ok := attribute .Value .(string )
136138 if ! ok {
137139 return "" , fmt .Errorf ("invalid trust anchor attribute type %T" , attribute .Value )
138140 }
139- return mtcaID , nil
141+ return caID , nil
140142 }
141143 }
142144
@@ -165,14 +167,14 @@ func (m *mtca) InitLog(ctx context.Context) error {
165167 _ , err = db .WithTransaction (ctx , m .db , func (tx db.Executor ) (any , error ) {
166168 var numLatestCheckpoints int64
167169 err := tx .SelectOne (ctx , & numLatestCheckpoints , "SELECT COUNT(*) FROM latestCheckpoint WHERE mtcLogID = ?" ,
168- m .mtcLogID ())
170+ m .logID . String ())
169171 if err != nil {
170172 return nil , fmt .Errorf ("getting latestCheckpoint: %s" , err )
171173 }
172174
173175 var numCheckpoints int64
174176 err = tx .SelectOne (ctx , & numCheckpoints , "SELECT COUNT(*) FROM checkpoints WHERE mtcLogID = ?" ,
175- m .mtcLogID ())
177+ m .logID . String ())
176178 if err != nil {
177179 return nil , fmt .Errorf ("getting checkpoints: %s" , err )
178180 }
@@ -183,11 +185,11 @@ func (m *mtca) InitLog(ctx context.Context) error {
183185 }
184186
185187 return nil , fmt .Errorf ("initializing issuance log for %s: already has %d checkpoints and %d latestCheckpoint rows" ,
186- m .mtcLogID (), numCheckpoints , numLatestCheckpoints )
188+ m .logID . String (), numCheckpoints , numLatestCheckpoints )
187189 }
188190
189191 firstCheckpoint := checkpoint {
190- MTCLogID : m .mtcLogID (),
192+ MTCLogID : m .logID . String (),
191193 TreeSize : candidate .TreeSize (),
192194 RootHash : rootHash [:],
193195 }
@@ -205,7 +207,7 @@ func (m *mtca) InitLog(ctx context.Context) error {
205207 }
206208
207209 _ , err = tx .ExecContext (ctx , "INSERT INTO latestCheckpoint (id, mtcLogID) VALUES (?, ?)" ,
208- firstCheckpoint .ID , m .mtcLogID ())
210+ firstCheckpoint .ID , m .logID . String ())
209211 if err != nil {
210212 return nil , fmt .Errorf ("inserting latestCheckpoint: %s" , err )
211213 }
@@ -224,7 +226,7 @@ func (m *mtca) InitLog(ctx context.Context) error {
224226 return err
225227 }
226228
227- err = candidate .Publish (ctx , m .s3c , m .tileStoragePrefix ())
229+ err = candidate .Publish (ctx , m .s3c , m .logID . TilePrefix ())
228230 if err != nil {
229231 return err
230232 }
@@ -246,7 +248,7 @@ func (m *mtca) Preflight(ctx context.Context) error {
246248 if err != nil {
247249 return err
248250 }
249- frontier , err := tiles .LoadFrontier (ctx , m .s3c , latest .TreeSize , m .tileStoragePrefix ())
251+ frontier , err := tiles .LoadFrontier (ctx , m .s3c , latest .TreeSize , m .logID . TilePrefix ())
250252 if err != nil {
251253 return err
252254 }
@@ -299,32 +301,6 @@ func (p *pool) append(e pendingEntry) error {
299301 return nil
300302}
301303
302- // mtcLogID returns the string-formatted relative OID for this log.
303- // The .0. arc relative to the MTCA ID contains log numbers.
304- // https://ietf-plants-wg.github.io/merkle-tree-certs/draft-ietf-plants-merkle-tree-certs.html#ca-ids
305- func (m * mtca ) mtcLogID () string {
306- return fmt .Sprintf ("%s.0.%d" , m .mtcaID , m .logNumber )
307- }
308-
309- // tileStoragePrefix returns the path within a bucket where we will store tiles.
310- //
311- // https://github.com/C2SP/C2SP/blob/main/mtc-tlog.md#serving-issuance-logs
312- //
313- // "Each log's prefix URL is the concatenation of the CA prefix URL and the log number, encoded as an
314- // ASCII decimal integer with no additional leading zeros:
315- //
316- // <CA prefix URL>/<log number>"
317- //
318- // We assume we will serve directly from tile storage (likely sync'ed somewhere), so we
319- // want to store tiles compatible with that pattern, with log number as the last component
320- // of the path before "tile/".
321- //
322- // As a matter of local convention we will put the MTCA ID as the path component right before
323- // log number.
324- func (m * mtca ) tileStoragePrefix () string {
325- return fmt .Sprintf ("%s/%d" , m .mtcaID , m .logNumber )
326- }
327-
328304// Issue requests a TBSCertificateLogEntry be issued and returns after it's been sequenced into the log
329305// and a new checkpoint signed by the CA. It does not wait for a mirror cosignature.
330306//
@@ -386,7 +362,7 @@ func (m *mtca) Issue(ctx context.Context, req *mtcapb.IssueRequest) (*mtcapb.Iss
386362 return nil , errors .New ("error during sequencing" )
387363 }
388364 return & mtcapb.IssueResponse {
389- MtcLogID : m .mtcLogID (),
365+ MtcLogID : m .logID . String (),
390366 MtcEntryIndex : entryIndex ,
391367 }, nil
392368 }
@@ -492,7 +468,7 @@ func (m *mtca) sequence(ctx context.Context) error {
492468 m .log .AuditInfo ("issuing" , map [string ]any {
493469 "TBSCertificateLogEntry" : hex .EncodeToString (e .mtcle .TBS ()),
494470 "entryIndex" : latest .TreeSize + int64 (i ),
495- "mtcLogID" : m .mtcLogID (),
471+ "mtcLogID" : m .logID . String (),
496472 "newRootHash" : newRootHash .String (),
497473 })
498474 }
@@ -501,14 +477,14 @@ func (m *mtca) sequence(ctx context.Context) error {
501477 // (but before publishing a new checkpoint signed note), we will flush
502478 // to the live location. This ensures we've persisted the tiles before
503479 // committing to a tree hash by signing it.
504- err = candidate .Stage (ctx , m .s3c , m .tileStoragePrefix ())
480+ err = candidate .Stage (ctx , m .s3c , m .logID . TilePrefix ())
505481 if err != nil {
506482 return fmt .Errorf ("staging candidate tiles: %s" , err )
507483 }
508484
509485 newCheckpoint := checkpoint {
510486 ID : 0 ,
511- MTCLogID : m .mtcLogID (),
487+ MTCLogID : m .logID . String (),
512488 MTCASignature : nil ,
513489 MirrorID : "" ,
514490 MirrorSignature : nil ,
@@ -541,7 +517,7 @@ func (m *mtca) sequence(ctx context.Context) error {
541517 // https://mariadb.com/docs/server/reference/sql-statements/data-manipulation/selecting-data/for-update
542518 err := tx .SelectOne (ctx , & latestID ,
543519 `SELECT id from latestCheckpoint WHERE mtcLogID = ? FOR UPDATE` ,
544- m .mtcLogID ())
520+ m .logID . String ())
545521 if err != nil {
546522 return nil , err
547523 }
@@ -558,7 +534,7 @@ func (m *mtca) sequence(ctx context.Context) error {
558534 }
559535
560536 result , err := tx .ExecContext (ctx , "UPDATE checkpoints SET mtcaSignature = ? WHERE mtcLogID = ? AND id = ?" ,
561- caSig , m .mtcLogID (), newCheckpoint .ID )
537+ caSig , m .logID . String (), newCheckpoint .ID )
562538 if err != nil {
563539 return nil , fmt .Errorf ("updating checkpoint: %s" , err )
564540 }
@@ -571,7 +547,7 @@ func (m *mtca) sequence(ctx context.Context) error {
571547 }
572548
573549 result , err = tx .ExecContext (ctx , "UPDATE latestCheckpoint SET id = ? WHERE mtcLogID = ? AND id = ?" ,
574- newCheckpoint .ID , m .mtcLogID (), latestID )
550+ newCheckpoint .ID , m .logID . String (), latestID )
575551 if err != nil {
576552 return nil , fmt .Errorf ("updating latestCheckpoint: %s" , err )
577553 }
@@ -599,7 +575,7 @@ func (m *mtca) sequence(ctx context.Context) error {
599575 // Once we add publishing of checkpoints as signed notes, publication of the signed note
600576 // should come after this flush succeeds, so monitors don't try to fetch tiles that aren't
601577 // yet available.
602- err = m .frontier .Publish (ctx , m .s3c , m .tileStoragePrefix ())
578+ err = m .frontier .Publish (ctx , m .s3c , m .logID . TilePrefix ())
603579 if err != nil {
604580 return fmt .Errorf ("publishing tiles: %s" , err )
605581 }
@@ -671,13 +647,13 @@ func (m *mtca) latestCheckpoint(ctx context.Context) (*checkpoint, error) {
671647 USING(id)
672648 WHERE latestCheckpoint.mtcLogID = ? AND
673649 checkpoints.mtcLogID = ?` ,
674- m .mtcLogID (),
675- m .mtcLogID ())
650+ m .logID . String (),
651+ m .logID . String ())
676652 if err != nil {
677653 if errors .Is (err , sql .ErrNoRows ) {
678- return nil , fmt .Errorf ("getting latest checkpoint for %q: issuance log DB is not initialized" , m .mtcLogID ())
654+ return nil , fmt .Errorf ("getting latest checkpoint for %q: issuance log DB is not initialized" , m .logID . String ())
679655 }
680- return nil , fmt .Errorf ("getting latest checkpoint for %q: %w" , m .mtcLogID (), err )
656+ return nil , fmt .Errorf ("getting latest checkpoint for %q: %w" , m .logID . String (), err )
681657 }
682658
683659 return & latest , nil
0 commit comments