func (c *LogClient) AddJSON(data interface{}) (*ct.SignedCertificateTimestamp, error) { req := addJSONRequest{ Data: data, } var resp addChainResponse _, _, err := c.postAndParse(c.uri+AddJSONPath, &req, &resp) if err != nil { return nil, err } rawLogID, err := base64.StdEncoding.DecodeString(resp.ID) if err != nil { return nil, err } rawSignature, err := base64.StdEncoding.DecodeString(resp.Signature) if err != nil { return nil, err } ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(rawSignature)) if err != nil { return nil, err } var logID ct.SHA256Hash copy(logID[:], rawLogID) return &ct.SignedCertificateTimestamp{ SCTVersion: resp.SCTVersion, LogID: logID, Timestamp: resp.Timestamp, Extensions: ct.CTExtensions(resp.Extensions), Signature: *ds}, nil }
// Attempts to add |chain| to the log, using the api end-point specified by // |path|. If provided context expires before submission is complete an // error will be returned. func (c *LogClient) addChainWithRetry(ctx context.Context, ctype ct.LogEntryType, path string, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) { var resp addChainResponse var req addChainRequest for _, link := range chain { req.Chain = append(req.Chain, link) } _, err := c.PostAndParseWithRetry(ctx, path, &req, &resp) if err != nil { return nil, err } ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(resp.Signature)) if err != nil { return nil, err } var logID ct.SHA256Hash copy(logID[:], resp.ID) sct := &ct.SignedCertificateTimestamp{ SCTVersion: resp.SCTVersion, LogID: logID, Timestamp: resp.Timestamp, Extensions: ct.CTExtensions(resp.Extensions), Signature: *ds} err = c.VerifySCTSignature(*sct, ctype, chain) if err != nil { return nil, err } return sct, nil }
// Attempts to add |chain| to the log, using the api end-point specified by // |path|. If provided context expires before submission is complete an // error will be returned. func (c *LogClient) addChainWithRetry(ctx context.Context, path string, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) { var resp addChainResponse var req addChainRequest for _, link := range chain { req.Chain = append(req.Chain, link) } httpStatus := "Unknown" backoffSeconds := 0 done := false for !done { if backoffSeconds > 0 { log.Printf("Got %s, backing-off %d seconds", httpStatus, backoffSeconds) } err := backoffForRetry(ctx, time.Second*time.Duration(backoffSeconds)) if err != nil { return nil, err } if backoffSeconds > 0 { backoffSeconds = 0 } httpResp, _, err := c.postAndParse(c.uri+path, &req, &resp) if err != nil { backoffSeconds = 10 continue } switch { case httpResp.StatusCode == 200: done = true case httpResp.StatusCode == 408: // request timeout, retry immediately case httpResp.StatusCode == 503: // Retry backoffSeconds = 10 if retryAfter := httpResp.Header.Get("Retry-After"); retryAfter != "" { if seconds, err := strconv.Atoi(retryAfter); err == nil { backoffSeconds = seconds } } default: return nil, fmt.Errorf("got HTTP Status %s", httpResp.Status) } httpStatus = httpResp.Status } ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(resp.Signature)) if err != nil { return nil, err } var logID ct.SHA256Hash copy(logID[:], resp.ID) return &ct.SignedCertificateTimestamp{ SCTVersion: resp.SCTVersion, LogID: logID, Timestamp: resp.Timestamp, Extensions: ct.CTExtensions(resp.Extensions), Signature: *ds}, nil }
// AddJSON submits arbitrary data to to XJSON server. func (c *LogClient) AddJSON(ctx context.Context, data interface{}) (*ct.SignedCertificateTimestamp, error) { req := addJSONRequest{ Data: data, } var resp addChainResponse _, err := c.PostAndParse(ctx, AddJSONPath, &req, &resp) if err != nil { return nil, err } ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(resp.Signature)) if err != nil { return nil, err } var logID ct.SHA256Hash copy(logID[:], resp.ID) return &ct.SignedCertificateTimestamp{ SCTVersion: resp.SCTVersion, LogID: logID, Timestamp: resp.Timestamp, Extensions: ct.CTExtensions(resp.Extensions), Signature: *ds}, nil }
// Attempts to add |chain| to the log, using the api end-point specified by // |path|. If provided context expires before submission is complete an // error will be returned. func (c *LogClient) addChainWithRetry(ctx context.Context, ctype ct.LogEntryType, path string, chain []ct.ASN1Cert) (*ct.SignedCertificateTimestamp, error) { var resp addChainResponse var req addChainRequest for _, link := range chain { req.Chain = append(req.Chain, link) } httpStatus := "Unknown" // Retry after 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 128s, .... maxInterval := 128.0 backoffInterval := 1.0 backoffSeconds := 0 loop: for { if backoffSeconds > 0 { log.Printf("Got %s, backing-off %d seconds", httpStatus, backoffSeconds) } err := backoffForRetry(ctx, time.Second*time.Duration(backoffSeconds)) if err != nil { return nil, err } if backoffSeconds > 0 { backoffSeconds = 0 } httpResp, _, err := c.postAndParse(c.uri+path, &req, &resp) if err != nil { backoffSeconds = int(backoffInterval) if backoffInterval < maxInterval { backoffInterval *= 2.0 } continue } switch { case httpResp.StatusCode == 200: break loop case httpResp.StatusCode == 408: // request timeout, retry immediately case httpResp.StatusCode == 503: // Retry backoffSeconds = int(backoffInterval) if backoffInterval < maxInterval { backoffInterval *= 2.0 } if retryAfter := httpResp.Header.Get("Retry-After"); retryAfter != "" { if seconds, err := strconv.Atoi(retryAfter); err == nil { backoffSeconds = seconds } } default: return nil, fmt.Errorf("got HTTP Status %s", httpResp.Status) } httpStatus = httpResp.Status } ds, err := ct.UnmarshalDigitallySigned(bytes.NewReader(resp.Signature)) if err != nil { return nil, err } var logID ct.SHA256Hash copy(logID[:], resp.ID) sct := &ct.SignedCertificateTimestamp{ SCTVersion: resp.SCTVersion, LogID: logID, Timestamp: resp.Timestamp, Extensions: ct.CTExtensions(resp.Extensions), Signature: *ds} err = c.VerifySCTSignature(*sct, ctype, chain) if err != nil { return nil, err } return sct, nil }