From 1562c8ebed9c3ecdd893e3be4b0b97ae4107b712 Mon Sep 17 00:00:00 2001 From: Ashutosh Narkar Date: Mon, 19 Oct 2020 11:33:55 -0700 Subject: [PATCH] bundle: Support for handling PEM file containing the public key The "verification-key" flag used by the `run` and `build` commands should be able to handle a PEM file containing a public key. Earlier we were not checking if the value of the flag represents a file on disk. This change will check if the value points to a file, then read it contents and set the public key accordingly. Fixes: #2796 Signed-off-by: Ashutosh Narkar --- bundle/keys.go | 19 ++++++++-- bundle/keys_test.go | 86 ++++++++++++++++++++++++++++++++++++++++++--- cmd/build.go | 19 ++++++---- cmd/build_test.go | 20 +++++++++++ cmd/run.go | 6 +++- 5 files changed, 136 insertions(+), 14 deletions(-) diff --git a/bundle/keys.go b/bundle/keys.go index 35b7cae538..67668dc839 100644 --- a/bundle/keys.go +++ b/bundle/keys.go @@ -79,12 +79,25 @@ type KeyConfig struct { } // NewKeyConfig return a new KeyConfig -func NewKeyConfig(key, alg, scope string) *KeyConfig { +func NewKeyConfig(key, alg, scope string) (*KeyConfig, error) { + var pubKey string + if _, err := os.Stat(key); err == nil { + bs, err := ioutil.ReadFile(key) + if err != nil { + return nil, err + } + pubKey = string(bs) + } else if os.IsNotExist(err) { + pubKey = key + } else { + return nil, err + } + return &KeyConfig{ - Key: key, + Key: pubKey, Algorithm: alg, Scope: scope, - } + }, nil } // ParseKeysConfig returns a map containing the public key and the signing algorithm diff --git a/bundle/keys_test.go b/bundle/keys_test.go index 96dda818de..02dd209f34 100644 --- a/bundle/keys_test.go +++ b/bundle/keys_test.go @@ -7,6 +7,7 @@ package bundle import ( "crypto/rsa" "fmt" + "os" "path/filepath" "reflect" "testing" @@ -286,6 +287,67 @@ EXrJfkELSzO66/ZSjyyWEczXHLyr+Q719BsaGsxie117zSNF6B6UXiitjCr/qQ== }) } +func TestNewKeyConfig(t *testing.T) { + publicKey := `-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA9KaakMv1XKKDaSch3PFR +3a27oaHp1GNTTNqvb1ZaHZXp+wuhYDwc/MTE67x9GCifvQBWzEGorgTq7aisiOyl +vKifwz6/wQ+62WHKG/sqKn2Xikp3P63aBIPlZcHbkyyRmL62yeyuzYoGvLEYel+m +z5SiKGBwviSY0Th2L4e5sGJuk2HOut6emxDi+E2Fuuj5zokFJvIT6Urlq8f3h6+l +GeR6HUOXqoYVf7ff126GP7dticTVBgibxkkuJFmpvQSW6xmxruT4k6iwjzbZHY7P +ypZ/TdlnuGC1cOpAVyU7k32IJ9CRbt3nwEf5U54LRXLLQjFixWZHwKdDiMTF4ws0 ++wIDAQAB +-----END PUBLIC KEY-----` + + files := map[string]string{ + "public.pem": publicKey, + } + + test.WithTempFS(files, func(rootDir string) { + + kc, err := NewKeyConfig(filepath.Join(rootDir, "public.pem"), "RS256", "read") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + expected := &KeyConfig{ + Key: publicKey, + Algorithm: "RS256", + Scope: "read", + } + + if !reflect.DeepEqual(kc, expected) { + t.Fatalf("Expected key config %v but got %v", expected, kc) + } + + // secret provided on command-line + kc, err = NewKeyConfig(publicKey, "HS256", "") + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + expected = &KeyConfig{ + Key: publicKey, + Algorithm: "HS256", + Scope: "", + } + + if !reflect.DeepEqual(kc, expected) { + t.Fatalf("Expected key config %v but got %v", expected, kc) + } + + // simulate error while reading file + err = os.Chmod(filepath.Join(rootDir, "public.pem"), 0111) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + _, err = NewKeyConfig(filepath.Join(rootDir, "public.pem"), "RS256", "read") + if err == nil { + t.Fatal("Expected error but got nil") + } + }) +} + func TestGetClaimsErrors(t *testing.T) { files := map[string]string{ "claims.json": `["foo", "read"]`, @@ -320,13 +382,29 @@ func TestKeyConfigEqual(t *testing.T) { exp bool }{ "equal": { - NewKeyConfig("foo", "RS256", "read"), - NewKeyConfig("foo", "RS256", "read"), + &KeyConfig{ + Key: "foo", + Algorithm: "RS256", + Scope: "read", + }, + &KeyConfig{ + Key: "foo", + Algorithm: "RS256", + Scope: "read", + }, true, }, "not_equal": { - NewKeyConfig("foo", "RS256", "read"), - NewKeyConfig("foo", "RS256", "write"), + &KeyConfig{ + Key: "foo", + Algorithm: "RS256", + Scope: "read", + }, + &KeyConfig{ + Key: "foo", + Algorithm: "RS256", + Scope: "write", + }, false, }, } diff --git a/cmd/build.go b/cmd/build.go index 75019ca10e..8f0a502630 100644 --- a/cmd/build.go +++ b/cmd/build.go @@ -241,7 +241,11 @@ func dobuild(params buildParams, args []string) error { buf := bytes.NewBuffer(nil) // generate the bundle verification and signing config - bvc := buildVerificationConfig(params.pubKey, params.pubKeyID, params.algorithm, params.scope, params.excludeVerifyFiles) + bvc, err := buildVerificationConfig(params.pubKey, params.pubKeyID, params.algorithm, params.scope, params.excludeVerifyFiles) + if err != nil { + return err + } + bsc := buildSigningConfig(params.key, params.algorithm, params.claimsFile) if bvc != nil || bsc != nil { @@ -275,7 +279,7 @@ func dobuild(params buildParams, args []string) error { compiler = compiler.WithBundleVerificationKeyID(params.pubKeyID) } - err := compiler.Build(context.Background()) + err = compiler.Build(context.Background()) if params.debug { printdebug(os.Stderr, compiler.Debug()) @@ -309,13 +313,16 @@ func buildCommandLoaderFilter(bundleMode bool, ignore []string) func(string, os. } } -func buildVerificationConfig(pubKey, pubKeyID, alg, scope string, excludeFiles []string) *bundle.VerificationConfig { +func buildVerificationConfig(pubKey, pubKeyID, alg, scope string, excludeFiles []string) (*bundle.VerificationConfig, error) { if pubKey == "" { - return nil + return nil, nil } - keyConfig := bundle.NewKeyConfig(pubKey, alg, scope) - return bundle.NewVerificationConfig(map[string]*bundle.KeyConfig{pubKeyID: keyConfig}, pubKeyID, scope, excludeFiles) + keyConfig, err := bundle.NewKeyConfig(pubKey, alg, scope) + if err != nil { + return nil, err + } + return bundle.NewVerificationConfig(map[string]*bundle.KeyConfig{pubKeyID: keyConfig}, pubKeyID, scope, excludeFiles), nil } func buildSigningConfig(key, alg, claimsFile string) *bundle.SigningConfig { diff --git a/cmd/build_test.go b/cmd/build_test.go index 6d8ac67ba7..a0f11c6be2 100644 --- a/cmd/build_test.go +++ b/cmd/build_test.go @@ -3,6 +3,7 @@ package cmd import ( "os" "path" + "path/filepath" "strings" "testing" @@ -117,3 +118,22 @@ func TestBuildErrorVerifyNonBundle(t *testing.T) { } }) } + +func TestBuildVerificationConfigError(t *testing.T) { + files := map[string]string{ + "public.pem": "foo", + } + + test.WithTempFS(files, func(rootDir string) { + // simulate error while reading file + err := os.Chmod(filepath.Join(rootDir, "public.pem"), 0111) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + _, err = buildVerificationConfig(filepath.Join(rootDir, "public.pem"), "default", "", "", nil) + if err == nil { + t.Fatal("Expected error but got nil") + } + }) +} diff --git a/cmd/run.go b/cmd/run.go index a9b21e05d9..0801ad0783 100644 --- a/cmd/run.go +++ b/cmd/run.go @@ -250,7 +250,11 @@ func initRuntime(ctx context.Context, params runCmdParams, args []string) (*runt params.rt.SkipBundleVerification = params.skipBundleVerify - params.rt.BundleVerificationConfig = buildVerificationConfig(params.pubKey, params.pubKeyID, params.algorithm, params.scope, params.excludeVerifyFiles) + bvc, err := buildVerificationConfig(params.pubKey, params.pubKeyID, params.algorithm, params.scope, params.excludeVerifyFiles) + if err != nil { + return nil, err + } + params.rt.BundleVerificationConfig = bvc if params.rt.BundleVerificationConfig != nil && !params.rt.BundleMode { return nil, fmt.Errorf("enable bundle mode (ie. --bundle) to verify bundle files or directories")