From 0cb49a596ac60135583a66ba3ed3aa4a03f0f2cf Mon Sep 17 00:00:00 2001 From: armfazh Date: Wed, 13 Mar 2024 15:00:19 -0700 Subject: [PATCH] Serializing ciphertext with 32-bit prefixes. 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 --- abe/cpabe/tkn20/example_test.go | 2 +- abe/cpabe/tkn20/format_test.go | 17 ++++++++- abe/cpabe/tkn20/internal/tkn/bk.go | 46 ++++++++++++++++------- abe/cpabe/tkn20/internal/tkn/util.go | 27 ++++++++++++- abe/cpabe/tkn20/testdata/ciphertext | Bin 2376 -> 2388 bytes abe/cpabe/tkn20/testdata/ciphertext_v137 | Bin 0 -> 2376 bytes abe/cpabe/tkn20/tkn20.go | 9 +++++ 7 files changed, 83 insertions(+), 18 deletions(-) create mode 100644 abe/cpabe/tkn20/testdata/ciphertext_v137 diff --git a/abe/cpabe/tkn20/example_test.go b/abe/cpabe/tkn20/example_test.go index 6e2c378af..bd880333d 100644 --- a/abe/cpabe/tkn20/example_test.go +++ b/abe/cpabe/tkn20/example_test.go @@ -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 } diff --git a/abe/cpabe/tkn20/format_test.go b/abe/cpabe/tkn20/format_test.go index f8c464d6b..35b64e746 100644 --- a/abe/cpabe/tkn20/format_test.go +++ b/abe/cpabe/tkn20/format_test.go @@ -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") } diff --git a/abe/cpabe/tkn20/internal/tkn/bk.go b/abe/cpabe/tkn20/internal/tkn/bk.go index 1d3fc3657..c1e985d77 100644 --- a/abe/cpabe/tkn20/internal/tkn/bk.go +++ b/abe/cpabe/tkn20/internal/tkn/bk.go @@ -1,6 +1,7 @@ package tkn import ( + "bytes" "crypto/subtle" "fmt" "io" @@ -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 CIPHERTEXT_VERSION = "v1.3.8" + func blakeEncrypt(key []byte, msg []byte) ([]byte, error) { xof, err := blake2b.NewXOF(blake2b.OutputLengthUnknown, key) if err != nil { @@ -117,27 +121,39 @@ 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(CIPHERTEXT_VERSION)...) + 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(CIPHERTEXT_VERSION) + if bytes.Equal(ciphertext[0:N], []byte(CIPHERTEXT_VERSION)) { + 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 } @@ -145,11 +161,11 @@ func DecryptCCA(ciphertext []byte, key *AttributesKey) ([]byte, error) { 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 } @@ -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 } @@ -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") } diff --git a/abe/cpabe/tkn20/internal/tkn/util.go b/abe/cpabe/tkn20/internal/tkn/util.go index 9afbe88a7..0c0f94f1c 100644 --- a/abe/cpabe/tkn20/internal/tkn/util.go +++ b/abe/cpabe/tkn20/internal/tkn/util.go @@ -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") } @@ -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 { diff --git a/abe/cpabe/tkn20/testdata/ciphertext b/abe/cpabe/tkn20/testdata/ciphertext index d96e2cb75943a60155431f099eeb8bb62450097b..19648be90b389f034fb7278520ea595a1221da87 100644 GIT binary patch delta 65 zcmV-H0KWgo64Vj~b}=q9E;x`P2nGoN0G9{=0I?>S2mt_-z6fd{N9_UP8EYF4^zTt? XX*$6d3sb?n8ZV7#-L?z0NlbrMZuk|g delta 53 zcmV-50LuT=637yf1|kFk36}`5FqsIG%Lr*8v18l>z>1#Y0J>T7f?LRm?uQy;7qKa; L)RND9a<<0CaM7Wgq}P>DoIQ`yUHMz!^r&Gs0vD+3?cM171^6Id@7B6OncS1Oo&B z0RR?=JqnOtRB4v*<)8zgjI!7#{w)9`|?klL);Rs(kk75xSkCF=U zAy}po7_HFBES4izJ$adsO`>mHFN_vjpNouZha!moC-c?lr_~I!p3>!`T>J->dVzUM zg@5!&nmC;_@FQ~CVK}+qG>B-19h+gu`12cQ|7Eq9XQXM~wjPWZPRbecR2^QPZbsvw za)#T~w%k-+<>~xJ|BA>ZmTn!Qyz+up0Qc*MFPi6|)t>|<>6sbGCNp0SsKtC3IBD`( zp~Rka&7p|AFLp`cU7MROBZv9Byfh8Zg+uYg#mv^|QUDKQ*0t)>caO{JaITeIX@BGK zkF^CXB+yRpZEigxR2{8iFe!SzuR2I~`)@5N1!xGjaI8r%b$5i~IRsZQ75Et0x%+5M z=XqR9p}eV20z@rd4TtN;7l_Ba!n;wp>tO^y&FJ^Eb>L5aDW5-Qk58Lb@H@<(|N!Za=07@344R&nJ^J4;a$ zp*aP1r`$ldM0(D_+kQmqu3av@LwC=Laj+SqoocCnPZe9f z@)s^5hTfW8;)NCnnRT84t)KPFs<$mdrTtRZ=L{x2IN~^D zS$&~cbMqDd3`^+=$in9m;e*am_2MHEYlWEWzRfI0V!c$FwkfmEAfMb%s-e`@zrjka ztO=r0!u1g< zn0j9CS~$S{=4<{syZ{IEqSQHh!$2_^6Ow+|#i}no5B9yT=tGijC2da?_{xc$(*S32VY<%NHQ0UzcF!ahffZi7f^T@E|Kq zd5)ZsOn{R9*YQhYihA&Q+A3?-+ zasbX3xO`INX{iCs#|{>#Y*u7sAuXW$<`YR>O~nH48guoesx%h_9P>1jW?hy4$>8Xx|wQ-=o9bR=w|+ z%wHH0`9(>xgt`{soY&G3uMB+Q|vz z=3+7d52mn=V%{qKCT`j*e{JP*qCd zmc=TJA%r6GLBZi9?sk?FkNX)I8LOQK<|%nmUlPeEr3=NnpcbN;f@7pimE1_*xTKF3 z#uac-{;5cN{1Aw>F#iuT)DwG}hfQxSxaN1sBfOll^VnJXBXZ{YS0PORlRmYmdqS}s zQ0T6cAld6f=4KB=vD&2<6vNbA>1kY}))m9*Y`Ag?VWXz}U(_*1W$eSV=>R)ERn zk#W&sKP3eVH~xsf-Wdmyo*p#{@Pg@LfEZuWl?P%qdhX8<8{BHOx zwZ#bAt_;gDfJN=z-Qy7>+cQB~EbZn6bW|5}mLK?<5kprjQi4PLtr@;lm7nzUn^+d+ zgf@FP9WTEeS^j~UlSUWAMS*-%zWwm-bkUjZ+p4n$kWIWA_24Bj3n_{X9$*WDgV8R$ z`RM9f9iheLg&N)RS6b5RL~XZi#I0Y|nOyv~uPxAqyr>*u^MMc2ovakRMD@-xbM){r z5w2B=vMhp4IC6XDo|uXpQExe4m#DSf8=^ceRo`i0=z}-NWp)ApBmn~e0RR&-=X``! zKjJlhtp}AXUw%W?6+6w`ETMcEutD=Mn zPv_G<^}ndEB_cD+N)A((Do5_xUJ}QfZBYBk653W)h5GEDB)b`Pv?{rh^Q!z)W58p! z+zIbVOuYI9+No57U$Q_^(jN?7(QC5Yjj(n3E-{&-Gnh8>wPAFOzs}|f$g}1V^ zxGxtv-@y{NtZGUJ&Ath^5+B+l@OsOF0q;obpyJ}5|9z(v)*(q(lOvZon--vpUbl#5 z${n4h6<&U^#t&uk5IZF+r=x$wEl@Riiz+^frWxUYAwS!%bmVbK(x9j`=$GE5LzPdh z{7bN*T-qQJ4kidVMX6$6YJzK?1`_QAtY!;?roDZe<(@tq3*0)Mi+yL{TnmN zP5mLsHqYx%4!*{VFl*hB1o1~^Gw|c#TUJb`OxOv?bTX_W62ttCiz@M9ytN`qtU3^8 uRrQy@pqTgZv-hZ$6lFxi{w%c{&8hLa8 literal 0 HcmV?d00001 diff --git a/abe/cpabe/tkn20/tkn20.go b/abe/cpabe/tkn20/tkn20.go index 56d4e2014..e03dc0e56 100644 --- a/abe/cpabe/tkn20/tkn20.go +++ b/abe/cpabe/tkn20/tkn20.go @@ -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 (