forked from tangled.org/core
Monorepo for Tangled

cmd/verifysig: tiny tool to verify commit signatures

Signed-off-by: Anirudh Oppiliappan <anirudh@tangled.sh>

authored by anirudh.fi and committed by Tangled fec4117e d0fbcd5e

Changed files
+107 -1
cmd
verifysig
crypto
+103
cmd/verifysig/main.go
··· 1 + package main 2 + 3 + import ( 4 + "flag" 5 + "fmt" 6 + "io" 7 + "os" 8 + "strings" 9 + 10 + "tangled.sh/tangled.sh/core/crypto" 11 + ) 12 + 13 + func parseCommitObject(commitData string) (string, string, error) { 14 + lines := strings.Split(commitData, "\n") 15 + 16 + var payloadLines []string 17 + var signatureLines []string 18 + var inSignature bool 19 + var foundSignature bool 20 + 21 + for _, line := range lines { 22 + if strings.HasPrefix(line, "gpgsig ") { 23 + foundSignature = true 24 + inSignature = true 25 + // remove 'gpgsig' prefix 26 + sigLine := strings.TrimPrefix(line, "gpgsig ") 27 + signatureLines = append(signatureLines, sigLine) 28 + continue 29 + } 30 + 31 + if inSignature { 32 + // check if this line is part of the signature (starts with space) 33 + if strings.HasPrefix(line, " ") { 34 + // remove the leading space and add to signature 35 + sigLine := strings.TrimPrefix(line, " ") 36 + signatureLines = append(signatureLines, sigLine) 37 + continue 38 + } else { 39 + // end of signature block 40 + inSignature = false 41 + // this line is part of payload, so add it 42 + payloadLines = append(payloadLines, line) 43 + } 44 + } else { 45 + // regular payload line 46 + payloadLines = append(payloadLines, line) 47 + } 48 + } 49 + 50 + if !foundSignature { 51 + return "", commitData, nil // no signature found, return empty signature and full data as payload 52 + } 53 + 54 + signature := strings.Join(signatureLines, "\n") 55 + payload := strings.Join(payloadLines, "\n") 56 + 57 + return signature, payload, nil 58 + } 59 + 60 + func main() { 61 + var pubkeyPath string 62 + flag.StringVar(&pubkeyPath, "pubkey", "", "Path to the public key file") 63 + flag.Parse() 64 + 65 + var pubKey []byte 66 + var err error 67 + if pubkeyPath != "" { 68 + pubKey, err = os.ReadFile(pubkeyPath) 69 + if err != nil { 70 + fmt.Fprintf(os.Stderr, "error reading public key file: %v\n", err) 71 + os.Exit(1) 72 + } 73 + } 74 + 75 + input, err := io.ReadAll(os.Stdin) 76 + if err != nil { 77 + fmt.Fprintf(os.Stderr, "error reading from stdin: %v\n", err) 78 + os.Exit(1) 79 + } 80 + 81 + commitData := string(input) 82 + 83 + signature, payload, err := parseCommitObject(commitData) 84 + if err != nil { 85 + fmt.Fprintf(os.Stderr, "error parsing commit: %v\n", err) 86 + os.Exit(1) 87 + } 88 + 89 + fmt.Println("signature") 90 + fmt.Println(signature) 91 + fmt.Println() 92 + fmt.Println("payload:") 93 + fmt.Println(payload) 94 + 95 + err, ok := crypto.VerifySignature(pubKey, []byte(signature), []byte(payload)) 96 + if err != nil { 97 + fmt.Fprintf(os.Stderr, "%v\n", err) 98 + os.Exit(1) 99 + } 100 + if ok { 101 + fmt.Println("ok") 102 + } 103 + }
+4 -1
crypto/verify.go
··· 24 24 } 25 25 26 26 buf := bytes.NewBuffer(payload) 27 - sshsig.Verify(buf, sig, pub, sshsig.HashSHA256, "git") 27 + // we use sha-512 because ed25519 keys require it internally; rsa keys support 28 + // multiple algorithms but sha-512 is most secure, and git's ssh signing defaults 29 + // to sha-512 for all key types anyway. 30 + err = sshsig.Verify(buf, sig, pub, sshsig.HashSHA512, "git") 28 31 return err, err == nil 29 32 } 30 33