"golang.org/x/crypto/chacha20poly1305"
)
-const ChaPolyChunkLen = 64 * 1024
+const ChaPolyChunkLen = 128 * 1024
-var ChaPolyPad = make([]byte, 16)
+var ChaPolyPad = make([]byte, 32)
func incr(data []byte) {
for i := len(data) - 1; i >= 0; i-- {
}
func demChaPolySeal(cek []byte) error {
- ciph, err := chacha20poly1305.New(cek)
+ if len(cek) != chacha20poly1305.KeySize+chacha20poly1305.NonceSize {
+ return errors.New("wrong CEK len")
+ }
+ key, iv := cek[:chacha20poly1305.KeySize], cek[chacha20poly1305.KeySize:]
+ ciph, err := chacha20poly1305.New(key)
if err != nil {
return err
}
_, e := keks.BlobEncode(bw, int64(len(out)), pr)
blobErr <- e
}()
+ ctr := make([]byte, ciph.NonceSize())
nonce := make([]byte, ciph.NonceSize())
var n int
var eof bool
}
eof = true
}
- incr(nonce[:len(nonce)-1])
+ incr(ctr[:len(ctr)-1])
if n != ChaPolyChunkLen {
- nonce[len(nonce)-1] = 0x01
+ ctr[len(ctr)-1] = 0x01
}
+ subtle.XORBytes(nonce, ctr, iv)
_, err = pw.Write(ciph.Seal(out[:0], nonce, in[:len(ChaPolyPad)+n], nil))
if err != nil {
return err
}
func demChaPolyOpen(cek []byte) error {
- ciph, err := chacha20poly1305.New(cek)
+ if len(cek) != chacha20poly1305.KeySize+chacha20poly1305.NonceSize {
+ return errors.New("wrong CEK len")
+ }
+ key, iv := cek[:chacha20poly1305.KeySize], cek[chacha20poly1305.KeySize:]
+ ciph, err := chacha20poly1305.New(key)
if err != nil {
return err
}
var eof bool
var chunk []byte
bw := bufio.NewWriterSize(os.Stdout, ChaPolyChunkLen)
+ ctr := make([]byte, ciph.NonceSize())
nonce := make([]byte, ciph.NonceSize())
for !eof {
n, err = io.ReadFull(pr, in)
}
eof = true
}
- incr(nonce[:len(nonce)-1])
+ incr(ctr[:len(ctr)-1])
if n != len(in) {
- nonce[len(nonce)-1] = 0x01
+ ctr[len(ctr)-1] = 0x01
}
+ subtle.XORBytes(nonce, ctr, iv)
chunk, err = ciph.Open(out[:0], nonce, in[:n], nil)
if err != nil {
return err
return nil, err
}
nonce := make([]byte, ciph.NonceSize())
- plaintext := append(ChaPolyPad, cek...)
- return ciph.Seal(nil, nonce, plaintext, nil), nil
+ return ciph.Seal(nil, nonce, cek, nil), nil
}
func kemChaPolyOpen(kek, ciphertext []byte, cekLenExpected int) ([]byte, error) {
if err != nil {
return nil, err
}
- if subtle.ConstantTimeCompare(cek[:len(ChaPolyPad)], ChaPolyPad) != 1 {
- return nil, errors.New("bad pad")
- }
- cek = cek[len(ChaPolyPad):]
if len(cek) != cekLenExpected {
return nil, errors.New("invalid CEK len")
}
const BindFdNum = 3 + 1
-func blake2b256() hash.Hash {
- h, err := blake2b.New256(nil)
+func blake2bHash() hash.Hash {
+ h, err := blake2b.New512(nil)
if err != nil {
panic(err)
}
passwd := readPasswd("Passphrase:")
{
var kek []byte
- kek, err = hkdf.Extract(blake2b256, balloon.H(blake2b256,
- passwd,
- append(encrypted.Bind[:], *kem.Salt...),
- int(kem.Cost.S), int(kem.Cost.T), int(kem.Cost.P),
- ), []byte(cmenc.BalloonHKDFSalt))
+ kek, err = hkdf.Expand(
+ blake2bHash,
+ balloon.H(
+ blake2bHash,
+ passwd,
+ append(encrypted.Bind[:], *kem.Salt...),
+ int(kem.Cost.S), int(kem.Cost.T), int(kem.Cost.P),
+ ),
+ cmenc.BalloonHKDFInfo,
+ chacha20poly1305.KeySize,
+ )
if err != nil {
log.Fatal(err)
}
var cekp []byte
- cekp, err = kemChaPolyOpen(kek, kem.CEK, chacha20poly1305.KeySize)
+ cekp, err = kemChaPolyOpen(
+ kek, kem.CEK,
+ chacha20poly1305.KeySize+chacha20poly1305.NonceSize,
+ )
if err != nil {
log.Println(kemIdx, kem.A, err, ", skipping")
continue
ourX25519.PublicKey().Bytes()...,
)
ikm := bytes.Join([][]byte{
- encrypted.Bind[:],
*kem.Encap, pub,
keySNTRUP[:], keyX25519,
}, []byte{})
+ var prk []byte
+ prk, err = hkdf.Extract(blake2bHash, ikm, encrypted.Bind[:])
+ if err != nil {
+ log.Fatal(err)
+ }
var kek []byte
- kek, err = hkdf.Extract(blake2b256,
- ikm, []byte(cmenc.SNTRUP4591761X25519Salt))
+ kek, err = hkdf.Expand(
+ blake2bHash,
+ prk,
+ cmenc.SNTRUP4591761X25519Info,
+ chacha20poly1305.KeySize,
+ )
if err != nil {
log.Fatal(err)
}
var cekp []byte
- cekp, err = kemChaPolyOpen(kek, kem.CEK, chacha20poly1305.KeySize)
+ cekp, err = kemChaPolyOpen(
+ kek, kem.CEK,
+ chacha20poly1305.KeySize+chacha20poly1305.NonceSize,
+ )
if err != nil {
log.Println(kemIdx, kem.A, err, ", skipping")
continue
ourX25519.PublicKey().Bytes()...,
)
ikm := bytes.Join([][]byte{
- encrypted.Bind[:],
*kem.Encap, pub,
keyMcEliece, keyX25519,
}, []byte{})
+ var prk []byte
+ prk, err = hkdf.Extract(
+ cmhash.NewSHAKE256, ikm, encrypted.Bind[:])
+ if err != nil {
+ log.Fatal(err)
+ }
var kek []byte
- kek, err = hkdf.Extract(cmhash.NewSHAKE256,
- ikm, []byte(cmenc.ClassicMcEliece6960119X25519Salt))
+ kek, err = hkdf.Expand(
+ cmhash.NewSHAKE256,
+ prk,
+ cmenc.ClassicMcEliece6960119X25519Info,
+ chacha20poly1305.KeySize,
+ )
if err != nil {
log.Fatal(err)
}
cekp, err = kemChaPolyOpen(
kek[:chacha20poly1305.KeySize],
kem.CEK,
- chacha20poly1305.KeySize,
+ chacha20poly1305.KeySize+chacha20poly1305.NonceSize,
)
if err != nil {
log.Println(kemIdx, kem.A, err, ", skipping")
bindFd.Close()
}
var kems []cmenc.KEM
- cek = make([]byte, chacha20poly1305.KeySize)
+ cek = make([]byte, chacha20poly1305.KeySize+chacha20poly1305.NonceSize)
_, err = io.ReadFull(rand.Reader, cek)
if err != nil {
log.Fatal(err)
}
{
var kek []byte
- kek, err = hkdf.Extract(blake2b256, balloon.H(blake2b256,
- passwd,
- append(binding[:], salt...),
- *balloonS, *balloonT, *balloonP,
- ), []byte(cmenc.BalloonHKDFSalt))
+ kek, err = hkdf.Expand(
+ blake2bHash,
+ balloon.H(blake2bHash,
+ passwd,
+ append(binding[:], salt...),
+ *balloonS, *balloonT, *balloonP,
+ ),
+ cmenc.BalloonHKDFInfo,
+ chacha20poly1305.KeySize,
+ )
if err != nil {
log.Fatal(err)
}
kem.Encap = &encap
{
ikm := bytes.Join([][]byte{
- binding[:],
encap, pub.V,
keySNTRUP[:], keyX25519,
}, []byte{})
+ var prk []byte
+ prk, err = hkdf.Extract(blake2bHash, ikm, binding[:])
+ if err != nil {
+ log.Fatal(err)
+ }
var kek []byte
- kek, err = hkdf.Extract(blake2b256,
- ikm, []byte(cmenc.SNTRUP4591761X25519Salt))
+ kek, err = hkdf.Expand(
+ blake2bHash,
+ prk,
+ cmenc.SNTRUP4591761X25519Info,
+ chacha20poly1305.KeySize,
+ )
if err != nil {
log.Fatal(err)
}
kem.Encap = &encap
{
ikm := bytes.Join([][]byte{
- binding[:],
encap, pub.V,
keyMcEliece[:], keyX25519,
}, []byte{})
+ var prk []byte
+ prk, err = hkdf.Extract(cmhash.NewSHAKE256, ikm, binding[:])
+ if err != nil {
+ log.Fatal(err)
+ }
var kek []byte
- kek, err = hkdf.Extract(cmhash.NewSHAKE256,
- ikm, []byte(cmenc.ClassicMcEliece6960119X25519Salt))
+ kek, err = hkdf.Expand(
+ cmhash.NewSHAKE256,
+ prk,
+ cmenc.ClassicMcEliece6960119X25519Info,
+ chacha20poly1305.KeySize,
+ )
if err != nil {
log.Fatal(err)
}
const (
BalloonSaltLen = 8
- BalloonHKDFSalt = "keks/cm/encrypted/balloon-blake2b-hkdf"
+ BalloonHKDFInfo = "keks/cm/encrypted/balloon-blake2b-hkdf"
)
type BalloonCost struct {
)
const (
- SNTRUP4591761X25519Salt = "keks/cm/encrypted/sntrup4591761-x25519-hkdf-blake2b"
- ClassicMcEliece6960119X25519Salt = "keks/cm/encrypted/mceliece6960119-x25519-hkdf-shake256"
+ SNTRUP4591761X25519Info = "keks/cm/encrypted/sntrup4591761-x25519-hkdf-blake2b"
+ ClassicMcEliece6960119X25519Info = "keks/cm/encrypted/mceliece6960119-x25519-hkdf-shake256"
)
type KEM struct {
? ciphertext: blob,
}
-dem = dem-chacha20poly1305 / dem-kuznechik-ctracpkm-hmac-hkdf
+dem = dem-chacha20poly1305 / dem-kuznechik-ctracpkm-hmac
dem-chacha20poly1305 = {a: "chacha20poly1305"}
-
-dem-kuznechik-ctracpkm-hmac-hkdf = {
- a: "kuznechik-ctracpkm-hmac-hkdf",
- seed: bytes,
- iv: bytes,
-}
+dem-kuznechik-ctracpkm-hmac-hkdf = {a: "kuznechik-ctracpkm-hmac"}
kem = kem-generic /
kem-balloon-blake2b-hkdf /
cek: bytes,
ukm: bytes,
pub: bytes,
- iv: bytes,
? to: uuid,
}
@cindex cm-encrypted-chacha20poly1305
@subsection Encrypted data with ChaCha20-Poly1305 DEM
- @code{/dem/a} equals to "chacha20poly1305". Data is split on 64 KiB
- chunks which are encrypted the following way:
+ @code{/dem/a} equals to "chacha20poly1305".
+
+ CEK is 32+12=44 bytes long and contains the key itself and
+ initialisation vector used in nonce.
+
+ Data is split on 128 KiB chunks which are encrypted the following way:
@verbatim
-ChaCha20-Poly1305(
- key=cek, nonce=BE(11-byte counter) || tail-flag,
- data=16*0x00 || chunk, ad="")
+KEY || IV = CEK
+ChaCha20-Poly1305(key=KEY, ad="",
+ nonce=IV XOR (BE(11-byte counter) || tail-flag),
+ data=32*0x00 || chunk)
@end verbatim
- where @code{counter} starts at zero and incremented with each chunk.
+ @code{counter} starts at zero and incremented with each chunk.
+
@code{tail-flag} is a byte indicating if that is the last chunk in the
payload. It equals to 0x01 for the last chunk and to 0x00 for other ones.
Last chunk should be smaller than previous ones, maybe even empty.
- @code{/ciphertext}'s chunk length equals to 16+64KiB+16 bytes.
+ @code{/ciphertext}'s chunk length equals to 32+128KiB+16 bytes.
-@node cm-encrypted-kuznechik-ctracpkm-hmac-hkdf
-@cindex cm-encrypted-kuznechik-ctracpkm-hmac-hkdf
-@subsection Encrypted data with Kuznechik-CTR-ACPKM+HMAC-HKDF DEM
+@node cm-encrypted-kuznechik-ctracpkm-hmac
+@cindex cm-encrypted-kuznechik-ctracpkm-hmac
+@subsection Encrypted data with Kuznechik-CTR-ACPKM+HMAC DEM
- @code{/dem/a} equals to "kuznechik-ctracpkm-hmac-hkdf".
- @code{/dem/seed} contains 16 bytes for the HKDF invocation below.
- @code{/dem/iv} contains 8 bytes of initialisation vector.
+ @code{/dem/a} equals to "kuznechik-ctracpkm-hmac".
+ CEK is 32+8+32=72 bytes long.
@verbatim
-Kenc, Kauth = HKDF-Extract(Streebog-512,
- salt="keks/cm/encrypted/kuznechik-ctracpkm-hmac-hkdf",
- secret=seed || CEK)
+Kenc || IV || Kauth = CEK
@end verbatim
Encryption is performed with Kuznechik (ГОСТ Р 34.12-2015) block cipher
in CTR-ACPKM mode of operation (Р 1323565.1.017) with 256KiB section
- size. Authentication of ciphertext is performed with Streebog-512 (ГОСТ
- Р 34.11-2012) in HMAC mode.
+ size and IV initialisation vector. Authentication of ciphertext is
+ performed with Streebog-512 (ГОСТ Р 34.11-2012) in HMAC mode.
- @code{/ciphertext}'s chunk length equals to 64KiB bytes.
+ @code{/ciphertext}'s chunk length equals to 128KiB bytes.
@node cm-encrypted-balloon-blake2b-hkdf
@cindex cm-encrypted-balloon-blake2b-hkdf
-@subsection Encrypted data with Balloon-BLAKE2b+HKDF-BLAKE2b KEM
+@subsection Encrypted data with Balloon-BLAKE2b+HKDF KEM
@code{/kem/*/a} equals to "balloon-blake2b-hkdf".
Recipient map must also contain additional fields:
@code{/kem/*/cek} is encrypted the following way:
@verbatim
-KEK = HKDF-Extract(BLAKE2b-256,
- salt="keks/cm/encrypted/balloon-blake2b-hkdf",
- secret=balloon(BLAKE2b-256, passphrase, bind || salt, s, t, p))
-ChaCha20-Poly1305(data=16*0x00 || CEK, key=KEK, nonce=12*0x00, ad="")
+KEK = HKDF-Expand(BLAKE2b,
+ prk=balloon(BLAKE2b, passphrase, bind || salt, s, t, p),
+ info="keks/cm/encrypted/balloon-blake2b-hkdf")
+ChaCha20-Poly1305(data=CEK, key=KEK, nonce=12*0x00, ad="")
@end verbatim
@node cm-encrypted-gost3410-hkdf-kexp15
Additional 16-bytes keying material.
@item /to/*/pub: bytes
Sender's ephemeral 512-bit public key.
- @item /to/*/iv: bytes
- 8-byte initialisation vector for KExp15.
@end table
ГОСТ Р 34.10-2012 VKO parameter set A/C ("gost3410-256A", "gost3410-512C")
and KExp15 (Р 1323565.1.017) key wrapping algorithm:
@verbatim
-KEKenv, KEKauth = HKDF-Extract(Streebog-512,
- salt="keks/cm/encrypted/gost3410-hkdf-kexp15",
- secret=bind || VKO(...))
-KExp15(KEKenc, KEKauth, IV, CEK):
- return CTR(Kenc, CEK+CMAC(Kauth, IV+CEK), IV=IV)
+PRK = HKDF-Extract(Streebog-512, salt=bind, ikm=VKO(..., ukm=UKM))
+KEKenv, IV, KEKauth = HKDF-Expand(Streebog-512, prk=PRK,
+ info="keks/cm/encrypted/gost3410-hkdf-kexp15")
+KExp15(KEKenc, KEKauth, IV, CEK) = CTR(Kenc, CEK || CMAC(Kauth, IV || CEK), IV=IV)
@end verbatim
@node cm-encrypted-sntrup4591761-x25519-hkdf-blake2b
@code{/kem/*/cek} is encrypted the following way:
@verbatim
-KEK = HKDF-Extract(BLAKE2b-256,
- salt="keks/cm/encrypted/sntrup4591761-x25519-hkdf-blake2b",
- secret=bind ||
+PRK = HKDF-Extract(BLAKE2b, salt=bind,
+ secret=
sntrup4591761-sender-ciphertext ||
x25519-sender-public-key ||
sntrup4591761-recipient-public-key ||
x25519-recipient-public-key ||
sntrup4591761-shared-key ||
x25519-shared-key)
-ChaCha20-Poly1305(data=16*0x00 || CEK, key=KEK, nonce=12*0x00, ad="")
+KEK = HKDF-Expand(BLAKE2b, prk=PRK,
+ info="keks/cm/encrypted/sntrup4591761-x25519-hkdf-blake2b")
+ChaCha20-Poly1305(data=CEK, key=KEK, nonce=12*0x00, ad="")
@end verbatim
@node cm-encrypted-mceliece6960119-x25519-hkdf-shake256
@code{/kem/*/cek} is encrypted the following way:
@verbatim
-KEK = HKDF-Extract(SHAKE256,
- salt="keks/cm/encrypted/mceliece6960119-x25519-hkdf-shake256",
- secret=bind ||
+PRK = HKDF-Extract(SHAKE256, salt=bind,
+ secret=
mceliece6960119-sender-ciphertext ||
x25519-sender-public-key ||
mceliece6960119-recipient-public-key ||
x25519-recipient-public-key ||
mceliece6960119-shared-key ||
x25519-shared-key)[:32]
-ChaCha20-Poly1305(data=16*0x00 || CEK, key=KEK, nonce=12*0x00, ad="")
+KEK = HKDF-Expand(SHAKE256, prk=PRK,
+ info="keks/cm/encrypted/mceliece6960119-x25519-hkdf-shake256")
+ChaCha20-Poly1305(data=CEK, key=KEK, nonce=12*0x00, ad="")
@end verbatim
@table @code
@item chacha20poly1305
@code{@ref{cm-encrypted-chacha20poly1305}}
-@item kuznechik-ctracpkm-hmac-hkdf
- @code{@ref{cm-encrypted-kuznechik-ctracpkm-hmac-hkdf}}
+@item kuznechik-ctracpkm-hmac
+ @code{@ref{cm-encrypted-kuznechik-ctracpkm-hmac}}
@end table
@node AI KEM