mtc

package module
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 24, 2025 License: BSD-3-Clause Imports: 22 Imported by: 2

README

Merkle Tree Certificates for TLS

🚨 Merkle Tree Certificates (MTC) is a moving target.

Implementation of Merkle Tree Certificates for TLS in Go. This contains a Certification Authority (CA), Mirror, and code to verify certificates. This does not contain integration with TLS (yet) or the ACME bits (yet). At the moment we differ from -04 and main branch of the specification, by including some unmerged PRs.

Demo

For a proper introduction and motivation, check out the draft specification and David's TLS working group presentation at IETF116.

Merkle Tree Certificates is an optimisation to the WebPKI (including Certificate Transparency) motivated by the large sizes of typical post-quantum signatures and public keys, to reduce the number of keys and signatures required for the common case where

  1. Certificate issuance does not have to be immediate. For instance, because a certificate can be requested ahead of time for an existing domain by an ACME client like certbot.

  2. The relying party (eg. browser) has a trusted update mechanism. There are also several ways to use MTC without trusted update mechanism, with various trade-offs: see the Relying Party Policy section of the specification.

If we're not in this case (which is estimated to be less than 0.1% of the time), then we fall back to regular X.509 certificates.

Intermezzo: mtc commandline tool

To play around with MTC, you can install the mtc commandline tool:

$ go install github.com/bwesterb/mtc/cmd/[email protected]
Assertions

In MTC CAs certify assertions, which bind a subject to a claim. An informal example of an assertion is:

For TLS, you can trust the P-256 public key a02342ff2…23ef when visiting example.com or 198.51.100.60.

The first part (TLS and the public key) is the subject, and the latter (domain and IP) are the claim. Roughly, an assertion is like a certificate without the signature.

You can create a request for an assertion to be signed with the mtc new-assertion-request command. First, let's quickly create a P-256 public key to play with.

$ openssl ecparam -name prime256v1 -genkey -out p256.priv
$ openssl ec -in p256.priv -pubout -out p256.pub

Now we create an assertion that this P-256 public key should be valid for example.com and 198.51.100.60, and write it to the my-asr.

$ mtc new-assertion-request --tls-pem p256.pub --dns example.com --ip4 198.51.100.60 -o my-asr

Let's check it using mtc inspect:

$ mtc inspect assertion-request my-asr
checksum         2024bdbffe399acca37d299a03c047aa33ef596ae471c17698a0566d00951bd9
not_after        unset
subject_type     TLS
signature_scheme p256
public_key_hash  20b57b9c55dab26db14fb6cc801b7d7294cbf448abb1196e1ffc19d73013498a
dns              [example.com]
ip4              [198.51.100.60]
evidence-list (0 entries)

An assertion request can contain two bits of extra information besides the assertion itself. First is a not_after field that limits the validity of the assertion when published. The second is optional "evidence" that's published alongside the assertions. In the future this could for instance be used for serialized DNSSEC proofs.

We can also create an assertion request derived from an existing X.509 certificate at a TLS server using the -X flag:

$ mtc new-assertion-request -X example.com:443 | mtc inspect assertion-request
checksum         015d4da06412b4e48f8d93bcbe7bbf43c4684579322cbfbc88d8b653bb2f7e51
not_after        unset
subject_type     TLS
signature_scheme p256
public_key_hash  8d566a5407ab85b413925911c4ce6b13013516006fa8568bf2ec58b9abe04af1
dns              [example.com]
dns_wildcard     [example.com]
evidence-list (1 entries)
umbilical
 certificate 0
  subject    CN=*.example.com,O=Internet Corporation for Assigned Names and Numbers,L=Los Angeles,ST=California,C=US
  issuer     CN=DigiCert Global G3 TLS ECC SHA384 2020 CA1,O=DigiCert Inc,C=US
  serial_no  ad893bafa68b0b7fb7a404f06ecaf9a
  not_before 2025-01-15 00:00:00 +0000 UTC
  not_after  2026-01-15 23:59:59 +0000 UTC
 certificate 1
  subject    CN=DigiCert Global G3 TLS ECC SHA384 2020 CA1,O=DigiCert Inc,C=US
  issuer     CN=DigiCert Global Root G3,OU=www.digicert.com,O=DigiCert Inc,C=US
  serial_no  b00e92d4d6d731fca3059c7cb1e1886
  not_before 2021-04-14 00:00:00 +0000 UTC
  not_after  2031-04-13 23:59:59 +0000 UTC
Batches, merkle trees and signed validity windows

An MTCA doesn't give you a certificate for an assertion request immediately. Instead, assertions are queued and issued in batches with a fixed rhythm, for instance a batch is issued once every hour. All assertions in a single batch by default are valid for the same period of time, the validity window, which is, for instance, two weeks. The CA publishes these batches publicly over HTTP.

For each batch, the CA computes a Merkle tree. This condenses all the assertions in that batch into a single tree head hash. For every batch, the CA signs that tree head together with all the tree heads of the currently valid batches. This signature, together with those signed tree heads is called the signed validity window for that batch, which is published alongside the assertions.

Creating a CA

Let's create an MTC CA.

$ mtc ca new --batch-duration 5m --lifetime 1h 62253.12.15 ca.example.com/path

This creates a new MTC CA in the current working directory. It's configured to issue a batch every 5 minutes, and for each batch to be valid for an hour. For a real CA we'd want batch durations in the order of an hour, and a lifetime of a week or two. In this demo we shorten things a bit, so we don't have to wait too long.

