cwtch/protocol/connections/spam/spamguard.go

101 lines
2.5 KiB
Go

package spam
import (
"crypto/rand"
"crypto/sha256"
"cwtch.im/cwtch/protocol"
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
"git.openprivacy.ca/openprivacy/libricochet-go/wire/control"
"github.com/golang/protobuf/proto"
"io"
//"fmt"
)
// Guard implements a spam protection mechanism for Cwtch Servers.
type Guard struct {
Difficulty int
nonce [24]byte
}
func getRandomness(arr *[24]byte) {
if _, err := io.ReadFull(rand.Reader, arr[:]); err != nil {
utils.CheckError(err)
}
}
//GenerateChallenge returns a channel result packet with a spamguard challenge nonce
func (sg *Guard) GenerateChallenge(channelID int32) []byte {
cr := &Protocol_Data_Control.ChannelResult{
ChannelIdentifier: proto.Int32(channelID),
Opened: proto.Bool(true),
}
var nonce [24]byte
getRandomness(&nonce)
sg.nonce = nonce
err := proto.SetExtension(cr, protocol.E_ServerNonce, sg.nonce[:])
utils.CheckError(err)
pc := &Protocol_Data_Control.Packet{
ChannelResult: cr,
}
ret, err := proto.Marshal(pc)
utils.CheckError(err)
return ret
}
// SolveChallenge takes in a challenge and a message and returns a solution
// The solution is a 24 byte nonce which when hashed with the challenge and the message
// produces a sha256 hash with Difficulty leading 0s
func (sg *Guard) SolveChallenge(challenge []byte, message []byte) []byte {
solved := false
var spamguard [24]byte
sum := sha256.Sum256([]byte{})
solve := make([]byte, len(challenge)+len(message)+len(spamguard))
for !solved {
getRandomness(&spamguard)
copy(solve[0:], challenge[:])
copy(solve[len(challenge):], message[:])
copy(solve[len(challenge)+len(message):], spamguard[:])
sum = sha256.Sum256(solve)
solved = true
for i := 0; i < sg.Difficulty; i++ {
if sum[i] != 0x00 {
solved = false
}
}
}
//fmt.Printf("[SOLVED] %x\n",sha256.Sum256(solve))
return spamguard[:]
}
// ValidateChallenge returns true if the message and spamguard pass the challenge
func (sg *Guard) ValidateChallenge(message []byte, spamguard []byte) bool {
if len(spamguard) != 24 {
return false
}
// If the message is too large just throw it away.
if len(message) > 2048 {
return false
}
solve := make([]byte, len(sg.nonce)+len(message)+len(spamguard))
copy(solve[0:], sg.nonce[:])
copy(solve[len(sg.nonce):], message[:])
copy(solve[len(sg.nonce)+len(message):], spamguard[:])
sum := sha256.Sum256(solve)
for i := 0; i < sg.Difficulty; i++ {
if sum[i] != 0x00 {
return false
}
}
return true
}