To safely use them in pinning and forgetting about possible collisions.
fdPubR := os.NewFile(FdPubR, "pub-in")
var pubs []cm.AV
- var pubIds []uuid.UUID
+ var pubIds [][]byte
if data, err := io.ReadAll(fdPubR); err == nil {
for len(data) > 0 {
var signed *sign.Signed
if kem.Encap == nil {
log.Fatalln("missing encap")
}
- if len(*kem.Encap) != sntrup4591761.CiphertextSize+32 {
+ if len(kem.Encap) != sntrup4591761.CiphertextSize+32 {
log.Fatalln("invalid encap len")
}
for _, prv := range prvs {
log.Fatal(err)
}
var theirSNTRUP sntrup4591761.Ciphertext
- copy(theirSNTRUP[:], *kem.Encap)
+ copy(theirSNTRUP[:], kem.Encap)
keySNTRUP, eq := sntrup4591761.Decapsulate(&theirSNTRUP, &ourSNTRUP)
if eq != 1 {
log.Println("can not KEM, skipping")
}
var theirX25519 *ecdh.PublicKey
theirX25519, err = x25519.NewPublicKey(
- (*kem.Encap)[sntrup4591761.CiphertextSize:],
+ kem.Encap[sntrup4591761.CiphertextSize:],
)
if err != nil {
log.Fatal(err)
ourX25519.PublicKey().Bytes()...,
)
ikm := bytes.Join([][]byte{
- *kem.Encap, pub,
+ kem.Encap, pub,
keySNTRUP[:], keyX25519,
}, []byte{})
var prk []byte
if kem.Encap == nil {
log.Fatalln("missing encap")
}
- if len(*kem.Encap) != mceliece6960119.CiphertextSize+32 {
+ if len(kem.Encap) != mceliece6960119.CiphertextSize+32 {
log.Fatalln("invalid encap len")
}
for _, prv := range prvs {
if err != nil {
log.Fatal(err)
}
- theirMcEliece := (*kem.Encap)[:len(*kem.Encap)-32]
+ theirMcEliece := (kem.Encap)[:len(kem.Encap)-32]
var keyMcEliece []byte
keyMcEliece, err = mceliece6960119.Decapsulate(ourMcEliece, theirMcEliece)
if err != nil {
}
var theirX25519 *ecdh.PublicKey
theirX25519, err = x25519.NewPublicKey(
- (*kem.Encap)[len(*kem.Encap)-32:],
+ (kem.Encap)[len(kem.Encap)-32:],
)
if err != nil {
log.Fatal(err)
ourX25519.PublicKey().Bytes()...,
)
ikm := bytes.Join([][]byte{
- *kem.Encap, pub,
+ kem.Encap, pub,
keyMcEliece, keyX25519,
}, []byte{})
var prk []byte
rand.Read(bSalt)
kem := cmenc.KEM{
A: cmballoon.BalloonBLAKE2bHKDF,
- Salt: &bSalt,
+ Salt: bSalt,
BalloonCost: &ballooncost.Cost{
S: uint64(*balloonS),
T: uint64(*balloonT),
}
kem := cmenc.KEM{A: sntrup4591761x25519.SNTRUP4591761X25519HKDFBLAKE2b}
encap := append(ciphertext[:], ourPubX25519.Bytes()...)
- kem.Encap = &encap
+ kem.Encap = encap
{
ikm := bytes.Join([][]byte{
encap, pub.V,
kem.CEK = cekp.Bytes()
}
if *includeTo {
- kem.To = &pubIds[pubId]
+ kem.To = pubIds[pubId]
}
kems = append(kems, kem)
case mceliece6960119x25519.ClassicMcEliece6960119X25519:
}
kem := cmenc.KEM{A: mceliece6960119x25519.ClassicMcEliece6960119X25519HKDFSHAKE256}
encap := append(ciphertext[:], ourPubX25519.Bytes()...)
- kem.Encap = &encap
+ kem.Encap = encap
{
ikm := bytes.Join([][]byte{
encap, pub.V,
kem.CEK = cekp.Bytes()
}
if *includeTo {
- kem.To = &pubIds[pubId]
+ kem.To = pubIds[pubId]
}
kems = append(kems, kem)
default:
"strings"
"time"
- "github.com/google/uuid"
"go.cypherpunks.su/keks"
"go.cypherpunks.su/keks/cm"
mceliece6960119x25519 "go.cypherpunks.su/keks/cm/enc/mceliece6960119-x25519"
if err != nil {
log.Fatal(err)
}
- pubLoad.Id, err = uuid.NewRandomFromReader(bytes.NewReader(hasher.Sum(nil)))
+ pubLoad.Id = hasher.Sum(nil)
if err != nil {
log.Fatal(err)
}
-algo $keyalgo -ku sig $sub \
5>$TMPDIR/sign.$keyalgo.pub 9>$TMPDIR/sign.$keyalgo.prv"
dd if=/dev/urandom of=$TMPDIR/sign.$keyalgo.data bs=300K count=1 2>/dev/null
-encTo="-encrypted-to $(uuidgen)"
-badEncTo="-encrypted-to $(uuidgen)"
+encTo=$(dd if=/dev/urandom bs=32 count=1 2>/dev/null | xxd -c 0 -p)
+badEncTo=$(dd if=/dev/urandom bs=32 count=1 2>/dev/null | xxd -c 0 -p)
+encTo="-encrypted-to $encTo"
+badEncTo="-encrypted-to $badEncTo"
for merkle in "" "-merkle" ; do
"cat $TMPDIR/sign.$algo.detached.sig $TMPDIR/sign.$keyalgo.data |
cmsigtool -detached -verify -type $typ 4<$TMPDIR/sign.$keyalgo.pub"
test_expect_success "$algo: differing type" "! cmsigtool -detached \
- -verify -pub 4<$TMPDIR/sign.$keyalgo.pub <$TMPDIR/sign.$algo.detached.sig >/dev/null"
+ -verify 4<$TMPDIR/sign.$keyalgo.pub <$TMPDIR/sign.$algo.detached.sig >/dev/null"
test_expect_success "$algo: good encTo" "! cmsigtool -detached \
- -verify -pub $encTo 4<$TMPDIR/sign.$keyalgo.pub <$TMPDIR/sign.$algo.detached.sig >/dev/null"
+ -verify $encTo 4<$TMPDIR/sign.$keyalgo.pub <$TMPDIR/sign.$algo.detached.sig >/dev/null"
test_expect_success "$algo: bad encTo" "! cmsigtool -detached \
- -verify -pub $badEncTo 4<$TMPDIR/sign.$keyalgo.pub <$TMPDIR/sign.$algo.detached.sig >/dev/null"
+ -verify $badEncTo 4<$TMPDIR/sign.$keyalgo.pub <$TMPDIR/sign.$algo.detached.sig >/dev/null"
done
import (
"bufio"
"bytes"
+ "encoding/hex"
"flag"
"hash"
"io"
"log"
"os"
- "slices"
"time"
- "github.com/google/uuid"
-
"go.cypherpunks.su/keks"
cmhash "go.cypherpunks.su/keks/cm/hash"
"go.cypherpunks.su/keks/cm/sign"
flag.Usage = usage
typ := flag.String("type", "data", "Set/check the load type")
verify := flag.Bool("verify", false, "Do verification")
- var encryptedTo []uuid.UUID
- flag.Func("encrypted-to", "Set/check encrypted-to, UUID", func(v string) error {
- to, err := uuid.Parse(v)
+ var encryptedTo [][]byte
+ flag.Func("encrypted-to", "Set/check encrypted-to, hex", func(v string) error {
+ to, err := hex.DecodeString(v)
if err != nil {
return err
}
log.Fatal("missing encrypted-to")
}
found := false
- for _, to := range *sig.TBS.EncryptedTo {
- if slices.Contains(encryptedTo, to) {
- found = true
- break
+ for _, their := range sig.TBS.EncryptedTo {
+ for _, our := range encryptedTo {
+ if bytes.Equal(our, their) {
+ found = true
+ break
+ }
}
}
if !found {
when := time.Now().UTC().Truncate(time.Millisecond)
sigTbs.When = &when
}
- if len(encryptedTo) > 0 {
- sigTbs.EncryptedTo = &encryptedTo
- }
+ sigTbs.EncryptedTo = encryptedTo
if err = signed.SignWith(pub.PubLoad(), signer, sigTbs); err != nil {
log.Fatal(err)
}
}
func Decapsulate(kem cmenc.KEM, encSalt, passphrase []byte) (cek []byte, err error) {
- if kem.Salt == nil {
+ if len(kem.Salt) == 0 {
return nil, errors.New("missing salt")
}
if kem.BalloonCost == nil {
balloon.H(
blake2bHash,
passphrase,
- *kem.Salt,
+ kem.Salt,
int(kem.BalloonCost.S),
int(kem.BalloonCost.T),
int(kem.BalloonCost.P),
import "github.com/google/uuid"
type Encrypted struct {
- DEM DEM `keks:"dem"`
- KEM []KEM `keks:"kem"`
- Salt uuid.UUID `keks:"salt"`
-
- Payload []byte `keks:"payload,omitempty"`
+ DEM DEM `keks:"dem"`
+ KEM []KEM `keks:"kem"`
+ Payload []byte `keks:"payload,omitempty"`
+ Salt uuid.UUID `keks:"salt"`
}
package encrypted
import (
- "github.com/google/uuid"
-
balloon "go.cypherpunks.su/keks/cm/enc/balloon/cost"
)
)
type KEM struct {
- A string `keks:"a"`
- CEK []byte `keks:"cek"`
- To *uuid.UUID `keks:"to,omitempty"`
+ A string `keks:"a"`
+ CEK []byte `keks:"cek"`
+ To []byte `keks:"to,omitempty"`
// balloon-blake2b-hkdf related
BalloonCost *balloon.Cost `keks:"cost,omitempty"`
- Salt *[]byte `keks:"salt,omitempty"`
+ Salt []byte `keks:"salt,omitempty"`
- Encap *[]byte `keks:"encap,omitempty"`
+ Encap []byte `keks:"encap,omitempty"`
}
type Signer struct {
Prv *ed25519.PrivateKey
- mode mode.Mode
prehasher *hash.Hash
+ mode mode.Mode
}
func (s *Signer) SetMode(m mode.Mode) error {
KUSig = "sig" // Signing-capable key usage
KUKEM = "kem" // Key-encapsulation-mechanism key usage
PubMagic = keks.Magic("cm/pub")
+ FPRLen = 32 // fingerprint's length
)
var (
Sub map[string]string `keks:"sub"`
Crit *[]map[string]any `keks:"crit,omitempty"`
Pub []cm.AV `keks:"pub"`
- Id uuid.UUID `keks:"id"`
+ Id []byte `keks:"id"`
}
// Parse Signed contents as PubLoad (certificate) and check its
if len(load.Pub) == 0 {
return errors.New("PubParse: empty pub")
}
- if load.Id == uuid.Nil {
- return errors.New("PubParse: empty id")
+ if len(load.Id) != FPRLen {
+ return errors.New("PubParse: invalid id len")
}
for _, pub := range load.Pub {
if len(pub.A) == 0 || len(pub.V) == 0 {
return
}
sig := signed.Sigs[0]
- if sig.TBS.SID != parent.Id {
+ if !bytes.Equal(sig.TBS.SID, parent.Id) {
err = errors.New("sid != parent pub id")
return
}
}
}
sid := signed.Sigs[0].TBS.SID
- if sid == signed.PubLoad().Id {
+ if bytes.Equal(sid, signed.PubLoad().Id) {
return signed.CertificationCheckSignatureFrom(signed.PubLoad(), nil)
}
- idToPub := make(map[uuid.UUID]*Signed, len(pubs))
+ type FPR [FPRLen]byte
+ idToPub := make(map[FPR]*Signed, len(pubs))
for _, cer := range pubs {
pubLoad := cer.PubLoad()
if !pubLoad.Can(KUSig) || len(pubLoad.Pub) != 1 {
err = errors.New("pub can not sign")
return
}
- idToPub[pubLoad.Id] = cer
+ idToPub[FPR(pubLoad.Id)] = cer
}
- signer := idToPub[sid]
+ signer := idToPub[FPR(sid)]
if signer == nil {
err = fmt.Errorf("no pub found for sid: %v", signed.Sigs[0].TBS.SID)
return
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"`
+ EncryptedTo [][]byte `keks:"encrypted-to,omitempty"`
+ SID []byte `keks:"sid"`
}
type Sig struct {
salt: bytes,
}
+fpr = bytes .size 32
+
kem-gost3410-hkdf-kexp15 = {
a: "gost3410-hkdf-kexp15",
cek: bytes,
ukm: bytes,
pub: bytes,
- ? to: uuid, ; recipient's public key id
+ ? to: fpr, ; recipient's public key fingerprint
}
kem-sntrup4591761-x25519-hkdf-blake2b = {
a: "sntrup4591761-x25519-hkdf-blake2b",
cek: bytes,
encap: bytes,
- ? to: uuid, ; recipient's public key id
+ ? to: fpr, ; recipient's public key fingerprint
}
kem-mceliece6960119-x25519-hkdf-shake256 = {
a: "mceliece6960119-x25519-hkdf-shake256 ",
cek: bytes,
encap: bytes,
- ? to: uuid, ; recipient's public key id
+ ? to: fpr, ; recipient's public key fingerprint
}
If KEM uses public-key based cryptography, then recipient's
@ref{cm-pub, public key}(s) should be provided, which may lack the
-signatures at all. Optional @code{/kem/*/to}, public key's identifier,
+signatures at all. Optional @code{/kem/*/to}, public key's fingerprint,
may provide a hint for quickly searching for the key on the recipient's
side.
-@code{/salt} is used in KEMs. Either UUIDv4 or UUIDv7 are recommended.
+@code{/salt} is used in KEMs. UUIDv4 is recommended.
@node cm-encrypted-chacha20poly1305
@cindex cm-encrypted-chacha20poly1305
ai = text ; algorithm identifier
av = {a: ai, v: bytes}
+fpr = bytes .size 32
+ku = "sig" / "kem" / "app-name" / text
cm-pub-load = {
? ku: set,
- id: uuid,
+ id: fpr,
pub: [+ av],
sub: {text => text}, ; subject
? crit: {+ crit-ext-type => any},
* text => any
}
-ku = "sig" / "kem" / "app-name" / text
crit-ext-type = text
+validity = [since: tai64, till: tai64]
+
cm-pub-sig-tbs = {
- sid: uuid, ; signer's public key id
cid: uuid, ; certification id
exp: validity,
+ sid: fpr, ; signer's public key fingerprint
* text => any
}
-
-validity = [since: tai64, till: tai64]
arbitrary data, then single public key @strong{should} be used, with a
key usage like "sig".
-Each public key contain the key itself, its algorithm identifier and key
-identifier, that @strong{should} be generated as an UUIDv4 based on the
-hash of the key.
-
@item id
-Public key(s)'s identifier @strong{should} be generated as an UUIDv4
-based on the hash of the encoded @code{pub} field.
+Public key(s)'s fingerprint @strong{should} be generated as 256-bit hash
+hash of the encoded @code{pub} field. If not stated otherwise for
+specific algorithm.
@item ku
Intended public key(s) usage.
@table @code
@item sid
-Signing public key identifier.
+Signing public key's fingerprint.
@item cid
Certification unique identifier. UUIDv7 is a good choice. But it may be
"load": {
"t": "pub",
"v": {
- "id": UUID(hash(pub)),
+ "id": hash(pub),
"pub": [{"a": "gost3410-256A", "v"}],
"sub": {"n": "test"},
},
"sigs": [{
"tbs": {
"cid": UUID(certification id),
- "sid": UUID(signer's pkid),
+ "sid": signer's pkid,
"exp": [TAI64, TAI64],
},
"sign": {"a": "gost3410-256A", "v": 'signature'},
Algorithm identifiers for the public key: @code{gost3410-256A},
@code{gost3410-512C}.
-Public key's identifier should be calculated using big-endian
+Public key's fingerprint should be calculated using big-endian
Streebog-256 hash.
@node cm-pub-ed25519-blake2b
Same calculation and serialisation rules must be used as with
@code{@ref{cm-signed-ed25519-blake2b}}.
-Public key's identifier should be calculated using BLAKE2b hash with 128
-or 256 bit output length specified.
+Public key's fingerprint should be calculated using BLAKE2b hash with
+256 bit output length specified.
-Algorithm identifier for the public key: @code{ed25519ph-blake2b}.
+Algorithm identifier for the public key: @code{ed25519-blake2b}.
@node cm-pub-sntrup4591761-x25519
@cindex cm-pub-sntrup4591761-x25519
value is a concatenation of 1218-byte SNTRUP4591761 public key and
32-byte X25519 one.
-Public key's identifier should be calculated using BLAKE2b hash with 128
-or 256 bit output length specified.
+Public key's fingerprint should be calculated using BLAKE2b hash with
+256 bit output length specified.
@node cm-pub-mceliece6960119-x25519
@cindex cm-pub-mceliece6960119-x25519
value is a concatenation of 1047319-byte @code{mceliece6960119} public key
and 32-byte X25519 one.
-Public key's identifier should be calculated using either SHAKE128 or
-SHAKE256 hash.
+Public key's fingerprint should be calculated using SHAKE128.
? pubs: [+ cm-pub],
}
+url = text
+
sig = {
tbs: sig-tbs,
sign: {a: ai, v: bytes},
* text => any
}
-url = text
+fpr = bytes .size 32
sig-tbs = {
- sid: uuid, ; signer's public key id
- ? encrypted-to: [+ uuid], ; recipient's public key ids
+ sid: fpr, ; signer's public key fingerprint
+ ? encrypted-to: [+ fpr], ; recipient's public key fingerprints
? when: tai64 / tai64n,
* text => any
}
If signed data is also intended to be @ref{cm-encrypted, encrypted},
then @code{/sigs/*/tbs/encrypted-to} should be set to corresponding
-recipient's public key id(s).
+recipient's public key fingerprint(s).
@node cm-signed-gost3410
@cindex cm-signed-gost3410
Algorithm identifiers for the signature: @code{gost3410-256A-merkle},
@code{gost3410-512C-merkle}.
-
@node cm-signed-ed25519-blake2b
@cindex cm-signed-ed25519-blake2b
@cindex cm-signed-ed25519ph-blake2b