]> Cypherpunks repositories - keks.git/commitdiff
Directories with keys
authorSergey Matveev <stargrave@stargrave.org>
Mon, 6 Oct 2025 10:08:10 +0000 (13:08 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Tue, 7 Oct 2025 08:01:48 +0000 (11:01 +0300)
28 files changed:
go/cm/cmd/cmenctool/main.go
go/cm/cmd/cmenctool/multirecipient.t
go/cm/cmd/cmenctool/no-from.t [moved from go/cm/cmd/cmenctool/missing-from.t with 94% similarity]
go/cm/cmd/cmenctool/no-id.t [new file with mode: 0755]
go/cm/cmd/cmenctool/no-to.t [moved from go/cm/cmd/cmenctool/missing-to.t with 80% similarity]
go/cm/cmd/cmenctool/prv-encrypted.t
go/cm/cmd/cmenctool/pub.t
go/cm/cmd/cmenctool/sender-auth.t
go/cm/cmd/cmenctool/usage.go
go/cm/cmd/cmkeytool/certification.t
go/cm/cmd/cmkeytool/kem-generation.t
go/cm/cmd/cmkeytool/main.go
go/cm/cmd/cmkeytool/usage.go
go/cm/cmd/cmsigtool/basic.t
go/cm/cmd/cmsigtool/main.go
go/cm/cmd/cmsigtool/usage.go
go/cm/enc/enc.go
go/cm/sign/.gitignore
go/cm/sign/prv.go
go/cm/sign/pub.go
go/cm/sign/schema.go
go/cm/sign/signed.go
go/cm/sign/storage.go [new file with mode: 0644]
go/cm/utils/mk-bin
spec/Integrity
spec/cm/prv/index
tcl/schemas/prv.tcl [new file with mode: 0644]
tcl/schemas/signed.tcl

index 7efd191e72bd444b58a65c2fc8385add498db9bdea319c949a58ab36bf39862b..ebde3eae596d298107968d265aaf36ab4c18aebb047302484a859cc2a0c588a3 100644 (file)
@@ -57,16 +57,14 @@ import (
        "go.cypherpunks.su/keks/types"
 )
 
-const (
-       FdPubR = 4
-       FdPrvR = 8
-
-       X25519KeyLen = 32
-)
+const X25519KeyLen = 32
 
 var (
-       PrvDir = flag.String("prvs", "prvs", "Path to directory with private keys")
-       PubDir = flag.String("pubs", "pubs", "Path to directory with public keys")
+       PrvDir = flag.String("prvs", os.Getenv("CM_PRVS"),
+               "Path to directory with private keys")
+       PubDirPth = flag.String("pubs", os.Getenv("CM_PUBS"),
+               "Path to directory with public keys")
+       PubDir sign.PubStorageDir
 )
 
 func blake2bHash() hash.Hash {
@@ -77,17 +75,7 @@ func blake2bHash() hash.Hash {
        return h
 }
 
-func pubParse(pth string) (pubData *sign.PubData, err error) {
-       var data []byte
-       data, err = os.ReadFile(pth)
-       if err != nil {
-               return
-       }
-       var signed *sign.Signed
-       signed, _, err = sign.PubParse(data)
-       if err != nil {
-               return
-       }
+func pubDataFromSigned(signed *sign.Signed) (pubData *sign.PubData, err error) {
        pubData = signed.PubData()
        if !pubData.Can(sign.KUKEM) {
                err = fmt.Errorf("does not have %s key usage", sign.KUKEM)
@@ -99,17 +87,18 @@ func pubParse(pth string) (pubData *sign.PubData, err error) {
        return
 }
 
-func findPub(id []byte) (pub *sign.PubData, err error) {
-       pth := path.Join(
-               *PubDir, strings.ToUpper(hex.EncodeToString(id)),
-       )
-       if _, err = os.Stat(pth); err != nil {
-               if errors.Is(err, fs.ErrNotExist) {
-                       err = nil
-               }
+func pubParse(pth string) (pubData *sign.PubData, err error) {
+       var data []byte
+       data, err = os.ReadFile(pth)
+       if err != nil {
+               return
+       }
+       var signed *sign.Signed
+       signed, _, err = sign.PubParse(data)
+       if err != nil {
                return
        }
-       return pubParse(pth)
+       return pubDataFromSigned(signed)
 }
 
 func findPrv(id []byte) (av *cm.AV, err error) {
@@ -141,7 +130,7 @@ func findPrv(id []byte) (av *cm.AV, err error) {
                if v, err = d.Decode(); err != nil {
                        return
                }
-               if err = schema.Check("av", sign.PubSchemas, v); err != nil {
+               if err = schema.Check("prv", sign.PrvSchemas, v); err != nil {
                        return
                }
        }
@@ -162,6 +151,7 @@ func main() {
                `Assume that hexadecimal "from" value if missing`)
        noFrom := flag.Bool("no-from", false, `Do not include "from" field in KEMs`)
        noTo := flag.Bool("no-to", false, `Do not include "to" field in KEMs`)
+       noId := flag.Bool("no-id", false, `Do not include "id" field`)
        passphrase := flag.Bool("p", false, "Use passphrase")
        balloonS := flag.Int("balloon-s", 1<<17, "Balloon's space cost")
        balloonT := flag.Int("balloon-t", 4, "Balloon's time cost")
@@ -170,18 +160,19 @@ func main() {
        parallel := flag.Int("parallel", cmhash.DefaultNumCPU, "Parallel cryptors")
        noblob := flag.Bool("embed", false, "Include payload into container")
        fromPth := flag.String("from", "", "Path to sender's public key for authentication")
+       flag.Parse()
+
+       PubDir.Dir = *PubDirPth
        var pubs []cm.AV
        var pubIds [][]byte
-       flag.Func("to", "Path to recipient's public key", func(v string) error {
-               pubData, err := pubParse(v)
+       for _, pth := range flag.Args() {
+               pubData, err := pubParse(pth)
                if err != nil {
-                       log.Fatalln(v, ":", err)
+                       log.Fatalln(pth, ":", err)
                }
                pubs = append(pubs, pubData.Pub[0])
                pubIds = append(pubIds, pubData.Id)
-               return nil
-       })
-       flag.Parse()
+       }
 
        var err error
        var assumeTo []byte
@@ -283,6 +274,10 @@ func main() {
                if len(encrypted.KEM) == 0 {
                        log.Fatalln("no KEMs")
                }
+               var id uuid.UUID
+               if encrypted.Id != nil {
+                       id = *encrypted.Id
+               }
                for kemIdx, kem := range encrypted.KEM {
                        switch kem.A {
                        case cmballoon.BalloonBLAKE2bHKDF:
@@ -295,7 +290,7 @@ func main() {
                                if err != nil {
                                        log.Fatal(err)
                                }
-                               cek, err = cmballoon.Decapsulate(kem, encrypted.Id[:], passwd)
+                               cek, err = cmballoon.Decapsulate(kem, id[:], passwd)
                                if err != nil {
                                        log.Print(err)
                                        continue
@@ -341,13 +336,18 @@ func main() {
                                        if bytes.Equal(kem.From, bytes.Repeat([]byte{0}, 32)) {
                                                kem.From = assumeFrom
                                        }
-                                       from, err = findPub(kem.From)
+                                       var signed *sign.Signed
+                                       signed, err = PubDir.Get(kem.From)
                                        if err != nil {
                                                log.Fatalln("from:", err)
                                        }
-                                       if from == nil {
+                                       if signed == nil {
                                                log.Fatalln(kemIdx, kem.A, "can not find public key")
                                        }
+                                       from, err = pubDataFromSigned(signed)
+                                       if err != nil {
+                                               log.Fatalln("from:", err)
+                                       }
                                }
                                var ourSNTRUP sntrup761kem.PrivateKey
                                ourSNTRUP, err = scheme.UnmarshalBinaryPrivateKey(
@@ -433,9 +433,7 @@ func main() {
                                        kek, err = hkdf.Expand(
                                                blake2bHash,
                                                prk,
-                                               string(append(
-                                                       []byte(cmenc.SNTRUP761X25519Info),
-                                                       encrypted.Id[:]...)),
+                                               string(append([]byte(cmenc.SNTRUP761X25519Info), id[:]...)),
                                                chacha20poly1305.KeySize)
                                        if err != nil {
                                                log.Fatal(err)
@@ -487,13 +485,18 @@ func main() {
                                        if bytes.Equal(kem.From, bytes.Repeat([]byte{0}, 32)) {
                                                kem.From = assumeFrom
                                        }
-                                       from, err = findPub(kem.From)
+                                       var signed *sign.Signed
+                                       signed, err = PubDir.Get(kem.From)
                                        if err != nil {
                                                log.Fatalln("from:", err)
                                        }
-                                       if from == nil {
+                                       if signed == nil {
                                                log.Fatalln(kemIdx, kem.A, "can not find public key")
                                        }
+                                       from, err = pubDataFromSigned(signed)
+                                       if err != nil {
+                                               log.Fatalln("from:", err)
+                                       }
                                }
                                var ourMcEliece *mceliece6960119.PrivateKey
                                ourMcEliece, err = mceliece6960119.UnmarshalBinaryPrivateKey(
@@ -525,7 +528,7 @@ func main() {
                                                keyMcEliece,
                                                string(append(
                                                        []byte(cmenc.ClassicMcEliece6960119X25519DecapInfo),
-                                                       encrypted.Id[:]...)),
+                                                       id[:]...)),
                                                chacha20poly1305.KeySize+chacha20poly1305.NonceSizeX,
                                        )
                                        if err != nil {
@@ -611,7 +614,7 @@ func main() {
                                                prk,
                                                string(append(
                                                        []byte(cmenc.ClassicMcEliece6960119X25519Info),
-                                                       encrypted.Id[:]...)),
+                                                       id[:]...)),
                                                chacha20poly1305.KeySize,
                                        )
                                        if err != nil {
@@ -649,10 +652,12 @@ func main() {
                }
        } else {
                var id uuid.UUID
-               if *setId == "" {
-                       id, err = uuid.NewRandom()
-               } else {
-                       id, err = uuid.Parse(*setId)
+               if !*noId {
+                       if *setId == "" {
+                               id, err = uuid.NewRandom()
+                       } else {
+                               id, err = uuid.Parse(*setId)
+                       }
                }
                if err != nil {
                        log.Fatal(err)
@@ -960,10 +965,12 @@ func main() {
                                log.Fatal(err)
                        }
                        enc := cmenc.Encrypted{
-                               Id:  id,
                                KEM: kems,
                                DEM: cmenc.DEM{A: chapoly.DEMAlgo},
                        }
+                       if !*noId {
+                               enc.Id = &id
+                       }
                        if *noblob {
                                var buf bytes.Buffer
                                if _, err = chapoly.Seal(&buf, os.Stdin, cek, *parallel); err != nil {
index 560da9ced5511577ef49cc92f479d2ce217ebe76666969661c8aedb9ffe8be28..fd17432212024dfee30fd8af44c341477d6fcc16e08c4a7cb5052f1088bb2cc4 100755 (executable)
@@ -11,7 +11,7 @@ test_expect_success "1: pub generation" "cmkeytool \
     -algo sntrup761-x25519 -ku kem -sub N=1 5>enc.1.pub 9>enc.1.prv"
 
 test_expect_success "encrypting" "
-    cmenctool -to enc.0.pub -to enc.1.pub <enc.data >enc.enc"
+    cmenctool enc.0.pub enc.1.pub <enc.data >enc.enc"
 
 pubId=$(kekspp -v -p /data/id <enc.0.pub)
 ln -s enc.0.prv $pubId
similarity index 94%
rename from go/cm/cmd/cmenctool/missing-from.t
rename to go/cm/cmd/cmenctool/no-from.t
index d0fa9c77c87284a1ee0877537573495e4b70b7b3dcf49eeadaebfb270360c1e9..2d3f4fa2fd8ac2f6f80127de03eeb54a1598f48b88debcc1fb79cb270d3e57e2 100755 (executable)
@@ -18,7 +18,7 @@ ln -fs ../r.prv prvs/$(kekspp -v -p /data/id <r.pub)
 ln -fs ../s.pub pubs/$senderId
 ln -fs ../r.pub pubs/$(kekspp -v -p /data/id <r.pub)
 test_expect_success "$algo: encrypting" "cmenctool \
-    -from s.pub -prvs prvs -no-from -to r.pub <enc.data >enc.enc"
+    -from s.pub -prvs prvs -no-from r.pub <enc.data >enc.enc"
 test_expect_success "$algo: decrypting fails" "
     ! cmenctool -d -prvs prvs -pubs pubs <enc.enc >enc.data.got"
 test_expect_success "$algo: decrypting" "
diff --git a/go/cm/cmd/cmenctool/no-id.t b/go/cm/cmd/cmenctool/no-id.t
new file mode 100755 (executable)
index 0000000..14aedcb
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+test_description="Check workability with missing \"id\""
+. $SHARNESS_TEST_SRCDIR/sharness.sh
+
+dd if=/dev/urandom of=enc.data bs=300K count=1 2>/dev/null
+
+for algo in sntrup761-x25519 mceliece6960119-x25519 ; do
+
+test_expect_success "$algo: pub generation" "cmkeytool \
+    -algo $algo -ku kem -sub N=0 5>enc.pub 9>enc.prv"
+test_expect_success "$algo: encrypting" "cmenctool -no-id enc.pub <enc.data >enc.enc"
+pubId=$(kekspp -v -p /data/id <enc.pub)
+ln -s enc.prv $pubId
+test_expect_success "$algo: decrypting" "
+    cmenctool -d -prvs . <enc.enc >enc.data.got"
+test_expect_success "$algo: comparing" "test_cmp enc.data enc.data.got"
+
+done
+
+test_done
similarity index 80%
rename from go/cm/cmd/cmenctool/missing-to.t
rename to go/cm/cmd/cmenctool/no-to.t
index 38a27f1815020ddc8c3c6f570b65603ad5888cfbc4673f94afe2bb560266f38b..cc33dc74a2eae084ef628dcd247e39a551b112ff4afe6fdf2c1cb8f682c8c699 100755 (executable)
@@ -8,8 +8,8 @@ dd if=/dev/urandom of=enc.data bs=300K count=1 2>/dev/null
 for algo in sntrup761-x25519 mceliece6960119-x25519 ; do
 
 test_expect_success "$algo: pub generation" "cmkeytool \
-    -algo sntrup761-x25519 -ku kem -sub N=0 5>enc.pub 9>enc.prv"
-test_expect_success "$algo: encrypting" "cmenctool -no-to -to enc.pub <enc.data >enc.enc"
+    -algo $algo -ku kem -sub N=0 5>enc.pub 9>enc.prv"
+test_expect_success "$algo: encrypting" "cmenctool -no-to enc.pub <enc.data >enc.enc"
 pubId=$(kekspp -v -p /data/id <enc.pub)
 ln -s enc.prv $pubId
 test_expect_success "$algo: decrypting fails" "
index f7c852b7e9ce13d708ef644c8f57326b434b86b76fabdf68b1d09f9e3c8e0fad..39ab49bac116dc627c80a9ea181822fdaa277fb69215fab48bacb9d9bfd7edfa 100755 (executable)
@@ -14,7 +14,7 @@ test_expect_success "$algo: key generation" "
 test_expect_success "$algo: key encrypting" "
     cmenctool -p -embed $balloonparams <enc.prv >enc.prv.enc"
 test_expect_success "$algo: data encrypting" "
-    cmenctool -to enc.pub <enc.data >enc.enc"
+    cmenctool enc.pub <enc.data >enc.enc"
 ln -s enc.prv.enc $(kekspp -v -p /data/id <enc.pub)
 test_expect_success "$algo: decrypting" "
     cmenctool -d -prvs . <enc.enc >enc.data.got"
index ab24e3bb19a37726fea4e6735b2a994f75c5f009608a6832df9ec61c3a5e9589..df47c34a3cacd846a9747a4064572edf572f2fe6e9161baa7e08634236514bd0 100755 (executable)
@@ -18,7 +18,7 @@ test_expect_success "$algo: pub generation" "
 id1=$(kekspp -v -p /data/id <enc.$algo.pub)
 
 test_expect_success "encrypting" "
-    cmenctool -to enc.$algo0.pub -to enc.$algo1.pub <enc.data >enc.enc"
+    cmenctool enc.$algo0.pub enc.$algo1.pub <enc.data >enc.enc"
 
 ln -s enc.$algo0.prv $id0
 ln -s enc.$algo1.prv $id1
@@ -27,8 +27,8 @@ test_expect_success "any: decrypting" "
 test_expect_success "comparing" "test_cmp enc.data enc.data.got"
 
 rm $id1
-test_expect_success "$algo0: decrypting" "
-    cmenctool -d -prvs . <enc.enc >enc.data.got"
+CM_PRVS=$(realpath .) test_expect_success "$algo0: decrypting" "
+    cmenctool -d <enc.enc >enc.data.got"
 test_expect_success "$algo0: comparing" "test_cmp enc.data enc.data.got"
 
 rm $id0
@@ -40,7 +40,7 @@ test_expect_success "$algo1: comparing" "test_cmp enc.data enc.data.got"
 
 export CM_PASSPHRASE=$(dd if=/dev/urandom bs=32 count=1 2>/dev/null | xxd -p)
 test_expect_success "encrypting also with passphrase" "
-    cmenctool $balloonparams -p -to enc.$algo0.pub -to enc.$algo1.pub <enc.data >enc.enc"
+    cmenctool $balloonparams -p enc.$algo0.pub enc.$algo1.pub <enc.data >enc.enc"
 test_expect_success "any: decrypting" "
     cmenctool -d -prvs . <enc.enc >enc.data.got"
 test_expect_success "comparing" "test_cmp enc.data enc.data.got"
index c9079b3e43482016308deca9a2368ab969bed4fb703a1740e05ba334a0a06826..694f882664a9117fcc721ee1b0f4909314b3b0235ec85a41c881c8c0f7fa8f62 100755 (executable)
@@ -17,13 +17,14 @@ ln -fs ../r.prv prvs/$(kekspp -v -p /data/id <r.pub)
 ln -fs ../s.pub pubs/$(kekspp -v -p /data/id <s.pub)
 ln -fs ../r.pub pubs/$(kekspp -v -p /data/id <r.pub)
 test_expect_success "$algo: encrypting" "cmenctool \
-    -from s.pub -prvs prvs -to r.pub <enc.data >enc.enc"
+    -from s.pub -prvs prvs r.pub <enc.data >enc.enc"
+export CM_PRVS=$(realpath prvs) CM_PUBS=$(realpath pubs)
 test_expect_success "$algo: decrypting" "
-    cmenctool -d -prvs prvs -pubs pubs <enc.enc >enc.data.got"
+    cmenctool -d <enc.enc >enc.data.got"
 test_expect_success "$algo: comparing" "test_cmp enc.data enc.data.got"
 ln -fs ../r.pub pubs/$(kekspp -v -p /data/id <s.pub)
 test_expect_success "$algo: bad auth" "
-    ! cmenctool -d -prvs prvs -pubs pubs <enc.enc >enc.data.got"
+    ! cmenctool -d <enc.enc >enc.data.got"
 
 done
 
index c2d43d9a3bfa30b0b49f476118c063c2152b4542df72cc06cfe9f2e75f568b50..69ee6628dc822a93494c63d626167c98b6b70def67f08174bff0719bd1a4045a 100644 (file)
@@ -24,17 +24,22 @@ import (
 func usage() {
        fmt.Fprintf(os.Stderr, `Usage:
   Encrypt to recipient(s):
-    cmenctool [-no-to] -to PUB0 [-to PUB1 ...] <DATA >DATA.encrypted
+    cmenctool [-no-to] [-no-from] [-from PUB] [-prvs dir] \
+        PUB0 [PUB1 ...] <DATA >DATA.encrypted
   Encrypt on passphrase:
     cmenctool -p [-balloon-s X] [-balloon-t X] [-balloon-p X] <DATA >DATA.encrypted
   Decrypt by providing possible KEMs and/or passphrase(s):
-    cmenctool -d [-p] -prvs prvs/dir <DATA.encrypted >DATA
-  With sender authentication:
-    cmenctool [...] -from PUB -prvs prvs/dir [...]
-    cmenctool [...] -d -pubs pubs/dir [...]
+    cmenctool -d [-p] [-prvs dir] [-pubs dir] <DATA.encrypted >DATA
+
+PUB is a path to public key files.
+-prvs/-pubs directory must contain key files with uppercase hexadecimal
+corresponding public key's id used as the filename. They can be set by
+CM_PRVS and CM_PUBS environment variables.
+
+If -from is specified, then sender authentication will be enabled and
+-prvs must be set to find corresponding private key. -pubs must be set
+on recipient side in that case.
 
-{prvs,pubs}/dir must contain key files with uppercase hexadecimal
-corresponding public key's id used as the filename.
 `)
        flag.PrintDefaults()
 }
index 78045023205ba48c28457e71e5d81f2f56bb3f85f0aa681b5a2cf3e75b4989b9..503ca34ec71f6b47e3030240f5e219c52c4762ee55ce1605311a9c28f459d193 100755 (executable)
@@ -3,55 +3,50 @@
 test_description="Check certification"
 . $SHARNESS_TEST_SRCDIR/sharness.sh
 
+mkdir prvs pubs
+
 echo "gost3410-512C gost3410-256A
 ed25519-blake2b ed25519-blake2b
 slh-dsa-shake-256s slh-dsa-shake-256s" | while read caAlgo eeAlgo ; do
 
+unset CM_PRVS CM_PUBS
 sub="-sub CN=CA -sub C=RU"
 test_expect_success "$caAlgo: CA load generation" "cmkeytool \
     -algo $caAlgo -ku sig $sub \
     5>ca.$caAlgo.pub 9>ca.$caAlgo.prv"
-test_expect_success "$caAlgo: CA generation" "cmkeytool \
-    4<ca.$caAlgo.pub \
-    8<ca.$caAlgo.prv \
-    <ca.$caAlgo.pub \
-    5>ca.$caAlgo.pub.certified"
+ln -fs ../ca.$caAlgo.prv prvs/$(kekspp -v -p /pub-id <ca.$caAlgo.prv)
+export CM_PRVS=$(realpath prvs)
+test_expect_success "$caAlgo: CA generation" "cmkeytool -c \
+    ca.$caAlgo.pub ca.$caAlgo.pub >ca.$caAlgo.pub.certified"
 mv ca.$caAlgo.pub.certified ca.$caAlgo.pub
-test_expect_success "$caAlgo: CA regeneration" "cmkeytool \
-    4<ca.$caAlgo.pub \
-    8<ca.$caAlgo.prv \
-    <ca.$caAlgo.pub \
-    5>ca.$caAlgo.pub.certified"
+test_expect_success "$caAlgo: CA regeneration" "cmkeytool -c \
+    ca.$caAlgo.pub ca.$caAlgo.pub >ca.$caAlgo.pub.certified"
 mv ca.$caAlgo.pub.certified ca.$caAlgo.pub
-test_expect_success "$caAlgo: CA self-signature" "
-    cmkeytool -verify 4<ca.$caAlgo.pub <ca.$caAlgo.pub"
+test_expect_success "$caAlgo: CA self-signature" "cmkeytool -v ca.$caAlgo.pub"
 
 sub="-sub CN=SubCA -sub C=RU"
 test_expect_success "$eeAlgo: SubCA load generation" "cmkeytool \
     -algo $eeAlgo -ku sig $sub \
     5>subca.$eeAlgo.pub 9>subca.$eeAlgo.prv"
-test_expect_success "$eeAlgo: SubCA generation" "cmkeytool \
-    4<ca.$caAlgo.pub \
-    8<ca.$caAlgo.prv \
-    <subca.$eeAlgo.pub \
-    5>subca.$eeAlgo.pub.certified"
+test_expect_success "$eeAlgo: SubCA generation" "cmkeytool -c \
+    subca.$eeAlgo.pub ca.$caAlgo.pub >subca.$eeAlgo.pub.certified"
 mv subca.$eeAlgo.pub.certified subca.$eeAlgo.pub
-test_expect_success "$eeAlgo: SubCA signature" "
-    cmkeytool -verify 4<ca.$caAlgo.pub <subca.$eeAlgo.pub"
+test_expect_success "$eeAlgo: SubCA signature fails" "
+    ! cmkeytool -v subca.$eeAlgo.pub"
+ln -fs ../ca.$caAlgo.pub pubs/$(kekspp -v -p /data/id <ca.$caAlgo.pub)
+export CM_PUBS=$(realpath pubs)
+test_expect_success "$eeAlgo: SubCA signature" "cmkeytool -v subca.$eeAlgo.pub"
 
 sub="-sub CN=EE -sub C=RU"
+ln -fs ../subca.$eeAlgo.prv prvs/$(kekspp -v -p /data/id <subca.$eeAlgo.pub)
 test_expect_success "$eeAlgo: EE load generation" "cmkeytool \
     -algo $eeAlgo $sub \
     5>ee.$eeAlgo.pub 9>ee.$eeAlgo.prv"
-test_expect_success "$eeAlgo: EE generation" "cmkeytool \
-    4<subca.$eeAlgo.pub \
-    8<subca.$eeAlgo.prv \
-    <ee.$eeAlgo.pub \
-    5>ee.$eeAlgo.pub.certified"
+test_expect_success "$eeAlgo: EE generation" "cmkeytool -c \
+    ee.$eeAlgo.pub subca.$eeAlgo.pub >ee.$eeAlgo.pub.certified"
 mv ee.$eeAlgo.pub.certified ee.$eeAlgo.pub
-test_expect_success "$eeAlgo: EE chain" "
-    cat ca.$caAlgo.pub subca.$eeAlgo.pub |
-    cmkeytool -verify 4<&0 <ee.$eeAlgo.pub"
+ln -fs ../subca.$eeAlgo.pub pubs/$(kekspp -v -p /data/id <subca.$eeAlgo.pub)
+test_expect_success "$eeAlgo: EE chain" "cmkeytool -v ee.$eeAlgo.pub"
 
 done
 
index 879a55ddae92de06e45457543b818199694b679d00fc120fa20de64a4d79be22..a1a746c941a5a28b0cfdb5002fcfacc2a4e96d4ce87da00bc3b5f57310559b3c 100755 (executable)
@@ -3,8 +3,7 @@
 test_description="Check KEM certificates generation"
 . $SHARNESS_TEST_SRCDIR/sharness.sh
 
-echo "mceliece6960119-x25519
-sntrup761-x25519" | while read algo ; do
+for algo in mceliece6960119-x25519 sntrup761-x25519 ; do
 
 test_expect_success "$algo: generation" "
     cmkeytool -algo $algo -ku kem -sub CN=DH 5>kem.$algo.pub 9>kem.$algo.prv"
index 8481ce96cd6e7a1c977f753294d48dc1ea97aa62f778f61ce369a7c5bd990f5a..f21f9fdd06fe5673800034133ee868eaffe24cc58ede5a0862ef7cb07cd8e174 100644 (file)
@@ -17,6 +17,7 @@ package main
 
 import (
        "bytes"
+       "encoding/hex"
        "errors"
        "flag"
        "fmt"
@@ -24,6 +25,7 @@ import (
        "io"
        "log"
        "os"
+       "path"
        "sort"
        "strings"
        "time"
@@ -40,21 +42,10 @@ import (
 )
 
 const (
-       FdPubR = 4
        FdPubW = 5
-       FdPrvR = 8
        FdPrvW = 9
 )
 
-func mustReadAll(r io.ReadCloser) []byte {
-       data, err := io.ReadAll(r)
-       if err != nil {
-               log.Fatal(err)
-       }
-       r.Close()
-       return data
-}
-
 func main() {
        flag.Usage = usage
        ku := make(map[string]*struct{})
@@ -85,9 +76,13 @@ func main() {
                "Lifetime of the certification, days")
        algo := flag.String("algo", ed25519blake2b.Ed25519BLAKE2b,
                "Public key algorithm")
-       verify := flag.Bool("verify", false, "Verify provided -pub with -ca-pub")
+       certify := flag.Bool("c", false, "Certify public key")
+       verify := flag.Bool("v", false, "Verify provided -pub with -ca-pub")
+       prvsDir := flag.String("prvs", os.Getenv("CM_PRVS"),
+               "Path to directory with private keys")
+       pubsDirPth := flag.String("pubs", os.Getenv("CM_PUBS"),
+               "Path to directory with public keys")
        doList := flag.Bool("list", false, "List available algorithms")
-
        flag.Parse()
        log.SetFlags(log.Lshortfile)
 
@@ -107,58 +102,77 @@ func main() {
                return
        }
 
-       fdPubR := os.NewFile(FdPubR, "pub-in")
        fdPubW := os.NewFile(FdPubW, "pub-out")
-       fdPrvR := os.NewFile(FdPrvR, "prv-in")
        fdPrvW := os.NewFile(FdPrvW, "prv-out")
 
-       var doCertify bool
-       if len(sub) == 0 && !*verify {
-               doCertify = true
-       }
-
+       pubsDir := sign.PubStorageDir{Dir: *pubsDirPth}
+       var caPrv sign.Iface
+       var caPub *sign.Signed
        var err error
-       var since time.Time
-       if *sinceRaw == "" {
-               since = time.Now().UTC().Truncate(time.Second)
-       } else {
-               since, err = time.Parse(time.DateTime, *sinceRaw)
+       if *certify {
+               if (len(sub) > 0) || *verify {
+                       log.Fatal("-c can not be used with -sub or -v")
+               }
+               if flag.NArg() != 2 {
+                       log.Fatal("must specify PUB and CA.PUB")
+               }
+               var data []byte
+               data, err = os.ReadFile(flag.Arg(1))
                if err != nil {
-                       log.Fatalln("while parsing -since:", err)
+                       log.Fatal(err)
+               }
+               caPub, _, err = sign.PubParse(data)
+               if err != nil {
+                       log.Fatal(err)
+               }
+               data, err = os.ReadFile(path.Join(
+                       *prvsDir, strings.ToUpper(hex.EncodeToString(caPub.PubData().Id)),
+               ))
+               if err != nil {
+                       log.Fatal(err)
+               }
+               caPrv, _, err = sign.PrvParse(data)
+               if err != nil {
+                       log.Fatal(err)
                }
        }
-       till := since.Add(time.Duration(*lifetime) * 24 * time.Hour)
-
-       var caPrv sign.Iface
-       var caPubs []*sign.Signed
-       if doCertify || *verify {
-               data := mustReadAll(fdPubR)
-               for len(data) > 0 {
-                       var signed *sign.Signed
-                       signed, data, err = sign.PubParse(data)
-                       if err != nil {
-                               log.Fatal(err)
-                       }
-                       caPubs = append(caPubs, signed)
+       if *verify {
+               if (len(sub) > 0) || *certify {
+                       log.Fatal("-v can not be used with -sub or -c")
+               }
+               if flag.NArg() != 1 {
+                       log.Fatal("must specify PUB")
                }
        }
-       if doCertify {
-               caPrv, _, err = sign.PrvParse(mustReadAll(fdPrvR))
+       var signed *sign.Signed
+       if *certify || *verify {
+               var data []byte
+               data, err = os.ReadFile(flag.Arg(0))
+               if err != nil {
+                       log.Fatal(err)
+               }
+               signed, _, err = sign.PubParse(data)
                if err != nil {
                        log.Fatal(err)
                }
        }
+       if !*certify && !*verify && len(sub) == 0 {
+               log.Fatal("must specify one of -v, -c, -sub")
+       }
 
-       var signed *sign.Signed
-       if doCertify || *verify {
-               signed, _, err = sign.PubParse(mustReadAll(os.Stdin))
+       var since time.Time
+       if *sinceRaw == "" {
+               since = time.Now().UTC().Truncate(time.Second)
+       } else {
+               since, err = time.Parse(time.DateTime, *sinceRaw)
                if err != nil {
-                       log.Fatal(err)
+                       log.Fatalln("while parsing -since:", err)
                }
        }
+       till := since.Add(time.Duration(*lifetime) * 24 * time.Hour)
 
        if *verify {
-               err = signed.CertificationVerify(caPubs, time.Now().UTC())
+               err = signed.CertificationVerify(&pubsDir, time.Now().UTC())
                if err != nil {
                        log.Fatal(err)
                }
@@ -167,7 +181,7 @@ func main() {
 
        var prvRaw []byte
        var pubData map[string]any
-       if doCertify {
+       if *certify {
                pubData = (*signed.Data).(map[string]any)
        } else {
                var pub []byte
@@ -188,18 +202,7 @@ func main() {
                if err != nil {
                        log.Fatal(err)
                }
-               {
-                       var buf bytes.Buffer
-                       if _, err = keks.Encode(&buf, sign.PrvMagic, nil); err != nil {
-                               log.Fatal(err)
-                       }
-                       if _, err = keks.Encode(&buf, cm.AV{A: *algo, V: prvRaw}, nil); err != nil {
-                               log.Fatal(err)
-                       }
-                       if _, err = io.Copy(fdPrvW, &buf); err != nil {
-                               log.Fatal(err)
-                       }
-               }
+               var pubId []byte
                {
                        pubData = map[string]any{
                                "sub": sub,
@@ -220,7 +223,8 @@ func main() {
                        if err != nil {
                                log.Fatal(err)
                        }
-                       pubData["id"] = hasher.Sum(nil)
+                       pubId = hasher.Sum(nil)
+                       pubData["id"] = pubId
                        if err != nil {
                                log.Fatal(err)
                        }
@@ -228,6 +232,22 @@ func main() {
                if len(ku) > 0 {
                        pubData["ku"] = ku
                }
+               {
+                       var buf bytes.Buffer
+                       if _, err = keks.Encode(&buf, sign.PrvMagic, nil); err != nil {
+                               log.Fatal(err)
+                       }
+                       if _, err = keks.Encode(
+                               &buf,
+                               sign.Prv{A: *algo, V: prvRaw, PubId: pubId},
+                               nil,
+                       ); err != nil {
+                               log.Fatal(err)
+                       }
+                       if _, err = io.Copy(fdPrvW, &buf); err != nil {
+                               log.Fatal(err)
+                       }
+               }
        }
 
        {
@@ -235,10 +255,8 @@ func main() {
                signed = &sign.Signed{TBS: sign.SDTBS{T: "pub"}, Data: &pubLoadAny}
        }
 
-       if doCertify {
-               if err = signed.CertifyWith(
-                       caPubs[0].PubData(), caPrv, since, till,
-               ); err != nil {
+       if *certify {
+               if err = signed.CertifyWith(caPub.PubData(), caPrv, since, till); err != nil {
                        log.Fatal(err)
                }
        }
@@ -251,7 +269,13 @@ func main() {
                if _, err = keks.Encode(&buf, signed, nil); err != nil {
                        log.Fatal(err)
                }
-               if _, err = io.Copy(fdPubW, &buf); err != nil {
+               var dst io.Writer
+               if *certify {
+                       dst = os.Stdout
+               } else {
+                       dst = fdPubW
+               }
+               if _, err = io.Copy(dst, &buf); err != nil {
                        log.Fatal(err)
                }
        }
index 22a7bde9c3c2c494f0015153844fc2184a3da3ce7900266a89b3a289dc691e89..1237d3bf4b719d7eb0420b6ea5f36e62ceb942f569b422061e245e31088be502 100644 (file)
@@ -26,9 +26,14 @@ func usage() {
   Generate public key load:
     cmkeytool -sub K=V [-sub K=V ...] [-algo ALGO] [-ku KU ...] 5>PUB 9>PRV
   Certify public key:
-    cmkeytool [-lifetime DAYS] [-since DATE] 4<CA-PUB 8<CA-PRV <PUB 5>PUB.certified
+    cmkeytool -c [-lifetime DAYS] [-since DATE] [-prvs dir] PUB CA.PUB >PUB.cer
   Verify certification:
-    cmkeytool -verify 4<CA-PUB0[,CA-PUB1,...] <PUB
+    cmkeytool -v [-pubs dir] PUB
+
+PUB is a path to public key files.
+-prvs/-pubs directory must contain key files with uppercase hexadecimal
+corresponding public key's id used as the filename. They can be set by
+CM_PRVS and CM_PUBS environment variables.
 
 `)
        flag.PrintDefaults()
index 440753a5beeed2c050a355399b9ab19b8947cb01c961a6a556845ef1d4a459a2..39628066f597f61ac57f2790340fbae6177930087266a5e958bacb922f0dac05 100755 (executable)
@@ -3,16 +3,20 @@
 test_description="Check signing"
 . $SHARNESS_TEST_SRCDIR/sharness.sh
 
+mkdir prvs pubs
 echo "gost3410-512C
 gost3410-256A
 ed25519-blake2b
 slh-dsa-shake-256s" | while read keyalgo ; do
 
+unset CM_PRVS CM_PUBS
 sub="-sub what=ever"
 typ="some-different-type"
 test_expect_success "$keyalgo: pub generation" "cmkeytool \
     -algo $keyalgo -ku sig $sub \
     5>sign.$keyalgo.pub 9>sign.$keyalgo.prv"
+ln -s ../sign.$keyalgo.prv prvs/$(kekspp -v -p /pub-id <sign.$keyalgo.prv)
+export CM_PRVS=$(realpath prvs)
 dd if=/dev/urandom of=sign.$keyalgo.data bs=300K count=1 2>/dev/null
 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)
@@ -21,36 +25,37 @@ badEncTo="-encrypted-to $badEncTo"
 
 for merkle in "" "-merkle" ; do
 
+unset CM_PUBS
 algo=${keyalgo}${merkle}
 test_expect_success "$algo: signing" "
-    cmsigtool $merkle -t $typ $encTo \
-    4<sign.$keyalgo.pub 8<sign.$keyalgo.prv \
+    cmsigtool $merkle -t $typ $encTo sign.$keyalgo.pub \
     <sign.$keyalgo.data >sign.$algo.sig"
+test_expect_success "$algo: verifying, no key" "
+    ! cmsigtool -v -t $typ <sign.$algo.sig >sign.data.got"
+ln -fs ../sign.$keyalgo.pub pubs/$(kekspp -v -p /pub-id <sign.$keyalgo.prv)
+export CM_PUBS=$(realpath pubs)
 test_expect_success "$algo: verifying" "
-    cmsigtool -v -t $typ 4<sign.$keyalgo.pub \
-    <sign.$algo.sig >sign.data.got"
+    cmsigtool -v -t $typ <sign.$algo.sig >sign.data.got"
 test_expect_success "$algo: comparing" \
     "test_cmp sign.$keyalgo.data sign.data.got"
 test_expect_success "$algo: differing type" "
-    ! cmsigtool -v 4<sign.$keyalgo.pub <sign.$algo.sig >/dev/null"
+    ! cmsigtool -v <sign.$algo.sig >/dev/null"
 test_expect_success "$algo: good encTo" "
-    ! cmsigtool -v $encTo 4<sign.$keyalgo.pub <sign.$algo.sig >/dev/null"
+    ! cmsigtool -v $encTo <sign.$algo.sig >/dev/null"
 test_expect_success "$algo: bad encTo" "
-    ! cmsigtool -v $badEncTo 4<sign.$keyalgo.pub <sign.$algo.sig >/dev/null"
+    ! cmsigtool -v $badEncTo <sign.$algo.sig >/dev/null"
 
 test_expect_success "$algo: detached signing" "
     cmsigtool -d $merkle -t $typ \
-    4<sign.$keyalgo.pub 8<sign.$keyalgo.prv \
-    <sign.$keyalgo.data >sign.$algo.detached.sig"
+    sign.$keyalgo.pub <sign.$keyalgo.data >sign.$algo.detached.sig"
 test_expect_success "$algo: detached verifying" "
-    cat sign.$algo.detached.sig sign.$keyalgo.data |
-    cmsigtool -d -v -t $typ 4<sign.$keyalgo.pub"
+    cat sign.$algo.detached.sig sign.$keyalgo.data | cmsigtool -d -v -t $typ"
 test_expect_success "$algo: differing type" "
-    ! cmsigtool -d -v 4<sign.$keyalgo.pub <sign.$algo.detached.sig >/dev/null"
+    ! cmsigtool -d -v <sign.$algo.detached.sig >/dev/null"
 test_expect_success "$algo: good encTo" "! cmsigtool -d \
-    -v $encTo 4<sign.$keyalgo.pub <sign.$algo.detached.sig >/dev/null"
+    -v $encTo <sign.$algo.detached.sig >/dev/null"
 test_expect_success "$algo: bad encTo" "! cmsigtool -d \
-    -v $badEncTo 4<sign.$keyalgo.pub <sign.$algo.detached.sig >/dev/null"
+    -v $badEncTo <sign.$algo.detached.sig >/dev/null"
 
 done
 
index c0ef13dc012e4325c7af8646695df9a205204ad6a462e5b8485c5e5980288a75..23f75797edfba15e1b82d37dd2a70e4f314b6fcae156c8a27845344876d9731d 100644 (file)
@@ -24,6 +24,8 @@ import (
        "io"
        "log"
        "os"
+       "path"
+       "strings"
        "time"
 
        "go.cypherpunks.su/keks"
@@ -35,26 +37,17 @@ import (
        "go.cypherpunks.su/keks/types"
 )
 
-const (
-       FdPubR = 4
-       FdPrvR = 8
-)
-
 const BlobChunkLen = 128 * 1024
 
-func mustReadAll(r io.Reader) []byte {
-       data, err := io.ReadAll(r)
-       if err != nil {
-               log.Fatal(err)
-       }
-       return data
-}
-
 func main() {
        log.SetFlags(log.Lshortfile)
        flag.Usage = usage
        typ := flag.String("t", "data", "Set/check the load type")
        verify := flag.Bool("v", false, "Do verification")
+       prvDir := flag.String("prvs", os.Getenv("CM_PRVS"),
+               "Path to directory with private keys")
+       pubDirPth := flag.String("pubs", os.Getenv("CM_PUBS"),
+               "Path to directory with public keys")
        var encryptedTo [][]byte
        flag.Func("encrypted-to", "Set/check encrypted-to, hex", func(v string) error {
                to, err := hex.DecodeString(v)
@@ -69,22 +62,39 @@ func main() {
        doMerkle := flag.Bool("merkle", false, "Use Merkle-tree based hasher")
        flag.Parse()
 
-       fdPubR := os.NewFile(FdPubR, "pub-in")
-       fdPrvR := os.NewFile(FdPrvR, "prv-in")
-
-       var pubs []*sign.PubData
+       pubsDir := sign.PubStorageDir{Dir: *pubDirPth}
+       var pub *sign.PubData
+       var signer sign.Iface
        var err error
-       {
-               data := mustReadAll(fdPubR)
-               for len(data) > 0 {
-                       var signed *sign.Signed
-                       signed, data, err = sign.PubParse(data)
-                       if err != nil {
-                               log.Fatal(err)
-                       }
-                       pubs = append(pubs, signed.PubData())
+       if !*verify {
+               if flag.NArg() != 1 {
+                       log.Fatal("no PUB specified")
+               }
+               var data []byte
+               data, err = os.ReadFile(flag.Arg(0))
+               if err != nil {
+                       log.Fatal(err)
+               }
+               var signed *sign.Signed
+               signed, _, err = sign.PubParse(data)
+               if err != nil {
+                       log.Fatal(err)
+               }
+               pub = signed.PubData()
+               data, err = os.ReadFile(path.Join(
+                       *prvDir, strings.ToUpper(hex.EncodeToString(pub.Id)),
+               ))
+               if err != nil {
+                       log.Fatal(err)
+               }
+               data, err = cmballoon.PossibleInteractiveDecrypt(data)
+               if err != nil {
+                       log.Fatal(err)
+               }
+               signer, _, err = sign.PrvParse(data)
+               if err != nil {
+                       log.Fatal(err)
                }
-               fdPubR.Close()
        }
 
        stdin := bufio.NewReaderSize(os.Stdin, BlobChunkLen)
@@ -160,74 +170,59 @@ func main() {
                }
                for _, sig := range signed.Sigs {
                        sid := sig.TBS["sid"].([]byte)
-                       var signerFound bool
-                       for _, pub := range pubs {
-                               if !bytes.Equal(sid, pub.Id) {
-                                       continue
-                               }
-                               signerFound = true
-                               var sigTBS *sign.SigTBS
-                               sigTBS, err = sig.TBSGet()
+                       {
+                               var p *sign.Signed
+                               p, err = pubsDir.Get(sid)
                                if err != nil {
                                        log.Fatal(err)
                                }
-                               if len(encryptedTo) > 0 {
-                                       if len(sigTBS.EncryptedTo) == 0 {
-                                               log.Fatalln(hex.EncodeToString(sid), "missing encrypted-to")
-                                       }
-                                       found := false
-                                       for _, their := range sigTBS.EncryptedTo {
-                                               for _, our := range encryptedTo {
-                                                       if bytes.Equal(our, their) {
-                                                               found = true
-                                                               break
-                                                       }
+                               if p == nil {
+                                       log.Fatalln(hex.EncodeToString(sid), "can not find signer")
+                               }
+                               pub = p.PubData()
+                       }
+                       var sigTBS *sign.SigTBS
+                       sigTBS, err = sig.TBSGet()
+                       if err != nil {
+                               log.Fatal(err)
+                       }
+                       if len(encryptedTo) > 0 {
+                               if len(sigTBS.EncryptedTo) == 0 {
+                                       log.Fatalln(hex.EncodeToString(sid), "missing encrypted-to")
+                               }
+                               found := false
+                               for _, their := range sigTBS.EncryptedTo {
+                                       for _, our := range encryptedTo {
+                                               if bytes.Equal(our, their) {
+                                                       found = true
+                                                       break
                                                }
                                        }
-                                       if !found {
-                                               log.Fatalln(
-                                                       hex.EncodeToString(sid),
-                                                       "corresponding encrypted-to not found")
-                                       }
                                }
-                               var hasher hash.Hash
-                               if prehash.T == "" {
-                                       hasher = cmhash.ByName(sig.Sign.A)
-                                       if _, err = io.Copy(hasher, stdin); err != nil {
-                                               log.Fatal(hex.EncodeToString(sid), err)
-                                       }
-                               } else {
-                                       var found bool
-                                       hasher, found = hashers[sig.Sign.A]
-                                       if !found {
-                                               log.Fatalln(hex.EncodeToString(sid), "no hasher in prehash")
-                                       }
+                               if !found {
+                                       log.Fatalln(
+                                               hex.EncodeToString(sid),
+                                               "corresponding encrypted-to not found")
+                               }
+                       }
+                       var hasher hash.Hash
+                       if prehash.T == "" {
+                               hasher = cmhash.ByName(sig.Sign.A)
+                               if _, err = io.Copy(hasher, stdin); err != nil {
+                                       log.Fatal(hex.EncodeToString(sid), err)
                                }
-                               if err = signed.CertificationCheckSignatureFrom(
-                                       pub, &hasher,
-                               ); err != nil {
-                                       log.Fatalln(hex.EncodeToString(sid), err)
+                       } else {
+                               var found bool
+                               hasher, found = hashers[sig.Sign.A]
+                               if !found {
+                                       log.Fatalln(hex.EncodeToString(sid), "no hasher in prehash")
                                }
-                               break
                        }
-                       if !signerFound {
-                               log.Fatalln(hex.EncodeToString(sid), "can not find signer")
+                       if err = signed.CertificationCheckSignatureFrom(pub, &hasher); err != nil {
+                               log.Fatalln(hex.EncodeToString(sid), err)
                        }
                }
        } else {
-               var signer sign.Iface
-               {
-                       prvRaw := mustReadAll(fdPrvR)
-                       fdPrvR.Close()
-                       prvRaw, err = cmballoon.PossibleInteractiveDecrypt(prvRaw)
-                       if err != nil {
-                               log.Fatal(err)
-                       }
-                       signer, _, err = sign.PrvParse(prvRaw)
-               }
-               if err != nil {
-                       log.Fatal(err)
-               }
                if *doMerkle {
                        err = signer.SetMode(mode.Merkle)
                } else {
@@ -236,7 +231,6 @@ func main() {
                if err != nil {
                        log.Fatal(err)
                }
-
                if _, err = keks.Encode(os.Stdout, sign.SignedMagic, nil); err != nil {
                        log.Fatal(err)
                }
@@ -276,7 +270,7 @@ func main() {
                if len(encryptedTo) > 0 {
                        tbs["encrypted-to"] = encryptedTo
                }
-               if err = signed.SignWith(pubs[0], signer, tbs); err != nil {
+               if err = signed.SignWith(pub, signer, tbs); err != nil {
                        log.Fatal(err)
                }
                if _, err = keks.Encode(os.Stdout, signed, nil); err != nil {
index 190322618d99a0cb99f063a7adae439ac957e41810379c796fce7e72416d6fb9..a0c89d7baf636b64f5a6a8e1fc15c1248b2a9101fb51c364db3be9f7b36798ff 100644 (file)
@@ -23,14 +23,19 @@ import (
 
 func usage() {
        fmt.Fprintf(os.Stderr, `Usage:
-  cmsigtool [-t TYPE] 4<PUB 8<PRV <DATA >DATA.signed
-  cmsigtool -v [-t TYPE] 4<PUB0[,PUB1...] <DATA.signed >DATA
-  cmsigtool -d [-t TYPE] 4<PUB 8<PRV <DATA >DATA.signature
-  cmsigtool -d -v [-t TYPE] 4<PUB0[,PUB1...] <<(cat DATA.signature DATA)
+  cmsigtool [-t TYPE] [-prvs dir] PUB <DATA >DATA.signed
+  cmsigtool -d [-t TYPE] [-prvs dir] PUB <DATA >DATA.signature
+  cmsigtool -v [-t TYPE] [-pubs dir] <DATA.signed >DATA
+  cmsigtool -d -v [-t TYPE] [-pubs dir] <<(cat DATA.signature DATA)
 
 DATA.signed holds completely encapsulated DATA.
 -d(etached) mode keeps DATA completely separate.
 
+PUB is a path to public key files.
+-prvs/-pubs directory must contain key files with uppercase hexadecimal
+corresponding public key's id used as the filename. They can be set by
+CM_PRVS and CM_PUBS environment variables.
+
 `)
        flag.PrintDefaults()
 }
index effe61226cd356650f3477d7b37b4af7d99b07aa02bc96c361982f41627d7c8e..cdf2326d29cb97490078c09ce179b8b047aa8fd5adeef6c499f611a0416fc52f 100644 (file)
@@ -3,8 +3,8 @@ package encrypted
 import "github.com/google/uuid"
 
 type Encrypted struct {
-       DEM     DEM       `keks:"dem"`
-       KEM     []KEM     `keks:"kem"`
-       Payload []byte    `keks:"payload,omitempty"`
-       Id      uuid.UUID `keks:"id"`
+       DEM     DEM        `keks:"dem"`
+       KEM     []KEM      `keks:"kem"`
+       Payload []byte     `keks:"payload,omitempty"`
+       Id      *uuid.UUID `keks:"id,omitempty"`
 }
index a530887df4964bdcd9850995d44360fcc16c918ae05791bd73b8ceb1b926c30a..cf6a0c0c5170c292a49bd7aaa2206e3915a19e104767762236f2e39fd1fad311 100644 (file)
@@ -1,2 +1,3 @@
+/prv.schema.keks
 /pub.schema.keks
 /signed.schema.keks
index 6b7ee064570ed69cab4416c4aa13011511527861a201b8acb94fda40eb531d76..baf0f754611b3d26ec26ae5ce86ed9612ff6c2651527cef8cf13b77c7d7578af 100644 (file)
@@ -29,7 +29,13 @@ import (
 
 const PrvMagic = keks.Magic("cm/prv")
 
-// Parse private key contained in AV KEKS-encoded structure.
+type Prv struct {
+       A     string `keks:"a"`
+       V     []byte `keks:"v"`
+       PubId []byte `keks:"pub-id,omitempty"`
+}
+
+// Parse private key contained in KEKS-encoded structure.
 func PrvParse(data []byte) (prv Iface, pub []byte, err error) {
        {
                var magic keks.Magic
@@ -46,7 +52,7 @@ func PrvParse(data []byte) (prv Iface, pub []byte, err error) {
                if err != nil {
                        return
                }
-               err = schema.Check("av", PubSchemas, v)
+               err = schema.Check("prv", PrvSchemas, v)
                if err != nil {
                        return
                }
index fedc13da4b53f24d234e13fdb9d9c8e46f948e41964056464d86cf5a6f27efe2..5e8cb01dcb476e2e8f7829dc9a7f70f639f9beadffcb869efd30c45eb7a41bb1 100644 (file)
@@ -17,6 +17,7 @@ package sign
 
 import (
        "bytes"
+       "encoding/hex"
        "errors"
        "fmt"
        "hash"
@@ -270,9 +271,9 @@ func (signed *Signed) PubData() *PubData {
        return &pubData
 }
 
-// Verify signed Signed PubData certification against pubs chain of
+// Verify signed Signed PubData certification against pubs storage of
 // public keys at specified point of time t.
-func (signed *Signed) CertificationVerify(pubs []*Signed, t time.Time) (err error) {
+func (signed *Signed) CertificationVerify(pubs PubStorage, t time.Time) (err error) {
        if len(signed.Sigs) == 0 {
                return errors.New("no sigs")
        }
@@ -297,22 +298,21 @@ func (signed *Signed) CertificationVerify(pubs []*Signed, t time.Time) (err erro
        if bytes.Equal(sid, signed.PubData().Id) {
                return signed.CertificationCheckSignatureFrom(signed.PubData(), nil)
        }
-       type FPR [FPRLen]byte
-       idToPub := make(map[FPR]*Signed, len(pubs))
-       for _, cer := range pubs {
-               pubData := cer.PubData()
-               if !pubData.Can(KUSig) || len(pubData.Pub) != 1 {
-                       err = errors.New("pub can not sign")
-                       return
-               }
-               idToPub[FPR(pubData.Id)] = cer
+       var signer *Signed
+       signer, err = pubs.Get(sid)
+       if err != nil {
+               return
        }
-       signer := idToPub[FPR(sid)]
        if signer == nil {
-               err = fmt.Errorf("no pub found for sid: %v", sigTBS.SID)
+               err = fmt.Errorf("no pub found for sid: %s", hex.EncodeToString(sid))
+               return
+       }
+       pubData := signer.PubData()
+       if !pubData.Can(KUSig) || len(pubData.Pub) != 1 {
+               err = errors.New("pub can not sign")
                return
        }
-       err = signed.CertificationCheckSignatureFrom(signer.PubData(), nil)
+       err = signed.CertificationCheckSignatureFrom(pubData, nil)
        if err != nil {
                return
        }
index 776a5e7cb599b4404c03eda8bca42cdbd4eda8986a4807575453bb6efe074914..9a06418e4ce558f97369731ffff2011ab09896632d786bfa8b78b9caad0f741a 100644 (file)
@@ -25,11 +25,15 @@ import (
 //go:embed signed.schema.keks
 var SignedSchemasRaw []byte
 
+//go:embed prv.schema.keks
+var PrvSchemasRaw []byte
+
 //go:embed pub.schema.keks
 var PubSchemasRaw []byte
 
 var (
        SignedSchemas map[string][][]any
+       PrvSchemas    map[string][][]any
        PubSchemas    map[string][][]any
 )
 
@@ -45,6 +49,16 @@ func init() {
                panic(err)
        }
 
+       magic, PrvSchemasRaw = keks.StripMagic(PrvSchemasRaw)
+       if magic != schema.Magic {
+               panic("wrong magic in prv.schema.keks")
+       }
+       if err := keks.NewDecoderFromBytes(
+               PrvSchemasRaw, nil,
+       ).DecodeStruct(&PrvSchemas); err != nil {
+               panic(err)
+       }
+
        magic, PubSchemasRaw = keks.StripMagic(PubSchemasRaw)
        if magic != schema.Magic {
                panic("wrong magic in pub.schema.keks")
index 9997d843abcec97f6921355892e7fe7d64bf50a2b032d1ebf9d1d094689b69d7..e972530df521636cb8cc0480d02a03746161af02cafdded30e43379ef6e68fa6 100644 (file)
@@ -33,7 +33,8 @@ import (
 const SignedMagic = keks.Magic("cm/signed")
 
 type SDTBS struct {
-       T string `keks:"t"`
+       T  string     `keks:"t"`
+       Id *uuid.UUID `keks:"id,omitempty"`
 }
 
 type SigTBS struct {
@@ -54,7 +55,7 @@ type Sig struct {
 }
 
 type Signed struct {
-       TBS  SDTBS        `keks:"tbs"`
+       TBS  SDTBS      `keks:"tbs"`
        Data *any       `keks:"data,omitempty"`
        Pubs *[]*Signed `keks:"pubs,omitempty"`
        Sigs []*Sig     `keks:"sigs,omitempty"`
diff --git a/go/cm/sign/storage.go b/go/cm/sign/storage.go
new file mode 100644 (file)
index 0000000..d1efcb2
--- /dev/null
@@ -0,0 +1,48 @@
+// KEKS/CM -- KEKS-encoded cryptographic messages
+// Copyright (C) 2024-2025 Sergey Matveev <stargrave@stargrave.org>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as
+// published by the Free Software Foundation, version 3 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package sign
+
+import (
+       "encoding/hex"
+       "errors"
+       "io/fs"
+       "os"
+       "path"
+       "strings"
+)
+
+type PubStorage interface {
+       Get(id []byte) (*Signed, error)
+}
+
+type PubStorageDir struct {
+       Dir string
+}
+
+func (s *PubStorageDir) Get(id []byte) (signed *Signed, err error) {
+       var data []byte
+       data, err = os.ReadFile(path.Join(
+               s.Dir, strings.ToUpper(hex.EncodeToString(id)),
+       ))
+       if err != nil {
+               if errors.Is(err, fs.ErrNotExist) {
+                       return nil, nil
+               }
+               return nil, err
+       }
+       signed, _, err = PubParse(data)
+       return signed, err
+}
index 52b34dd455bf34146ae2143aa3e45753882e3f31010ae1fe821cc588b6213703..d2cebd0f6b06f80003f6b368d57200434aee7cfb1af1ddb126435f18b60cea6b 100755 (executable)
@@ -11,6 +11,7 @@ cd "$root/.."
 [ -d vendor ] && mod_vendor="-mod=vendor" || redo-ifchange \
     enc/encrypted.schema.keks \
     hash/prehash.schema.keks \
+    sign/prv.schema.keks \
     sign/pub.schema.keks \
     sign/signed.schema.keks
 mkdir -p bin
index c73d16665f0a2a55fb4a254c1b6f36349ac9f8724d31093fd74d2d083a0f2a1a..b22e18a1dff908de7450686f9b01f125aac41fa54baf0f247bf22f8e3acc5930 100644 (file)
@@ -12,5 +12,6 @@ Metalink4 file contains its OpenSSH signature.
 => PUBKEY-CM.pub\r
 => PUBKEY-CM.pub.asc\r
 
+    $ ln -s PUBKEY-CM.pub $(kekspp -v -p /data/id <PUBKEY-CM.pub)
     $ cat keks-$version.tar.zst.sig keks-$version.tar.zst |
-        cmsigtool -v -d 4<PUBKEY-CM.pub
+        cmsigtool -v -d -pubs .
index 7044e5a902b224477dc3c8fd7493fff6a649c3bb024652491c9df99e3f6ffeb3..9dae158e6544e0cd2943439c6a08cea6ce304641b89160be04abd4e524c89b90 100644 (file)
@@ -1,6 +1,9 @@
 do-backs\r
 Private key container.
 
-<<    [schemas/av.tcl]\r
+<<    [schemas/prv.tcl]\r
 
 Stored in a file, it should begin with "cm/prv" [encoding/MAGIC].
+
+Optional /pub-id contains corresponding public key's /data/id to ease
+their searching.
diff --git a/tcl/schemas/prv.tcl b/tcl/schemas/prv.tcl
new file mode 100644 (file)
index 0000000..a7c484e
--- /dev/null
@@ -0,0 +1,8 @@
+prv {
+    {field . {map}}
+    {field a {str} >0} {# algorithm identifier}
+    {field v {bin}}
+    {field pub-id {with fpr} optional}
+}
+
+schema-include fpr.tcl
index 0edbb4a81991452b60e9bb9f7488d6fb6ab8ae799f80e9b65a835661c3861553..27eedf47cb7325a71964a255f2a949dff83f6703782e7833bd5370ec0348e3fb 100644 (file)
@@ -12,6 +12,7 @@ signed {
 tbs {
     {field . {map}}
     {field t {str} >0} {# type of the data we sign}
+    {field id {hexlet} optional}
 }
 
 sig {