1// Copyright 2015 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package acme 6 7import ( 8 "crypto" 9 "crypto/ecdsa" 10 "crypto/elliptic" 11 "crypto/rsa" 12 "crypto/sha256" 13 "crypto/x509" 14 "encoding/base64" 15 "encoding/json" 16 "encoding/pem" 17 "fmt" 18 "io" 19 "math/big" 20 "testing" 21) 22 23// The following shell command alias is used in the comments 24// throughout this file: 25// alias b64raw="base64 -w0 | tr -d '=' | tr '/+' '_-'" 26 27const ( 28 // Modulus in raw base64: 29 // 4xgZ3eRPkwoRvy7qeRUbmMDe0V-xH9eWLdu0iheeLlrmD2mqWXfP9IeSKApbn34 30 // g8TuAS9g5zhq8ELQ3kmjr-KV86GAMgI6VAcGlq3QrzpTCf_30Ab7-zawrfRaFON 31 // a1HwEzPY1KHnGVkxJc85gNkwYI9SY2RHXtvln3zs5wITNrdosqEXeaIkVYBEhbh 32 // Nu54pp3kxo6TuWLi9e6pXeWetEwmlBwtWZlPoib2j3TxLBksKZfoyFyek380mHg 33 // JAumQ_I2fjj98_97mk3ihOY4AgVdCDj1z_GCoZkG5Rq7nbCGyosyKWyDX00Zs-n 34 // NqVhoLeIvXC4nnWdJMZ6rogxyQQ 35 testKeyPEM = ` 36-----BEGIN RSA PRIVATE KEY----- 37MIIEowIBAAKCAQEA4xgZ3eRPkwoRvy7qeRUbmMDe0V+xH9eWLdu0iheeLlrmD2mq 38WXfP9IeSKApbn34g8TuAS9g5zhq8ELQ3kmjr+KV86GAMgI6VAcGlq3QrzpTCf/30 39Ab7+zawrfRaFONa1HwEzPY1KHnGVkxJc85gNkwYI9SY2RHXtvln3zs5wITNrdosq 40EXeaIkVYBEhbhNu54pp3kxo6TuWLi9e6pXeWetEwmlBwtWZlPoib2j3TxLBksKZf 41oyFyek380mHgJAumQ/I2fjj98/97mk3ihOY4AgVdCDj1z/GCoZkG5Rq7nbCGyosy 42KWyDX00Zs+nNqVhoLeIvXC4nnWdJMZ6rogxyQQIDAQABAoIBACIEZTOI1Kao9nmV 439IeIsuaR1Y61b9neOF/MLmIVIZu+AAJFCMB4Iw11FV6sFodwpEyeZhx2WkpWVN+H 44r19eGiLX3zsL0DOdqBJoSIHDWCCMxgnYJ6nvS0nRxX3qVrBp8R2g12Ub+gNPbmFm 45ecf/eeERIVxfifd9VsyRu34eDEvcmKFuLYbElFcPh62xE3x12UZvV/sN7gXbawpP 46G+w255vbE5MoaKdnnO83cTFlcHvhn24M/78qP7Te5OAeelr1R89kYxQLpuGe4fbS 47zc6E3ym5Td6urDetGGrSY1Eu10/8sMusX+KNWkm+RsBRbkyKq72ks/qKpOxOa+c6 489gm+Y8ECgYEA/iNUyg1ubRdH11p82l8KHtFC1DPE0V1gSZsX29TpM5jS4qv46K+s 498Ym1zmrORM8x+cynfPx1VQZQ34EYeCMIX212ryJ+zDATl4NE0I4muMvSiH9vx6Xc 507FmhNnaYzPsBL5Tm9nmtQuP09YEn8poiOJFiDs/4olnD5ogA5O4THGkCgYEA5MIL 51qWYBUuqbEWLRtMruUtpASclrBqNNsJEsMGbeqBJmoMxdHeSZckbLOrqm7GlMyNRJ 52Ne/5uWRGSzaMYuGmwsPpERzqEvYFnSrpjW5YtXZ+JtxFXNVfm9Z1gLLgvGpOUCIU 53RbpoDckDe1vgUuk3y5+DjZihs+rqIJ45XzXTzBkCgYBWuf3segruJZy5rEKhTv+o 54JqeUvRn0jNYYKFpLBeyTVBrbie6GkbUGNIWbrK05pC+c3K9nosvzuRUOQQL1tJbd 554gA3oiD9U4bMFNr+BRTHyZ7OQBcIXdz3t1qhuHVKtnngIAN1p25uPlbRFUNpshnt 56jgeVoHlsBhApcs5DUc+pyQKBgDzeHPg/+g4z+nrPznjKnktRY1W+0El93kgi+J0Q 57YiJacxBKEGTJ1MKBb8X6sDurcRDm22wMpGfd9I5Cv2v4GsUsF7HD/cx5xdih+G73 58c4clNj/k0Ff5Nm1izPUno4C+0IOl7br39IPmfpSuR6wH/h6iHQDqIeybjxyKvT1G 59N0rRAoGBAKGD+4ZI/E1MoJ5CXB8cDDMHagbE3cq/DtmYzE2v1DFpQYu5I4PCm5c7 60EQeIP6dZtv8IMgtGIb91QX9pXvP0aznzQKwYIA8nZgoENCPfiMTPiEDT9e/0lObO 619XWsXpbSTsRPj0sv1rB+UzBJ0PgjK4q2zOF0sNo7b1+6nlM3BWPx 62-----END RSA PRIVATE KEY----- 63` 64 65 // This thumbprint is for the testKey defined above. 66 testKeyThumbprint = "6nicxzh6WETQlrvdchkz-U3e3DOQZ4heJKU63rfqMqQ" 67 68 // openssl ecparam -name secp256k1 -genkey -noout 69 testKeyECPEM = ` 70-----BEGIN EC PRIVATE KEY----- 71MHcCAQEEIK07hGLr0RwyUdYJ8wbIiBS55CjnkMD23DWr+ccnypWLoAoGCCqGSM49 72AwEHoUQDQgAE5lhEug5xK4xBDZ2nAbaxLtaLiv85bxJ7ePd1dkO23HThqIrvawF5 73QAaS/RNouybCiRhRjI3EaxLkQwgrCw0gqQ== 74-----END EC PRIVATE KEY----- 75` 76 // openssl ecparam -name secp384r1 -genkey -noout 77 testKeyEC384PEM = ` 78-----BEGIN EC PRIVATE KEY----- 79MIGkAgEBBDAQ4lNtXRORWr1bgKR1CGysr9AJ9SyEk4jiVnlUWWUChmSNL+i9SLSD 80Oe/naPqXJ6CgBwYFK4EEACKhZANiAAQzKtj+Ms0vHoTX5dzv3/L5YMXOWuI5UKRj 81JigpahYCqXD2BA1j0E/2xt5vlPf+gm0PL+UHSQsCokGnIGuaHCsJAp3ry0gHQEke 82WYXapUUFdvaK1R2/2hn5O+eiQM8YzCg= 83-----END EC PRIVATE KEY----- 84` 85 // openssl ecparam -name secp521r1 -genkey -noout 86 testKeyEC512PEM = ` 87-----BEGIN EC PRIVATE KEY----- 88MIHcAgEBBEIBSNZKFcWzXzB/aJClAb305ibalKgtDA7+70eEkdPt28/3LZMM935Z 89KqYHh/COcxuu3Kt8azRAUz3gyr4zZKhlKUSgBwYFK4EEACOhgYkDgYYABAHUNKbx 907JwC7H6pa2sV0tERWhHhB3JmW+OP6SUgMWryvIKajlx73eS24dy4QPGrWO9/ABsD 91FqcRSkNVTXnIv6+0mAF25knqIBIg5Q8M9BnOu9GGAchcwt3O7RDHmqewnJJDrbjd 92GGnm6rb+NnWR9DIopM0nKNkToWoF/hzopxu4Ae/GsQ== 93-----END EC PRIVATE KEY----- 94` 95 // 1. openssl ec -in key.pem -noout -text 96 // 2. remove first byte, 04 (the header); the rest is X and Y 97 // 3. convert each with: echo <val> | xxd -r -p | b64raw 98 testKeyECPubX = "5lhEug5xK4xBDZ2nAbaxLtaLiv85bxJ7ePd1dkO23HQ" 99 testKeyECPubY = "4aiK72sBeUAGkv0TaLsmwokYUYyNxGsS5EMIKwsNIKk" 100 testKeyEC384PubX = "MyrY_jLNLx6E1-Xc79_y-WDFzlriOVCkYyYoKWoWAqlw9gQNY9BP9sbeb5T3_oJt" 101 testKeyEC384PubY = "Dy_lB0kLAqJBpyBrmhwrCQKd68tIB0BJHlmF2qVFBXb2itUdv9oZ-TvnokDPGMwo" 102 testKeyEC512PubX = "AdQ0pvHsnALsfqlraxXS0RFaEeEHcmZb44_pJSAxavK8gpqOXHvd5Lbh3LhA8atY738AGwMWpxFKQ1VNeci_r7SY" 103 testKeyEC512PubY = "AXbmSeogEiDlDwz0Gc670YYByFzC3c7tEMeap7CckkOtuN0Yaebqtv42dZH0MiikzSco2ROhagX-HOinG7gB78ax" 104 105 // echo -n '{"crv":"P-256","kty":"EC","x":"<testKeyECPubX>","y":"<testKeyECPubY>"}' | \ 106 // openssl dgst -binary -sha256 | b64raw 107 testKeyECThumbprint = "zedj-Bd1Zshp8KLePv2MB-lJ_Hagp7wAwdkA0NUTniU" 108) 109 110var ( 111 testKey *rsa.PrivateKey 112 testKeyEC *ecdsa.PrivateKey 113 testKeyEC384 *ecdsa.PrivateKey 114 testKeyEC512 *ecdsa.PrivateKey 115) 116 117func init() { 118 testKey = parseRSA(testKeyPEM, "testKeyPEM") 119 testKeyEC = parseEC(testKeyECPEM, "testKeyECPEM") 120 testKeyEC384 = parseEC(testKeyEC384PEM, "testKeyEC384PEM") 121 testKeyEC512 = parseEC(testKeyEC512PEM, "testKeyEC512PEM") 122} 123 124func decodePEM(s, name string) []byte { 125 d, _ := pem.Decode([]byte(s)) 126 if d == nil { 127 panic("no block found in " + name) 128 } 129 return d.Bytes 130} 131 132func parseRSA(s, name string) *rsa.PrivateKey { 133 b := decodePEM(s, name) 134 k, err := x509.ParsePKCS1PrivateKey(b) 135 if err != nil { 136 panic(fmt.Sprintf("%s: %v", name, err)) 137 } 138 return k 139} 140 141func parseEC(s, name string) *ecdsa.PrivateKey { 142 b := decodePEM(s, name) 143 k, err := x509.ParseECPrivateKey(b) 144 if err != nil { 145 panic(fmt.Sprintf("%s: %v", name, err)) 146 } 147 return k 148} 149 150func TestJWSEncodeJSON(t *testing.T) { 151 claims := struct{ Msg string }{"Hello JWS"} 152 // JWS signed with testKey and "nonce" as the nonce value 153 // JSON-serialized JWS fields are split for easier testing 154 const ( 155 // {"alg":"RS256","jwk":{"e":"AQAB","kty":"RSA","n":"..."},"nonce":"nonce","url":"url"} 156 protected = "eyJhbGciOiJSUzI1NiIsImp3ayI6eyJlIjoiQVFBQiIsImt0eSI6" + 157 "IlJTQSIsIm4iOiI0eGdaM2VSUGt3b1J2eTdxZVJVYm1NRGUwVi14" + 158 "SDllV0xkdTBpaGVlTGxybUQybXFXWGZQOUllU0tBcGJuMzRnOFR1" + 159 "QVM5ZzV6aHE4RUxRM2ttanItS1Y4NkdBTWdJNlZBY0dscTNRcnpw" + 160 "VENmXzMwQWI3LXphd3JmUmFGT05hMUh3RXpQWTFLSG5HVmt4SmM4" + 161 "NWdOa3dZSTlTWTJSSFh0dmxuM3pzNXdJVE5yZG9zcUVYZWFJa1ZZ" + 162 "QkVoYmhOdTU0cHAza3hvNlR1V0xpOWU2cFhlV2V0RXdtbEJ3dFda" + 163 "bFBvaWIyajNUeExCa3NLWmZveUZ5ZWszODBtSGdKQXVtUV9JMmZq" + 164 "ajk4Xzk3bWszaWhPWTRBZ1ZkQ0RqMXpfR0NvWmtHNVJxN25iQ0d5" + 165 "b3N5S1d5RFgwMFpzLW5OcVZob0xlSXZYQzRubldkSk1aNnJvZ3h5" + 166 "UVEifSwibm9uY2UiOiJub25jZSIsInVybCI6InVybCJ9" 167 // {"Msg":"Hello JWS"} 168 payload = "eyJNc2ciOiJIZWxsbyBKV1MifQ" 169 // printf '<protected>.<payload>' | openssl dgst -binary -sha256 -sign testKey | b64raw 170 signature = "YFyl_xz1E7TR-3E1bIuASTr424EgCvBHjt25WUFC2VaDjXYV0Rj_" + 171 "Hd3dJ_2IRqBrXDZZ2n4ZeA_4mm3QFwmwyeDwe2sWElhb82lCZ8iX" + 172 "uFnjeOmSOjx-nWwPa5ibCXzLq13zZ-OBV1Z4oN_TuailQeRoSfA3" + 173 "nO8gG52mv1x2OMQ5MAFtt8jcngBLzts4AyhI6mBJ2w7Yaj3ZCriq" + 174 "DWA3GLFvvHdW1Ba9Z01wtGT2CuZI7DUk_6Qj1b3BkBGcoKur5C9i" + 175 "bUJtCkABwBMvBQNyD3MmXsrRFRTgvVlyU_yMaucYm7nmzEr_2PaQ" + 176 "50rFt_9qOfJ4sfbLtG1Wwae57BQx1g" 177 ) 178 179 b, err := jwsEncodeJSON(claims, testKey, noKeyID, "nonce", "url") 180 if err != nil { 181 t.Fatal(err) 182 } 183 var jws struct{ Protected, Payload, Signature string } 184 if err := json.Unmarshal(b, &jws); err != nil { 185 t.Fatal(err) 186 } 187 if jws.Protected != protected { 188 t.Errorf("protected:\n%s\nwant:\n%s", jws.Protected, protected) 189 } 190 if jws.Payload != payload { 191 t.Errorf("payload:\n%s\nwant:\n%s", jws.Payload, payload) 192 } 193 if jws.Signature != signature { 194 t.Errorf("signature:\n%s\nwant:\n%s", jws.Signature, signature) 195 } 196} 197 198func TestJWSEncodeKID(t *testing.T) { 199 kid := keyID("https://example.org/account/1") 200 claims := struct{ Msg string }{"Hello JWS"} 201 // JWS signed with testKeyEC 202 const ( 203 // {"alg":"ES256","kid":"https://example.org/account/1","nonce":"nonce","url":"url"} 204 protected = "eyJhbGciOiJFUzI1NiIsImtpZCI6Imh0dHBzOi8vZXhhbXBsZS5" + 205 "vcmcvYWNjb3VudC8xIiwibm9uY2UiOiJub25jZSIsInVybCI6InVybCJ9" 206 // {"Msg":"Hello JWS"} 207 payload = "eyJNc2ciOiJIZWxsbyBKV1MifQ" 208 ) 209 210 b, err := jwsEncodeJSON(claims, testKeyEC, kid, "nonce", "url") 211 if err != nil { 212 t.Fatal(err) 213 } 214 var jws struct{ Protected, Payload, Signature string } 215 if err := json.Unmarshal(b, &jws); err != nil { 216 t.Fatal(err) 217 } 218 if jws.Protected != protected { 219 t.Errorf("protected:\n%s\nwant:\n%s", jws.Protected, protected) 220 } 221 if jws.Payload != payload { 222 t.Errorf("payload:\n%s\nwant:\n%s", jws.Payload, payload) 223 } 224 225 sig, err := base64.RawURLEncoding.DecodeString(jws.Signature) 226 if err != nil { 227 t.Fatalf("jws.Signature: %v", err) 228 } 229 r, s := big.NewInt(0), big.NewInt(0) 230 r.SetBytes(sig[:len(sig)/2]) 231 s.SetBytes(sig[len(sig)/2:]) 232 h := sha256.Sum256([]byte(protected + "." + payload)) 233 if !ecdsa.Verify(testKeyEC.Public().(*ecdsa.PublicKey), h[:], r, s) { 234 t.Error("invalid signature") 235 } 236} 237 238func TestJWSEncodeJSONEC(t *testing.T) { 239 tt := []struct { 240 key *ecdsa.PrivateKey 241 x, y string 242 alg, crv string 243 }{ 244 {testKeyEC, testKeyECPubX, testKeyECPubY, "ES256", "P-256"}, 245 {testKeyEC384, testKeyEC384PubX, testKeyEC384PubY, "ES384", "P-384"}, 246 {testKeyEC512, testKeyEC512PubX, testKeyEC512PubY, "ES512", "P-521"}, 247 } 248 for i, test := range tt { 249 claims := struct{ Msg string }{"Hello JWS"} 250 b, err := jwsEncodeJSON(claims, test.key, noKeyID, "nonce", "url") 251 if err != nil { 252 t.Errorf("%d: %v", i, err) 253 continue 254 } 255 var jws struct{ Protected, Payload, Signature string } 256 if err := json.Unmarshal(b, &jws); err != nil { 257 t.Errorf("%d: %v", i, err) 258 continue 259 } 260 261 b, err = base64.RawURLEncoding.DecodeString(jws.Protected) 262 if err != nil { 263 t.Errorf("%d: jws.Protected: %v", i, err) 264 } 265 var head struct { 266 Alg string 267 Nonce string 268 URL string `json:"url"` 269 KID string `json:"kid"` 270 JWK struct { 271 Crv string 272 Kty string 273 X string 274 Y string 275 } `json:"jwk"` 276 } 277 if err := json.Unmarshal(b, &head); err != nil { 278 t.Errorf("%d: jws.Protected: %v", i, err) 279 } 280 if head.Alg != test.alg { 281 t.Errorf("%d: head.Alg = %q; want %q", i, head.Alg, test.alg) 282 } 283 if head.Nonce != "nonce" { 284 t.Errorf("%d: head.Nonce = %q; want nonce", i, head.Nonce) 285 } 286 if head.URL != "url" { 287 t.Errorf("%d: head.URL = %q; want 'url'", i, head.URL) 288 } 289 if head.KID != "" { 290 // We used noKeyID in jwsEncodeJSON: expect no kid value. 291 t.Errorf("%d: head.KID = %q; want empty", i, head.KID) 292 } 293 if head.JWK.Crv != test.crv { 294 t.Errorf("%d: head.JWK.Crv = %q; want %q", i, head.JWK.Crv, test.crv) 295 } 296 if head.JWK.Kty != "EC" { 297 t.Errorf("%d: head.JWK.Kty = %q; want EC", i, head.JWK.Kty) 298 } 299 if head.JWK.X != test.x { 300 t.Errorf("%d: head.JWK.X = %q; want %q", i, head.JWK.X, test.x) 301 } 302 if head.JWK.Y != test.y { 303 t.Errorf("%d: head.JWK.Y = %q; want %q", i, head.JWK.Y, test.y) 304 } 305 } 306} 307 308type customTestSigner struct { 309 sig []byte 310 pub crypto.PublicKey 311} 312 313func (s *customTestSigner) Public() crypto.PublicKey { return s.pub } 314func (s *customTestSigner) Sign(io.Reader, []byte, crypto.SignerOpts) ([]byte, error) { 315 return s.sig, nil 316} 317 318func TestJWSEncodeJSONCustom(t *testing.T) { 319 claims := struct{ Msg string }{"hello"} 320 const ( 321 // printf '{"Msg":"hello"}' | b64raw 322 payload = "eyJNc2ciOiJoZWxsbyJ9" 323 // printf 'testsig' | b64raw 324 testsig = "dGVzdHNpZw" 325 326 // the example P256 curve point from https://tools.ietf.org/html/rfc7515#appendix-A.3.1 327 // encoded as ASN.1… 328 es256stdsig = "MEUCIA7RIVN5Y2xIPC9/FVgH1AKjsigDOvl8fheBmsMWnqZlAiEA" + 329 "xQoH04w8cOXY8S2vCEpUgKZlkMXyk1Cajz9/ioOjVNU" 330 // …and RFC7518 (https://tools.ietf.org/html/rfc7518#section-3.4) 331 es256jwsig = "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw" + 332 "5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q" 333 334 // printf '{"alg":"ES256","jwk":{"crv":"P-256","kty":"EC","x":<testKeyECPubY>,"y":<testKeyECPubY>},"nonce":"nonce","url":"url"}' | b64raw 335 es256phead = "eyJhbGciOiJFUzI1NiIsImp3ayI6eyJjcnYiOiJQLTI1NiIsImt0" + 336 "eSI6IkVDIiwieCI6IjVsaEV1ZzV4SzR4QkRaMm5BYmF4THRhTGl2" + 337 "ODVieEo3ZVBkMWRrTzIzSFEiLCJ5IjoiNGFpSzcyc0JlVUFHa3Yw" + 338 "VGFMc213b2tZVVl5TnhHc1M1RU1JS3dzTklLayJ9LCJub25jZSI6" + 339 "Im5vbmNlIiwidXJsIjoidXJsIn0" 340 341 // {"alg":"RS256","jwk":{"e":"AQAB","kty":"RSA","n":"..."},"nonce":"nonce","url":"url"} 342 rs256phead = "eyJhbGciOiJSUzI1NiIsImp3ayI6eyJlIjoiQVFBQiIsImt0eSI6" + 343 "IlJTQSIsIm4iOiI0eGdaM2VSUGt3b1J2eTdxZVJVYm1NRGUwVi14" + 344 "SDllV0xkdTBpaGVlTGxybUQybXFXWGZQOUllU0tBcGJuMzRnOFR1" + 345 "QVM5ZzV6aHE4RUxRM2ttanItS1Y4NkdBTWdJNlZBY0dscTNRcnpw" + 346 "VENmXzMwQWI3LXphd3JmUmFGT05hMUh3RXpQWTFLSG5HVmt4SmM4" + 347 "NWdOa3dZSTlTWTJSSFh0dmxuM3pzNXdJVE5yZG9zcUVYZWFJa1ZZ" + 348 "QkVoYmhOdTU0cHAza3hvNlR1V0xpOWU2cFhlV2V0RXdtbEJ3dFda" + 349 "bFBvaWIyajNUeExCa3NLWmZveUZ5ZWszODBtSGdKQXVtUV9JMmZq" + 350 "ajk4Xzk3bWszaWhPWTRBZ1ZkQ0RqMXpfR0NvWmtHNVJxN25iQ0d5" + 351 "b3N5S1d5RFgwMFpzLW5OcVZob0xlSXZYQzRubldkSk1aNnJvZ3h5" + 352 "UVEifSwibm9uY2UiOiJub25jZSIsInVybCI6InVybCJ9" 353 ) 354 355 tt := []struct { 356 alg, phead string 357 pub crypto.PublicKey 358 stdsig, jwsig string 359 }{ 360 {"ES256", es256phead, testKeyEC.Public(), es256stdsig, es256jwsig}, 361 {"RS256", rs256phead, testKey.Public(), testsig, testsig}, 362 } 363 for _, tc := range tt { 364 tc := tc 365 t.Run(tc.alg, func(t *testing.T) { 366 stdsig, err := base64.RawStdEncoding.DecodeString(tc.stdsig) 367 if err != nil { 368 t.Errorf("couldn't decode test vector: %v", err) 369 } 370 signer := &customTestSigner{ 371 sig: stdsig, 372 pub: tc.pub, 373 } 374 375 b, err := jwsEncodeJSON(claims, signer, noKeyID, "nonce", "url") 376 if err != nil { 377 t.Fatal(err) 378 } 379 var j jsonWebSignature 380 if err := json.Unmarshal(b, &j); err != nil { 381 t.Fatal(err) 382 } 383 if j.Protected != tc.phead { 384 t.Errorf("j.Protected = %q\nwant %q", j.Protected, tc.phead) 385 } 386 if j.Payload != payload { 387 t.Errorf("j.Payload = %q\nwant %q", j.Payload, payload) 388 } 389 if j.Sig != tc.jwsig { 390 t.Errorf("j.Sig = %q\nwant %q", j.Sig, tc.jwsig) 391 } 392 }) 393 } 394} 395 396func TestJWSWithMAC(t *testing.T) { 397 // Example from RFC 7520 Section 4.4.3. 398 // https://tools.ietf.org/html/rfc7520#section-4.4.3 399 b64Key := "hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg" 400 rawPayload := []byte("It\xe2\x80\x99s a dangerous business, Frodo, going out your " + 401 "door. You step onto the road, and if you don't keep your feet, " + 402 "there\xe2\x80\x99s no knowing where you might be swept off " + 403 "to.") 404 protected := "eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LW" + 405 "VlZjMxNGJjNzAzNyJ9" 406 payload := "SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywg" + 407 "Z29pbmcgb3V0IHlvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9h" + 408 "ZCwgYW5kIGlmIHlvdSBkb24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXi" + 409 "gJlzIG5vIGtub3dpbmcgd2hlcmUgeW91IG1pZ2h0IGJlIHN3ZXB0IG9m" + 410 "ZiB0by4" 411 sig := "s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0" 412 413 key, err := base64.RawURLEncoding.DecodeString(b64Key) 414 if err != nil { 415 t.Fatalf("unable to decode key: %q", b64Key) 416 } 417 got, err := jwsWithMAC(key, "018c0ae5-4d9b-471b-bfd6-eef314bc7037", "", rawPayload) 418 if err != nil { 419 t.Fatalf("jwsWithMAC() = %q", err) 420 } 421 if got.Protected != protected { 422 t.Errorf("got.Protected = %q\nwant %q", got.Protected, protected) 423 } 424 if got.Payload != payload { 425 t.Errorf("got.Payload = %q\nwant %q", got.Payload, payload) 426 } 427 if got.Sig != sig { 428 t.Errorf("got.Signature = %q\nwant %q", got.Sig, sig) 429 } 430} 431 432func TestJWSWithMACError(t *testing.T) { 433 p := "{}" 434 if _, err := jwsWithMAC(nil, "", "", []byte(p)); err == nil { 435 t.Errorf("jwsWithMAC(nil, ...) = success; want err") 436 } 437} 438 439func TestJWKThumbprintRSA(t *testing.T) { 440 // Key example from RFC 7638 441 const base64N = "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAt" + 442 "VT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn6" + 443 "4tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FD" + 444 "W2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n9" + 445 "1CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINH" + 446 "aQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw" 447 const base64E = "AQAB" 448 const expected = "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs" 449 450 b, err := base64.RawURLEncoding.DecodeString(base64N) 451 if err != nil { 452 t.Fatalf("Error parsing example key N: %v", err) 453 } 454 n := new(big.Int).SetBytes(b) 455 456 b, err = base64.RawURLEncoding.DecodeString(base64E) 457 if err != nil { 458 t.Fatalf("Error parsing example key E: %v", err) 459 } 460 e := new(big.Int).SetBytes(b) 461 462 pub := &rsa.PublicKey{N: n, E: int(e.Uint64())} 463 th, err := JWKThumbprint(pub) 464 if err != nil { 465 t.Error(err) 466 } 467 if th != expected { 468 t.Errorf("thumbprint = %q; want %q", th, expected) 469 } 470} 471 472func TestJWKThumbprintEC(t *testing.T) { 473 // Key example from RFC 7520 474 // expected was computed with 475 // printf '{"crv":"P-521","kty":"EC","x":"<base64X>","y":"<base64Y>"}' | \ 476 // openssl dgst -binary -sha256 | b64raw 477 const ( 478 base64X = "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9A5RkT" + 479 "KqjqvjyekWF-7ytDyRXYgCF5cj0Kt" 480 base64Y = "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVySsUda" + 481 "QkAgDPrwQrJmbnX9cwlGfP-HqHZR1" 482 expected = "dHri3SADZkrush5HU_50AoRhcKFryN-PI6jPBtPL55M" 483 ) 484 485 b, err := base64.RawURLEncoding.DecodeString(base64X) 486 if err != nil { 487 t.Fatalf("Error parsing example key X: %v", err) 488 } 489 x := new(big.Int).SetBytes(b) 490 491 b, err = base64.RawURLEncoding.DecodeString(base64Y) 492 if err != nil { 493 t.Fatalf("Error parsing example key Y: %v", err) 494 } 495 y := new(big.Int).SetBytes(b) 496 497 pub := &ecdsa.PublicKey{Curve: elliptic.P521(), X: x, Y: y} 498 th, err := JWKThumbprint(pub) 499 if err != nil { 500 t.Error(err) 501 } 502 if th != expected { 503 t.Errorf("thumbprint = %q; want %q", th, expected) 504 } 505} 506 507func TestJWKThumbprintErrUnsupportedKey(t *testing.T) { 508 _, err := JWKThumbprint(struct{}{}) 509 if err != ErrUnsupportedKey { 510 t.Errorf("err = %q; want %q", err, ErrUnsupportedKey) 511 } 512} 513