diff --git a/cmd/tdf-decrypt.go b/cmd/tdf-decrypt.go index 5adbaf84..6bed38fc 100644 --- a/cmd/tdf-decrypt.go +++ b/cmd/tdf-decrypt.go @@ -15,6 +15,7 @@ import ( var TDF = "tdf" var assertionVerification string +var kasAllowList []string const TDF_MAX_FILE_SIZE = int64(10 * 1024 * 1024 * 1024) // 10 GB @@ -59,7 +60,16 @@ func dev_tdfDecryptCmd(cmd *cobra.Command, args []string) { cli.ExitWithError("Must provide ONE of the following to decrypt: [file argument, stdin input]", errors.New("no input provided")) } - decrypted, err := h.DecryptBytes(bytesToDecrypt, assertionVerification, disableAssertionVerification, sessionKeyAlgorithm) + ignoreAllowlist := len(kasAllowList) == 1 && kasAllowList[0] == "*" + + decrypted, err := h.DecryptBytes( + bytesToDecrypt, + assertionVerification, + disableAssertionVerification, + sessionKeyAlgorithm, + kasAllowList, + ignoreAllowlist, + ) if err != nil { cli.ExitWithError("Failed to decrypt file", err) } @@ -115,6 +125,13 @@ func init() { decryptCmd.GetDocFlag("no-verify-assertions").DefaultAsBool(), decryptCmd.GetDocFlag("no-verify-assertions").Description, ) + decryptCmd.Flags().StringSliceVarP( + &kasAllowList, + decryptCmd.GetDocFlag("kas-allowlist").Name, + decryptCmd.GetDocFlag("kas-allowlist").Shorthand, + nil, + decryptCmd.GetDocFlag("kas-allowlist").Description, + ) decryptCmd.Command.GroupID = TDF diff --git a/docs/man/decrypt/_index.md b/docs/man/decrypt/_index.md index 8bd8f6bc..2f40c067 100644 --- a/docs/man/decrypt/_index.md +++ b/docs/man/decrypt/_index.md @@ -25,6 +25,8 @@ command: - name: with-assertion-verification-keys description: > EXPERIMENTAL: path to JSON file of keys to verify signed assertions. See examples for more information. + - name: kas-allowlist + description: A custom allowlist of comma-separated KAS Urls, e.g. `https://example.com/kas,http://localhost:8080`. If none specified, the platform will use the list of KASes in the KAS registry. To ignore the allowlist, use a quoted wildcard e.g. `--kas-allowlist '*'` **WARNING:** Bypassing the allowlist may expose you to potential security risks, as untrusted KAS URLs could be used. --- Decrypt a Trusted Data Format (TDF) file and output the contents to stdout or a file in the current working directory. diff --git a/e2e/encrypt-decrypt.bats b/e2e/encrypt-decrypt.bats index e0af5269..bd234e0e 100755 --- a/e2e/encrypt-decrypt.bats +++ b/e2e/encrypt-decrypt.bats @@ -53,6 +53,11 @@ setup_file() { export MIXED_CASE_FQN="https://Testing-Enc-Dec.io/attr/Attr1/value/VALUE1" } +setup() { + load "${BATS_LIB_PATH}/bats-support/load.bash" + load "${BATS_LIB_PATH}/bats-assert/load.bash" +} + teardown() { rm -f $OUTFILE_GO_MOD $RESULTFILE_GO_MOD $OUTFILE_TXT } @@ -152,3 +157,43 @@ teardown_file(){ schema_version_present=$(./otdfctl --host $HOST --tls-no-verify $WITH_CREDS inspect $OUTFILE_GO_MOD | jq '.manifest | has("schemaVersion")') [[ $schema_version_present == true ]] } + +@test "roundtrip TDF3, with allowlist containing platform kas" { + ./otdfctl encrypt -o $OUTFILE_GO_MOD --host $HOST --tls-no-verify $DEBUG_LEVEL $WITH_CREDS --tdf-type tdf3 $INFILE_GO_MOD + run sh -c "./otdfctl decrypt --host $HOST --tls-no-verify $DEBUG_LEVEL $WITH_CREDS --tdf-type tdf3 --kas-allowlist http://localhost:8080/kas $OUTFILE_GO_MOD" + assert_success +} + +@test "roundtrip TDF3, with allowlist containing non existent kas (should fail)" { + ./otdfctl encrypt -o $OUTFILE_GO_MOD --host $HOST --tls-no-verify $DEBUG_LEVEL $WITH_CREDS --tdf-type tdf3 $INFILE_GO_MOD + run sh -c "./otdfctl decrypt --host $HOST --tls-no-verify $DEBUG_LEVEL $WITH_CREDS --tdf-type tdf3 --kas-allowlist http://not-a-real-kas.com/kas $OUTFILE_GO_MOD" + assert_failure + assert_output --partial "KasAllowlist: kas url http://localhost:8080/kas is not allowed" +} + +@test "roundtrip TDF3, ignoring allowlist" { + ./otdfctl encrypt -o $OUTFILE_GO_MOD --host $HOST --tls-no-verify $DEBUG_LEVEL $WITH_CREDS --tdf-type tdf3 $INFILE_GO_MOD + run sh -c "./otdfctl decrypt --host $HOST --tls-no-verify $DEBUG_LEVEL $WITH_CREDS --tdf-type tdf3 --kas-allowlist '*' $OUTFILE_GO_MOD" + assert_success + assert_output --partial "KasAllowlist is ignored" +} + +@test "roundtrip NANO, with allowlist containing platform kas" { + ./otdfctl encrypt -o $OUTFILE_GO_MOD --host $HOST --tls-no-verify $DEBUG_LEVEL $WITH_CREDS --tdf-type nano $INFILE_GO_MOD + run sh -c "./otdfctl decrypt --host $HOST --tls-no-verify $DEBUG_LEVEL $WITH_CREDS --tdf-type nano --kas-allowlist http://localhost:8080/kas $OUTFILE_GO_MOD" + assert_success +} + +@test "roundtrip NANO, with allowlist containing non existent kas (should fail)" { + ./otdfctl encrypt -o $OUTFILE_GO_MOD --host $HOST --tls-no-verify $DEBUG_LEVEL $WITH_CREDS --tdf-type nano $INFILE_GO_MOD + run sh -c "./otdfctl decrypt --host $HOST --tls-no-verify $DEBUG_LEVEL $WITH_CREDS --tdf-type nano --kas-allowlist http://not-a-real-kas.com/kas $OUTFILE_GO_MOD" + assert_failure + assert_output --partial "KasAllowlist: kas url http://localhost:8080/kas is not allowed" +} + +@test "roundtrip NANO, ignoring allowlist" { + ./otdfctl encrypt -o $OUTFILE_GO_MOD --host $HOST --tls-no-verify $DEBUG_LEVEL $WITH_CREDS --tdf-type nano $INFILE_GO_MOD + run sh -c "./otdfctl decrypt --host $HOST --tls-no-verify $DEBUG_LEVEL $WITH_CREDS --tdf-type nano --kas-allowlist '*' $OUTFILE_GO_MOD" + assert_success + assert_output --partial "KasAllowlist is ignored" +} diff --git a/go.mod b/go.mod index 66aee40a..b1244043 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/opentdf/platform/lib/flattening v0.1.3 github.com/opentdf/platform/lib/ocrypto v0.1.9 github.com/opentdf/platform/protocol/go v0.3.1 - github.com/opentdf/platform/sdk v0.4.3 + github.com/opentdf/platform/sdk v0.4.4 github.com/spf13/cobra v1.9.1 github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.10.0 diff --git a/go.sum b/go.sum index e5ddd377..6fe63df2 100644 --- a/go.sum +++ b/go.sum @@ -234,8 +234,8 @@ github.com/opentdf/platform/lib/ocrypto v0.1.9 h1:GvgPB7CoK7JmWvsSvJ0hc+RC0wezgc github.com/opentdf/platform/lib/ocrypto v0.1.9/go.mod h1:UTtqh8mvhAYA+sEnaMxpr/406e84L5Q1sAxtKGIXfu4= github.com/opentdf/platform/protocol/go v0.3.1 h1:vEGD8KaXK56S9HcqSGUEDOhVuAdHlHzPk1tuMSX+Xc8= github.com/opentdf/platform/protocol/go v0.3.1/go.mod h1:nErYkgt32GW22CNqSyLO+JE49C3JndI1TsVdF+CUYd4= -github.com/opentdf/platform/sdk v0.4.3 h1:oaBw6OL7AVkgAfYPATM6ji3nbSvg2RBq3I264VlJlco= -github.com/opentdf/platform/sdk v0.4.3/go.mod h1:xPjymAKCbFzo+z+PvFVa10NOT+9i5ljxmJaGJ9tkPrw= +github.com/opentdf/platform/sdk v0.4.4 h1:jBJPXZBOodmanla9aS1aaPQgcg7zqOEbBTLF0c0BULM= +github.com/opentdf/platform/sdk v0.4.4/go.mod h1:xPjymAKCbFzo+z+PvFVa10NOT+9i5ljxmJaGJ9tkPrw= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= diff --git a/pkg/handlers/tdf.go b/pkg/handlers/tdf.go index ef2e54c5..84ded432 100644 --- a/pkg/handlers/tdf.go +++ b/pkg/handlers/tdf.go @@ -131,19 +131,36 @@ func (h Handler) EncryptBytes( } } -func (h Handler) DecryptBytes(toDecrypt []byte, assertionVerificationKeysFile string, disableAssertionCheck bool, sessionKeyAlgorithm ocrypto.KeyType) (*bytes.Buffer, error) { +func (h Handler) DecryptBytes( + toDecrypt []byte, + assertionVerificationKeysFile string, + disableAssertionCheck bool, + sessionKeyAlgorithm ocrypto.KeyType, + kasAllowList []string, + ignoreAllowlist bool, +) (*bytes.Buffer, error) { out := &bytes.Buffer{} pt := io.Writer(out) ec := bytes.NewReader(toDecrypt) switch sdk.GetTdfType(ec) { case sdk.Nano: - if _, err := h.sdk.ReadNanoTDF(pt, ec); err != nil { + opts := []sdk.NanoTDFReaderOption{ + sdk.WithNanoIgnoreAllowlist(ignoreAllowlist), + } + if kasAllowList != nil { + opts = append(opts, sdk.WithNanoKasAllowlist(kasAllowList)) + } + if _, err := h.sdk.ReadNanoTDF(pt, ec, opts...); err != nil { return nil, err } case sdk.Standard: opts := []sdk.TDFReaderOption{ sdk.WithDisableAssertionVerification(disableAssertionCheck), - sdk.WithSessionKeyType(sessionKeyAlgorithm)} + sdk.WithSessionKeyType(sessionKeyAlgorithm), + sdk.WithIgnoreAllowlist(ignoreAllowlist)} + if kasAllowList != nil { + opts = append(opts, sdk.WithKasAllowlist(kasAllowList)) + } var assertionVerificationKeys sdk.AssertionVerificationKeys if assertionVerificationKeysFile != "" { // read the file