203 lines
4.4 KiB
Go
203 lines
4.4 KiB
Go
package contentmessage
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"github.com/cloudflare/circl/sign/ed25519"
|
|
"github.com/ipfs/go-cid"
|
|
"github.com/multiformats/go-multihash"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
var messageCacheLimit = 256
|
|
var messageCacheMutex sync.RWMutex
|
|
var messageCache = make(map[string]*ContentMessage)
|
|
|
|
func SetMessageCacheLimit(limit int) {
|
|
messageCacheLimit = limit
|
|
}
|
|
|
|
type ContentMessage struct {
|
|
Version uint64
|
|
PublicKey ed25519.PublicKey
|
|
IssueTime int64
|
|
Identifier cid.Cid
|
|
VerificationResult *bool
|
|
Signature []byte
|
|
}
|
|
|
|
func NewContentMessageV0(identifier cid.Cid, privateKey ed25519.PrivateKey) ContentMessage {
|
|
message := ContentMessage{
|
|
Version: 0,
|
|
IssueTime: time.Now().UTC().Unix(),
|
|
Identifier: identifier,
|
|
}
|
|
|
|
message.Sign(privateKey)
|
|
|
|
return message
|
|
}
|
|
func NewContentMessageV1(hash multihash.Multihash, privateKey ed25519.PrivateKey) ContentMessage {
|
|
message := ContentMessage{
|
|
Version: 1,
|
|
IssueTime: time.Now().UTC().Unix(),
|
|
Identifier: cid.NewCidV1(cid.Raw, hash),
|
|
}
|
|
|
|
message.Sign(privateKey)
|
|
|
|
return message
|
|
}
|
|
|
|
func (s *ContentMessage) Sign(privateKey ed25519.PrivateKey) {
|
|
s.PublicKey = make([]byte, ed25519.PublicKeySize)
|
|
copy(s.PublicKey, privateKey[32:])
|
|
s.Signature = ed25519.Sign(privateKey, s.EncodeMessage())
|
|
s.VerificationResult = nil
|
|
}
|
|
|
|
func (s *ContentMessage) Verify() (bool, bool) {
|
|
currentTime := time.Now()
|
|
|
|
issueTime := time.Unix(s.IssueTime, 0)
|
|
validityStart := issueTime.Add(-time.Hour) //Only one hour before time
|
|
validityEnd := issueTime.Add(time.Hour * 24) //Only 24 hours after time
|
|
|
|
if validityStart.After(currentTime) {
|
|
return false, false
|
|
}
|
|
|
|
if validityEnd.Before(currentTime) {
|
|
return false, false
|
|
}
|
|
|
|
messageCacheMutex.RLock()
|
|
k := string(s.Encode())
|
|
cachedMessage, ok := messageCache[k]
|
|
messageCacheMutex.RUnlock()
|
|
if ok {
|
|
return *cachedMessage.VerificationResult, true
|
|
}
|
|
|
|
messageCacheMutex.Lock()
|
|
defer messageCacheMutex.Unlock()
|
|
|
|
if s.VerificationResult == nil {
|
|
makeBool := func(v bool) *bool { return &v }
|
|
s.VerificationResult = makeBool(ed25519.Verify(s.PublicKey, s.EncodeMessage(), s.Signature))
|
|
}
|
|
|
|
if len(messageCache) >= messageCacheLimit {
|
|
//Find oldest value, remove it
|
|
var item *ContentMessage
|
|
for _, e := range messageCache {
|
|
if item == nil || e.IssueTime < item.IssueTime {
|
|
item = e
|
|
}
|
|
}
|
|
|
|
if item != nil {
|
|
delete(messageCache, string(item.Encode()))
|
|
}
|
|
}
|
|
|
|
messageCache[k] = s
|
|
|
|
return *s.VerificationResult, false
|
|
}
|
|
|
|
func (s *ContentMessage) EncodeMessage() []byte {
|
|
message := &bytes.Buffer{}
|
|
|
|
buf := make([]byte, binary.MaxVarintLen64)
|
|
|
|
n := binary.PutUvarint(buf, s.Version) //signature version
|
|
_, _ = message.Write(buf[:n])
|
|
|
|
if s.Version == 0 || s.Version == 1 {
|
|
_, _ = message.Write(s.PublicKey)
|
|
n = binary.PutVarint(buf, s.IssueTime) //time
|
|
_, _ = message.Write(buf[:n])
|
|
|
|
if s.Version == 1 {
|
|
_, _ = message.Write(s.Identifier.Hash())
|
|
} else {
|
|
_, _ = s.Identifier.WriteBytes(message)
|
|
}
|
|
|
|
return message.Bytes()
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *ContentMessage) Encode() []byte {
|
|
message := s.EncodeMessage()
|
|
|
|
if message == nil || len(s.Signature) != ed25519.SignatureSize {
|
|
return nil
|
|
}
|
|
|
|
return append(message, s.Signature...)
|
|
}
|
|
|
|
func (s *ContentMessage) String() string {
|
|
return fmt.Sprintf("%d %x %d %s %x", s.Version, s.PublicKey, s.IssueTime, s.Identifier.String(), s.Signature)
|
|
}
|
|
|
|
func DecodeContentMessage(signatureBytes []byte) *ContentMessage {
|
|
message := ContentMessage{}
|
|
|
|
buffer := bytes.NewBuffer(signatureBytes)
|
|
|
|
var err error
|
|
|
|
message.Version, err = binary.ReadUvarint(buffer)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
if message.Version == 0 || message.Version == 1 {
|
|
message.PublicKey = make([]byte, ed25519.PublicKeySize)
|
|
_, err := buffer.Read(message.PublicKey)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
message.IssueTime, err = binary.ReadVarint(buffer)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
if message.Version == 1 {
|
|
read, mh, err := multihash.MHFromBytes(buffer.Bytes())
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
buffer.Next(read)
|
|
message.Identifier = cid.NewCidV1(cid.Raw, mh)
|
|
} else {
|
|
_, message.Identifier, err = cid.CidFromReader(buffer)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
}
|
|
|
|
message.Signature = make([]byte, ed25519.SignatureSize)
|
|
_, err = buffer.Read(message.Signature)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
if buffer.Len() != 0 { //Unknown extra data
|
|
return nil
|
|
}
|
|
|
|
return &message
|
|
|
|
}
|
|
|
|
return nil
|
|
}
|