From 0e8167959ee4b7a9ae02b7fcc8b32ea5db27bd9c Mon Sep 17 00:00:00 2001 From: Rodney Osodo Date: Fri, 23 Jan 2026 16:58:53 +0300 Subject: [PATCH 1/3] feat(spec): add WASM encrypted layer media type support Signed-off-by: Rodney Osodo --- encryption.go | 8 ++++++- encryption_test.go | 52 ++++++++++++++++++++++++++++++++++++++++++++++ spec/spec.go | 2 ++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/encryption.go b/encryption.go index b6fa9db..440c95d 100644 --- a/encryption.go +++ b/encryption.go @@ -213,6 +213,7 @@ func DecryptLayer(dc *config.DecryptConfig, encLayerReader io.Reader, desc ocisp func decryptLayerKeyOptsData(dc *config.DecryptConfig, desc ocispec.Descriptor) ([]byte, error) { privKeyGiven := false + keyproviderTried := false errs := "" if len(keyWrapperAnnotations) == 0 { return nil, errors.New("missing Annotations needed for decryption") @@ -226,6 +227,11 @@ func decryptLayerKeyOptsData(dc *config.DecryptConfig, desc ocispec.Descriptor) continue } + isKeyprovider := strings.HasPrefix(scheme, "provider.") + if isKeyprovider { + keyproviderTried = true + } + if len(keywrapper.GetPrivateKeys(dc.Parameters)) > 0 { privKeyGiven = true } @@ -242,7 +248,7 @@ func decryptLayerKeyOptsData(dc *config.DecryptConfig, desc ocispec.Descriptor) return optsData, nil } } - if !privKeyGiven { + if !privKeyGiven && !keyproviderTried { return nil, fmt.Errorf("missing private key needed for decryption:\n%s", errs) } return nil, fmt.Errorf("no suitable key unwrapper found or none of the private keys could be used for decryption:\n%s", errs) diff --git a/encryption_test.go b/encryption_test.go index f76de5d..d26f644 100644 --- a/encryption_test.go +++ b/encryption_test.go @@ -141,3 +141,55 @@ func TestEncryptLayer(t *testing.T) { t.Fatalf("Expected %v, got %v", data, decLayer) } } + +func TestWasmMediaTypeEncryption(t *testing.T) { + data := []byte("This is WASM module data!") + desc := ocispec.Descriptor{ + Digest: digest.FromBytes(data), + Size: int64(len(data)), + MediaType: "application/vnd.wasm.content.layer.v1+wasm", + } + + dataReader := bytes.NewReader(data) + + encLayerReader, encLayerFinalizer, err := EncryptLayer(ec, dataReader, desc) + if err != nil { + t.Fatal(err) + } + + encLayer := make([]byte, 1024) + encsize, err := encLayerReader.Read(encLayer) + if err != io.EOF { + t.Fatal("Expected EOF") + } + encLayerReaderAt := bytes.NewReader(encLayer[:encsize]) + + annotations, err := encLayerFinalizer() + if err != nil { + t.Fatal(err) + } + + if len(annotations) == 0 { + t.Fatal("No keys created for annotations") + } + + newDesc := ocispec.Descriptor{ + Annotations: annotations, + MediaType: "application/vnd.wasm.content.layer.v1+wasm+encrypted", + } + + decLayerReader, _, err := DecryptLayer(dc, encLayerReaderAt, newDesc, false) + if err != nil { + t.Fatal(err) + } + + decLayer := make([]byte, 1024) + decsize, err := decLayerReader.Read(decLayer) + if err != nil && err != io.EOF { + t.Fatal(err) + } + + if !reflect.DeepEqual(decLayer[:decsize], data) { + t.Fatalf("Expected %v, got %v", data, decLayer) + } +} diff --git a/spec/spec.go b/spec/spec.go index c0c1718..3cfbe2b 100644 --- a/spec/spec.go +++ b/spec/spec.go @@ -17,4 +17,6 @@ const ( // // Deprecated: Use [MediaTypeLayerNonDistributableZstdEnc]. MediaTypeLayerNonDistributableZsdtEnc = MediaTypeLayerNonDistributableZstdEnc + // MediaTypeWasmEnc is MIME type used for encrypted WASM layers. + MediaTypeWasmEnc = "application/vnd.wasm.content.layer.v1+wasm+encrypted" ) From 29caa5d34d8ec604a504f1a5c3b8aef87c97b71c Mon Sep 17 00:00:00 2001 From: Rodney Osodo Date: Tue, 27 Jan 2026 17:21:31 +0300 Subject: [PATCH 2/3] fix(spec): add wasm and encrypted wasm Signed-off-by: Rodney Osodo --- spec/spec.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/spec.go b/spec/spec.go index 3cfbe2b..e8b49e2 100644 --- a/spec/spec.go +++ b/spec/spec.go @@ -17,6 +17,8 @@ const ( // // Deprecated: Use [MediaTypeLayerNonDistributableZstdEnc]. MediaTypeLayerNonDistributableZsdtEnc = MediaTypeLayerNonDistributableZstdEnc + // MediaTypeWasmLayer is MIME type used for WASM layers. + MediaTypeWasmLayer = "application/vnd.wasm.content.layer.v1+wasm" // MediaTypeWasmEnc is MIME type used for encrypted WASM layers. - MediaTypeWasmEnc = "application/vnd.wasm.content.layer.v1+wasm+encrypted" + MediaTypeWasmEnc = MediaTypeWasmLayer + "+encrypted" ) From a2f0a9f4d456d6a9b1626b99c9de9d557b3c2f71 Mon Sep 17 00:00:00 2001 From: Rodney Osodo Date: Tue, 27 Jan 2026 17:23:34 +0300 Subject: [PATCH 3/3] fix(encryption): add key provider scheme prefix as constant Signed-off-by: Rodney Osodo --- encryption.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/encryption.go b/encryption.go index 440c95d..42aa9a8 100644 --- a/encryption.go +++ b/encryption.go @@ -42,6 +42,8 @@ import ( // the encrypted layer type EncryptLayerFinalizer func() (map[string]string, error) +const keyProviderSchemePrefix = "provider." + func init() { keyWrappers = make(map[string]keywrap.KeyWrapper) keyWrapperAnnotations = make(map[string]string) @@ -54,7 +56,7 @@ func init() { log.Error(err) } else if ic != nil { for provider, attrs := range ic.KeyProviderConfig { - RegisterKeyWrapper("provider."+provider, keyprovider.NewKeyWrapper(provider, attrs)) + RegisterKeyWrapper(keyProviderSchemePrefix+provider, keyprovider.NewKeyWrapper(provider, attrs)) } } } @@ -227,7 +229,7 @@ func decryptLayerKeyOptsData(dc *config.DecryptConfig, desc ocispec.Descriptor) continue } - isKeyprovider := strings.HasPrefix(scheme, "provider.") + isKeyprovider := strings.HasPrefix(scheme, keyProviderSchemePrefix) if isKeyprovider { keyproviderTried = true }