Skip to content

Commit

Permalink
Serializing ciphertext with 32-bit prefixes.
Browse files Browse the repository at this point in the history
Notice about ciphertext change and testing format.

Previously, tkn20 ciphertext was encoding the ciphertext header
`C1`, the envelope `env` (containing inner ciphertext), and
macData using 16-bit prefixes, which caused a limitation on
the maximum size allowed for encrypting plaintexts.

With this change, the encoding now uses 32-bit prefixes for
these three elements allowing to encrypt plaintexts longer
than 2^16 bytes. So, ciphertexts produced by tkn20 package are
now 12 bytes longer.

Ciphertexts in the previous format are still decryptable.
The following functions are backwards-compatible:
 - AttributeKey.Decrypt
 - Attributes.CouldDecrypt
 - Policy.ExtractFromCiphertext
  • Loading branch information
armfazh committed Apr 24, 2024
1 parent a4252c7 commit 4bb5601
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 18 deletions.
2 changes: 1 addition & 1 deletion abe/cpabe/tkn20/example_test.go
Expand Up @@ -132,6 +132,6 @@ func Example() {
// Output:
// (occupation:doctor and country:US)
// plaintext size: 27 bytes
// ciphertext size: 2735 bytes
// ciphertext size: 2747 bytes
// Successfully recovered plaintext
}
17 changes: 16 additions & 1 deletion abe/cpabe/tkn20/format_test.go
Expand Up @@ -41,8 +41,23 @@ func TestAttributeKeyFormat(t *testing.T) {
}
}

func TestCiphertext_v137(t *testing.T) {
// As of v1.3.8 ciphertext format changed to use wider prefixes.
// Ciphertexts in the previous format are still decryptable.
// The following functions are backwards-compatible:
// - AttributeKey.Decrypt
// - Attributes.CouldDecrypt
// - Policy.ExtractFromCiphertext
testCiphertext(t, "testdata/ciphertext_v137")
}

func TestCiphertext(t *testing.T) {
ciphertext, err := os.ReadFile("testdata/ciphertext")
testCiphertext(t, "testdata/ciphertext")
}

