...

Source file src/github.com/golang-jwt/jwt/v5/ecdsa_test.go

Documentation: github.com/golang-jwt/jwt/v5

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

View as plain text