The CA is configured to be hosted at https://ca.example.com/path and to be identified by the trust anchor identifier 62253.12.15. You can get your own by requesting a private enterprise number here.

Let's have a look at the files created:

$ find .
.
./signing.key
./www
./www/mtc
./www/mtc/v04b
./www/mtc/v04b/ca-params
./www/mtc/v04b/batches
./queue
./tmp

The signing.key file contains the private key of the keypair used by the CA.

The www folder contains the files that have to be served at https://ca.example.com/path. At the moment, the only file of interest is ca-params, which contains the information about the CA:

$ mtc inspect ca-params www/mtc/v04b/ca-params
issuer                 62253.12.15
start_time             1745420554 2025-04-23 15:02:34 +0000 UTC
batch_duration         300        5m0s
life_time              3600       1h0m0s
storage_window_size    24         2h0m0s
validity_window_size   12
server_prefix          ca.example.com/path
public_key fingerprint ml-dsa-87:84489bcb42b411a85d163ae95e7deb92b106a75840819a985e44d0e01ae3238e

The batches folder is empty, because there are no batches issued yet.

The queue file contains the assertion requests that will be fulfilled during the next issuance.

Issuing our first batch

Let's issue our first assertion. We can read the assertion request from disk we've created earlier with mtc new-assertion-request:

$ mtc ca queue -i my-asr 
$ mtc ca show-queue
not_after        2025-04-23 16:02:33 +0000 UTC
subject_type     TLS
signature_scheme p256
public_key_hash  20b57b9c55dab26db14fb6cc801b7d7294cbf448abb1196e1ffc19d73013498a
dns              [example.com]
ip4              [198.51.100.60]
evidence-list (0 entries)

Total number of assertion requests in queue: 1

We can also queue an assertion request ad hoc:

$ mtc ca queue --tls-pem p256.pub -d other.example.com -d second.example.com
$ mtc ca show-queue | tail -n 10
not_after        2025-04-23 16:02:33 +0000 UTC
subject_type     TLS
signature_scheme p256
public_key_hash  20b57b9c55dab26db14fb6cc801b7d7294cbf448abb1196e1ffc19d73013498a
dns              [example.com]
ip4              [198.51.100.60]
evidence-list (0 entries)

Total number of assertion requests in queue: 2

Let's issue our first batch.

$ mtc ca issue
2025/04/23 17:05:20 INFO Starting issuance time=2025-04-23T15:05:20.664Z
2025/04/23 17:05:20 INFO Current state expectedStored=0 expectedActive=0 existingBatches=⌀
2025/04/23 17:05:20 INFO To issue batches=0

And let's check:

$ find .
.
./signing.key
./www
./www/mtc
./www/mtc/v04b
./www/mtc/v04b/ca-params
./www/mtc/v04b/batches
./www/mtc/v04b/batches/0
./www/mtc/v04b/batches/0/validity-window
./www/mtc/v04b/batches/0/tree
./www/mtc/v04b/batches/0/entries
./www/mtc/v04b/batches/0/evidence
./www/mtc/v04b/batches/0/index
./www/mtc/v04b/batches/latest
./queue
./tmp

We see batch 0 has been created. latest is a symlink to to 0.

Now, let's have a look at the batch. The entries file is essentially the list of assertions: the difference between a regular assertion and an entry is that with an entry, the public key has been replaced by the hash of the public key.

$ mtc inspect entries www/mtc/v04b/batches/0/entries
key              0b65c8a5f69e88fd1eb58dff4d317f6173bd31773e14d99ace88a2aa7062fdd9
not_after        2025-04-23 16:02:33 +0000 UTC
subject_type     TLS
signature_scheme p256
public_key_hash  20b57b9c55dab26db14fb6cc801b7d7294cbf448abb1196e1ffc19d73013498a
dns              [other.example.com second.example.com]

key              78b5ccc905b693659bf6581011f8efb17fd7aedf9ca70a196a22923f560feeca
not_after        2025-04-23 16:02:33 +0000 UTC
subject_type     TLS
signature_scheme p256
public_key_hash  20b57b9c55dab26db14fb6cc801b7d7294cbf448abb1196e1ffc19d73013498a
dns              [example.com]
ip4              [198.51.100.60]

Total number of entries: 2

The validity-window is the signed validity window: the tree heads of the currently valid batches:

$ mtc inspect -ca-params www/mtc/v04b/ca-params validity-window www/mtc/v04b/batches/0/validity-window               
signature       ✅
batch_number    0
tree_heads[0]   043bc6b0e49a085f2370b2e0f0876d154c2e8d8fe049077dbad118a363580345
tree_heads[-1]  a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf
tree_heads[-2]  a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf
tree_heads[-3]  a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf
tree_heads[-4]  a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf
tree_heads[-5]  a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf
tree_heads[-6]  a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf
tree_heads[-7]  a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf
tree_heads[-8]  a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf
tree_heads[-9]  a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf
tree_heads[-10] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf
tree_heads[-11] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf

We need to pass the ca-params file to be able to parse the file, and check the signature therein. (As not all previous batches exist, they use the placeholder value for an empty tree.)

The tree file contains the Merkle tree.

$ mtc inspect tree www/mtc/v04b/batches/0/tree 
number of leaves 2
number of nodes  3
tree head        043bc6b0e49a085f2370b2e0f0876d154c2e8d8fe049077dbad118a363580345

The evidence file contains the optional evidence that can be provided with the assertion request. We did not pass any, so they're empty:

$ mtc inspect evidence www/mtc/v04b/batches/0/evidence
evidence-list (0 entries)

