]> Cypherpunks repositories - keks.git/commitdiff
Ability to verify multiple signatures
authorSergey Matveev <stargrave@stargrave.org>
Thu, 17 Apr 2025 08:06:29 +0000 (11:06 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Thu, 17 Apr 2025 08:06:29 +0000 (11:06 +0300)
go/cm/cmd/cmsigtool/main.go
go/cm/cmd/cmsigtool/usage.go
go/cm/sign/pub.go
tcl/schemas/pub-sig-tbs.tcl
tcl/schemas/signed.tcl

index 611f4cd2e6cd13b0602d2de24b1c499eed627ba5394d5127b9e3078d0bdcf42a..412c7d9036043b4bb5f8f567185c085a108acdcaf6133eaacf0e53b76fc6e514 100644 (file)
@@ -71,11 +71,20 @@ func main() {
        fdPubR := os.NewFile(FdPubR, "pub-in")
        fdPrvR := os.NewFile(FdPrvR, "prv-in")
 
-       pub, _, err := sign.PubParse(mustReadAll(fdPubR))
-       if err != nil {
-               log.Fatal(err)
+       var pubs []*sign.PubLoad
+       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.PubLoad())
+               }
+               fdPubR.Close()
        }
-       fdPubR.Close()
 
        stdin := bufio.NewReaderSize(os.Stdin, BlobChunkLen)
        if *verify {
@@ -100,16 +109,17 @@ func main() {
                        err = decoder.UnmarshalStruct(&prehash)
                }
                var signed sign.Signed
-               var hasher hash.Hash
+               hashers := make(map[string]hash.Hash)
                if err == nil && prehash.T == mode.PrehashT {
-                       if len(prehash.Sigs) > 1 {
-                               log.Fatal("prehash: currently only single signature support")
-                       }
+                       dsts := make([]io.Writer, 0, len(prehash.Sigs)+1)
+                       dsts = append(dsts, os.Stdout)
                        for algo := range prehash.Sigs {
-                               hasher = cmhash.ByName(algo)
-                       }
-                       if hasher == nil {
-                               log.Fatal("prehash: unsupported algorithm")
+                               hasher := cmhash.ByName(algo)
+                               if hasher == nil {
+                                       log.Fatalln("prehash: unsupported algorithm:", algo)
+                               }
+                               hashers[algo] = hasher
+                               dsts = append(dsts, hasher)
                        }
                        var blob *keks.BlobDecoder
                        blob, err = keks.NewBlobDecoder(stdin, 1<<32)
@@ -117,7 +127,7 @@ func main() {
                                log.Fatal(err)
                        }
                        var chunk []byte
-                       mw := io.MultiWriter(hasher, os.Stdout)
+                       mw := io.MultiWriter(dsts...)
                        for {
                                chunk, err = blob.Next()
                                if err != nil {
@@ -144,48 +154,65 @@ func main() {
                if err != nil {
                        log.Fatal(err)
                }
-               if len(signed.Sigs) == 0 {
-                       log.Fatal("no sigs")
-               }
-               if len(signed.Sigs) > 1 {
-                       log.Fatal("prehash: currently only single signature support")
-               }
-               signer := pub.PubLoad()
                if signed.Load.T != *typ {
                        log.Fatalln("differing load type:", signed.Load.T)
                }
-               sig := signed.Sigs[0]
-               var tbs *sign.TBS
-               tbs, err = sig.TBSGet()
-               if err != nil {
-                       log.Fatal(err)
-               }
-               if len(encryptedTo) > 0 {
-                       if len(tbs.EncryptedTo) == 0 {
-                               log.Fatal("missing encrypted-to")
-                       }
-                       found := false
-                       for _, their := range tbs.EncryptedTo {
-                               for _, our := range encryptedTo {
-                                       if bytes.Equal(our, their) {
-                                               found = true
-                                               break
+               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 tbs *sign.TBS
+                               tbs, err = sig.TBSGet()
+                               if err != nil {
+                                       log.Fatal(err)
+                               }
+                               if len(encryptedTo) > 0 {
+                                       if len(tbs.EncryptedTo) == 0 {
+                                               log.Fatalln(hex.EncodeToString(sid), "missing encrypted-to")
+                                       }
+                                       found := false
+                                       for _, their := range tbs.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 err = signed.CertificationCheckSignatureFrom(
+                                       pub, &hasher,
+                               ); err != nil {
+                                       log.Fatalln(hex.EncodeToString(sid), err)
+                               }
+                               break
                        }
-                       if !found {
-                               log.Fatalln("corresponding encrypted-to not found")
-                       }
-               }
-               if prehash.T == "" {
-                       hasher = cmhash.ByName(sig.Sign.A)
-                       if _, err = io.Copy(hasher, stdin); err != nil {
-                               log.Fatal(err)
+                       if !signerFound {
+                               log.Fatalln(hex.EncodeToString(sid), "can not find signer")
                        }
                }
-               if err = signed.CertificationCheckSignatureFrom(signer, &hasher); err != nil {
-                       log.Fatal(err)
-               }
        } else {
                var signer sign.Iface
                signer, _, err = sign.PrvParse(mustReadAll(fdPrvR))
@@ -241,7 +268,7 @@ func main() {
                if len(encryptedTo) > 0 {
                        tbs["encrypted-to"] = encryptedTo
                }
-               if err = signed.SignWith(pub.PubLoad(), signer, tbs); err != nil {
+               if err = signed.SignWith(pubs[0], signer, tbs); err != nil {
                        log.Fatal(err)
                }
                if _, err = keks.Encode(os.Stdout, signed, nil); err != nil {
index 53b5078c706392a7632ec82146597cb361f045c8d00c5b6623fd98c5cf6ca2bc..6f50677544eaa96344cc12ea474b652955d8b2b7bb1dcb0908500acae0c8b198 100644 (file)
@@ -24,9 +24,9 @@ import (
 func usage() {
        fmt.Fprintf(os.Stderr, `Usage:
   cmsigtool [-t TYPE] 4<PUB 8<PRV <DATA >DATA.signed
-  cmsigtool -v [-t TYPE] 4<PUB <DATA.signed >DATA
+  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<PUB <(cat DATA.signature DATA)
+  cmsigtool -d -v [-t TYPE] 4<PUB0[,PUB1...] <(cat DATA.signature DATA)
 
 DATA.signed holds completely encapsulated DATA.
 -d(etached) mode keeps DATA completely separate.
index 88a1972e0e2cdc145c6a4dc7e3c77ac5672e7ce5b016f041b8789b4494963a56..bc7bf64d1c9886c43099d8f10a626f04155ba0810c720c92093b3a6d706f46aa 100644 (file)
@@ -215,37 +215,35 @@ func (signed *Signed) CertificationCheckSignatureFrom(
        parent *PubLoad,
        prehasher *hash.Hash,
 ) (err error) {
-       if len(signed.Sigs) != 1 {
-               err = errors.New("can verify only single signature")
-               return
-       }
        if !parent.Can(KUSig) || len(parent.Pub) != 1 {
                err = errors.New("parent can not sign")
                return
        }
-       sig := signed.Sigs[0]
-       if !bytes.Equal(sig.TBS["sid"].([]byte), parent.Id) {
-               err = errors.New("sid != parent pub id")
-               return
-       }
-       if prehasher == nil {
-               var tbs bytes.Buffer
-               if _, err = keks.Encode(&tbs, signed.Load, nil); err != nil {
-                       return
+       for _, sig := range signed.Sigs {
+               if !bytes.Equal(sig.TBS["sid"].([]byte), parent.Id) {
+                       continue
                }
-               if _, err = keks.Encode(&tbs, sig.TBS, nil); err != nil {
-                       return
-               }
-               return parent.CheckSignature(sig.Sign.A, tbs.Bytes(), sig.Sign.V)
-       } else {
-               if _, err = keks.Encode(*prehasher, signed.Load, nil); err != nil {
-                       return
-               }
-               if _, err = keks.Encode(*prehasher, sig.TBS, nil); err != nil {
-                       return
+               if prehasher == nil {
+                       var tbs bytes.Buffer
+                       if _, err = keks.Encode(&tbs, signed.Load, nil); err != nil {
+                               return
+                       }
+                       if _, err = keks.Encode(&tbs, sig.TBS, nil); err != nil {
+                               return
+                       }
+                       return parent.CheckSignature(sig.Sign.A, tbs.Bytes(), sig.Sign.V)
+               } else {
+                       if _, err = keks.Encode(*prehasher, signed.Load, nil); err != nil {
+                               return
+                       }
+                       if _, err = keks.Encode(*prehasher, sig.TBS, nil); err != nil {
+                               return
+                       }
+                       return parent.CheckSignaturePrehash(
+                               sig.Sign.A, (*prehasher).Sum(nil), sig.Sign.V)
                }
-               return parent.CheckSignaturePrehash(sig.Sign.A, (*prehasher).Sum(nil), sig.Sign.V)
        }
+       return errors.New("can not find necessary sid")
 }
 
 // Get PubLoad from Signed.
index 60e6efbd3470ccf383b5ae4679b2fd0ef2677b6d925a371bba5a3ddc7b8e72a8..834bb2602597367c423d26354aab292030e4dafff10f899770529ba1c1bc7641 100644 (file)
@@ -7,5 +7,5 @@ pub-sig-tbs {
     {field cid {hexlet}}
     {field exp {with expiration}}
     {field nonce {bin} >0 optional}
-    {field when {tai} utc optional}
+    {field when {tai} utc prec=ms optional}
 }
index 8af5736dde4a2d20dda56e65f5573e9ce79c61d993dd1c7add49eac10e761622..e81f2f9e1ad93fa09e1d60cd769fcb91df617aedee7493c0d2eeb070cda595b7 100644 (file)
@@ -21,7 +21,7 @@ tbs {
     {field . {map}}
     {field sid {with fpr}}
     {field nonce {bin} >0 optional} {# random bytes}
-    {field when {tai} utc optional}
+    {field when {tai} utc prec=ms optional}
 
     {# recipient's fingerprints}
     {field encrypted-to {list} {of fpr} >0 optional}