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 }