Browse Source

friendlier error reporting

license v0.5.0
AJ ONeal 5 years ago
parent
commit
5701d77d7a
  1. 2
      README.md
  2. 6
      fixtures/pub-ec-p256.jwk.json
  3. 2
      go.mod
  4. 39
      keypairs.go
  5. 49
      keypairs_test.go

2
README.md

@ -23,7 +23,7 @@ Don't get the two mixed up!
(furthermore, [just because you _do_ know someone doesn't make them _not_ smart](https://www.humancondition.com/asid-prophets-without-honour-in-their-own-home/))
Although I would not want to invent my own cryptographic algorithm,
Although I would **not** want to invent my own cryptographic algorithm,
I've read enough source code to know that, for standards I know well,
I feel much more confident in the security, extensibility, and documentation
of tooling that I've write myself.

6
fixtures/pub-ec-p256.jwk.json

@ -0,0 +1,6 @@
{
"kty": "EC",
"crv": "P-256",
"x": "IT1SWLxsacPiE5Z16jkopAn8_-85rMjgyCokrnjDft4",
"y": "mP2JwOAOdMmXuwpxbKng3KZz27mz-nKWIlXJ3rzSGMo"
}

2
go.mod

@ -1 +1,3 @@
module github.com/big-squid/go-keypairs
go 1.12

39
keypairs.go

@ -1,6 +1,7 @@
package keypairs
import (
"bytes"
"crypto"
"crypto/dsa"
"crypto/ecdsa"
@ -16,6 +17,7 @@ import (
"io"
"log"
"math/big"
"strings"
"time"
)
@ -26,6 +28,8 @@ var ErrParsePrivateKey = errors.New("PrivateKey bytes could not be parsed as PEM
var ErrParseJWK = errors.New("JWK is missing required base64-encoded JSON fields")
var ErrInvalidKeyType = errors.New("The JWK's 'kty' must be either 'RSA' or 'EC'")
var ErrInvalidCurve = errors.New("The JWK's 'crv' must be either of the NIST standards 'P-256' or 'P-384'")
var ErrUnexpectedPublicKey = errors.New("PrivateKey was given where PublicKey was expected")
var ErrUnexpectedPrivateKey = errors.New("PublicKey was given where PrivateKey was expected")
const ErrDevSwapPrivatePublic = "[Developer Error] You passed either crypto.PrivateKey or crypto.PublicKey where the other was expected."
@ -239,6 +243,13 @@ func ParsePrivateKey(block []byte) (PrivateKey, error) {
}
}
for i := range blocks {
block = blocks[i]
if _, err := parsePublicKey(block); nil == err {
return nil, ErrUnexpectedPublicKey
}
}
// If we didn't parse a key arleady, we failed
return nil, ErrParsePrivateKey
}
@ -331,6 +342,13 @@ func ParsePublicKey(block []byte) (PublicKey, error) {
}
}
for i := range blocks {
block = blocks[i]
if _, err := parsePrivateKey(block); nil == err {
return nil, ErrUnexpectedPrivateKey
}
}
// If we didn't parse a key arleady, we failed
return nil, ErrParsePublicKey
}
@ -399,17 +417,31 @@ func NewJWKPublicKey(m map[string]string) (PublicKey, error) {
// ParseJWKPublicKey parses a JSON-encoded JWK and returns a PublicKey, or a (hopefully) helpful error message
func ParseJWKPublicKey(b []byte) (PublicKey, error) {
// RSA and EC have "d" as a private part
if bytes.Contains(b, []byte(`"d"`)) {
return nil, ErrUnexpectedPrivateKey
}
return newJWKPublicKey(b)
}
// ParseJWKPublicKeyString calls ParseJWKPublicKey([]byte(key)) for all you lazy folk.
func ParseJWKPublicKeyString(s string) (PublicKey, error) {
if strings.Contains(s, `"d"`) {
return nil, ErrUnexpectedPrivateKey
}
return newJWKPublicKey(s)
}
// DecodeJWKPublicKey stream-decodes a JSON-encoded JWK and returns a PublicKey, or a (hopefully) helpful error message
func DecodeJWKPublicKey(r io.Reader) (PublicKey, error) {
return newJWKPublicKey(r)
m := make(map[string]string)
if err := json.NewDecoder(r).Decode(&m); nil != err {
return nil, err
}
if d := m["d"]; "" != d {
return nil, ErrUnexpectedPrivateKey
}
return newJWKPublicKey(m)
}
// the underpinnings of the parser as used by the typesafe wrappers
@ -419,11 +451,6 @@ func newJWKPublicKey(data interface{}) (PublicKey, error) {
switch d := data.(type) {
case map[string]string:
m = d
case io.Reader:
m = make(map[string]string)
if err := json.NewDecoder(d).Decode(&m); nil != err {
return nil, err
}
case string:
if err := json.Unmarshal([]byte(d), &m); nil != err {
return nil, err

49
keypairs_test.go

@ -43,6 +43,53 @@ func TestParsePrivateKeyEC(t *testing.T) {
}
}
func TestParseUnexpectedPrivateKey(t *testing.T) {
keypaths := []string{
"fixtures/privkey-ec-p256.jwk.json",
"fixtures/privkey-ec-p256.sec1.pem",
"fixtures/privkey-ec-p256.pkcs8.pem",
"fixtures/privkey-rsa-2048.jwk.json",
"fixtures/privkey-rsa-2048.pkcs1.pem",
"fixtures/privkey-rsa-2048.pkcs8.pem",
}
for i := range keypaths {
path := keypaths[i]
b, err := ioutil.ReadFile(path)
if nil != err {
t.Fatal(path, err)
}
_, err = ParsePublicKey(b)
switch err {
case ErrUnexpectedPrivateKey:
continue
default:
t.Fatal(path, err)
}
}
}
func TestParseUnexpectedPublicKey(t *testing.T) {
keypaths := []string{
"fixtures/pub-ec-p256.jwk.json",
}
for i := range keypaths {
path := keypaths[i]
b, err := ioutil.ReadFile(path)
if nil != err {
t.Fatal(path, err)
}
_, err = ParsePrivateKey(b)
switch err {
case ErrUnexpectedPublicKey:
continue
default:
t.Fatal(path, err)
}
}
}
func TestParsePrivateKeyRSA(t *testing.T) {
keypaths := []string{
"fixtures/privkey-rsa-2048.jwk.json",
@ -71,7 +118,7 @@ func TestParsePrivateKeyRSA(t *testing.T) {
}
func TestParseCertificate(t *testing.T) {
resp, err := http.Get("http://bigsquid.auth0.com/pem")
resp, err := http.Get("https://example.auth0.com/pem")
if nil != err {
log.Fatal(err)
}

Loading…
Cancel
Save