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

Commit

Permalink
Merge pull request #291 from square/mbyczkowski/v2-cherry-picks
Browse files Browse the repository at this point in the history
Cherry-pick v3 PRs
  • Loading branch information
mbyczkowski authored Mar 8, 2020
2 parents 4ef0f1b + 18864eb commit 2204b3e
Show file tree
Hide file tree
Showing 10 changed files with 327 additions and 43 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
*.pem
*.cov
jose-util/jose-util
jose-util.t.err
3 changes: 1 addition & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,9 @@ script:
- go test ./cipher -v -covermode=count -coverprofile=cipher/profile.cov
- go test ./jwt -v -covermode=count -coverprofile=jwt/profile.cov
- go test ./json -v # no coverage for forked encoding/json package
- cd jose-util && go build && PATH=$PWD:$PATH cram -v jose-util.t
- cd jose-util && go build && PATH=$PWD:$PATH cram -v jose-util.t # cram tests jose-util
- cd ..

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

8 changes: 8 additions & 0 deletions jose-util/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ Keys are specified via the `--key` flag. Supported key types are naked RSA/EC
keys and X.509 certificates with embedded RSA/EC keys. Keys must be in PEM
or DER formats.


## Testing

`cram` is used for testing. This can be installed with pip or `sudo apt install
python-cram` See the travis file for how this is used in testing. For example,
`go build && PATH=$PWD:$PATH cram -v jose-util.t`


## Examples

### Encrypt
Expand Down
13 changes: 8 additions & 5 deletions jose-util/jose-util.t
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
Set up test keys.
This is a cram test file. See the travis file for how this is used in testing.
For example, `go build && PATH=$PWD:$PATH cram -v jose-util.t`

Set up static test keys.

$ cat > rsa.pub <<EOF
> -----BEGIN PUBLIC KEY-----
Expand Down Expand Up @@ -61,28 +64,28 @@ Set up test keys.

Encrypt and then decrypt a test message (RSA).

$ echo "Lorem ipsum dolor sit amet" |
$ echo "Lorem ipsum dolor sit amet" |
> jose-util encrypt --alg RSA-OAEP --enc A128GCM --key rsa.pub |
> jose-util decrypt --key rsa.key
Lorem ipsum dolor sit amet

Encrypt and then decrypt a test message (EC).

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

Sign and verify a test message (RSA).

$ echo "Lorem ipsum dolor sit amet" |
$ echo "Lorem ipsum dolor sit amet" |
> jose-util sign --alg PS256 --key rsa.key |
> jose-util verify --key rsa.pub
Lorem ipsum dolor sit amet

Sign and verify a test message (EC).

$ echo "Lorem ipsum dolor sit amet" |
$ echo "Lorem ipsum dolor sit amet" |
> jose-util sign --alg ES384 --key ec.key |
> jose-util verify --key ec.pub
Lorem ipsum dolor sit amet
Expand Down
61 changes: 44 additions & 17 deletions jwk-keygen/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@ import (
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"encoding/base32"
"encoding/base64"
"errors"
"fmt"
"golang.org/x/crypto/ed25519"
"io"
"os"

"golang.org/x/crypto/ed25519"

"gopkg.in/alecthomas/kingpin.v2"
"gopkg.in/square/go-jose.v2"
)
Expand Down Expand Up @@ -75,20 +76,32 @@ func KeygenSig(alg jose.SignatureAlgorithm, bits int) (crypto.PublicKey, crypto.
case jose.ES256:
// The cryptographic operations are implemented using constant-time algorithms.
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, nil, err
}
return key.Public(), key, err
case jose.ES384:
// NB: The cryptographic operations do not use constant-time algorithms.
key, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
if err != nil {
return nil, nil, err
}
return key.Public(), key, err
case jose.ES512:
// NB: The cryptographic operations do not use constant-time algorithms.
key, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
if err != nil {
return nil, nil, err
}
return key.Public(), key, err
case jose.EdDSA:
pub, key, err := ed25519.GenerateKey(rand.Reader)
return pub, key, err
case jose.RS256, jose.RS384, jose.RS512, jose.PS256, jose.PS384, jose.PS512:
key, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
return nil, nil, err
}
return key.Public(), key, err
default:
return nil, nil, errors.New("unknown `alg` for `use` = `sig`")
Expand All @@ -106,6 +119,9 @@ func KeygenEnc(alg jose.KeyAlgorithm, bits int) (crypto.PublicKey, crypto.Privat
return nil, nil, errors.New("too short key for RSA `alg`, 2048+ is required")
}
key, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
return nil, nil, err
}
return key.Public(), key, err
case jose.ECDH_ES, jose.ECDH_ES_A128KW, jose.ECDH_ES_A192KW, jose.ECDH_ES_A256KW:
var crv elliptic.Curve
Expand All @@ -120,6 +136,9 @@ func KeygenEnc(alg jose.KeyAlgorithm, bits int) (crypto.PublicKey, crypto.Privat
return nil, nil, errors.New("unknown elliptic curve bit length, use one of 256, 384, 521")
}
key, err := ecdsa.GenerateKey(crv, rand.Reader)
if err != nil {
return nil, nil, err
}
return key.Public(), key, err
default:
return nil, nil, errors.New("unknown `alg` for `use` = `enc`")
Expand All @@ -130,29 +149,39 @@ func main() {
app.Version("v2")
kingpin.MustParse(app.Parse(os.Args[1:]))

if *kidRand {
if *kid == "" {
b := make([]byte, 5)
_, err := rand.Read(b)
app.FatalIfError(err, "can't Read() crypto/rand")
*kid = base32.StdEncoding.EncodeToString(b)
} else {
app.FatalUsage("can't combine --kid and --kid-rand")
}
}

var privKey crypto.PublicKey
var pubKey crypto.PrivateKey
var privKey crypto.PrivateKey
var pubKey crypto.PublicKey
var err error
switch *use {
case "sig":
pubKey, privKey, err = KeygenSig(jose.SignatureAlgorithm(*alg), *bits)
case "enc":
pubKey, privKey, err = KeygenEnc(jose.KeyAlgorithm(*alg), *bits)
default:
// According to RFC 7517 section-8.2. This is unlikely to change in the
// near future. If it were, new values could be found in the registry under
// "JSON Web Key Use": https://www.iana.org/assignments/jose/jose.xhtml
app.FatalIfError(errors.New("invalid key use. Must be \"sig\" or \"enc\""), "unable to generate key")
}
app.FatalIfError(err, "unable to generate key")

priv := jose.JSONWebKey{Key: privKey, KeyID: *kid, Algorithm: *alg, Use: *use}

if *kidRand {
// Generate a canonical kid based on RFC 7638
if *kid == "" {
thumb, err := priv.Thumbprint(crypto.SHA256)
app.FatalIfError(err, "unable to compute thumbprint")
*kid = base64.URLEncoding.EncodeToString(thumb)
priv.KeyID = *kid
} else {
app.FatalUsage("can't combine --kid and --kid-rand")
}
}

// I'm not sure why we couldn't use `pub := priv.Public()` here as the private
// key should contain the public key. In case for some reason it doesn't,
// this builds a public JWK from scratch.
pub := jose.JSONWebKey{Key: pubKey, KeyID: *kid, Algorithm: *alg, Use: *use}

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

0 comments on commit 2204b3e

Please sign in to comment.