"go.cypherpunks.su/keks/types"
)
-const BindFdNum = 3 + 1
-
func blake2bHash() hash.Hash {
h, err := blake2b.New512(nil)
if err != nil {
func main() {
log.SetFlags(log.Lshortfile)
flag.Usage = usage
- setBind := flag.String("bind", "", "Set that /bind instead of autogeneration")
+ setSalt := flag.String("salt", "", "Set that /salt instead of autogeneration")
includeTo := flag.Bool("include-to", false, `Include "to" field in KEMs`)
passphrase := flag.Bool("p", false, "Use passphrase")
balloonS := flag.Int("balloon-s", 1<<16, "Balloon's space cost")
log.Fatal(err)
}
}
- if encrypted.Bind == uuid.Nil {
- log.Fatalln("unll bind")
- }
- if bindFd := os.NewFile(BindFdNum, "bind"); bindFd != nil {
- bindFd.WriteString(encrypted.Bind.String() + "\n")
- bindFd.Close()
- }
if encrypted.DEM.A != cm.ChaCha20Poly1305 {
log.Fatalln("unsupported DEM:", encrypted.DEM.A)
}
balloon.H(
blake2bHash,
passwd,
- append(encrypted.Bind[:], *kem.Salt...),
+ append(encrypted.Salt[:], *kem.Salt...),
int(kem.Cost.S), int(kem.Cost.T), int(kem.Cost.P),
),
cmenc.BalloonHKDFInfo,
keySNTRUP[:], keyX25519,
}, []byte{})
var prk []byte
- prk, err = hkdf.Extract(blake2bHash, ikm, encrypted.Bind[:])
+ prk, err = hkdf.Extract(blake2bHash, ikm, encrypted.Salt[:])
if err != nil {
log.Fatal(err)
}
continue
}
cek = cekp
+ break
}
}
case cm.ClassicMcEliece6960119X25519HKDFSHAKE256:
if err != nil {
log.Fatal(err)
}
-
x25519 := ecdh.X25519()
var ourX25519 *ecdh.PrivateKey
ourX25519, err = x25519.NewPrivateKey(prv.V[len(prv.V)-32:])
}, []byte{})
var prk []byte
prk, err = hkdf.Extract(
- cmhash.NewSHAKE256, ikm, encrypted.Bind[:])
+ cmhash.NewSHAKE256, ikm, encrypted.Salt[:])
if err != nil {
log.Fatal(err)
}
continue
}
cek = cekp
+ break
}
}
default:
log.Fatal(err)
}
} else {
- var binding uuid.UUID
- if *setBind == "" {
- binding, err = uuid.NewV7()
+ var salt uuid.UUID
+ if *setSalt == "" {
+ salt, err = uuid.NewRandom()
} else {
- binding, err = uuid.Parse(*setBind)
+ salt, err = uuid.Parse(*setSalt)
}
if err != nil {
log.Fatal(err)
}
- if bindFd := os.NewFile(BindFdNum, "bind"); bindFd != nil {
- bindFd.WriteString(binding.String() + "\n")
- bindFd.Close()
- }
var kems []cmenc.KEM
cek = make([]byte, chacha20poly1305.KeySize+chacha20poly1305.NonceSize)
_, err = io.ReadFull(rand.Reader, cek)
log.Fatal("passphrases do not match")
}
}
- salt := make([]byte, cmenc.BalloonSaltLen)
- if _, err = io.ReadFull(rand.Reader, salt); err != nil {
+ bSalt := make([]byte, cmenc.BalloonSaltLen)
+ if _, err = io.ReadFull(rand.Reader, bSalt); err != nil {
log.Fatal(err)
}
kem := cmenc.KEM{
A: cm.BalloonBLAKE2bHKDF,
- Salt: &salt,
+ Salt: &bSalt,
Cost: &cmenc.BalloonCost{
S: uint64(*balloonS),
T: uint64(*balloonT),
blake2bHash,
balloon.H(blake2bHash,
passwd,
- append(binding[:], salt...),
+ append(salt[:], bSalt...),
*balloonS, *balloonT, *balloonP,
),
cmenc.BalloonHKDFInfo,
keySNTRUP[:], keyX25519,
}, []byte{})
var prk []byte
- prk, err = hkdf.Extract(blake2bHash, ikm, binding[:])
+ prk, err = hkdf.Extract(blake2bHash, ikm, salt[:])
if err != nil {
log.Fatal(err)
}
keyMcEliece[:], keyX25519,
}, []byte{})
var prk []byte
- prk, err = hkdf.Extract(cmhash.NewSHAKE256, ikm, binding[:])
+ prk, err = hkdf.Extract(cmhash.NewSHAKE256, ikm, salt[:])
if err != nil {
log.Fatal(err)
}
log.Fatal(err)
}
if _, err = keks.Encode(&hdr, &cmenc.Encrypted{
- Bind: binding,
+ Salt: salt,
KEM: kems,
DEM: cmenc.DEM{A: cm.ChaCha20Poly1305},
}, nil); err != nil {
TMPDIR=${TMPDIR:-/tmp}
dd if=/dev/urandom of=$TMPDIR/enc.data bs=300K count=1 2>/dev/null
-bind=$(uuidgen)
algo=mceliece6960119-x25519
algo0=$algo
-algo $algo -ku kem -subj A=$algo \
-prv $TMPDIR/enc.$algo.prv -pub $TMPDIR/enc.$algo.pub"
-test_expect_success "encrypting" "enctool -bind $bind \
+test_expect_success "encrypting" "enctool \
-pub $TMPDIR/enc.$algo0.pub -pub $TMPDIR/enc.$algo1.pub \
<$TMPDIR/enc.data >$TMPDIR/enc.enc"
test_expect_success "any: decrypting" "enctool -d \
-prv $TMPDIR/enc.$algo0.prv -prv $TMPDIR/enc.$algo1.prv \
- <$TMPDIR/enc.enc >$TMPDIR/enc.data.got 4>$TMPDIR/bind.got"
+ <$TMPDIR/enc.enc >$TMPDIR/enc.data.got"
test_expect_success "comparing" \
"test_cmp $TMPDIR/enc.data $TMPDIR/enc.data.got"
-echo $bind >$TMPDIR/bind
-test_expect_success "comparing bind" \
- "test_cmp $TMPDIR/bind $TMPDIR/bind.got"
test_expect_success "$algo0: decrypting" "enctool -d \
-prv $TMPDIR/enc.$algo0.prv \
func usage() {
fmt.Fprintf(os.Stderr, `Usage:
Encrypt to recipient(s):
- enctool -pub PUB [-pub ...] [-include-to] [-bind UUID]
- <DATA >DATA.encrypted [4>bind.value]
+ enctool -pub PUB [-pub ...] [-include-to] <DATA >DATA.encrypted
Encrypt on passphrase:
- enctool -p [-bind UUID] [-balloon-s X] [-balloon-t X] [-balloon-p X]
- <DATA >DATA.encrypted [4>bind.value]
+ enctool -p [-balloon-s X] [-balloon-t X] [-balloon-p X] <DATA >DATA.encrypted
Decrypt by providing possible KEMs:
- enctool -d [-p] [-prv PRV ...] <DATA.encrypted >DATA [4>bind.value]
+ enctool -d [-p] [-prv PRV ...] <DATA.encrypted >DATA
`)
flag.PrintDefaults()
-algo $keyalgo -ku sig $subj \
-prv $TMPDIR/sign.$keyalgo.prv -pub $TMPDIR/sign.$keyalgo.pub"
dd if=/dev/urandom of=$TMPDIR/sign.$keyalgo.data bs=300K count=1 2>/dev/null
-bind="-encrypted-binding $(uuidgen)"
-badBind="-encrypted-binding $(uuidgen)"
+encTo="-encrypted-to $(uuidgen)"
+badEncTo="-encrypted-to $(uuidgen)"
for merkle in "" "-merkle" ; do
algo=${keyalgo}${merkle}
test_expect_success "$algo: signing" "sigtool $merkle \
-prv $TMPDIR/sign.$keyalgo.prv -pub $TMPDIR/sign.$keyalgo.pub -type $typ \
- $bind <$TMPDIR/sign.$keyalgo.data >$TMPDIR/sign.$algo.sig"
+ $encTo <$TMPDIR/sign.$keyalgo.data >$TMPDIR/sign.$algo.sig"
test_expect_success "$algo: verifying" "sigtool \
-verify -pub $TMPDIR/sign.$keyalgo.pub -type $typ \
<$TMPDIR/sign.$algo.sig >$TMPDIR/sign.data.got"
"test_cmp $TMPDIR/sign.$keyalgo.data $TMPDIR/sign.data.got"
test_expect_success "$algo: differing type" "! sigtool \
-verify -pub $TMPDIR/sign.$keyalgo.pub <$TMPDIR/sign.$algo.sig >/dev/null"
-test_expect_success "$algo: good bind" "! sigtool \
- -verify -pub $TMPDIR/sign.$keyalgo.pub $bind <$TMPDIR/sign.$algo.sig >/dev/null"
-test_expect_success "$algo: bad bind" "! sigtool \
- -verify -pub $TMPDIR/sign.$keyalgo.pub $badBind <$TMPDIR/sign.$algo.sig >/dev/null"
+test_expect_success "$algo: good encTo" "! sigtool \
+ -verify -pub $TMPDIR/sign.$keyalgo.pub $encTo <$TMPDIR/sign.$algo.sig >/dev/null"
+test_expect_success "$algo: bad encTo" "! sigtool \
+ -verify -pub $TMPDIR/sign.$keyalgo.pub $badEncTo <$TMPDIR/sign.$algo.sig >/dev/null"
test_expect_success "$algo: detached signing" "sigtool -detached $merkle \
-prv $TMPDIR/sign.$keyalgo.prv -pub $TMPDIR/sign.$keyalgo.pub -type $typ \
sigtool -detached -verify -pub $TMPDIR/sign.$keyalgo.pub -type $typ"
test_expect_success "$algo: differing type" "! sigtool -detached \
-verify -pub $TMPDIR/sign.$keyalgo.pub <$TMPDIR/sign.$algo.detached.sig >/dev/null"
-test_expect_success "$algo: good bind" "! sigtool -detached \
- -verify -pub $TMPDIR/sign.$keyalgo.pub $bind <$TMPDIR/sign.$algo.detached.sig >/dev/null"
-test_expect_success "$algo: bad bind" "! sigtool -detached \
- -verify -pub $TMPDIR/sign.$keyalgo.pub $badBind <$TMPDIR/sign.$algo.detached.sig >/dev/null"
+test_expect_success "$algo: good encTo" "! sigtool -detached \
+ -verify -pub $TMPDIR/sign.$keyalgo.pub $encTo <$TMPDIR/sign.$algo.detached.sig >/dev/null"
+test_expect_success "$algo: bad encTo" "! sigtool -detached \
+ -verify -pub $TMPDIR/sign.$keyalgo.pub $badEncTo <$TMPDIR/sign.$algo.detached.sig >/dev/null"
done
"io"
"log"
"os"
+ "slices"
"time"
"github.com/google/uuid"
const BlobChunkLen = 128 * 1024
func main() {
+ log.SetFlags(log.Lshortfile)
flag.Usage = usage
prvPath := flag.String("prv", "", "Path to private key file")
pubPath := flag.String("pub", "", "Path to public key file")
typ := flag.String("type", "data", "Set/check the load type")
verify := flag.Bool("verify", false, "Do verification")
- encryptedBindingHex := flag.String("encrypted-binding", "",
- "Set/check encrypted-binding, UUID")
+ var encryptedTo []uuid.UUID
+ flag.Func("encrypted-to", "Set/check encrypted-to, UUID", func(v string) error {
+ to, err := uuid.Parse(v)
+ if err != nil {
+ return err
+ }
+ encryptedTo = append(encryptedTo, to)
+ return nil
+ })
detached := flag.Bool("detached", false, "Detached data mode")
noWhen := flag.Bool("no-when", false, `Do not include "when"`)
doMerkle := flag.Bool("merkle", false, "Use Merkle-tree based hasher")
-
flag.Parse()
- log.SetFlags(log.Lshortfile)
-
- encryptedBinding := uuid.Nil
- var err error
- if *encryptedBindingHex != "" {
- encryptedBinding, err = uuid.Parse(*encryptedBindingHex)
- if err != nil {
- log.Fatal(err)
- }
- }
if *pubPath == "" {
log.Fatal("no -pub is set")
if signed.Load.T != *typ {
log.Fatalln("differing load type:", signed.Load.T)
}
- if encryptedBinding != uuid.Nil {
- if sig.TBS.EncryptedBinding == nil {
- log.Fatal("missing encrypted-binding")
+ if len(encryptedTo) > 0 {
+ if sig.TBS.EncryptedTo == nil {
+ log.Fatal("missing encrypted-to")
+ }
+ found := false
+ for _, to := range *sig.TBS.EncryptedTo {
+ if slices.Contains(encryptedTo, to) {
+ found = true
+ break
+ }
}
- if *sig.TBS.EncryptedBinding != encryptedBinding {
- log.Fatalln("differing encrypted-binding:", *sig.TBS.EncryptedBinding)
+ if !found {
+ log.Fatalln("corresponding encrypted-to not found")
}
}
if prehash.T == "" {
when := time.Now().UTC().Truncate(time.Millisecond)
sigTbs.When = &when
}
- if encryptedBinding != uuid.Nil {
- sigTbs.EncryptedBinding = &encryptedBinding
+ if len(encryptedTo) > 0 {
+ sigTbs.EncryptedTo = &encryptedTo
}
if err = signed.SignWith(pub.PubLoad(), signer, sigTbs); err != nil {
log.Fatal(err)
func usage() {
fmt.Fprintf(os.Stderr, `Usage:
- sigtool -prv PRV -pub PUB [-type TYPE] [bind] <DATA >DATA.signed
- sigtool -verify -pub PUB [-type TYPE] [bind] <DATA.signed >DATA
- sigtool -detached -prv PRV -pub PUB [-type TYPE] [bind] <DATA >DATA.signature
- sigtool -detached -verify -pub PUB [-type TYPE] [bind] <(cat DATA.signature DATA)
+ sigtool -prv PRV -pub PUB [-type TYPE] <DATA >DATA.signed
+ sigtool -verify -pub PUB [-type TYPE] <DATA.signed >DATA
+ sigtool -detached -prv PRV -pub PUB [-type TYPE] <DATA >DATA.signature
+ sigtool -detached -verify -pub PUB [-type TYPE] <(cat DATA.signature DATA)
-"bind" is optional -encrypted-binding flag.
DATA.signed holds completely encapsulated DATA.
-detached mode keeps DATA completely separate.
type Encrypted struct {
DEM DEM `keks:"dem"`
KEM []KEM `keks:"kem"`
- Bind uuid.UUID `keks:"bind"`
+ Salt uuid.UUID `keks:"salt"`
Ciphertext *keks.BlobChunked `keks:"ciphertext,omitempty"`
}
}
type SigTBS struct {
- CID *uuid.UUID `keks:"cid,omitempty"`
- Exp *[]time.Time `keks:"exp,omitempty"`
- When *time.Time `keks:"when,omitempty"`
- EncryptedBinding *uuid.UUID `keks:"encrypted-binding,omitempty"`
- SID uuid.UUID `keks:"sid"`
+ CID *uuid.UUID `keks:"cid,omitempty"`
+ Exp *[]time.Time `keks:"exp,omitempty"`
+ When *time.Time `keks:"when,omitempty"`
+ EncryptedTo *[]uuid.UUID `keks:"encrypted-to,omitempty"`
+ SID uuid.UUID `keks:"sid"`
}
type Sig struct {
- TBS SigTBS `keks:"tbs,omitempty"`
+ TBS SigTBS `keks:"tbs"`
PubLoc *[]string `keks:"pub-loc,omitempty"`
Sign AV `keks:"sign"`
}
cm-encrypted = {
dem: dem,
kem: [+ kem],
- bind: uuid,
+ salt: uuid,
? ciphertext: blob,
}
cek: bytes,
ukm: bytes,
pub: bytes,
- ? to: uuid,
+ ? to: uuid, ; recipient's public key id
}
kem-sntrup4591761-x25519-hkdf-blake2b = {
a: "sntrup4591761-x25519-hkdf-blake2b",
cek: bytes,
encap: bytes,
- ? to: uuid,
+ ? to: uuid, ; recipient's public key id
}
kem-mceliece6960119-x25519-hkdf-shake256 = {
a: "mceliece6960119-x25519-hkdf-shake256 ",
cek: bytes,
encap: bytes,
- ? to: uuid,
+ ? to: uuid, ; recipient's public key id
}
may provide a hint for quickly searching for the key on the recipient's
side.
-@code{/bind} value can be used to bind the encrypted
-@code{@ref{cm-signed, cm-signed}} to the envelope.
-Either UUIDv4 or UUIDv7 are recommended.
+@code{/salt} is used in KEMs. Either UUIDv4 or UUIDv7 are recommended.
@node cm-encrypted-chacha20poly1305
@cindex cm-encrypted-chacha20poly1305
Recipient map must also contain additional fields:
@table @code
- @item /to/*/cost/s: uint64
+ @item /kem/*/cost/s: uint64
Balloon's space cost (buffer size, number of hash-output sized blocks).
- @item /to/*/cost/t: uint64
+ @item /kem/*/cost/t: uint64
Balloon's time cost (number of rounds).
- @item /to/*/cost/p: uint64
+ @item /kem/*/cost/p: uint64
Balloon's parallel cost (number of threads).
- @item /to/*/salt: bin
+ @item /kem/*/salt: bin
Salt.
@end table
@verbatim
KEK = HKDF-Expand(BLAKE2b,
- prk=balloon(BLAKE2b, passphrase, bind || salt, s, t, p),
+ prk=balloon(BLAKE2b, passphrase, /salt || /kem/salt, s, t, p),
info="keks/cm/encrypted/balloon-blake2b-hkdf")
ChaCha20-Poly1305(data=CEK, key=KEK, nonce=12*0x00, ad="")
@end verbatim
@code{/kem/*/cek} is encrypted the following way:
@verbatim
-PRK = HKDF-Extract(BLAKE2b, salt=bind,
+PRK = HKDF-Extract(BLAKE2b, salt=/salt,
secret=
sntrup4591761-sender-ciphertext ||
x25519-sender-public-key ||
@code{/kem/*/cek} is encrypted the following way:
@verbatim
-PRK = HKDF-Extract(SHAKE256, salt=bind,
+PRK = HKDF-Extract(SHAKE256, salt=/salt,
secret=
mceliece6960119-sender-ciphertext ||
x25519-sender-public-key ||
sig-tbs = {
sid: uuid, ; signer's public key id
- ? encrypted-binding: uuid,
+ ? encrypted-to: [+ uuid], ; recipient's public key ids
? when: tai64 / tai64n,
* text => any
}
@code{/sigs}, because some of them may be shared among signers.
If signed data is also intended to be @ref{cm-encrypted, encrypted},
-then @code{/sigs/*/tbs/encrypted-binding} should be set to
-@ref{cm-encrypted, encrypted}'s @code{/bind} value.
+then @code{/sigs/*/tbs/encrypted-to} should be set to corresponding
+recipient's public key id(s).
@node cm-signed-gost3410
@cindex cm-signed-gost3410