evidence-list (0 entries)

Total number of evidence lists: 2

Finally, the index file allows a quick lookup in entries (and evidence) by key (hash of the assertion):

$ mtc inspect index www/mtc/v04b/batches/0/index
                                                             key   seqno  offset
0b65c8a5f69e88fd1eb58dff4d317f6173bd31773e14d99ace88a2aa7062fdd9       0       0       0
78b5ccc905b693659bf6581011f8efb17fd7aedf9ca70a196a22923f560feeca       1      91       3

total number of entries: 2
Issuing more batches

As we just issued a new batch, we need to wait a while before the next batch is ready to issue.

Let's queue some more assertions, wait a bit, and issue a new batch.

$ mtc ca queue --tls-pem p256.pub -d 1.example.com
$ mtc ca queue --tls-pem p256.pub -d 2.example.com
$ mtc ca queue --tls-pem p256.pub -d 3.example.com
$ mtc ca issue
2025/04/23 17:12:45 INFO Starting issuance time=2025-04-23T15:12:45.869Z
2025/04/23 17:12:45 INFO Current state expectedStored=0,…,2 expectedActive=0,…,2 existingBatches=0
2025/04/23 17:12:45 INFO To issue batches=1,2
$ find .
.
./signing.key
./www
./www/mtc
./www/mtc/v04b
./www/mtc/v04b/ca-params
./www/mtc/v04b/batches
./www/mtc/v04b/batches/0
./www/mtc/v04b/batches/0/validity-window
./www/mtc/v04b/batches/0/tree
./www/mtc/v04b/batches/0/entries
./www/mtc/v04b/batches/0/evidence
./www/mtc/v04b/batches/0/latest
./www/mtc/v04b/batches/0/index
./www/mtc/v04b/batches/latest
./www/mtc/v04b/batches/1
./www/mtc/v04b/batches/1/validity-window
./www/mtc/v04b/batches/1/tree
./www/mtc/v04b/batches/1/entries
./www/mtc/v04b/batches/1/evidence
./www/mtc/v04b/batches/1/index
./www/mtc/v04b/batches/2
./www/mtc/v04b/batches/2/validity-window
./www/mtc/v04b/batches/2/tree
./www/mtc/v04b/batches/2/entries
./www/mtc/v04b/batches/2/evidence
./www/mtc/v04b/batches/2/index
./queue
./tmp

As we waited a bit longer, the current batch is 2, which will contain the queued assertions. The batch 1 in between will be empty. Now latest points to 2, and its signed validity window is more interesting.

$ mtc inspect -ca-params www/mtc/v04b/ca-params validity-window www/mtc/v04b/batches/1/validity-window
signature      ✅
batch_number   2
tree_heads[2]  03a95ba3c354e2b0eb4bea9b111dbc8b97e2c90b85ddcc63d4b635b16f77005d
tree_heads[1]  7ceda88ec6c8e34ecacde47588e2605fb86192b94ca96cb897fa6ff442198c8c
tree_heads[0]  043bc6b0e49a085f2370b2e0f0876d154c2e8d8fe049077dbad118a363580345
tree_heads[-1] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf
tree_heads[-2] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf
tree_heads[-3] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf
tree_heads[-4] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf
tree_heads[-5] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf
tree_heads[-6] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf
tree_heads[-7] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf
tree_heads[-8] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf
tree_heads[-9] a7b081f10c7116c30781a957c3f52625c4d831c8d61ceea021db101ab3c901cf
Creating a certificate

In MTC, a certificate is an assertion together with a trust anchor identifier (to identify the CA), and an authentication path in the Merkle tree. Let's create one for our initial assertion.

$ mtc ca cert -i my-asr -o my-cert

If we inspect the certificate, it can recompute the root from the authentication path and CA parameters:

$ mtc inspect -ca-params www/mtc/v04b/ca-params cert my-cert
subject_type     TLS
signature_scheme p256
public_key_hash  20b57b9c55dab26db14fb6cc801b7d7294cbf448abb1196e1ffc19d73013498a
dns              [example.com]
ip4              [198.51.100.60]

proof_type           merkle_tree_sha256
CA TAI               62253.12.15
Batch number         0
index                1
recomputed tree head 043bc6b0e49a085f2370b2e0f0876d154c2e8d8fe049077dbad118a363580345
authentication path
 8964f010faa9e499b21917f8792b541b7b1ac19f313a5d53094c698c2edc330b

This is indeed the root of batch 0, and so this certificate is valid.

Verify certificate

To automate this, there is the mtc verify command that takes a certificate, the CA parameters, and a signed validity window.

$ mtc verify -ca-params www/mtc/v04b/ca-params -validity-window www/mtc/v04b/batches/1/validity-window my-cert
$ echo $?
0

Status code 0 means verification succeeded.

For transparency, you should not get the signed validity window directly from the CA, but rather from one or more mirrors (see below).

Run CA as server

An Merkle Tree CA can be run just from the commandline, but it's often more convenient to run it as a server. To start the server, run:

$ mtc ca serve -listen-addr localhost:8080

This will accept HTTP requests on localhost:8080 and serve the static files. It will also accept queue requests; periodically issue new batches; and return issued certificates.

Get and inspect CA parameters.

