Skip to content

Commit

Permalink
Merge pull request #403 from fuller-inc/fix-base64-error-handling
Browse files Browse the repository at this point in the history
fix base64 error handling
  • Loading branch information
shogo82148 authored Aug 17, 2022
2 parents 19ce1a6 + 5f5cbb4 commit c6f0c33
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 29 deletions.
21 changes: 19 additions & 2 deletions provider/assume-role/github/jwk/base64.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,25 @@ func (ctx *base64Context) decode(s string, name string) []byte {
src := ctx.src[:len(s)]
copy(src, s)
n, err := base64.RawURLEncoding.Decode(ctx.dst, src)
if err != nil && ctx.err != nil {
ctx.err = fmt.Errorf("jwk: failed to parse the parameter %s: %w", name, err)
if err != nil && ctx.err == nil {
ctx.err = &base64DecodeError{
name: name,
err: err,
}
}
return ctx.dst[:n]
}

type base64DecodeError struct {
name string
err error
}

// Error implements the error interface.
func (err *base64DecodeError) Error() string {
return fmt.Sprintf("jwk: failed to parse the parameter %s: %v", err.name, err.err)
}

func (err *base64DecodeError) Unwrap() error {
return err.err
}
6 changes: 3 additions & 3 deletions provider/assume-role/github/jwk/ecdsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ type ecdsaPrivateKey struct {
privateKey ecdsa.PrivateKey
}

func (key *ecdsaPrivateKey) PrivateKey() interface{} {
func (key *ecdsaPrivateKey) PrivateKey() any {
return &key.privateKey
}

func (key *ecdsaPrivateKey) PublicKey() interface{} {
func (key *ecdsaPrivateKey) PublicKey() any {
return &key.privateKey.PublicKey
}

Expand Down Expand Up @@ -116,7 +116,7 @@ type ecdsaPublicKey struct {
publicKey ecdsa.PublicKey
}

func (key *ecdsaPublicKey) PublicKey() interface{} {
func (key *ecdsaPublicKey) PublicKey() any {
return &key.publicKey
}

Expand Down
24 changes: 16 additions & 8 deletions provider/assume-role/github/jwk/ed25519.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,29 +51,34 @@ func parseEd25519PrivateKey(data []byte) (Key, error) {
return &key, nil
}

func (key *ed25519PrivateKey) PrivateKey() interface{} {
func (key *ed25519PrivateKey) PrivateKey() any {
return key.privateKey
}

func (key *ed25519PrivateKey) PublicKey() interface{} {
func (key *ed25519PrivateKey) PublicKey() any {
return key.publicKey
}

func (key *ed25519PrivateKey) decode() error {
const keySize = ed25519.PrivateKeySize - ed25519.PublicKeySize
ctx := key.getContext()

privateKey := make([]byte, ed25519.PrivateKeySize)
data := ctx.decode(key.D, "d")
if len(data) != ed25519.PrivateKeySize-ed25519.PublicKeySize {
if ctx.err != nil {
return ctx.err
}
if copy(privateKey, data) != keySize {
return fmt.Errorf("jwk: the parameter d has invalid size")
}
copy(privateKey, data)

publicKey := ctx.decode(key.X, "x")
if len(publicKey) != ed25519.PublicKeySize {
if ctx.err != nil {
return ctx.err
}
if copy(privateKey[keySize:], publicKey) != ed25519.PublicKeySize {
return fmt.Errorf("jwk: the parameter x has invalid size")
}
copy(privateKey[32:], publicKey)

key.publicKey = ed25519.PublicKey(publicKey)
key.privateKey = ed25519.PrivateKey(privateKey)
Expand Down Expand Up @@ -103,7 +108,7 @@ type ed25519PublicKey struct {
publicKey ed25519.PublicKey
}

func (key *ed25519PublicKey) PublicKey() interface{} {
func (key *ed25519PublicKey) PublicKey() any {
return key.publicKey
}

Expand Down Expand Up @@ -138,11 +143,14 @@ func parseEd25519PublicKey(data []byte) (Key, error) {
func (key *ed25519PublicKey) decode() error {
ctx := key.getContext()
data := ctx.decode(key.X, "x")
if ctx.err != nil {
return ctx.err
}
if len(data) != ed25519.PublicKeySize {
return fmt.Errorf("jwk: the parameter x has invalid size")
}
key.publicKey = ed25519.PublicKey(data)
return ctx.err
return nil
}

func (key *ed25519PublicKey) getContext() base64Context {
Expand Down
8 changes: 4 additions & 4 deletions provider/assume-role/github/jwk/jwk.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ type Key interface {

// PrivateKey returns the private key.
// If the key doesn't contain any private key, it returns nil.
PrivateKey() interface{}
PrivateKey() any

// PublicKey returns the public key.
// If the key doesn't contain any public key, it returns nil.
PublicKey() interface{}
PublicKey() any
}

type commonKey struct {
Expand Down Expand Up @@ -115,11 +115,11 @@ func (key *commonKey) X509CertificateSHA256() string {
return key.X5tS256
}

func (key *commonKey) PrivateKey() interface{} {
func (key *commonKey) PrivateKey() any {
return nil
}

func (key *commonKey) PublicKey() interface{} {
func (key *commonKey) PublicKey() any {
return nil
}

Expand Down
111 changes: 111 additions & 0 deletions provider/assume-role/github/jwk/jwk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"crypto/ed25519"
"crypto/elliptic"
"crypto/rsa"
"errors"
"reflect"
"testing"
)
Expand Down Expand Up @@ -479,3 +480,113 @@ func BenchmarkKey_RFC8037AppendixA(b *testing.B) {
}
})
}

func TestKey_Base64Error(t *testing.T) {
t.Run("EC Private Key", func(t *testing.T) {
rawKey := `{"kty":"EC",` +
`"crv":"P-256",` +
`"x":"!!!INVALID BASE64!!!",` +
`"y":"!!!INVALID BASE64!!!",` +
`"use":"enc",` +
`"kid":"1"}`
var e *base64DecodeError
_, err := ParseKey([]byte(rawKey))
if !errors.As(err, &e) {
t.Errorf("want *base64DecodeError, got %T", err)
}
if e.err == nil {
t.Error("want not nil, got nil")
}
if e.name != "x" && e.name != "y" {
t.Errorf("want name is x or y, got %s", e.name)
}
})

t.Run("RFC 7517 A.2. Example Private Keys (EC)", func(t *testing.T) {
rawKey := `{"kty":"EC",` +
`"crv":"P-256",` +
`"x":"!!!INVALID BASE64!!!",` +
`"y":"!!!INVALID BASE64!!!",` +
`"d":"!!!INVALID BASE64!!!",` +
`"use":"enc",` +
`"kid":"1"}`
var e *base64DecodeError
_, err := ParseKey([]byte(rawKey))
if !errors.As(err, &e) {
t.Errorf("want *base64DecodeError, got %T", err)
}
if e.err == nil {
t.Error("want not nil, got nil")
}
if e.name != "x" && e.name != "y" && e.name != "d" {
t.Errorf("want name is x or y or d, got %s", e.name)
}
})

t.Run("RSA Public Key", func(t *testing.T) {
rawKey := `{"kty":"RSA",` +
`"n":"!!!INVALID BASE64!!!",` +
`"e":"!!!INVALID BASE64!!!",` +
`"alg":"RS256",` +
`"kid":"2011-04-29"}`
var e *base64DecodeError
_, err := ParseKey([]byte(rawKey))
if !errors.As(err, &e) {
t.Errorf("want *base64DecodeError, got %T", err)
}
if e.err == nil {
t.Error("want not nil, got nil")
}
if e.name != "n" && e.name != "e" {
t.Errorf("want name is n or e, got %s", e.name)
}
})

t.Run("Symmetric Keys (HMAC)", func(t *testing.T) {
rawKey := `{"kty":"oct","k":"!!!INVALID BASE64!!!"}`
var e *base64DecodeError
_, err := ParseKey([]byte(rawKey))
if !errors.As(err, &e) {
t.Errorf("want *base64DecodeError, got %T", err)
}
if e.err == nil {
t.Error("want not nil, got nil")
}
if e.name != "k" {
t.Errorf("want name is k, got %s", e.name)
}
})

t.Run("Ed25519 Public Key", func(t *testing.T) {
rawKey := `{"kty":"OKP","crv":"Ed25519",` +
`"x":"!!!INVALID BASE64!!!"}`
var e *base64DecodeError
_, err := ParseKey([]byte(rawKey))
if !errors.As(err, &e) {
t.Errorf("want *base64DecodeError, got %T", err)
}
if e.err == nil {
t.Error("want not nil, got nil")
}
if e.name != "x" {
t.Errorf("want name is x, got %s", e.name)
}
})

t.Run("Ed25519 Private Key", func(t *testing.T) {
rawKey := `{"kty":"OKP","crv":"Ed25519",` +
`"d":"!!!INVALID BASE64!!!",` +
`"x":"!!!INVALID BASE64!!!"}`
var e *base64DecodeError
_, err := ParseKey([]byte(rawKey))
if !errors.As(err, &e) {
t.Errorf("want *base64DecodeError, got %T", err)
}
if e.err == nil {
t.Error("want not nil, got nil")
}
if e.name != "d" && e.name != "x" {
t.Errorf("want name is d or x, got %s", e.name)
}
})
}
6 changes: 3 additions & 3 deletions provider/assume-role/github/jwk/rsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,11 @@ func parseRSAPrivateKey(data []byte) (Key, error) {
return &key, nil
}

func (key *rsaPrivateKey) PrivateKey() interface{} {
func (key *rsaPrivateKey) PrivateKey() any {
return &key.privateKey
}

func (key *rsaPrivateKey) PublicKey() interface{} {
func (key *rsaPrivateKey) PublicKey() any {
return &key.privateKey.PublicKey
}

Expand Down Expand Up @@ -192,7 +192,7 @@ type rsaPublicKey struct {
publicKey rsa.PublicKey
}

func (key *rsaPublicKey) PublicKey() interface{} {
func (key *rsaPublicKey) PublicKey() any {
return &key.publicKey
}

Expand Down
16 changes: 7 additions & 9 deletions provider/assume-role/github/jwk/symmetric.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package jwk

import (
"encoding/base64"
"encoding/json"
"fmt"
)

// RFC7518 6.4. Parameters for Symmetric Keys
Expand All @@ -30,17 +28,17 @@ func parseSymmetricKey(data []byte) (Key, error) {
return &key, nil
}

func (key *symmetricKey) PrivateKey() interface{} {
func (key *symmetricKey) PrivateKey() any {
return key.key
}

// decode decodes the encoded values into publicKey.
func (key *symmetricKey) decode() error {
k, err := base64.RawURLEncoding.DecodeString(key.K)
if err != nil {
return fmt.Errorf("jwk: failed to parse parameter k: %w", err)
}
key.key = k
ctx := key.getContext()
key.key = ctx.decode(key.K, "k")
return ctx.err
}

return nil
func (key *symmetricKey) getContext() base64Context {
return newBase64Context(len(key.K))
}

0 comments on commit c6f0c33

Please sign in to comment.