Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions kms/decapsulate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package kms

// [START kms_decapsulate]
import (
"context"
"fmt"
"hash/crc32"
"io"

kms "cloud.google.com/go/kms/apiv1"
"cloud.google.com/go/kms/apiv1/kmspb"
"google.golang.org/protobuf/types/known/wrapperspb"
)

// decapsulate decapsulates the given ciphertext using a saved private key of purpose
// KEY_ENCAPSULATION stored in KMS.
func decapsulate(w io.Writer, keyVersionName string, ciphertext []byte) error {
// keyVersionName := "projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key/cryptoKeyVersions/1"
// ciphertext := []byte("...")

// Create the client.
ctx := context.Background()
client, err := kms.NewKeyManagementClient(ctx)
if err != nil {
return fmt.Errorf("failed to create kms client: %w", err)
}
defer client.Close()

// crc32c calculates the CRC32C checksum of the given data.
crc32c := func(data []byte) uint32 {
t := crc32.MakeTable(crc32.Castagnoli)
return crc32.Checksum(data, t)
}

// Optional but recommended: Compute ciphertext's CRC32C.
ciphertextCRC32C := crc32c(ciphertext)

// Build the request.
req := &kmspb.DecapsulateRequest{
Name: keyVersionName,
Ciphertext: ciphertext,
CiphertextCrc32C: wrapperspb.Int64(int64(ciphertextCRC32C)),
}

// Call the API.
result, err := client.Decapsulate(ctx, req)
if err != nil {
return fmt.Errorf("failed to decapsulate: %w", err)
}

// Optional, but recommended: perform integrity verification on the response.
// For more details on ensuring E2E in-transit integrity to and from Cloud KMS visit:
// https://cloud.google.com/kms/docs/data-integrity-guidelines
if !result.GetVerifiedCiphertextCrc32C() {
return fmt.Errorf("Decapsulate: request corrupted in-transit")
}
if result.GetName() != req.GetName() {
return fmt.Errorf("Decapsulate: request corrupted in-transit")
}
if int64(crc32c(result.GetSharedSecret())) != result.GetSharedSecretCrc32C() {
return fmt.Errorf("Decapsulate: response corrupted in-transit")
}

fmt.Fprintf(w, "Decapsulated plaintext: %x", result.GetSharedSecret())
return nil
}

// [END kms_decapsulate]
85 changes: 85 additions & 0 deletions kms/encapsulate_mlkem.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package kms

// [START kms_encapsulate_mlkem]
import (
"context"
"crypto/mlkem"
"fmt"
"hash/crc32"
"io"

kms "cloud.google.com/go/kms/apiv1"
"cloud.google.com/go/kms/apiv1/kmspb"
)

// encapsulateMLKEM demonstrates how to encapsulate a shared secret using an ML-KEM-768 public key
// from Cloud KMS.
func encapsulateMLKEM(w io.Writer, keyVersionName string) error {
// keyVersionName := "projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key/cryptoKeyVersions/1"

// Create the client.
ctx := context.Background()
client, err := kms.NewKeyManagementClient(ctx)
if err != nil {
return fmt.Errorf("failed to create kms client: %w", err)
}
defer client.Close()

// crc32c calculates the CRC32C checksum of the given data.
crc32c := func(data []byte) uint32 {
t := crc32.MakeTable(crc32.Castagnoli)
return crc32.Checksum(data, t)
}

// Build the request to get the public key in NIST PQC format.
req := &kmspb.GetPublicKeyRequest{
Name: keyVersionName,
PublicKeyFormat: kmspb.PublicKey_NIST_PQC,
}

// Call the API to get the public key.
response, err := client.GetPublicKey(ctx, req)
if err != nil {
return fmt.Errorf("failed to get public key: %w", err)
}

// Optional, but recommended: perform integrity verification on the response.
// For more details on ensuring E2E in-transit integrity to and from Cloud KMS visit:
// https://cloud.google.com/kms/docs/data-integrity-guidelines
if response.GetName() != req.GetName() {
return fmt.Errorf("GetPublicKey: request corrupted in-transit")
}
if response.GetPublicKeyFormat() != req.GetPublicKeyFormat() {
return fmt.Errorf("GetPublicKey: request corrupted in-transit")
}
if int64(crc32c(response.GetPublicKey().GetData())) != response.GetPublicKey().GetCrc32CChecksum().GetValue() {
return fmt.Errorf("GetPublicKey: response corrupted in-transit")
}

// Use the public key with crypto/mlkem to encapsulate a shared secret.
ek, err := mlkem.NewEncapsulationKey768(response.GetPublicKey().GetData())
if err != nil {
return fmt.Errorf("NewEncapsulationKey768: %w", err)
}
sharedSecret, ciphertext := ek.Encapsulate()

fmt.Fprintf(w, "Encapsulated ciphertext: %x\n", ciphertext)
fmt.Fprintf(w, "Shared secret: %x\n", sharedSecret)
return nil
}

// [END kms_encapsulate_mlkem]
82 changes: 42 additions & 40 deletions kms/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,71 +3,73 @@ module github.com/GoogleCloudPlatform/golang-samples/kms
go 1.24.0

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

require (
cel.dev/expr v0.19.1 // indirect
cloud.google.com/go/auth v0.14.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
cloud.google.com/go/longrunning v0.6.4 // indirect
cloud.google.com/go/monitoring v1.23.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 // indirect
cel.dev/expr v0.24.0 // indirect
cloud.google.com/go/auth v0.16.4 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/longrunning v0.6.7 // indirect
cloud.google.com/go/monitoring v1.24.2 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.50.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.32.3 // indirect
github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
github.com/zeebo/errs v1.4.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.34.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/time v0.9.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250115164207-1a7da9e5054f // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.36.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/otel v1.36.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.opentelemetry.io/otel/sdk v1.36.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.36.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/time v0.12.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250811230008-5f3141c8851a // indirect
)

require (
cloud.google.com/go v0.118.0 // indirect
cloud.google.com/go/compute/metadata v0.6.0 // indirect
cloud.google.com/go/iam v1.3.1 // indirect
cloud.google.com/go v0.120.0 // indirect
cloud.google.com/go/compute/metadata v0.8.0 // indirect
cloud.google.com/go/iam v1.5.2 // indirect
cloud.google.com/go/storage v1.50.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
github.com/googleapis/gax-go/v2 v2.15.0 // indirect
// Note that the library 'lestrrat-go' is NOT endoresed for anything beyond conversion to JWK.
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/httprc v1.0.5 // indirect
github.com/lestrrat-go/iter v1.0.2 // indirect
github.com/lestrrat-go/jwx/v2 v2.1.0
github.com/lestrrat-go/option v1.0.1 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/oauth2 v0.25.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/crypto v0.41.0 // indirect
golang.org/x/net v0.43.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/text v0.28.0 // indirect
)
Loading
Loading