$ curl -s "http://localhost:8080/mtc/v04b/ca-params" -o ca-params
$ mtc inspect ca-params ca-params
issuer                 62253.12.15
start_time             1745420554 2025-04-23 15:02:34 +0000 UTC
batch_duration         300        5m0s
life_time              3600       1h0m0s
storage_window_size    24         2h0m0s
validity_window_size   12
server_prefix          ca.example.com/path
public_key fingerprint ml-dsa-87:84489bcb42b411a85d163ae95e7deb92b106a75840819a985e44d0e01ae3238e

Queue up the assertion created in above.

$ curl -X POST "http://localhost:8080/ca/queue" --data-binary "@my-asr" -w "%{http_code}"
200

After it's been issued, we can get the certificate via the /ca/cert endpoint:

$ curl -X POST "http://localhost:8080/ca/cert" --data-binary "@my-asr" -o my-cert
$ mtc inspect -ca-params ca-params cert my-cert
subject_type     TLS
signature_scheme p256
public_key_hash  20b57b9c55dab26db14fb6cc801b7d7294cbf448abb1196e1ffc19d73013498a
dns              [example.com]
ip4              [198.51.100.60]

proof_type           merkle_tree_sha256
CA TAI               62253.12.15
Batch number         0
index                1
recomputed tree head 043bc6b0e49a085f2370b2e0f0876d154c2e8d8fe049077dbad118a363580345
authentication path
 8964f010faa9e499b21917f8792b541b7b1ac19f313a5d53094c698c2edc330b
Mirroring a CA

We can set up a new mirror with the mtc mirror new command:

$ mtc mirror new ca.example.com/path

This will download the ca-params from https://ca.example.com/path/mtc/v04b/ca-params and set up a directory structure similar to that of a CA:

$ find .
.
./www
./www/mtc
./www/mtc/v04b
./www/mtc/v04b/ca-params
./www/mtc/v04b/batches
./tmp

To bring the mirror up to date with the CA, use the update command:

$ mtc mirror update
2025/04/24 11:54:53 INFO Current state expectedStoredRemote=0 expectedActiveRemote=0 latestRemoteBatch=0 mirroredBatches=⌀
2025/04/24 11:54:53 INFO Fetching batch=0
2025/04/24 11:54:53 INFO Next batch at the earliest in 49s
$ find .
.
./www
./www/mtc
./www/mtc/v04b
./www/mtc/v04b/ca-params
./www/mtc/v04b/batches
./www/mtc/v04b/batches/0
./www/mtc/v04b/batches/0/validity-window
./www/mtc/v04b/batches/0/tree
./www/mtc/v04b/batches/0/entries
./www/mtc/v04b/batches/0/evidence
./www/mtc/v04b/batches/latest
./tmp
Local testing

To make local testing convenient, when you use localhost as server prefix, the mirror will use http instead of https. This allows a quick testing set up as follows:

# Set up a CA in the ca folder
$ mtc ca -p ca new --batch-duration 5m --lifetime 1h 62253.12.15 localhost:8080
$ mtc ca -p ca queue -X example.com:443
$ mtc ca -p ca issue
$ mtc ca -p ca server -listen-addr localhost:8080 &
# Set up a mirror of the CA in the mirror folder
$ mtc mirror -p mirror new localhost:8080
$ mtc mirror -p mirror update

Documentation

Index

Constants

View Source
const (
	HashLen = 32

	// Version of the API we implement. It's close to draft -04, but there
	// are changes on top, hence the "b".
	ApiVersion = "v04b"
)

Variables

View Source
var (
	// ErrTruncated is a parsing error returned when the input seems to have
	// been truncated.
	ErrTruncated = errors.New("Input truncated")

	// ErrExtraBytes is a parsing error returned when there are extraneous
	// bytes at the end of, or within, the data.
	ErrExtraBytes = errors.New("Unexpected extra (internal) bytes")

	// ErrChecksumInvalid is an error returned when a checksum does not
	// match the corresponding data.
	ErrChecksumInvalid = errors.New("Invalid checksum")

	// Used to indicate end of stream for Cursor[T].
	EOF = errors.New("EOF")
)

Functions

func ForEach added in v0.1.2

func ForEach[T any](c Cursor[T], f func(T) error) error

Pull from c and call f on each.

Abort early if f returns an error. Closes c.

func GenerateSigningKeypair

func GenerateSigningKeypair(scheme SignatureScheme) (Signer, Verifier, error)

func TreeNodeCount

func TreeNodeCount(nLeaves uint64) uint

TreeNodeCount returns the number of nodes in the Merkle tree for a batch, which has nLeaves assertions.

func VerifierFingerprint

func VerifierFingerprint(v Verifier) string

Returns [scheme]:sha256

Types

type AbridgedSubject

type AbridgedSubject interface {
	SubjectBase
}

type AbridgedTLSSubject

type AbridgedTLSSubject struct {
	SignatureScheme SignatureScheme
	PublicKeyHash   [HashLen]byte
}

func (*AbridgedTLSSubject) Info

func (s *AbridgedTLSSubject) Info() []byte

func (*AbridgedTLSSubject) Type

func (s *AbridgedTLSSubject) Type() SubjectType

type Assertion

type Assertion struct {
	Subject Subject
	Claims  Claims
}

func (*Assertion) EntryKey added in v0.1.2

func (a *Assertion) EntryKey(out []byte) error

Computes the key a BatchEntry for this assertion would have in the index.

func (*Assertion) MarshalBinary

func (a *Assertion) MarshalBinary() ([]byte, error)

func (*Assertion) UnmarshalBinary

func (a *Assertion) UnmarshalBinary(data []byte) error

type AssertionRequest added in v0.1.2

