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

Update marcellanz/transit_pkcs1v15 RSA encryption support #25486

Merged
merged 39 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
381d091
[transit-pkcs1v15] transit support for the pkcs1v15 padding scheme – …
marcellanz Jul 19, 2022
e61c72f
Merge branch 'hashicorp:main' into feature/transit_pkcs1v15_support
marcellanz Jul 19, 2022
6d92f49
Merge branch 'hashicorp:main' into feature/transit_pkcs1v15_support
marcellanz Jul 19, 2022
9b57e68
Merge branch 'hashicorp:main' into feature/transit_pkcs1v15_support
marcellanz Jul 19, 2022
91d11dc
[transit-pkcs1v15] renamed padding_scheme parameter in transit docume…
marcellanz Jul 19, 2022
cbc17d1
Merge remote-tracking branch 'upstream/feature/transit_pkcs1v15_suppo…
marcellanz Jul 19, 2022
31b0c51
[transit-pkcs1v15] add changelog file.
marcellanz Jul 19, 2022
8a7728d
[transit-pkcs1v15] remove the algorithm path as padding_scheme is cho…
marcellanz Jul 19, 2022
99a82d4
Update ui/app/templates/components/transit-key-action/datakey.hbs
marcellanz Aug 16, 2023
a19b563
Update ui/app/templates/components/transit-key-action/datakey.hbs
marcellanz Aug 16, 2023
efa5fb1
Update ui/app/templates/components/transit-key-action/datakey.hbs
marcellanz Aug 16, 2023
9b56e02
Update website/content/api-docs/secret/transit.mdx
marcellanz Nov 2, 2023
8f79fc0
Update website/content/api-docs/secret/transit.mdx
marcellanz Nov 2, 2023
ee4a3c3
Update website/content/api-docs/secret/transit.mdx
marcellanz Nov 2, 2023
1026a1e
Modify the community PKCS1v15 encryption padding PR for the factory s…
sgmiller Feb 16, 2024
6128b7f
Add warnings to PKCS1v1.5 usage
sgmiller Feb 16, 2024
13a28a3
Update transit
sgmiller Feb 16, 2024
c67784d
Update transit, including separating encrypt/decrypt paddings for rewrap
sgmiller Feb 16, 2024
873157c
Clean up factory use in the presence of padding
sgmiller Feb 16, 2024
fe5a01f
Merge branch 'sgm/pkcs15-padding-conflicts' of github.com:/hashicorp/…
sgmiller Feb 16, 2024
3614da2
address review feedback
sgmiller Feb 21, 2024
6663a6d
resolve ui conflicts
sgmiller Feb 21, 2024
b19aff2
remove defaults
sgmiller Feb 21, 2024
c47d53c
lint
sgmiller Feb 21, 2024
6a6075d
more lint
sgmiller Feb 21, 2024
2c085fb
Some fixes for UI issues
stevendpclark Feb 22, 2024
93cec1e
Fix Transit rewrap API to use decrypt_padding_scheme, encrypt_padding…
stevendpclark Feb 22, 2024
4c23b97
Fix code linting issues
stevendpclark Feb 23, 2024
8bd7e2f
Merge remote-tracking branch 'origin/main' into sgm/pkcs15-padding-co…
sgmiller Oct 3, 2024
abbed2a
simply padding scheme enum
sgmiller Oct 3, 2024
389bf49
Apply suggestions from code review
stevendpclark Oct 4, 2024
f0add52
Fix padding_scheme processing on data key api
stevendpclark Oct 4, 2024
cbba3df
Add tests for parsePaddingSchemeArg
stevendpclark Oct 7, 2024
66785d5
Add missing copywrite headers
stevendpclark Oct 7, 2024
f1d9bc8
Some small UI fixes
stevendpclark Oct 7, 2024
e1a831a
Add missing param to datakey in api-docs
stevendpclark Oct 7, 2024
31d0bef
Do not send padding_scheme for non-RSA key types within UI
stevendpclark Oct 7, 2024
89c8685
add UI tests for transit key actions form
hellobontempo Oct 8, 2024
fc0978f
Merge branch 'main' into sgm/pkcs15-padding-conflicts
stevendpclark Oct 9, 2024
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
145 changes: 79 additions & 66 deletions builtin/logical/transit/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,83 +148,96 @@ func testTransit_RSA(t *testing.T, keyType string) {

plaintext := "dGhlIHF1aWNrIGJyb3duIGZveA==" // "the quick brown fox"

encryptReq := &logical.Request{
Path: "encrypt/rsa",
Operation: logical.UpdateOperation,
Storage: storage,
Data: map[string]interface{}{
"plaintext": plaintext,
},
}
for _, padding := range []keysutil.PaddingScheme{keysutil.PaddingScheme_OAEP, keysutil.PaddingScheme_PKCS1v15, ""} {
encryptReq := &logical.Request{
Path: "encrypt/rsa",
Operation: logical.UpdateOperation,
Storage: storage,
Data: map[string]interface{}{
"plaintext": plaintext,
},
}

resp, err = b.HandleRequest(context.Background(), encryptReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v\nresp: %#v", err, resp)
}
if padding != "" {
encryptReq.Data["padding_scheme"] = padding
}

ciphertext1 := resp.Data["ciphertext"].(string)
resp, err = b.HandleRequest(context.Background(), encryptReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v\nresp: %#v", err, resp)
}

decryptReq := &logical.Request{
Path: "decrypt/rsa",
Operation: logical.UpdateOperation,
Storage: storage,
Data: map[string]interface{}{
"ciphertext": ciphertext1,
},
}
ciphertext1 := resp.Data["ciphertext"].(string)

resp, err = b.HandleRequest(context.Background(), decryptReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v\nresp: %#v", err, resp)
}
decryptReq := &logical.Request{
Path: "decrypt/rsa",
Operation: logical.UpdateOperation,
Storage: storage,
Data: map[string]interface{}{
"ciphertext": ciphertext1,
},
}
if padding != "" {
decryptReq.Data["padding_scheme"] = padding
}

decryptedPlaintext := resp.Data["plaintext"]
resp, err = b.HandleRequest(context.Background(), decryptReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v\nresp: %#v", err, resp)
}

if plaintext != decryptedPlaintext {
t.Fatalf("bad: plaintext; expected: %q\nactual: %q", plaintext, decryptedPlaintext)
}
decryptedPlaintext := resp.Data["plaintext"]

// Rotate the key
rotateReq := &logical.Request{
Path: "keys/rsa/rotate",
Operation: logical.UpdateOperation,
Storage: storage,
}
resp, err = b.HandleRequest(context.Background(), rotateReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v\nresp: %#v", err, resp)
}
if plaintext != decryptedPlaintext {
t.Fatalf("bad: plaintext; expected: %q\nactual: %q", plaintext, decryptedPlaintext)
}

// Encrypt again
resp, err = b.HandleRequest(context.Background(), encryptReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v\nresp: %#v", err, resp)
}
ciphertext2 := resp.Data["ciphertext"].(string)
// Rotate the key
rotateReq := &logical.Request{
Path: "keys/rsa/rotate",
Operation: logical.UpdateOperation,
Storage: storage,
}
resp, err = b.HandleRequest(context.Background(), rotateReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v\nresp: %#v", err, resp)
}

if ciphertext1 == ciphertext2 {
t.Fatalf("expected different ciphertexts")
}
// Encrypt again
resp, err = b.HandleRequest(context.Background(), encryptReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v\nresp: %#v", err, resp)
}
ciphertext2 := resp.Data["ciphertext"].(string)

// See if the older ciphertext can still be decrypted
resp, err = b.HandleRequest(context.Background(), decryptReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v\nresp: %#v", err, resp)
}
if resp.Data["plaintext"].(string) != plaintext {
t.Fatal("failed to decrypt old ciphertext after rotating the key")
}
if ciphertext1 == ciphertext2 {
t.Fatalf("expected different ciphertexts")
}

// Decrypt the new ciphertext
decryptReq.Data = map[string]interface{}{
"ciphertext": ciphertext2,
}
resp, err = b.HandleRequest(context.Background(), decryptReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v\nresp: %#v", err, resp)
}
if resp.Data["plaintext"].(string) != plaintext {
t.Fatal("failed to decrypt ciphertext after rotating the key")
// See if the older ciphertext can still be decrypted
resp, err = b.HandleRequest(context.Background(), decryptReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v\nresp: %#v", err, resp)
}
if resp.Data["plaintext"].(string) != plaintext {
t.Fatal("failed to decrypt old ciphertext after rotating the key")
}

// Decrypt the new ciphertext
decryptReq.Data = map[string]interface{}{
sgmiller marked this conversation as resolved.
Show resolved Hide resolved
"ciphertext": ciphertext2,
}
if padding != "" {
decryptReq.Data["padding_scheme"] = padding
}

resp, err = b.HandleRequest(context.Background(), decryptReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v\nresp: %#v", err, resp)
}
if resp.Data["plaintext"].(string) != plaintext {
t.Fatal("failed to decrypt ciphertext after rotating the key")
}
}

signReq := &logical.Request{
Expand Down
17 changes: 13 additions & 4 deletions builtin/logical/transit/path_datakey.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ func (b *backend) pathDatakey() *framework.Path {
ciphertext; "wrapped" will return the ciphertext only.`,
},

"padding_scheme": {
stevendpclark marked this conversation as resolved.
Show resolved Hide resolved
Type: framework.TypeString,
Description: `The padding scheme to use for decrypt. Currently only applies to RSA key types.
Options are 'oaep' or 'pkcs1v15'. Defaults to 'oaep'`,
},

"context": {
Type: framework.TypeString,
Description: "Context for key derivation. Required for derived keys.",
Expand Down Expand Up @@ -142,23 +148,26 @@ func (b *backend) pathDatakeyWrite(ctx context.Context, req *logical.Request, d
return nil, err
}

var managedKeyFactory ManagedKeyFactory
factories := make([]any, 0)
if ps, ok := d.GetOk("decrypt_padding_scheme"); ok {
factories = append(factories, keysutil.PaddingScheme(ps.(string)))
}
if p.Type == keysutil.KeyType_MANAGED_KEY {
managedKeySystemView, ok := b.System().(logical.ManagedKeySystemView)
if !ok {
return nil, errors.New("unsupported system view")
}

managedKeyFactory = ManagedKeyFactory{
factories = append(factories, ManagedKeyFactory{
managedKeyParams: keysutil.ManagedKeyParameters{
ManagedKeySystemView: managedKeySystemView,
BackendUUID: b.backendUUID,
Context: ctx,
},
}
})
}

ciphertext, err := p.EncryptWithFactory(ver, context, nonce, base64.StdEncoding.EncodeToString(newKey), nil, managedKeyFactory)
ciphertext, err := p.EncryptWithFactory(ver, context, nonce, base64.StdEncoding.EncodeToString(newKey), factories...)
if err != nil {
switch err.(type) {
case errutil.UserError:
Expand Down
23 changes: 17 additions & 6 deletions builtin/logical/transit/path_decrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ func (b *backend) pathDecrypt() *framework.Path {
The ciphertext to decrypt, provided as returned by encrypt.`,
},

"padding_scheme": {
Type: framework.TypeString,
Description: `The padding scheme to use for decrypt. Currently only applies to RSA key types.
Options are 'oaep' or 'pkcs1v15'. Defaults to 'oaep'`,
},

"context": {
Type: framework.TypeString,
Description: `
Expand Down Expand Up @@ -130,6 +136,9 @@ func (b *backend) pathDecryptWrite(ctx context.Context, req *logical.Request, d
Nonce: d.Get("nonce").(string),
AssociatedData: d.Get("associated_data").(string),
}
if ps, ok := d.GetOk("padding_scheme"); ok {
batchInputItems[0].PaddingScheme = ps.(string)
}
}

batchResponseItems := make([]DecryptBatchResponseItem, len(batchInputItems))
Expand Down Expand Up @@ -192,33 +201,35 @@ func (b *backend) pathDecryptWrite(ctx context.Context, req *logical.Request, d
continue
}

var factory interface{}
var factories []any
if item.PaddingScheme != "" {
factories = append(factories, keysutil.PaddingScheme(item.PaddingScheme))
}
if item.AssociatedData != "" {
if !p.Type.AssociatedDataSupported() {
batchResponseItems[i].Error = fmt.Sprintf("'[%d].associated_data' provided for non-AEAD cipher suite %v", i, p.Type.String())
continue
}

factory = AssocDataFactory{item.AssociatedData}
factories = append(factories, AssocDataFactory{item.AssociatedData})
}

var managedKeyFactory ManagedKeyFactory
if p.Type == keysutil.KeyType_MANAGED_KEY {
managedKeySystemView, ok := b.System().(logical.ManagedKeySystemView)
if !ok {
batchResponseItems[i].Error = errors.New("unsupported system view").Error()
}

managedKeyFactory = ManagedKeyFactory{
factories = append(factories, ManagedKeyFactory{
managedKeyParams: keysutil.ManagedKeyParameters{
ManagedKeySystemView: managedKeySystemView,
BackendUUID: b.backendUUID,
Context: ctx,
},
}
})
}

plaintext, err := p.DecryptWithFactory(item.DecodedContext, item.DecodedNonce, item.Ciphertext, factory, managedKeyFactory)
plaintext, err := p.DecryptWithFactory(item.DecodedContext, item.DecodedNonce, item.Ciphertext, factories...)
if err != nil {
switch err.(type) {
case errutil.InternalError:
Expand Down
39 changes: 33 additions & 6 deletions builtin/logical/transit/path_encrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ type BatchRequestItem struct {
// Ciphertext for decryption
Ciphertext string `json:"ciphertext" structs:"ciphertext" mapstructure:"ciphertext"`

// PaddingScheme for encryption/decryption
PaddingScheme string `json:"padding_scheme" structs:"padding_scheme" mapstructure:"padding_scheme"`

// Nonce to be used when v1 convergent encryption is used
Nonce string `json:"nonce" structs:"nonce" mapstructure:"nonce"`

Expand Down Expand Up @@ -105,6 +108,12 @@ func (b *backend) pathEncrypt() *framework.Path {
Description: "Base64 encoded plaintext value to be encrypted",
},

"padding_scheme": {
Type: framework.TypeString,
Description: `The padding scheme to use for decrypt. Currently only applies to RSA key types.
Options are 'oaep' or 'pkcs1v15'. Defaults to 'oaep'`,
},

"context": {
Type: framework.TypeString,
Description: "Base64 encoded context for key derivation. Required if key derivation is enabled",
Expand Down Expand Up @@ -259,6 +268,13 @@ func decodeBatchRequestItems(src interface{}, requirePlaintext bool, requireCiph
} else if requirePlaintext {
errs.Errors = append(errs.Errors, fmt.Sprintf("'[%d].plaintext' missing plaintext to encrypt", i))
}
if v, has := item["padding_scheme"]; has {
if casted, ok := v.(string); ok {
(*dst)[i].PaddingScheme = casted
} else {
errs.Errors = append(errs.Errors, fmt.Sprintf("'[%d].padding_scheme' expected type 'string', got unconvertible type '%T'", i, item["padding_scheme"]))
}
}

if v, has := item["nonce"]; has {
if !reflect.ValueOf(v).IsValid() {
Expand Down Expand Up @@ -358,6 +374,9 @@ func (b *backend) pathEncryptWrite(ctx context.Context, req *logical.Request, d
KeyVersion: d.Get("key_version").(int),
AssociatedData: d.Get("associated_data").(string),
}
if ps, ok := d.GetOk("padding_scheme"); ok {
batchInputItems[0].PaddingScheme = ps.(string)
}
}

batchResponseItems := make([]EncryptBatchResponseItem, len(batchInputItems))
Expand Down Expand Up @@ -435,6 +454,12 @@ func (b *backend) pathEncryptWrite(ctx context.Context, req *logical.Request, d
polReq.KeyType = keysutil.KeyType_AES256_GCM96
case "chacha20-poly1305":
polReq.KeyType = keysutil.KeyType_ChaCha20_Poly1305
case "rsa-2048":
polReq.KeyType = keysutil.KeyType_RSA2048
case "rsa-3072":
polReq.KeyType = keysutil.KeyType_RSA3072
case "rsa-4096":
polReq.KeyType = keysutil.KeyType_RSA4096
case "ecdsa-p256", "ecdsa-p384", "ecdsa-p521":
return logical.ErrorResponse(fmt.Sprintf("key type %v not supported for this operation", keyType)), logical.ErrInvalidRequest
case "managed_key":
Expand Down Expand Up @@ -482,33 +507,35 @@ func (b *backend) pathEncryptWrite(ctx context.Context, req *logical.Request, d
warnAboutNonceUsage = true
}

var factory interface{}
var factories []any
if item.PaddingScheme != "" {
factories = append(factories, keysutil.PaddingScheme(item.PaddingScheme))
}
if item.AssociatedData != "" {
if !p.Type.AssociatedDataSupported() {
batchResponseItems[i].Error = fmt.Sprintf("'[%d].associated_data' provided for non-AEAD cipher suite %v", i, p.Type.String())
continue
}

factory = AssocDataFactory{item.AssociatedData}
factories = append(factories, AssocDataFactory{item.AssociatedData})
}

var managedKeyFactory ManagedKeyFactory
if p.Type == keysutil.KeyType_MANAGED_KEY {
managedKeySystemView, ok := b.System().(logical.ManagedKeySystemView)
if !ok {
batchResponseItems[i].Error = errors.New("unsupported system view").Error()
}

managedKeyFactory = ManagedKeyFactory{
factories = append(factories, ManagedKeyFactory{
managedKeyParams: keysutil.ManagedKeyParameters{
ManagedKeySystemView: managedKeySystemView,
BackendUUID: b.backendUUID,
Context: ctx,
},
}
})
}

ciphertext, err := p.EncryptWithFactory(item.KeyVersion, item.DecodedContext, item.DecodedNonce, item.Plaintext, factory, managedKeyFactory)
ciphertext, err := p.EncryptWithFactory(item.KeyVersion, item.DecodedContext, item.DecodedNonce, item.Plaintext, factories...)
if err != nil {
switch err.(type) {
case errutil.InternalError:
Expand Down
Loading
Loading