Skip to content

Commit 53814c3

Browse files
committed
feat(cloudkms): Add code samples for KMS KEMs.
One sample for encapsulation and one for decapsulation.
1 parent 1ed9bcd commit 53814c3

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: %s", 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)