diff --git a/abe/cpabe/tkn20/bench_test.go b/abe/cpabe/tkn20/bench_test.go new file mode 100644 index 000000000..11a43f1d4 --- /dev/null +++ b/abe/cpabe/tkn20/bench_test.go @@ -0,0 +1,265 @@ +package tkn20 + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "fmt" + "strconv" + "strings" + "testing" + + "golang.org/x/crypto/nacl/box" +) + +type abeTestCase struct { + desc string + attrs Attributes + policy Policy + msk SystemSecretKey + pk PublicKey +} + +var testCases []abeTestCase + +var ( + msg = []byte("drink your ovaltine now") + longMsg = []byte(strings.Repeat("a", 10000)) +) + +func generateAttrs() Attributes { + benchableAttrs := make(map[string]string, 50) + for i := 0; i < 50; i++ { + benchableAttrs["k"+strconv.Itoa(i)] = "v" + strconv.Itoa(i) + } + attrs := Attributes{} + attrs.FromMap(benchableAttrs) + return attrs +} + +func generatePolicy() string { + var policyBuilder strings.Builder + for i := 0; i < 50; i++ { + policyBuilder.WriteString("k") + policyBuilder.WriteString(strconv.Itoa(i)) + policyBuilder.WriteString(":v") + policyBuilder.WriteString(strconv.Itoa(i)) + if i != 49 { + if i%2 == 0 { + policyBuilder.WriteString(" and ") + } else { + policyBuilder.WriteString(" or ") + } + } + } + return policyBuilder.String() +} + +func init() { + smallPolicy := Policy{} + _ = smallPolicy.FromString("(k1:v1 or k1:v2) and not k2:v3") + smallAttrs := Attributes{} + smallAttrs.FromMap(map[string]string{"k1": "v2", "k2": "v4"}) + longPolicy := Policy{} + _ = longPolicy.FromString(generatePolicy()) + testCases = []abeTestCase{ + { + desc: "smallPolicy/Attrs", + attrs: smallAttrs, + policy: smallPolicy, + }, + { + desc: "longPolicy/Attrs", + attrs: generateAttrs(), + policy: longPolicy, + }, + } + var err error + for i := range testCases { + testCases[i].pk, testCases[i].msk, err = Setup(rand.Reader) + if err != nil { + panic(err) + } + } +} + +func BenchmarkTKN20KeyGen(b *testing.B) { + for _, tc := range testCases { + b.Run(fmt.Sprintf("keygen:%s", tc.desc), func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := tc.msk.KeyGen(rand.Reader, tc.attrs) + if err != nil { + b.Fatal(err) + } + } + }) + } +} + +func BenchmarkRSAKeyGen(b *testing.B) { + for i := 0; i < b.N; i++ { + _, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkX25519KeyGen(b *testing.B) { + for i := 0; i < b.N; i++ { + _, _, err := box.GenerateKey(rand.Reader) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkTKN20Encrypt(b *testing.B) { + for _, tc := range testCases { + b.Run(fmt.Sprintf("encrypt:%s", tc.desc), func(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := tc.pk.Encrypt(rand.Reader, tc.policy, msg) + if err != nil { + b.Fatal(err) + } + } + }) + } +} + +func BenchmarkRSAEncrypt(b *testing.B) { + privKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + b.Fatal(err) + } + pubKey := privKey.PublicKey + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := rsa.EncryptPKCS1v15(rand.Reader, &pubKey, msg) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkX25519Encrypt(b *testing.B) { + pubKey, _, err := box.GenerateKey(rand.Reader) + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := box.SealAnonymous(nil, msg, pubKey, rand.Reader) + if err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkTKN20Decrypt(b *testing.B) { + for _, tc := range testCases { + b.Run(fmt.Sprintf("decrypt:%s", tc.desc), func(b *testing.B) { + userKey, err := tc.msk.KeyGen(rand.Reader, tc.attrs) + if err != nil { + b.Fatal(err) + } + ciphertext, err := tc.pk.Encrypt(rand.Reader, tc.policy, msg) + if err != nil { + b.Fatal(err) + } + keyBytes, _ := userKey.MarshalBinary() + pubKeyBytes, _ := tc.pk.MarshalBinary() + // longCt is only benchmarked to measure size overhead + longCt, err := tc.pk.Encrypt(rand.Reader, tc.policy, longMsg) + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err = userKey.Decrypt(ciphertext) + if err != nil { + b.Fatal(err) + } + } + b.ReportMetric(float64(len(pubKeyBytes)), "public_key_size") + b.ReportMetric(float64(len(keyBytes)), "attribute_secret_key_size") + b.ReportMetric(float64(len(ciphertext)-len(msg)), "ciphertext_bytes_overhead_32b_msg") + b.ReportMetric(float64(len(longCt)-len(longMsg)), "ciphertext_bytes_overhead_10kb_msg") + }) + } +} + +func BenchmarkRSADecrypt(b *testing.B) { + privKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + b.Fatal(err) + } + pubKey := privKey.PublicKey + ct, err := rsa.EncryptPKCS1v15(rand.Reader, &pubKey, msg) + if err != nil { + b.Fatal(err) + } + // longCt is only benchmarked to measure size overhead + longCt, err := rsaEncrypt(longMsg, &privKey.PublicKey) + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, err := rsa.DecryptPKCS1v15(rand.Reader, privKey, ct) + if err != nil { + b.Fatal(err) + } + } + b.ReportMetric(float64(privKey.PublicKey.Size()), "public_key_size") + b.ReportMetric(float64(len(x509.MarshalPKCS1PrivateKey(privKey))), "secret_key_size") + b.ReportMetric(float64(len(ct)-len(msg)), "ciphertext_bytes_overhead") + b.ReportMetric(float64(len(longCt)-len(longMsg)), "ciphertext_bytes_overhead_10kb_msg") +} + +func BenchmarkX25519Decrypt(b *testing.B) { + pubKey, privKey, err := box.GenerateKey(rand.Reader) + if err != nil { + b.Fatal(err) + } + ct, err := box.SealAnonymous(nil, msg, pubKey, rand.Reader) + if err != nil { + b.Fatal(err) + } + // longCt is only benchmarked to measure size overhead + longCt, err := box.SealAnonymous(nil, longMsg, pubKey, rand.Reader) + if err != nil { + b.Fatal(err) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + _, ok := box.OpenAnonymous(nil, ct, pubKey, privKey) + if !ok { + b.Fatal(err) + } + } + b.ReportMetric(float64(len(pubKey)), "public_key_size") + b.ReportMetric(float64(len(privKey)), "secret_key_size") + b.ReportMetric(float64(len(ct)-len(msg)), "ciphertext_bytes_overhead_32b_msg") + b.ReportMetric(float64(len(longCt)-len(longMsg)), "ciphertext_bytes_overhead_10kb_msg") +} + +func rsaEncrypt(data []byte, pubKey *rsa.PublicKey) ([]byte, error) { + chunkSize := 245 // Max chunk size for 2048 bit key with PKCS1v15 padding + var ct []byte + for len(data) > 0 { + if len(data) < chunkSize { + chunkSize = len(data) + } + chunk := data[:chunkSize] + data = data[chunkSize:] + encryptedChunk, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey, chunk) + if err != nil { + return nil, err + } + ct = append(ct, encryptedChunk...) + } + return ct, nil +}