| Cedric Staub | 218edee | 2014-12-23 22:00:33 | [diff] [blame] | 1 | /*- |
| 2 | * Copyright 2014 Square Inc. |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package jose |
| 18 | |
| 19 | import ( |
| Cedric Staub | 9d1ab6c | 2019-05-28 21:30:13 | [diff] [blame] | 20 | "bytes" |
| Richard Barnes | f2a0135 | 2015-06-29 17:11:42 | [diff] [blame] | 21 | "crypto" |
| Cedric Staub | 218edee | 2014-12-23 22:00:33 | [diff] [blame] | 22 | "crypto/ecdsa" |
| 23 | "crypto/elliptic" |
| Cedric Staub | 06a2537 | 2015-03-04 06:39:47 | [diff] [blame] | 24 | "crypto/rsa" |
| Cedric Staub | 9d1ab6c | 2019-05-28 21:30:13 | [diff] [blame] | 25 | "crypto/sha1" |
| 26 | "crypto/sha256" |
| Cedric Staub | 7cd6062 | 2016-06-11 20:06:20 | [diff] [blame] | 27 | "crypto/x509" |
| 28 | "encoding/base64" |
| Filipe Azevedo | 1323f1f | 2020-04-30 18:02:04 | [diff] [blame] | 29 | "encoding/hex" |
| Cedric Staub | 7f0dd80 | 2016-08-31 18:22:30 | [diff] [blame] | 30 | "errors" |
| Cedric Staub | 218edee | 2014-12-23 22:00:33 | [diff] [blame] | 31 | "fmt" |
| Robert Coie | 4a4b7a1 | 2015-03-18 00:07:32 | [diff] [blame] | 32 | "math/big" |
| Cedric Staub | 9d1ab6c | 2019-05-28 21:30:13 | [diff] [blame] | 33 | "net/url" |
| Cedric Staub | 06a2537 | 2015-03-04 06:39:47 | [diff] [blame] | 34 | "reflect" |
| Cedric Staub | 98e5a54 | 2015-10-15 21:19:39 | [diff] [blame] | 35 | "strings" |
| Cedric Staub | 1f36a88 | 2016-06-01 23:11:57 | [diff] [blame] | 36 | |
| Rahul Patel | 13f4936 | 2017-07-20 19:51:51 | [diff] [blame] | 37 | "golang.org/x/crypto/ed25519" |
| 38 | |
| Cedric Staub | 3a50ebc | 2016-08-31 23:02:52 | [diff] [blame] | 39 | "gopkg.in/square/go-jose.v2/json" |
| Cedric Staub | 218edee | 2014-12-23 22:00:33 | [diff] [blame] | 40 | ) |
| 41 | |
| Cedric Staub | d94ed21 | 2016-02-15 01:18:38 | [diff] [blame] | 42 | // rawJSONWebKey represents a public or private key in JWK format, used for parsing/serializing. |
| 43 | type rawJSONWebKey struct { |
| Daisuke Maki | 0384452 | 2015-11-02 03:17:31 | [diff] [blame] | 44 | Use string `json:"use,omitempty"` |
| Cedric Staub | ec9beea | 2014-12-23 23:01:15 | [diff] [blame] | 45 | Kty string `json:"kty,omitempty"` |
| Robert Coie | 4a4b7a1 | 2015-03-18 00:07:32 | [diff] [blame] | 46 | Kid string `json:"kid,omitempty"` |
| Cedric Staub | ec9beea | 2014-12-23 23:01:15 | [diff] [blame] | 47 | Crv string `json:"crv,omitempty"` |
| Russell Haering | fa57672 | 2015-04-05 19:56:21 | [diff] [blame] | 48 | Alg string `json:"alg,omitempty"` |
| Cedric Staub | d509a11 | 2015-11-17 00:03:39 | [diff] [blame] | 49 | K *byteBuffer `json:"k,omitempty"` |
| Cedric Staub | ec9beea | 2014-12-23 23:01:15 | [diff] [blame] | 50 | X *byteBuffer `json:"x,omitempty"` |
| 51 | Y *byteBuffer `json:"y,omitempty"` |
| Cedric Staub | 06a2537 | 2015-03-04 06:39:47 | [diff] [blame] | 52 | N *byteBuffer `json:"n,omitempty"` |
| 53 | E *byteBuffer `json:"e,omitempty"` |
| Cedric Staub | 97198ac | 2015-03-19 01:55:26 | [diff] [blame] | 54 | // -- Following fields are only used for private keys -- |
| 55 | // RSA uses D, P and Q, while ECDSA uses only D. Fields Dp, Dq, and Qi are |
| 56 | // completely optional. Therefore for RSA/ECDSA, D != nil is a contract that |
| 57 | // we have a private key whereas D == nil means we have only a public key. |
| Robert Coie | c20b416 | 2015-03-18 05:41:54 | [diff] [blame] | 58 | D *byteBuffer `json:"d,omitempty"` |
| 59 | P *byteBuffer `json:"p,omitempty"` |
| Cedric Staub | 97198ac | 2015-03-19 01:55:26 | [diff] [blame] | 60 | Q *byteBuffer `json:"q,omitempty"` |
| Robert Coie | c20b416 | 2015-03-18 05:41:54 | [diff] [blame] | 61 | Dp *byteBuffer `json:"dp,omitempty"` |
| 62 | Dq *byteBuffer `json:"dq,omitempty"` |
| 63 | Qi *byteBuffer `json:"qi,omitempty"` |
| Cedric Staub | 7cd6062 | 2016-06-11 20:06:20 | [diff] [blame] | 64 | // Certificates |
| Filipe Azevedo | 1323f1f | 2020-04-30 18:02:04 | [diff] [blame] | 65 | X5c []string `json:"x5c,omitempty"` |
| 66 | X5u *url.URL `json:"x5u,omitempty"` |
| 67 | X5tSHA1 string `json:"x5t,omitempty"` |
| 68 | X5tSHA256 string `json:"x5t#S256,omitempty"` |
| Cedric Staub | 218edee | 2014-12-23 22:00:33 | [diff] [blame] | 69 | } |
| 70 | |
| Cedric Staub | d94ed21 | 2016-02-15 01:18:38 | [diff] [blame] | 71 | // JSONWebKey represents a public or private key in JWK format. |
| 72 | type JSONWebKey struct { |
| Cedric Staub | 9d1ab6c | 2019-05-28 21:30:13 | [diff] [blame] | 73 | // Cryptographic key, can be a symmetric or asymmetric key. |
| 74 | Key interface{} |
| 75 | // Key identifier, parsed from `kid` header. |
| 76 | KeyID string |
| 77 | // Key algorithm, parsed from `alg` header. |
| 78 | Algorithm string |
| 79 | // Key use, parsed from `use` header. |
| 80 | Use string |
| 81 | |
| 82 | // X.509 certificate chain, parsed from `x5c` header. |
| Cedric Staub | 7cd6062 | 2016-06-11 20:06:20 | [diff] [blame] | 83 | Certificates []*x509.Certificate |
| Cedric Staub | 9d1ab6c | 2019-05-28 21:30:13 | [diff] [blame] | 84 | // X.509 certificate URL, parsed from `x5u` header. |
| 85 | CertificatesURL *url.URL |
| 86 | // X.509 certificate thumbprint (SHA-1), parsed from `x5t` header. |
| 87 | CertificateThumbprintSHA1 []byte |
| 88 | // X.509 certificate thumbprint (SHA-256), parsed from `x5t#S256` header. |
| 89 | CertificateThumbprintSHA256 []byte |
| Cedric Staub | 06a2537 | 2015-03-04 06:39:47 | [diff] [blame] | 90 | } |
| 91 | |
| Cedric Staub | 52b8488 | 2015-03-19 02:07:37 | [diff] [blame] | 92 | // MarshalJSON serializes the given key to its JSON representation. |
| Cedric Staub | d94ed21 | 2016-02-15 01:18:38 | [diff] [blame] | 93 | func (k JSONWebKey) MarshalJSON() ([]byte, error) { |
| 94 | var raw *rawJSONWebKey |
| Cedric Staub | ff36cce | 2015-03-26 19:08:15 | [diff] [blame] | 95 | var err error |
| 96 | |
| Cedric Staub | 414a7e5 | 2015-03-19 02:01:55 | [diff] [blame] | 97 | switch key := k.Key.(type) { |
| Rahul Patel | 13f4936 | 2017-07-20 19:51:51 | [diff] [blame] | 98 | case ed25519.PublicKey: |
| 99 | raw = fromEdPublicKey(key) |
| Cedric Staub | 06a2537 | 2015-03-04 06:39:47 | [diff] [blame] | 100 | case *ecdsa.PublicKey: |
| Cedric Staub | ff36cce | 2015-03-26 19:08:15 | [diff] [blame] | 101 | raw, err = fromEcPublicKey(key) |
| Cedric Staub | 06a2537 | 2015-03-04 06:39:47 | [diff] [blame] | 102 | case *rsa.PublicKey: |
| Cedric Staub | ff36cce | 2015-03-26 19:08:15 | [diff] [blame] | 103 | raw = fromRsaPublicKey(key) |
| Rahul Patel | 13f4936 | 2017-07-20 19:51:51 | [diff] [blame] | 104 | case ed25519.PrivateKey: |
| 105 | raw, err = fromEdPrivateKey(key) |
| Robert Coie | 4a4b7a1 | 2015-03-18 00:07:32 | [diff] [blame] | 106 | case *ecdsa.PrivateKey: |
| Cedric Staub | ff36cce | 2015-03-26 19:08:15 | [diff] [blame] | 107 | raw, err = fromEcPrivateKey(key) |
| Robert Coie | 4a4b7a1 | 2015-03-18 00:07:32 | [diff] [blame] | 108 | case *rsa.PrivateKey: |
| Cedric Staub | ff36cce | 2015-03-26 19:08:15 | [diff] [blame] | 109 | raw, err = fromRsaPrivateKey(key) |
| Cedric Staub | d509a11 | 2015-11-17 00:03:39 | [diff] [blame] | 110 | case []byte: |
| 111 | raw, err = fromSymmetricKey(key) |
| Cedric Staub | 06a2537 | 2015-03-04 06:39:47 | [diff] [blame] | 112 | default: |
| Bay Dodd | a882681 | 2015-10-16 10:30:34 | [diff] [blame] | 113 | return nil, fmt.Errorf("square/go-jose: unknown key type '%s'", reflect.TypeOf(key)) |
| Cedric Staub | 218edee | 2014-12-23 22:00:33 | [diff] [blame] | 114 | } |
| 115 | |
| Cedric Staub | ff36cce | 2015-03-26 19:08:15 | [diff] [blame] | 116 | if err != nil { |
| 117 | return nil, err |
| 118 | } |
| 119 | |
| Cedric Staub | c49ca9f | 2015-03-19 02:02:59 | [diff] [blame] | 120 | raw.Kid = k.KeyID |
| Russell Haering | fa57672 | 2015-04-05 19:56:21 | [diff] [blame] | 121 | raw.Alg = k.Algorithm |
| Daisuke Maki | 0384452 | 2015-11-02 03:17:31 | [diff] [blame] | 122 | raw.Use = k.Use |
| Robert Coie | 4a4b7a1 | 2015-03-18 00:07:32 | [diff] [blame] | 123 | |
| Cedric Staub | 7cd6062 | 2016-06-11 20:06:20 | [diff] [blame] | 124 | for _, cert := range k.Certificates { |
| 125 | raw.X5c = append(raw.X5c, base64.StdEncoding.EncodeToString(cert.Raw)) |
| 126 | } |
| 127 | |
| Cedric Staub | 9d1ab6c | 2019-05-28 21:30:13 | [diff] [blame] | 128 | x5tSHA1Len := len(k.CertificateThumbprintSHA1) |
| 129 | x5tSHA256Len := len(k.CertificateThumbprintSHA256) |
| 130 | if x5tSHA1Len > 0 { |
| 131 | if x5tSHA1Len != sha1.Size { |
| 132 | return nil, fmt.Errorf("square/go-jose: invalid SHA-1 thumbprint (must be %d bytes, not %d)", sha1.Size, x5tSHA1Len) |
| 133 | } |
| Filipe Azevedo | 1323f1f | 2020-04-30 18:02:04 | [diff] [blame] | 134 | raw.X5tSHA1 = base64.RawURLEncoding.EncodeToString(k.CertificateThumbprintSHA1) |
| Cedric Staub | 9d1ab6c | 2019-05-28 21:30:13 | [diff] [blame] | 135 | } |
| 136 | if x5tSHA256Len > 0 { |
| 137 | if x5tSHA256Len != sha256.Size { |
| 138 | return nil, fmt.Errorf("square/go-jose: invalid SHA-256 thumbprint (must be %d bytes, not %d)", sha256.Size, x5tSHA256Len) |
| 139 | } |
| Filipe Azevedo | 1323f1f | 2020-04-30 18:02:04 | [diff] [blame] | 140 | raw.X5tSHA256 = base64.RawURLEncoding.EncodeToString(k.CertificateThumbprintSHA256) |
| Cedric Staub | 9d1ab6c | 2019-05-28 21:30:13 | [diff] [blame] | 141 | } |
| 142 | |
| 143 | // If cert chain is attached (as opposed to being behind a URL), check the |
| 144 | // keys thumbprints to make sure they match what is expected. This is to |
| 145 | // ensure we don't accidentally produce a JWK with semantically inconsistent |
| 146 | // data in the headers. |
| 147 | if len(k.Certificates) > 0 { |
| 148 | expectedSHA1 := sha1.Sum(k.Certificates[0].Raw) |
| 149 | expectedSHA256 := sha256.Sum256(k.Certificates[0].Raw) |
| Filipe Azevedo | 1323f1f | 2020-04-30 18:02:04 | [diff] [blame] | 150 | |
| 151 | if len(k.CertificateThumbprintSHA1) > 0 && !bytes.Equal(k.CertificateThumbprintSHA1, expectedSHA1[:]) { |
| 152 | return nil, errors.New("square/go-jose: invalid SHA-1 thumbprint, does not match cert chain") |
| 153 | } |
| 154 | if len(k.CertificateThumbprintSHA256) > 0 && !bytes.Equal(k.CertificateThumbprintSHA256, expectedSHA256[:]) { |
| 155 | return nil, errors.New("square/go-jose: invalid or SHA-256 thumbprint, does not match cert chain") |
| Cedric Staub | 9d1ab6c | 2019-05-28 21:30:13 | [diff] [blame] | 156 | } |
| 157 | } |
| 158 | |
| 159 | raw.X5u = k.CertificatesURL |
| 160 | |
| Cedric Staub | 1f36a88 | 2016-06-01 23:11:57 | [diff] [blame] | 161 | return json.Marshal(raw) |
| Cedric Staub | 06a2537 | 2015-03-04 06:39:47 | [diff] [blame] | 162 | } |
| 163 | |
| Cedric Staub | 52b8488 | 2015-03-19 02:07:37 | [diff] [blame] | 164 | // UnmarshalJSON reads a key from its JSON representation. |
| Cedric Staub | d94ed21 | 2016-02-15 01:18:38 | [diff] [blame] | 165 | func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) { |
| 166 | var raw rawJSONWebKey |
| Cedric Staub | 1f36a88 | 2016-06-01 23:11:57 | [diff] [blame] | 167 | err = json.Unmarshal(data, &raw) |
| Cedric Staub | 06a2537 | 2015-03-04 06:39:47 | [diff] [blame] | 168 | if err != nil { |
| 169 | return err |
| Cedric Staub | 218edee | 2014-12-23 22:00:33 | [diff] [blame] | 170 | } |
| 171 | |
| Cedric Staub | 9d1ab6c | 2019-05-28 21:30:13 | [diff] [blame] | 172 | certs, err := parseCertificateChain(raw.X5c) |
| 173 | if err != nil { |
| 174 | return fmt.Errorf("square/go-jose: failed to unmarshal x5c field: %s", err) |
| 175 | } |
| 176 | |
| Cedric Staub | 52b8488 | 2015-03-19 02:07:37 | [diff] [blame] | 177 | var key interface{} |
| Cedric Staub | 9d1ab6c | 2019-05-28 21:30:13 | [diff] [blame] | 178 | var certPub interface{} |
| 179 | var keyPub interface{} |
| 180 | |
| 181 | if len(certs) > 0 { |
| 182 | // We need to check that leaf public key matches the key embedded in this |
| 183 | // JWK, as required by the standard (see RFC 7517, Section 4.7). Otherwise |
| 184 | // the JWK parsed could be semantically invalid. Technically, should also |
| 185 | // check key usage fields and other extensions on the cert here, but the |
| 186 | // standard doesn't exactly explain how they're supposed to map from the |
| 187 | // JWK representation to the X.509 extensions. |
| 188 | certPub = certs[0].PublicKey |
| 189 | } |
| 190 | |
| Cedric Staub | 06a2537 | 2015-03-04 06:39:47 | [diff] [blame] | 191 | switch raw.Kty { |
| 192 | case "EC": |
| Robert Coie | 4a4b7a1 | 2015-03-18 00:07:32 | [diff] [blame] | 193 | if raw.D != nil { |
| 194 | key, err = raw.ecPrivateKey() |
| Cedric Staub | 9d1ab6c | 2019-05-28 21:30:13 | [diff] [blame] | 195 | if err == nil { |
| 196 | keyPub = key.(*ecdsa.PrivateKey).Public() |
| 197 | } |
| Robert Coie | 4a4b7a1 | 2015-03-18 00:07:32 | [diff] [blame] | 198 | } else { |
| 199 | key, err = raw.ecPublicKey() |
| Cedric Staub | 9d1ab6c | 2019-05-28 21:30:13 | [diff] [blame] | 200 | keyPub = key |
| Robert Coie | 4a4b7a1 | 2015-03-18 00:07:32 | [diff] [blame] | 201 | } |
| Cedric Staub | 06a2537 | 2015-03-04 06:39:47 | [diff] [blame] | 202 | case "RSA": |
| Robert Coie | 4a4b7a1 | 2015-03-18 00:07:32 | [diff] [blame] | 203 | if raw.D != nil { |
| 204 | key, err = raw.rsaPrivateKey() |
| Cedric Staub | 9d1ab6c | 2019-05-28 21:30:13 | [diff] [blame] | 205 | if err == nil { |
| 206 | keyPub = key.(*rsa.PrivateKey).Public() |
| 207 | } |
| Robert Coie | 4a4b7a1 | 2015-03-18 00:07:32 | [diff] [blame] | 208 | } else { |
| 209 | key, err = raw.rsaPublicKey() |
| Cedric Staub | 9d1ab6c | 2019-05-28 21:30:13 | [diff] [blame] | 210 | keyPub = key |
| Robert Coie | 4a4b7a1 | 2015-03-18 00:07:32 | [diff] [blame] | 211 | } |
| Cedric Staub | d509a11 | 2015-11-17 00:03:39 | [diff] [blame] | 212 | case "oct": |
| Cedric Staub | 9d1ab6c | 2019-05-28 21:30:13 | [diff] [blame] | 213 | if certPub != nil { |
| 214 | return errors.New("square/go-jose: invalid JWK, found 'oct' (symmetric) key with cert chain") |
| 215 | } |
| Cedric Staub | d509a11 | 2015-11-17 00:03:39 | [diff] [blame] | 216 | key, err = raw.symmetricKey() |
| Rahul Patel | 13f4936 | 2017-07-20 19:51:51 | [diff] [blame] | 217 | case "OKP": |
| Rahul Patel | 40a5835 | 2017-07-25 16:59:50 | [diff] [blame] | 218 | if raw.Crv == "Ed25519" && raw.X != nil { |
| Rahul Patel | 13f4936 | 2017-07-20 19:51:51 | [diff] [blame] | 219 | if raw.D != nil { |
| 220 | key, err = raw.edPrivateKey() |
| Cedric Staub | 9d1ab6c | 2019-05-28 21:30:13 | [diff] [blame] | 221 | if err == nil { |
| 222 | keyPub = key.(ed25519.PrivateKey).Public() |
| 223 | } |
| Rahul Patel | 13f4936 | 2017-07-20 19:51:51 | [diff] [blame] | 224 | } else { |
| 225 | key, err = raw.edPublicKey() |
| Cedric Staub | 9d1ab6c | 2019-05-28 21:30:13 | [diff] [blame] | 226 | keyPub = key |
| Rahul Patel | 13f4936 | 2017-07-20 19:51:51 | [diff] [blame] | 227 | } |
| 228 | } else { |
| 229 | err = fmt.Errorf("square/go-jose: unknown curve %s'", raw.Crv) |
| 230 | } |
| Cedric Staub | 06a2537 | 2015-03-04 06:39:47 | [diff] [blame] | 231 | default: |
| Cedric Staub | 4d2b9e8 | 2016-02-15 01:02:47 | [diff] [blame] | 232 | err = fmt.Errorf("square/go-jose: unknown json web key type '%s'", raw.Kty) |
| Cedric Staub | 06a2537 | 2015-03-04 06:39:47 | [diff] [blame] | 233 | } |
| 234 | |
| Cedric Staub | 9d1ab6c | 2019-05-28 21:30:13 | [diff] [blame] | 235 | if err != nil { |
| 236 | return |
| 237 | } |
| Cedric Staub | 7cd6062 | 2016-06-11 20:06:20 | [diff] [blame] | 238 | |
| Cedric Staub | 9d1ab6c | 2019-05-28 21:30:13 | [diff] [blame] | 239 | if certPub != nil && keyPub != nil { |
| 240 | if !reflect.DeepEqual(certPub, keyPub) { |
| adeinega | d1dab36 | 2021-04-13 03:34:19 | [diff] [blame] | 241 | return errors.New("square/go-jose: invalid JWK, public keys in key and x5c fields do not match") |
| Cedric Staub | 9d1ab6c | 2019-05-28 21:30:13 | [diff] [blame] | 242 | } |
| 243 | } |
| 244 | |
| 245 | *k = JSONWebKey{Key: key, KeyID: raw.Kid, Algorithm: raw.Alg, Use: raw.Use, Certificates: certs} |
| 246 | |
| 247 | k.CertificatesURL = raw.X5u |
| Filipe Azevedo | 1323f1f | 2020-04-30 18:02:04 | [diff] [blame] | 248 | |
| 249 | // x5t parameters are base64url-encoded SHA thumbprints |
| 250 | // See RFC 7517, Section 4.8, https://tools.ietf.org/html/rfc7517#section-4.8 |
| 251 | x5tSHA1bytes, err := base64.RawURLEncoding.DecodeString(raw.X5tSHA1) |
| 252 | if err != nil { |
| 253 | return errors.New("square/go-jose: invalid JWK, x5t header has invalid encoding") |
| 254 | } |
| 255 | |
| 256 | // RFC 7517, Section 4.8 is ambiguous as to whether the digest output should be byte or hex, |
| 257 | // for this reason, after base64 decoding, if the size is sha1.Size it's likely that the value is a byte encoded |
| 258 | // checksum so we skip this. Otherwise if the checksum was hex encoded we expect a 40 byte sized array so we'll |
| 259 | // try to hex decode it. When Marshalling this value we'll always use a base64 encoded version of byte format checksum. |
| 260 | if len(x5tSHA1bytes) == 2*sha1.Size { |
| 261 | hx, err := hex.DecodeString(string(x5tSHA1bytes)) |
| 262 | if err != nil { |
| 263 | return fmt.Errorf("square/go-jose: invalid JWK, unable to hex decode x5t: %v", err) |
| 264 | |
| 265 | } |
| 266 | x5tSHA1bytes = hx |
| 267 | } |
| 268 | |
| 269 | k.CertificateThumbprintSHA1 = x5tSHA1bytes |
| 270 | |
| 271 | x5tSHA256bytes, err := base64.RawURLEncoding.DecodeString(raw.X5tSHA256) |
| 272 | if err != nil { |
| 273 | return errors.New("square/go-jose: invalid JWK, x5t#S256 header has invalid encoding") |
| 274 | } |
| 275 | |
| 276 | if len(x5tSHA256bytes) == 2*sha256.Size { |
| 277 | hx256, err := hex.DecodeString(string(x5tSHA256bytes)) |
| 278 | if err != nil { |
| 279 | return fmt.Errorf("square/go-jose: invalid JWK, unable to hex decode x5t#S256: %v", err) |
| 280 | } |
| 281 | x5tSHA256bytes = hx256 |
| 282 | } |
| 283 | |
| 284 | k.CertificateThumbprintSHA256 = x5tSHA256bytes |
| Cedric Staub | 9d1ab6c | 2019-05-28 21:30:13 | [diff] [blame] | 285 | |
| 286 | x5tSHA1Len := len(k.CertificateThumbprintSHA1) |
| 287 | x5tSHA256Len := len(k.CertificateThumbprintSHA256) |
| 288 | if x5tSHA1Len > 0 && x5tSHA1Len != sha1.Size { |
| 289 | return errors.New("square/go-jose: invalid JWK, x5t header is of incorrect size") |
| 290 | } |
| 291 | if x5tSHA256Len > 0 && x5tSHA256Len != sha256.Size { |
| Filipe Azevedo | 1323f1f | 2020-04-30 18:02:04 | [diff] [blame] | 292 | return errors.New("square/go-jose: invalid JWK, x5t#S256 header is of incorrect size") |
| Cedric Staub | 9d1ab6c | 2019-05-28 21:30:13 | [diff] [blame] | 293 | } |
| 294 | |
| 295 | // If certificate chain *and* thumbprints are set, verify correctness. |
| 296 | if len(k.Certificates) > 0 { |
| 297 | leaf := k.Certificates[0] |
| 298 | sha1sum := sha1.Sum(leaf.Raw) |
| 299 | sha256sum := sha256.Sum256(leaf.Raw) |
| 300 | |
| 301 | if len(k.CertificateThumbprintSHA1) > 0 && !bytes.Equal(sha1sum[:], k.CertificateThumbprintSHA1) { |
| 302 | return errors.New("square/go-jose: invalid JWK, x5c thumbprint does not match x5t value") |
| 303 | } |
| 304 | |
| 305 | if len(k.CertificateThumbprintSHA256) > 0 && !bytes.Equal(sha256sum[:], k.CertificateThumbprintSHA256) { |
| 306 | return errors.New("square/go-jose: invalid JWK, x5c thumbprint does not match x5t#S256 value") |
| Cedric Staub | 28e45bb | 2018-03-28 18:50:24 | [diff] [blame] | 307 | } |
| Cedric Staub | 7cd6062 | 2016-06-11 20:06:20 | [diff] [blame] | 308 | } |
| 309 | |
| Cedric Staub | 06a2537 | 2015-03-04 06:39:47 | [diff] [blame] | 310 | return |
| 311 | } |
| 312 | |
| Cedric Staub | d94ed21 | 2016-02-15 01:18:38 | [diff] [blame] | 313 | // JSONWebKeySet represents a JWK Set object. |
| 314 | type JSONWebKeySet struct { |
| 315 | Keys []JSONWebKey `json:"keys"` |
| Bay Dodd | a6c3672 | 2015-10-17 13:56:17 | [diff] [blame] | 316 | } |
| 317 | |
| 318 | // Key convenience method returns keys by key ID. Specification states |
| 319 | // that a JWK Set "SHOULD" use distinct key IDs, but allows for some |
| 320 | // cases where they are not distinct. Hence method returns a slice |
| Cedric Staub | d94ed21 | 2016-02-15 01:18:38 | [diff] [blame] | 321 | // of JSONWebKeys. |
| 322 | func (s *JSONWebKeySet) Key(kid string) []JSONWebKey { |
| 323 | var keys []JSONWebKey |
| Bay Dodd | a6c3672 | 2015-10-17 13:56:17 | [diff] [blame] | 324 | for _, key := range s.Keys { |
| 325 | if key.KeyID == kid { |
| 326 | keys = append(keys, key) |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | return keys |
| 331 | } |
| 332 | |
| Richard Barnes | f2a0135 | 2015-06-29 17:11:42 | [diff] [blame] | 333 | const rsaThumbprintTemplate = `{"e":"%s","kty":"RSA","n":"%s"}` |
| 334 | const ecThumbprintTemplate = `{"crv":"%s","kty":"EC","x":"%s","y":"%s"}` |
| Cedric Staub | a10ff54 | 2021-06-05 20:43:59 | [diff] [blame] | 335 | const edThumbprintTemplate = `{"crv":"%s","kty":"OKP","x":"%s"}` |
| Richard Barnes | f2a0135 | 2015-06-29 17:11:42 | [diff] [blame] | 336 | |
| Richard Barnes | 5e569ac | 2015-10-03 15:40:10 | [diff] [blame] | 337 | func ecThumbprintInput(curve elliptic.Curve, x, y *big.Int) (string, error) { |
| 338 | coordLength := curveSize(curve) |
| 339 | crv, err := curveName(curve) |
| 340 | if err != nil { |
| 341 | return "", err |
| Richard Barnes | f2a0135 | 2015-06-29 17:11:42 | [diff] [blame] | 342 | } |
| 343 | |
| Cedric Staub | 9d410fe | 2018-07-25 18:22:28 | [diff] [blame] | 344 | if len(x.Bytes()) > coordLength || len(y.Bytes()) > coordLength { |
| 345 | return "", errors.New("square/go-jose: invalid elliptic key (too large)") |
| 346 | } |
| 347 | |
| Richard Barnes | f2a0135 | 2015-06-29 17:11:42 | [diff] [blame] | 348 | return fmt.Sprintf(ecThumbprintTemplate, crv, |
| Richard Barnes | 5e569ac | 2015-10-03 15:40:10 | [diff] [blame] | 349 | newFixedSizeBuffer(x.Bytes(), coordLength).base64(), |
| 350 | newFixedSizeBuffer(y.Bytes(), coordLength).base64()), nil |
| Richard Barnes | f2a0135 | 2015-06-29 17:11:42 | [diff] [blame] | 351 | } |
| 352 | |
| Richard Barnes | 5e569ac | 2015-10-03 15:40:10 | [diff] [blame] | 353 | func rsaThumbprintInput(n *big.Int, e int) (string, error) { |
| Richard Barnes | f2a0135 | 2015-06-29 17:11:42 | [diff] [blame] | 354 | return fmt.Sprintf(rsaThumbprintTemplate, |
| Richard Barnes | 5e569ac | 2015-10-03 15:40:10 | [diff] [blame] | 355 | newBufferFromInt(uint64(e)).base64(), |
| 356 | newBuffer(n.Bytes()).base64()), nil |
| Richard Barnes | f2a0135 | 2015-06-29 17:11:42 | [diff] [blame] | 357 | } |
| 358 | |
| Rahul Patel | 13f4936 | 2017-07-20 19:51:51 | [diff] [blame] | 359 | func edThumbprintInput(ed ed25519.PublicKey) (string, error) { |
| 360 | crv := "Ed25519" |
| Cedric Staub | 9d410fe | 2018-07-25 18:22:28 | [diff] [blame] | 361 | if len(ed) > 32 { |
| 362 | return "", errors.New("square/go-jose: invalid elliptic key (too large)") |
| 363 | } |
| Rahul Patel | 13f4936 | 2017-07-20 19:51:51 | [diff] [blame] | 364 | return fmt.Sprintf(edThumbprintTemplate, crv, |
| 365 | newFixedSizeBuffer(ed, 32).base64()), nil |
| 366 | } |
| 367 | |
| Richard Barnes | f2a0135 | 2015-06-29 17:11:42 | [diff] [blame] | 368 | // Thumbprint computes the JWK Thumbprint of a key using the |
| 369 | // indicated hash algorithm. |
| Cedric Staub | d94ed21 | 2016-02-15 01:18:38 | [diff] [blame] | 370 | func (k *JSONWebKey) Thumbprint(hash crypto.Hash) ([]byte, error) { |
| Richard Barnes | f2a0135 | 2015-06-29 17:11:42 | [diff] [blame] | 371 | var input string |
| 372 | var err error |
| 373 | switch key := k.Key.(type) { |
| Rahul Patel | 13f4936 | 2017-07-20 19:51:51 | [diff] [blame] | 374 | case ed25519.PublicKey: |
| 375 | input, err = edThumbprintInput(key) |
| Richard Barnes | f2a0135 | 2015-06-29 17:11:42 | [diff] [blame] | 376 | case *ecdsa.PublicKey: |
| Richard Barnes | 5e569ac | 2015-10-03 15:40:10 | [diff] [blame] | 377 | input, err = ecThumbprintInput(key.Curve, key.X, key.Y) |
| Richard Barnes | f2a0135 | 2015-06-29 17:11:42 | [diff] [blame] | 378 | case *ecdsa.PrivateKey: |
| Richard Barnes | 5e569ac | 2015-10-03 15:40:10 | [diff] [blame] | 379 | input, err = ecThumbprintInput(key.Curve, key.X, key.Y) |
| 380 | case *rsa.PublicKey: |
| 381 | input, err = rsaThumbprintInput(key.N, key.E) |
| Richard Barnes | f2a0135 | 2015-06-29 17:11:42 | [diff] [blame] | 382 | case *rsa.PrivateKey: |
| Richard Barnes | 5e569ac | 2015-10-03 15:40:10 | [diff] [blame] | 383 | input, err = rsaThumbprintInput(key.N, key.E) |
| Rahul Patel | 13f4936 | 2017-07-20 19:51:51 | [diff] [blame] | 384 | case ed25519.PrivateKey: |
| Mariano Cano | 86617ab | 2019-04-05 18:30:18 | [diff] [blame] | 385 | input, err = edThumbprintInput(ed25519.PublicKey(key[32:])) |
| Richard Barnes | f2a0135 | 2015-06-29 17:11:42 | [diff] [blame] | 386 | default: |
| Cedric Staub | 4d2b9e8 | 2016-02-15 01:02:47 | [diff] [blame] | 387 | return nil, fmt.Errorf("square/go-jose: unknown key type '%s'", reflect.TypeOf(key)) |
| Richard Barnes | f2a0135 | 2015-06-29 17:11:42 | [diff] [blame] | 388 | } |
| 389 | |
| 390 | if err != nil { |
| 391 | return nil, err |
| 392 | } |
| 393 | |
| 394 | h := hash.New() |
| 395 | h.Write([]byte(input)) |
| 396 | return h.Sum(nil), nil |
| 397 | } |
| 398 | |
| Cedric Staub | df56d35 | 2016-09-22 23:03:22 | [diff] [blame] | 399 | // IsPublic returns true if the JWK represents a public key (not symmetric, not private). |
| Cedric Staub | d2f5e5b | 2016-09-25 00:03:42 | [diff] [blame] | 400 | func (k *JSONWebKey) IsPublic() bool { |
| Cedric Staub | df56d35 | 2016-09-22 23:03:22 | [diff] [blame] | 401 | switch k.Key.(type) { |
| Paul Querna | 11369b4 | 2017-09-29 17:15:03 | [diff] [blame] | 402 | case *ecdsa.PublicKey, *rsa.PublicKey, ed25519.PublicKey: |
| Cedric Staub | df56d35 | 2016-09-22 23:03:22 | [diff] [blame] | 403 | return true |
| 404 | default: |
| 405 | return false |
| 406 | } |
| 407 | } |
| 408 | |
| Evan Jones | 636770b | 2020-05-29 14:58:41 | [diff] [blame] | 409 | // Public creates JSONWebKey with corresponding public key if JWK represents asymmetric private key. |
| Leonid Evdokimov | 72b5a23 | 2017-10-04 17:32:55 | [diff] [blame] | 410 | func (k *JSONWebKey) Public() JSONWebKey { |
| 411 | if k.IsPublic() { |
| 412 | return *k |
| 413 | } |
| 414 | ret := *k |
| 415 | switch key := k.Key.(type) { |
| 416 | case *ecdsa.PrivateKey: |
| 417 | ret.Key = key.Public() |
| 418 | case *rsa.PrivateKey: |
| 419 | ret.Key = key.Public() |
| 420 | case ed25519.PrivateKey: |
| 421 | ret.Key = key.Public() |
| 422 | default: |
| 423 | return JSONWebKey{} // returning invalid key |
| 424 | } |
| 425 | return ret |
| 426 | } |
| 427 | |
| Cedric Staub | df56d35 | 2016-09-22 23:03:22 | [diff] [blame] | 428 | // Valid checks that the key contains the expected parameters. |
| Cedric Staub | 8fbcaa5 | 2016-05-02 22:10:55 | [diff] [blame] | 429 | func (k *JSONWebKey) Valid() bool { |
| Roland Shoemaker | 3c794e4 | 2016-05-02 20:27:18 | [diff] [blame] | 430 | if k.Key == nil { |
| 431 | return false |
| 432 | } |
| 433 | switch key := k.Key.(type) { |
| 434 | case *ecdsa.PublicKey: |
| 435 | if key.Curve == nil || key.X == nil || key.Y == nil { |
| 436 | return false |
| 437 | } |
| 438 | case *ecdsa.PrivateKey: |
| 439 | if key.Curve == nil || key.X == nil || key.Y == nil || key.D == nil { |
| 440 | return false |
| 441 | } |
| 442 | case *rsa.PublicKey: |
| 443 | if key.N == nil || key.E == 0 { |
| 444 | return false |
| 445 | } |
| 446 | case *rsa.PrivateKey: |
| 447 | if key.N == nil || key.E == 0 || key.D == nil || len(key.Primes) < 2 { |
| 448 | return false |
| 449 | } |
| Paul Querna | 11369b4 | 2017-09-29 17:15:03 | [diff] [blame] | 450 | case ed25519.PublicKey: |
| 451 | if len(key) != 32 { |
| Cedric Staub | 3567d65 | 2017-09-13 00:11:56 | [diff] [blame] | 452 | return false |
| 453 | } |
| Paul Querna | 11369b4 | 2017-09-29 17:15:03 | [diff] [blame] | 454 | case ed25519.PrivateKey: |
| 455 | if len(key) != 64 { |
| Cedric Staub | 3567d65 | 2017-09-13 00:11:56 | [diff] [blame] | 456 | return false |
| 457 | } |
| Roland Shoemaker | 3c794e4 | 2016-05-02 20:27:18 | [diff] [blame] | 458 | default: |
| 459 | return false |
| 460 | } |
| 461 | return true |
| 462 | } |
| 463 | |
| Cedric Staub | d94ed21 | 2016-02-15 01:18:38 | [diff] [blame] | 464 | func (key rawJSONWebKey) rsaPublicKey() (*rsa.PublicKey, error) { |
| Cedric Staub | 06a2537 | 2015-03-04 06:39:47 | [diff] [blame] | 465 | if key.N == nil || key.E == nil { |
| 466 | return nil, fmt.Errorf("square/go-jose: invalid RSA key, missing n/e values") |
| 467 | } |
| 468 | |
| 469 | return &rsa.PublicKey{ |
| 470 | N: key.N.bigInt(), |
| 471 | E: key.E.toInt(), |
| 472 | }, nil |
| 473 | } |
| 474 | |
| Rahul Patel | 13f4936 | 2017-07-20 19:51:51 | [diff] [blame] | 475 | func fromEdPublicKey(pub ed25519.PublicKey) *rawJSONWebKey { |
| 476 | return &rawJSONWebKey{ |
| 477 | Kty: "OKP", |
| 478 | Crv: "Ed25519", |
| 479 | X: newBuffer(pub), |
| 480 | } |
| 481 | } |
| 482 | |
| Cedric Staub | d94ed21 | 2016-02-15 01:18:38 | [diff] [blame] | 483 | func fromRsaPublicKey(pub *rsa.PublicKey) *rawJSONWebKey { |
| 484 | return &rawJSONWebKey{ |
| Cedric Staub | 06a2537 | 2015-03-04 06:39:47 | [diff] [blame] | 485 | Kty: "RSA", |
| 486 | N: newBuffer(pub.N.Bytes()), |
| Cedric Staub | feed15e | 2015-07-23 19:12:53 | [diff] [blame] | 487 | E: newBufferFromInt(uint64(pub.E)), |
| Cedric Staub | 06a2537 | 2015-03-04 06:39:47 | [diff] [blame] | 488 | } |
| 489 | } |
| 490 | |
| Cedric Staub | d94ed21 | 2016-02-15 01:18:38 | [diff] [blame] | 491 | func (key rawJSONWebKey) ecPublicKey() (*ecdsa.PublicKey, error) { |
| Cedric Staub | 218edee | 2014-12-23 22:00:33 | [diff] [blame] | 492 | var curve elliptic.Curve |
| 493 | switch key.Crv { |
| 494 | case "P-256": |
| 495 | curve = elliptic.P256() |
| 496 | case "P-384": |
| 497 | curve = elliptic.P384() |
| 498 | case "P-521": |
| 499 | curve = elliptic.P521() |
| 500 | default: |
| 501 | return nil, fmt.Errorf("square/go-jose: unsupported elliptic curve '%s'", key.Crv) |
| 502 | } |
| 503 | |
| 504 | if key.X == nil || key.Y == nil { |
| Cedric Staub | 7f0dd80 | 2016-08-31 18:22:30 | [diff] [blame] | 505 | return nil, errors.New("square/go-jose: invalid EC key, missing x/y values") |
| 506 | } |
| 507 | |
| Jacob Hoffman-Andrews | b56d11b | 2018-12-05 02:58:17 | [diff] [blame] | 508 | // The length of this octet string MUST be the full size of a coordinate for |
| 509 | // the curve specified in the "crv" parameter. |
| 510 | // https://tools.ietf.org/html/rfc7518#section-6.2.1.2 |
| Jacob Hoffman-Andrews | e1428aa | 2018-12-03 22:02:30 | [diff] [blame] | 511 | if curveSize(curve) != len(key.X.data) { |
| Jacob Hoffman-Andrews | fe1ef12 | 2019-06-26 17:39:57 | [diff] [blame] | 512 | return nil, fmt.Errorf("square/go-jose: invalid EC public key, wrong length for x") |
| Jacob Hoffman-Andrews | e1428aa | 2018-12-03 22:02:30 | [diff] [blame] | 513 | } |
| 514 | |
| 515 | if curveSize(curve) != len(key.Y.data) { |
| Jacob Hoffman-Andrews | fe1ef12 | 2019-06-26 17:39:57 | [diff] [blame] | 516 | return nil, fmt.Errorf("square/go-jose: invalid EC public key, wrong length for y") |
| Jacob Hoffman-Andrews | e1428aa | 2018-12-03 22:02:30 | [diff] [blame] | 517 | } |
| 518 | |
| Cedric Staub | 7f0dd80 | 2016-08-31 18:22:30 | [diff] [blame] | 519 | x := key.X.bigInt() |
| 520 | y := key.Y.bigInt() |
| 521 | |
| 522 | if !curve.IsOnCurve(x, y) { |
| 523 | return nil, errors.New("square/go-jose: invalid EC key, X/Y are not on declared curve") |
| Cedric Staub | 218edee | 2014-12-23 22:00:33 | [diff] [blame] | 524 | } |
| 525 | |
| 526 | return &ecdsa.PublicKey{ |
| 527 | Curve: curve, |
| Cedric Staub | 7f0dd80 | 2016-08-31 18:22:30 | [diff] [blame] | 528 | X: x, |
| 529 | Y: y, |
| Cedric Staub | 218edee | 2014-12-23 22:00:33 | [diff] [blame] | 530 | }, nil |
| 531 | } |
| 532 | |
| Cedric Staub | d94ed21 | 2016-02-15 01:18:38 | [diff] [blame] | 533 | func fromEcPublicKey(pub *ecdsa.PublicKey) (*rawJSONWebKey, error) { |
| Cedric Staub | 97198ac | 2015-03-19 01:55:26 | [diff] [blame] | 534 | if pub == nil || pub.X == nil || pub.Y == nil { |
| Cedric Staub | 7190f38 | 2015-08-18 02:18:51 | [diff] [blame] | 535 | return nil, fmt.Errorf("square/go-jose: invalid EC key (nil, or X/Y missing)") |
| 536 | } |
| 537 | |
| 538 | name, err := curveName(pub.Curve) |
| 539 | if err != nil { |
| 540 | return nil, err |
| 541 | } |
| 542 | |
| 543 | size := curveSize(pub.Curve) |
| 544 | |
| 545 | xBytes := pub.X.Bytes() |
| 546 | yBytes := pub.Y.Bytes() |
| 547 | |
| 548 | if len(xBytes) > size || len(yBytes) > size { |
| 549 | return nil, fmt.Errorf("square/go-jose: invalid EC key (X/Y too large)") |
| Cedric Staub | 97198ac | 2015-03-19 01:55:26 | [diff] [blame] | 550 | } |
| 551 | |
| Cedric Staub | d94ed21 | 2016-02-15 01:18:38 | [diff] [blame] | 552 | key := &rawJSONWebKey{ |
| Cedric Staub | 218edee | 2014-12-23 22:00:33 | [diff] [blame] | 553 | Kty: "EC", |
| Cedric Staub | 7190f38 | 2015-08-18 02:18:51 | [diff] [blame] | 554 | Crv: name, |
| 555 | X: newFixedSizeBuffer(xBytes, size), |
| 556 | Y: newFixedSizeBuffer(yBytes, size), |
| Cedric Staub | 218edee | 2014-12-23 22:00:33 | [diff] [blame] | 557 | } |
| 558 | |
| Cedric Staub | ff36cce | 2015-03-26 19:08:15 | [diff] [blame] | 559 | return key, nil |
| Cedric Staub | 218edee | 2014-12-23 22:00:33 | [diff] [blame] | 560 | } |
| Robert Coie | 4a4b7a1 | 2015-03-18 00:07:32 | [diff] [blame] | 561 | |
| Rahul Patel | 13f4936 | 2017-07-20 19:51:51 | [diff] [blame] | 562 | func (key rawJSONWebKey) edPrivateKey() (ed25519.PrivateKey, error) { |
| 563 | var missing []string |
| 564 | switch { |
| 565 | case key.D == nil: |
| 566 | missing = append(missing, "D") |
| 567 | case key.X == nil: |
| 568 | missing = append(missing, "X") |
| 569 | } |
| 570 | |
| 571 | if len(missing) > 0 { |
| 572 | return nil, fmt.Errorf("square/go-jose: invalid Ed25519 private key, missing %s value(s)", strings.Join(missing, ", ")) |
| 573 | } |
| 574 | |
| 575 | privateKey := make([]byte, ed25519.PrivateKeySize) |
| Mariano Cano | 86617ab | 2019-04-05 18:30:18 | [diff] [blame] | 576 | copy(privateKey[0:32], key.D.bytes()) |
| 577 | copy(privateKey[32:], key.X.bytes()) |
| Rahul Patel | 13f4936 | 2017-07-20 19:51:51 | [diff] [blame] | 578 | rv := ed25519.PrivateKey(privateKey) |
| 579 | return rv, nil |
| 580 | } |
| 581 | |
| 582 | func (key rawJSONWebKey) edPublicKey() (ed25519.PublicKey, error) { |
| 583 | if key.X == nil { |
| 584 | return nil, fmt.Errorf("square/go-jose: invalid Ed key, missing x value") |
| 585 | } |
| 586 | publicKey := make([]byte, ed25519.PublicKeySize) |
| 587 | copy(publicKey[0:32], key.X.bytes()) |
| 588 | rv := ed25519.PublicKey(publicKey) |
| 589 | return rv, nil |
| 590 | } |
| 591 | |
| Cedric Staub | d94ed21 | 2016-02-15 01:18:38 | [diff] [blame] | 592 | func (key rawJSONWebKey) rsaPrivateKey() (*rsa.PrivateKey, error) { |
| Cedric Staub | 98e5a54 | 2015-10-15 21:19:39 | [diff] [blame] | 593 | var missing []string |
| Daisuke Maki | d367f61 | 2015-10-15 07:55:50 | [diff] [blame] | 594 | switch { |
| 595 | case key.N == nil: |
| Cedric Staub | 98e5a54 | 2015-10-15 21:19:39 | [diff] [blame] | 596 | missing = append(missing, "N") |
| Daisuke Maki | d367f61 | 2015-10-15 07:55:50 | [diff] [blame] | 597 | case key.E == nil: |
| Cedric Staub | 98e5a54 | 2015-10-15 21:19:39 | [diff] [blame] | 598 | missing = append(missing, "E") |
| Daisuke Maki | d367f61 | 2015-10-15 07:55:50 | [diff] [blame] | 599 | case key.D == nil: |
| Cedric Staub | 98e5a54 | 2015-10-15 21:19:39 | [diff] [blame] | 600 | missing = append(missing, "D") |
| Daisuke Maki | d367f61 | 2015-10-15 07:55:50 | [diff] [blame] | 601 | case key.P == nil: |
| Cedric Staub | 98e5a54 | 2015-10-15 21:19:39 | [diff] [blame] | 602 | missing = append(missing, "P") |
| Daisuke Maki | d367f61 | 2015-10-15 07:55:50 | [diff] [blame] | 603 | case key.Q == nil: |
| Cedric Staub | 98e5a54 | 2015-10-15 21:19:39 | [diff] [blame] | 604 | missing = append(missing, "Q") |
| Daisuke Maki | d367f61 | 2015-10-15 07:55:50 | [diff] [blame] | 605 | } |
| Cedric Staub | 98e5a54 | 2015-10-15 21:19:39 | [diff] [blame] | 606 | |
| 607 | if len(missing) > 0 { |
| 608 | return nil, fmt.Errorf("square/go-jose: invalid RSA private key, missing %s value(s)", strings.Join(missing, ", ")) |
| Robert Coie | 4a4b7a1 | 2015-03-18 00:07:32 | [diff] [blame] | 609 | } |
| 610 | |
| Robert Coie | c20b416 | 2015-03-18 05:41:54 | [diff] [blame] | 611 | rv := &rsa.PrivateKey{ |
| 612 | PublicKey: rsa.PublicKey{ |
| Cedric Staub | 52b8488 | 2015-03-19 02:07:37 | [diff] [blame] | 613 | N: key.N.bigInt(), |
| 614 | E: key.E.toInt(), |
| Robert Coie | c20b416 | 2015-03-18 05:41:54 | [diff] [blame] | 615 | }, |
| Cedric Staub | 52b8488 | 2015-03-19 02:07:37 | [diff] [blame] | 616 | D: key.D.bigInt(), |
| Robert Coie | c20b416 | 2015-03-18 05:41:54 | [diff] [blame] | 617 | Primes: []*big.Int{ |
| Cedric Staub | 52b8488 | 2015-03-19 02:07:37 | [diff] [blame] | 618 | key.P.bigInt(), |
| 619 | key.Q.bigInt(), |
| Robert Coie | c20b416 | 2015-03-18 05:41:54 | [diff] [blame] | 620 | }, |
| Robert Coie | 4a4b7a1 | 2015-03-18 00:07:32 | [diff] [blame] | 621 | } |
| Robert Coie | 4a4b7a1 | 2015-03-18 00:07:32 | [diff] [blame] | 622 | |
| Cedric Staub | 52b8488 | 2015-03-19 02:07:37 | [diff] [blame] | 623 | if key.Dp != nil { |
| 624 | rv.Precomputed.Dp = key.Dp.bigInt() |
| Robert Coie | c20b416 | 2015-03-18 05:41:54 | [diff] [blame] | 625 | } |
| Cedric Staub | 52b8488 | 2015-03-19 02:07:37 | [diff] [blame] | 626 | if key.Dq != nil { |
| 627 | rv.Precomputed.Dq = key.Dq.bigInt() |
| Robert Coie | c20b416 | 2015-03-18 05:41:54 | [diff] [blame] | 628 | } |
| Cedric Staub | 52b8488 | 2015-03-19 02:07:37 | [diff] [blame] | 629 | if key.Qi != nil { |
| 630 | rv.Precomputed.Qinv = key.Qi.bigInt() |
| Robert Coie | c20b416 | 2015-03-18 05:41:54 | [diff] [blame] | 631 | } |
| 632 | |
| 633 | err := rv.Validate() |
| Robert Coie | 4a4b7a1 | 2015-03-18 00:07:32 | [diff] [blame] | 634 | return rv, err |
| 635 | } |
| 636 | |
| Rahul Patel | 13f4936 | 2017-07-20 19:51:51 | [diff] [blame] | 637 | func fromEdPrivateKey(ed ed25519.PrivateKey) (*rawJSONWebKey, error) { |
| Mariano Cano | 86617ab | 2019-04-05 18:30:18 | [diff] [blame] | 638 | raw := fromEdPublicKey(ed25519.PublicKey(ed[32:])) |
| Rahul Patel | 13f4936 | 2017-07-20 19:51:51 | [diff] [blame] | 639 | |
| Mariano Cano | 86617ab | 2019-04-05 18:30:18 | [diff] [blame] | 640 | raw.D = newBuffer(ed[0:32]) |
| Rahul Patel | 13f4936 | 2017-07-20 19:51:51 | [diff] [blame] | 641 | return raw, nil |
| 642 | } |
| 643 | |
| Cedric Staub | d94ed21 | 2016-02-15 01:18:38 | [diff] [blame] | 644 | func fromRsaPrivateKey(rsa *rsa.PrivateKey) (*rawJSONWebKey, error) { |
| Robert Coie | c20b416 | 2015-03-18 05:41:54 | [diff] [blame] | 645 | if len(rsa.Primes) != 2 { |
| Cedric Staub | ff36cce | 2015-03-26 19:08:15 | [diff] [blame] | 646 | return nil, ErrUnsupportedKeyType |
| Robert Coie | c20b416 | 2015-03-18 05:41:54 | [diff] [blame] | 647 | } |
| 648 | |
| Cedric Staub | ff36cce | 2015-03-26 19:08:15 | [diff] [blame] | 649 | raw := fromRsaPublicKey(&rsa.PublicKey) |
| Robert Coie | 4a4b7a1 | 2015-03-18 00:07:32 | [diff] [blame] | 650 | |
| Cedric Staub | 52b8488 | 2015-03-19 02:07:37 | [diff] [blame] | 651 | raw.D = newBuffer(rsa.D.Bytes()) |
| 652 | raw.P = newBuffer(rsa.Primes[0].Bytes()) |
| 653 | raw.Q = newBuffer(rsa.Primes[1].Bytes()) |
| Cedric Staub | ff36cce | 2015-03-26 19:08:15 | [diff] [blame] | 654 | |
| Kyle Boutette | b01747d | 2018-09-19 22:33:51 | [diff] [blame] | 655 | if rsa.Precomputed.Dp != nil { |
| 656 | raw.Dp = newBuffer(rsa.Precomputed.Dp.Bytes()) |
| 657 | } |
| 658 | if rsa.Precomputed.Dq != nil { |
| 659 | raw.Dq = newBuffer(rsa.Precomputed.Dq.Bytes()) |
| 660 | } |
| 661 | if rsa.Precomputed.Qinv != nil { |
| 662 | raw.Qi = newBuffer(rsa.Precomputed.Qinv.Bytes()) |
| 663 | } |
| 664 | |
| Cedric Staub | ff36cce | 2015-03-26 19:08:15 | [diff] [blame] | 665 | return raw, nil |
| Robert Coie | 4a4b7a1 | 2015-03-18 00:07:32 | [diff] [blame] | 666 | } |
| 667 | |
| Cedric Staub | d94ed21 | 2016-02-15 01:18:38 | [diff] [blame] | 668 | func (key rawJSONWebKey) ecPrivateKey() (*ecdsa.PrivateKey, error) { |
| Robert Coie | c20b416 | 2015-03-18 05:41:54 | [diff] [blame] | 669 | var curve elliptic.Curve |
| Cedric Staub | 52b8488 | 2015-03-19 02:07:37 | [diff] [blame] | 670 | switch key.Crv { |
| Robert Coie | c20b416 | 2015-03-18 05:41:54 | [diff] [blame] | 671 | case "P-256": |
| 672 | curve = elliptic.P256() |
| 673 | case "P-384": |
| 674 | curve = elliptic.P384() |
| 675 | case "P-521": |
| 676 | curve = elliptic.P521() |
| 677 | default: |
| Cedric Staub | 52b8488 | 2015-03-19 02:07:37 | [diff] [blame] | 678 | return nil, fmt.Errorf("square/go-jose: unsupported elliptic curve '%s'", key.Crv) |
| Robert Coie | c20b416 | 2015-03-18 05:41:54 | [diff] [blame] | 679 | } |
| 680 | |
| Cedric Staub | 52b8488 | 2015-03-19 02:07:37 | [diff] [blame] | 681 | if key.X == nil || key.Y == nil || key.D == nil { |
| Cedric Staub | 97198ac | 2015-03-19 01:55:26 | [diff] [blame] | 682 | return nil, fmt.Errorf("square/go-jose: invalid EC private key, missing x/y/d values") |
| Robert Coie | 4a4b7a1 | 2015-03-18 00:07:32 | [diff] [blame] | 683 | } |
| 684 | |
| Jacob Hoffman-Andrews | b56d11b | 2018-12-05 02:58:17 | [diff] [blame] | 685 | // The length of this octet string MUST be the full size of a coordinate for |
| 686 | // the curve specified in the "crv" parameter. |
| 687 | // https://tools.ietf.org/html/rfc7518#section-6.2.1.2 |
| Jacob Hoffman-Andrews | e1428aa | 2018-12-03 22:02:30 | [diff] [blame] | 688 | if curveSize(curve) != len(key.X.data) { |
| 689 | return nil, fmt.Errorf("square/go-jose: invalid EC private key, wrong length for x") |
| 690 | } |
| 691 | |
| 692 | if curveSize(curve) != len(key.Y.data) { |
| 693 | return nil, fmt.Errorf("square/go-jose: invalid EC private key, wrong length for y") |
| 694 | } |
| 695 | |
| Jacob Hoffman-Andrews | b56d11b | 2018-12-05 02:58:17 | [diff] [blame] | 696 | // https://tools.ietf.org/html/rfc7518#section-6.2.2.1 |
| 697 | if dSize(curve) != len(key.D.data) { |
| Jacob Hoffman-Andrews | e1428aa | 2018-12-03 22:02:30 | [diff] [blame] | 698 | return nil, fmt.Errorf("square/go-jose: invalid EC private key, wrong length for d") |
| 699 | } |
| 700 | |
| Cedric Staub | d163d44 | 2016-08-31 22:11:35 | [diff] [blame] | 701 | x := key.X.bigInt() |
| 702 | y := key.Y.bigInt() |
| 703 | |
| 704 | if !curve.IsOnCurve(x, y) { |
| 705 | return nil, errors.New("square/go-jose: invalid EC key, X/Y are not on declared curve") |
| 706 | } |
| 707 | |
| Robert Coie | c20b416 | 2015-03-18 05:41:54 | [diff] [blame] | 708 | return &ecdsa.PrivateKey{ |
| 709 | PublicKey: ecdsa.PublicKey{ |
| 710 | Curve: curve, |
| Cedric Staub | d163d44 | 2016-08-31 22:11:35 | [diff] [blame] | 711 | X: x, |
| 712 | Y: y, |
| Robert Coie | c20b416 | 2015-03-18 05:41:54 | [diff] [blame] | 713 | }, |
| Cedric Staub | 52b8488 | 2015-03-19 02:07:37 | [diff] [blame] | 714 | D: key.D.bigInt(), |
| Robert Coie | c20b416 | 2015-03-18 05:41:54 | [diff] [blame] | 715 | }, nil |
| Robert Coie | 4a4b7a1 | 2015-03-18 00:07:32 | [diff] [blame] | 716 | } |
| 717 | |
| Cedric Staub | d94ed21 | 2016-02-15 01:18:38 | [diff] [blame] | 718 | func fromEcPrivateKey(ec *ecdsa.PrivateKey) (*rawJSONWebKey, error) { |
| Cedric Staub | ff36cce | 2015-03-26 19:08:15 | [diff] [blame] | 719 | raw, err := fromEcPublicKey(&ec.PublicKey) |
| Cedric Staub | 97198ac | 2015-03-19 01:55:26 | [diff] [blame] | 720 | if err != nil { |
| Cedric Staub | ff36cce | 2015-03-26 19:08:15 | [diff] [blame] | 721 | return nil, err |
| Cedric Staub | 97198ac | 2015-03-19 01:55:26 | [diff] [blame] | 722 | } |
| 723 | |
| 724 | if ec.D == nil { |
| Cedric Staub | ff36cce | 2015-03-26 19:08:15 | [diff] [blame] | 725 | return nil, fmt.Errorf("square/go-jose: invalid EC private key") |
| Cedric Staub | 97198ac | 2015-03-19 01:55:26 | [diff] [blame] | 726 | } |
| 727 | |
| Jacob Hoffman-Andrews | b56d11b | 2018-12-05 02:58:17 | [diff] [blame] | 728 | raw.D = newFixedSizeBuffer(ec.D.Bytes(), dSize(ec.PublicKey.Curve)) |
| 729 | |
| 730 | return raw, nil |
| 731 | } |
| 732 | |
| 733 | // dSize returns the size in octets for the "d" member of an elliptic curve |
| 734 | // private key. |
| 735 | // The length of this octet string MUST be ceiling(log-base-2(n)/8) |
| 736 | // octets (where n is the order of the curve). |
| 737 | // https://tools.ietf.org/html/rfc7518#section-6.2.2.1 |
| 738 | func dSize(curve elliptic.Curve) int { |
| 739 | order := curve.Params().P |
| Jacob Hoffman-Andrews | 25e77d5 | 2018-12-05 02:50:33 | [diff] [blame] | 740 | bitLen := order.BitLen() |
| 741 | size := bitLen / 8 |
| 742 | if bitLen%8 != 0 { |
| 743 | size = size + 1 |
| 744 | } |
| Jacob Hoffman-Andrews | b56d11b | 2018-12-05 02:58:17 | [diff] [blame] | 745 | return size |
| Robert Coie | 4a4b7a1 | 2015-03-18 00:07:32 | [diff] [blame] | 746 | } |
| Cedric Staub | d509a11 | 2015-11-17 00:03:39 | [diff] [blame] | 747 | |
| Cedric Staub | d94ed21 | 2016-02-15 01:18:38 | [diff] [blame] | 748 | func fromSymmetricKey(key []byte) (*rawJSONWebKey, error) { |
| 749 | return &rawJSONWebKey{ |
| Cedric Staub | d509a11 | 2015-11-17 00:03:39 | [diff] [blame] | 750 | Kty: "oct", |
| 751 | K: newBuffer(key), |
| 752 | }, nil |
| 753 | } |
| 754 | |
| Cedric Staub | d94ed21 | 2016-02-15 01:18:38 | [diff] [blame] | 755 | func (key rawJSONWebKey) symmetricKey() ([]byte, error) { |
| Cedric Staub | d509a11 | 2015-11-17 00:03:39 | [diff] [blame] | 756 | if key.K == nil { |
| 757 | return nil, fmt.Errorf("square/go-jose: invalid OCT (symmetric) key, missing k value") |
| 758 | } |
| 759 | return key.K.bytes(), nil |
| 760 | } |