type AssertionRequest struct {
	Checksum  []byte
	Assertion Assertion
	Evidence  EvidenceList
	NotAfter  time.Time
}

func (*AssertionRequest) Check added in v0.1.2

func (ar *AssertionRequest) Check() error

If set, checks whether the Checksum is correct. If not set, sets the Checksum to the correct value.

func (*AssertionRequest) MarshalBinary added in v0.1.2

func (ar *AssertionRequest) MarshalBinary() ([]byte, error)

func (*AssertionRequest) UnmarshalBinary added in v0.1.2

func (ar *AssertionRequest) UnmarshalBinary(data []byte) error

type Batch

type Batch struct {
	CA     *CAParams
	Number uint32
}

func (*Batch) Anchor

func (batch *Batch) Anchor() TrustAnchorIdentifier

func (*Batch) ComputeTree

func (batch *Batch) ComputeTree(r io.Reader) (*Tree, error)

Convenience function to compute Merkle tree from a stream of BatchEntry from r.

func (*Batch) ComputeTreeHeadFromAuthenticationPath added in v0.1.2

func (batch *Batch) ComputeTreeHeadFromAuthenticationPath(index uint64,
	path []byte, be *BatchEntry) ([]byte, error)

Compute batch tree head from authentication path.

To verify a certificate/proof, use VerifyAuthenticationPath instead.

func (*Batch) NewTreeBuilder added in v0.1.2

func (batch *Batch) NewTreeBuilder() *TreeBuilder

func (*Batch) SignValidityWindow

func (batch *Batch) SignValidityWindow(signer Signer, prevHeads []byte,
	root []byte) (SignedValidityWindow, error)

func (*Batch) ValidityInterval added in v0.1.2

func (batch *Batch) ValidityInterval() (time.Time, time.Time)

ValidityInterval returns the largest closed interval [a,b] in which assertions issued in this batch are valid. That is: for all times x with a ≤ x ≤ b. Note that NotAfter may be smaller than b for some assertions.

func (*Batch) VerifyAuthenticationPath

func (batch *Batch) VerifyAuthenticationPath(index uint64, path, root []byte,
	be *BatchEntry) error

Check validity of authentication path.

Return nil on valid authentication path.

type BatchEntry added in v0.1.2

type BatchEntry struct {
	Subject  AbridgedSubject
	Claims   Claims
	NotAfter time.Time
}

func NewBatchEntry added in v0.1.2

func NewBatchEntry(a Assertion, notAfter time.Time) (ret BatchEntry)

func UnmarshalBatchEntry added in v0.1.2

func UnmarshalBatchEntry(r io.Reader) (*BatchEntry, error)

Unmarshals a single BatchEntry from r.

func (*BatchEntry) Hash added in v0.1.2

func (be *BatchEntry) Hash(out []byte, batch *Batch, index uint64) error

Computes the leaf hash of the BatchEntry in the Merkle tree computed for the batch.

func (*BatchEntry) Key added in v0.1.2

func (be *BatchEntry) Key(out []byte) error

Computes the key of the BatchEntry used in the index.

Note that keys are not unique: we leave out the not_after field when computing the key. This allows us to look up a BatchEntry for some assertion that does not contain the not_after field.

func (*BatchEntry) MarshalBinary added in v0.1.2

func (be *BatchEntry) MarshalBinary() ([]byte, error)

func (*BatchEntry) UnmarshalBinary added in v0.1.2

func (be *BatchEntry) UnmarshalBinary(data []byte) error

type BatchEntryWithOffset added in v0.1.2

type BatchEntryWithOffset struct {
	BatchEntry
	Offset int
}

Same as BatchEntry, but keeps track of offset within stream it was unmarshalled from. Used to create index.

type BatchRange

type BatchRange struct {
	Begin uint32
	End   uint32
}

Range of batch numbers Begin, …, End-1.

func (BatchRange) AreAllPast

func (r BatchRange) AreAllPast(batch uint32) bool

Returns whether each batch in the range is after the given batch

func (BatchRange) Contains

func (r BatchRange) Contains(batch uint32) bool

Returns whether r contains the batch with the given number.

func (BatchRange) Len

func (r BatchRange) Len() int

func (BatchRange) String

func (r BatchRange) String() string

type BikeshedCertificate

type BikeshedCertificate struct {
	Assertion Assertion
	Proof     Proof
}

func (*BikeshedCertificate) MarshalBinary

func (c *BikeshedCertificate) MarshalBinary() ([]byte, error)

func (*BikeshedCertificate) UnmarshalBinary

func (c *BikeshedCertificate) UnmarshalBinary(data []byte, caStore CAStore) error

func (*BikeshedCertificate) Verify added in v0.1.2

func (c *BikeshedCertificate) Verify(opts VerifyOptions) error

Verify is used to verify that a BikeshedCertificate is covered by a validity window. It is the caller's responsibility to verify the validity window was signed by the CA. An error indicates that the certificate does not belong to a batch in the validity window or that the certificate is otherwise invalid.

type CAParams

type CAParams struct {
	Issuer        RelativeOID
	PublicKey     Verifier
	ProofType     ProofType
	StartTime     uint64
	BatchDuration uint64
	Lifetime      uint64

	// ValidityWindowSize is the number of tree heads in each validity
	// window.
	ValidityWindowSize uint64

	StorageWindowSize uint64
	ServerPrefix      string
	EvidencePolicy    EvidencePolicyType
}

CAParams holds the public parameters of a Merkle Tree CA

func (*CAParams) ActiveBatches

func (p *CAParams) ActiveBatches(dt time.Time) BatchRange

