Skip to content

Commit 55c2802

Browse files
committed
feat(cloudkms): Add code samples for KMS KEMs.
One sample for encapsulation and one for decapsulation. Also, updating the KMS Client version to the latest one (previous ones don't have support for these new APIs).
1 parent 1ed9bcd commit 55c2802

File tree

4 files changed

+299
-128
lines changed

4 files changed

+299
-128
lines changed

kms/decapsulate.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright 2025 Google LLC
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+
// https://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 kms
16+
17+
// [START kms_decapsulate]
18+
import (
19+
"context"
20+
"fmt"
21+
"hash/crc32"
22+
"io"
23+
24+
kms "cloud.google.com/go/kms/apiv1"
25+
"cloud.google.com/go/kms/apiv1/kmspb"
26+
"google.golang.org/protobuf/types/known/wrapperspb"
27+
)
28+
29+
// decapsulate decapsulates the given ciphertext using a saved private key of purpose
30+
// KEY_ENCAPSULATION stored in KMS.
31+
func decapsulate(w io.Writer, keyVersionName string, ciphertext []byte) error {
32+
// keyVersionName := "projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key/cryptoKeyVersions/1"
33+
// ciphertext := []byte("...")
34+
35+
// Create the client.
36+
ctx := context.Background()
37+
client, err := kms.NewKeyManagementClient(ctx)
38+
if err != nil {
39+
return fmt.Errorf("failed to create kms client: %w", err)
40+
}
41+
defer client.Close()
42+
43+
// crc32c calculates the CRC32C checksum of the given data.
44+
crc32c := func(data []byte) uint32 {
45+
t := crc32.MakeTable(crc32.Castagnoli)
46+
return crc32.Checksum(data, t)
47+
}
48+
49+
// Optional but recommended: Compute ciphertext's CRC32C.
50+
ciphertextCRC32C := crc32c(ciphertext)
51+
52+
// Build the request.
53+
req := &kmspb.DecapsulateRequest{
54+
Name: keyVersionName,
55+
Ciphertext: ciphertext,
56+
CiphertextCrc32C: wrapperspb.Int64(int64(ciphertextCRC32C)),
57+
}
58+
59+
// Call the API.
60+
result, err := client.Decapsulate(ctx, req)
61+
if err != nil {
62+
return fmt.Errorf("failed to decapsulate: %w", err)
63+
}
64+
65+
// Optional, but recommended: perform integrity verification on the response.
66+
// For more details on ensuring E2E in-transit integrity to and from Cloud KMS visit:
67+
// https://cloud.google.com/kms/docs/data-integrity-guidelines
68+
if !result.GetVerifiedCiphertextCrc32C() {
69+
return fmt.Errorf("Decapsulate: request corrupted in-transit")
70+
}
71+
if result.GetName() != req.GetName() {
72+
return fmt.Errorf("Decapsulate: request corrupted in-transit")
73+
}
74+
if int64(crc32c(result.GetSharedSecret())) != result.GetSharedSecretCrc32C() {
75+
return fmt.Errorf("Decapsulate: response corrupted in-transit")
76+
}
77+
78+
fmt.Fprintf(w, "Decapsulated plaintext: %x", result.GetSharedSecret())
79+
return nil
80+
}
81+
82+
// [END kms_decapsulate]

kms/encapsulate_mlkem.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright 2025 Google LLC
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+
// https://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 kms
16+
17+
// [START kms_encapsulate_mlkem]
18+
import (
19+
"context"
20+
"crypto/mlkem"
21+
"fmt"
22+
"hash/crc32"
23+
"io"
24+
25+
kms "cloud.google.com/go/kms/apiv1"
26+
"cloud.google.com/go/kms/apiv1/kmspb"
27+
)
28+
29+
// encapsulateMLKEM demonstrates how to encapsulate a shared secret using an ML-KEM-768 public key
30+
// from Cloud KMS.
31+
func encapsulateMLKEM(w io.Writer, keyVersionName string) error {
32+
// keyVersionName := "projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key/cryptoKeyVersions/1"
33+
34+
// Create the client.
35+
ctx := context.Background()
36+
client, err := kms.NewKeyManagementClient(ctx)
37+
if err != nil {
38+
return fmt.Errorf("failed to create kms client: %w", err)
39+
}
40+
defer client.Close()
41+
42+
// crc32c calculates the CRC32C checksum of the given data.
43+
crc32c := func(data []byte) uint32 {
44+
t := crc32.MakeTable(crc32.Castagnoli)
45+
return crc32.Checksum(data, t)
46+
}
47+
48+
// Build the request to get the public key in NIST PQC format.
49+
req := &kmspb.GetPublicKeyRequest{
50+
Name: keyVersionName,
51+
PublicKeyFormat: kmspb.PublicKey_NIST_PQC,
52+
}
53+
54+
// Call the API to get the public key.
55+
response, err := client.GetPublicKey(ctx, req)
56+
if err != nil {
57+
return fmt.Errorf("failed to get public key: %w", err)
58+
}
59+
60+
// Optional, but recommended: perform integrity verification on the response.
61+
// For more details on ensuring E2E in-transit integrity to and from Cloud KMS visit:
62+
// https://cloud.google.com/kms/docs/data-integrity-guidelines
63+
if response.GetName() != req.GetName() {
64+
return fmt.Errorf("GetPublicKey: request corrupted in-transit")
65+
}
66+
if response.GetPublicKeyFormat() != req.GetPublicKeyFormat() {
67+
return fmt.Errorf("GetPublicKey: request corrupted in-transit")
68+
}
69+
if int64(crc32c(response.GetPublicKey().GetData())) != response.GetPublicKey().GetCrc32CChecksum().GetValue() {
70+
return fmt.Errorf("GetPublicKey: response corrupted in-transit")
71+
}
72+
73+
// Use the public key with crypto/mlkem to encapsulate a shared secret.
74+
ek, err := mlkem.NewEncapsulationKey768(response.GetPublicKey().GetData())
75+
if err != nil {
76+
return fmt.Errorf("NewEncapsulationKey768: %w", err)
77+
}
78+
sharedSecret, ciphertext := ek.Encapsulate()
79+
80+
fmt.Fprintf(w, "Encapsulated ciphertext: %x\n", ciphertext)
81+
fmt.Fprintf(w, "Shared secret: %x\n", sharedSecret)
82+
return nil
83+
}
84+
85+
// [END kms_encapsulate_mlkem]

kms/go.mod

Lines changed: 42 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,71 +3,73 @@ module github.com/GoogleCloudPlatform/golang-samples/kms
33
go 1.24.0
44

55
require (
6-
cloud.google.com/go/kms v1.20.5
6+
cloud.google.com/go/kms v1.23.0
77
github.com/GoogleCloudPlatform/golang-samples v0.0.0-20240724083556-7f760db013b7
88
github.com/gofrs/uuid v4.4.0+incompatible
99
github.com/google/tink/go v1.7.0
10-
google.golang.org/api v0.217.0
11-
google.golang.org/genproto v0.0.0-20250115164207-1a7da9e5054f
12-
google.golang.org/grpc v1.69.4
13-
google.golang.org/protobuf v1.36.3
10+
google.golang.org/api v0.247.0
11+
google.golang.org/genproto v0.0.0-20250603155806-513f23925822
12+
google.golang.org/grpc v1.74.2
13+
google.golang.org/protobuf v1.36.7
1414
)
1515

1616
require (
17-
cel.dev/expr v0.19.1 // indirect
18-
cloud.google.com/go/auth v0.14.0 // indirect
19-
cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
20-
cloud.google.com/go/longrunning v0.6.4 // indirect
21-
cloud.google.com/go/monitoring v1.23.0 // indirect
22-
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect
23-
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 // indirect
24-
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 // indirect
17+
cel.dev/expr v0.24.0 // indirect
18+
cloud.google.com/go/auth v0.16.4 // indirect
19+
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
20+
cloud.google.com/go/longrunning v0.6.7 // indirect
21+
cloud.google.com/go/monitoring v1.24.2 // indirect
22+
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect
23+
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0 // indirect
24+
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0 // indirect
2525
github.com/cespare/xxhash/v2 v2.3.0 // indirect
26-
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect
27-
github.com/envoyproxy/go-control-plane/envoy v1.32.3 // indirect
28-
github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect
26+
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect
27+
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
28+
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
2929
github.com/felixge/httpsnoop v1.0.4 // indirect
30-
github.com/go-logr/logr v1.4.2 // indirect
30+
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
31+
github.com/go-logr/logr v1.4.3 // indirect
3132
github.com/go-logr/stdr v1.2.2 // indirect
3233
github.com/google/s2a-go v0.1.9 // indirect
3334
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
3435
github.com/segmentio/asm v1.2.0 // indirect
36+
github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
37+
github.com/zeebo/errs v1.4.0 // indirect
3538
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
36-
go.opentelemetry.io/contrib/detectors/gcp v1.34.0 // indirect
37-
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
38-
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
39-
go.opentelemetry.io/otel v1.34.0 // indirect
40-
go.opentelemetry.io/otel/metric v1.34.0 // indirect
41-
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
42-
go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect
43-
go.opentelemetry.io/otel/trace v1.34.0 // indirect
44-
golang.org/x/sync v0.10.0 // indirect
45-
golang.org/x/time v0.9.0 // indirect
46-
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect
47-
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
39+
go.opentelemetry.io/contrib/detectors/gcp v1.36.0 // indirect
40+
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
41+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
42+
go.opentelemetry.io/otel v1.36.0 // indirect
43+
go.opentelemetry.io/otel/metric v1.36.0 // indirect
44+
go.opentelemetry.io/otel/sdk v1.36.0 // indirect
45+
go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
46+
go.opentelemetry.io/otel/trace v1.36.0 // indirect
47+
golang.org/x/sync v0.16.0 // indirect
48+
golang.org/x/time v0.12.0 // indirect
49+
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c // indirect
50+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a // indirect
4851
)
4952

5053
require (
51-
cloud.google.com/go v0.118.0 // indirect
52-
cloud.google.com/go/compute/metadata v0.6.0 // indirect
53-
cloud.google.com/go/iam v1.3.1 // indirect
54+
cloud.google.com/go v0.120.0 // indirect
55+
cloud.google.com/go/compute/metadata v0.8.0 // indirect
56+
cloud.google.com/go/iam v1.5.2 // indirect
5457
cloud.google.com/go/storage v1.50.0 // indirect
5558
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
5659
github.com/goccy/go-json v0.10.3 // indirect
57-
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
5860
github.com/google/uuid v1.6.0 // indirect
59-
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
60-
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
61+
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
62+
github.com/googleapis/gax-go/v2 v2.15.0 // indirect
6163
// Note that the library 'lestrrat-go' is NOT endoresed for anything beyond conversion to JWK.
6264
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
6365
github.com/lestrrat-go/httpcc v1.0.1 // indirect
6466
github.com/lestrrat-go/httprc v1.0.5 // indirect
6567
github.com/lestrrat-go/iter v1.0.2 // indirect
6668
github.com/lestrrat-go/jwx/v2 v2.1.0
6769
github.com/lestrrat-go/option v1.0.1 // indirect
68-
golang.org/x/crypto v0.32.0 // indirect
69-
golang.org/x/net v0.34.0 // indirect
70-
golang.org/x/oauth2 v0.25.0 // indirect
71-
golang.org/x/sys v0.29.0 // indirect
72-
golang.org/x/text v0.21.0 // indirect
70+
golang.org/x/crypto v0.41.0 // indirect
71+
golang.org/x/net v0.43.0 // indirect
72+
golang.org/x/oauth2 v0.30.0 // indirect
73+
golang.org/x/sys v0.35.0 // indirect
74+
golang.org/x/text v0.28.0 // indirect
7375
)

0 commit comments

Comments
 (0)