Skip to content

Commit 922bc56

Browse files
Add support for Tink keyset signer
This PR adds support for in-memory signing using a Tink keyset. The keyset is encrypted with a key-encryption-key stored in GCP KMS. The key is decrypted on startup and loaded into memory. This uses a utility to unpack the keyset into a crypto.Signer so that it can be used to sign certificates. This also validates that the key is an ECDSA P-256 key as per RFC 6962, since Tink supports many key types. Signed-off-by: Hayden B <8418760+haydentherapper@users.noreply.github.com>
1 parent 9285389 commit 922bc56

File tree

4 files changed

+161
-4
lines changed

4 files changed

+161
-4
lines changed

cmd/gcp/main.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package main
1717

1818
import (
1919
"context"
20+
"crypto"
2021
"errors"
2122
"flag"
2223
"fmt"
@@ -62,6 +63,8 @@ var (
6263
signerPublicKeySecretName = flag.String("signer_public_key_secret_name", "", "Public key secret name for checkpoints and SCTs signer. Format: projects/{projectId}/secrets/{secretName}/versions/{secretVersion}.")
6364
signerPrivateKeySecretName = flag.String("signer_private_key_secret_name", "", "Private key secret name for checkpoints and SCTs signer. Format: projects/{projectId}/secrets/{secretName}/versions/{secretVersion}.")
6465
traceFraction = flag.Float64("trace_fraction", 0, "Fraction of open-telemetry span traces to sample")
66+
signerTinkKekUri = flag.String("signer-tink-kek-uri", "", "Encryption key for decrypting Tink keyset. Format: gcp-kms://projects/{projectId}/locations/{location}/keyRings/{keyRing}/cryptoKeys/{cryptoKey}/cryptoKeyVersions/{version}")
67+
signerTinkKeysetFile = flag.String("signer-tink-keyset-path", "", "Path to encrypted Tink keyset")
6568
)
6669

6770
// nolint:staticcheck
@@ -73,9 +76,22 @@ func main() {
7376
shutdownOTel := initOTel(ctx, *traceFraction, *origin)
7477
defer shutdownOTel(ctx)
7578

76-
signer, err := NewSecretManagerSigner(ctx, *signerPublicKeySecretName, *signerPrivateKeySecretName)
77-
if err != nil {
78-
klog.Exitf("Can't create secret manager signer: %v", err)
79+
var signer crypto.Signer
80+
var err error
81+
if *signerPrivateKeySecretName != "" && *signerPublicKeySecretName != "" {
82+
signer, err = NewSecretManagerSigner(ctx, *signerPublicKeySecretName, *signerPrivateKeySecretName)
83+
if err != nil {
84+
klog.Exitf("Can't create secret manager signer: %v", err)
85+
}
86+
}
87+
if *signerTinkKekUri != "" && *signerTinkKeysetFile != "" {
88+
signer, err = NewTinkSignerVerifier(ctx, *signerTinkKekUri, *signerTinkKeysetFile)
89+
if err != nil {
90+
klog.Exitf("Can't initialize Tink signer: %v", err)
91+
}
92+
}
93+
if signer == nil {
94+
klog.Exit("Signer not initialized, provide either a key either in GCP Secret Manager or a GCP KMS-encrypted Tink keyset")
7995
}
8096

8197
chainValidationConfig := tesseract.ChainValidationConfig{

cmd/gcp/tink.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright 2025 Google LLC. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package main
16+
17+
import (
18+
"context"
19+
"crypto"
20+
"crypto/ecdsa"
21+
"crypto/elliptic"
22+
"fmt"
23+
"os"
24+
"path/filepath"
25+
"strings"
26+
27+
tinkUtils "github.com/sigstore/sigstore/pkg/signature/tink"
28+
"github.com/tink-crypto/tink-go-gcpkms/v2/integration/gcpkms"
29+
"github.com/tink-crypto/tink-go/v2/core/registry"
30+
"github.com/tink-crypto/tink-go/v2/keyset"
31+
"github.com/tink-crypto/tink-go/v2/tink"
32+
)
33+
34+
const TinkScheme = "tink"
35+
36+
// NewTinkSignerVerifier returns a crypto.Signer. Only ECDSA P-256 is supported.
37+
// Provide a path to the encrypted keyset and GCP KMS key URI for decryption.
38+
func NewTinkSignerVerifier(ctx context.Context, kekURI, keysetPath string) (crypto.Signer, error) {
39+
if kekURI == "" || keysetPath == "" {
40+
return nil, fmt.Errorf("key encryption key URI or keyset path unset")
41+
}
42+
kek, err := getKeyEncryptionKey(ctx, kekURI)
43+
if err != nil {
44+
return nil, err
45+
}
46+
47+
f, err := os.Open(filepath.Clean(keysetPath))
48+
if err != nil {
49+
return nil, err
50+
}
51+
defer f.Close()
52+
53+
kh, err := keyset.Read(keyset.NewJSONReader(f), kek)
54+
if err != nil {
55+
return nil, err
56+
}
57+
signer, _, err := tinkUtils.KeyHandleToSigner(kh)
58+
if err != nil {
59+
return nil, err
60+
}
61+
62+
// validate that key is ECDSA P-256
63+
pub, ok := signer.Public().(*ecdsa.PublicKey)
64+
if !ok {
65+
return nil, fmt.Errorf("key must be ECDSA")
66+
}
67+
if pub.Curve != elliptic.P256() {
68+
return nil, fmt.Errorf("elliptic curve must be P-256, was %s", pub.Curve.Params().Name)
69+
}
70+
71+
return signer, err
72+
}
73+
74+
// getKeyEncryptionKey returns a Tink AEAD encryption key from KMS
75+
func getKeyEncryptionKey(ctx context.Context, kmsKey string) (tink.AEAD, error) {
76+
switch {
77+
case strings.HasPrefix(kmsKey, "gcp-kms://"):
78+
gcpClient, err := gcpkms.NewClientWithOptions(ctx, kmsKey)
79+
if err != nil {
80+
return nil, err
81+
}
82+
registry.RegisterKMSClient(gcpClient)
83+
return gcpClient.GetAEAD(kmsKey)
84+
default:
85+
return nil, fmt.Errorf("unsupported KMS key type for key %s", kmsKey)
86+
}
87+
}

go.mod

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ require (
1919
github.com/google/go-cmp v0.7.0
2020
github.com/kylelemons/godebug v1.1.0
2121
github.com/rivo/tview v0.0.0-20240625185742-b0a7293b8130
22+
github.com/sigstore/sigstore v1.9.3
23+
github.com/tink-crypto/tink-go-gcpkms/v2 v2.2.0
24+
github.com/tink-crypto/tink-go/v2 v2.4.0
2225
github.com/transparency-dev/formats v0.0.0-20250414062418-2207ab4ca61e
2326
github.com/transparency-dev/merkle v0.0.2
2427
github.com/transparency-dev/trillian-tessera v0.1.2-0.20250417180933-3f459e774877
@@ -72,18 +75,25 @@ require (
7275
github.com/felixge/httpsnoop v1.0.4 // indirect
7376
github.com/gdamore/encoding v1.0.1 // indirect
7477
github.com/globocom/go-buffer v1.2.2 // indirect
78+
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
7579
github.com/go-logr/logr v1.4.2 // indirect
7680
github.com/go-logr/stdr v1.2.2 // indirect
7781
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
82+
github.com/google/go-containerregistry v0.20.3 // indirect
7883
github.com/google/s2a-go v0.1.9 // indirect
7984
github.com/google/uuid v1.6.0 // indirect
8085
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
8186
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
8287
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
88+
github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect
8389
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
8490
github.com/mattn/go-runewidth v0.0.16 // indirect
91+
github.com/opencontainers/go-digest v1.0.0 // indirect
8592
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
8693
github.com/rivo/uniseg v0.4.7 // indirect
94+
github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect
95+
github.com/sigstore/protobuf-specs v0.4.1 // indirect
96+
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
8797
go.opencensus.io v0.24.0 // indirect
8898
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
8999
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
@@ -100,4 +110,5 @@ require (
100110
google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e // indirect
101111
google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e // indirect
102112
google.golang.org/protobuf v1.36.6 // indirect
113+
gopkg.in/yaml.v3 v3.0.1 // indirect
103114
)

0 commit comments

Comments
 (0)