Batches that are non-expired, and either issued or ready, at the given time.

func (*CAParams) MarshalBinary

func (p *CAParams) MarshalBinary() ([]byte, error)

func (*CAParams) NewTreeHeads added in v0.1.2

func (p *CAParams) NewTreeHeads(prevHeads, root []byte) ([]byte, error)

Returns TreeHeads from the previous batch's TreeHeads and the new root.

func (*CAParams) NextBatchAt added in v0.1.1

func (p *CAParams) NextBatchAt(dt time.Time) time.Time

Returns the time when the next batch starts.

func (*CAParams) PreEpochTreeHeads added in v0.1.2

func (p *CAParams) PreEpochTreeHeads() []byte

Returns the tree heads of the validity window prior the epoch.

func (*CAParams) StoredBatches

func (p *CAParams) StoredBatches(dt time.Time) BatchRange

Batches that are expected to be available at this CA, at the given time. The last few might not yet have been published.

func (*CAParams) UnmarshalBinary

func (p *CAParams) UnmarshalBinary(data []byte) error

func (*CAParams) Validate

func (p *CAParams) Validate() error

type CAStore added in v0.1.2

type CAStore interface {
	Lookup(oid RelativeOID) *CAParams
}

type ClaimType

type ClaimType uint16
const (
	DnsClaimType ClaimType = iota
	DnsWildcardClaimType
	Ipv4ClaimType
	Ipv6ClaimType
)

type Claims

type Claims struct {
	DNS         []string
	DNSWildcard []string
	IPv4        []net.IP
	IPv6        []net.IP
	Unknown     []UnknownClaim
}

List of claims.

func (*Claims) MarshalBinary

func (c *Claims) MarshalBinary() ([]byte, error)

func (Claims) String

func (c Claims) String() string

func (*Claims) UnmarshalBinary

func (c *Claims) UnmarshalBinary(data []byte) error

type CompressedUmbilicalEvidence added in v0.1.2

type CompressedUmbilicalEvidence [][32]byte

func NewCompressedUmbilicalEvidence added in v0.1.2

func NewCompressedUmbilicalEvidence(certs [][32]byte) (
	CompressedUmbilicalEvidence, error)

func (CompressedUmbilicalEvidence) Chain added in v0.1.2

func (e CompressedUmbilicalEvidence) Chain() [][32]byte

func (CompressedUmbilicalEvidence) Info added in v0.1.2

func (e CompressedUmbilicalEvidence) Info() []byte

func (CompressedUmbilicalEvidence) Type added in v0.1.2

func (*CompressedUmbilicalEvidence) UnmarshalBinary added in v0.1.2

func (e *CompressedUmbilicalEvidence) UnmarshalBinary(buf []byte) error

type Cursor added in v0.1.2

type Cursor[T any] interface {
	// Pull one value and write to out.
	Pull(out T) error

	// Release underlying resources. Closing twice is no-op.
	Close() error
}

Pull-style iterator similar to io.ReadCloser but for general T and only pulls one value at a time. Assumes T is a reference.

func UnmarshalBatchEntries added in v0.1.2

func UnmarshalBatchEntries(r io.Reader) Cursor[*BatchEntry]

Unmarshals BatchEntry from r.

func UnmarshalBatchEntriesWithOffset added in v0.1.2

func UnmarshalBatchEntriesWithOffset(r io.Reader) Cursor[*BatchEntryWithOffset]

Unmarshals BatchEntry from r, keeping note of the offset of each.

func UnmarshalEvidenceLists added in v0.1.2

func UnmarshalEvidenceLists(r io.Reader) Cursor[*EvidenceList]

Unmarshals EvidenceLists from r.

type Evidence added in v0.1.2

type Evidence interface {
	Type() EvidenceType
	Info() []byte
}

type EvidenceList added in v0.1.2

type EvidenceList []Evidence

func UnmarshalEvidenceList added in v0.1.2

func UnmarshalEvidenceList(r io.Reader) (*EvidenceList, error)

Unmarshals single EvidenceList from r.

func (*EvidenceList) MarshalBinary added in v0.1.2

func (el *EvidenceList) MarshalBinary() ([]byte, error)

func (*EvidenceList) UnmarshalBinary added in v0.1.2

func (el *EvidenceList) UnmarshalBinary(data []byte) error

type EvidenceListWithOffset added in v0.1.2

type EvidenceListWithOffset struct {
	EvidenceList
	Offset int
}

Same as EvidenceList, but keeps track of offset within stream it was unmarshalled from. Used to create index.

type EvidencePolicyType added in v0.1.2

type EvidencePolicyType uint16
const (
	// No policy set.
	UnsetEvidencePolicy EvidencePolicyType = iota

	// Policy requiring no evidence to queue an assertion request.
	EmptyEvidencePolicy

	// Policy requiring an X509 chain to an accepted root to queue an assertion request.
	UmbilicalEvidencePolicy
)

type EvidenceType added in v0.1.2

type EvidenceType uint16
const (
	UmbilicalEvidenceType EvidenceType = iota
	CompressedUmbilicalEvidenceType
)

type LocalCAStore added in v0.1.2

type LocalCAStore struct {
	// contains filtered or unexported fields
}

func (*LocalCAStore) Add added in v0.1.2

func (s *LocalCAStore) Add(params CAParams)

func (*LocalCAStore) Lookup added in v0.1.2

func (s *LocalCAStore) Lookup(oid RelativeOID) *CAParams

type MerkleTreeProof

