193 lines
4.9 KiB
Go
193 lines
4.9 KiB
Go
|
package keygen
|
||
|
|
||
|
import (
|
||
|
"crypto"
|
||
|
"crypto/ecdsa"
|
||
|
"crypto/rand"
|
||
|
"encoding/binary"
|
||
|
"io"
|
||
|
|
||
|
"golang.org/x/crypto/curve25519"
|
||
|
|
||
|
"github.com/lestrrat-go/jwx/internal/ecutil"
|
||
|
"github.com/lestrrat-go/jwx/jwa"
|
||
|
"github.com/lestrrat-go/jwx/jwe/internal/concatkdf"
|
||
|
"github.com/lestrrat-go/jwx/jwk"
|
||
|
"github.com/lestrrat-go/jwx/x25519"
|
||
|
"github.com/pkg/errors"
|
||
|
)
|
||
|
|
||
|
// Bytes returns the byte from this ByteKey
|
||
|
func (k ByteKey) Bytes() []byte {
|
||
|
return []byte(k)
|
||
|
}
|
||
|
|
||
|
// Size returns the size of the key
|
||
|
func (g Static) Size() int {
|
||
|
return len(g)
|
||
|
}
|
||
|
|
||
|
// Generate returns the key
|
||
|
func (g Static) Generate() (ByteSource, error) {
|
||
|
buf := make([]byte, g.Size())
|
||
|
copy(buf, g)
|
||
|
return ByteKey(buf), nil
|
||
|
}
|
||
|
|
||
|
// NewRandom creates a new Generator that returns
|
||
|
// random bytes
|
||
|
func NewRandom(n int) Random {
|
||
|
return Random{keysize: n}
|
||
|
}
|
||
|
|
||
|
// Size returns the key size
|
||
|
func (g Random) Size() int {
|
||
|
return g.keysize
|
||
|
}
|
||
|
|
||
|
// Generate generates a random new key
|
||
|
func (g Random) Generate() (ByteSource, error) {
|
||
|
buf := make([]byte, g.keysize)
|
||
|
if _, err := io.ReadFull(rand.Reader, buf); err != nil {
|
||
|
return nil, errors.Wrap(err, "failed to read from rand.Reader")
|
||
|
}
|
||
|
return ByteKey(buf), nil
|
||
|
}
|
||
|
|
||
|
// NewEcdhes creates a new key generator using ECDH-ES
|
||
|
func NewEcdhes(alg jwa.KeyEncryptionAlgorithm, enc jwa.ContentEncryptionAlgorithm, keysize int, pubkey *ecdsa.PublicKey) (*Ecdhes, error) {
|
||
|
return &Ecdhes{
|
||
|
algorithm: alg,
|
||
|
enc: enc,
|
||
|
keysize: keysize,
|
||
|
pubkey: pubkey,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// Size returns the key size associated with this generator
|
||
|
func (g Ecdhes) Size() int {
|
||
|
return g.keysize
|
||
|
}
|
||
|
|
||
|
// Generate generates new keys using ECDH-ES
|
||
|
func (g Ecdhes) Generate() (ByteSource, error) {
|
||
|
priv, err := ecdsa.GenerateKey(g.pubkey.Curve, rand.Reader)
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "failed to generate key for ECDH-ES")
|
||
|
}
|
||
|
|
||
|
var algorithm string
|
||
|
if g.algorithm == jwa.ECDH_ES {
|
||
|
algorithm = g.enc.String()
|
||
|
} else {
|
||
|
algorithm = g.algorithm.String()
|
||
|
}
|
||
|
|
||
|
pubinfo := make([]byte, 4)
|
||
|
binary.BigEndian.PutUint32(pubinfo, uint32(g.keysize)*8)
|
||
|
|
||
|
z, _ := priv.PublicKey.Curve.ScalarMult(g.pubkey.X, g.pubkey.Y, priv.D.Bytes())
|
||
|
zBytes := ecutil.AllocECPointBuffer(z, priv.PublicKey.Curve)
|
||
|
defer ecutil.ReleaseECPointBuffer(zBytes)
|
||
|
kdf := concatkdf.New(crypto.SHA256, []byte(algorithm), zBytes, []byte{}, []byte{}, pubinfo, []byte{})
|
||
|
kek := make([]byte, g.keysize)
|
||
|
if _, err := kdf.Read(kek); err != nil {
|
||
|
return nil, errors.Wrap(err, "failed to read kdf")
|
||
|
}
|
||
|
|
||
|
return ByteWithECPublicKey{
|
||
|
PublicKey: &priv.PublicKey,
|
||
|
ByteKey: ByteKey(kek),
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// NewX25519 creates a new key generator using ECDH-ES
|
||
|
func NewX25519(alg jwa.KeyEncryptionAlgorithm, enc jwa.ContentEncryptionAlgorithm, keysize int, pubkey x25519.PublicKey) (*X25519, error) {
|
||
|
return &X25519{
|
||
|
algorithm: alg,
|
||
|
enc: enc,
|
||
|
keysize: keysize,
|
||
|
pubkey: pubkey,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// Size returns the key size associated with this generator
|
||
|
func (g X25519) Size() int {
|
||
|
return g.keysize
|
||
|
}
|
||
|
|
||
|
// Generate generates new keys using ECDH-ES
|
||
|
func (g X25519) Generate() (ByteSource, error) {
|
||
|
pub, priv, err := x25519.GenerateKey(rand.Reader)
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "failed to generate key for X25519")
|
||
|
}
|
||
|
|
||
|
var algorithm string
|
||
|
if g.algorithm == jwa.ECDH_ES {
|
||
|
algorithm = g.enc.String()
|
||
|
} else {
|
||
|
algorithm = g.algorithm.String()
|
||
|
}
|
||
|
|
||
|
pubinfo := make([]byte, 4)
|
||
|
binary.BigEndian.PutUint32(pubinfo, uint32(g.keysize)*8)
|
||
|
|
||
|
zBytes, err := curve25519.X25519(priv.Seed(), g.pubkey)
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "failed to compute Z")
|
||
|
}
|
||
|
kdf := concatkdf.New(crypto.SHA256, []byte(algorithm), zBytes, []byte{}, []byte{}, pubinfo, []byte{})
|
||
|
kek := make([]byte, g.keysize)
|
||
|
if _, err := kdf.Read(kek); err != nil {
|
||
|
return nil, errors.Wrap(err, "failed to read kdf")
|
||
|
}
|
||
|
|
||
|
return ByteWithECPublicKey{
|
||
|
PublicKey: pub,
|
||
|
ByteKey: ByteKey(kek),
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// HeaderPopulate populates the header with the required EC-DSA public key
|
||
|
// information ('epk' key)
|
||
|
func (k ByteWithECPublicKey) Populate(h Setter) error {
|
||
|
key, err := jwk.New(k.PublicKey)
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "failed to create JWK")
|
||
|
}
|
||
|
|
||
|
if err := h.Set("epk", key); err != nil {
|
||
|
return errors.Wrap(err, "failed to write header")
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// HeaderPopulate populates the header with the required AES GCM
|
||
|
// parameters ('iv' and 'tag')
|
||
|
func (k ByteWithIVAndTag) Populate(h Setter) error {
|
||
|
if err := h.Set("iv", k.IV); err != nil {
|
||
|
return errors.Wrap(err, "failed to write header")
|
||
|
}
|
||
|
|
||
|
if err := h.Set("tag", k.Tag); err != nil {
|
||
|
return errors.Wrap(err, "failed to write header")
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// HeaderPopulate populates the header with the required PBES2
|
||
|
// parameters ('p2s' and 'p2c')
|
||
|
func (k ByteWithSaltAndCount) Populate(h Setter) error {
|
||
|
if err := h.Set("p2c", k.Count); err != nil {
|
||
|
return errors.Wrap(err, "failed to write header")
|
||
|
}
|
||
|
|
||
|
if err := h.Set("p2s", k.Salt); err != nil {
|
||
|
return errors.Wrap(err, "failed to write header")
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|