jwt/ecdsa_test.go

165 lines
4.6 KiB
Go
Raw Permalink Normal View History

2015-07-16 21:26:45 +03:00
package jwt_test
import (
"crypto/ecdsa"
"os"
2015-07-16 21:26:45 +03:00
"strings"
"testing"
"github.com/golang-jwt/jwt/v4"
2015-07-16 21:26:45 +03:00
)
var ecdsaTestData = []struct {
name string
keys map[string]string
tokenString string
alg string
claims map[string]interface{}
valid bool
}{
{
"Basic ES256",
map[string]string{"private": "test/ec256-private.pem", "public": "test/ec256-public.pem"},
2015-09-17 05:53:08 +03:00
"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJmb28iOiJiYXIifQ.feG39E-bn8HXAKhzDZq7yEAPWYDhZlwTn3sePJnU9VrGMmwdXAIEyoOnrjreYlVM_Z4N13eK9-TmMTWyfKJtHQ",
2015-07-16 21:26:45 +03:00
"ES256",
map[string]interface{}{"foo": "bar"},
true,
},
{
"Basic ES384",
map[string]string{"private": "test/ec384-private.pem", "public": "test/ec384-public.pem"},
2015-09-17 05:53:08 +03:00
"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzM4NCJ9.eyJmb28iOiJiYXIifQ.ngAfKMbJUh0WWubSIYe5GMsA-aHNKwFbJk_wq3lq23aPp8H2anb1rRILIzVR0gUf4a8WzDtrzmiikuPWyCS6CN4-PwdgTk-5nehC7JXqlaBZU05p3toM3nWCwm_LXcld",
2015-07-16 21:26:45 +03:00
"ES384",
map[string]interface{}{"foo": "bar"},
true,
},
{
"Basic ES512",
map[string]string{"private": "test/ec512-private.pem", "public": "test/ec512-public.pem"},
2015-09-17 05:53:08 +03:00
"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJmb28iOiJiYXIifQ.AAU0TvGQOcdg2OvrwY73NHKgfk26UDekh9Prz-L_iWuTBIBqOFCWwwLsRiHB1JOddfKAls5do1W0jR_F30JpVd-6AJeTjGKA4C1A1H6gIKwRY0o_tFDIydZCl_lMBMeG5VNFAjO86-WCSKwc3hqaGkq1MugPRq_qrF9AVbuEB4JPLyL5",
2015-07-16 21:26:45 +03:00
"ES512",
map[string]interface{}{"foo": "bar"},
true,
},
{
"basic ES256 invalid: foo => bar",
map[string]string{"private": "test/ec256-private.pem", "public": "test/ec256-public.pem"},
"eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.MEQCIHoSJnmGlPaVQDqacx_2XlXEhhqtWceVopjomc2PJLtdAiAUTeGPoNYxZw0z8mgOnnIcjoxRuNDVZvybRZF3wR1l8W",
"ES256",
map[string]interface{}{"foo": "bar"},
false,
},
}
func TestECDSAVerify(t *testing.T) {
for _, data := range ecdsaTestData {
var err error
key, _ := os.ReadFile(data.keys["public"])
2015-07-16 21:26:45 +03:00
var ecdsaKey *ecdsa.PublicKey
if ecdsaKey, err = jwt.ParseECPublicKeyFromPEM(key); err != nil {
t.Errorf("Unable to parse ECDSA public key: %v", err)
}
parts := strings.Split(data.tokenString, ".")
method := jwt.GetSigningMethod(data.alg)
err = method.Verify(strings.Join(parts[0:2], "."), parts[2], ecdsaKey)
if data.valid && err != nil {
t.Errorf("[%v] Error while verifying key: %v", data.name, err)
}
if !data.valid && err == nil {
t.Errorf("[%v] Invalid key passed validation", data.name)
}
}
}
func TestECDSASign(t *testing.T) {
for _, data := range ecdsaTestData {
var err error
key, _ := os.ReadFile(data.keys["private"])
2015-07-16 21:26:45 +03:00
var ecdsaKey *ecdsa.PrivateKey
if ecdsaKey, err = jwt.ParseECPrivateKeyFromPEM(key); err != nil {
t.Errorf("Unable to parse ECDSA private key: %v", err)
}
if data.valid {
parts := strings.Split(data.tokenString, ".")
Allocation optimization (#33) * Test to ensure ECDSA signature is valid Add assertions to ensure ECDSA signing methods return valid signatures. This is probably covered elsewhere as well, but putting it in ecdsa_test.go makes it more obvious and easier to find. * Benchmark ECDSA signing methods Add benchmark coverage of ECDSA signing methods. Benchmarks are run using the existing helper for comparability with existing benchmarks. Sign method is also tested directly, to avoid the overhead of *Token. Report allocations for all benchmarks. Allocation count for ES384 and ES512 fluctuate across test runs, other signing methods consistently report the same number of allocations. Sample output: ``` $ go test -bench=Bench -run=NONE . 2021/02/26 18:18:30 Listening... goos: darwin goarch: amd64 pkg: github.com/dgrijalva/jwt-go BenchmarkECDSASigning/Basic_ES256-8 190572 6702 ns/op 4249 B/op 65 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 47383 24650 ns/op 3329 B/op 43 allocs/op BenchmarkECDSASigning/Basic_ES384-8 1113 1252975 ns/op 1750744 B/op 14474 allocs/op BenchmarkECDSASigning/Basic_ES384/sign-only-8 286 3937773 ns/op 1746175 B/op 14423 allocs/op BenchmarkECDSASigning/Basic_ES512-8 662 1949937 ns/op 3028386 B/op 19608 allocs/op BenchmarkECDSASigning/Basic_ES512/sign-only-8 170 6856189 ns/op 3025471 B/op 19571 allocs/op BenchmarkECDSASigning/basic_ES256_invalid:_foo_=>_bar-8 190638 6665 ns/op 4249 B/op 65 allocs/op BenchmarkHS256Signing-8 1000000 1024 ns/op 1584 B/op 32 allocs/op BenchmarkHS384Signing-8 917286 1447 ns/op 1969 B/op 32 allocs/op BenchmarkHS512Signing-8 827744 1470 ns/op 2065 B/op 32 allocs/op BenchmarkRS256Signing-8 3037 390077 ns/op 32576 B/op 136 allocs/op BenchmarkRS384Signing-8 2976 379155 ns/op 32684 B/op 136 allocs/op BenchmarkRS512Signing-8 3205 388628 ns/op 32704 B/op 136 allocs/op ``` * Reduce allocations during ECDSA signing Reduce the number of byte arrays allocated by using big.Int.FillBytes when calculating ECDSA signature. After this change, Benchmarks of ES256 signing method consistently report 4 fewer allocations. Before: ``` BenchmarkECDSASigning/Basic_ES256-8 190572 6702 ns/op 4249 B/op 65 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 47383 24650 ns/op 3329 B/op 43 allocs/op ``` After: ``` BenchmarkECDSASigning/Basic_ES256-8 187682 6725 ns/op 4121 B/op 61 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 48656 24446 ns/op 3201 B/op 39 allocs/op ``` * Use base64.RawURLEncoding to avoid padding JWT uses a non-padded base64 encoding. Current code uses base64.URLEncoding to generate a padded string and then removes the padding. Likewise, current code adds padding before decoding. Instead, use base64.RawURLEncoding which does not add or require the padding in the first place. In addition to making the code cleaner, this reduces memory allocations as reported by benchmarks. Before: ``` BenchmarkECDSASigning/Basic_ES256-8 191396 6917 ns/op 4121 B/op 61 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 49347 25039 ns/op 3201 B/op 39 allocs/op BenchmarkECDSASigning/basic_ES256_invalid:_foo_=>_bar-8 190668 6586 ns/op 4121 B/op 61 allocs/op BenchmarkHS256Signing-8 1260060 1131 ns/op 1585 B/op 32 allocs/op BenchmarkHS384Signing-8 861378 1387 ns/op 1969 B/op 32 allocs/op BenchmarkHS512Signing-8 896745 1463 ns/op 2065 B/op 32 allocs/op BenchmarkRS256Signing-8 3086 355769 ns/op 32576 B/op 136 allocs/op BenchmarkRS384Signing-8 3414 353570 ns/op 32694 B/op 136 allocs/op BenchmarkRS512Signing-8 3235 349394 ns/op 32706 B/op 136 allocs/op ``` After: ``` BenchmarkECDSASigning/Basic_ES256-8 176617 6827 ns/op 4021 B/op 58 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 48038 24213 ns/op 3169 B/op 38 allocs/op BenchmarkECDSASigning/basic_ES256_invalid:_foo_=>_bar-8 194352 6928 ns/op 4021 B/op 58 allocs/op BenchmarkHS256Signing-8 1000000 1127 ns/op 1488 B/op 29 allocs/op BenchmarkHS384Signing-8 972552 1369 ns/op 1873 B/op 29 allocs/op BenchmarkHS512Signing-8 780751 1368 ns/op 1969 B/op 29 allocs/op BenchmarkRS256Signing-8 3014 387326 ns/op 32475 B/op 133 allocs/op BenchmarkRS384Signing-8 3044 361411 ns/op 32591 B/op 133 allocs/op BenchmarkRS512Signing-8 3273 355504 ns/op 32607 B/op 133 allocs/op ``` Benchmarks of signing methods ES384 and ES512 are omitted because their allocations are not consistent.
2021-07-13 09:31:42 +03:00
toSign := strings.Join(parts[0:2], ".")
2015-07-16 21:26:45 +03:00
method := jwt.GetSigningMethod(data.alg)
Allocation optimization (#33) * Test to ensure ECDSA signature is valid Add assertions to ensure ECDSA signing methods return valid signatures. This is probably covered elsewhere as well, but putting it in ecdsa_test.go makes it more obvious and easier to find. * Benchmark ECDSA signing methods Add benchmark coverage of ECDSA signing methods. Benchmarks are run using the existing helper for comparability with existing benchmarks. Sign method is also tested directly, to avoid the overhead of *Token. Report allocations for all benchmarks. Allocation count for ES384 and ES512 fluctuate across test runs, other signing methods consistently report the same number of allocations. Sample output: ``` $ go test -bench=Bench -run=NONE . 2021/02/26 18:18:30 Listening... goos: darwin goarch: amd64 pkg: github.com/dgrijalva/jwt-go BenchmarkECDSASigning/Basic_ES256-8 190572 6702 ns/op 4249 B/op 65 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 47383 24650 ns/op 3329 B/op 43 allocs/op BenchmarkECDSASigning/Basic_ES384-8 1113 1252975 ns/op 1750744 B/op 14474 allocs/op BenchmarkECDSASigning/Basic_ES384/sign-only-8 286 3937773 ns/op 1746175 B/op 14423 allocs/op BenchmarkECDSASigning/Basic_ES512-8 662 1949937 ns/op 3028386 B/op 19608 allocs/op BenchmarkECDSASigning/Basic_ES512/sign-only-8 170 6856189 ns/op 3025471 B/op 19571 allocs/op BenchmarkECDSASigning/basic_ES256_invalid:_foo_=>_bar-8 190638 6665 ns/op 4249 B/op 65 allocs/op BenchmarkHS256Signing-8 1000000 1024 ns/op 1584 B/op 32 allocs/op BenchmarkHS384Signing-8 917286 1447 ns/op 1969 B/op 32 allocs/op BenchmarkHS512Signing-8 827744 1470 ns/op 2065 B/op 32 allocs/op BenchmarkRS256Signing-8 3037 390077 ns/op 32576 B/op 136 allocs/op BenchmarkRS384Signing-8 2976 379155 ns/op 32684 B/op 136 allocs/op BenchmarkRS512Signing-8 3205 388628 ns/op 32704 B/op 136 allocs/op ``` * Reduce allocations during ECDSA signing Reduce the number of byte arrays allocated by using big.Int.FillBytes when calculating ECDSA signature. After this change, Benchmarks of ES256 signing method consistently report 4 fewer allocations. Before: ``` BenchmarkECDSASigning/Basic_ES256-8 190572 6702 ns/op 4249 B/op 65 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 47383 24650 ns/op 3329 B/op 43 allocs/op ``` After: ``` BenchmarkECDSASigning/Basic_ES256-8 187682 6725 ns/op 4121 B/op 61 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 48656 24446 ns/op 3201 B/op 39 allocs/op ``` * Use base64.RawURLEncoding to avoid padding JWT uses a non-padded base64 encoding. Current code uses base64.URLEncoding to generate a padded string and then removes the padding. Likewise, current code adds padding before decoding. Instead, use base64.RawURLEncoding which does not add or require the padding in the first place. In addition to making the code cleaner, this reduces memory allocations as reported by benchmarks. Before: ``` BenchmarkECDSASigning/Basic_ES256-8 191396 6917 ns/op 4121 B/op 61 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 49347 25039 ns/op 3201 B/op 39 allocs/op BenchmarkECDSASigning/basic_ES256_invalid:_foo_=>_bar-8 190668 6586 ns/op 4121 B/op 61 allocs/op BenchmarkHS256Signing-8 1260060 1131 ns/op 1585 B/op 32 allocs/op BenchmarkHS384Signing-8 861378 1387 ns/op 1969 B/op 32 allocs/op BenchmarkHS512Signing-8 896745 1463 ns/op 2065 B/op 32 allocs/op BenchmarkRS256Signing-8 3086 355769 ns/op 32576 B/op 136 allocs/op BenchmarkRS384Signing-8 3414 353570 ns/op 32694 B/op 136 allocs/op BenchmarkRS512Signing-8 3235 349394 ns/op 32706 B/op 136 allocs/op ``` After: ``` BenchmarkECDSASigning/Basic_ES256-8 176617 6827 ns/op 4021 B/op 58 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 48038 24213 ns/op 3169 B/op 38 allocs/op BenchmarkECDSASigning/basic_ES256_invalid:_foo_=>_bar-8 194352 6928 ns/op 4021 B/op 58 allocs/op BenchmarkHS256Signing-8 1000000 1127 ns/op 1488 B/op 29 allocs/op BenchmarkHS384Signing-8 972552 1369 ns/op 1873 B/op 29 allocs/op BenchmarkHS512Signing-8 780751 1368 ns/op 1969 B/op 29 allocs/op BenchmarkRS256Signing-8 3014 387326 ns/op 32475 B/op 133 allocs/op BenchmarkRS384Signing-8 3044 361411 ns/op 32591 B/op 133 allocs/op BenchmarkRS512Signing-8 3273 355504 ns/op 32607 B/op 133 allocs/op ``` Benchmarks of signing methods ES384 and ES512 are omitted because their allocations are not consistent.
2021-07-13 09:31:42 +03:00
sig, err := method.Sign(toSign, ecdsaKey)
2015-07-16 21:26:45 +03:00
if err != nil {
t.Errorf("[%v] Error signing token: %v", data.name, err)
}
if sig == parts[2] {
t.Errorf("[%v] Identical signatures\nbefore:\n%v\nafter:\n%v", data.name, parts[2], sig)
}
Allocation optimization (#33) * Test to ensure ECDSA signature is valid Add assertions to ensure ECDSA signing methods return valid signatures. This is probably covered elsewhere as well, but putting it in ecdsa_test.go makes it more obvious and easier to find. * Benchmark ECDSA signing methods Add benchmark coverage of ECDSA signing methods. Benchmarks are run using the existing helper for comparability with existing benchmarks. Sign method is also tested directly, to avoid the overhead of *Token. Report allocations for all benchmarks. Allocation count for ES384 and ES512 fluctuate across test runs, other signing methods consistently report the same number of allocations. Sample output: ``` $ go test -bench=Bench -run=NONE . 2021/02/26 18:18:30 Listening... goos: darwin goarch: amd64 pkg: github.com/dgrijalva/jwt-go BenchmarkECDSASigning/Basic_ES256-8 190572 6702 ns/op 4249 B/op 65 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 47383 24650 ns/op 3329 B/op 43 allocs/op BenchmarkECDSASigning/Basic_ES384-8 1113 1252975 ns/op 1750744 B/op 14474 allocs/op BenchmarkECDSASigning/Basic_ES384/sign-only-8 286 3937773 ns/op 1746175 B/op 14423 allocs/op BenchmarkECDSASigning/Basic_ES512-8 662 1949937 ns/op 3028386 B/op 19608 allocs/op BenchmarkECDSASigning/Basic_ES512/sign-only-8 170 6856189 ns/op 3025471 B/op 19571 allocs/op BenchmarkECDSASigning/basic_ES256_invalid:_foo_=>_bar-8 190638 6665 ns/op 4249 B/op 65 allocs/op BenchmarkHS256Signing-8 1000000 1024 ns/op 1584 B/op 32 allocs/op BenchmarkHS384Signing-8 917286 1447 ns/op 1969 B/op 32 allocs/op BenchmarkHS512Signing-8 827744 1470 ns/op 2065 B/op 32 allocs/op BenchmarkRS256Signing-8 3037 390077 ns/op 32576 B/op 136 allocs/op BenchmarkRS384Signing-8 2976 379155 ns/op 32684 B/op 136 allocs/op BenchmarkRS512Signing-8 3205 388628 ns/op 32704 B/op 136 allocs/op ``` * Reduce allocations during ECDSA signing Reduce the number of byte arrays allocated by using big.Int.FillBytes when calculating ECDSA signature. After this change, Benchmarks of ES256 signing method consistently report 4 fewer allocations. Before: ``` BenchmarkECDSASigning/Basic_ES256-8 190572 6702 ns/op 4249 B/op 65 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 47383 24650 ns/op 3329 B/op 43 allocs/op ``` After: ``` BenchmarkECDSASigning/Basic_ES256-8 187682 6725 ns/op 4121 B/op 61 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 48656 24446 ns/op 3201 B/op 39 allocs/op ``` * Use base64.RawURLEncoding to avoid padding JWT uses a non-padded base64 encoding. Current code uses base64.URLEncoding to generate a padded string and then removes the padding. Likewise, current code adds padding before decoding. Instead, use base64.RawURLEncoding which does not add or require the padding in the first place. In addition to making the code cleaner, this reduces memory allocations as reported by benchmarks. Before: ``` BenchmarkECDSASigning/Basic_ES256-8 191396 6917 ns/op 4121 B/op 61 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 49347 25039 ns/op 3201 B/op 39 allocs/op BenchmarkECDSASigning/basic_ES256_invalid:_foo_=>_bar-8 190668 6586 ns/op 4121 B/op 61 allocs/op BenchmarkHS256Signing-8 1260060 1131 ns/op 1585 B/op 32 allocs/op BenchmarkHS384Signing-8 861378 1387 ns/op 1969 B/op 32 allocs/op BenchmarkHS512Signing-8 896745 1463 ns/op 2065 B/op 32 allocs/op BenchmarkRS256Signing-8 3086 355769 ns/op 32576 B/op 136 allocs/op BenchmarkRS384Signing-8 3414 353570 ns/op 32694 B/op 136 allocs/op BenchmarkRS512Signing-8 3235 349394 ns/op 32706 B/op 136 allocs/op ``` After: ``` BenchmarkECDSASigning/Basic_ES256-8 176617 6827 ns/op 4021 B/op 58 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 48038 24213 ns/op 3169 B/op 38 allocs/op BenchmarkECDSASigning/basic_ES256_invalid:_foo_=>_bar-8 194352 6928 ns/op 4021 B/op 58 allocs/op BenchmarkHS256Signing-8 1000000 1127 ns/op 1488 B/op 29 allocs/op BenchmarkHS384Signing-8 972552 1369 ns/op 1873 B/op 29 allocs/op BenchmarkHS512Signing-8 780751 1368 ns/op 1969 B/op 29 allocs/op BenchmarkRS256Signing-8 3014 387326 ns/op 32475 B/op 133 allocs/op BenchmarkRS384Signing-8 3044 361411 ns/op 32591 B/op 133 allocs/op BenchmarkRS512Signing-8 3273 355504 ns/op 32607 B/op 133 allocs/op ``` Benchmarks of signing methods ES384 and ES512 are omitted because their allocations are not consistent.
2021-07-13 09:31:42 +03:00
err = method.Verify(toSign, sig, ecdsaKey.Public())
if err != nil {
t.Errorf("[%v] Sign produced an invalid signature: %v", data.name, err)
}
}
}
}
2021-08-03 23:57:36 +03:00
func BenchmarkECDSAParsing(b *testing.B) {
for _, data := range ecdsaTestData {
key, _ := os.ReadFile(data.keys["private"])
2021-08-03 23:57:36 +03:00
b.Run(data.name, func(b *testing.B) {
b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
if _, err := jwt.ParseECPrivateKeyFromPEM(key); err != nil {
b.Fatalf("Unable to parse ECDSA private key: %v", err)
}
}
})
})
}
}
Allocation optimization (#33) * Test to ensure ECDSA signature is valid Add assertions to ensure ECDSA signing methods return valid signatures. This is probably covered elsewhere as well, but putting it in ecdsa_test.go makes it more obvious and easier to find. * Benchmark ECDSA signing methods Add benchmark coverage of ECDSA signing methods. Benchmarks are run using the existing helper for comparability with existing benchmarks. Sign method is also tested directly, to avoid the overhead of *Token. Report allocations for all benchmarks. Allocation count for ES384 and ES512 fluctuate across test runs, other signing methods consistently report the same number of allocations. Sample output: ``` $ go test -bench=Bench -run=NONE . 2021/02/26 18:18:30 Listening... goos: darwin goarch: amd64 pkg: github.com/dgrijalva/jwt-go BenchmarkECDSASigning/Basic_ES256-8 190572 6702 ns/op 4249 B/op 65 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 47383 24650 ns/op 3329 B/op 43 allocs/op BenchmarkECDSASigning/Basic_ES384-8 1113 1252975 ns/op 1750744 B/op 14474 allocs/op BenchmarkECDSASigning/Basic_ES384/sign-only-8 286 3937773 ns/op 1746175 B/op 14423 allocs/op BenchmarkECDSASigning/Basic_ES512-8 662 1949937 ns/op 3028386 B/op 19608 allocs/op BenchmarkECDSASigning/Basic_ES512/sign-only-8 170 6856189 ns/op 3025471 B/op 19571 allocs/op BenchmarkECDSASigning/basic_ES256_invalid:_foo_=>_bar-8 190638 6665 ns/op 4249 B/op 65 allocs/op BenchmarkHS256Signing-8 1000000 1024 ns/op 1584 B/op 32 allocs/op BenchmarkHS384Signing-8 917286 1447 ns/op 1969 B/op 32 allocs/op BenchmarkHS512Signing-8 827744 1470 ns/op 2065 B/op 32 allocs/op BenchmarkRS256Signing-8 3037 390077 ns/op 32576 B/op 136 allocs/op BenchmarkRS384Signing-8 2976 379155 ns/op 32684 B/op 136 allocs/op BenchmarkRS512Signing-8 3205 388628 ns/op 32704 B/op 136 allocs/op ``` * Reduce allocations during ECDSA signing Reduce the number of byte arrays allocated by using big.Int.FillBytes when calculating ECDSA signature. After this change, Benchmarks of ES256 signing method consistently report 4 fewer allocations. Before: ``` BenchmarkECDSASigning/Basic_ES256-8 190572 6702 ns/op 4249 B/op 65 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 47383 24650 ns/op 3329 B/op 43 allocs/op ``` After: ``` BenchmarkECDSASigning/Basic_ES256-8 187682 6725 ns/op 4121 B/op 61 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 48656 24446 ns/op 3201 B/op 39 allocs/op ``` * Use base64.RawURLEncoding to avoid padding JWT uses a non-padded base64 encoding. Current code uses base64.URLEncoding to generate a padded string and then removes the padding. Likewise, current code adds padding before decoding. Instead, use base64.RawURLEncoding which does not add or require the padding in the first place. In addition to making the code cleaner, this reduces memory allocations as reported by benchmarks. Before: ``` BenchmarkECDSASigning/Basic_ES256-8 191396 6917 ns/op 4121 B/op 61 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 49347 25039 ns/op 3201 B/op 39 allocs/op BenchmarkECDSASigning/basic_ES256_invalid:_foo_=>_bar-8 190668 6586 ns/op 4121 B/op 61 allocs/op BenchmarkHS256Signing-8 1260060 1131 ns/op 1585 B/op 32 allocs/op BenchmarkHS384Signing-8 861378 1387 ns/op 1969 B/op 32 allocs/op BenchmarkHS512Signing-8 896745 1463 ns/op 2065 B/op 32 allocs/op BenchmarkRS256Signing-8 3086 355769 ns/op 32576 B/op 136 allocs/op BenchmarkRS384Signing-8 3414 353570 ns/op 32694 B/op 136 allocs/op BenchmarkRS512Signing-8 3235 349394 ns/op 32706 B/op 136 allocs/op ``` After: ``` BenchmarkECDSASigning/Basic_ES256-8 176617 6827 ns/op 4021 B/op 58 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 48038 24213 ns/op 3169 B/op 38 allocs/op BenchmarkECDSASigning/basic_ES256_invalid:_foo_=>_bar-8 194352 6928 ns/op 4021 B/op 58 allocs/op BenchmarkHS256Signing-8 1000000 1127 ns/op 1488 B/op 29 allocs/op BenchmarkHS384Signing-8 972552 1369 ns/op 1873 B/op 29 allocs/op BenchmarkHS512Signing-8 780751 1368 ns/op 1969 B/op 29 allocs/op BenchmarkRS256Signing-8 3014 387326 ns/op 32475 B/op 133 allocs/op BenchmarkRS384Signing-8 3044 361411 ns/op 32591 B/op 133 allocs/op BenchmarkRS512Signing-8 3273 355504 ns/op 32607 B/op 133 allocs/op ``` Benchmarks of signing methods ES384 and ES512 are omitted because their allocations are not consistent.
2021-07-13 09:31:42 +03:00
func BenchmarkECDSASigning(b *testing.B) {
for _, data := range ecdsaTestData {
key, _ := os.ReadFile(data.keys["private"])
Allocation optimization (#33) * Test to ensure ECDSA signature is valid Add assertions to ensure ECDSA signing methods return valid signatures. This is probably covered elsewhere as well, but putting it in ecdsa_test.go makes it more obvious and easier to find. * Benchmark ECDSA signing methods Add benchmark coverage of ECDSA signing methods. Benchmarks are run using the existing helper for comparability with existing benchmarks. Sign method is also tested directly, to avoid the overhead of *Token. Report allocations for all benchmarks. Allocation count for ES384 and ES512 fluctuate across test runs, other signing methods consistently report the same number of allocations. Sample output: ``` $ go test -bench=Bench -run=NONE . 2021/02/26 18:18:30 Listening... goos: darwin goarch: amd64 pkg: github.com/dgrijalva/jwt-go BenchmarkECDSASigning/Basic_ES256-8 190572 6702 ns/op 4249 B/op 65 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 47383 24650 ns/op 3329 B/op 43 allocs/op BenchmarkECDSASigning/Basic_ES384-8 1113 1252975 ns/op 1750744 B/op 14474 allocs/op BenchmarkECDSASigning/Basic_ES384/sign-only-8 286 3937773 ns/op 1746175 B/op 14423 allocs/op BenchmarkECDSASigning/Basic_ES512-8 662 1949937 ns/op 3028386 B/op 19608 allocs/op BenchmarkECDSASigning/Basic_ES512/sign-only-8 170 6856189 ns/op 3025471 B/op 19571 allocs/op BenchmarkECDSASigning/basic_ES256_invalid:_foo_=>_bar-8 190638 6665 ns/op 4249 B/op 65 allocs/op BenchmarkHS256Signing-8 1000000 1024 ns/op 1584 B/op 32 allocs/op BenchmarkHS384Signing-8 917286 1447 ns/op 1969 B/op 32 allocs/op BenchmarkHS512Signing-8 827744 1470 ns/op 2065 B/op 32 allocs/op BenchmarkRS256Signing-8 3037 390077 ns/op 32576 B/op 136 allocs/op BenchmarkRS384Signing-8 2976 379155 ns/op 32684 B/op 136 allocs/op BenchmarkRS512Signing-8 3205 388628 ns/op 32704 B/op 136 allocs/op ``` * Reduce allocations during ECDSA signing Reduce the number of byte arrays allocated by using big.Int.FillBytes when calculating ECDSA signature. After this change, Benchmarks of ES256 signing method consistently report 4 fewer allocations. Before: ``` BenchmarkECDSASigning/Basic_ES256-8 190572 6702 ns/op 4249 B/op 65 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 47383 24650 ns/op 3329 B/op 43 allocs/op ``` After: ``` BenchmarkECDSASigning/Basic_ES256-8 187682 6725 ns/op 4121 B/op 61 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 48656 24446 ns/op 3201 B/op 39 allocs/op ``` * Use base64.RawURLEncoding to avoid padding JWT uses a non-padded base64 encoding. Current code uses base64.URLEncoding to generate a padded string and then removes the padding. Likewise, current code adds padding before decoding. Instead, use base64.RawURLEncoding which does not add or require the padding in the first place. In addition to making the code cleaner, this reduces memory allocations as reported by benchmarks. Before: ``` BenchmarkECDSASigning/Basic_ES256-8 191396 6917 ns/op 4121 B/op 61 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 49347 25039 ns/op 3201 B/op 39 allocs/op BenchmarkECDSASigning/basic_ES256_invalid:_foo_=>_bar-8 190668 6586 ns/op 4121 B/op 61 allocs/op BenchmarkHS256Signing-8 1260060 1131 ns/op 1585 B/op 32 allocs/op BenchmarkHS384Signing-8 861378 1387 ns/op 1969 B/op 32 allocs/op BenchmarkHS512Signing-8 896745 1463 ns/op 2065 B/op 32 allocs/op BenchmarkRS256Signing-8 3086 355769 ns/op 32576 B/op 136 allocs/op BenchmarkRS384Signing-8 3414 353570 ns/op 32694 B/op 136 allocs/op BenchmarkRS512Signing-8 3235 349394 ns/op 32706 B/op 136 allocs/op ``` After: ``` BenchmarkECDSASigning/Basic_ES256-8 176617 6827 ns/op 4021 B/op 58 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 48038 24213 ns/op 3169 B/op 38 allocs/op BenchmarkECDSASigning/basic_ES256_invalid:_foo_=>_bar-8 194352 6928 ns/op 4021 B/op 58 allocs/op BenchmarkHS256Signing-8 1000000 1127 ns/op 1488 B/op 29 allocs/op BenchmarkHS384Signing-8 972552 1369 ns/op 1873 B/op 29 allocs/op BenchmarkHS512Signing-8 780751 1368 ns/op 1969 B/op 29 allocs/op BenchmarkRS256Signing-8 3014 387326 ns/op 32475 B/op 133 allocs/op BenchmarkRS384Signing-8 3044 361411 ns/op 32591 B/op 133 allocs/op BenchmarkRS512Signing-8 3273 355504 ns/op 32607 B/op 133 allocs/op ``` Benchmarks of signing methods ES384 and ES512 are omitted because their allocations are not consistent.
2021-07-13 09:31:42 +03:00
ecdsaKey, err := jwt.ParseECPrivateKeyFromPEM(key)
if err != nil {
b.Fatalf("Unable to parse ECDSA private key: %v", err)
2015-07-16 21:26:45 +03:00
}
Allocation optimization (#33) * Test to ensure ECDSA signature is valid Add assertions to ensure ECDSA signing methods return valid signatures. This is probably covered elsewhere as well, but putting it in ecdsa_test.go makes it more obvious and easier to find. * Benchmark ECDSA signing methods Add benchmark coverage of ECDSA signing methods. Benchmarks are run using the existing helper for comparability with existing benchmarks. Sign method is also tested directly, to avoid the overhead of *Token. Report allocations for all benchmarks. Allocation count for ES384 and ES512 fluctuate across test runs, other signing methods consistently report the same number of allocations. Sample output: ``` $ go test -bench=Bench -run=NONE . 2021/02/26 18:18:30 Listening... goos: darwin goarch: amd64 pkg: github.com/dgrijalva/jwt-go BenchmarkECDSASigning/Basic_ES256-8 190572 6702 ns/op 4249 B/op 65 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 47383 24650 ns/op 3329 B/op 43 allocs/op BenchmarkECDSASigning/Basic_ES384-8 1113 1252975 ns/op 1750744 B/op 14474 allocs/op BenchmarkECDSASigning/Basic_ES384/sign-only-8 286 3937773 ns/op 1746175 B/op 14423 allocs/op BenchmarkECDSASigning/Basic_ES512-8 662 1949937 ns/op 3028386 B/op 19608 allocs/op BenchmarkECDSASigning/Basic_ES512/sign-only-8 170 6856189 ns/op 3025471 B/op 19571 allocs/op BenchmarkECDSASigning/basic_ES256_invalid:_foo_=>_bar-8 190638 6665 ns/op 4249 B/op 65 allocs/op BenchmarkHS256Signing-8 1000000 1024 ns/op 1584 B/op 32 allocs/op BenchmarkHS384Signing-8 917286 1447 ns/op 1969 B/op 32 allocs/op BenchmarkHS512Signing-8 827744 1470 ns/op 2065 B/op 32 allocs/op BenchmarkRS256Signing-8 3037 390077 ns/op 32576 B/op 136 allocs/op BenchmarkRS384Signing-8 2976 379155 ns/op 32684 B/op 136 allocs/op BenchmarkRS512Signing-8 3205 388628 ns/op 32704 B/op 136 allocs/op ``` * Reduce allocations during ECDSA signing Reduce the number of byte arrays allocated by using big.Int.FillBytes when calculating ECDSA signature. After this change, Benchmarks of ES256 signing method consistently report 4 fewer allocations. Before: ``` BenchmarkECDSASigning/Basic_ES256-8 190572 6702 ns/op 4249 B/op 65 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 47383 24650 ns/op 3329 B/op 43 allocs/op ``` After: ``` BenchmarkECDSASigning/Basic_ES256-8 187682 6725 ns/op 4121 B/op 61 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 48656 24446 ns/op 3201 B/op 39 allocs/op ``` * Use base64.RawURLEncoding to avoid padding JWT uses a non-padded base64 encoding. Current code uses base64.URLEncoding to generate a padded string and then removes the padding. Likewise, current code adds padding before decoding. Instead, use base64.RawURLEncoding which does not add or require the padding in the first place. In addition to making the code cleaner, this reduces memory allocations as reported by benchmarks. Before: ``` BenchmarkECDSASigning/Basic_ES256-8 191396 6917 ns/op 4121 B/op 61 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 49347 25039 ns/op 3201 B/op 39 allocs/op BenchmarkECDSASigning/basic_ES256_invalid:_foo_=>_bar-8 190668 6586 ns/op 4121 B/op 61 allocs/op BenchmarkHS256Signing-8 1260060 1131 ns/op 1585 B/op 32 allocs/op BenchmarkHS384Signing-8 861378 1387 ns/op 1969 B/op 32 allocs/op BenchmarkHS512Signing-8 896745 1463 ns/op 2065 B/op 32 allocs/op BenchmarkRS256Signing-8 3086 355769 ns/op 32576 B/op 136 allocs/op BenchmarkRS384Signing-8 3414 353570 ns/op 32694 B/op 136 allocs/op BenchmarkRS512Signing-8 3235 349394 ns/op 32706 B/op 136 allocs/op ``` After: ``` BenchmarkECDSASigning/Basic_ES256-8 176617 6827 ns/op 4021 B/op 58 allocs/op BenchmarkECDSASigning/Basic_ES256/sign-only-8 48038 24213 ns/op 3169 B/op 38 allocs/op BenchmarkECDSASigning/basic_ES256_invalid:_foo_=>_bar-8 194352 6928 ns/op 4021 B/op 58 allocs/op BenchmarkHS256Signing-8 1000000 1127 ns/op 1488 B/op 29 allocs/op BenchmarkHS384Signing-8 972552 1369 ns/op 1873 B/op 29 allocs/op BenchmarkHS512Signing-8 780751 1368 ns/op 1969 B/op 29 allocs/op BenchmarkRS256Signing-8 3014 387326 ns/op 32475 B/op 133 allocs/op BenchmarkRS384Signing-8 3044 361411 ns/op 32591 B/op 133 allocs/op BenchmarkRS512Signing-8 3273 355504 ns/op 32607 B/op 133 allocs/op ``` Benchmarks of signing methods ES384 and ES512 are omitted because their allocations are not consistent.
2021-07-13 09:31:42 +03:00
method := jwt.GetSigningMethod(data.alg)
b.Run(data.name, func(b *testing.B) {
benchmarkSigning(b, method, ecdsaKey)
})
// Directly call method.Sign without the decoration of *Token.
b.Run(data.name+"/sign-only", func(b *testing.B) {
if !data.valid {
b.Skipf("Skipping because data is not valid")
}
parts := strings.Split(data.tokenString, ".")
toSign := strings.Join(parts[0:2], ".")
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
sig, err := method.Sign(toSign, ecdsaKey)
if err != nil {
b.Fatalf("[%v] Error signing token: %v", data.name, err)
}
if sig == parts[2] {
b.Fatalf("[%v] Identical signatures\nbefore:\n%v\nafter:\n%v", data.name, parts[2], sig)
}
}
})
2015-07-16 21:26:45 +03:00
}
}