type MerkleTreeProof struct {
	// contains filtered or unexported fields
}

func NewMerkleTreeProof

func NewMerkleTreeProof(batch *Batch, index uint64, notAfter time.Time,
	path []byte) *MerkleTreeProof

func (*MerkleTreeProof) Index

func (p *MerkleTreeProof) Index() uint64

func (*MerkleTreeProof) Info

func (p *MerkleTreeProof) Info() []byte

func (*MerkleTreeProof) NotAfter added in v0.1.2

func (p *MerkleTreeProof) NotAfter() time.Time

func (*MerkleTreeProof) Path

func (p *MerkleTreeProof) Path() []byte

func (*MerkleTreeProof) TrustAnchorIdentifier added in v0.1.2

func (p *MerkleTreeProof) TrustAnchorIdentifier() TrustAnchorIdentifier

type Proof

type Proof interface {
	TrustAnchorIdentifier() TrustAnchorIdentifier
	Info() []byte
	NotAfter() time.Time
}

type ProofType

type ProofType uint16
const (
	MerkleTreeProofType ProofType = iota
)

func (ProofType) String

func (p ProofType) String() string

type RelativeOID added in v0.1.2

type RelativeOID []byte

func (*RelativeOID) Equal added in v0.1.2

func (oid *RelativeOID) Equal(rhs *RelativeOID) bool

func (*RelativeOID) FromSegments added in v0.1.2

func (oid *RelativeOID) FromSegments(segments []uint32) error

func (RelativeOID) MarshalBinary added in v0.1.2

func (oid RelativeOID) MarshalBinary() ([]byte, error)

func (RelativeOID) String added in v0.1.2

func (oid RelativeOID) String() string

func (*RelativeOID) UnmarshalText added in v0.1.2

func (oid *RelativeOID) UnmarshalText(text []byte) error

type SignatureScheme

type SignatureScheme uint16

Copy of tls.SignatureScheme to prevent cycling dependencies

const (
	TLSPSSWithSHA256          SignatureScheme = 0x0804
	TLSPSSWithSHA384          SignatureScheme = 0x0805
	TLSPSSWithSHA512          SignatureScheme = 0x0806
	TLSECDSAWithP256AndSHA256 SignatureScheme = 0x0403
	TLSECDSAWithP384AndSHA384 SignatureScheme = 0x0503
	TLSECDSAWithP521AndSHA512 SignatureScheme = 0x0603
	TLSEd25519                SignatureScheme = 0x0807

	// Just for testing we use ML-DSA-87 with a codepoint in the
	// private use region.
	// For production SLH-DSA-128s would be a better choice.
	TLSMLDSA87 SignatureScheme = 0x0906
)

func SignatureSchemeFromString

func SignatureSchemeFromString(s string) SignatureScheme

func SignatureSchemesFor

func SignatureSchemesFor(pk crypto.PublicKey) []SignatureScheme

Returns valid signature schemes for given public key

func (SignatureScheme) String

func (s SignatureScheme) String() string

type SignedValidityWindow

type SignedValidityWindow struct {
	ValidityWindow
	Signature []byte
}

func (*SignedValidityWindow) MarshalBinary

func (w *SignedValidityWindow) MarshalBinary() ([]byte, error)

func (*SignedValidityWindow) UnmarshalBinary

func (w *SignedValidityWindow) UnmarshalBinary(data []byte, p *CAParams) error

func (*SignedValidityWindow) UnmarshalBinaryWithoutVerification

func (w *SignedValidityWindow) UnmarshalBinaryWithoutVerification(
	data []byte, p *CAParams) error

Like UnmarshalBinary() but doesn't check the signature.

type Signer

type Signer interface {
	Sign(message []byte) []byte
	Scheme() SignatureScheme
	Bytes() []byte
}

Signing private key with specific hash and options.

func UnmarshalSigner

func UnmarshalSigner(scheme SignatureScheme, data []byte) (
	Signer, error)

type Subject

type Subject interface {
	SubjectBase
	Abridge() AbridgedSubject
}

type SubjectBase

type SubjectBase interface {
	Type() SubjectType
	Info() []byte
}

type SubjectType

type SubjectType uint16
const (
	TLSSubjectType SubjectType = iota
)

func (SubjectType) String

func (s SubjectType) String() string

type TLSSubject

type TLSSubject struct {
	// contains filtered or unexported fields
}

func NewTLSSubject

func NewTLSSubject(scheme SignatureScheme, pk crypto.PublicKey) (*TLSSubject, error)

func (*TLSSubject) Abridge

func (s *TLSSubject) Abridge() AbridgedSubject

func (*TLSSubject) Info

func (s *TLSSubject) Info() []byte

func (*TLSSubject) Type

func (s *TLSSubject) Type() SubjectType

func (*TLSSubject) Verifier

func (s *TLSSubject) Verifier() (Verifier, error)

type Tree

type Tree struct {
	// contains filtered or unexported fields
}

Merkle tree built upon the assertions of a batch.

func (*Tree) AuthenticationPath

func (t *Tree) AuthenticationPath(index uint64) ([]byte, error)

Return authentication path proving that the leaf at the given index is included in the Merkle tree.

func (*Tree) Head added in v0.1.2

func (t *Tree) Head() []byte

Return head (root) of the tree

func (*Tree) LeafCount

func (t *Tree) LeafCount() uint64

func (*Tree) NodeCount

func (t *Tree) NodeCount() uint

func (*Tree) UnmarshalBinary

func (t *Tree) UnmarshalBinary(buf []byte) error

func (*Tree) WriteTo