func testCiphertext(t *testing.T, ctName string) {
t.Logf("Checking ciphertext: %v\n", ctName)
ciphertext, err := os.ReadFile(ctName)
if err != nil {
t.Fatalf("Unable to read ciphertext data")
}
Expand Down
46 changes: 32 additions & 14 deletions abe/cpabe/tkn20/internal/tkn/bk.go
@@ -1,6 +1,7 @@
package tkn

import (
"bytes"
"crypto/subtle"
"fmt"
"io"
Expand All @@ -20,6 +21,9 @@ import (
// for our output size of 256 bits.
const macKeySeedSize = 72

// As of v1.3.8, ciphertexts are prefixed with this string.
const CiphertextVersion = "v1.3.8"

func blakeEncrypt(key []byte, msg []byte) ([]byte, error) {
xof, err := blake2b.NewXOF(blake2b.OutputLengthUnknown, key)
if err != nil {
Expand Down Expand Up @@ -117,39 +121,51 @@ func EncryptCCA(rand io.Reader, public *PublicParams, policy *Policy, msg []byte
if err != nil {
return nil, err
}
macData := appendLenPrefixed(nil, C1)
macData = appendLenPrefixed(macData, env)
macData := appendLen32Prefixed(nil, C1)
macData = appendLen32Prefixed(macData, env)

tag, err := blakeMac(macKey, macData)
if err != nil {
return nil, err
}

ret := appendLenPrefixed(nil, id)
ret = appendLenPrefixed(ret, macData)
ret := append([]byte{}, []byte(CiphertextVersion)...)
ret = appendLenPrefixed(ret, id)
ret = appendLen32Prefixed(ret, macData)
ret = appendLenPrefixed(ret, tag)

return ret, nil
}

type rmLenPref = func([]byte) ([]byte, []byte, error)

func checkCiphertextFormat(ciphertext []byte) (ct []byte, fn rmLenPref) {
const N = len(CiphertextVersion)
if bytes.Equal(ciphertext[0:N], []byte(CiphertextVersion)) {
return ciphertext[N:], removeLen32Prefixed
}
return ciphertext, removeLenPrefixed
}

func DecryptCCA(ciphertext []byte, key *AttributesKey) ([]byte, error) {
id, rest, err := removeLenPrefixed(ciphertext)
rest, removeLenPrefixedVar := checkCiphertextFormat(ciphertext)
id, rest, err := removeLenPrefixed(rest)
if err != nil {
return nil, err
}
macData, rest, err := removeLenPrefixed(rest)
macData, rest, err := removeLenPrefixedVar(rest)
if err != nil {
return nil, err
}
tag, _, err := removeLenPrefixed(rest)
if err != nil {
return nil, err
}
C1, envRaw, err := removeLenPrefixed(macData)
C1, envRaw, err := removeLenPrefixedVar(macData)
if err != nil {
return nil, err
}
env, _, err := removeLenPrefixed(envRaw)
env, _, err := removeLenPrefixedVar(envRaw)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -208,15 +224,16 @@ func DecryptCCA(ciphertext []byte, key *AttributesKey) ([]byte, error) {
}

func CouldDecrypt(ciphertext []byte, a *Attributes) bool {
id, rest, err := removeLenPrefixed(ciphertext)
rest, removeLenPrefixedVar := checkCiphertextFormat(ciphertext)
id, rest, err := removeLenPrefixed(rest)
if err != nil {
return false
}
macData, _, err := removeLenPrefixed(rest)
macData, _, err := removeLenPrefixedVar(rest)
if err != nil {
return false
}
C1, _, err := removeLenPrefixed(macData)
C1, _, err := removeLenPrefixedVar(macData)
if err != nil {
return false
}
Expand All @@ -237,15 +254,16 @@ func CouldDecrypt(ciphertext []byte, a *Attributes) bool {
}

func (p *Policy) ExtractFromCiphertext(ct []byte) error {
_, rest, err := removeLenPrefixed(ct)
rest, removeLenPrefixedVar := checkCiphertextFormat(ct)
_, rest, err := removeLenPrefixed(rest)
if err != nil {
return fmt.Errorf("invalid ciphertext")
}
macData, _, err := removeLenPrefixed(rest)
macData, _, err := removeLenPrefixedVar(rest)
if err != nil {
return fmt.Errorf("invalid ciphertext")
}
C1, _, err := removeLenPrefixed(macData)
C1, _, err := removeLenPrefixedVar(macData)
if err != nil {
return fmt.Errorf("invalid ciphertext")
}
Expand Down
27 changes: 25 additions & 2 deletions abe/cpabe/tkn20/internal/tkn/util.go
Expand Up @@ -42,14 +42,14 @@ func HashStringToScalar(key []byte, value string) *pairing.Scalar {
return s
}

func appendLenPrefixed(a []byte, b []byte) []byte {
func appendLen16Prefixed(a []byte, b []byte) []byte {
a = append(a, 0, 0)
binary.LittleEndian.PutUint16(a[len(a)-2:], uint16(len(b)))
a = append(a, b...)
return a
}

func removeLenPrefixed(data []byte) (next []byte, remainder []byte, err error) {
func removeLen16Prefixed(data []byte) (next []byte, remainder []byte, err error) {
if len(data) < 2 {
return nil, nil, fmt.Errorf("data too short")
}
Expand All @@ -60,6 +60,29 @@ func removeLenPrefixed(data []byte) (next []byte, remainder []byte, err error) {
return data[2 : 2+itemLen], data[2+itemLen:], nil
}

var (
appendLenPrefixed = appendLen16Prefixed
removeLenPrefixed = removeLen16Prefixed
)

func appendLen32Prefixed(a []byte, b []byte) []byte {
a = append(a, 0, 0, 0, 0)
binary.LittleEndian.PutUint32(a[len(a)-4:], uint32(len(b)))
a = append(a, b...)
return a
}

func removeLen32Prefixed(data []byte) (next []byte, remainder []byte, err error) {
if len(data) < 4 {
return nil, nil, fmt.Errorf("data too short")
}
itemLen := int(binary.LittleEndian.Uint32(data))
if (4 + itemLen) > len(data) {
return nil, nil, fmt.Errorf("data too short")
}
return data[4 : 4+itemLen], data[4+itemLen:], nil
}

func marshalBinarySortedMapMatrixG1(m map[string]*matrixG1) ([]byte, error) {
sortedKeys := make([]string, 0, len(m))
for key := range m {
Expand Down
Binary file modified abe/cpabe/tkn20/testdata/ciphertext
Binary file not shown.
Binary file added abe/cpabe/tkn20/testdata/ciphertext_v137
Binary file not shown.
9 changes: 9 additions & 0 deletions abe/cpabe/tkn20/tkn20.go
Expand Up @@ -8,6 +8,15 @@
// attribute-based encryption. In A. Kiayias, M. Kohlweiss, P. Wallden, and
// V. Zikas, editors, PKC, volume 12110 of Lecture Notes in Computer Science,
// pages 3–33. Springer, 2020. https://eprint.iacr.org/2019/966
//
// # Update v1.3.8
//
// As of v1.3.8, ciphertext format changed to use wider prefixes.
// Ciphertexts in the previous format are still decryptable.
// The following functions are backwards-compatible:
// - [AttributeKey.Decrypt]
// - [Attributes.CouldDecrypt]
// - [Policy.ExtractFromCiphertext]
package tkn20

import (
Expand Down

0 comments on commit 4bb5601

Please sign in to comment.