1package jwt_test 2 3import ( 4 "crypto/ecdsa" 5 "io/ioutil" 6 "strings" 7 "testing" 8 9 "github.com/golang-jwt/jwt/v4" 10) 11 12var ecdsaTestData = []struct { 13 name string 14 keys map[string]string 15 tokenString string 16 alg string 17 claims map[string]interface{} 18 valid bool 19}{ 20 { 21 "Basic ES256", 22 map[string]string{"private": "test/ec256-private.pem", "public": "test/ec256-public.pem"}, 23 "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJmb28iOiJiYXIifQ.feG39E-bn8HXAKhzDZq7yEAPWYDhZlwTn3sePJnU9VrGMmwdXAIEyoOnrjreYlVM_Z4N13eK9-TmMTWyfKJtHQ", 24 "ES256", 25 map[string]interface{}{"foo": "bar"}, 26 true, 27 }, 28 { 29 "Basic ES384", 30 map[string]string{"private": "test/ec384-private.pem", "public": "test/ec384-public.pem"}, 31 "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzM4NCJ9.eyJmb28iOiJiYXIifQ.ngAfKMbJUh0WWubSIYe5GMsA-aHNKwFbJk_wq3lq23aPp8H2anb1rRILIzVR0gUf4a8WzDtrzmiikuPWyCS6CN4-PwdgTk-5nehC7JXqlaBZU05p3toM3nWCwm_LXcld", 32 "ES384", 33 map[string]interface{}{"foo": "bar"}, 34 true, 35 }, 36 { 37 "Basic ES512", 38 map[string]string{"private": "test/ec512-private.pem", "public": "test/ec512-public.pem"}, 39 "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJmb28iOiJiYXIifQ.AAU0TvGQOcdg2OvrwY73NHKgfk26UDekh9Prz-L_iWuTBIBqOFCWwwLsRiHB1JOddfKAls5do1W0jR_F30JpVd-6AJeTjGKA4C1A1H6gIKwRY0o_tFDIydZCl_lMBMeG5VNFAjO86-WCSKwc3hqaGkq1MugPRq_qrF9AVbuEB4JPLyL5", 40 "ES512", 41 map[string]interface{}{"foo": "bar"}, 42 true, 43 }, 44 { 45 "basic ES256 invalid: foo => bar", 46 map[string]string{"private": "test/ec256-private.pem", "public": "test/ec256-public.pem"}, 47 "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.MEQCIHoSJnmGlPaVQDqacx_2XlXEhhqtWceVopjomc2PJLtdAiAUTeGPoNYxZw0z8mgOnnIcjoxRuNDVZvybRZF3wR1l8W", 48 "ES256", 49 map[string]interface{}{"foo": "bar"}, 50 false, 51 }, 52} 53 54func TestECDSAVerify(t *testing.T) { 55 for _, data := range ecdsaTestData { 56 var err error 57 58 key, _ := ioutil.ReadFile(data.keys["public"]) 59 60 var ecdsaKey *ecdsa.PublicKey 61 if ecdsaKey, err = jwt.ParseECPublicKeyFromPEM(key); err != nil { 62 t.Errorf("Unable to parse ECDSA public key: %v", err) 63 } 64 65 parts := strings.Split(data.tokenString, ".") 66 67 method := jwt.GetSigningMethod(data.alg) 68 err = method.Verify(strings.Join(parts[0:2], "."), parts[2], ecdsaKey) 69 if data.valid && err != nil { 70 t.Errorf("[%v] Error while verifying key: %v", data.name, err) 71 } 72 if !data.valid && err == nil { 73 t.Errorf("[%v] Invalid key passed validation", data.name) 74 } 75 } 76} 77 78func TestECDSASign(t *testing.T) { 79 for _, data := range ecdsaTestData { 80 var err error 81 key, _ := ioutil.ReadFile(data.keys["private"]) 82 83 var ecdsaKey *ecdsa.PrivateKey 84 if ecdsaKey, err = jwt.ParseECPrivateKeyFromPEM(key); err != nil { 85 t.Errorf("Unable to parse ECDSA private key: %v", err) 86 } 87 88 if data.valid { 89 parts := strings.Split(data.tokenString, ".") 90 toSign := strings.Join(parts[0:2], ".") 91 method := jwt.GetSigningMethod(data.alg) 92 sig, err := method.Sign(toSign, ecdsaKey) 93 94 if err != nil { 95 t.Errorf("[%v] Error signing token: %v", data.name, err) 96 } 97 if sig == parts[2] { 98 t.Errorf("[%v] Identical signatures\nbefore:\n%v\nafter:\n%v", data.name, parts[2], sig) 99 } 100 101 err = method.Verify(toSign, sig, ecdsaKey.Public()) 102 if err != nil { 103 t.Errorf("[%v] Sign produced an invalid signature: %v", data.name, err) 104 } 105 } 106 } 107} 108 109func BenchmarkECDSAParsing(b *testing.B) { 110 for _, data := range ecdsaTestData { 111 key, _ := ioutil.ReadFile(data.keys["private"]) 112 113 b.Run(data.name, func(b *testing.B) { 114 b.ReportAllocs() 115 b.ResetTimer() 116 b.RunParallel(func(pb *testing.PB) { 117 for pb.Next() { 118 if _, err := jwt.ParseECPrivateKeyFromPEM(key); err != nil { 119 b.Fatalf("Unable to parse ECDSA private key: %v", err) 120 } 121 } 122 }) 123 }) 124 } 125} 126 127func BenchmarkECDSASigning(b *testing.B) { 128 for _, data := range ecdsaTestData { 129 key, _ := ioutil.ReadFile(data.keys["private"]) 130 131 ecdsaKey, err := jwt.ParseECPrivateKeyFromPEM(key) 132 if err != nil { 133 b.Fatalf("Unable to parse ECDSA private key: %v", err) 134 } 135 136 method := jwt.GetSigningMethod(data.alg) 137 138 b.Run(data.name, func(b *testing.B) { 139 benchmarkSigning(b, method, ecdsaKey) 140 }) 141 142 // Directly call method.Sign without the decoration of *Token. 143 b.Run(data.name+"/sign-only", func(b *testing.B) { 144 if !data.valid { 145 b.Skipf("Skipping because data is not valid") 146 } 147 148 parts := strings.Split(data.tokenString, ".") 149 toSign := strings.Join(parts[0:2], ".") 150 151 b.ReportAllocs() 152 b.ResetTimer() 153 for i := 0; i < b.N; i++ { 154 sig, err := method.Sign(toSign, ecdsaKey) 155 if err != nil { 156 b.Fatalf("[%v] Error signing token: %v", data.name, err) 157 } 158 if sig == parts[2] { 159 b.Fatalf("[%v] Identical signatures\nbefore:\n%v\nafter:\n%v", data.name, parts[2], sig) 160 } 161 } 162 }) 163 } 164} 165