func (t *Tree) WriteTo(w io.Writer) (int64, error)

Write the tree to w

type TreeBuilder added in v0.1.2

type TreeBuilder struct {
	// contains filtered or unexported fields
}

func (*TreeBuilder) Finish added in v0.1.2

func (b *TreeBuilder) Finish() (*Tree, error)

func (*TreeBuilder) Push added in v0.1.2

func (b *TreeBuilder) Push(be *BatchEntry) error

type TrustAnchorIdentifier added in v0.1.2

type TrustAnchorIdentifier struct {
	Issuer      RelativeOID
	BatchNumber uint32
}

A TrustAnchorIdentifier (TAI) is used to identify a CA, or a specific batch.

TAI are OIDs relative to the Private Enterprise Numbers (PEN) arc 1.3.6.1.4.1.

func (TrustAnchorIdentifier) MarshalBinary added in v0.1.2

func (tai TrustAnchorIdentifier) MarshalBinary() ([]byte, error)

func (TrustAnchorIdentifier) String added in v0.1.2

func (tai TrustAnchorIdentifier) String() string

func (*TrustAnchorIdentifier) UnmarshalBinary added in v0.1.2

func (tai *TrustAnchorIdentifier) UnmarshalBinary(buf []byte) error

type UmbilicalEvidence added in v0.1.2

type UmbilicalEvidence []byte

func NewUmbilicalEvidence added in v0.1.2

func NewUmbilicalEvidence(certs []*x509.Certificate) (UmbilicalEvidence, error)

func (UmbilicalEvidence) Chain added in v0.1.2

func (e UmbilicalEvidence) Chain() ([]*x509.Certificate, error)

func (UmbilicalEvidence) Info added in v0.1.2

func (e UmbilicalEvidence) Info() []byte

func (UmbilicalEvidence) RawChain added in v0.1.2

func (e UmbilicalEvidence) RawChain() ([][]byte, error)

func (UmbilicalEvidence) Type added in v0.1.2

type UnknownClaim

type UnknownClaim struct {
	Type ClaimType
	Info []byte
}

Represents a claim we do not how to interpret.

type UnknownEvidence added in v0.1.2

type UnknownEvidence struct {
	// contains filtered or unexported fields
}

func (UnknownEvidence) Info added in v0.1.2

func (e UnknownEvidence) Info() []byte

func (UnknownEvidence) Type added in v0.1.2

func (e UnknownEvidence) Type() EvidenceType

type UnknownProof

type UnknownProof struct {
	// contains filtered or unexported fields
}

func (*UnknownProof) Info

func (p *UnknownProof) Info() []byte

func (*UnknownProof) NotAfter added in v0.1.2

func (p *UnknownProof) NotAfter() time.Time

func (*UnknownProof) TrustAnchorIdentifier added in v0.1.2

func (p *UnknownProof) TrustAnchorIdentifier() TrustAnchorIdentifier

type UnknownSubject

type UnknownSubject struct {
	// contains filtered or unexported fields
}

Used for either an unknown (abridged) subject

func (*UnknownSubject) Abridge

func (s *UnknownSubject) Abridge() AbridgedSubject

func (*UnknownSubject) Info

func (s *UnknownSubject) Info() []byte

func (*UnknownSubject) Type

func (s *UnknownSubject) Type() SubjectType

type ValidityWindow

type ValidityWindow struct {
	// BatchNumber is the batch number of the last tree head.
	BatchNumber uint32
	TreeHeads   []byte
}

func (*ValidityWindow) CurHead added in v0.1.2

func (w *ValidityWindow) CurHead() []byte

Return the tree head recorded for this ValidityWindow's batch.

func (*ValidityWindow) LabeledValdityWindow

func (w *ValidityWindow) LabeledValdityWindow(ca *CAParams) ([]byte, error)

Returns the corresponding marshalled LabeledValdityWindow, which is signed by the CA.

func (*ValidityWindow) MarshalBinary

func (w *ValidityWindow) MarshalBinary() ([]byte, error)

type Verifier

type Verifier interface {
	Verify(message, signature []byte) error
	Scheme() SignatureScheme
	Bytes() []byte
}

Signing public key with specific hash and options.

func NewVerifier

func NewVerifier(scheme SignatureScheme, pk crypto.PublicKey) (
	Verifier, error)

func UnmarshalVerifier

func UnmarshalVerifier(scheme SignatureScheme, data []byte) (
	Verifier, error)

type VerifyOptions added in v0.1.2

type VerifyOptions struct {
	// ValidityWindow is a validity window that covers the certificate. It
	// is the caller's responsibility to verify the validity window was
	// signed by the CA, e.g., by verifying the SignedValidityWindow that
	// contains the validity window.
	ValidityWindow *ValidityWindow

	// CA includes the parameters of the CA that issued the batch
	// containing the certificate.
	CA *CAParams

	// CurrentTime is used to to check if the certificate has expired.
	CurrentTime time.Time
}

VerifyOptions includes parameters for verifying a BikeshedCertificate.

Directories

Path Synopsis
cmd
mtc command
Package umbilical has the temporary logic to back an MTC with an existing X509 certificate chain.
Package umbilical has the temporary logic to back an MTC with an existing X509 certificate chain.
frozencas
frozencas implements a simple file format to store small blobs by their hash.
frozencas implements a simple file format to store small blobs by their hash.
revocation
Package revocation implements the code to check for revocation of X.509 certificates on demand.
Package revocation implements the code to check for revocation of X.509 certificates on demand.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL