friendlier error reporting

This commit is contained in:
AJ ONeal 2019-07-11 10:59:19 -06:00
parent fe5cf33bef
commit 5701d77d7a
5 changed files with 90 additions and 8 deletions

View File

@ -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/)) (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've read enough source code to know that, for standards I know well,
I feel much more confident in the security, extensibility, and documentation I feel much more confident in the security, extensibility, and documentation
of tooling that I've write myself. of tooling that I've write myself.

View File

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

2
go.mod
View File

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

View File

@ -1,6 +1,7 @@
package keypairs package keypairs
import ( import (
"bytes"
"crypto" "crypto"
"crypto/dsa" "crypto/dsa"
"crypto/ecdsa" "crypto/ecdsa"
@ -16,6 +17,7 @@ import (
"io" "io"
"log" "log"
"math/big" "math/big"
"strings"
"time" "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 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 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 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." 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 // If we didn't parse a key arleady, we failed
return nil, ErrParsePrivateKey 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 // If we didn't parse a key arleady, we failed
return nil, ErrParsePublicKey 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 // ParseJWKPublicKey parses a JSON-encoded JWK and returns a PublicKey, or a (hopefully) helpful error message
func ParseJWKPublicKey(b []byte) (PublicKey, error) { 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) return newJWKPublicKey(b)
} }
// ParseJWKPublicKeyString calls ParseJWKPublicKey([]byte(key)) for all you lazy folk. // ParseJWKPublicKeyString calls ParseJWKPublicKey([]byte(key)) for all you lazy folk.
func ParseJWKPublicKeyString(s string) (PublicKey, error) { func ParseJWKPublicKeyString(s string) (PublicKey, error) {
if strings.Contains(s, `"d"`) {
return nil, ErrUnexpectedPrivateKey
}
return newJWKPublicKey(s) return newJWKPublicKey(s)
} }
// DecodeJWKPublicKey stream-decodes a JSON-encoded JWK and returns a PublicKey, or a (hopefully) helpful error message // DecodeJWKPublicKey stream-decodes a JSON-encoded JWK and returns a PublicKey, or a (hopefully) helpful error message
func DecodeJWKPublicKey(r io.Reader) (PublicKey, error) { 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 // 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) { switch d := data.(type) {
case map[string]string: case map[string]string:
m = d m = d
case io.Reader:
m = make(map[string]string)
if err := json.NewDecoder(d).Decode(&m); nil != err {
return nil, err
}
case string: case string:
if err := json.Unmarshal([]byte(d), &m); nil != err { if err := json.Unmarshal([]byte(d), &m); nil != err {
return nil, err return nil, err

View File

@ -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) { func TestParsePrivateKeyRSA(t *testing.T) {
keypaths := []string{ keypaths := []string{
"fixtures/privkey-rsa-2048.jwk.json", "fixtures/privkey-rsa-2048.jwk.json",
@ -71,7 +118,7 @@ func TestParsePrivateKeyRSA(t *testing.T) {
} }
func TestParseCertificate(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 { if nil != err {
log.Fatal(err) log.Fatal(err)
} }