From b8a8e56d4a311f15060efcd455c444949b2d20b9 Mon Sep 17 00:00:00 2001 From: Rasmus Dahlberg Date: Fri, 30 Oct 2020 13:27:05 +0100 Subject: started refactoring add-entry code path according to doc --- reqres.go | 99 +++++++++++++++++++++++++-------------------------------------- 1 file changed, 39 insertions(+), 60 deletions(-) (limited to 'reqres.go') diff --git a/reqres.go b/reqres.go index 66e07b8..59709cf 100644 --- a/reqres.go +++ b/reqres.go @@ -4,6 +4,7 @@ import ( "fmt" "strconv" + stdtls "crypto/tls" "crypto/x509" "encoding/base64" "encoding/json" @@ -16,9 +17,10 @@ import ( // AddEntryRequest is a collection of add-entry input parameters type AddEntryRequest struct { - Item string `json:"item"` // base64-encoded StItem - Signature string `json:"signature"` // base64-encoded DigitallySigned - Certificate string `json:"certificate"` // base64-encoded X.509 certificate + Item string `json:"item"` // base64-encoded StItem + Signature string `json:"signature"` // base64-encoded DigitallySigned + SignatureScheme uint16 `json:"signature_scheme"` // RFC 8446, ยง4.2.3 + Chain []string `json:"chain"` // base64-encoded X.509 certificates } // GetEntriesRequest is a collection of get-entry input parameters @@ -48,24 +50,46 @@ type GetEntryResponse struct { } // NewAddEntryRequest parses and sanitizes the JSON-encoded add-entry -// parameters from an incoming HTTP post. The resulting AddEntryRequest is -// well-formed, but not necessarily trusted (further sanitization is needed). -func NewAddEntryRequest(r *http.Request) (AddEntryRequest, error) { - var ret AddEntryRequest - if err := UnpackJsonPost(r, &ret); err != nil { - return ret, err +// parameters from an incoming HTTP post. The serialized leaf value and +// associated appendix is returned if the submitted data is valid: well-formed, +// signed using a supported scheme, and chains back to a valid trust anchor. +func NewAddEntryRequest(lp *LogParameters, r *http.Request) ([]byte, []byte, error) { + var entry AddEntryRequest + if err := UnpackJsonPost(r, &entry); err != nil { + return nil, nil, err } - item, err := StItemFromB64(ret.Item) + item, err := StItemFromB64(entry.Item) if err != nil { - return ret, fmt.Errorf("failed decoding StItem: %v", err) + return nil, nil, fmt.Errorf("StItem(%s): %v", item.Format, err) } if item.Format != StFormatChecksumV1 { - return ret, fmt.Errorf("invalid StItem format: %s", item.Format) + return nil, nil, fmt.Errorf("invalid StItem format: %s", item.Format) + } // note that decode would have failed if invalid checksum/package length + + chain, err := buildChainFromB64List(lp, entry.Chain) + if err != nil { + return nil, nil, fmt.Errorf("invalid certificate chain: %v", err) + } // the final entry in chain is a valid trust anchor + + signature, err := base64.StdEncoding.DecodeString(entry.Signature) + if err != nil { + return nil, nil, fmt.Errorf("invalid signature encoding: %v", err) + } + serialized, err := tls.Marshal(item) + if err != nil { + return nil, nil, fmt.Errorf("failed tls marshaling StItem(%s): %v", item.Format, err) + } + if err := verifySignature(lp, chain[0], stdtls.SignatureScheme(entry.SignatureScheme), serialized, signature); err != nil { + return nil, nil, fmt.Errorf("invalid signature: %v", err) + } + + extra, err := tls.Marshal(NewAppendix(chain, signature, entry.SignatureScheme)) + if err != nil { + return nil, nil, fmt.Errorf("failed tls marshaling appendix: %v", err) } - // TODO: verify that we got a checksum length - // TODO: verify that we got a signature and certificate - return ret, nil + + return serialized, extra, nil } // NewGetEntriesRequest parses and sanitizes the URL-encoded get-entries @@ -173,51 +197,6 @@ func NewGetAnchorsResponse(anchors []*x509.Certificate) []string { return certificates } -// VerifyAddEntryRequest determines whether a well-formed AddEntryRequest should -// be inserted into the log. The corresponding leaf and appendix is returned. -func VerifyAddEntryRequest(ld *LogParameters, r AddEntryRequest) ([]byte, []byte, error) { - item, err := StItemFromB64(r.Item) - if err != nil { - return nil, nil, fmt.Errorf("failed decoding StItem: %v", err) - } - - leaf, err := tls.Marshal(item) - if err != nil { - return nil, nil, fmt.Errorf("failed tls marshaling StItem: %v", err) - } // leaf is the serialized data that should be added to the tree - - c, err := base64.StdEncoding.DecodeString(r.Certificate) - if err != nil { - return nil, nil, fmt.Errorf("failed decoding certificate: %v", err) - } - certificate, err := x509.ParseCertificate(c) - if err != nil { - return nil, nil, fmt.Errorf("failed decoding certificate: %v", err) - } // certificate is the end-entity certificate that signed leaf - - chain, err := VerifyChain(ld, certificate) - if err != nil { - return nil, nil, fmt.Errorf("chain verification failed: %v", err) - } // chain is a valid path to some trust anchor - - signature, err := base64.StdEncoding.DecodeString(r.Signature) - if err != nil { - return nil, nil, fmt.Errorf("failed decoding signature: %v", err) - } - if err := VerifySignature(leaf, signature, certificate); err != nil { - return nil, nil, fmt.Errorf("signature verification failed: %v", err) - } // signature is valid for certificate - - // TODO: update doc of what signature "is", i.e., w/e x509 does - // TODO: doc in markdown/api.md what signature schemes we expect - appendix, err := tls.Marshal(NewAppendix(chain, signature)) - if err != nil { - return nil, nil, fmt.Errorf("failed tls marshaling appendix: %v", err) - } - - return leaf, appendix, nil -} - // UnpackJsonPost unpacks a json-encoded HTTP POST request into `unpack` func UnpackJsonPost(r *http.Request, unpack interface{}) error { body, err := ioutil.ReadAll(r.Body) -- cgit v1.2.3