Skip to content

Commit

Permalink
Merge pull request #20 from bwesterb/lvalenta/notafter
Browse files Browse the repository at this point in the history
Add NotAfter field to AssertionRequest
  • Loading branch information
lukevalenta authored Feb 26, 2025
2 parents b7a883c + b9bce7c commit 7c2460e
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 45 deletions.
40 changes: 32 additions & 8 deletions ca/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,22 @@ func (h *Handle) QueueMultiple(it func(yield func(ar mtc.AssertionRequest) error
defer w.Close()
bw := bufio.NewWriter(w)

nextBatch := mtc.Batch{
Number: h.params.ActiveBatches(time.Now()).End + 1,
CA: &h.params,
}
batchStart, batchEnd := nextBatch.ValidityInterval()

if err := it(func(ar mtc.AssertionRequest) error {
// Check that the assertion matches the checksum.
err = ar.Check()
if err != nil {
return err
}
notAfter := ar.NotAfter
if notAfter.IsZero() || batchEnd.Before(notAfter) {
notAfter = batchEnd
}
switch h.params.EvidencePolicy {
case mtc.EmptyEvidencePolicyType:
case mtc.UmbilicalEvidencePolicyType:
Expand All @@ -145,23 +160,25 @@ func (h *Handle) QueueMultiple(it func(yield func(ar mtc.AssertionRequest) error
}
}
if chain == nil {
return fmt.Errorf("missing x509 chain evidence")
return errors.New("missing x509 chain evidence")
}
// TODO validate based on the min of the certificate 'not_after' and the
// next batch's expiration, and add the timestamp to the queued assertion.
// https://github.com/davidben/merkle-tree-certs/pull/92
batch := mtc.Batch{
CA: &h.params,
Number: h.params.ActiveBatches(time.Now()).End + 1,
if notAfter.IsZero() || chain[0].NotAfter.Before(notAfter) {
notAfter = chain[0].NotAfter
}
_, err = umbilical.CheckAssertionValidForX509(ar.Assertion, batch, chain, h.umbilicalRoots, h.revocationChecker)
_, err = umbilical.CheckAssertionValidForX509(ar.Assertion, batchStart, notAfter, chain, h.umbilicalRoots, h.revocationChecker)
if err != nil {
return err
}
default:
return fmt.Errorf("unknown evidence policy: %d", h.params.EvidencePolicy)
}

if notAfter != ar.NotAfter {
ar.NotAfter = notAfter
// Recompute the checksum.
ar.Checksum = nil
}

buf, err := ar.MarshalBinary()
if err != nil {
return err
Expand Down Expand Up @@ -1009,6 +1026,13 @@ func (h *Handle) issueBatchTo(dir string, batch mtc.Batch, empty bool) error {

if !empty {
err = h.WalkQueue(func(ar mtc.AssertionRequest) error {
// Skip assertions that are already expired.
if start, _ := batch.ValidityInterval(); ar.NotAfter.Before(start) {
return nil
}

// TODO add not_after to abridged assertion and proof
// https://github.com/davidben/merkle-tree-certs/pull/92
aa := ar.Assertion.Abridge()
buf, err := aa.MarshalBinary()
if err != nil {
Expand Down
59 changes: 31 additions & 28 deletions cmd/mtc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,11 @@ func assertionRequestFlags(inFile bool) []cli.Flag {
Category: "Assertion",
Usage: "Only proceed if assertion matches checksum",
},

&cli.StringFlag{
Name: "not_after",
Category: "Assertion",
Usage: "An initial not_after value for the assertion request in RFC3339 format, which can be used to shorten an assertion's lifetime",
},
&cli.StringFlag{
Name: "from-x509-pem",
Category: "Assertion",
Expand Down Expand Up @@ -142,6 +146,7 @@ func assertionRequestFromFlags(cc *cli.Context) (*mtc.AssertionRequest, error) {
func assertionRequestFromFlagsUnchecked(cc *cli.Context) (*mtc.AssertionRequest, error) {
var (
checksum []byte
notAfter time.Time
err error
)

Expand All @@ -152,6 +157,13 @@ func assertionRequestFromFlagsUnchecked(cc *cli.Context) (*mtc.AssertionRequest,
}
}

if cc.String("not_after") != "" {
notAfter, err = time.Parse(time.RFC3339, cc.String("not_after"))
if err != nil {
return nil, fmt.Errorf("parsing not_after: %w", err)
}
}

assertionPath := cc.String("in-file")
if assertionPath != "" {
assertionBuf, err := os.ReadFile(assertionPath)
Expand Down Expand Up @@ -353,6 +365,7 @@ func assertionRequestFromFlagsUnchecked(cc *cli.Context) (*mtc.AssertionRequest,
Assertion: a,
Evidence: el,
Checksum: checksum,
NotAfter: notAfter,
}, nil
}

Expand Down Expand Up @@ -483,31 +496,8 @@ func handleCaShowQueue(cc *cli.Context) error {

err = h.WalkQueue(func(ar mtc.AssertionRequest) error {
count++
a := ar.Assertion
cs := a.Claims
subj := a.Subject
w := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0)
fmt.Fprintf(w, "checksum\t%x\n", ar.Checksum)
fmt.Fprintf(w, "subject_type\t%s\n", subj.Type())
switch subj := subj.(type) {
case *mtc.TLSSubject:
asubj := subj.Abridge().(*mtc.AbridgedTLSSubject)
fmt.Fprintf(w, "signature_scheme\t%s\n", asubj.SignatureScheme)
fmt.Fprintf(w, "public_key_hash\t%x\n", asubj.PublicKeyHash[:])
}
if len(cs.DNS) != 0 {
fmt.Fprintf(w, "dns\t%s\n", cs.DNS)
}
if len(cs.DNSWildcard) != 0 {
fmt.Fprintf(w, "dns_wildcard\t%s\n", cs.DNSWildcard)
}
if len(cs.IPv4) != 0 {
fmt.Fprintf(w, "ip4\t%s\n", cs.IPv4)
}
if len(cs.IPv6) != 0 {
fmt.Fprintf(w, "ip6\t%s\n", cs.IPv6)
}
err = writeEvidenceList(w, ar.Evidence)
err = writeAssertionRequest(w, ar)
if err != nil {
return err
}
Expand Down Expand Up @@ -715,6 +705,21 @@ func handleInspectTree(cc *cli.Context) error {
return nil
}

func writeAssertionRequest(w *tabwriter.Writer, ar mtc.AssertionRequest) error {
fmt.Fprintf(w, "checksum\t%x\n", ar.Checksum)
if ar.NotAfter.IsZero() {
fmt.Fprintf(w, "not_after\tunset\n")
} else {
fmt.Fprintf(w, "not_after\t%v\n", ar.NotAfter.UTC())
}
writeAssertion(w, ar.Assertion)
err := writeEvidenceList(w, ar.Evidence)
if err != nil {
return err
}
return nil
}

func writeAssertion(w *tabwriter.Writer, a mtc.Assertion) {
aa := a.Abridge()
cs := aa.Claims
Expand Down Expand Up @@ -849,9 +854,7 @@ func handleInspectAssertionRequest(cc *cli.Context) error {
}

w := tabwriter.NewWriter(os.Stdout, 1, 1, 1, ' ', 0)
fmt.Fprintf(w, "checksum\t%x\n", ar.Checksum)
writeAssertion(w, ar.Assertion)
err = writeEvidenceList(w, ar.Evidence)
err = writeAssertionRequest(w, ar)
if err != nil {
return err
}
Expand Down
11 changes: 10 additions & 1 deletion mtc.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ type AssertionRequest struct {
Checksum []byte
Assertion Assertion
Evidence EvidenceList
NotAfter time.Time
}

// Copy of tls.SignatureScheme to prevent cycling dependencies
Expand Down Expand Up @@ -957,7 +958,8 @@ func (e UnknownEvidence) Info() []byte { return e.info }

func (ar *AssertionRequest) UnmarshalBinary(data []byte) error {
var (
s cryptobyte.String = cryptobyte.String(data)
notAfter uint64
s cryptobyte.String = cryptobyte.String(data)
)
ar.Checksum = make([]byte, sha256.Size)
if !s.CopyBytes(ar.Checksum) {
Expand All @@ -974,6 +976,11 @@ func (ar *AssertionRequest) UnmarshalBinary(data []byte) error {
return err
}

if !s.ReadUint64(&notAfter) {
return ErrTruncated
}
ar.NotAfter = time.Unix(int64(notAfter), 0)

if !s.Empty() {
return ErrExtraBytes
}
Expand Down Expand Up @@ -1001,6 +1008,8 @@ func (ar *AssertionRequest) marshalAndCheckAssertionRequest() ([]byte, error) {
}
b.AddBytes(buf)

b.AddUint64(uint64(ar.NotAfter.Unix()))

checksumBytes, err := b.Bytes()
if err != nil {
return nil, err
Expand Down
16 changes: 8 additions & 8 deletions umbilical/x509.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
package umbilical

import (
"time"

"github.com/bwesterb/mtc"
"github.com/bwesterb/mtc/umbilical/revocation"

Expand Down Expand Up @@ -87,9 +89,9 @@ func GetChainFromTLSServer(addr string) (chain []*x509.Certificate, err error) {
return
}

// Checks whether the given assertion (to be) issued in the given batch
// is consistent with the given X.509 certificate chain and
// trusted roots. The assertion is allowed to cover less than the certificate:
// Checks whether the given assertion (to be) issued is consistent with
// the given X.509 certificate chain and accepted roots for the given validity
// interval. The assertion is allowed to cover less than the certificate:
// eg, only example.com where the certificate covers some.example.com too.
//
// On the other hand, we are more strict than is perhaps required. For
Expand All @@ -102,7 +104,7 @@ func GetChainFromTLSServer(addr string) (chain []*x509.Certificate, err error) {
// revocation of intermediates.
//
// If consistent, returns one or more verified chains.
func CheckAssertionValidForX509(a mtc.Assertion, batch mtc.Batch,
func CheckAssertionValidForX509(a mtc.Assertion, start, end time.Time,
chain []*x509.Certificate, roots *x509.CertPool, rc *revocation.Checker) (
[][]*x509.Certificate, error) {
if len(chain) == 0 {
Expand Down Expand Up @@ -168,9 +170,7 @@ func CheckAssertionValidForX509(a mtc.Assertion, batch mtc.Batch,
return nil, fmt.Errorf("Subjects don't match")
}

// Verify chain at the start of the batch's validity period
start, end := batch.ValidityInterval()

// Verify chain at the start of the validity period
opts := x509.VerifyOptions{
Roots: roots,
Intermediates: x509.NewCertPool(),
Expand All @@ -187,7 +187,7 @@ func CheckAssertionValidForX509(a mtc.Assertion, batch mtc.Batch,
var ret [][]*x509.Certificate
var errs []error

// Verify each chain at the end of the batch's validity period
// Verify each chain at the end of the validity period
for _, candidateChain := range chains {
opts = x509.VerifyOptions{
Roots: x509.NewCertPool(),
Expand Down

0 comments on commit 7c2460e

Please sign in to comment.