Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix base64 error handling #403

Merged
merged 1 commit into from
Aug 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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))
}