2018-03-09 20:44:13 +00:00
|
|
|
package spam
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/rand"
|
|
|
|
"crypto/sha256"
|
2018-05-28 20:48:35 +00:00
|
|
|
"cwtch.im/cwtch/protocol"
|
2018-03-09 20:44:13 +00:00
|
|
|
"github.com/golang/protobuf/proto"
|
2018-06-23 16:15:36 +00:00
|
|
|
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
|
|
|
|
"git.openprivacy.ca/openprivacy/libricochet-go/wire/control"
|
2018-03-09 20:44:13 +00:00
|
|
|
"io"
|
2018-05-09 19:09:00 +00:00
|
|
|
//"fmt"
|
2018-03-09 20:44:13 +00:00
|
|
|
)
|
|
|
|
|
2018-03-15 16:33:26 +00:00
|
|
|
// Guard implements a spam protection mechanism for Cwtch Servers.
|
2018-03-14 22:23:35 +00:00
|
|
|
type Guard struct {
|
2018-03-09 20:44:13 +00:00
|
|
|
Difficulty int
|
|
|
|
nonce [24]byte
|
|
|
|
}
|
|
|
|
|
2018-04-02 21:10:29 +00:00
|
|
|
func getRandomness(arr *[24]byte) {
|
|
|
|
if _, err := io.ReadFull(rand.Reader, arr[:]); err != nil {
|
|
|
|
utils.CheckError(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-09 21:02:42 +00:00
|
|
|
//GenerateChallenge returns a channel result packet with a spamguard challenge nonce
|
2018-03-14 22:23:35 +00:00
|
|
|
func (sg *Guard) GenerateChallenge(channelID int32) []byte {
|
2018-03-09 20:44:13 +00:00
|
|
|
|
|
|
|
cr := &Protocol_Data_Control.ChannelResult{
|
|
|
|
ChannelIdentifier: proto.Int32(channelID),
|
|
|
|
Opened: proto.Bool(true),
|
|
|
|
}
|
|
|
|
|
|
|
|
var nonce [24]byte
|
2018-04-02 21:10:29 +00:00
|
|
|
getRandomness(&nonce)
|
2018-03-09 20:44:13 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2018-03-09 21:02:42 +00:00
|
|
|
// 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
|
2018-03-14 22:23:35 +00:00
|
|
|
func (sg *Guard) SolveChallenge(challenge []byte, message []byte) []byte {
|
2018-03-09 20:44:13 +00:00
|
|
|
solved := false
|
|
|
|
var spamguard [24]byte
|
|
|
|
sum := sha256.Sum256([]byte{})
|
|
|
|
solve := make([]byte, len(challenge)+len(message)+len(spamguard))
|
|
|
|
for !solved {
|
|
|
|
|
2018-04-02 21:10:29 +00:00
|
|
|
getRandomness(&spamguard)
|
2018-03-09 20:44:13 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-05-09 19:09:00 +00:00
|
|
|
//fmt.Printf("[SOLVED] %x\n",sha256.Sum256(solve))
|
2018-03-09 20:44:13 +00:00
|
|
|
return spamguard[:]
|
|
|
|
}
|
|
|
|
|
2018-03-09 21:02:42 +00:00
|
|
|
// ValidateChallenge returns true if the message and spamguard pass the challenge
|
2018-03-14 22:23:35 +00:00
|
|
|
func (sg *Guard) ValidateChallenge(message []byte, spamguard []byte) bool {
|
2018-03-09 21:02:42 +00:00
|
|
|
if len(spamguard) != 24 {
|
2018-03-10 21:26:19 +00:00
|
|
|
return false
|
|
|
|
}
|
2018-05-28 17:44:47 +00:00
|
|
|
|
|
|
|
// If the message is too large just throw it away.
|
|
|
|
if len(message) > 2048 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2018-03-09 20:44:13 +00:00
|
|
|
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)
|
2018-04-02 21:10:29 +00:00
|
|
|
|
2018-03-09 20:44:13 +00:00
|
|
|
for i := 0; i < sg.Difficulty; i++ {
|
|
|
|
if sum[i] != 0x00 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|