Skip to content
This repository was archived by the owner on Feb 27, 2023. It is now read-only.

Commit 2204b3e

Browse files
authored
Merge pull request #291 from square/mbyczkowski/v2-cherry-picks
Cherry-pick v3 PRs
2 parents 4ef0f1b + 18864eb commit 2204b3e

File tree

10 files changed

+327
-43
lines changed

10 files changed

+327
-43
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@
55
*.pem
66
*.cov
77
jose-util/jose-util
8+
jose-util.t.err

.travis.yml

+1-2
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,9 @@ script:
3535
- go test ./cipher -v -covermode=count -coverprofile=cipher/profile.cov
3636
- go test ./jwt -v -covermode=count -coverprofile=jwt/profile.cov
3737
- go test ./json -v # no coverage for forked encoding/json package
38-
- cd jose-util && go build && PATH=$PWD:$PATH cram -v jose-util.t
38+
- cd jose-util && go build && PATH=$PWD:$PATH cram -v jose-util.t # cram tests jose-util
3939
- cd ..
4040

4141
after_success:
4242
- gocovmerge *.cov */*.cov > merged.coverprofile
4343
- $HOME/gopath/bin/goveralls -coverprofile merged.coverprofile -service=travis-ci
44-

jose-util/README.md

+8
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ Keys are specified via the `--key` flag. Supported key types are naked RSA/EC
2525
keys and X.509 certificates with embedded RSA/EC keys. Keys must be in PEM
2626
or DER formats.
2727

28+
29+
## Testing
30+
31+
`cram` is used for testing. This can be installed with pip or `sudo apt install
32+
python-cram` See the travis file for how this is used in testing. For example,
33+
`go build && PATH=$PWD:$PATH cram -v jose-util.t`
34+
35+
2836
## Examples
2937

3038
### Encrypt

jose-util/jose-util.t

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
Set up test keys.
1+
This is a cram test file. See the travis file for how this is used in testing.
2+
For example, `go build && PATH=$PWD:$PATH cram -v jose-util.t`
3+
4+
Set up static test keys.
25

36
$ cat > rsa.pub <<EOF
47
> -----BEGIN PUBLIC KEY-----
@@ -61,28 +64,28 @@ Set up test keys.
6164

6265
Encrypt and then decrypt a test message (RSA).
6366

64-
$ echo "Lorem ipsum dolor sit amet" |
67+
$ echo "Lorem ipsum dolor sit amet" |
6568
> jose-util encrypt --alg RSA-OAEP --enc A128GCM --key rsa.pub |
6669
> jose-util decrypt --key rsa.key
6770
Lorem ipsum dolor sit amet
6871

6972
Encrypt and then decrypt a test message (EC).
7073

71-
$ echo "Lorem ipsum dolor sit amet" |
74+
$ echo "Lorem ipsum dolor sit amet" |
7275
> jose-util encrypt --alg ECDH-ES+A128KW --enc A128GCM --key ec.pub |
7376
> jose-util decrypt --key ec.key
7477
Lorem ipsum dolor sit amet
7578

7679
Sign and verify a test message (RSA).
7780

78-
$ echo "Lorem ipsum dolor sit amet" |
81+
$ echo "Lorem ipsum dolor sit amet" |
7982
> jose-util sign --alg PS256 --key rsa.key |
8083
> jose-util verify --key rsa.pub
8184
Lorem ipsum dolor sit amet
8285

8386
Sign and verify a test message (EC).
8487

85-
$ echo "Lorem ipsum dolor sit amet" |
88+
$ echo "Lorem ipsum dolor sit amet" |
8689
> jose-util sign --alg ES384 --key ec.key |
8790
> jose-util verify --key ec.pub
8891
Lorem ipsum dolor sit amet

jwk-keygen/main.go

+44-17
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@ import (
2222
"crypto/elliptic"
2323
"crypto/rand"
2424
"crypto/rsa"
25-
"encoding/base32"
25+
"encoding/base64"
2626
"errors"
2727
"fmt"
28-
"golang.org/x/crypto/ed25519"
2928
"io"
3029
"os"
3130

31+
"golang.org/x/crypto/ed25519"
32+
3233
"gopkg.in/alecthomas/kingpin.v2"
3334
"gopkg.in/square/go-jose.v2"
3435
)
@@ -75,20 +76,32 @@ func KeygenSig(alg jose.SignatureAlgorithm, bits int) (crypto.PublicKey, crypto.
7576
case jose.ES256:
7677
// The cryptographic operations are implemented using constant-time algorithms.
7778
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
79+
if err != nil {
80+
return nil, nil, err
81+
}
7882
return key.Public(), key, err
7983
case jose.ES384:
8084
// NB: The cryptographic operations do not use constant-time algorithms.
8185
key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
86+
if err != nil {
87+
return nil, nil, err
88+
}
8289
return key.Public(), key, err
8390
case jose.ES512:
8491
// NB: The cryptographic operations do not use constant-time algorithms.
8592
key, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
93+
if err != nil {
94+
return nil, nil, err
95+
}
8696
return key.Public(), key, err
8797
case jose.EdDSA:
8898
pub, key, err := ed25519.GenerateKey(rand.Reader)
8999
return pub, key, err
90100
case jose.RS256, jose.RS384, jose.RS512, jose.PS256, jose.PS384, jose.PS512:
91101
key, err := rsa.GenerateKey(rand.Reader, bits)
102+
if err != nil {
103+
return nil, nil, err
104+
}
92105
return key.Public(), key, err
93106
default:
94107
return nil, nil, errors.New("unknown `alg` for `use` = `sig`")
@@ -106,6 +119,9 @@ func KeygenEnc(alg jose.KeyAlgorithm, bits int) (crypto.PublicKey, crypto.Privat
106119
return nil, nil, errors.New("too short key for RSA `alg`, 2048+ is required")
107120
}
108121
key, err := rsa.GenerateKey(rand.Reader, bits)
122+
if err != nil {
123+
return nil, nil, err
124+
}
109125
return key.Public(), key, err
110126
case jose.ECDH_ES, jose.ECDH_ES_A128KW, jose.ECDH_ES_A192KW, jose.ECDH_ES_A256KW:
111127
var crv elliptic.Curve
@@ -120,6 +136,9 @@ func KeygenEnc(alg jose.KeyAlgorithm, bits int) (crypto.PublicKey, crypto.Privat
120136
return nil, nil, errors.New("unknown elliptic curve bit length, use one of 256, 384, 521")
121137
}
122138
key, err := ecdsa.GenerateKey(crv, rand.Reader)
139+
if err != nil {
140+
return nil, nil, err
141+
}
123142
return key.Public(), key, err
124143
default:
125144
return nil, nil, errors.New("unknown `alg` for `use` = `enc`")
@@ -130,29 +149,39 @@ func main() {
130149
app.Version("v2")
131150
kingpin.MustParse(app.Parse(os.Args[1:]))
132151

133-
if *kidRand {
134-
if *kid == "" {
135-
b := make([]byte, 5)
136-
_, err := rand.Read(b)
137-
app.FatalIfError(err, "can't Read() crypto/rand")
138-
*kid = base32.StdEncoding.EncodeToString(b)
139-
} else {
140-
app.FatalUsage("can't combine --kid and --kid-rand")
141-
}
142-
}
143-
144-
var privKey crypto.PublicKey
145-
var pubKey crypto.PrivateKey
152+
var privKey crypto.PrivateKey
153+
var pubKey crypto.PublicKey
146154
var err error
147155
switch *use {
148156
case "sig":
149157
pubKey, privKey, err = KeygenSig(jose.SignatureAlgorithm(*alg), *bits)
150158
case "enc":
151159
pubKey, privKey, err = KeygenEnc(jose.KeyAlgorithm(*alg), *bits)
160+
default:
161+
// According to RFC 7517 section-8.2. This is unlikely to change in the
162+
// near future. If it were, new values could be found in the registry under
163+
// "JSON Web Key Use": https://www.iana.org/assignments/jose/jose.xhtml
164+
app.FatalIfError(errors.New("invalid key use. Must be \"sig\" or \"enc\""), "unable to generate key")
152165
}
153166
app.FatalIfError(err, "unable to generate key")
154167

155168
priv := jose.JSONWebKey{Key: privKey, KeyID: *kid, Algorithm: *alg, Use: *use}
169+
170+
if *kidRand {
171+
// Generate a canonical kid based on RFC 7638
172+
if *kid == "" {
173+
thumb, err := priv.Thumbprint(crypto.SHA256)
174+
app.FatalIfError(err, "unable to compute thumbprint")
175+
*kid = base64.URLEncoding.EncodeToString(thumb)
176+
priv.KeyID = *kid
177+
} else {
178+
app.FatalUsage("can't combine --kid and --kid-rand")
179+
}
180+
}
181+
182+
// I'm not sure why we couldn't use `pub := priv.Public()` here as the private
183+
// key should contain the public key. In case for some reason it doesn't,
184+
// this builds a public JWK from scratch.
156185
pub := jose.JSONWebKey{Key: pubKey, KeyID: *kid, Algorithm: *alg, Use: *use}
157186

158187
if priv.IsPublic() || !pub.IsPublic() || !priv.Valid() || !pub.Valid() {
@@ -170,8 +199,6 @@ func main() {
170199
fmt.Printf("==> jwk_%s <==\n", *alg)
171200
fmt.Println(string(privJS))
172201
} else {
173-
// JWK Thumbprint (RFC7638) is not used for key id because of
174-
// lack of canonical representation.
175202
fname := fmt.Sprintf("jwk_%s_%s_%s", *use, *alg, *kid)
176203
err = writeNewFile(fname+".pub", pubJS, 0444)
177204
app.FatalIfError(err, "can't write public key to file %s.pub", fname)

0 commit comments

Comments
 (0)