Initial QSfera import

This commit is contained in:
Курнат Андрей
2026-06-07 10:20:04 +03:00
commit 2315f25754
16485 changed files with 4826827 additions and 0 deletions
@@ -0,0 +1,22 @@
load("@rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "aescbc",
srcs = ["aescbc.go"],
importpath = "github.com/lestrrat-go/jwx/v3/jwe/internal/aescbc",
visibility = ["//:__subpackages__"],
deps = ["//internal/pool"],
)
go_test(
name = "aescbc_test",
srcs = ["aescbc_test.go"],
embed = [":aescbc"],
deps = ["@com_github_stretchr_testify//require"]
)
alias(
name = "go_default_library",
actual = ":aescbc",
visibility = ["//jwe:__subpackages__"],
)
@@ -0,0 +1,268 @@
package aescbc
import (
"crypto/cipher"
"crypto/hmac"
"crypto/sha256"
"crypto/sha512"
"crypto/subtle"
"encoding/binary"
"errors"
"fmt"
"hash"
"sync/atomic"
"github.com/lestrrat-go/jwx/v3/internal/pool"
)
const (
NonceSize = 16
)
const defaultBufSize int64 = 256 * 1024 * 1024
var maxBufSize atomic.Int64
func init() {
SetMaxBufferSize(defaultBufSize)
}
func SetMaxBufferSize(siz int64) {
if siz <= 0 {
siz = defaultBufSize
}
maxBufSize.Store(siz)
}
func pad(buf []byte, n int) []byte {
rem := n - len(buf)%n
if rem == 0 {
return buf
}
bufsiz := len(buf) + rem
mbs := maxBufSize.Load()
if int64(bufsiz) > mbs {
panic(fmt.Errorf("failed to allocate buffer"))
}
newbuf := make([]byte, bufsiz)
copy(newbuf, buf)
for i := len(buf); i < len(newbuf); i++ {
newbuf[i] = byte(rem)
}
return newbuf
}
// ref. https://github.com/golang/go/blob/c3db64c0f45e8f2d75c5b59401e0fc925701b6f4/src/crypto/tls/conn.go#L279-L324
//
// extractPadding returns, in constant time, the length of the padding to remove
// from the end of payload. It also returns a byte which is equal to 255 if the
// padding was valid and 0 otherwise. See RFC 2246, Section 6.2.3.2.
func extractPadding(payload []byte) (toRemove int, good byte) {
if len(payload) < 1 {
return 0, 0
}
paddingLen := payload[len(payload)-1]
t := uint(len(payload)) - uint(paddingLen)
// if len(payload) > paddingLen then the MSB of t is zero
good = byte(int32(^t) >> 31)
// The maximum possible padding length plus the actual length field
toCheck := 256
// The length of the padded data is public, so we can use an if here
toCheck = min(toCheck, len(payload))
for i := 1; i <= toCheck; i++ {
t := uint(paddingLen) - uint(i)
// if i <= paddingLen then the MSB of t is zero
mask := byte(int32(^t) >> 31)
b := payload[len(payload)-i]
good &^= mask&paddingLen ^ mask&b
}
// We AND together the bits of good and replicate the result across
// all the bits.
good &= good << 4
good &= good << 2
good &= good << 1
good = uint8(int8(good) >> 7)
// Zero the padding length on error. This ensures any unchecked bytes
// are included in the MAC. Otherwise, an attacker that could
// distinguish MAC failures from padding failures could mount an attack
// similar to POODLE in SSL 3.0: given a good ciphertext that uses a
// full block's worth of padding, replace the final block with another
// block. If the MAC check passed but the padding check failed, the
// last byte of that block decrypted to the block size.
//
// See also macAndPaddingGood logic below.
paddingLen &= good
toRemove = int(paddingLen)
return
}
type Hmac struct {
blockCipher cipher.Block
hash func() hash.Hash
keysize int
tagsize int
integrityKey []byte
}
type BlockCipherFunc func([]byte) (cipher.Block, error)
func New(key []byte, f BlockCipherFunc) (hmac *Hmac, err error) {
keysize := len(key) / 2
ikey := key[:keysize]
ekey := key[keysize:]
bc, ciphererr := f(ekey)
if ciphererr != nil {
err = fmt.Errorf(`failed to execute block cipher function: %w`, ciphererr)
return
}
var hfunc func() hash.Hash
switch keysize {
case 16:
hfunc = sha256.New
case 24:
hfunc = sha512.New384
case 32:
hfunc = sha512.New
default:
return nil, fmt.Errorf("unsupported key size %d", keysize)
}
return &Hmac{
blockCipher: bc,
hash: hfunc,
integrityKey: ikey,
keysize: keysize,
tagsize: keysize, // NonceSize,
// While investigating GH #207, I stumbled upon another problem where
// the computed tags don't match on decrypt. After poking through the
// code using a bunch of debug statements, I've finally found out that
// tagsize = keysize makes the whole thing work.
}, nil
}
// NonceSize fulfills the crypto.AEAD interface
func (c Hmac) NonceSize() int {
return NonceSize
}
// Overhead fulfills the crypto.AEAD interface
func (c Hmac) Overhead() int {
return c.blockCipher.BlockSize() + c.tagsize
}
func (c Hmac) ComputeAuthTag(aad, nonce, ciphertext []byte) ([]byte, error) {
var buf [8]byte
binary.BigEndian.PutUint64(buf[:], uint64(len(aad)*8))
h := hmac.New(c.hash, c.integrityKey)
// compute the tag
// no need to check errors because Write never returns an error: https://pkg.go.dev/hash#Hash
//
// > Write (via the embedded io.Writer interface) adds more data to the running hash.
// > It never returns an error.
h.Write(aad)
h.Write(nonce)
h.Write(ciphertext)
h.Write(buf[:])
s := h.Sum(nil)
return s[:c.tagsize], nil
}
func ensureSize(dst []byte, n int) []byte {
// if the dst buffer has enough length just copy the relevant parts to it.
// Otherwise create a new slice that's big enough, and operate on that
// Note: I think go-jose has a bug in that it checks for cap(), but not len().
ret := dst
if diff := n - len(dst); diff > 0 {
// dst is not big enough
ret = make([]byte, n)
copy(ret, dst)
}
return ret
}
// Seal fulfills the crypto.AEAD interface
func (c Hmac) Seal(dst, nonce, plaintext, data []byte) []byte {
ctlen := len(plaintext)
bufsiz := ctlen + c.Overhead()
mbs := maxBufSize.Load()
if int64(bufsiz) > mbs {
panic(fmt.Errorf("failed to allocate buffer"))
}
ciphertext := make([]byte, bufsiz)[:ctlen]
copy(ciphertext, plaintext)
ciphertext = pad(ciphertext, c.blockCipher.BlockSize())
cbc := cipher.NewCBCEncrypter(c.blockCipher, nonce)
cbc.CryptBlocks(ciphertext, ciphertext)
authtag, err := c.ComputeAuthTag(data, nonce, ciphertext)
if err != nil {
// Hmac implements cipher.AEAD interface. Seal can't return error.
// But currently it never reach here because of Hmac.ComputeAuthTag doesn't return error.
panic(fmt.Errorf("failed to seal on hmac: %v", err))
}
retlen := len(dst) + len(ciphertext) + len(authtag)
ret := ensureSize(dst, retlen)
out := ret[len(dst):]
n := copy(out, ciphertext)
copy(out[n:], authtag)
return ret
}
// Open fulfills the crypto.AEAD interface
func (c Hmac) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
if len(ciphertext) < c.keysize {
return nil, fmt.Errorf(`invalid ciphertext (too short)`)
}
tagOffset := len(ciphertext) - c.tagsize
if tagOffset%c.blockCipher.BlockSize() != 0 {
return nil, fmt.Errorf(
"invalid ciphertext (invalid length: %d %% %d != 0)",
tagOffset,
c.blockCipher.BlockSize(),
)
}
tag := ciphertext[tagOffset:]
ciphertext = ciphertext[:tagOffset]
expectedTag, err := c.ComputeAuthTag(data, nonce, ciphertext[:tagOffset])
if err != nil {
return nil, fmt.Errorf(`failed to compute auth tag: %w`, err)
}
cbc := cipher.NewCBCDecrypter(c.blockCipher, nonce)
buf := pool.ByteSlice().GetCapacity(tagOffset)
defer pool.ByteSlice().Put(buf)
buf = buf[:tagOffset]
cbc.CryptBlocks(buf, ciphertext)
toRemove, good := extractPadding(buf)
cmp := subtle.ConstantTimeCompare(expectedTag, tag) & int(good)
if cmp != 1 {
return nil, errors.New(`invalid ciphertext`)
}
plaintext := buf[:len(buf)-toRemove]
ret := ensureSize(dst, len(plaintext))
out := ret[len(dst):]
copy(out, plaintext)
return ret, nil
}
@@ -0,0 +1,34 @@
load("@rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "cipher",
srcs = [
"cipher.go",
"interface.go",
],
importpath = "github.com/lestrrat-go/jwx/v3/jwe/internal/cipher",
visibility = ["//:__subpackages__"],
deps = [
"//jwa",
"//jwe/internal/aescbc",
"//jwe/internal/keygen",
"//internal/tokens",
],
)
go_test(
name = "cipher_test",
srcs = ["cipher_test.go"],
deps = [
":cipher",
"//jwa",
"//internal/tokens",
"@com_github_stretchr_testify//require",
],
)
alias(
name = "go_default_library",
actual = ":cipher",
visibility = ["//jwe:__subpackages__"],
)
@@ -0,0 +1,169 @@
package cipher
import (
"crypto/aes"
"crypto/cipher"
"fmt"
"github.com/lestrrat-go/jwx/v3/internal/tokens"
"github.com/lestrrat-go/jwx/v3/jwe/internal/aescbc"
"github.com/lestrrat-go/jwx/v3/jwe/internal/keygen"
)
var gcm = &gcmFetcher{}
var cbc = &cbcFetcher{}
func (f gcmFetcher) Fetch(key []byte, size int) (cipher.AEAD, error) {
if len(key) != size {
return nil, fmt.Errorf(`key size (%d) does not match expected key size (%d)`, len(key), size)
}
aescipher, err := aes.NewCipher(key)
if err != nil {
return nil, fmt.Errorf(`cipher: failed to create AES cipher for GCM: %w`, err)
}
aead, err := cipher.NewGCM(aescipher)
if err != nil {
return nil, fmt.Errorf(`failed to create GCM for cipher: %w`, err)
}
return aead, nil
}
func (f cbcFetcher) Fetch(key []byte, size int) (cipher.AEAD, error) {
if len(key) != size {
return nil, fmt.Errorf(`key size (%d) does not match expected key size (%d)`, len(key), size)
}
aead, err := aescbc.New(key, aes.NewCipher)
if err != nil {
return nil, fmt.Errorf(`cipher: failed to create AES cipher for CBC: %w`, err)
}
return aead, nil
}
func (c AesContentCipher) KeySize() int {
return c.keysize
}
func (c AesContentCipher) TagSize() int {
return c.tagsize
}
func NewAES(alg string) (*AesContentCipher, error) {
var keysize int
var tagsize int
var fetcher Fetcher
switch alg {
case tokens.A128GCM:
keysize = 16
tagsize = 16
fetcher = gcm
case tokens.A192GCM:
keysize = 24
tagsize = 16
fetcher = gcm
case tokens.A256GCM:
keysize = 32
tagsize = 16
fetcher = gcm
case tokens.A128CBC_HS256:
tagsize = 16
keysize = tagsize * 2
fetcher = cbc
case tokens.A192CBC_HS384:
tagsize = 24
keysize = tagsize * 2
fetcher = cbc
case tokens.A256CBC_HS512:
tagsize = 32
keysize = tagsize * 2
fetcher = cbc
default:
return nil, fmt.Errorf("failed to create AES content cipher: invalid algorithm (%s)", alg)
}
return &AesContentCipher{
keysize: keysize,
tagsize: tagsize,
fetch: fetcher,
}, nil
}
func (c AesContentCipher) Encrypt(cek, plaintext, aad []byte) (iv, ciphertxt, tag []byte, err error) {
var aead cipher.AEAD
aead, err = c.fetch.Fetch(cek, c.keysize)
if err != nil {
return nil, nil, nil, fmt.Errorf(`failed to fetch AEAD: %w`, err)
}
// Seal may panic (argh!), so protect ourselves from that
defer func() {
if e := recover(); e != nil {
switch e := e.(type) {
case error:
err = e
default:
err = fmt.Errorf("%s", e)
}
err = fmt.Errorf(`failed to encrypt: %w`, err)
}
}()
if c.NonceGenerator != nil {
iv, err = c.NonceGenerator(aead.NonceSize())
if err != nil {
return nil, nil, nil, fmt.Errorf(`failed to generate nonce: %w`, err)
}
} else {
bs, err := keygen.Random(aead.NonceSize())
if err != nil {
return nil, nil, nil, fmt.Errorf(`failed to generate random nonce: %w`, err)
}
iv = bs.Bytes()
}
combined := aead.Seal(nil, iv, plaintext, aad)
tagoffset := len(combined) - c.TagSize()
if tagoffset < 0 {
panic(fmt.Sprintf("tag offset is less than 0 (combined len = %d, tagsize = %d)", len(combined), c.TagSize()))
}
tag = combined[tagoffset:]
ciphertxt = make([]byte, tagoffset)
copy(ciphertxt, combined[:tagoffset])
return
}
func (c AesContentCipher) Decrypt(cek, iv, ciphertxt, tag, aad []byte) (plaintext []byte, err error) {
aead, err := c.fetch.Fetch(cek, c.keysize)
if err != nil {
return nil, fmt.Errorf(`failed to fetch AEAD data: %w`, err)
}
// Open may panic (argh!), so protect ourselves from that
defer func() {
if e := recover(); e != nil {
switch e := e.(type) {
case error:
err = e
default:
err = fmt.Errorf(`%s`, e)
}
err = fmt.Errorf(`failed to decrypt: %w`, err)
return
}
}()
combined := make([]byte, len(ciphertxt)+len(tag))
copy(combined, ciphertxt)
copy(combined[len(ciphertxt):], tag)
buf, aeaderr := aead.Open(nil, iv, combined, aad)
if aeaderr != nil {
err = fmt.Errorf(`aead.Open failed: %w`, aeaderr)
return
}
plaintext = buf
return
}
@@ -0,0 +1,32 @@
package cipher
import (
"crypto/cipher"
)
const (
TagSize = 16
)
// ContentCipher knows how to encrypt/decrypt the content given a content
// encryption key and other data
type ContentCipher interface {
KeySize() int
Encrypt(cek, aad, plaintext []byte) ([]byte, []byte, []byte, error)
Decrypt(cek, iv, aad, ciphertext, tag []byte) ([]byte, error)
}
type Fetcher interface {
Fetch([]byte, int) (cipher.AEAD, error)
}
type gcmFetcher struct{}
type cbcFetcher struct{}
// AesContentCipher represents a cipher based on AES
type AesContentCipher struct {
NonceGenerator func(int) ([]byte, error)
fetch Fetcher
keysize int
tagsize int
}
@@ -0,0 +1,24 @@
load("@rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "concatkdf",
srcs = ["concatkdf.go"],
importpath = "github.com/lestrrat-go/jwx/v3/jwe/internal/concatkdf",
visibility = ["//:__subpackages__"],
)
go_test(
name = "concatkdf_test",
srcs = ["concatkdf_test.go"],
embed = [":concatkdf"],
deps = [
"//jwa",
"@com_github_stretchr_testify//require",
],
)
alias(
name = "go_default_library",
actual = ":concatkdf",
visibility = ["//jwe:__subpackages__"],
)
@@ -0,0 +1,66 @@
package concatkdf
import (
"crypto"
"encoding/binary"
"fmt"
)
type KDF struct {
buf []byte
otherinfo []byte
z []byte
hash crypto.Hash
}
func ndata(src []byte) []byte {
buf := make([]byte, 4+len(src))
binary.BigEndian.PutUint32(buf, uint32(len(src)))
copy(buf[4:], src)
return buf
}
func New(hash crypto.Hash, alg, Z, apu, apv, pubinfo, privinfo []byte) *KDF {
algbuf := ndata(alg)
apubuf := ndata(apu)
apvbuf := ndata(apv)
concat := make([]byte, len(algbuf)+len(apubuf)+len(apvbuf)+len(pubinfo)+len(privinfo))
n := copy(concat, algbuf)
n += copy(concat[n:], apubuf)
n += copy(concat[n:], apvbuf)
n += copy(concat[n:], pubinfo)
copy(concat[n:], privinfo)
return &KDF{
hash: hash,
otherinfo: concat,
z: Z,
}
}
func (k *KDF) Read(out []byte) (int, error) {
var round uint32 = 1
h := k.hash.New()
for len(out) > len(k.buf) {
h.Reset()
if err := binary.Write(h, binary.BigEndian, round); err != nil {
return 0, fmt.Errorf(`failed to write round using kdf: %w`, err)
}
if _, err := h.Write(k.z); err != nil {
return 0, fmt.Errorf(`failed to write z using kdf: %w`, err)
}
if _, err := h.Write(k.otherinfo); err != nil {
return 0, fmt.Errorf(`failed to write other info using kdf: %w`, err)
}
k.buf = append(k.buf, h.Sum(nil)...)
round++
}
n := copy(out, k.buf[:len(out)])
k.buf = k.buf[len(out):]
return n, nil
}
@@ -0,0 +1,21 @@
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "content_crypt",
srcs = [
"content_crypt.go",
"interface.go",
],
importpath = "github.com/lestrrat-go/jwx/v3/jwe/internal/content_crypt",
visibility = ["//:__subpackages__"],
deps = [
"//jwa",
"//jwe/internal/cipher",
],
)
alias(
name = "go_default_library",
actual = ":content_crypt",
visibility = ["//jwe:__subpackages__"],
)
@@ -0,0 +1,43 @@
package content_crypt //nolint:golint
import (
"fmt"
"github.com/lestrrat-go/jwx/v3/jwa"
"github.com/lestrrat-go/jwx/v3/jwe/internal/cipher"
)
func (c Generic) Algorithm() jwa.ContentEncryptionAlgorithm {
return c.alg
}
func (c Generic) Encrypt(cek, plaintext, aad []byte) ([]byte, []byte, []byte, error) {
iv, encrypted, tag, err := c.cipher.Encrypt(cek, plaintext, aad)
if err != nil {
return nil, nil, nil, fmt.Errorf(`failed to crypt content: %w`, err)
}
return iv, encrypted, tag, nil
}
func (c Generic) Decrypt(cek, iv, ciphertext, tag, aad []byte) ([]byte, error) {
return c.cipher.Decrypt(cek, iv, ciphertext, tag, aad)
}
func NewGeneric(alg jwa.ContentEncryptionAlgorithm) (*Generic, error) {
c, err := cipher.NewAES(alg.String())
if err != nil {
return nil, fmt.Errorf(`aes crypt: failed to create content cipher: %w`, err)
}
return &Generic{
alg: alg,
cipher: c,
keysize: c.KeySize(),
tagsize: 16,
}, nil
}
func (c Generic) KeySize() int {
return c.keysize
}
@@ -0,0 +1,20 @@
package content_crypt //nolint:golint
import (
"github.com/lestrrat-go/jwx/v3/jwa"
"github.com/lestrrat-go/jwx/v3/jwe/internal/cipher"
)
// Generic encrypts a message by applying all the necessary
// modifications to the keys and the contents
type Generic struct {
alg jwa.ContentEncryptionAlgorithm
keysize int
tagsize int
cipher cipher.ContentCipher
}
type Cipher interface {
Decrypt([]byte, []byte, []byte, []byte, []byte) ([]byte, error)
KeySize() int
}
@@ -0,0 +1,24 @@
load("@rules_go//go:def.bzl", "go_library")
go_library(
name = "keygen",
srcs = [
"interface.go",
"keygen.go",
],
importpath = "github.com/lestrrat-go/jwx/v3/jwe/internal/keygen",
visibility = ["//:__subpackages__"],
deps = [
"//internal/ecutil",
"//jwa",
"//jwe/internal/concatkdf",
"//internal/tokens",
"//jwk",
],
)
alias(
name = "go_default_library",
actual = ":keygen",
visibility = ["//jwe:__subpackages__"],
)
@@ -0,0 +1,40 @@
package keygen
// ByteKey is a generated key that only has the key's byte buffer
// as its instance data. If a key needs to do more, such as providing
// values to be set in a JWE header, that key type wraps a ByteKey
type ByteKey []byte
// ByteWithECPublicKey holds the EC private key that generated
// the key along with the key itself. This is required to set the
// proper values in the JWE headers
type ByteWithECPublicKey struct {
ByteKey
PublicKey any
}
type ByteWithIVAndTag struct {
ByteKey
IV []byte
Tag []byte
}
type ByteWithSaltAndCount struct {
ByteKey
Salt []byte
Count int
}
// ByteSource is an interface for things that return a byte sequence.
// This is used for KeyGenerator so that the result of computations can
// carry more than just the generate byte sequence.
type ByteSource interface {
Bytes() []byte
}
type Setter interface {
Set(string, any) error
}
@@ -0,0 +1,139 @@
package keygen
import (
"crypto"
"crypto/ecdh"
"crypto/ecdsa"
"crypto/rand"
"encoding/binary"
"fmt"
"io"
"github.com/lestrrat-go/jwx/v3/internal/ecutil"
"github.com/lestrrat-go/jwx/v3/internal/tokens"
"github.com/lestrrat-go/jwx/v3/jwe/internal/concatkdf"
"github.com/lestrrat-go/jwx/v3/jwk"
)
// Bytes returns the byte from this ByteKey
func (k ByteKey) Bytes() []byte {
return []byte(k)
}
func Random(n int) (ByteSource, error) {
buf := make([]byte, n)
if _, err := io.ReadFull(rand.Reader, buf); err != nil {
return nil, fmt.Errorf(`failed to read from rand.Reader: %w`, err)
}
return ByteKey(buf), nil
}
// Ecdhes generates a new key using ECDH-ES
func Ecdhes(alg string, enc string, keysize int, pubkey *ecdsa.PublicKey, apu, apv []byte) (ByteSource, error) {
priv, err := ecdsa.GenerateKey(pubkey.Curve, rand.Reader)
if err != nil {
return nil, fmt.Errorf(`failed to generate key for ECDH-ES: %w`, err)
}
var algorithm string
if alg == tokens.ECDH_ES {
algorithm = enc
} else {
algorithm = alg
}
pubinfo := make([]byte, 4)
binary.BigEndian.PutUint32(pubinfo, uint32(keysize)*8)
if !priv.PublicKey.Curve.IsOnCurve(pubkey.X, pubkey.Y) {
return nil, fmt.Errorf(`public key used does not contain a point (X,Y) on the curve`)
}
z, _ := priv.PublicKey.Curve.ScalarMult(pubkey.X, pubkey.Y, priv.D.Bytes())
zBytes := ecutil.AllocECPointBuffer(z, priv.PublicKey.Curve)
defer ecutil.ReleaseECPointBuffer(zBytes)
kdf := concatkdf.New(crypto.SHA256, []byte(algorithm), zBytes, apu, apv, pubinfo, []byte{})
kek := make([]byte, keysize)
if _, err := kdf.Read(kek); err != nil {
return nil, fmt.Errorf(`failed to read kdf: %w`, err)
}
return ByteWithECPublicKey{
PublicKey: &priv.PublicKey,
ByteKey: ByteKey(kek),
}, nil
}
// X25519 generates a new key using ECDH-ES with X25519
func X25519(alg string, enc string, keysize int, pubkey *ecdh.PublicKey) (ByteSource, error) {
priv, err := ecdh.X25519().GenerateKey(rand.Reader)
if err != nil {
return nil, fmt.Errorf(`failed to generate key for X25519: %w`, err)
}
var algorithm string
if alg == tokens.ECDH_ES {
algorithm = enc
} else {
algorithm = alg
}
pubinfo := make([]byte, 4)
binary.BigEndian.PutUint32(pubinfo, uint32(keysize)*8)
zBytes, err := priv.ECDH(pubkey)
if err != nil {
return nil, fmt.Errorf(`failed to compute Z: %w`, err)
}
kdf := concatkdf.New(crypto.SHA256, []byte(algorithm), zBytes, []byte{}, []byte{}, pubinfo, []byte{})
kek := make([]byte, keysize)
if _, err := kdf.Read(kek); err != nil {
return nil, fmt.Errorf(`failed to read kdf: %w`, err)
}
return ByteWithECPublicKey{
PublicKey: priv.PublicKey(),
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.Import(k.PublicKey)
if err != nil {
return fmt.Errorf(`failed to create JWK: %w`, err)
}
if err := h.Set("epk", key); err != nil {
return fmt.Errorf(`failed to write header: %w`, err)
}
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 fmt.Errorf(`failed to write header: %w`, err)
}
if err := h.Set("tag", k.Tag); err != nil {
return fmt.Errorf(`failed to write header: %w`, err)
}
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 fmt.Errorf(`failed to write header: %w`, err)
}
if err := h.Set("p2s", k.Salt); err != nil {
return fmt.Errorf(`failed to write header: %w`, err)
}
return nil
}