Skip to content

Commit

Permalink
Add EncryptBytes() and DecryptBytes() to the crypt API.
Browse files Browse the repository at this point in the history
Most encrypted secrets are strings, but encryption keys are sometimes
used to secure binary data, which is not possible with the current API
without an additional encoding step.

This commit adds methods for this usecase.

Signed-off-by: Aaron Jacobs <[email protected]>
  • Loading branch information
atheriel committed Feb 9, 2022
1 parent 0e29d3c commit 76c9608
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 10 deletions.
21 changes: 17 additions & 4 deletions crypt/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,13 @@ func (k *Key) base64String() string {
// Encrypt produces base64-encoded cipher text for the given payload and key, or
// an error if one cannot be created.
func (k *Key) Encrypt(s string) (string, error) {
output, err := k.encryptSecretbox(s)
return k.EncryptBytes([]byte(s))
}

// EncryptBytes produces base64-encoded cipher text for the given bytes and key,
// or an error if one cannot be created.
func (k *Key) EncryptBytes(bytes []byte) (string, error) {
output, err := k.encryptSecretbox(bytes)
if err != nil {
return "", err
}
Expand All @@ -111,7 +117,7 @@ func (k *Key) Encrypt(s string) (string, error) {
// version for the given payload and key, or an error if one cannot be created.
// This emulates the format used by some implementations.
func (k *Key) encryptVersioned(s string) (string, error) {
output, err := k.encryptSecretbox(s)
output, err := k.encryptSecretbox([]byte(s))
if err != nil {
return "", err
}
Expand All @@ -122,12 +128,19 @@ func (k *Key) encryptVersioned(s string) (string, error) {
// Decrypt takes base64-encoded cipher text encrypted with the given key and
// returns the original clear text, or an error.
func (k *Key) Decrypt(s string) (string, error) {
bytes, err := k.DecryptBytes(s)
return string(bytes), err
}

// DecryptBytes takes base64-encoded cipher text encrypted with the given key
// and returns the original bytes, or an error.
func (k *Key) DecryptBytes(s string) ([]byte, error) {
buf, err := base64.StdEncoding.DecodeString(s)
if err != nil {
return "", fmt.Errorf("invalid decryption payload: %v", err)
return []byte{}, fmt.Errorf("invalid decryption payload: %v", err)
}
if len(buf) < 1 {
return "", ErrPayLoadTooShort
return []byte{}, ErrPayLoadTooShort
}
// Some implementations use a version-prefixed cipher text. In order to
// handle the (unlikely but possible) case where a versionless payload
Expand Down
18 changes: 18 additions & 0 deletions crypt/key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,24 @@ func (s *KeySuite) TestVersionedEncryption(c *check.C) {
c.Check(err, check.ErrorMatches, `cannot read`)
}

func (s *KeySuite) TestByteEncryption(c *check.C) {
key, _ := NewKey()

// Not everything you might want to encrypt is a valid UTF-8 string.
bytes := []byte{
0x80, 0x3a, 0x42, 0x5e, 0xef, 0x19, 0x11, 0xdd, 0x39, 0x46,
0x81, 0x14, 0x19, 0xdc, 0xe7, 0x3e, 0xc9, 0x0c, 0xfe, 0x5b,
0xe5, 0x92, 0x0c, 0xa0, 0xcf, 0xa1, 0xf7, 0x13, 0xd8, 0x7a,
}

// Roundtrip encryption test.
cipher, err := key.EncryptBytes(bytes)
c.Check(err, check.IsNil)
out, err := key.DecryptBytes(cipher)
c.Check(err, check.IsNil)
c.Check(out, check.DeepEquals, bytes)
}

func Test(t *testing.T) {
_ = check.Suite(&KeySuite{})
check.TestingT(t)
Expand Down
12 changes: 6 additions & 6 deletions crypt/nacl.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,30 @@ const (
minimumSecretboxLength = secretbox.Overhead + 24
)

func (k *Key) encryptSecretbox(s string) ([]byte, error) {
func (k *Key) encryptSecretbox(bytes []byte) ([]byte, error) {
var nonce [24]byte
_, err := rand.Read(nonce[:])
if err != nil {
return []byte{}, err
}
output := secretbox.Seal(nil, []byte(s), &nonce, k.key32())
output := secretbox.Seal(nil, bytes, &nonce, k.key32())
output = append(nonce[:], output...)
return output, nil
}

func (k *Key) decryptSecretbox(buf []byte) (string, error) {
func (k *Key) decryptSecretbox(buf []byte) ([]byte, error) {
if len(buf) < minimumSecretboxLength {
return "", ErrPayLoadTooShort
return []byte{}, ErrPayLoadTooShort
}

var nonce [24]byte
copy(nonce[0:24], buf[0:24])

bytes, ok := secretbox.Open(nil, buf[24:], &nonce, k.key32())
if !ok {
return "", ErrFailedToDecrypt
return []byte{}, ErrFailedToDecrypt
}
return string(bytes[:]), nil
return bytes, nil
}

// NACL Secretbox only uses 32 bytes, so we pass it the *first* 32 bytes of the
Expand Down

0 comments on commit 76c9608

Please sign in to comment.