diff --git a/abe/cpabe/tkn20/gen_testdata.go b/abe/cpabe/tkn20/gen_testdata.go new file mode 100644 index 00000000..9629efcb --- /dev/null +++ b/abe/cpabe/tkn20/gen_testdata.go @@ -0,0 +1,72 @@ +//go:build ignore +// +build ignore + +// Generates golden files for tests. +package main + +import ( + "encoding" + mrand "math/rand" + "os" + "path/filepath" + + cpabe "github.com/cloudflare/circl/abe/cpabe/tkn20" +) + +func writeToFile(name string, data []byte) { + err := os.WriteFile("testdata/"+name, data, 0o644) + if err != nil { + panic(err) + } +} + +func dumpToFile(name string, m encoding.BinaryMarshaler) { + data, err := m.MarshalBinary() + if err != nil { + panic(err) + } + writeToFile(name, data) +} + +func main() { + // Using fixed PRNG for reproducibility, + fixedSeed := int64(0xC1C1C1C1) + prng := mrand.New(mrand.NewSource(fixedSeed)) + if prng == nil { + panic("failed to create PRNG") + } + + err := os.MkdirAll(filepath.Join(".", "testdata"), 0o755) + if err != nil { + panic(err) + } + + publicParams, secretParams, err := cpabe.Setup(prng) + if err != nil { + panic(err) + } + + dumpToFile("publicKey", &publicParams) + dumpToFile("secretKey", &secretParams) + + attrs := cpabe.Attributes{} + attrs.FromMap(map[string]string{"country": "NL", "EU": "true"}) + + policy := cpabe.Policy{} + err = policy.FromString("EU: true") + if err != nil { + panic(err) + } + msg := []byte("Be sure to drink your ovaltine!") + ciphertext, err := publicParams.Encrypt(prng, policy, msg) + if err != nil { + panic(err) + } + writeToFile("ciphertext", ciphertext) + + key, err := secretParams.KeyGen(prng, attrs) + if err != nil { + panic(err) + } + dumpToFile("attributeKey", &key) +} diff --git a/abe/cpabe/tkn20/internal/tkn/policy.go b/abe/cpabe/tkn20/internal/tkn/policy.go index 50292fe7..8e24ad86 100644 --- a/abe/cpabe/tkn20/internal/tkn/policy.go +++ b/abe/cpabe/tkn20/internal/tkn/policy.go @@ -76,14 +76,13 @@ type Attributes map[string]Attribute func (a *Attributes) marshalBinary() ([]byte, error) { ret := make([]byte, 2) binary.LittleEndian.PutUint16(ret[0:], uint16(len(*a))) - for label, attr := range *a { - ret = appendLenPrefixed(ret, []byte(label)) - attrBytes, err := attr.marshalBinary() - if err != nil { - return nil, fmt.Errorf("marshalling Attributes failed: %w", err) - } - ret = append(ret, attrBytes...) + + aBytes, err := marshalBinarySortedMapAttribute(*a) + if err != nil { + return nil, fmt.Errorf("marshalling Attributes failed: %w", err) } + ret = append(ret, aBytes...) + return ret, nil } diff --git a/abe/cpabe/tkn20/internal/tkn/tk.go b/abe/cpabe/tkn20/internal/tkn/tk.go index d6b8c330..606c80f5 100644 --- a/abe/cpabe/tkn20/internal/tkn/tk.go +++ b/abe/cpabe/tkn20/internal/tkn/tk.go @@ -210,24 +210,20 @@ func (a *AttributesKey) MarshalBinary() ([]byte, error) { ret = appendLenPrefixed(ret, k2Bytes) ret = append(ret, 0, 0) binary.LittleEndian.PutUint16(ret[len(ret)-2:], uint16(len(a.k3))) - for s, m := range a.k3 { - ret = appendLenPrefixed(ret, []byte(s)) - g1Bytes, err := m.marshalBinary() - if err != nil { - return nil, fmt.Errorf("AttributesKey serializing failed: %w", err) - } - ret = appendLenPrefixed(ret, g1Bytes) + k3Bytes, err := marshalBinarySortedMapMatrixG1(a.k3) + if err != nil { + return nil, fmt.Errorf("AttributesKey serializing failed: %w", err) } + ret = append(ret, k3Bytes...) + ret = append(ret, 0, 0) binary.LittleEndian.PutUint16(ret[len(ret)-2:], uint16(len(a.k3wild))) - for s, m := range a.k3wild { - ret = appendLenPrefixed(ret, []byte(s)) - g1Bytes, err := m.marshalBinary() - if err != nil { - return nil, fmt.Errorf("AttributesKey serializing failed: %w", err) - } - ret = appendLenPrefixed(ret, g1Bytes) + k3wildBytes, err := marshalBinarySortedMapMatrixG1(a.k3wild) + if err != nil { + return nil, fmt.Errorf("AttributesKey serializing failed: %w", err) } + ret = append(ret, k3wildBytes...) + return ret, nil } diff --git a/abe/cpabe/tkn20/internal/tkn/util.go b/abe/cpabe/tkn20/internal/tkn/util.go index aa9423bf..69a31f18 100644 --- a/abe/cpabe/tkn20/internal/tkn/util.go +++ b/abe/cpabe/tkn20/internal/tkn/util.go @@ -4,6 +4,7 @@ import ( "encoding/binary" "errors" "fmt" + "sort" pairing "github.com/cloudflare/circl/ecc/bls12381" "golang.org/x/crypto/blake2b" @@ -60,6 +61,49 @@ func removeLenPrefixed(data []byte) (next []byte, remainder []byte, err error) { return data[2 : 2+itemLen], data[2+itemLen:], nil } +func marshalBinarySortedMapMatrixG1(m map[string]*matrixG1) ([]byte, error) { + sortedKeys := make([]string, 0, len(m)) + for key := range m { + sortedKeys = append(sortedKeys, key) + } + sort.Strings(sortedKeys) + + ret := []byte{} + for _, key := range sortedKeys { + b, err := m[key].marshalBinary() + if err != nil { + return nil, err + } + + ret = appendLenPrefixed(ret, []byte(key)) + ret = appendLenPrefixed(ret, b) + } + + return ret, nil +} + +func marshalBinarySortedMapAttribute(m map[string]Attribute) ([]byte, error) { + sortedKeys := make([]string, 0, len(m)) + for key := range m { + sortedKeys = append(sortedKeys, key) + } + sort.Strings(sortedKeys) + + ret := []byte{} + for _, key := range sortedKeys { + a := m[key] + b, err := a.marshalBinary() + if err != nil { + return nil, err + } + + ret = appendLenPrefixed(ret, []byte(key)) + ret = append(ret, b...) + } + + return ret, nil +} + var ( errBadMatrixSize = errors.New("matrix inputs do not conform") errMatrixNonInvertible = errors.New("matrix has no inverse") diff --git a/abe/cpabe/tkn20/testdata/attributeKey b/abe/cpabe/tkn20/testdata/attributeKey index 3f6ea037..4b37c6f0 100644 Binary files a/abe/cpabe/tkn20/testdata/attributeKey and b/abe/cpabe/tkn20/testdata/attributeKey differ diff --git a/abe/cpabe/tkn20/testdata/ciphertext b/abe/cpabe/tkn20/testdata/ciphertext index 75974fd9..a7404fe1 100644 Binary files a/abe/cpabe/tkn20/testdata/ciphertext and b/abe/cpabe/tkn20/testdata/ciphertext differ diff --git a/abe/cpabe/tkn20/testdata/publicKey b/abe/cpabe/tkn20/testdata/publicKey index f9866f56..dbe323db 100644 Binary files a/abe/cpabe/tkn20/testdata/publicKey and b/abe/cpabe/tkn20/testdata/publicKey differ diff --git a/abe/cpabe/tkn20/testdata/secretKey b/abe/cpabe/tkn20/testdata/secretKey index 6d79bc86..75adb17e 100644 Binary files a/abe/cpabe/tkn20/testdata/secretKey and b/abe/cpabe/tkn20/testdata/secretKey differ diff --git a/abe/cpabe/tkn20/testdata/serialize.go b/abe/cpabe/tkn20/testdata/serialize.go deleted file mode 100644 index dda77eb0..00000000 --- a/abe/cpabe/tkn20/testdata/serialize.go +++ /dev/null @@ -1,55 +0,0 @@ -//go:build exclude - -package main - -import ( - "log" - "os" - - "code.cfops.it/crypto/cpabe" -) - -func main() { - publicParams, secretParams, err := cpabe.Setup() - ppData, err := publicParams.MarshalBinary() - if err != nil { - log.Fatal(err) - } - err = os.WriteFile("publicKey", ppData, 0o400) - if err != nil { - log.Fatal(err) - } - spData, err := secretParams.MarshalBinary() - if err != nil { - log.Fatal(err) - } - err = os.WriteFile("secretKey", spData, 0o400) - if err != nil { - log.Fatal(err) - } - attrs := cpabe.Attributes{} - attrs.FromMap(map[string]string{"country": "NL", "EU": "true"}) - - policy := cpabe.Policy{} - err = policy.FromString("EU: true") - if err != nil { - log.Fatal(err) - } - ciphertext, err := publicParams.Encrypt(policy, []byte("Be sure to drink your ovaltine!")) - if err != nil { - log.Fatal(err) - } - err = os.WriteFile("ciphertext", ciphertext, 0o400) - if err != nil { - log.Fatal(err) - } - key, err := secretParams.KeyGen(attrs) - if err != nil { - log.Fatal(err) - } - keyData, err := key.MarshalBinary() - err = os.WriteFile("attributeKey", keyData, 0o400) - if err != nil { - log.Fatal(err) - } -} diff --git a/abe/cpabe/tkn20/tkn20.go b/abe/cpabe/tkn20/tkn20.go index 79abcefe..67c78260 100644 --- a/abe/cpabe/tkn20/tkn20.go +++ b/abe/cpabe/tkn20/tkn20.go @@ -1,3 +1,5 @@ +//go:generate go run gen_testdata.go + // Package tkn20 implements a ciphertext-policy ABE by Tomida, Kawahara, Nishimaki. // // This is an implementation of an IND-CCA2 secure variant of the Ciphertext-Policy