aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/cli/internal/cache/cache_signature_authentication.go
diff options
context:
space:
mode:
Diffstat (limited to 'cli/internal/cache/cache_signature_authentication.go')
-rw-r--r--cli/internal/cache/cache_signature_authentication.go88
1 files changed, 88 insertions, 0 deletions
diff --git a/cli/internal/cache/cache_signature_authentication.go b/cli/internal/cache/cache_signature_authentication.go
new file mode 100644
index 0000000..f9fe4c0
--- /dev/null
+++ b/cli/internal/cache/cache_signature_authentication.go
@@ -0,0 +1,88 @@
+// Adapted from https://github.com/thought-machine/please
+// Copyright Thought Machine, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+package cache
+
+import (
+ "crypto/hmac"
+ "crypto/sha256"
+ "encoding/base64"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "hash"
+ "os"
+)
+
+type ArtifactSignatureAuthentication struct {
+ teamId string
+ enabled bool
+}
+
+func (asa *ArtifactSignatureAuthentication) isEnabled() bool {
+ return asa.enabled
+}
+
+// If the secret key is not found or the secret key length is 0, an error is returned
+// Preference is given to the environment specified secret key.
+func (asa *ArtifactSignatureAuthentication) secretKey() ([]byte, error) {
+ secret := os.Getenv("TURBO_REMOTE_CACHE_SIGNATURE_KEY")
+ if len(secret) == 0 {
+ return nil, errors.New("signature secret key not found. You must specify a secret key in the TURBO_REMOTE_CACHE_SIGNATURE_KEY environment variable")
+ }
+ return []byte(secret), nil
+}
+
+func (asa *ArtifactSignatureAuthentication) generateTag(hash string, artifactBody []byte) (string, error) {
+ tag, err := asa.getTagGenerator(hash)
+ if err != nil {
+ return "", err
+ }
+ tag.Write(artifactBody)
+ return base64.StdEncoding.EncodeToString(tag.Sum(nil)), nil
+}
+
+func (asa *ArtifactSignatureAuthentication) getTagGenerator(hash string) (hash.Hash, error) {
+ teamId := asa.teamId
+ secret, err := asa.secretKey()
+ if err != nil {
+ return nil, err
+ }
+ artifactMetadata := &struct {
+ Hash string `json:"hash"`
+ TeamId string `json:"teamId"`
+ }{
+ Hash: hash,
+ TeamId: teamId,
+ }
+ metadata, err := json.Marshal(artifactMetadata)
+ if err != nil {
+ return nil, err
+ }
+
+ // TODO(Gaspar) Support additional signing algorithms here
+ h := hmac.New(sha256.New, secret)
+ h.Write(metadata)
+ return h, nil
+}
+
+func (asa *ArtifactSignatureAuthentication) validate(hash string, artifactBody []byte, expectedTag string) (bool, error) {
+ computedTag, err := asa.generateTag(hash, artifactBody)
+ if err != nil {
+ return false, fmt.Errorf("failed to verify artifact tag: %w", err)
+ }
+ return hmac.Equal([]byte(computedTag), []byte(expectedTag)), nil
+}
+
+type StreamValidator struct {
+ currentHash hash.Hash
+}
+
+func (sv *StreamValidator) Validate(expectedTag string) bool {
+ computedTag := base64.StdEncoding.EncodeToString(sv.currentHash.Sum(nil))
+ return hmac.Equal([]byte(computedTag), []byte(expectedTag))
+}
+
+func (sv *StreamValidator) CurrentValue() string {
+ return base64.StdEncoding.EncodeToString(sv.currentHash.Sum(nil))
+}