-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
737b9ff
commit bc1e7fc
Showing
2 changed files
with
257 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
// Package x provides experimental functionality. | ||
package x | ||
|
||
import ( | ||
"crypto/ecdsa" | ||
"crypto/elliptic" | ||
"crypto/rsa" | ||
"encoding/base64" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"math/big" | ||
|
||
"github.com/pascaldekloe/jwt" | ||
) | ||
|
||
var encoding = base64.RawURLEncoding | ||
|
||
type jwk struct { | ||
Keys []*jwk | ||
|
||
Kty *string | ||
Crv string | ||
|
||
K, X, Y, N, E *string | ||
} | ||
|
||
// LoadJWK adds keys from a JWK or a JWK Set, and returns the count. | ||
// Both private and public keys can be used. | ||
func LoadJWK(keys *jwt.KeyRegister, data []byte) (n int, err error) { | ||
j := new(jwk) | ||
if err := json.Unmarshal(data, j); err != nil { | ||
return 0, err | ||
} | ||
|
||
if j.Keys == nil { | ||
// single key | ||
if err := addJWK(keys, j); err != nil { | ||
return 0, err | ||
} | ||
return 1, nil | ||
} | ||
|
||
// key set | ||
for i, k := range j.Keys { | ||
if err := addJWK(keys, k); err != nil { | ||
return i, err | ||
} | ||
} | ||
return len(j.Keys), nil | ||
} | ||
|
||
var ( | ||
errJWKNoKty = errors.New("jwt: JWK missing \"kty\" field") | ||
errJWKParam = errors.New("jwt: JWK missing key–parameter field") | ||
|
||
errJWKCurveSize = errors.New("jwt: JWK curve parameters don't match curve size") | ||
errJWKCurveMiss = errors.New("jwt: JWK curve parameters are not on the curve") | ||
) | ||
|
||
func addJWK(keys *jwt.KeyRegister, j *jwk) error { | ||
// See RFC 7518, subsection 6.1 | ||
|
||
if j.Kty == nil { | ||
return errJWKNoKty | ||
} | ||
switch *j.Kty { | ||
default: | ||
return fmt.Errorf("jwt: JWK with unsupported key type %q", *j.Kty) | ||
|
||
case "EC": | ||
var curve elliptic.Curve | ||
switch j.Crv { | ||
case "P-256": | ||
curve = elliptic.P256() | ||
case "P-384": | ||
curve = elliptic.P384() | ||
case "P-521": | ||
curve = elliptic.P521() | ||
default: | ||
return fmt.Errorf("jwt: JWK with unsupported elliptic curve %q", j.Crv) | ||
} | ||
|
||
x, err := keyParam(j.X) | ||
if err != nil { | ||
return err | ||
} | ||
y, err := keyParam(j.Y) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
size := (curve.Params().BitSize + 7) / 8 | ||
xSize, ySize := (x.BitLen()+7)/8, (y.BitLen()+7)/8 | ||
if xSize != size || ySize != size { | ||
return errJWKCurveSize | ||
} | ||
|
||
if !curve.IsOnCurve(x, y) { | ||
return errJWKCurveMiss | ||
} | ||
|
||
keys.ECDSAs = append(keys.ECDSAs, &ecdsa.PublicKey{Curve: curve, X: x, Y: y}) | ||
|
||
case "RSA": | ||
n, err := keyParam(j.N) | ||
if err != nil { | ||
return err | ||
} | ||
e, err := keyParam(j.E) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
keys.RSAs = append(keys.RSAs, &rsa.PublicKey{N: n, E: int(e.Int64())}) | ||
|
||
case "oct": | ||
if j.K == nil { | ||
return errJWKParam | ||
} | ||
|
||
bytes, err := encoding.DecodeString(*j.K) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
keys.Secrets = append(keys.Secrets, bytes) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func keyParam(p *string) (*big.Int, error) { | ||
if p == nil { | ||
return nil, errJWKParam | ||
} | ||
|
||
bytes, err := encoding.DecodeString(*p) | ||
if err != nil { | ||
return nil, fmt.Errorf("jwt: JWK with malformed key–parameter field: %s", err) | ||
} | ||
|
||
return new(big.Int).SetBytes(bytes), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
package x | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/pascaldekloe/jwt" | ||
) | ||
|
||
var GoldenJWKs = []struct { | ||
Serial string | ||
}{ | ||
// RFC 7517, appendix A.1 | ||
{ | ||
Serial: `{ | ||
"keys": [{ | ||
"kty":"EC", | ||
"crv":"P-256", | ||
"x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", | ||
"y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", | ||
"use":"enc", | ||
"kid":"1" | ||
}, { | ||
"kty":"RSA", | ||
"n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", | ||
"e":"AQAB", | ||
"alg":"RS256", | ||
"kid":"2011-04-29" | ||
}] | ||
}`, | ||
}, | ||
|
||
// RFC 7517, appendix A.2 | ||
{ | ||
Serial: `{ | ||
"keys": [{ | ||
"kty":"EC", | ||
"crv":"P-256", | ||
"x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", | ||
"y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", | ||
"d":"870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE", | ||
"use":"enc", | ||
"kid":"1" | ||
}, { | ||
"kty":"RSA", | ||
"n":"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", | ||
"e":"AQAB", | ||
"d":"X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9M7dx5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqijwp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBznbJSzFHK66jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q", | ||
"p":"83i-7IvMGXoMXCskv73TKr8637FiO7Z27zv8oj6pbWUQyLPQBQxtPVnwD20R-60eTDmD2ujnMt5PoqMrm8RfmNhVWDtjjMmCMjOpSXicFHj7XOuVIYQyqVWlWEh6dN36GVZYk93N8Bc9vY41xy8B9RzzOGVQzXvNEvn7O0nVbfs", | ||
"q":"3dfOR9cuYq-0S-mkFLzgItgMEfFzB2q3hWehMuG0oCuqnb3vobLyumqjVZQO1dIrdwgTnCdpYzBcOfW5r370AFXjiWft_NGEiovonizhKpo9VVS78TzFgxkIdrecRezsZ-1kYd_s1qDbxtkDEgfAITAG9LUnADun4vIcb6yelxk", | ||
"dp":"G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", | ||
"dq":"s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", | ||
"qi":"GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", | ||
"alg":"RS256", | ||
"kid":"2011-04-29" | ||
}] | ||
}`, | ||
}, | ||
|
||
// RFC 7517, appendix A.3 | ||
{ | ||
Serial: `{ | ||
"keys": [{ | ||
"kty":"oct", | ||
"alg":"A128KW", | ||
"k":"GawgguFyGrWKav7AX4VKUg" | ||
}, { | ||
"kty":"oct", | ||
"k":"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow", | ||
"kid":"HMAC key used in JWS spec Appendix A.1 example" | ||
}] | ||
}`, | ||
}, | ||
|
||
// RFC 7517, appendix B | ||
{ | ||
Serial: `{ | ||
"kty":"RSA", | ||
"use":"sig", | ||
"kid":"1b94c", | ||
"n":"vrjOfz9Ccdgx5nQudyhdoR17V-IubWMeOZCwX_jj0hgAsz2J_pqYW08PLbK_PdiVGKPrqzmDIsLI7sA25VEnHU1uCLNwBuUiCO11_-7dYbsr4iJmG0Qu2j8DsVyT1azpJC_NG84Ty5KKthuCaPod7iI7w0LK9orSMhBEwwZDCxTWq4aYWAchc8t-emd9qOvWtVMDC2BXksRngh6X5bUYLy6AyHKvj-nUy1wgzjYQDwHMTplCoLtU-o-8SNnZ1tmRoGE9uJkBLdh5gFENabWnU5m1ZqZPdwS-qo-meMvVfJb6jJVWRpl2SUtCnYG2C32qvbWbjZ_jBPD5eunqsIo1vQ", | ||
"e":"AQAB", | ||
"x5c": ["MIIDQjCCAiqgAwIBAgIGATz/FuLiMA0GCSqGSIb3DQEBBQUAMGIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRwwGgYDVQQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5CcmlhbiBDYW1wYmVsbDAeFw0xMzAyMjEyMzI5MTVaFw0xODA4MTQyMjI5MTVaMGIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRwwGgYDVQQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5CcmlhbiBDYW1wYmVsbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL64zn8/QnHYMeZ0LncoXaEde1fiLm1jHjmQsF/449IYALM9if6amFtPDy2yvz3YlRij66s5gyLCyO7ANuVRJx1NbgizcAblIgjtdf/u3WG7K+IiZhtELto/A7Fck9Ws6SQvzRvOE8uSirYbgmj6He4iO8NCyvaK0jIQRMMGQwsU1quGmFgHIXPLfnpnfajr1rVTAwtgV5LEZ4Iel+W1GC8ugMhyr4/p1MtcIM42EA8BzE6ZQqC7VPqPvEjZ2dbZkaBhPbiZAS3YeYBRDWm1p1OZtWamT3cEvqqPpnjL1XyW+oyVVkaZdklLQp2Btgt9qr21m42f4wTw+Xrp6rCKNb0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAh8zGlfSlcI0o3rYDPBB07aXNswb4ECNIKG0CETTUxmXl9KUL+9gGlqCz5iWLOgWsnrcKcY0vXPG9J1r9AqBNTqNgHq2G03X09266X5CpOe1zFo+Owb1zxtp3PehFdfQJ610CDLEaS9V9Rqp17hCyybEpOGVwe8fnk+fbEL2Bo3UPGrpsHzUoaGpDftmWssZkhpBJKVMJyf/RuP2SmmaIzmnw9JiSlYhzo4tpzd5rFXhjRbg4zW9C+2qok+2+qDM1iJ684gPHMIY8aLWrdgQTxkumGmTqgawR+N5MDtdPTEQ0XfIBc2cJEUyMTY5MPvACWpkA6SdS4xSvdXK3IVfOWA=="] | ||
}`, | ||
}, | ||
|
||
// RFC 7517, appendix C | ||
{ | ||
Serial: `{ | ||
"kty":"RSA", | ||
"kid":"juliet@capulet.lit", | ||
"use":"enc", | ||
"n":"t6Q8PWSi1dkJj9hTP8hNYFlvadM7DflW9mWepOJhJ66w7nyoK1gPNqFMSQRyO125Gp-TEkodhWr0iujjHVx7BcV0llS4w5ACGgPrcAd6ZcSR0-Iqom-QFcNP8Sjg086MwoqQU_LYywlAGZ21WSdS_PERyGFiNnj3QQlO8Yns5jCtLCRwLHL0Pb1fEv45AuRIuUfVcPySBWYnDyGxvjYGDSM-AqWS9zIQ2ZilgT-GqUmipg0XOC0Cc20rgLe2ymLHjpHciCKVAbY5-L32-lSeZO-Os6U15_aXrk9Gw8cPUaX1_I8sLGuSiVdt3C_Fn2PZ3Z8i744FPFGGcG1qs2Wz-Q", | ||
"e":"AQAB", | ||
"d":"GRtbIQmhOZtyszfgKdg4u_N-R_mZGU_9k7JQ_jn1DnfTuMdSNprTeaSTyWfSNkuaAwnOEbIQVy1IQbWVV25NY3ybc_IhUJtfri7bAXYEReWaCl3hdlPKXy9UvqPYGR0kIXTQRqns-dVJ7jahlI7LyckrpTmrM8dWBo4_PMaenNnPiQgO0xnuToxutRZJfJvG4Ox4ka3GORQd9CsCZ2vsUDmsXOfUENOyMqADC6p1M3h33tsurY15k9qMSpG9OX_IJAXmxzAh_tWiZOwk2K4yxH9tS3Lq1yX8C1EWmeRDkK2ahecG85-oLKQt5VEpWHKmjOi_gJSdSgqcN96X52esAQ", | ||
"p":"2rnSOV4hKSN8sS4CgcQHFbs08XboFDqKum3sc4h3GRxrTmQdl1ZK9uw-PIHfQP0FkxXVrx-WE-ZEbrqivH_2iCLUS7wAl6XvARt1KkIaUxPPSYB9yk31s0Q8UK96E3_OrADAYtAJs-M3JxCLfNgqh56HDnETTQhH3rCT5T3yJws", | ||
"q":"1u_RiFDP7LBYh3N4GXLT9OpSKYP0uQZyiaZwBtOCBNJgQxaj10RWjsZu0c6Iedis4S7B_coSKB0Kj9PaPaBzg-IySRvvcQuPamQu66riMhjVtG6TlV8CLCYKrYl52ziqK0E_ym2QnkwsUX7eYTB7LbAHRK9GqocDE5B0f808I4s", | ||
"dp":"KkMTWqBUefVwZ2_Dbj1pPQqyHSHjj90L5x_MOzqYAJMcLMZtbUtwKqvVDq3tbEo3ZIcohbDtt6SbfmWzggabpQxNxuBpoOOf_a_HgMXK_lhqigI4y_kqS1wY52IwjUn5rgRrJ-yYo1h41KR-vz2pYhEAeYrhttWtxVqLCRViD6c", | ||
"dq":"AvfS0-gRxvn0bwJoMSnFxYcK1WnuEjQFluMGfwGitQBWtfZ1Er7t1xDkbN9GQTB9yqpDoYaN06H7CFtrkxhJIBQaj6nkF5KKS3TQtQ5qCzkOkmxIe3KRbBymXxkb5qwUpX5ELD5xFc6FeiafWYY63TmmEAu_lRFCOJ3xDea-ots", | ||
"qi":"lSQi-w9CpyUReMErP1RsBLk7wNtOvs5EQpPqmuMvqW57NBUczScEoPwmUqqabu9V0-Py4dQ57_bapoKRu1R90bvuFnU63SHWEFglZQvJDMeAvmj4sm-Fp0oYu_neotgQ0hzbI5gry7ajdYy9-2lNx_76aBZoOUu9HCJ-UsfSOI8" | ||
}`, | ||
}, | ||
} | ||
|
||
func Test(t *testing.T) { | ||
for _, gold := range GoldenJWKs { | ||
var keys jwt.KeyRegister | ||
_, err := LoadJWK(&keys, []byte(gold.Serial)) | ||
if err != nil { | ||
t.Errorf("got error %q for serial: %s", err, gold.Serial) | ||
continue | ||
} | ||
} | ||
} |