Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): add ecdsa-binding encrypt flag #360

Merged
merged 8 commits into from
Sep 5, 2024
Merged
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
1 change: 1 addition & 0 deletions .github/spellcheck.ignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ decrypt
decryptable
decrypted
dev
ecdsa
encodings
enum
https
Expand Down
8 changes: 7 additions & 1 deletion cmd/tdf-encrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ func dev_tdfEncryptCmd(cmd *cobra.Command, args []string) {
if tdfType == TDF3 {
encrypted, err = h.EncryptBytes(bytesSlice, values, fileMimeType, kasURLPath)
} else if tdfType == NANO {
encrypted, err = h.EncryptNanoBytes(bytesSlice, values, kasURLPath)
ecdsaBinding := c.Flags.GetOptionalBool("ecdsa-binding")
encrypted, err = h.EncryptNanoBytes(bytesSlice, values, kasURLPath, ecdsaBinding)
} else {
cli.ExitWithError("Failed to encrypt", fmt.Errorf("unrecognized tdf-type: %s", tdfType))
}
Expand Down Expand Up @@ -151,6 +152,11 @@ func init() {
encryptCmd.GetDocFlag("tdf-type").Default,
encryptCmd.GetDocFlag("tdf-type").Description,
)
encryptCmd.Flags().Bool(
encryptCmd.GetDocFlag("ecdsa-binding").Name,
false,
encryptCmd.GetDocFlag("ecdsa-binding").Description,
)
encryptCmd.Command.GroupID = "tdf"
encryptCmd.Flags().String(
encryptCmd.GetDocFlag("kas-url-path").Name,
Expand Down
73 changes: 55 additions & 18 deletions cmd/tdf-inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package cmd

import (
"errors"
"fmt"
"strings"

"github.com/opentdf/otdfctl/pkg/cli"
"github.com/opentdf/otdfctl/pkg/handlers"
Expand All @@ -24,6 +26,13 @@ type tdfInspectManifest struct {
EncryptionInformation sdk.EncryptionInformation `json:"encryptionInformation"`
}

type nanoInspectResult struct {
Cipher string `json:"cipher"`
ECDSAEnabled bool `json:"ecdsaEnabled"`
Kas string `json:"kas"`
KID string `json:"kid"`
}

type tdfInspectResult struct {
Manifest tdfInspectManifest `json:"manifest"`
Attributes []string `json:"attributes"`
Expand All @@ -42,30 +51,58 @@ func tdf_InspectCmd(cmd *cobra.Command, args []string) {
result, errs := h.InspectTDF(data)
for _, err := range errs {
if errors.Is(err, handlers.ErrTDFInspectFailNotValidTDF) {
c.ExitWithError("not a valid ZTDF", err)
c.ExitWithError("not a valid TDF", err)
} else if errors.Is(err, handlers.ErrTDFInspectFailNotInspectable) {
c.ExitWithError("failed to inspect TDF", err)
}
}

m := tdfInspectResult{
Manifest: tdfInspectManifest{
Algorithm: result.Manifest.Algorithm,
KeyAccessType: result.Manifest.KeyAccessType,
MimeType: result.Manifest.MimeType,
Policy: result.Manifest.Policy,
Protocol: result.Manifest.Protocol,
SegmentHashAlgorithm: result.Manifest.SegmentHashAlgorithm,
Signature: result.Manifest.Signature,
Type: result.Manifest.Type,
Method: result.Manifest.Method,
IntegrityInformation: result.Manifest.IntegrityInformation,
EncryptionInformation: result.Manifest.EncryptionInformation,
},
Attributes: result.Attributes,
}
if result.ZTDFManifest != nil {
m := tdfInspectResult{
Manifest: tdfInspectManifest{
Algorithm: result.ZTDFManifest.Algorithm,
KeyAccessType: result.ZTDFManifest.KeyAccessType,
MimeType: result.ZTDFManifest.MimeType,
Policy: result.ZTDFManifest.Policy,
Protocol: result.ZTDFManifest.Protocol,
SegmentHashAlgorithm: result.ZTDFManifest.SegmentHashAlgorithm,
Signature: result.ZTDFManifest.Signature,
Type: result.ZTDFManifest.Type,
Method: result.ZTDFManifest.Method,
IntegrityInformation: result.ZTDFManifest.IntegrityInformation,
EncryptionInformation: result.ZTDFManifest.EncryptionInformation,
},
Attributes: result.Attributes,
}

c.PrintJson(m)
c.PrintJson(m)
} else if result.NanoHeader != nil {
dmihalcik-virtru marked this conversation as resolved.
Show resolved Hide resolved
kas, err := result.NanoHeader.GetKasURL().GetURL()
if err != nil {
c.ExitWithError("not a valid NanoTDF", err)
}
kid, err := result.NanoHeader.GetKasURL().GetIdentifier()
if err != nil {
c.ExitWithError("not a valid NanoTDF", err)
}
cipher := result.NanoHeader.GetCipher()
cipherBytes, err := sdk.SizeOfAuthTagForCipher(cipher)
if err != nil {
c.ExitWithError("not a valid NanoTDF", err)
}
cipherName := fmt.Sprintf("AES-%d", 8*cipherBytes)

n := nanoInspectResult{
Kas: kas,
KID: strings.TrimRight(kid, "\u0000"),
ECDSAEnabled: result.NanoHeader.IsEcdsaBindingEnabled(),
Cipher: cipherName,
}

c.PrintJson(n)
} else {
c.ExitWithError("failed to inspect TDF", nil)
}
}

func init() {
Expand Down
2 changes: 2 additions & 0 deletions docs/man/encrypt/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ command:
- tdf3
- nano
default: tdf3
- name: ecdsa-binding
description: For nano type containers only, enables ECDSA policy binding
- name: kas-url-path
description: URL path to the KAS service at the platform endpoint domain. Leading slash is required if needed.
default: /kas
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ require (
github.com/google/uuid v1.6.0
github.com/itchyny/gojq v0.12.16
github.com/opentdf/platform/protocol/go v0.2.14
github.com/opentdf/platform/sdk v0.3.10
github.com/opentdf/platform/sdk v0.3.12
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.9.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@ github.com/opentdf/platform/protocol/go v0.2.14 h1:0wqKDVTpuPICyH37ecKxR2+tZNsgX
github.com/opentdf/platform/protocol/go v0.2.14/go.mod h1:WqDcnFQJb0v8ivRQPidbehcL8ils5ZSZYXkuv0nyvsI=
github.com/opentdf/platform/sdk v0.3.10 h1:WoPtM6IcwwDIEqCcLq2jb6pd15bFXmEDaju9MKd6JtM=
github.com/opentdf/platform/sdk v0.3.10/go.mod h1:XqFivuo4tcqxGwJF9ORnLB3S5bjrgJwiaj6BAJUXJXg=
github.com/opentdf/platform/sdk v0.3.12 h1:1WBiogmIoFseG4xj3j0NXpcQz7a8huHos2KKwaFpYDs=
github.com/opentdf/platform/sdk v0.3.12/go.mod h1:XqFivuo4tcqxGwJF9ORnLB3S5bjrgJwiaj6BAJUXJXg=
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=
Expand Down
5 changes: 4 additions & 1 deletion pkg/handlers/nano-tdf.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"io"
)

func (h Handler) EncryptNanoBytes(b []byte, values []string, kasUrlPath string) (*bytes.Buffer, error) {
func (h Handler) EncryptNanoBytes(b []byte, values []string, kasUrlPath string, ecdsaBinding bool) (*bytes.Buffer, error) {
var encrypted []byte
enc := bytes.NewBuffer(encrypted)

Expand All @@ -16,6 +16,9 @@ func (h Handler) EncryptNanoBytes(b []byte, values []string, kasUrlPath string)

nanoTDFConfig.SetKasURL(h.platformEndpoint + kasUrlPath)
nanoTDFConfig.SetAttributes(values)
if ecdsaBinding {
nanoTDFConfig.EnableECDSAPolicyBinding()
}

// TODO: validate values are FQNs or return an error [https://github.com/opentdf/platform/issues/515]
_, err = h.sdk.CreateNanoTDF(enc, bytes.NewReader(b), *nanoTDFConfig)
Expand Down
35 changes: 33 additions & 2 deletions pkg/handlers/tdf.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package handlers
import (
"bytes"
"errors"
"fmt"
"io"
"strings"

Expand Down Expand Up @@ -47,12 +48,26 @@ func (h Handler) DecryptTDF(toDecrypt []byte) (*bytes.Buffer, error) {
}

type TDFInspect struct {
Manifest sdk.Manifest
NanoHeader *sdk.NanoTDFHeader
ZTDFManifest *sdk.Manifest
Attributes []string
UnencryptedMetadata []byte
}

func (h Handler) InspectTDF(toInspect []byte) (TDFInspect, []error) {
if len(toInspect) < 3 {
return TDFInspect{}, []error{fmt.Errorf("tdf too small [%d] bytes", len(toInspect))}
}
switch {
case bytes.Equal([]byte("PK"), toInspect[0:2]):
return h.InspectZTDF(toInspect)
case bytes.Equal([]byte("L1L"), toInspect[0:3]):
return h.InspectNanoTDF(toInspect)
}
return TDFInspect{}, []error{fmt.Errorf("tdf format unrecognized")}
}

func (h Handler) InspectZTDF(toInspect []byte) (TDFInspect, []error) {
// grouping errors so we don't impact the piping of the data
errs := []error{}

Expand All @@ -74,9 +89,25 @@ func (h Handler) InspectTDF(toInspect []byte) (TDFInspect, []error) {
errs = append(errs, errors.Join(ErrTDFUnableToReadUnencryptedMetadata, err))
}

m := tdfreader.Manifest()
return TDFInspect{
Manifest: tdfreader.Manifest(),
ZTDFManifest: &m,
Attributes: attributes,
UnencryptedMetadata: unencryptedMetadata,
}, errs
}

func (h Handler) InspectNanoTDF(toInspect []byte) (TDFInspect, []error) {
header, size, err := sdk.NewNanoTDFHeaderFromReader(bytes.NewReader(toInspect))
if err != nil {
return TDFInspect{}, []error{errors.Join(ErrTDFInspectFailNotValidTDF, err)}
}
r := TDFInspect{
NanoHeader: &header,
}
remainder := uint32(len(toInspect)) - size
if remainder < 18 {
return r, []error{ErrTDFInspectFailNotValidTDF}
}
return r, nil
}
9 changes: 9 additions & 0 deletions tests/encrypt-decrypt.bats
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,15 @@ teardown() {
diff $INFILE_GO_MOD $RESULTFILE_GO_MOD
}

@test "roundtrip NANO, no attributes, file, ecdsa binding" {
./otdfctl encrypt -o $OUTFILE_GO_MOD --host $HOST --tls-no-verify $DEBUG_LEVEL $WITH_CREDS --ecdsa-binding --tdf-type nano $INFILE_GO_MOD
./otdfctl decrypt -o $RESULTFILE_GO_MOD --host $HOST --tls-no-verify $DEBUG_LEVEL $WITH_CREDS --tdf-type nano $OUTFILE_GO_MOD
diff $INFILE_GO_MOD $RESULTFILE_GO_MOD
./otdfctl --host $HOST --tls-no-verify $WITH_CREDS inspect $OUTFILE_GO_MOD
ecdsa_enabled="$(./otdfctl --host $HOST --tls-no-verify $WITH_CREDS inspect $OUTFILE_GO_MOD | jq .ecdsaEnabled)"
[[ "$ecdsa_enabled" == true ]]
}

@test "roundtrip NANO, one attribute, stdin" {
echo $SECRET_TEXT | ./otdfctl encrypt --tdf-type nano -o $OUT_TXT --host $HOST --tls-no-verify $DEBUG_LEVEL $WITH_CREDS -a $FQN
./otdfctl decrypt --tdf-type nano --host $HOST --tls-no-verify $DEBUG_LEVEL $WITH_CREDS $OUTFILE_TXT | grep "$SECRET_TEXT"
Expand Down
Loading