k8s.io/apiserver@v0.31.1/pkg/storage/value/encrypt/envelope/kmsv2/envelope.go (about)

     1  /*
     2  Copyright 2022 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Package kmsv2 transforms values for storage at rest using a Envelope v2 provider
    18  package kmsv2
    19  
    20  import (
    21  	"context"
    22  	"crypto/aes"
    23  	"crypto/cipher"
    24  	"crypto/sha256"
    25  	"fmt"
    26  	"sort"
    27  	"time"
    28  	"unsafe"
    29  
    30  	"github.com/gogo/protobuf/proto"
    31  	"go.opentelemetry.io/otel/attribute"
    32  	"golang.org/x/crypto/cryptobyte"
    33  
    34  	utilerrors "k8s.io/apimachinery/pkg/util/errors"
    35  	"k8s.io/apimachinery/pkg/util/uuid"
    36  	"k8s.io/apimachinery/pkg/util/validation"
    37  	"k8s.io/apimachinery/pkg/util/validation/field"
    38  	genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
    39  	"k8s.io/apiserver/pkg/storage/value"
    40  	aestransformer "k8s.io/apiserver/pkg/storage/value/encrypt/aes"
    41  	kmstypes "k8s.io/apiserver/pkg/storage/value/encrypt/envelope/kmsv2/v2"
    42  	"k8s.io/apiserver/pkg/storage/value/encrypt/envelope/metrics"
    43  	"k8s.io/component-base/tracing"
    44  	"k8s.io/klog/v2"
    45  	kmsservice "k8s.io/kms/pkg/service"
    46  	"k8s.io/utils/clock"
    47  )
    48  
    49  func init() {
    50  	value.RegisterMetrics()
    51  	metrics.RegisterMetrics()
    52  }
    53  
    54  const (
    55  	// KMSAPIVersionv2 is a version of the KMS API.
    56  	KMSAPIVersionv2 = "v2"
    57  	// KMSAPIVersionv2beta1 is a version of the KMS API.
    58  	KMSAPIVersionv2beta1 = "v2beta1"
    59  	// annotationsMaxSize is the maximum size of the annotations.
    60  	annotationsMaxSize = 32 * 1024 // 32 kB
    61  	// KeyIDMaxSize is the maximum size of the keyID.
    62  	KeyIDMaxSize = 1 * 1024 // 1 kB
    63  	// encryptedDEKSourceMaxSize is the maximum size of the encrypted DEK source.
    64  	encryptedDEKSourceMaxSize = 1 * 1024 // 1 kB
    65  	// cacheTTL is the default time-to-live for the cache entry.
    66  	// this allows the cache to grow to an infinite size for up to a day.
    67  	// there is unlikely to be any meaningful memory impact on the server
    68  	// because the cache will likely never have more than a few thousand entries.
    69  	// each entry can be large due to an internal cache that maps the DEK seed to individual
    70  	// DEK entries, but that cache has an aggressive TTL to keep the size under control.
    71  	// with DEK/seed reuse and no storage migration, the number of entries in this cache
    72  	// would be approximated by unique key IDs used by the KMS plugin
    73  	// combined with the number of server restarts.  If storage migration
    74  	// is performed after key ID changes, and the number of restarts
    75  	// is limited, this cache size may be as small as the number of API
    76  	// servers in use (once old entries expire out from the TTL).
    77  	cacheTTL = 24 * time.Hour
    78  	// key ID related error codes for metrics
    79  	errKeyIDOKCode      ErrCodeKeyID = "ok"
    80  	errKeyIDEmptyCode   ErrCodeKeyID = "empty"
    81  	errKeyIDTooLongCode ErrCodeKeyID = "too_long"
    82  )
    83  
    84  // NowFunc is exported so tests can override it.
    85  var NowFunc = time.Now
    86  
    87  type StateFunc func() (State, error)
    88  type ErrCodeKeyID string
    89  
    90  type State struct {
    91  	Transformer value.Transformer
    92  
    93  	EncryptedObject kmstypes.EncryptedObject
    94  
    95  	UID string
    96  
    97  	ExpirationTimestamp time.Time
    98  
    99  	// CacheKey is the key used to cache the DEK/seed in envelopeTransformer.cache.
   100  	CacheKey []byte
   101  }
   102  
   103  func (s *State) ValidateEncryptCapability() error {
   104  	if now := NowFunc(); now.After(s.ExpirationTimestamp) {
   105  		return fmt.Errorf("encryptedDEKSource with keyID hash %q expired at %s (current time is %s)",
   106  			GetHashIfNotEmpty(s.EncryptedObject.KeyID), s.ExpirationTimestamp.Format(time.RFC3339), now.Format(time.RFC3339))
   107  	}
   108  	return nil
   109  }
   110  
   111  type envelopeTransformer struct {
   112  	envelopeService kmsservice.Service
   113  	providerName    string
   114  	stateFunc       StateFunc
   115  
   116  	// cache is a thread-safe expiring lru cache which caches decrypted DEKs indexed by their encrypted form.
   117  	cache       *simpleCache
   118  	apiServerID string
   119  }
   120  
   121  // NewEnvelopeTransformer returns a transformer which implements a KEK-DEK based envelope encryption scheme.
   122  // It uses envelopeService to encrypt and decrypt DEKs. Respective DEKs (in encrypted form) are prepended to
   123  // the data items they encrypt.
   124  func NewEnvelopeTransformer(envelopeService kmsservice.Service, providerName string, stateFunc StateFunc, apiServerID string) value.Transformer {
   125  	return newEnvelopeTransformerWithClock(envelopeService, providerName, stateFunc, apiServerID, cacheTTL, clock.RealClock{})
   126  }
   127  
   128  func newEnvelopeTransformerWithClock(envelopeService kmsservice.Service, providerName string, stateFunc StateFunc, apiServerID string, cacheTTL time.Duration, clock clock.Clock) value.Transformer {
   129  	return &envelopeTransformer{
   130  		envelopeService: envelopeService,
   131  		providerName:    providerName,
   132  		stateFunc:       stateFunc,
   133  		cache:           newSimpleCache(clock, cacheTTL, providerName),
   134  		apiServerID:     apiServerID,
   135  	}
   136  }
   137  
   138  // TransformFromStorage decrypts data encrypted by this transformer using envelope encryption.
   139  func (t *envelopeTransformer) TransformFromStorage(ctx context.Context, data []byte, dataCtx value.Context) ([]byte, bool, error) {
   140  	ctx, span := tracing.Start(ctx, "TransformFromStorage with envelopeTransformer",
   141  		attribute.String("transformer.provider.name", t.providerName),
   142  		// The service.instance_id of the apiserver is already available in the trace
   143  		/*
   144  			{
   145  			"key": "service.instance.id",
   146  			"type": "string",
   147  			"value": "apiserver-zsteyir5lyrtdcmqqmd5kzze6m"
   148  			}
   149  		*/
   150  	)
   151  	defer span.End(500 * time.Millisecond)
   152  
   153  	span.AddEvent("About to decode encrypted object")
   154  	// Deserialize the EncryptedObject from the data.
   155  	encryptedObject, err := t.doDecode(data)
   156  	if err != nil {
   157  		span.AddEvent("Decoding encrypted object failed")
   158  		span.RecordError(err)
   159  		return nil, false, err
   160  	}
   161  	span.AddEvent("Decoded encrypted object")
   162  
   163  	useSeed := encryptedObject.EncryptedDEKSourceType == kmstypes.EncryptedDEKSourceType_HKDF_SHA256_XNONCE_AES_GCM_SEED
   164  
   165  	// TODO: consider marking state.EncryptedDEK != encryptedObject.EncryptedDEK as a stale read to support DEK defragmentation
   166  	//  at a minimum we should have a metric that helps the user understand if DEK fragmentation is high
   167  	state, err := t.stateFunc() // no need to call state.ValidateEncryptCapability on reads
   168  	if err != nil {
   169  		return nil, false, err
   170  	}
   171  
   172  	encryptedObjectCacheKey, err := generateCacheKey(encryptedObject.EncryptedDEKSourceType, encryptedObject.EncryptedDEKSource, encryptedObject.KeyID, encryptedObject.Annotations)
   173  	if err != nil {
   174  		return nil, false, err
   175  	}
   176  
   177  	// Look up the decrypted DEK from cache first
   178  	transformer := t.cache.get(encryptedObjectCacheKey)
   179  
   180  	// fallback to the envelope service if we do not have the transformer locally
   181  	if transformer == nil {
   182  		span.AddEvent("About to decrypt DEK using remote service")
   183  		value.RecordCacheMiss()
   184  
   185  		requestInfo := getRequestInfoFromContext(ctx)
   186  		uid := string(uuid.NewUUID())
   187  		klog.V(6).InfoS("decrypting content using envelope service", "uid", uid, "key", string(dataCtx.AuthenticatedData()),
   188  			"group", requestInfo.APIGroup, "version", requestInfo.APIVersion, "resource", requestInfo.Resource, "subresource", requestInfo.Subresource,
   189  			"verb", requestInfo.Verb, "namespace", requestInfo.Namespace, "name", requestInfo.Name)
   190  
   191  		key, err := t.envelopeService.Decrypt(ctx, uid, &kmsservice.DecryptRequest{
   192  			Ciphertext:  encryptedObject.EncryptedDEKSource,
   193  			KeyID:       encryptedObject.KeyID,
   194  			Annotations: encryptedObject.Annotations,
   195  		})
   196  		if err != nil {
   197  			span.AddEvent("DEK decryption failed")
   198  			span.RecordError(err)
   199  			return nil, false, fmt.Errorf("failed to decrypt DEK, error: %w", err)
   200  		}
   201  		span.AddEvent("DEK decryption succeeded")
   202  
   203  		transformer, err = t.addTransformerForDecryption(encryptedObjectCacheKey, key, useSeed)
   204  		if err != nil {
   205  			return nil, false, err
   206  		}
   207  	}
   208  	metrics.RecordKeyID(metrics.FromStorageLabel, t.providerName, encryptedObject.KeyID, t.apiServerID)
   209  
   210  	span.AddEvent("About to decrypt data using DEK")
   211  	out, stale, err := transformer.TransformFromStorage(ctx, encryptedObject.EncryptedData, dataCtx)
   212  	if err != nil {
   213  		span.AddEvent("Data decryption failed")
   214  		span.RecordError(err)
   215  		return nil, false, err
   216  	}
   217  
   218  	span.AddEvent("Data decryption succeeded")
   219  	// data is considered stale if the key ID does not match our current write transformer
   220  	return out,
   221  		stale ||
   222  			encryptedObject.KeyID != state.EncryptedObject.KeyID ||
   223  			encryptedObject.EncryptedDEKSourceType != state.EncryptedObject.EncryptedDEKSourceType,
   224  		nil
   225  }
   226  
   227  // TransformToStorage encrypts data to be written to disk using envelope encryption.
   228  func (t *envelopeTransformer) TransformToStorage(ctx context.Context, data []byte, dataCtx value.Context) ([]byte, error) {
   229  	ctx, span := tracing.Start(ctx, "TransformToStorage with envelopeTransformer",
   230  		attribute.String("transformer.provider.name", t.providerName),
   231  		// The service.instance_id of the apiserver is already available in the trace
   232  		/*
   233  			{
   234  			"key": "service.instance.id",
   235  			"type": "string",
   236  			"value": "apiserver-zsteyir5lyrtdcmqqmd5kzze6m"
   237  			}
   238  		*/
   239  	)
   240  	defer span.End(500 * time.Millisecond)
   241  
   242  	state, err := t.stateFunc()
   243  	if err != nil {
   244  		return nil, err
   245  	}
   246  	if err := state.ValidateEncryptCapability(); err != nil {
   247  		return nil, err
   248  	}
   249  
   250  	// this prevents a cache miss every time the DEK rotates
   251  	// this has the side benefit of causing the cache to perform a GC
   252  	// TODO see if we can do this inside the stateFunc control loop
   253  	t.cache.set(state.CacheKey, state.Transformer)
   254  
   255  	requestInfo := getRequestInfoFromContext(ctx)
   256  	klog.V(6).InfoS("encrypting content using DEK", "uid", state.UID, "key", string(dataCtx.AuthenticatedData()),
   257  		"group", requestInfo.APIGroup, "version", requestInfo.APIVersion, "resource", requestInfo.Resource, "subresource", requestInfo.Subresource,
   258  		"verb", requestInfo.Verb, "namespace", requestInfo.Namespace, "name", requestInfo.Name)
   259  
   260  	span.AddEvent("About to encrypt data using DEK")
   261  	result, err := state.Transformer.TransformToStorage(ctx, data, dataCtx)
   262  	if err != nil {
   263  		span.AddEvent("Data encryption failed")
   264  		span.RecordError(err)
   265  		return nil, err
   266  	}
   267  	span.AddEvent("Data encryption succeeded")
   268  
   269  	metrics.RecordKeyID(metrics.ToStorageLabel, t.providerName, state.EncryptedObject.KeyID, t.apiServerID)
   270  
   271  	encObjectCopy := state.EncryptedObject
   272  	encObjectCopy.EncryptedData = result
   273  
   274  	span.AddEvent("About to encode encrypted object")
   275  	// Serialize the EncryptedObject to a byte array.
   276  	out, err := t.doEncode(&encObjectCopy)
   277  	if err != nil {
   278  		span.AddEvent("Encoding encrypted object failed")
   279  		span.RecordError(err)
   280  		return nil, err
   281  	}
   282  	span.AddEvent("Encoded encrypted object")
   283  
   284  	return out, nil
   285  }
   286  
   287  // addTransformerForDecryption inserts a new transformer to the Envelope cache of DEKs for future reads.
   288  func (t *envelopeTransformer) addTransformerForDecryption(cacheKey []byte, key []byte, useSeed bool) (value.Read, error) {
   289  	var transformer value.Read
   290  	var err error
   291  	if useSeed {
   292  		// the input key is considered safe to use here because it is coming from the KMS plugin / etcd
   293  		transformer, err = aestransformer.NewHKDFExtendedNonceGCMTransformer(key)
   294  	} else {
   295  		var block cipher.Block
   296  		block, err = aes.NewCipher(key)
   297  		if err != nil {
   298  			return nil, err
   299  		}
   300  		// this is compatible with NewGCMTransformerWithUniqueKeyUnsafe for decryption
   301  		// it would use random nonces for encryption but we never do that
   302  		transformer, err = aestransformer.NewGCMTransformer(block)
   303  	}
   304  	if err != nil {
   305  		return nil, err
   306  	}
   307  	t.cache.set(cacheKey, transformer)
   308  	return transformer, nil
   309  }
   310  
   311  // doEncode encodes the EncryptedObject to a byte array.
   312  func (t *envelopeTransformer) doEncode(request *kmstypes.EncryptedObject) ([]byte, error) {
   313  	if err := ValidateEncryptedObject(request); err != nil {
   314  		return nil, err
   315  	}
   316  	return proto.Marshal(request)
   317  }
   318  
   319  // doDecode decodes the byte array to an EncryptedObject.
   320  func (t *envelopeTransformer) doDecode(originalData []byte) (*kmstypes.EncryptedObject, error) {
   321  	o := &kmstypes.EncryptedObject{}
   322  	if err := proto.Unmarshal(originalData, o); err != nil {
   323  		return nil, err
   324  	}
   325  	if err := ValidateEncryptedObject(o); err != nil {
   326  		return nil, err
   327  	}
   328  
   329  	return o, nil
   330  }
   331  
   332  // GenerateTransformer generates a new transformer and encrypts the DEK/seed using the envelope service.
   333  // It returns the transformer, the encrypted DEK/seed, cache key and error.
   334  func GenerateTransformer(ctx context.Context, uid string, envelopeService kmsservice.Service, useSeed bool) (value.Transformer, *kmstypes.EncryptedObject, []byte, error) {
   335  	newTransformerFunc := func() (value.Transformer, []byte, error) {
   336  		seed, err := aestransformer.GenerateKey(aestransformer.MinSeedSizeExtendedNonceGCM)
   337  		if err != nil {
   338  			return nil, nil, err
   339  		}
   340  		transformer, err := aestransformer.NewHKDFExtendedNonceGCMTransformer(seed)
   341  		if err != nil {
   342  			return nil, nil, err
   343  		}
   344  		return transformer, seed, nil
   345  	}
   346  	if !useSeed {
   347  		newTransformerFunc = aestransformer.NewGCMTransformerWithUniqueKeyUnsafe
   348  	}
   349  	transformer, newKey, err := newTransformerFunc()
   350  	if err != nil {
   351  		return nil, nil, nil, err
   352  	}
   353  
   354  	klog.V(6).InfoS("encrypting content using envelope service", "uid", uid)
   355  
   356  	resp, err := envelopeService.Encrypt(ctx, uid, newKey)
   357  	if err != nil {
   358  		return nil, nil, nil, fmt.Errorf("failed to encrypt DEK, error: %w", err)
   359  	}
   360  
   361  	o := &kmstypes.EncryptedObject{
   362  		KeyID:              resp.KeyID,
   363  		EncryptedDEKSource: resp.Ciphertext,
   364  		EncryptedData:      []byte{0}, // any non-empty value to pass validation
   365  		Annotations:        resp.Annotations,
   366  	}
   367  
   368  	if useSeed {
   369  		o.EncryptedDEKSourceType = kmstypes.EncryptedDEKSourceType_HKDF_SHA256_XNONCE_AES_GCM_SEED
   370  	} else {
   371  		o.EncryptedDEKSourceType = kmstypes.EncryptedDEKSourceType_AES_GCM_KEY
   372  	}
   373  
   374  	if err := ValidateEncryptedObject(o); err != nil {
   375  		return nil, nil, nil, err
   376  	}
   377  
   378  	cacheKey, err := generateCacheKey(o.EncryptedDEKSourceType, resp.Ciphertext, resp.KeyID, resp.Annotations)
   379  	if err != nil {
   380  		return nil, nil, nil, err
   381  	}
   382  
   383  	o.EncryptedData = nil // make sure that later code that uses this encrypted object sets this field
   384  
   385  	return transformer, o, cacheKey, nil
   386  }
   387  
   388  func ValidateEncryptedObject(o *kmstypes.EncryptedObject) error {
   389  	if o == nil {
   390  		return fmt.Errorf("encrypted object is nil")
   391  	}
   392  	switch t := o.EncryptedDEKSourceType; t {
   393  	case kmstypes.EncryptedDEKSourceType_AES_GCM_KEY:
   394  	case kmstypes.EncryptedDEKSourceType_HKDF_SHA256_XNONCE_AES_GCM_SEED:
   395  	default:
   396  		return fmt.Errorf("unknown encryptedDEKSourceType: %d", t)
   397  	}
   398  	if len(o.EncryptedData) == 0 {
   399  		return fmt.Errorf("encrypted data is empty")
   400  	}
   401  	if err := validateEncryptedDEKSource(o.EncryptedDEKSource); err != nil {
   402  		return fmt.Errorf("failed to validate encrypted DEK source: %w", err)
   403  	}
   404  	if _, err := ValidateKeyID(o.KeyID); err != nil {
   405  		return fmt.Errorf("failed to validate key id: %w", err)
   406  	}
   407  	if err := validateAnnotations(o.Annotations); err != nil {
   408  		return fmt.Errorf("failed to validate annotations: %w", err)
   409  	}
   410  	return nil
   411  }
   412  
   413  // validateEncryptedDEKSource tests the following:
   414  // 1. The encrypted DEK source is not empty.
   415  // 2. The size of encrypted DEK source is less than 1 kB.
   416  func validateEncryptedDEKSource(encryptedDEKSource []byte) error {
   417  	if len(encryptedDEKSource) == 0 {
   418  		return fmt.Errorf("encrypted DEK source is empty")
   419  	}
   420  	if len(encryptedDEKSource) > encryptedDEKSourceMaxSize {
   421  		return fmt.Errorf("encrypted DEK source is %d bytes, which exceeds the max size of %d", len(encryptedDEKSource), encryptedDEKSourceMaxSize)
   422  	}
   423  	return nil
   424  }
   425  
   426  // validateAnnotations tests the following:
   427  //  1. checks if the annotation key is fully qualified
   428  //  2. The size of annotations keys + values is less than 32 kB.
   429  func validateAnnotations(annotations map[string][]byte) error {
   430  	var errs []error
   431  	var totalSize uint64
   432  	for k, v := range annotations {
   433  		if fieldErr := validation.IsFullyQualifiedDomainName(field.NewPath("annotations"), k); fieldErr != nil {
   434  			errs = append(errs, fieldErr.ToAggregate())
   435  		}
   436  		totalSize += uint64(len(k)) + uint64(len(v))
   437  	}
   438  	if totalSize > annotationsMaxSize {
   439  		errs = append(errs, fmt.Errorf("total size of annotations is %d, which exceeds the max size of %d", totalSize, annotationsMaxSize))
   440  	}
   441  	return utilerrors.NewAggregate(errs)
   442  }
   443  
   444  // ValidateKeyID tests the following:
   445  // 1. The keyID is not empty.
   446  // 2. The size of keyID is less than 1 kB.
   447  func ValidateKeyID(keyID string) (ErrCodeKeyID, error) {
   448  	if len(keyID) == 0 {
   449  		return errKeyIDEmptyCode, fmt.Errorf("keyID is empty")
   450  	}
   451  	if len(keyID) > KeyIDMaxSize {
   452  		return errKeyIDTooLongCode, fmt.Errorf("keyID is %d bytes, which exceeds the max size of %d", len(keyID), KeyIDMaxSize)
   453  	}
   454  	return errKeyIDOKCode, nil
   455  }
   456  
   457  func getRequestInfoFromContext(ctx context.Context) *genericapirequest.RequestInfo {
   458  	if reqInfo, found := genericapirequest.RequestInfoFrom(ctx); found {
   459  		return reqInfo
   460  	}
   461  	return &genericapirequest.RequestInfo{}
   462  }
   463  
   464  // generateCacheKey returns a key for the cache.
   465  // The key is a concatenation of:
   466  //  0. encryptedDEKSourceType
   467  //  1. encryptedDEKSource
   468  //  2. keyID
   469  //  3. length of annotations
   470  //  4. annotations (sorted by key) - each annotation is a concatenation of:
   471  //     a. annotation key
   472  //     b. annotation value
   473  func generateCacheKey(encryptedDEKSourceType kmstypes.EncryptedDEKSourceType, encryptedDEKSource []byte, keyID string, annotations map[string][]byte) ([]byte, error) {
   474  	// TODO(aramase): use sync pool buffer to avoid allocations
   475  	b := cryptobyte.NewBuilder(nil)
   476  	b.AddUint32(uint32(encryptedDEKSourceType))
   477  	b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
   478  		b.AddBytes(encryptedDEKSource)
   479  	})
   480  	b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
   481  		b.AddBytes(toBytes(keyID))
   482  	})
   483  	if len(annotations) == 0 {
   484  		return b.Bytes()
   485  	}
   486  
   487  	// add the length of annotations to the cache key
   488  	b.AddUint32(uint32(len(annotations)))
   489  
   490  	// Sort the annotations by key.
   491  	keys := make([]string, 0, len(annotations))
   492  	for k := range annotations {
   493  		k := k
   494  		keys = append(keys, k)
   495  	}
   496  	sort.Strings(keys)
   497  	for _, k := range keys {
   498  		// The maximum size of annotations is annotationsMaxSize (32 kB) so we can safely
   499  		// assume that the length of the key and value will fit in a uint16.
   500  		b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
   501  			b.AddBytes(toBytes(k))
   502  		})
   503  		b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
   504  			b.AddBytes(annotations[k])
   505  		})
   506  	}
   507  
   508  	return b.Bytes()
   509  }
   510  
   511  // toBytes performs unholy acts to avoid allocations
   512  func toBytes(s string) []byte {
   513  	// unsafe.StringData is unspecified for the empty string, so we provide a strict interpretation
   514  	if len(s) == 0 {
   515  		return nil
   516  	}
   517  	// Copied from go 1.20.1 os.File.WriteString
   518  	// https://github.com/golang/go/blob/202a1a57064127c3f19d96df57b9f9586145e21c/src/os/file.go#L246
   519  	return unsafe.Slice(unsafe.StringData(s), len(s))
   520  }
   521  
   522  // GetHashIfNotEmpty returns the sha256 hash of the data if it is not empty.
   523  func GetHashIfNotEmpty(data string) string {
   524  	if len(data) > 0 {
   525  		return fmt.Sprintf("sha256:%x", sha256.Sum256([]byte(data)))
   526  	}
   527  	return ""
   528  }