blob: 0f3a19f67dbfc908b4aaed70815788a14d663c58 [file] [log] [blame]
/*-
* Copyright 2014 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jose
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
"crypto/x509"
"encoding/hex"
"math/big"
"reflect"
"strings"
"testing"
"golang.org/x/crypto/ed25519"
"gopkg.in/square/go-jose.v2/json"
)
// Test chain of two X.509 certificates
var testCertificates, _ = x509.ParseCertificates(fromBase64Bytes(`
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`))
func TestCurveSize(t *testing.T) {
size256 := curveSize(elliptic.P256())
size384 := curveSize(elliptic.P384())
size521 := curveSize(elliptic.P521())
if size256 != 32 {
t.Error("P-256 have 32 bytes")
}
if size384 != 48 {
t.Error("P-384 have 48 bytes")
}
if size521 != 66 {
t.Error("P-521 have 66 bytes")
}
}
func TestRoundtripRsaPrivate(t *testing.T) {
jwk, err := fromRsaPrivateKey(rsaTestKey)
if err != nil {
t.Error("problem constructing JWK from rsa key", err)
}
rsa2, err := jwk.rsaPrivateKey()
if err != nil {
t.Error("problem converting RSA private -> JWK", err)
}
if rsa2.N.Cmp(rsaTestKey.N) != 0 {
t.Error("RSA private N mismatch")
}
if rsa2.E != rsaTestKey.E {
t.Error("RSA private E mismatch")
}
if rsa2.D.Cmp(rsaTestKey.D) != 0 {
t.Error("RSA private D mismatch")
}
if len(rsa2.Primes) != 2 {
t.Error("RSA private roundtrip expected two primes")
}
if rsa2.Primes[0].Cmp(rsaTestKey.Primes[0]) != 0 {
t.Error("RSA private P mismatch")
}
if rsa2.Primes[1].Cmp(rsaTestKey.Primes[1]) != 0 {
t.Error("RSA private Q mismatch")
}
}
func TestRoundtripRsaPrivatePrecomputed(t *testing.T) {
// Isolate a shallow copy of the rsaTestKey to avoid polluting it with Precompute
localKey := &(*rsaTestKey)
localKey.Precompute()
jwk, err := fromRsaPrivateKey(localKey)
if err != nil {
t.Error("problem constructing JWK from rsa key", err)
}
rsa2, err := jwk.rsaPrivateKey()
if err != nil {
t.Error("problem converting RSA private -> JWK", err)
}
if rsa2.Precomputed.Dp == nil {
t.Error("RSA private Dp nil")
}
if rsa2.Precomputed.Dq == nil {
t.Error("RSA private Dq nil")
}
if rsa2.Precomputed.Qinv == nil {
t.Error("RSA private Qinv nil")
}
if rsa2.Precomputed.Dp.Cmp(localKey.Precomputed.Dp) != 0 {
t.Error("RSA private Dp mismatch")
}
if rsa2.Precomputed.Dq.Cmp(localKey.Precomputed.Dq) != 0 {
t.Error("RSA private Dq mismatch")
}
if rsa2.Precomputed.Qinv.Cmp(localKey.Precomputed.Qinv) != 0 {
t.Error("RSA private Qinv mismatch")
}
}
func TestRsaPrivateInsufficientPrimes(t *testing.T) {
brokenRsaPrivateKey := rsa.PrivateKey{
PublicKey: rsa.PublicKey{
N: rsaTestKey.N,
E: rsaTestKey.E,
},
D: rsaTestKey.D,
Primes: []*big.Int{rsaTestKey.Primes[0]},
}
_, err := fromRsaPrivateKey(&brokenRsaPrivateKey)
if err != ErrUnsupportedKeyType {
t.Error("expected unsupported key type error, got", err)
}
}
func TestRsaPrivateExcessPrimes(t *testing.T) {
brokenRsaPrivateKey := rsa.PrivateKey{
PublicKey: rsa.PublicKey{
N: rsaTestKey.N,
E: rsaTestKey.E,
},
D: rsaTestKey.D,
Primes: []*big.Int{
rsaTestKey.Primes[0],
rsaTestKey.Primes[1],
big.NewInt(3),
},
}
_, err := fromRsaPrivateKey(&brokenRsaPrivateKey)
if err != ErrUnsupportedKeyType {
t.Error("expected unsupported key type error, got", err)
}
}
func TestRoundtripEcPublic(t *testing.T) {
for i, ecTestKey := range []*ecdsa.PrivateKey{ecTestKey256, ecTestKey384, ecTestKey521} {
jwk, err := fromEcPublicKey(&ecTestKey.PublicKey)
ec2, err := jwk.ecPublicKey()
if err != nil {
t.Error("problem converting ECDSA private -> JWK", i, err)
}
if !reflect.DeepEqual(ec2.Curve, ecTestKey.Curve) {
t.Error("ECDSA private curve mismatch", i)
}
if ec2.X.Cmp(ecTestKey.X) != 0 {
t.Error("ECDSA X mismatch", i)
}
if ec2.Y.Cmp(ecTestKey.Y) != 0 {
t.Error("ECDSA Y mismatch", i)
}
}
}
func TestRoundtripEcPrivate(t *testing.T) {
for i, ecTestKey := range []*ecdsa.PrivateKey{ecTestKey256, ecTestKey384, ecTestKey521} {
jwk, err := fromEcPrivateKey(ecTestKey)
ec2, err := jwk.ecPrivateKey()
if err != nil {
t.Fatalf("problem converting ECDSA private -> JWK for %#v: %s", ecTestKey, err)
}
if !reflect.DeepEqual(ec2.Curve, ecTestKey.Curve) {
t.Error("ECDSA private curve mismatch", i)
}
if ec2.X.Cmp(ecTestKey.X) != 0 {
t.Error("ECDSA X mismatch", i)
}
if ec2.Y.Cmp(ecTestKey.Y) != 0 {
t.Error("ECDSA Y mismatch", i)
}
if ec2.D.Cmp(ecTestKey.D) != 0 {
t.Error("ECDSA D mismatch", i)
}
}
}
func TestRoundtripX5C(t *testing.T) {
jwk := JSONWebKey{
Key: rsaTestKey,
KeyID: "bar",
Algorithm: "foo",
Certificates: testCertificates,
}
jsonbar, err := jwk.MarshalJSON()
if err != nil {
t.Error("problem marshaling", err)
}
var jwk2 JSONWebKey
err = jwk2.UnmarshalJSON(jsonbar)
if err != nil {
t.Fatal("problem unmarshalling", err)
}
if !reflect.DeepEqual(testCertificates, jwk2.Certificates) {
t.Error("Certificates not equal", jwk.Certificates, jwk2.Certificates)
}
jsonbar2, err := jwk2.MarshalJSON()
if err != nil {
t.Error("problem marshaling", err)
}
if !bytes.Equal(jsonbar, jsonbar2) {
t.Error("roundtrip should not lose information")
}
}
func TestMarshalUnmarshal(t *testing.T) {
kid := "DEADBEEF"
for i, key := range []interface{}{ecTestKey256, ecTestKey384, ecTestKey521, rsaTestKey, ed25519PrivateKey} {
for _, use := range []string{"", "sig", "enc"} {
jwk := JSONWebKey{Key: key, KeyID: kid, Algorithm: "foo"}
if use != "" {
jwk.Use = use
}
jsonbar, err := jwk.MarshalJSON()
if err != nil {
t.Error("problem marshaling", i, err)
}
var jwk2 JSONWebKey
err = jwk2.UnmarshalJSON(jsonbar)
if err != nil {
t.Fatal("problem unmarshalling", i, err)
}
jsonbar2, err := jwk2.MarshalJSON()
if err != nil {
t.Fatal("problem marshaling", i, err)
}
if !bytes.Equal(jsonbar, jsonbar2) {
t.Error("roundtrip should not lose information", i)
}
if jwk2.KeyID != kid {
t.Error("kid did not roundtrip JSON marshalling", i)
}
if jwk2.Algorithm != "foo" {
t.Error("alg did not roundtrip JSON marshalling", i)
}
if jwk2.Use != use {
t.Error("use did not roundtrip JSON marshalling", i)
}
}
}
}
func TestMarshalNonPointer(t *testing.T) {
type EmbedsKey struct {
Key JSONWebKey
}
keyJSON := []byte(`{
"e": "AQAB",
"kty": "RSA",
"n": "vd7rZIoTLEe-z1_8G1FcXSw9CQFEJgV4g9V277sER7yx5Qjz_Pkf2YVth6wwwFJEmzc0hoKY-MMYFNwBE4hQHw"
}`)
var parsedKey JSONWebKey
err := json.Unmarshal(keyJSON, &parsedKey)
if err != nil {
t.Errorf("Error unmarshalling key: %v", err)
return
}
ek := EmbedsKey{
Key: parsedKey,
}
out, err := json.Marshal(ek)
if err != nil {
t.Errorf("Error marshalling JSON: %v", err)
return
}
expected := "{\"Key\":{\"kty\":\"RSA\",\"n\":\"vd7rZIoTLEe-z1_8G1FcXSw9CQFEJgV4g9V277sER7yx5Qjz_Pkf2YVth6wwwFJEmzc0hoKY-MMYFNwBE4hQHw\",\"e\":\"AQAB\"}}"
if string(out) != expected {
t.Error("Failed to marshal embedded non-pointer JWK properly:", string(out))
}
}
func TestMarshalUnmarshalInvalid(t *testing.T) {
// Make an invalid curve coordinate by creating a byte array that is one
// byte too large, and setting the first byte to 1 (otherwise it's just zero).
invalidCoord := make([]byte, curveSize(ecTestKey256.Curve)+1)
invalidCoord[0] = 1
keys := []interface{}{
// Empty keys
&rsa.PrivateKey{},
&ecdsa.PrivateKey{},
// Invalid keys
&ecdsa.PrivateKey{
PublicKey: ecdsa.PublicKey{
// Missing values in pub key
Curve: elliptic.P256(),
},
},
&ecdsa.PrivateKey{
PublicKey: ecdsa.PublicKey{
// Invalid curve
Curve: nil,
X: ecTestKey256.X,
Y: ecTestKey256.Y,
},
},
&ecdsa.PrivateKey{
// Valid pub key, but missing priv key values
PublicKey: ecTestKey256.PublicKey,
},
&ecdsa.PrivateKey{
// Invalid pub key, values too large
PublicKey: ecdsa.PublicKey{
Curve: ecTestKey256.Curve,
X: big.NewInt(0).SetBytes(invalidCoord),
Y: big.NewInt(0).SetBytes(invalidCoord),
},
D: ecTestKey256.D,
},
nil,
}
for i, key := range keys {
jwk := JSONWebKey{Key: key}
_, err := jwk.MarshalJSON()
if err == nil {
t.Error("managed to serialize invalid key", i)
}
}
}
func TestWebKeyVectorsInvalid(t *testing.T) {
keys := []string{
// Invalid JSON
"{X",
// Empty key
"{}",
// Invalid RSA keys
`{"kty":"RSA"}`,
`{"kty":"RSA","e":""}`,
`{"kty":"RSA","e":"XXXX"}`,
`{"kty":"RSA","d":"XXXX"}`,
// Invalid EC keys
`{"kty":"EC","crv":"ABC"}`,
`{"kty":"EC","crv":"P-256"}`,
`{"kty":"EC","crv":"P-256","d":"XXX"}`,
`{"kty":"EC","crv":"ABC","d":"dGVzdA","x":"dGVzdA"}`,
`{"kty":"EC","crv":"P-256","d":"dGVzdA","x":"dGVzdA"}`,
}
for _, key := range keys {
var jwk2 JSONWebKey
err := jwk2.UnmarshalJSON([]byte(key))
if err == nil {
t.Error("managed to parse invalid key:", key)
}
}
}
// Test vectors from RFC 7520
var cookbookJWKs = []string{
// EC Public
stripWhitespace(`{
"kty": "EC",
"kid": "bilbo.baggins@hobbiton.example",
"use": "sig",
"crv": "P-521",
"x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9
A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt",
"y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVy
SsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1"
}`),
//ED Private
stripWhitespace(`{
"kty": "OKP",
"crv": "Ed25519",
"d": "nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A",
"x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"
}`),
// EC Private
stripWhitespace(`{
"kty": "EC",
"kid": "bilbo.baggins@hobbiton.example",
"use": "sig",
"crv": "P-521",
"x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9
A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt",
"y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVy
SsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1",
"d": "AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zb
KipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt"
}`),
// RSA Public
stripWhitespace(`{
"kty": "RSA",
"kid": "bilbo.baggins@hobbiton.example",
"use": "sig",
"n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT
-O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV
wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-
oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde
3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC
LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g
HdrNP5zw",
"e": "AQAB"
}`),
// RSA Private
stripWhitespace(`{"kty":"RSA",
"kid":"juliet@capulet.lit",
"use":"enc",
"n":"t6Q8PWSi1dkJj9hTP8hNYFlvadM7DflW9mWepOJhJ66w7nyoK1gPNqFMSQRy
O125Gp-TEkodhWr0iujjHVx7BcV0llS4w5ACGgPrcAd6ZcSR0-Iqom-QFcNP
8Sjg086MwoqQU_LYywlAGZ21WSdS_PERyGFiNnj3QQlO8Yns5jCtLCRwLHL0
Pb1fEv45AuRIuUfVcPySBWYnDyGxvjYGDSM-AqWS9zIQ2ZilgT-GqUmipg0X
OC0Cc20rgLe2ymLHjpHciCKVAbY5-L32-lSeZO-Os6U15_aXrk9Gw8cPUaX1
_I8sLGuSiVdt3C_Fn2PZ3Z8i744FPFGGcG1qs2Wz-Q",
"e":"AQAB",
"d":"GRtbIQmhOZtyszfgKdg4u_N-R_mZGU_9k7JQ_jn1DnfTuMdSNprTeaSTyWfS
NkuaAwnOEbIQVy1IQbWVV25NY3ybc_IhUJtfri7bAXYEReWaCl3hdlPKXy9U
vqPYGR0kIXTQRqns-dVJ7jahlI7LyckrpTmrM8dWBo4_PMaenNnPiQgO0xnu
ToxutRZJfJvG4Ox4ka3GORQd9CsCZ2vsUDmsXOfUENOyMqADC6p1M3h33tsu
rY15k9qMSpG9OX_IJAXmxzAh_tWiZOwk2K4yxH9tS3Lq1yX8C1EWmeRDkK2a
hecG85-oLKQt5VEpWHKmjOi_gJSdSgqcN96X52esAQ",
"p":"2rnSOV4hKSN8sS4CgcQHFbs08XboFDqKum3sc4h3GRxrTmQdl1ZK9uw-PIHf
QP0FkxXVrx-WE-ZEbrqivH_2iCLUS7wAl6XvARt1KkIaUxPPSYB9yk31s0Q8
UK96E3_OrADAYtAJs-M3JxCLfNgqh56HDnETTQhH3rCT5T3yJws",
"q":"1u_RiFDP7LBYh3N4GXLT9OpSKYP0uQZyiaZwBtOCBNJgQxaj10RWjsZu0c6I
edis4S7B_coSKB0Kj9PaPaBzg-IySRvvcQuPamQu66riMhjVtG6TlV8CLCYK
rYl52ziqK0E_ym2QnkwsUX7eYTB7LbAHRK9GqocDE5B0f808I4s",
"dp":"KkMTWqBUefVwZ2_Dbj1pPQqyHSHjj90L5x_MOzqYAJMcLMZtbUtwKqvVDq3
tbEo3ZIcohbDtt6SbfmWzggabpQxNxuBpoOOf_a_HgMXK_lhqigI4y_kqS1w
Y52IwjUn5rgRrJ-yYo1h41KR-vz2pYhEAeYrhttWtxVqLCRViD6c",
"dq":"AvfS0-gRxvn0bwJoMSnFxYcK1WnuEjQFluMGfwGitQBWtfZ1Er7t1xDkbN9
GQTB9yqpDoYaN06H7CFtrkxhJIBQaj6nkF5KKS3TQtQ5qCzkOkmxIe3KRbBy
mXxkb5qwUpX5ELD5xFc6FeiafWYY63TmmEAu_lRFCOJ3xDea-ots",
"qi":"lSQi-w9CpyUReMErP1RsBLk7wNtOvs5EQpPqmuMvqW57NBUczScEoPwmUqq
abu9V0-Py4dQ57_bapoKRu1R90bvuFnU63SHWEFglZQvJDMeAvmj4sm-Fp0o
Yu_neotgQ0hzbI5gry7ajdYy9-2lNx_76aBZoOUu9HCJ-UsfSOI8"}`),
// X.509 Certificate Chain
stripWhitespace(`{"kty":"RSA",
"use":"sig",
"kid":"1b94c",
"n":"vrjOfz9Ccdgx5nQudyhdoR17V-IubWMeOZCwX_jj0hgAsz2J_pqYW08
PLbK_PdiVGKPrqzmDIsLI7sA25VEnHU1uCLNwBuUiCO11_-7dYbsr4iJmG0Q
u2j8DsVyT1azpJC_NG84Ty5KKthuCaPod7iI7w0LK9orSMhBEwwZDCxTWq4a
YWAchc8t-emd9qOvWtVMDC2BXksRngh6X5bUYLy6AyHKvj-nUy1wgzjYQDwH
MTplCoLtU-o-8SNnZ1tmRoGE9uJkBLdh5gFENabWnU5m1ZqZPdwS-qo-meMv
VfJb6jJVWRpl2SUtCnYG2C32qvbWbjZ_jBPD5eunqsIo1vQ",
"e":"AQAB",
"x5c":
["MIIDQjCCAiqgAwIBAgIGATz/FuLiMA0GCSqGSIb3DQEBBQUAMGIxCzAJB
gNVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRwwGgYD
VQQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5CcmlhbiBDYW1
wYmVsbDAeFw0xMzAyMjEyMzI5MTVaFw0xODA4MTQyMjI5MTVaMGIxCzAJBg
NVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRwwGgYDV
QQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5CcmlhbiBDYW1w
YmVsbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL64zn8/QnH
YMeZ0LncoXaEde1fiLm1jHjmQsF/449IYALM9if6amFtPDy2yvz3YlRij66
s5gyLCyO7ANuVRJx1NbgizcAblIgjtdf/u3WG7K+IiZhtELto/A7Fck9Ws6
SQvzRvOE8uSirYbgmj6He4iO8NCyvaK0jIQRMMGQwsU1quGmFgHIXPLfnpn
fajr1rVTAwtgV5LEZ4Iel+W1GC8ugMhyr4/p1MtcIM42EA8BzE6ZQqC7VPq
PvEjZ2dbZkaBhPbiZAS3YeYBRDWm1p1OZtWamT3cEvqqPpnjL1XyW+oyVVk
aZdklLQp2Btgt9qr21m42f4wTw+Xrp6rCKNb0CAwEAATANBgkqhkiG9w0BA
QUFAAOCAQEAh8zGlfSlcI0o3rYDPBB07aXNswb4ECNIKG0CETTUxmXl9KUL
+9gGlqCz5iWLOgWsnrcKcY0vXPG9J1r9AqBNTqNgHq2G03X09266X5CpOe1
zFo+Owb1zxtp3PehFdfQJ610CDLEaS9V9Rqp17hCyybEpOGVwe8fnk+fbEL
2Bo3UPGrpsHzUoaGpDftmWssZkhpBJKVMJyf/RuP2SmmaIzmnw9JiSlYhzo
4tpzd5rFXhjRbg4zW9C+2qok+2+qDM1iJ684gPHMIY8aLWrdgQTxkumGmTq
gawR+N5MDtdPTEQ0XfIBc2cJEUyMTY5MPvACWpkA6SdS4xSvdXK3IVfOWA=="]}`),
}
// SHA-256 thumbprints of the above keys, hex-encoded
var cookbookJWKThumbprints = []string{
"747ae2dd2003664aeeb21e4753fe7402846170a16bc8df8f23a8cf06d3cbe793",
"f6934029a341ddf81dceb753e91d17efe16664f40d9f4ed84bc5ea87e111f29d",
"747ae2dd2003664aeeb21e4753fe7402846170a16bc8df8f23a8cf06d3cbe793",
"f63838e96077ad1fc01c3f8405774dedc0641f558ebb4b40dccf5f9b6d66a932",
"0fc478f8579325fcee0d4cbc6d9d1ce21730a6e97e435d6008fb379b0ebe47d4",
"0ddb05bfedbec2070fa037324ba397396561d3425d6d69245570c261dc49dee3",
}
func TestWebKeyVectorsValid(t *testing.T) {
for _, key := range cookbookJWKs {
var jwk2 JSONWebKey
err := jwk2.UnmarshalJSON([]byte(key))
if err != nil {
t.Error("unable to parse valid key:", key, err)
}
}
}
func TestThumbprint(t *testing.T) {
for i, key := range cookbookJWKs {
var jwk2 JSONWebKey
err := jwk2.UnmarshalJSON([]byte(key))
if err != nil {
t.Error("unable to parse valid key:", key, err)
}
tp, err := jwk2.Thumbprint(crypto.SHA256)
if err != nil {
t.Error("unable to compute thumbprint:", key, err)
}
tpHex := hex.EncodeToString(tp)
if cookbookJWKThumbprints[i] != tpHex {
t.Error("incorrect thumbprint:", i, cookbookJWKThumbprints[i], tpHex)
}
}
}
func TestMarshalUnmarshalJWKSet(t *testing.T) {
jwk1 := JSONWebKey{Key: rsaTestKey, KeyID: "ABCDEFG", Algorithm: "foo"}
jwk2 := JSONWebKey{Key: rsaTestKey, KeyID: "GFEDCBA", Algorithm: "foo"}
var set JSONWebKeySet
set.Keys = append(set.Keys, jwk1)
set.Keys = append(set.Keys, jwk2)
jsonbar, err := json.Marshal(&set)
if err != nil {
t.Error("problem marshalling set", err)
}
var set2 JSONWebKeySet
err = json.Unmarshal(jsonbar, &set2)
if err != nil {
t.Fatal("problem unmarshalling set", err)
}
jsonbar2, err := json.Marshal(&set2)
if err != nil {
t.Fatal("problem marshalling set", err)
}
if !bytes.Equal(jsonbar, jsonbar2) {
t.Error("roundtrip should not lose information")
}
}
func TestJWKSetKey(t *testing.T) {
jwk1 := JSONWebKey{Key: rsaTestKey, KeyID: "ABCDEFG", Algorithm: "foo"}
jwk2 := JSONWebKey{Key: rsaTestKey, KeyID: "GFEDCBA", Algorithm: "foo"}
var set JSONWebKeySet
set.Keys = append(set.Keys, jwk1)
set.Keys = append(set.Keys, jwk2)
k := set.Key("ABCDEFG")
if len(k) != 1 {
t.Errorf("method should return slice with one key not %d", len(k))
}
if k[0].KeyID != "ABCDEFG" {
t.Error("method should return key with ID ABCDEFG")
}
}
func TestJWKSymmetricKey(t *testing.T) {
sample1 := `{"kty":"oct","alg":"A128KW","k":"GawgguFyGrWKav7AX4VKUg"}`
sample2 := `{"kty":"oct","k":"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow","kid":"HMAC key used in JWS spec Appendix A.1 example"}`
var jwk1 JSONWebKey
json.Unmarshal([]byte(sample1), &jwk1)
if jwk1.Algorithm != "A128KW" {
t.Errorf("expected Algorithm to be A128KW, but was '%s'", jwk1.Algorithm)
}
expected1 := fromHexBytes("19ac2082e1721ab58a6afec05f854a52")
if !bytes.Equal(jwk1.Key.([]byte), expected1) {
t.Errorf("expected Key to be '%s', but was '%s'", hex.EncodeToString(expected1), hex.EncodeToString(jwk1.Key.([]byte)))
}
var jwk2 JSONWebKey
json.Unmarshal([]byte(sample2), &jwk2)
if jwk2.KeyID != "HMAC key used in JWS spec Appendix A.1 example" {
t.Errorf("expected KeyID to be 'HMAC key used in JWS spec Appendix A.1 example', but was '%s'", jwk2.KeyID)
}
expected2 := fromHexBytes(`
0323354b2b0fa5bc837e0665777ba68f5ab328e6f054c928a90f84b2d2502ebf
d3fb5a92d20647ef968ab4c377623d223d2e2172052e4f08c0cd9af567d080a3`)
if !bytes.Equal(jwk2.Key.([]byte), expected2) {
t.Errorf("expected Key to be '%s', but was '%s'", hex.EncodeToString(expected2), hex.EncodeToString(jwk2.Key.([]byte)))
}
}
func TestJWKSymmetricRoundtrip(t *testing.T) {
jwk1 := JSONWebKey{Key: []byte{1, 2, 3, 4}}
marshaled, err := jwk1.MarshalJSON()
if err != nil {
t.Error("failed to marshal valid JWK object", err)
}
var jwk2 JSONWebKey
err = jwk2.UnmarshalJSON(marshaled)
if err != nil {
t.Error("failed to unmarshal valid JWK object", err)
}
if !bytes.Equal(jwk1.Key.([]byte), jwk2.Key.([]byte)) {
t.Error("round-trip of symmetric JWK gave different raw keys")
}
}
func TestJWKSymmetricInvalid(t *testing.T) {
invalid := JSONWebKey{}
_, err := invalid.MarshalJSON()
if err == nil {
t.Error("excepted error on marshaling invalid symmetric JWK object")
}
var jwk JSONWebKey
err = jwk.UnmarshalJSON([]byte(`{"kty":"oct"}`))
if err == nil {
t.Error("excepted error on unmarshaling invalid symmetric JWK object")
}
}
func TestJWKIsPublic(t *testing.T) {
bigInt := big.NewInt(0)
eccPub := ecdsa.PublicKey{elliptic.P256(), bigInt, bigInt}
rsaPub := rsa.PublicKey{bigInt, 1}
cases := []struct {
key interface{}
expectedIsPublic bool
}{
{&eccPub, true},
{&ecdsa.PrivateKey{eccPub, bigInt}, false},
{&rsaPub, true},
{&rsa.PrivateKey{rsaPub, bigInt, []*big.Int{bigInt, bigInt}, rsa.PrecomputedValues{}}, false},
{ed25519PublicKey, true},
{ed25519PrivateKey, false},
}
for _, tc := range cases {
k := &JSONWebKey{Key: tc.key}
if public := k.IsPublic(); public != tc.expectedIsPublic {
t.Errorf("expected IsPublic to return %t, got %t", tc.expectedIsPublic, public)
}
}
}
func TestJWKValid(t *testing.T) {
bigInt := big.NewInt(0)
eccPub := ecdsa.PublicKey{elliptic.P256(), bigInt, bigInt}
rsaPub := rsa.PublicKey{bigInt, 1}
edPubEmpty := ed25519.PublicKey([]byte{})
edPrivEmpty := ed25519.PublicKey([]byte{})
cases := []struct {
key interface{}
expectedValidity bool
}{
{nil, false},
{&ecdsa.PublicKey{}, false},
{&eccPub, true},
{&ecdsa.PrivateKey{}, false},
{&ecdsa.PrivateKey{eccPub, bigInt}, true},
{&rsa.PublicKey{}, false},
{&rsaPub, true},
{&rsa.PrivateKey{}, false},
{&rsa.PrivateKey{rsaPub, bigInt, []*big.Int{bigInt, bigInt}, rsa.PrecomputedValues{}}, true},
{ed25519PublicKey, true},
{ed25519PrivateKey, true},
{edPubEmpty, false},
{edPrivEmpty, false},
}
for _, tc := range cases {
k := &JSONWebKey{Key: tc.key}
valid := k.Valid()
if valid != tc.expectedValidity {
t.Errorf("expected Valid to return %t, got %t", tc.expectedValidity, valid)
}
if valid {
wasPublic := k.IsPublic()
p := k.Public() // all aforemention keys are asymmetric
if !p.Valid() {
t.Errorf("unable to derive public key from valid asymmetric key")
}
if wasPublic != k.IsPublic() {
t.Errorf("original key was touched during public key derivation")
}
}
}
}
func TestJWKBufferSizeCheck(t *testing.T) {
key := `{
"kty":"EC",
"crv":"P-256",
"x":"m9GSmJ5iGmAYlMlaOJGSFN_CjN9cIn8GGYExP-C0FBiIXlWTNvGN38R9WdrHcppfsKF0FXMOMyutpHIRaiMxYSA",
"y":"ZaPcRZ3q_7T3h-Gwz2i-T2JjJXfj6YVGgKHcFz5zqmg"}`
var jwk JSONWebKey
jwk.UnmarshalJSON([]byte(key))
jwk.Valid() // true
// panic: square/go-jose: invalid call to newFixedSizeBuffer (len(data) > length)
// github.com/square/go-jose.newFixedSizeBuffer(0xc420014557, 0x41, 0x41, 0x20, 0x0)
jwk.Thumbprint(crypto.SHA256)
}
func TestJWKPaddingPrivateX(t *testing.T) {
key := `{
"kty": "EC",
"crv": "P-256",
"x": "nPTIABcDASY6FNGSNfHCB51tY7qChtgzeVazOtLrwQ",
"y": "vEEs4V0egJkNyM2Q4pp001zu14VcpQ0_Ei8xOOPxKZs",
"d": "nIVCvMR2wkLmeGJErOpI23VDHl2s3JwGdbzKy0odir0"
}`
var jwk JSONWebKey
err := jwk.UnmarshalJSON([]byte(key))
if err == nil {
t.Errorf("Expected key with short x to fail unmarshalling")
}
if !strings.Contains(err.Error(), "wrong length for x") {
t.Errorf("Wrong error for short x, got %q", err)
}
if jwk.Valid() {
t.Errorf("Expected key to be invalid, but it was valid.")
}
}
func TestJWKPaddingPrivateY(t *testing.T) {
key := `{
"kty": "EC",
"crv": "P-256",
"x": "vEEs4V0egJkNyM2Q4pp001zu14VcpQ0_Ei8xOOPxKZs",
"y": "nPTIABcDASY6FNGSNfHCB51tY7qChtgzeVazOtLrwQ",
"d": "nIVCvMR2wkLmeGJErOpI23VDHl2s3JwGdbzKy0odir0"
}`
var jwk JSONWebKey
err := jwk.UnmarshalJSON([]byte(key))
if err == nil {
t.Errorf("Expected key with short x to fail unmarshalling")
}
if !strings.Contains(err.Error(), "wrong length for y") {
t.Errorf("Wrong error for short y, got %q", err)
}
if jwk.Valid() {
t.Errorf("Expected key to be invalid, but it was valid.")
}
}
func TestJWKPaddingPrivateD(t *testing.T) {
key := `{
"kty": "EC",
"crv": "P-256",
"x": "vEEs4V0egJkNyM2Q4pp001zu14VcpQ0_Ei8xOOPxKZs",
"y": "qnPTIABcDASY6FNGSNfHCB51tY7qChtgzeVazOtLrwQ",
"d": "IVCvMR2wkLmeGJErOpI23VDHl2s3JwGdbzKy0odir0"
}`
var jwk JSONWebKey
err := jwk.UnmarshalJSON([]byte(key))
if err == nil {
t.Errorf("Expected key with short x to fail unmarshalling")
}
if !strings.Contains(err.Error(), "wrong length for d") {
t.Errorf("Wrong error for short d, got %q", err)
}
if jwk.Valid() {
t.Errorf("Expected key to be invalid, but it was valid.")
}
}
func TestJWKPaddingX(t *testing.T) {
key := `{
"kty": "EC",
"crv": "P-256",
"x": "nPTIABcDASY6FNGSNfHCB51tY7qChtgzeVazOtLrwQ",
"y": "vEEs4V0egJkNyM2Q4pp001zu14VcpQ0_Ei8xOOPxKZs"
}`
var jwk JSONWebKey
err := jwk.UnmarshalJSON([]byte(key))
if err == nil {
t.Errorf("Expected key with short x to fail unmarshalling")
}
if !strings.Contains(err.Error(), "wrong length for x") {
t.Errorf("Wrong error for short x, got %q", err)
}
if jwk.Valid() {
t.Errorf("Expected key to be invalid, but it was valid.")
}
}
func TestJWKPaddingY(t *testing.T) {
key := `{
"kty": "EC",
"crv": "P-256",
"x": "vEEs4V0egJkNyM2Q4pp001zu14VcpQ0_Ei8xOOPxKZs",
"y": "nPTIABcDASY6FNGSNfHCB51tY7qChtgzeVazOtLrwQ"
}`
var jwk JSONWebKey
err := jwk.UnmarshalJSON([]byte(key))
if err == nil {
t.Errorf("Expected key with short y to fail unmarshalling")
}
if !strings.Contains(err.Error(), "wrong length for y") {
t.Errorf("Wrong error for short y, got %q", err)
}
if jwk.Valid() {
t.Errorf("Expected key to be invalid, but it was valid.")
}
}