arche / internal/archesrv/signatures.go

commit 154431fd
  1package archesrv
  2
  3import (
  4	"bytes"
  5	"encoding/hex"
  6	"time"
  7
  8	"arche/internal/object"
  9	"arche/internal/repo"
 10
 11	"golang.org/x/crypto/ssh"
 12)
 13
 14type CommitSigStatus struct {
 15	CommitID   [32]byte
 16	Status     string
 17	KeyID      string
 18	VerifiedAt time.Time
 19}
 20
 21func (d *DB) RecordCommitSignature(r *repo.Repo, commitID [32]byte, c *object.Commit, userID int64) error {
 22	var existing int
 23	_ = d.db.QueryRow("SELECT COUNT(*) FROM commit_signatures WHERE commit_id=?", commitID[:]).Scan(&existing)
 24	if existing > 0 {
 25		return nil
 26	}
 27
 28	status := "unsigned"
 29	keyID := ""
 30
 31	if len(c.CommitSig) > 0 {
 32		body := object.CommitBodyForSigning(c)
 33
 34		keys, err := d.ListSSHKeys(userID)
 35		if err == nil {
 36			for _, k := range keys {
 37				pub, _, _, _, err := ssh.ParseAuthorizedKey([]byte(k.PublicKey))
 38				if err != nil {
 39					continue
 40				}
 41				if err := object.VerifyCommitSig(body, c.CommitSig, pub); err == nil {
 42					status = "verified"
 43					keyID = ssh.FingerprintSHA256(pub)
 44					break
 45				}
 46			}
 47		}
 48
 49		if status == "unsigned" {
 50			var sig ssh.Signature
 51			if err := ssh.Unmarshal(c.CommitSig, &sig); err == nil {
 52				status = "unknown_key"
 53			} else {
 54				status = "invalid"
 55			}
 56		}
 57	}
 58
 59	_, err := d.db.Exec(
 60		"INSERT OR IGNORE INTO commit_signatures (commit_id, status, key_id, verified_at) VALUES (?,?,?,?)",
 61		commitID[:], status, keyID, time.Now().Unix(),
 62	)
 63	return err
 64}
 65
 66func (d *DB) GetCommitSigStatus(commitID [32]byte) string {
 67	var status string
 68	if err := d.db.QueryRow(
 69		"SELECT status FROM commit_signatures WHERE commit_id=?", commitID[:],
 70	).Scan(&status); err != nil {
 71		return "unsigned"
 72	}
 73	return status
 74}
 75
 76func collectNewCommitIDs(r *repo.Repo, oldHex, newHex string) [][32]byte {
 77	if len(newHex) != 64 {
 78		return nil
 79	}
 80	newBytes, err := hex.DecodeString(newHex)
 81	if err != nil || len(newBytes) != 32 {
 82		return nil
 83	}
 84	var newID [32]byte
 85	copy(newID[:], newBytes)
 86
 87	var oldID [32]byte
 88	if len(oldHex) == 64 {
 89		if b, err := hex.DecodeString(oldHex); err == nil && len(b) == 32 {
 90			copy(oldID[:], b)
 91		}
 92	}
 93
 94	seen := make(map[[32]byte]bool)
 95	queue := [][32]byte{newID}
 96	var result [][32]byte
 97	for len(queue) > 0 {
 98		id := queue[0]
 99		queue = queue[1:]
100		if seen[id] || bytes.Equal(id[:], oldID[:]) {
101			continue
102		}
103		seen[id] = true
104		result = append(result, id)
105		c, err := r.ReadCommit(id)
106		if err != nil {
107			continue
108		}
109		for _, p := range c.Parents {
110			if !seen[p] {
111				queue = append(queue, p)
112			}
113		}
114	}
115	return result
116}