ctclient: add methods for submitting entries

This commit is contained in:
Andrew Ayer
2025-09-16 08:59:56 -04:00
parent 4bad3fd315
commit cf7371ec2c
4 changed files with 76 additions and 0 deletions

View File

@@ -22,6 +22,8 @@ import (
"net/http"
"net/url"
"time"
"software.sslmate.com/src/certspotter/cttypes"
)
var UserAgent = ""
@@ -108,3 +110,55 @@ func getRoots(ctx context.Context, httpClient *http.Client, logURL *url.URL) ([]
}
return parsedResponse.Certificates, nil
}
func addChainOrPreChain(ctx context.Context, httpClient *http.Client, logURL *url.URL, isPreChain bool, chain [][]byte) (*cttypes.SignedCertificateTimestamp, error) {
var endpoint string
if isPreChain {
endpoint = "/ct/v1/add-pre-chain"
} else {
endpoint = "/ct/v1/add-chain"
}
fullURL := logURL.JoinPath(endpoint).String()
requestBody, err := json.Marshal(struct {
Chain [][]byte `json:"chain"`
}{
Chain: chain,
})
if err != nil {
return nil, err
}
request, err := http.NewRequestWithContext(ctx, http.MethodPost, fullURL, bytes.NewReader(requestBody))
if err != nil {
return nil, err
}
request.Header.Set("User-Agent", UserAgent)
request.Header.Set("Content-Type", "application/json")
if httpClient == nil {
httpClient = defaultHTTPClient
}
response, err := httpClient.Do(request)
if err != nil {
return nil, err
}
responseBody, err := io.ReadAll(response.Body)
response.Body.Close()
if err != nil {
return nil, fmt.Errorf("Post %q: error reading response: %w", fullURL, err)
}
if response.StatusCode != 200 {
return nil, fmt.Errorf("Post %q: %s (%q)", fullURL, response.Status, bytes.TrimSpace(responseBody))
}
sct := new(cttypes.SignedCertificateTimestamp)
if err := json.Unmarshal(responseBody, sct); err != nil {
return nil, fmt.Errorf("Post %q: error parsing response JSON: %w", fullURL, err)
}
return sct, nil
}

View File

@@ -16,6 +16,12 @@ import (
"software.sslmate.com/src/certspotter/merkletree"
)
type WritableLog interface {
AddChain(context.Context, [][]byte) (*cttypes.SignedCertificateTimestamp, error)
AddPreChain(context.Context, [][]byte) (*cttypes.SignedCertificateTimestamp, error)
GetRoots(context.Context) ([][]byte, error)
}
type Log interface {
GetSTH(context.Context) (*cttypes.SignedTreeHead, string, error)
GetRoots(context.Context) ([][]byte, error)

View File

@@ -31,6 +31,14 @@ type RFC6962LogEntry struct {
Extra_data []byte `json:"extra_data"`
}
func (ctlog *RFC6962Log) AddChain(ctx context.Context, chain [][]byte) (*cttypes.SignedCertificateTimestamp, error) {
return addChainOrPreChain(ctx, ctlog.HTTPClient, ctlog.URL, false, chain)
}
func (ctlog *RFC6962Log) AddPreChain(ctx context.Context, chain [][]byte) (*cttypes.SignedCertificateTimestamp, error) {
return addChainOrPreChain(ctx, ctlog.HTTPClient, ctlog.URL, true, chain)
}
func (ctlog *RFC6962Log) GetSTH(ctx context.Context) (*cttypes.SignedTreeHead, string, error) {
fullURL := ctlog.URL.JoinPath("/ct/v1/get-sth").String()
sth := new(cttypes.SignedTreeHead)

View File

@@ -46,6 +46,14 @@ type StaticLogEntry struct {
chain [][32]byte
}
func (ctlog *StaticLog) AddChain(ctx context.Context, chain [][]byte) (*cttypes.SignedCertificateTimestamp, error) {
return addChainOrPreChain(ctx, ctlog.HTTPClient, ctlog.SubmissionURL, false, chain)
}
func (ctlog *StaticLog) AddPreChain(ctx context.Context, chain [][]byte) (*cttypes.SignedCertificateTimestamp, error) {
return addChainOrPreChain(ctx, ctlog.HTTPClient, ctlog.SubmissionURL, true, chain)
}
func (ctlog *StaticLog) GetSTH(ctx context.Context) (*cttypes.SignedTreeHead, string, error) {
fullURL := ctlog.MonitoringURL.JoinPath("/checkpoint").String()
responseBody, err := get(ctx, ctlog.HTTPClient, fullURL)