From fd6bae7c9f0eaf6cc84ea8608908fc581169c3a7 Mon Sep 17 00:00:00 2001 From: bstorozhuk Date: Fri, 8 May 2020 18:21:06 +0100 Subject: [PATCH 1/3] Benchmark suite for all algorithms --- jwt_bech_test.go | 189 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 jwt_bech_test.go diff --git a/jwt_bech_test.go b/jwt_bech_test.go new file mode 100644 index 0000000..0e7c0f9 --- /dev/null +++ b/jwt_bech_test.go @@ -0,0 +1,189 @@ +package jwt_test + +import ( + "crypto/ecdsa" + "crypto/ed25519" + "crypto/elliptic" + "crypto/rand" + "crypto/rsa" + mathRand "math/rand" + "testing" + "time" + "unsafe" + + "github.com/cristalhq/jwt/v2" +) + +func BenchmarkEDSA(b *testing.B) { + pubKey, privKey, keyErr := ed25519.GenerateKey(rand.Reader) + if keyErr != nil { + b.Fatal(keyErr) + } + signer, signerErr := jwt.NewSignerEdDSA(privKey) + if signerErr != nil { + b.Fatal(signerErr) + } + verifier, verifierErr := jwt.NewVerifierEdDSA(pubKey) + if verifierErr != nil { + b.Fatal(verifierErr) + } + builder := jwt.NewBuilder(signer) + b.Run("Sign-"+string(jwt.EdDSA), func(b *testing.B) { + runSignerBench(b, builder) + }) + b.Run("Verify-"+string(jwt.EdDSA), func(b *testing.B) { + runVerifyBench(b, builder, verifier) + }) +} + +func BenchmarkES(b *testing.B) { + esAlgos := map[jwt.Algorithm]elliptic.Curve{ + jwt.ES256: elliptic.P256(), + jwt.ES384: elliptic.P384(), + jwt.ES512: elliptic.P521(), + } + for algo, curve := range esAlgos { + key, keyErr := ecdsa.GenerateKey(curve, rand.Reader) + if keyErr != nil { + b.Fatal(keyErr) + } + signer, signerErr := jwt.NewSignerES(algo, key) + if signerErr != nil { + b.Fatal(signerErr) + } + verifier, verifierErr := jwt.NewVerifierES(algo, &key.PublicKey) + if verifierErr != nil { + b.Fatal(verifierErr) + } + builder := jwt.NewBuilder(signer) + b.Run("Sign-"+string(algo), func(b *testing.B) { + runSignerBench(b, builder) + }) + b.Run("Verify-"+string(algo), func(b *testing.B) { + runVerifyBench(b, builder, verifier) + }) + } +} + +func BenchmarkPS(b *testing.B) { + psAlgos := []jwt.Algorithm{jwt.PS256, jwt.PS384, jwt.PS512} + for _, algo := range psAlgos { + key, keyErr := rsa.GenerateKey(rand.Reader, 2048) + if keyErr != nil { + b.Fatal(keyErr) + } + signer, signerErr := jwt.NewSignerPS(algo, key) + if signerErr != nil { + b.Fatal(signerErr) + } + verifier, verifierErr := jwt.NewVerifierPS(algo, &key.PublicKey) + if verifierErr != nil { + b.Fatal(verifierErr) + } + builder := jwt.NewBuilder(signer) + b.Run("Sign-"+string(algo), func(b *testing.B) { + runSignerBench(b, builder) + }) + b.Run("Verify-"+string(algo), func(b *testing.B) { + runVerifyBench(b, builder, verifier) + }) + } +} + +func BenchmarkRS(b *testing.B) { + rsAlgos := []jwt.Algorithm{jwt.RS256, jwt.RS384, jwt.RS512} + for _, algo := range rsAlgos { + key, keyErr := rsa.GenerateKey(rand.Reader, 2048) + if keyErr != nil { + b.Fatal(keyErr) + } + signer, signerErr := jwt.NewSignerRS(algo, key) + if signerErr != nil { + b.Fatal(signerErr) + } + verifier, verifierErr := jwt.NewVerifierRS(algo, &key.PublicKey) + if verifierErr != nil { + b.Fatal(verifierErr) + } + builder := jwt.NewBuilder(signer) + b.Run("Sign-"+string(algo), func(b *testing.B) { + runSignerBench(b, builder) + }) + b.Run("Verify-"+string(algo), func(b *testing.B) { + runVerifyBench(b, builder, verifier) + }) + } +} + +func BenchmarkHS(b *testing.B) { + key := []byte("12345") + hsAlgos := []jwt.Algorithm{jwt.HS256, jwt.HS384, jwt.HS512} + for _, algo := range hsAlgos { + signer, signerErr := jwt.NewSignerHS(algo, key) + if signerErr != nil { + b.Fatal(signerErr) + } + verifier, verifierErr := jwt.NewVerifierHS(algo, key) + if verifierErr != nil { + b.Fatal(verifierErr) + } + builder := jwt.NewBuilder(signer) + b.Run("Sign-"+string(algo), func(b *testing.B) { + runSignerBench(b, builder) + }) + b.Run("Verify-"+string(algo), func(b *testing.B) { + runVerifyBench(b, builder, verifier) + }) + } +} + +func runSignerBench(b *testing.B, builder *jwt.Builder) { + b.ReportAllocs() + + sink := uintptr(0) + for i := 0; i < b.N; i++ { + token, tokenErr := builder.Build(jwt.StandardClaims{ + ID: "id", + Issuer: "sdf", + IssuedAt: jwt.NewNumericDate(time.Now()), + }) + if tokenErr != nil { + b.Fatal(tokenErr) + } + sink += uintptr(unsafe.Pointer(token)) + } + + if mathRand.Intn(10000) > 9999 { + b.Log(sink) + } +} + +func runVerifyBench(b *testing.B, builder *jwt.Builder, verifier jwt.Verifier) { + builder.Build(jwt.StandardClaims{ + ID: "id", + Issuer: "sdf", + IssuedAt: jwt.NewNumericDate(time.Now()), + }) + token, tokenErr := builder.Build(jwt.StandardClaims{ + ID: "id", + Issuer: "sdf", + IssuedAt: jwt.NewNumericDate(time.Now()), + }) + if tokenErr != nil { + b.Fatal(tokenErr) + } + + b.ReportAllocs() + sink := uintptr(0) + for i := 0; i < b.N; i++ { + verificationErr := verifier.Verify(token.Payload(), token.Signature()) + sink += uintptr(unsafe.Pointer(&verificationErr)) + if verificationErr != nil { + b.Fatal(verificationErr) + } + } + + if mathRand.Intn(10000) > 9999 { + b.Log(sink) + } +} From 12e6ea5712006bcf48728a810105f26fd8233b39 Mon Sep 17 00:00:00 2001 From: bstorozhuk Date: Fri, 8 May 2020 19:09:49 +0100 Subject: [PATCH 2/3] Remove usage of unsafe --- jwt_bech_test.go => jwt_bench_test.go | 35 +++++++++++++++------------ 1 file changed, 20 insertions(+), 15 deletions(-) rename jwt_bech_test.go => jwt_bench_test.go (87%) diff --git a/jwt_bech_test.go b/jwt_bench_test.go similarity index 87% rename from jwt_bech_test.go rename to jwt_bench_test.go index 0e7c0f9..adc48a5 100644 --- a/jwt_bech_test.go +++ b/jwt_bench_test.go @@ -9,7 +9,6 @@ import ( mathRand "math/rand" "testing" "time" - "unsafe" "github.com/cristalhq/jwt/v2" ) @@ -140,7 +139,7 @@ func BenchmarkHS(b *testing.B) { func runSignerBench(b *testing.B, builder *jwt.Builder) { b.ReportAllocs() - sink := uintptr(0) + sink := int(0) for i := 0; i < b.N; i++ { token, tokenErr := builder.Build(jwt.StandardClaims{ ID: "id", @@ -150,7 +149,7 @@ func runSignerBench(b *testing.B, builder *jwt.Builder) { if tokenErr != nil { b.Fatal(tokenErr) } - sink += uintptr(unsafe.Pointer(token)) + sink += int(token.Payload()[0]) } if mathRand.Intn(10000) > 9999 { @@ -164,22 +163,28 @@ func runVerifyBench(b *testing.B, builder *jwt.Builder, verifier jwt.Verifier) { Issuer: "sdf", IssuedAt: jwt.NewNumericDate(time.Now()), }) - token, tokenErr := builder.Build(jwt.StandardClaims{ - ID: "id", - Issuer: "sdf", - IssuedAt: jwt.NewNumericDate(time.Now()), - }) - if tokenErr != nil { - b.Fatal(tokenErr) + tokensCount := 32 + tokens := make([]*jwt.Token, 0, tokensCount) + for i := 0; i < tokensCount; i++ { + token, tokenErr := builder.Build(jwt.StandardClaims{ + ID: "id", + Issuer: "sdf", + IssuedAt: jwt.NewNumericDate(time.Now()), + }) + if tokenErr != nil { + b.Fatal(tokenErr) + } + tokens = append(tokens, token) } b.ReportAllocs() sink := uintptr(0) - for i := 0; i < b.N; i++ { - verificationErr := verifier.Verify(token.Payload(), token.Signature()) - sink += uintptr(unsafe.Pointer(&verificationErr)) - if verificationErr != nil { - b.Fatal(verificationErr) + for i := 0; i < b.N/tokensCount; i++ { + for _, token := range tokens { + verificationErr := verifier.Verify(token.Payload(), token.Signature()) + if verificationErr != nil { + b.Fatal(verificationErr) + } } } From ba5f57cec2afaaced541e56b7df0783ff756305f Mon Sep 17 00:00:00 2001 From: bstorozhuk Date: Fri, 8 May 2020 21:41:04 +0100 Subject: [PATCH 3/3] Remove no-op build --- jwt_bench_test.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/jwt_bench_test.go b/jwt_bench_test.go index adc48a5..1d7d25c 100644 --- a/jwt_bench_test.go +++ b/jwt_bench_test.go @@ -158,11 +158,6 @@ func runSignerBench(b *testing.B, builder *jwt.Builder) { } func runVerifyBench(b *testing.B, builder *jwt.Builder, verifier jwt.Verifier) { - builder.Build(jwt.StandardClaims{ - ID: "id", - Issuer: "sdf", - IssuedAt: jwt.NewNumericDate(time.Now()), - }) tokensCount := 32 tokens := make([]*jwt.Token, 0, tokensCount) for i := 0; i < tokensCount; i++ {