friendlier error reporting
This commit is contained in:
parent
fe5cf33bef
commit
5701d77d7a
|
@ -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.
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "P-256",
|
||||||
|
"x": "IT1SWLxsacPiE5Z16jkopAn8_-85rMjgyCokrnjDft4",
|
||||||
|
"y": "mP2JwOAOdMmXuwpxbKng3KZz27mz-nKWIlXJ3rzSGMo"
|
||||||
|
}
|
2
go.mod
2
go.mod
|
@ -1 +1,3 @@
|
||||||
module github.com/big-squid/go-keypairs
|
module github.com/big-squid/go-keypairs
|
||||||
|
|
||||||
|
go 1.12
|
||||||
|
|
39
keypairs.go
39
keypairs.go
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue