...

Source file src/github.com/lestrrat-go/jwx/internal/jwxtest/jwxtest.go

Documentation: github.com/lestrrat-go/jwx/internal/jwxtest

     1  package jwxtest
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto/ecdsa"
     7  	"crypto/ed25519"
     8  	"crypto/elliptic"
     9  	"crypto/rand"
    10  	"crypto/rsa"
    11  	"encoding/json"
    12  	"io"
    13  	"io/ioutil"
    14  	"os"
    15  	"strings"
    16  	"testing"
    17  
    18  	"github.com/lestrrat-go/jwx/internal/ecutil"
    19  	"github.com/lestrrat-go/jwx/jwa"
    20  	"github.com/lestrrat-go/jwx/jwe"
    21  	"github.com/lestrrat-go/jwx/jwk"
    22  	"github.com/lestrrat-go/jwx/jws"
    23  	"github.com/lestrrat-go/jwx/x25519"
    24  	"github.com/pkg/errors"
    25  	"github.com/stretchr/testify/assert"
    26  )
    27  
    28  func GenerateRsaKey() (*rsa.PrivateKey, error) {
    29  	return rsa.GenerateKey(rand.Reader, 2048)
    30  }
    31  
    32  func GenerateRsaJwk() (jwk.Key, error) {
    33  	key, err := GenerateRsaKey()
    34  	if err != nil {
    35  		return nil, errors.Wrap(err, `failed to generate RSA private key`)
    36  	}
    37  
    38  	k, err := jwk.New(key)
    39  	if err != nil {
    40  		return nil, errors.Wrap(err, `failed to generate jwk.RSAPrivateKey`)
    41  	}
    42  
    43  	return k, nil
    44  }
    45  
    46  func GenerateRsaPublicJwk() (jwk.Key, error) {
    47  	key, err := GenerateRsaJwk()
    48  	if err != nil {
    49  		return nil, errors.Wrap(err, `failed to generate jwk.RSAPrivateKey`)
    50  	}
    51  
    52  	return jwk.PublicKeyOf(key)
    53  }
    54  
    55  func GenerateEcdsaKey(alg jwa.EllipticCurveAlgorithm) (*ecdsa.PrivateKey, error) {
    56  	var crv elliptic.Curve
    57  	if tmp, ok := ecutil.CurveForAlgorithm(alg); ok {
    58  		crv = tmp
    59  	} else {
    60  		return nil, errors.Errorf(`invalid curve algorithm %s`, alg)
    61  	}
    62  
    63  	return ecdsa.GenerateKey(crv, rand.Reader)
    64  }
    65  
    66  func GenerateEcdsaJwk() (jwk.Key, error) {
    67  	key, err := GenerateEcdsaKey(jwa.P521)
    68  	if err != nil {
    69  		return nil, errors.Wrap(err, `failed to generate ECDSA private key`)
    70  	}
    71  
    72  	k, err := jwk.New(key)
    73  	if err != nil {
    74  		return nil, errors.Wrap(err, `failed to generate jwk.ECDSAPrivateKey`)
    75  	}
    76  
    77  	return k, nil
    78  }
    79  
    80  func GenerateEcdsaPublicJwk() (jwk.Key, error) {
    81  	key, err := GenerateEcdsaJwk()
    82  	if err != nil {
    83  		return nil, errors.Wrap(err, `failed to generate jwk.ECDSAPrivateKey`)
    84  	}
    85  
    86  	return jwk.PublicKeyOf(key)
    87  }
    88  
    89  func GenerateSymmetricKey() []byte {
    90  	sharedKey := make([]byte, 64)
    91  	rand.Read(sharedKey)
    92  	return sharedKey
    93  }
    94  
    95  func GenerateSymmetricJwk() (jwk.Key, error) {
    96  	key, err := jwk.New(GenerateSymmetricKey())
    97  	if err != nil {
    98  		return nil, errors.Wrap(err, `failed to generate jwk.SymmetricKey`)
    99  	}
   100  
   101  	return key, nil
   102  }
   103  
   104  func GenerateEd25519Key() (ed25519.PrivateKey, error) {
   105  	_, priv, err := ed25519.GenerateKey(rand.Reader)
   106  	return priv, err
   107  }
   108  
   109  func GenerateEd25519Jwk() (jwk.Key, error) {
   110  	key, err := GenerateEd25519Key()
   111  	if err != nil {
   112  		return nil, errors.Wrap(err, `failed to generate Ed25519 private key`)
   113  	}
   114  
   115  	k, err := jwk.New(key)
   116  	if err != nil {
   117  		return nil, errors.Wrap(err, `failed to generate jwk.OKPPrivateKey`)
   118  	}
   119  
   120  	return k, nil
   121  }
   122  
   123  func GenerateX25519Key() (x25519.PrivateKey, error) {
   124  	_, priv, err := x25519.GenerateKey(rand.Reader)
   125  	return priv, err
   126  }
   127  
   128  func GenerateX25519Jwk() (jwk.Key, error) {
   129  	key, err := GenerateX25519Key()
   130  	if err != nil {
   131  		return nil, errors.Wrap(err, `failed to generate X25519 private key`)
   132  	}
   133  
   134  	k, err := jwk.New(key)
   135  	if err != nil {
   136  		return nil, errors.Wrap(err, `failed to generate jwk.OKPPrivateKey`)
   137  	}
   138  
   139  	return k, nil
   140  }
   141  
   142  func WriteFile(template string, src io.Reader) (string, func(), error) {
   143  	file, cleanup, err := CreateTempFile(template)
   144  	if err != nil {
   145  		return "", nil, errors.Wrap(err, `failed to create temporary file`)
   146  	}
   147  
   148  	if _, err := io.Copy(file, src); err != nil {
   149  		defer cleanup()
   150  		return "", nil, errors.Wrap(err, `failed to copy content to temporary file`)
   151  	}
   152  
   153  	if err := file.Sync(); err != nil {
   154  		defer cleanup()
   155  		return "", nil, errors.Wrap(err, `failed to sync file`)
   156  	}
   157  	return file.Name(), cleanup, nil
   158  }
   159  
   160  func WriteJSONFile(template string, v interface{}) (string, func(), error) {
   161  	var buf bytes.Buffer
   162  
   163  	enc := json.NewEncoder(&buf)
   164  	if err := enc.Encode(v); err != nil {
   165  		return "", nil, errors.Wrap(err, `failed to encode object to JSON`)
   166  	}
   167  	return WriteFile(template, &buf)
   168  }
   169  
   170  func DumpFile(t *testing.T, file string) {
   171  	buf, err := ioutil.ReadFile(file)
   172  	if !assert.NoError(t, err, `failed to read file %s for debugging`, file) {
   173  		return
   174  	}
   175  
   176  	if isHash, isArray := bytes.ContainsRune(buf, '{'), bytes.ContainsRune(buf, '['); isHash || isArray {
   177  		// Looks like a JSON-like thing. Dump that in a formatted manner, and
   178  		// be done with it
   179  
   180  		var v interface{}
   181  		if isHash {
   182  			v = map[string]interface{}{}
   183  		} else {
   184  			v = []interface{}{}
   185  		}
   186  
   187  		if !assert.NoError(t, json.Unmarshal(buf, &v), `failed to parse contents as JSON`) {
   188  			return
   189  		}
   190  
   191  		buf, _ = json.MarshalIndent(v, "", "  ")
   192  		t.Logf("=== BEGIN %s (formatted JSON) ===", file)
   193  		t.Logf("%s", buf)
   194  		t.Logf("=== END   %s (formatted JSON) ===", file)
   195  		return
   196  	}
   197  
   198  	// If the contents do not look like JSON, then we attempt to parse each content
   199  	// based on heuristics (from its file name) and do our best
   200  	t.Logf("=== BEGIN %s (raw) ===", file)
   201  	t.Logf("%s", buf)
   202  	t.Logf("=== END   %s (raw) ===", file)
   203  
   204  	if strings.HasSuffix(file, ".jwe") {
   205  		// cross our fingers our jwe implementation works
   206  		m, err := jwe.Parse(buf)
   207  		if !assert.NoError(t, err, `failed to parse JWE encrypted message`) {
   208  			return
   209  		}
   210  
   211  		buf, _ = json.MarshalIndent(m, "", "  ")
   212  	}
   213  
   214  	t.Logf("=== BEGIN %s (formatted JSON) ===", file)
   215  	t.Logf("%s", buf)
   216  	t.Logf("=== END   %s (formatted JSON) ===", file)
   217  }
   218  
   219  func CreateTempFile(template string) (*os.File, func(), error) {
   220  	file, err := ioutil.TempFile("", template)
   221  	if err != nil {
   222  		return nil, nil, errors.Wrap(err, "failed to create temporary file")
   223  	}
   224  
   225  	cleanup := func() {
   226  		file.Close()
   227  		os.Remove(file.Name())
   228  	}
   229  
   230  	return file, cleanup, nil
   231  }
   232  
   233  func ReadFile(file string) ([]byte, error) {
   234  	f, err := os.Open(file)
   235  	if err != nil {
   236  		return nil, errors.Wrapf(err, `failed to open file %s`, file)
   237  	}
   238  	defer f.Close()
   239  
   240  	buf, err := ioutil.ReadAll(f)
   241  	if err != nil {
   242  		return nil, errors.Wrapf(err, `failed to read from key file %s`, file)
   243  	}
   244  
   245  	return buf, nil
   246  }
   247  
   248  func ParseJwkFile(_ context.Context, file string) (jwk.Key, error) {
   249  	buf, err := ReadFile(file)
   250  	if err != nil {
   251  		return nil, errors.Wrapf(err, `failed to read from key file %s`, file)
   252  	}
   253  
   254  	key, err := jwk.ParseKey(buf)
   255  	if err != nil {
   256  		return nil, errors.Wrapf(err, `filed to parse JWK in key file %s`, file)
   257  	}
   258  
   259  	return key, nil
   260  }
   261  
   262  func DecryptJweFile(ctx context.Context, file string, alg jwa.KeyEncryptionAlgorithm, jwkfile string) ([]byte, error) {
   263  	key, err := ParseJwkFile(ctx, jwkfile)
   264  	if err != nil {
   265  		return nil, errors.Wrapf(err, `failed to parse keyfile %s`, file)
   266  	}
   267  
   268  	buf, err := ReadFile(file)
   269  	if err != nil {
   270  		return nil, errors.Wrapf(err, `failed to read from encrypted file %s`, file)
   271  	}
   272  
   273  	var rawkey interface{}
   274  	if err := key.Raw(&rawkey); err != nil {
   275  		return nil, errors.Wrap(err, `failed to obtain raw key from JWK`)
   276  	}
   277  
   278  	return jwe.Decrypt(buf, alg, rawkey)
   279  }
   280  
   281  func EncryptJweFile(ctx context.Context, payload []byte, keyalg jwa.KeyEncryptionAlgorithm, keyfile string, contentalg jwa.ContentEncryptionAlgorithm, compressalg jwa.CompressionAlgorithm) (string, func(), error) {
   282  	key, err := ParseJwkFile(ctx, keyfile)
   283  	if err != nil {
   284  		return "", nil, errors.Wrapf(err, `failed to parse keyfile %s`, keyfile)
   285  	}
   286  
   287  	var keyif interface{}
   288  
   289  	switch keyalg {
   290  	case jwa.RSA1_5, jwa.RSA_OAEP, jwa.RSA_OAEP_256:
   291  		var rawkey rsa.PrivateKey
   292  		if err := key.Raw(&rawkey); err != nil {
   293  			return "", nil, errors.Wrap(err, `failed to obtain raw key`)
   294  		}
   295  		keyif = rawkey.PublicKey
   296  	case jwa.ECDH_ES, jwa.ECDH_ES_A128KW, jwa.ECDH_ES_A192KW, jwa.ECDH_ES_A256KW:
   297  		var rawkey ecdsa.PrivateKey
   298  		if err := key.Raw(&rawkey); err != nil {
   299  			return "", nil, errors.Wrap(err, `failed to obtain raw key`)
   300  		}
   301  		keyif = rawkey.PublicKey
   302  	default:
   303  		var rawkey []byte
   304  		if err := key.Raw(&rawkey); err != nil {
   305  			return "", nil, errors.Wrap(err, `failed to obtain raw key`)
   306  		}
   307  		keyif = rawkey
   308  	}
   309  
   310  	buf, err := jwe.Encrypt(payload, keyalg, keyif, contentalg, compressalg)
   311  	if err != nil {
   312  		return "", nil, errors.Wrap(err, `failed to encrypt payload`)
   313  	}
   314  
   315  	return WriteFile("jwx-test-*.jwe", bytes.NewReader(buf))
   316  }
   317  
   318  func VerifyJwsFile(ctx context.Context, file string, alg jwa.SignatureAlgorithm, jwkfile string) ([]byte, error) {
   319  	key, err := ParseJwkFile(ctx, jwkfile)
   320  	if err != nil {
   321  		return nil, errors.Wrapf(err, `failed to parse keyfile %s`, file)
   322  	}
   323  
   324  	buf, err := ReadFile(file)
   325  	if err != nil {
   326  		return nil, errors.Wrapf(err, `failed to read from encrypted file %s`, file)
   327  	}
   328  
   329  	var rawkey, pubkey interface{}
   330  	if err := key.Raw(&rawkey); err != nil {
   331  		return nil, errors.Wrap(err, `failed to obtain raw key from JWK`)
   332  	}
   333  	pubkey = rawkey
   334  	switch tkey := rawkey.(type) {
   335  	case *ecdsa.PrivateKey:
   336  		pubkey = tkey.PublicKey
   337  	case *rsa.PrivateKey:
   338  		pubkey = tkey.PublicKey
   339  	case *ed25519.PrivateKey:
   340  		pubkey = tkey.Public()
   341  	}
   342  
   343  	return jws.Verify(buf, alg, pubkey)
   344  }
   345  
   346  func SignJwsFile(ctx context.Context, payload []byte, alg jwa.SignatureAlgorithm, keyfile string) (string, func(), error) {
   347  	key, err := ParseJwkFile(ctx, keyfile)
   348  	if err != nil {
   349  		return "", nil, errors.Wrapf(err, `failed to parse keyfile %s`, keyfile)
   350  	}
   351  
   352  	buf, err := jws.Sign(payload, alg, key)
   353  	if err != nil {
   354  		return "", nil, errors.Wrap(err, `failed to sign payload`)
   355  	}
   356  
   357  	return WriteFile("jwx-test-*.jws", bytes.NewReader(buf))
   358  }
   359  

View as plain text