github.com/lestrrat-go/jwx/v2@v2.0.21/jwe/internal/keygen/keygen.go (about)

     1  package keygen
     2  
     3  import (
     4  	"crypto"
     5  	"crypto/ecdsa"
     6  	"crypto/rand"
     7  	"encoding/binary"
     8  	"fmt"
     9  	"io"
    10  
    11  	"golang.org/x/crypto/curve25519"
    12  
    13  	"github.com/lestrrat-go/jwx/v2/internal/ecutil"
    14  	"github.com/lestrrat-go/jwx/v2/jwa"
    15  	"github.com/lestrrat-go/jwx/v2/jwe/internal/concatkdf"
    16  	"github.com/lestrrat-go/jwx/v2/jwk"
    17  	"github.com/lestrrat-go/jwx/v2/x25519"
    18  )
    19  
    20  // Bytes returns the byte from this ByteKey
    21  func (k ByteKey) Bytes() []byte {
    22  	return []byte(k)
    23  }
    24  
    25  // NewRandom creates a new Generator that returns
    26  // random bytes
    27  func NewRandom(n int) Random {
    28  	return Random{keysize: n}
    29  }
    30  
    31  // Size returns the key size
    32  func (g Random) Size() int {
    33  	return g.keysize
    34  }
    35  
    36  // Generate generates a random new key
    37  func (g Random) Generate() (ByteSource, error) {
    38  	buf := make([]byte, g.keysize)
    39  	if _, err := io.ReadFull(rand.Reader, buf); err != nil {
    40  		return nil, fmt.Errorf(`failed to read from rand.Reader: %w`, err)
    41  	}
    42  	return ByteKey(buf), nil
    43  }
    44  
    45  // NewEcdhes creates a new key generator using ECDH-ES
    46  func NewEcdhes(alg jwa.KeyEncryptionAlgorithm, enc jwa.ContentEncryptionAlgorithm, keysize int, pubkey *ecdsa.PublicKey, apu, apv []byte) (*Ecdhes, error) {
    47  	return &Ecdhes{
    48  		algorithm: alg,
    49  		enc:       enc,
    50  		keysize:   keysize,
    51  		pubkey:    pubkey,
    52  		apu:       apu,
    53  		apv:       apv,
    54  	}, nil
    55  }
    56  
    57  // Size returns the key size associated with this generator
    58  func (g Ecdhes) Size() int {
    59  	return g.keysize
    60  }
    61  
    62  // Generate generates new keys using ECDH-ES
    63  func (g Ecdhes) Generate() (ByteSource, error) {
    64  	priv, err := ecdsa.GenerateKey(g.pubkey.Curve, rand.Reader)
    65  	if err != nil {
    66  		return nil, fmt.Errorf(`failed to generate key for ECDH-ES: %w`, err)
    67  	}
    68  
    69  	var algorithm string
    70  	if g.algorithm == jwa.ECDH_ES {
    71  		algorithm = g.enc.String()
    72  	} else {
    73  		algorithm = g.algorithm.String()
    74  	}
    75  
    76  	pubinfo := make([]byte, 4)
    77  	binary.BigEndian.PutUint32(pubinfo, uint32(g.keysize)*8)
    78  
    79  	if !priv.PublicKey.Curve.IsOnCurve(g.pubkey.X, g.pubkey.Y) {
    80  		return nil, fmt.Errorf(`public key used does not contain a point (X,Y) on the curve`)
    81  	}
    82  	z, _ := priv.PublicKey.Curve.ScalarMult(g.pubkey.X, g.pubkey.Y, priv.D.Bytes())
    83  	zBytes := ecutil.AllocECPointBuffer(z, priv.PublicKey.Curve)
    84  	defer ecutil.ReleaseECPointBuffer(zBytes)
    85  	kdf := concatkdf.New(crypto.SHA256, []byte(algorithm), zBytes, g.apu, g.apv, pubinfo, []byte{})
    86  	kek := make([]byte, g.keysize)
    87  	if _, err := kdf.Read(kek); err != nil {
    88  		return nil, fmt.Errorf(`failed to read kdf: %w`, err)
    89  	}
    90  
    91  	return ByteWithECPublicKey{
    92  		PublicKey: &priv.PublicKey,
    93  		ByteKey:   ByteKey(kek),
    94  	}, nil
    95  }
    96  
    97  // NewX25519 creates a new key generator using ECDH-ES
    98  func NewX25519(alg jwa.KeyEncryptionAlgorithm, enc jwa.ContentEncryptionAlgorithm, keysize int, pubkey x25519.PublicKey) (*X25519, error) {
    99  	return &X25519{
   100  		algorithm: alg,
   101  		enc:       enc,
   102  		keysize:   keysize,
   103  		pubkey:    pubkey,
   104  	}, nil
   105  }
   106  
   107  // Size returns the key size associated with this generator
   108  func (g X25519) Size() int {
   109  	return g.keysize
   110  }
   111  
   112  // Generate generates new keys using ECDH-ES
   113  func (g X25519) Generate() (ByteSource, error) {
   114  	pub, priv, err := x25519.GenerateKey(rand.Reader)
   115  	if err != nil {
   116  		return nil, fmt.Errorf(`failed to generate key for X25519: %w`, err)
   117  	}
   118  
   119  	var algorithm string
   120  	if g.algorithm == jwa.ECDH_ES {
   121  		algorithm = g.enc.String()
   122  	} else {
   123  		algorithm = g.algorithm.String()
   124  	}
   125  
   126  	pubinfo := make([]byte, 4)
   127  	binary.BigEndian.PutUint32(pubinfo, uint32(g.keysize)*8)
   128  
   129  	zBytes, err := curve25519.X25519(priv.Seed(), g.pubkey)
   130  	if err != nil {
   131  		return nil, fmt.Errorf(`failed to compute Z: %w`, err)
   132  	}
   133  	kdf := concatkdf.New(crypto.SHA256, []byte(algorithm), zBytes, []byte{}, []byte{}, pubinfo, []byte{})
   134  	kek := make([]byte, g.keysize)
   135  	if _, err := kdf.Read(kek); err != nil {
   136  		return nil, fmt.Errorf(`failed to read kdf: %w`, err)
   137  	}
   138  
   139  	return ByteWithECPublicKey{
   140  		PublicKey: pub,
   141  		ByteKey:   ByteKey(kek),
   142  	}, nil
   143  }
   144  
   145  // HeaderPopulate populates the header with the required EC-DSA public key
   146  // information ('epk' key)
   147  func (k ByteWithECPublicKey) Populate(h Setter) error {
   148  	key, err := jwk.FromRaw(k.PublicKey)
   149  	if err != nil {
   150  		return fmt.Errorf(`failed to create JWK: %w`, err)
   151  	}
   152  
   153  	if err := h.Set("epk", key); err != nil {
   154  		return fmt.Errorf(`failed to write header: %w`, err)
   155  	}
   156  	return nil
   157  }
   158  
   159  // HeaderPopulate populates the header with the required AES GCM
   160  // parameters ('iv' and 'tag')
   161  func (k ByteWithIVAndTag) Populate(h Setter) error {
   162  	if err := h.Set("iv", k.IV); err != nil {
   163  		return fmt.Errorf(`failed to write header: %w`, err)
   164  	}
   165  
   166  	if err := h.Set("tag", k.Tag); err != nil {
   167  		return fmt.Errorf(`failed to write header: %w`, err)
   168  	}
   169  
   170  	return nil
   171  }
   172  
   173  // HeaderPopulate populates the header with the required PBES2
   174  // parameters ('p2s' and 'p2c')
   175  func (k ByteWithSaltAndCount) Populate(h Setter) error {
   176  	if err := h.Set("p2c", k.Count); err != nil {
   177  		return fmt.Errorf(`failed to write header: %w`, err)
   178  	}
   179  
   180  	if err := h.Set("p2s", k.Salt); err != nil {
   181  		return fmt.Errorf(`failed to write header: %w`, err)
   182  	}
   183  
   184  	return nil
   185  }