-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
444 additions
and
177 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// Copyright 2024 The Tessera authors. All Rights Reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package tessera | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/transparency-dev/trillian-tessera/ctonly" | ||
) | ||
|
||
// Storage described the expected functions from Tessera storage implementations. | ||
type Storage interface { | ||
// Add should duably assign an index to the provided Entry, and return it. | ||
// | ||
// Implementations MUST call MarshalBundleData method on the entry before persisting/integrating it. | ||
Add(context.Context, *Entry) (uint64, error) | ||
} | ||
|
||
// NewCertificateTransparencySequencedWriter returns a function which knows how to add a CT-specific entry type to the log. | ||
// | ||
// This entry point MUST ONLY be used for CT logs participating in the CT ecosystem. | ||
// It should not be used as the basis for any other/new transparency application as this protocol: | ||
// a) embodies some techniques which are not considered to be best practice (it does this to retain backawards-compatibility with RFC6962) | ||
// b) is not compatible with the https://c2sp.org/tlog-tiles API which we _very strongly_ encourage you to use instead. | ||
// | ||
// Returns the assigned index in the log, or an error. | ||
func NewCertificateTransparencySequencedWriter(s Storage) func(context.Context, *ctonly.Entry) (uint64, error) { | ||
return func(ctx context.Context, e *ctonly.Entry) (uint64, error) { | ||
return s.Add(ctx, convertCTEntry(e)) | ||
} | ||
} | ||
|
||
// convertCTEntry returns an Entry struct which will do the right thing for CT Static API logs. | ||
// | ||
// This MUST NOT be used for any other purpose. | ||
func convertCTEntry(e *ctonly.Entry) *Entry { | ||
r := &Entry{} | ||
r.internal.Identity = e.Identity() | ||
r.marshalForBundle = func(idx uint64) []byte { | ||
r.internal.LeafHash = e.MerkleLeafHash(idx) | ||
r.internal.Data = e.LeafData(idx) | ||
return r.internal.Data | ||
} | ||
|
||
return r | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
// Copyright 2024 The Tessera authors. All Rights Reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
// | ||
// Original source: https://github.com/FiloSottile/sunlight/blob/main/tile.go | ||
// | ||
// # Copyright 2023 The Sunlight Authors | ||
// | ||
// Permission to use, copy, modify, and/or distribute this software for any | ||
// purpose with or without fee is hereby granted, provided that the above | ||
// copyright notice and this permission notice appear in all copies. | ||
// | ||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
|
||
// Package ctonly has support for CT Tiles API. | ||
// | ||
// This code should not be reused outside of CT. | ||
// Most of this code came from Filipo's Sunlight implementation of https://c2sp.org/ct-static-api. | ||
package ctonly | ||
|
||
import ( | ||
"crypto/sha256" | ||
"errors" | ||
|
||
"github.com/transparency-dev/merkle/rfc6962" | ||
"golang.org/x/crypto/cryptobyte" | ||
) | ||
|
||
// Entry represents a CT log entry. | ||
type Entry struct { | ||
Timestamp uint64 | ||
IsPrecert bool | ||
Certificate []byte | ||
Precertificate []byte | ||
PrecertSigningCert []byte | ||
IssuerKeyHash []byte | ||
} | ||
|
||
// LeafData returns the data which should be added to an entry bundle for this entry. | ||
// | ||
// Note that this will include data which IS NOT directly committed to by the entry's | ||
// MerkleLeafHash. | ||
func (c Entry) LeafData(idx uint64) []byte { | ||
b := cryptobyte.NewBuilder([]byte{}) | ||
b.AddUint64(uint64(c.Timestamp)) | ||
if !c.IsPrecert { | ||
b.AddUint16(0 /* entry_type = x509_entry */) | ||
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { | ||
b.AddBytes(c.Certificate) | ||
}) | ||
} else { | ||
b.AddUint16(1 /* entry_type = precert_entry */) | ||
b.AddBytes(c.IssuerKeyHash[:]) | ||
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { | ||
b.AddBytes(c.Certificate) | ||
}) | ||
} | ||
addExtensions(b, idx) | ||
if c.IsPrecert { | ||
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { | ||
b.AddBytes(c.Precertificate) | ||
}) | ||
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { | ||
b.AddBytes(c.PrecertSigningCert) | ||
}) | ||
} | ||
return b.BytesOrPanic() | ||
} | ||
|
||
// MerkleLeafHash returns the RFC6962 leaf hash for this entry. | ||
// | ||
// Note that we embed an SCT extension which captures the index of the entry in the log according to | ||
// the mechanism specified in https://c2sp.org/ct-static-api. | ||
func (c Entry) MerkleLeafHash(leafIndex uint64) []byte { | ||
b := &cryptobyte.Builder{} | ||
b.AddUint8(0 /* version = v1 */) | ||
b.AddUint8(0 /* leaf_type = timestamped_entry */) | ||
b.AddUint64(uint64(c.Timestamp)) | ||
if !c.IsPrecert { | ||
b.AddUint16(0 /* entry_type = x509_entry */) | ||
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { | ||
b.AddBytes(c.Certificate) | ||
}) | ||
} else { | ||
b.AddUint16(1 /* entry_type = precert_entry */) | ||
b.AddBytes(c.IssuerKeyHash[:]) | ||
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { | ||
b.AddBytes(c.Certificate) | ||
}) | ||
} | ||
addExtensions(b, leafIndex) | ||
return rfc6962.DefaultHasher.HashLeaf(b.BytesOrPanic()) | ||
} | ||
|
||
func (c Entry) Identity() []byte { | ||
var r [sha256.Size]byte | ||
if c.IsPrecert { | ||
r = sha256.Sum256(c.Precertificate) | ||
} else { | ||
r = sha256.Sum256(c.Certificate) | ||
} | ||
return r[:] | ||
} | ||
|
||
func addExtensions(b *cryptobyte.Builder, leafIndex uint64) { | ||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { | ||
ext, err := extensions{LeafIndex: leafIndex}.Marshal() | ||
if err != nil { | ||
b.SetError(err) | ||
return | ||
} | ||
b.AddBytes(ext) | ||
}) | ||
} | ||
|
||
// extensions is the CTExtensions field of SignedCertificateTimestamp and | ||
// TimestampedEntry, according to c2sp.org/static-ct-api. | ||
type extensions struct { | ||
LeafIndex uint64 | ||
} | ||
|
||
func (c extensions) Marshal() ([]byte, error) { | ||
// enum { | ||
// leaf_index(0), (255) | ||
// } ExtensionType; | ||
// | ||
// struct { | ||
// ExtensionType extension_type; | ||
// opaque extension_data<0..2^16-1>; | ||
// } Extension; | ||
// | ||
// Extension CTExtensions<0..2^16-1>; | ||
// | ||
// uint8 uint40[5]; | ||
// uint40 LeafIndex; | ||
|
||
b := &cryptobyte.Builder{} | ||
b.AddUint8(0 /* extension_type = leaf_index */) | ||
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { | ||
if c.LeafIndex >= 1<<40 { | ||
b.SetError(errors.New("leaf_index out of range")) | ||
return | ||
} | ||
addUint40(b, uint64(c.LeafIndex)) | ||
}) | ||
return b.Bytes() | ||
} | ||
|
||
// addUint40 appends a big-endian, 40-bit value to the byte string. | ||
func addUint40(b *cryptobyte.Builder, v uint64) { | ||
b.AddBytes([]byte{byte(v >> 32), byte(v >> 24), byte(v >> 16), byte(v >> 8), byte(v)}) | ||
} |
Oops, something went wrong.