Files
NixMsg/internal/protocol/fingerprint.go

82 lines
1.8 KiB
Go

package protocol
import (
"crypto/sha256"
"encoding/binary"
"encoding/hex"
)
// RequestFingerprint 计算第 7.3 节发送请求指纹(十六进制小写 SHA-256)。
// 字段:to.kind、to.id、body.enc、content_type、解码后正文、meta(键排序)、
// send_at_ms、delay_ms、offline.keep、offline.ttl_seconds、receipt。
// 不含 talk_password 与 rid。meta 键顺序不影响结果。
// receipt / offline 使用文档默认值后的有效值;缺省的 send_at_ms、delay_ms 不写入对应槽位。
func RequestFingerprint(s *Send) (string, error) {
raw, err := DecodeBody(s.Body)
if err != nil {
return "", err
}
metaJSON, err := MetaCanonicalJSON(s.Meta)
if err != nil {
return "", badRequest("invalid meta")
}
h := sha256.New()
writeStr(h, s.To.Kind)
writeStr(h, s.To.ID)
writeStr(h, s.Body.Enc)
writeStr(h, EffectiveContentType(s.Body))
writeBytes(h, raw)
writeBytes(h, metaJSON)
writeOptInt64(h, s.SendAtMs)
writeOptInt64(h, s.DelayMs)
keep := EffectiveOfflineKeep(s)
writeBool(h, keep)
ttl := EffectiveOfflineTTL(s)
writeInt64(h, ttl)
writeBool(h, EffectiveReceipt(s))
sum := h.Sum(nil)
return hex.EncodeToString(sum), nil
}
type hashWriter interface {
Write(p []byte) (int, error)
}
func writeStr(h hashWriter, s string) {
writeBytes(h, []byte(s))
}
func writeBytes(h hashWriter, b []byte) {
var lenBuf [8]byte
binary.BigEndian.PutUint64(lenBuf[:], uint64(len(b)))
_, _ = h.Write(lenBuf[:])
_, _ = h.Write(b)
}
func writeBool(h hashWriter, v bool) {
if v {
_, _ = h.Write([]byte{1})
} else {
_, _ = h.Write([]byte{0})
}
}
func writeInt64(h hashWriter, v int64) {
var buf [8]byte
binary.BigEndian.PutUint64(buf[:], uint64(v))
_, _ = h.Write(buf[:])
}
func writeOptInt64(h hashWriter, v *int64) {
if v == nil {
_, _ = h.Write([]byte{0})
return
}
_, _ = h.Write([]byte{1})
writeInt64(h, *v)
}