Fixing up messaging to use a proper ordering of messages based on timeline and previous hash.

This isn't perfect, but under normal conditions should prevent malicous server reordering. Still need a second order function
This commit is contained in:
Sarah Jamie Lewis 2018-04-02 14:10:29 -07:00
rodič 5f44fb8ef8
revize f1d0a8e900
14 změnil soubory, kde provedl 315 přidání a 62 odebrání

Zobrazit soubor

@ -0,0 +1,49 @@
# Attacks On Cwtch
## Server Censorship
Servers must keep things for as long as possible. This messes with bandwidth requirements, means syncing takes really long.
We could improve fetch to say something like...fetch messages sent within the last day to improve that.
We should already restrict length to 1kb.
Force servers to keep things forever? Have clients do checks? Is this potentially creating a bigger issue down the line?
This means that secure key rotation is essential! We can't just rely on kdf because the rotation rate is known. Send secret
salt on invite!
## Subgroup Attack
* Alice invites Bob and Carol to her Group
* Carol invites Eve to the group, pretending that she is the Owner.
* After some time passes Carol send Eve a group key update
* Carol can now selectively reencrypt messages from Alice and Bob to Carol under the new group key.
Defenses
--------
Eve rejects the initial group invitation because the signed group id doesn't match Carol
Carol can create a new group with all the sam parameters and sign it herself though.
However Carol will notice messages she can decrypt but are intended for another group, and if she tries to send
a message to the group, Alice and Bob will discover their group has been compromised.
## Key Rotation Attacks
* Alice invites Bob and Carol to a new Group
* Alice invites Eve
* Alice rotates the key (using a kdf), sends the new key to the Group
* Alice sends invite to Eve with new Key
Now there is a window where Bob and Carol send messages without receiving the new Key. There is also a possibility that
Bob or Carol miss the Key rotation message by being offline during the entire Server buffer period.
Alice should then technically rebroadcast the key rotation message, along with the iteration, until she received confirmation from Bob and Carol?
RotateKey, 1
AckRotateKey
Invite
mAckInvite

Zobrazit soubor

@ -19,7 +19,7 @@ type Group struct {
SignedGroupID []byte
GroupKey [32]byte
GroupServer string
Timeline []Message
Timeline Timeline
Accepted bool
Owner string
}
@ -66,13 +66,14 @@ func (g *Group) Invite() []byte {
func (g *Group) AddMessage(message *protocol.DecryptedGroupMessage, verified bool) *Message {
timelineMessage := &Message{
Message: message.GetText(),
Timestamp: time.Unix(int64(message.GetTimestamp()), 0),
Signature: message.GetSignature(),
Verified: verified,
PeerID: message.GetOnion(),
Message: message.GetText(),
Timestamp: time.Unix(int64(message.GetTimestamp()), 0),
Signature: message.GetSignature(),
Verified: verified,
PeerID: message.GetOnion(),
PreviousMessageSig: message.GetPreviousMessageSig(),
}
g.Timeline = append(g.Timeline, *timelineMessage)
g.Timeline.Insert(timelineMessage)
return timelineMessage
}

Zobrazit soubor

@ -1,14 +1,73 @@
package model
import (
"log"
"sync"
"time"
)
type Timeline struct {
Messages []Message
lock sync.Mutex
}
// Message is a local representation of a given message sent over a group chat channel.
type Message struct {
Timestamp time.Time
PeerID string
Message string
Signature []byte
Verified bool
Timestamp time.Time
PeerID string
Message string
Signature []byte
Verified bool
PreviousMessageSig []byte
}
func (m *Message) IsBefore(message *Message) bool {
if m.Timestamp.Before(message.Timestamp) {
return true
}
return false
}
func compareSignatures(a []byte, b []byte) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}
func (t *Timeline) Insert(mi *Message) {
t.lock.Lock()
insert := false
place := len(t.Messages)
for i, m := range t.Messages {
if compareSignatures(m.Signature, mi.PreviousMessageSig) {
log.Printf("comp %d %v %v %x %x", place, mi.Message, m.Message, m.Signature[0:3], mi.PreviousMessageSig[0:3])
insert = true
place = i + 1
break
}
if mi.IsBefore(&m) {
place = i
insert = true
break
}
}
if insert == false {
t.Messages = append(t.Messages, *mi)
} else {
temp := make([]Message, len(t.Messages)+1)
copy(temp[0:place], t.Messages[0:place])
temp[place] = *mi
copy(temp[place+1:], t.Messages[place:])
t.Messages = temp
}
t.lock.Unlock()
}

58
model/message_test.go Normal file
Zobrazit soubor

@ -0,0 +1,58 @@
package model
import (
"git.mascherari.press/cwtch/protocol"
"github.com/golang/protobuf/proto"
"testing"
"time"
)
func TestMessageInsert(t *testing.T) {
timeline := new(Timeline)
// Setup the Group
sarah := GenerateNewProfile("Sarah")
alice := GenerateNewProfile("Alice")
sarah.AddContact(alice.Onion, alice.PublicProfile)
alice.AddContact(sarah.Onion, sarah.PublicProfile)
gid, invite := alice.StartGroup("aaa.onion")
gci := &protocol.CwtchPeerPacket{}
proto.Unmarshal(invite, gci)
sarah.ProcessInvite(gci.GetGroupChatInvite(), alice.Onion)
group := alice.GetGroupByGroupId(gid)
c1 := sarah.EncryptMessageToGroup("Hello World 1", group.GroupID)
alice.AttemptDecryption(c1)
c2 := alice.EncryptMessageToGroup("Hello World 2", group.GroupID)
alice.AttemptDecryption(c2)
c3 := alice.EncryptMessageToGroup("Hello World 3", group.GroupID)
alice.AttemptDecryption(c3)
time.Sleep(time.Second * 1)
c4 := alice.EncryptMessageToGroup("Hello World 4", group.GroupID)
alice.AttemptDecryption(c4)
c5 := alice.EncryptMessageToGroup("Hello World 5", group.GroupID)
_, m1 := sarah.AttemptDecryption(c1)
_, m2 := sarah.AttemptDecryption(c2)
_, m3 := sarah.AttemptDecryption(c3)
_, m4 := sarah.AttemptDecryption(c4)
_, m5 := sarah.AttemptDecryption(c5)
// Now we simulate a client receiving these messages completely out of order
timeline.Insert(m1)
timeline.Insert(m5)
timeline.Insert(m4)
timeline.Insert(m3)
timeline.Insert(m2)
for i, m := range timeline.Messages {
t.Logf("Messages %v: %v %x %x", i, m.Message, m.Signature, m.PreviousMessageSig)
}
}

Zobrazit soubor

@ -10,7 +10,7 @@ import (
"github.com/s-rah/go-ricochet/utils"
"golang.org/x/crypto/ed25519"
"io/ioutil"
"log"
// "log"
"strconv"
"time"
)
@ -127,10 +127,9 @@ func (p *Profile) ProcessInvite(gci *protocol.GroupChatInvite, peerHostname stri
func (p *Profile) AddGroup(group *Group) {
existingGroup, exists := p.Groups[group.GroupID]
if !exists {
// owned := ed25519.Verify(p.Contacts[group.Owner].Ed25519PublicKey,[]byte(group.GroupID),group.SignedGroupID)
p.Groups[group.GroupID] = group
}
if exists && existingGroup.Owner == group.Owner {
} else if exists && existingGroup.Owner == group.Owner {
p.Groups[group.GroupID] = group
}
@ -145,7 +144,7 @@ func (p *Profile) AddGroup(group *Group) {
func (p *Profile) AttemptDecryption(ciphertext []byte) (bool, *Message) {
for _, group := range p.Groups {
success, dgm := group.DecryptMessage(ciphertext)
log.Printf("Decrypt Attempt %v %v", success, dgm)
//log.Printf("Decrypt Attempt %v %v", success, dgm)
if success {
verified := p.VerifyGroupMessage(dgm.GetOnion(), group.GroupID, dgm.GetText(), dgm.GetTimestamp(), dgm.GetSignature())
return true, group.AddMessage(dgm, verified)
@ -160,12 +159,19 @@ func (p *Profile) EncryptMessageToGroup(message string, groupID string) (ciphert
group := p.Groups[groupID]
timestamp := time.Now().Unix()
signature := p.SignMessage(message + groupID + strconv.Itoa(int(timestamp)))
var prevSig []byte
if len(group.Timeline.Messages) > 0 {
prevSig = group.Timeline.Messages[len(group.Timeline.Messages)-1].Signature
} else {
prevSig = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
}
dm := &protocol.DecryptedGroupMessage{
Onion: proto.String(p.Onion),
Text: proto.String(message),
SignedGroupId: group.SignedGroupID[:],
Timestamp: proto.Int32(int32(timestamp)),
Signature: signature,
Onion: proto.String(p.Onion),
Text: proto.String(message),
SignedGroupId: group.SignedGroupID[:],
Timestamp: proto.Int32(int32(timestamp)),
Signature: signature,
PreviousMessageSig: prevSig,
}
ciphertext = group.EncryptMessage(dm)
return

Zobrazit soubor

@ -1 +1 @@
{"Name":"Sarah","Ed25519PublicKey":"UsCWNjTraCR3Z2dqtsW1A++ubJD5E5FnC+ACSIP/8hk=","Trusted":false,"Blocked":false,"Contacts":{},"Ed25519PrivateKey":"n89RBVJD3T2KAxOUFsBgJcyBcOtgu0eeGs464orKNT9SwJY2NOtoJHdnZ2q2xbUD765skPkTkWcL4AJIg//yGQ==","OnionPrivateKey":{"N":123805553263348528457396531577890431199849926799850215350792942327090732394534595405543245927983354464965037398489749312360344572108681888509184005620780768892052483022600381118025061152186760477945751128712525497823295804735134482165349819329437248988124357774675993218222587847682647131583478848460913040243,"E":65537,"D":36374504906934646313489635099749458363262131933581273121740667172866198517734465027903858894110494685794311535589363611540022197170341482875998871297559138745889655544439413518334368519721005053246504047688085489173938252813690494094575739926433903954150053038634347562274030322729843548978331756633920952393,"Primes":[11696469543379902568303131723781955090117870500969155291133497643805055498454292242391505499241962759128566231590491957401660779652639301001660955427807509,10584865185531250093066719901396357065936568829189765800518363959386090914453659853194245194887656726848216858880202829552571699371710179494672080022887527],"Precomputed":{"Dp":10160546066712868045776669547990150376665097357075773683255583172245687391587977961328574691812932875168964159645365177332406882316926439191991697109426877,"Dq":9841112737610664672944159437140589723997848725150538012987853468625559479370285520771957035184623948003655181733807144213151331097197684620433635655501209,"Qinv":1336220763953049451525874385731316715417274408983287245077551897537966825948439529869489565316701741855547495791046015721525313869844240508900847744658372,"CRTValues":[]}},"Onion":"ln6hn7x65lfagspl","Groups":{}}
{"Name":"Sarah","Ed25519PublicKey":"j8RkHDyZPCFwuXjVxGtw8Qgv1l45moymwACxEXjQJnI=","Trusted":false,"Blocked":false,"Contacts":{},"Ed25519PrivateKey":"/TkSp0/IJ07+jLw/UAkyFC3mgd5WIAyJ7dRSz7M9kVePxGQcPJk8IXC5eNXEa3DxCC/WXjmajKbAALEReNAmcg==","OnionPrivateKey":{"N":110864111714001498673773375154532122354477779797956408709787762036989130004412378714867422979598016165479959916442576967379813396145904497664600025217339110732358507374657769751998789538776326442350416409397455856063370247022405840568758973841985645812850695658944529625449004363327908735536586292681231570813,"E":65537,"D":29258368192095608908884817685777310347483828667553504814753332196953842753808024509091764161544277089249452778045849081096193791289493937647541419902636631139610918217176285511499379852356646785160340611490902468063399499748079623910235136509595342699975150851412254552332235937599936863279394875342688545217,"Primes":[10479919459766681018465636257798402760984111495664175755942255404420416305820714717114953497775485113275270637658830475401064210738386560406839990913034719,10578717912825421118563933365610407240275592456669407680773574905407145537212719150546363664213151033473733068380589365500463635725956703879386470742958627],"Precomputed":{"Dp":8284056755927383160071597514918996216378258022687682149573348508872845365565133068650383233169949895375519101941128107453074315841465367073200569590456743,"Dq":6372702186525895688861316344573277352428100007466136918640473889031754670020876025200580396770300788890901041238776082975392592557803541040300560979782513,"Qinv":10360189342388704857040075408428853576974943026521455545260795237873844460252519600072106320342798867873256125020528899224644053095341386063420556723083365,"CRTValues":[]}},"Onion":"p2c3insyliefym26","Groups":{}}

Zobrazit soubor

@ -0,0 +1,23 @@
package model
import (
"testing"
)
// Nothing in this file constitutes a cryptographic proof, but acts to prevent aggressions in expected protocol
// behaviour.
// When a new member joins the group, they should be unable to decrypt any past messages
func TestGroupForwardSecrecy(t *testing.T) {
t.Fatalf("Failed")
}
// When a member leaves the group, they should be unable to decrypt any future messages.
func TestGroupBackwardSecrecy(t *testing.T) {
t.Fatalf("Failed")
}
// If a server reorders stuff, we will still be able to put things in the right order.
func TestTranscriptConsistency(t *testing.T) {
t.Fatalf("Failed")
}

Zobrazit soubor

@ -0,0 +1,10 @@
package connections
import (
"testing"
)
func TestConnectionsManager(t *testing.T) {
// TODO We need to encapsulate connections behind a well defined interface for tesintg
NewConnectionsManager()
}

Zobrazit soubor

@ -1 +1 @@
{"Profile":{"Name":"alice","Ed25519PublicKey":"GQjDT/ADqudCIZq/7i4flbRaLzBnPCj2IYI8S1qIWGM=","Trusted":false,"Blocked":false,"Contacts":{},"Ed25519PrivateKey":"hbG3RyQQ+r1PpW1UnE8B5kf54zDfHOvBz4bMgdW2IsAZCMNP8AOq50Ihmr/uLh+VtFovMGc8KPYhgjxLWohYYw==","OnionPrivateKey":{"N":118949437147999046779871097106577144824161312908242780099796773587901402109754265146160871793580686203857212353483161141299741736678609148375472482302726670540249479081752805338231525747022348201429435662507924057367176907320212979720249273484193414490356905146774628953953268592916404780858351871034973364651,"E":65537,"D":94181992508763271685525597753290425592727080704359767797710520442647537373960641663479161362904853560650542536805082850652072851335729546948313816304847468284377523922190377615541996345142801491115074550379060687848886736978676340143503908396698667066436989395508107231468852742294096428057812643803322726345,"Primes":[11093306403644218894981011451014980780451441495773111118759223267959318051098530285527407597744590710343913726393148960031173198769212333898347445271695669,10722631541929052848558646606992517878067322185889508722580532907231300392570442727893142668003376876119846036715047467381256380482736518424207582333117279],"Precomputed":{"Dp":288432398672654363139106817714108476889226792633129092670792322636109037018354450257697217549731946387933976071134257410212843595262795321158796507972129,"Dq":2883332097340673182326762426644935571740855072431310743824659832219009213395012164024304640710186775224072610968297015695864644296309957187387433410997235,"Qinv":9556280274383976646018218885445548987752711277953338363729408513927401398507573496665859225463898071345877217574624168387272500232882404725411489440703861,"CRTValues":[]}},"Onion":"kuzx57bbs6q7nymu","Groups":{}}}
{"Profile":{"Name":"alice","Ed25519PublicKey":"HePVPZDgQV5CPsKsfk8k9APAbFAaHo3h5IgcOLk8JkU=","Trusted":false,"Blocked":false,"Contacts":{},"Ed25519PrivateKey":"vW4vk9yJONpqLoooQrJLT3BsiJjNseagLkQelqIQppgd49U9kOBBXkI+wqx+TyT0A8BsUBoejeHkiBw4uTwmRQ==","OnionPrivateKey":{"N":124180052987038276908488971306097118499288790640484988517147166769877815582841930618153187145182425589345556238768813257991033435204827844882770722270092969225417271833713672261037744190852387384632814869090061694598739251480895491455885307591459276013910000175922066726291096488800388268492421362423280138243,"E":65537,"D":8323892957749960334757343957576401751184455456973168661303195196912785813440111711026549142145450594534309299432555604351047650653139581039260444984245820156701213023813296520282033216231123406154310401988822683123845588539154050789818510723459725427699399566036015352152852003341066008723635142189324534393,"Primes":[10318605891039665378109893427013360413384828451813995161940801274879978602208412774582196558435137474501959569416200754358158605387475412222231908504565973,12034576598653904425712782466302887117485091173704960260057943271715893247294740735187656822073438435145528143604740414620929110675044452637939150841583991],"Precomputed":{"Dp":1751913083442915553995892155002176805769763434141543313958760635756740801482719821517251340551257681596400569585029308539347708960532812179330369805305485,"Dq":11627835564337130720737672022387833102726565807121862361526909743375403088411089504151133028477566391355801045353607466539915973361080989821621113410604413,"Qinv":5168969236254874132993321993214963606790484960035248152574500117234718152121266178902971986905125868496286012732445791508496771810043600157469938696477522,"CRTValues":[]}},"Onion":"lxjwzgx5jwl7otgt","Groups":{}}}

Zobrazit soubor

@ -84,12 +84,13 @@ func (m *GroupMessage) GetSpamguard() []byte {
// and is only ever sent when encrypted in the ciphertext parameter of
// GroupMessage
type DecryptedGroupMessage struct {
Onion *string `protobuf:"bytes,1,req,name=onion" json:"onion,omitempty"`
Timestamp *int32 `protobuf:"varint,2,req,name=timestamp" json:"timestamp,omitempty"`
Text *string `protobuf:"bytes,3,req,name=text" json:"text,omitempty"`
Signature []byte `protobuf:"bytes,4,req,name=signature" json:"signature,omitempty"`
SignedGroupId []byte `protobuf:"bytes,5,req,name=signed_group_id,json=signedGroupId" json:"signed_group_id,omitempty"`
XXX_unrecognized []byte `json:"-"`
Onion *string `protobuf:"bytes,1,req,name=onion" json:"onion,omitempty"`
Timestamp *int32 `protobuf:"varint,2,req,name=timestamp" json:"timestamp,omitempty"`
Text *string `protobuf:"bytes,3,req,name=text" json:"text,omitempty"`
Signature []byte `protobuf:"bytes,4,req,name=signature" json:"signature,omitempty"`
SignedGroupId []byte `protobuf:"bytes,5,req,name=signed_group_id,json=signedGroupId" json:"signed_group_id,omitempty"`
PreviousMessageSig []byte `protobuf:"bytes,6,req,name=previous_message_sig,json=previousMessageSig" json:"previous_message_sig,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *DecryptedGroupMessage) Reset() { *m = DecryptedGroupMessage{} }
@ -132,6 +133,13 @@ func (m *DecryptedGroupMessage) GetSignedGroupId() []byte {
return nil
}
func (m *DecryptedGroupMessage) GetPreviousMessageSig() []byte {
if m != nil {
return m.PreviousMessageSig
}
return nil
}
var E_ServerNonce = &proto.ExtensionDesc{
ExtendedType: (*control.ChannelResult)(nil),
ExtensionType: ([]byte)(nil),
@ -152,26 +160,28 @@ func init() {
func init() { proto.RegisterFile("group_message.proto", fileDescriptor2) }
var fileDescriptor2 = []byte{
// 332 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x51, 0xdb, 0x4a, 0xf3, 0x30,
0x1c, 0xa7, 0x3b, 0xc0, 0xb7, 0xff, 0xba, 0x7d, 0x18, 0xa7, 0x06, 0x11, 0x29, 0xbd, 0x90, 0x5d,
0xed, 0xc2, 0x4b, 0x87, 0x20, 0x4c, 0x14, 0x41, 0x45, 0xea, 0x03, 0x8c, 0x90, 0xfe, 0xd7, 0x16,
0xdb, 0xa4, 0x24, 0xa9, 0x87, 0x37, 0xf0, 0x45, 0x7c, 0x1b, 0x1f, 0x4a, 0x9a, 0xee, 0x90, 0x7a,
0xe1, 0x55, 0xf8, 0x9d, 0x43, 0x02, 0xfb, 0x89, 0x92, 0x55, 0xb9, 0x2c, 0x50, 0x6b, 0x96, 0xe0,
0xac, 0x54, 0xd2, 0x48, 0xf2, 0xcf, 0x1e, 0x5c, 0xe6, 0xc7, 0x93, 0x85, 0x14, 0x46, 0xc9, 0x7c,
0x91, 0x32, 0x21, 0x30, 0x6f, 0xf4, 0xf0, 0xdb, 0x83, 0xbd, 0xc5, 0x9b, 0xe1, 0xe9, 0x33, 0xaa,
0x57, 0x54, 0x4f, 0x8c, 0xbf, 0xa0, 0x21, 0x73, 0x18, 0xb5, 0xca, 0xa8, 0x17, 0x78, 0xd3, 0xe1,
0xf9, 0xe1, 0x6c, 0xd3, 0x36, 0xbb, 0xad, 0xe5, 0x87, 0x46, 0x8d, 0xfc, 0xc4, 0x41, 0x75, 0x78,
0x85, 0x86, 0xa7, 0xdb, 0x70, 0xe7, 0x77, 0xf8, 0xa6, 0x96, 0xb7, 0xe1, 0x95, 0x83, 0xc8, 0x25,
0x8c, 0x5b, 0xcb, 0x9a, 0x76, 0x83, 0xee, 0x1f, 0xd3, 0x23, 0x77, 0x5a, 0x87, 0x63, 0xf0, 0xdd,
0xf2, 0xf0, 0x1e, 0x7c, 0xd7, 0x4e, 0x4e, 0x01, 0x78, 0x56, 0xa6, 0xa8, 0x0c, 0xbe, 0x1b, 0xea,
0x05, 0x9d, 0xa9, 0x1f, 0x39, 0x0c, 0x39, 0x81, 0x81, 0x2e, 0x59, 0x91, 0x54, 0x4c, 0xc5, 0xb4,
0x63, 0xe5, 0x1d, 0x11, 0x7e, 0x79, 0x70, 0x70, 0x8d, 0x5c, 0x7d, 0x94, 0x06, 0xe3, 0x56, 0xef,
0x04, 0xfa, 0x52, 0x64, 0x52, 0xd8, 0xca, 0x41, 0xd4, 0x80, 0xba, 0xcd, 0x64, 0x05, 0x6a, 0xc3,
0x8a, 0xd2, 0xb6, 0xf5, 0xa3, 0x1d, 0x41, 0x08, 0xf4, 0xec, 0x2d, 0xba, 0x36, 0xd2, 0xdb, 0xee,
0x67, 0x89, 0x60, 0xa6, 0x52, 0x48, 0x7b, 0xeb, 0xfd, 0x0d, 0x41, 0xce, 0xe0, 0x7f, 0x0d, 0x30,
0x5e, 0x36, 0x6f, 0x94, 0xc5, 0xb4, 0x6f, 0x3d, 0xa3, 0x86, 0xb6, 0x57, 0xba, 0x8b, 0x2f, 0xe6,
0xe0, 0x6b, 0xfb, 0x9d, 0x4b, 0x21, 0x05, 0x47, 0x72, 0xb4, 0x7b, 0xbc, 0xf5, 0xef, 0x47, 0xa8,
0xab, 0xdc, 0xd0, 0xcf, 0xab, 0xc0, 0x9b, 0xfa, 0xd1, 0xb0, 0x71, 0x3f, 0xd6, 0xe6, 0x9f, 0x00,
0x00, 0x00, 0xff, 0xff, 0xbf, 0x17, 0x09, 0x79, 0x47, 0x02, 0x00, 0x00,
// 358 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x52, 0xcd, 0x4a, 0xeb, 0x40,
0x14, 0x26, 0xfd, 0xb9, 0xdc, 0x9e, 0xa6, 0xbd, 0xdc, 0xb9, 0xbd, 0x3a, 0x88, 0x48, 0xc8, 0x42,
0xba, 0x2a, 0xe2, 0xd2, 0x22, 0x08, 0x15, 0x45, 0x50, 0x91, 0xf4, 0x01, 0x42, 0x48, 0x4e, 0x93,
0xc1, 0x64, 0x66, 0x98, 0x99, 0x54, 0x7d, 0x03, 0x1f, 0xce, 0xbd, 0xaf, 0x23, 0x99, 0x34, 0x6d,
0xea, 0xc2, 0xd5, 0x70, 0xbe, 0xdf, 0xc3, 0x61, 0xe0, 0x5f, 0xaa, 0x44, 0x29, 0xc3, 0x02, 0xb5,
0x8e, 0x52, 0x9c, 0x49, 0x25, 0x8c, 0x20, 0xbf, 0xed, 0x13, 0x8b, 0xfc, 0x68, 0xb2, 0x10, 0xdc,
0x28, 0x91, 0x2f, 0xb2, 0x88, 0x73, 0xcc, 0x6b, 0xde, 0xff, 0x70, 0xe0, 0xef, 0xe2, 0xc5, 0xc4,
0xd9, 0x12, 0xd5, 0x1a, 0xd5, 0x53, 0x14, 0x3f, 0xa3, 0x21, 0x73, 0x18, 0xed, 0x85, 0x51, 0xc7,
0x73, 0xa6, 0xc3, 0xf3, 0x83, 0x59, 0x93, 0x36, 0xbb, 0xad, 0xe8, 0x87, 0x9a, 0x0d, 0xdc, 0xb4,
0x35, 0x55, 0xe6, 0x15, 0x9a, 0x38, 0xdb, 0x9a, 0x3b, 0xdf, 0xcd, 0x37, 0x15, 0xbd, 0x35, 0xaf,
0x5a, 0x13, 0xb9, 0x84, 0xf1, 0x5e, 0xb3, 0xa6, 0x5d, 0xaf, 0xfb, 0x43, 0xf5, 0xa8, 0x5d, 0xad,
0xfd, 0x31, 0xb8, 0xed, 0x70, 0xff, 0x1e, 0xdc, 0xb6, 0x9c, 0x9c, 0x00, 0xc4, 0x4c, 0x66, 0xa8,
0x0c, 0xbe, 0x1a, 0xea, 0x78, 0x9d, 0xa9, 0x1b, 0xb4, 0x10, 0x72, 0x0c, 0x03, 0x2d, 0xa3, 0x22,
0x2d, 0x23, 0x95, 0xd0, 0x8e, 0xa5, 0x77, 0x80, 0xff, 0xe9, 0xc0, 0xff, 0x6b, 0x8c, 0xd5, 0x9b,
0x34, 0x98, 0xec, 0xe5, 0x4e, 0xa0, 0x2f, 0x38, 0x13, 0xdc, 0x46, 0x0e, 0x82, 0x7a, 0xa8, 0xd2,
0x0c, 0x2b, 0x50, 0x9b, 0xa8, 0x90, 0x36, 0xad, 0x1f, 0xec, 0x00, 0x42, 0xa0, 0x67, 0xb7, 0xe8,
0x5a, 0x4b, 0x6f, 0xdb, 0xcf, 0x52, 0x1e, 0x99, 0x52, 0x21, 0xed, 0x6d, 0xfa, 0x1b, 0x80, 0x9c,
0xc2, 0x9f, 0x6a, 0xc0, 0x24, 0xac, 0x6f, 0xc4, 0x12, 0xda, 0xb7, 0x9a, 0x51, 0x0d, 0xdb, 0x95,
0xee, 0x12, 0x72, 0x06, 0x13, 0xa9, 0x70, 0xcd, 0x44, 0xa9, 0x9b, 0x3b, 0x86, 0x9a, 0xa5, 0xf4,
0x97, 0x15, 0x93, 0x86, 0xdb, 0x2c, 0xbf, 0x64, 0xe9, 0xc5, 0x1c, 0x5c, 0x6d, 0x3f, 0x40, 0xc8,
0x05, 0x8f, 0x91, 0x1c, 0xee, 0xce, 0xbd, 0xf9, 0x2f, 0x01, 0xea, 0x32, 0x37, 0xf4, 0xfd, 0xca,
0x73, 0xa6, 0x6e, 0x30, 0xac, 0xd5, 0x8f, 0x95, 0xf8, 0x2b, 0x00, 0x00, 0xff, 0xff, 0x54, 0x1a,
0x6d, 0xda, 0x79, 0x02, 0x00, 0x00,
}

Zobrazit soubor

@ -30,4 +30,5 @@ message DecryptedGroupMessage {
required string text = 3;
required bytes signature = 4;
required bytes signed_group_id = 5;
required bytes previous_message_sig =6;
}

Zobrazit soubor

@ -16,6 +16,12 @@ type Guard struct {
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 {
@ -25,9 +31,7 @@ func (sg *Guard) GenerateChallenge(channelID int32) []byte {
}
var nonce [24]byte
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
utils.CheckError(err)
}
getRandomness(&nonce)
sg.nonce = nonce
err := proto.SetExtension(cr, protocol.E_ServerNonce, sg.nonce[:])
utils.CheckError(err)
@ -50,9 +54,7 @@ func (sg *Guard) SolveChallenge(challenge []byte, message []byte) []byte {
solve := make([]byte, len(challenge)+len(message)+len(spamguard))
for !solved {
if _, err := io.ReadFull(rand.Reader, spamguard[:]); err != nil {
utils.CheckError(err)
}
getRandomness(&spamguard)
copy(solve[0:], challenge[:])
copy(solve[len(challenge):], message[:])
@ -67,8 +69,6 @@ func (sg *Guard) SolveChallenge(challenge []byte, message []byte) []byte {
}
}
}
//log.Printf("Solved answer: %v %x %x\n", len(solve), solve, sum)
return spamguard[:]
}
@ -82,7 +82,7 @@ func (sg *Guard) ValidateChallenge(message []byte, spamguard []byte) bool {
copy(solve[len(sg.nonce):], message[:])
copy(solve[len(sg.nonce)+len(message):], spamguard[:])
sum := sha256.Sum256(solve)
//log.Printf("Got answer: %v %x %x\n", len(solve), solve, sum)
for i := 0; i < sg.Difficulty; i++ {
if sum[i] != 0x00 {
return false

Zobrazit soubor

@ -31,3 +31,39 @@ func TestSpamGuard(t *testing.T) {
t.Errorf("Failed SpamGaurd")
}
func TestSpamGuardBadLength(t *testing.T) {
var spamGuard Guard
spamGuard.Difficulty = 2
spamGuard.GenerateChallenge(3)
result := spamGuard.ValidateChallenge([]byte("test"), []byte{0x00, 0x00})
if result {
t.Errorf("Validating Guard should have failed")
}
}
func TestSpamGuardFail(t *testing.T) {
var spamGuard Guard
spamGuard.Difficulty = 2
challenge := spamGuard.GenerateChallenge(3)
control := new(Protocol_Data_Control.Packet)
proto.Unmarshal(challenge[:], control)
if control.GetChannelResult() != nil {
ce, _ := proto.GetExtension(control.GetChannelResult(), protocol.E_ServerNonce)
challenge := ce.([]byte)[:]
var spamGuard2 Guard
spamGuard2.Difficulty = 1
sgsolve := spamGuard2.SolveChallenge(challenge, []byte("Hello"))
t.Logf("Solved: %v %v", challenge, sgsolve)
result := spamGuard.ValidateChallenge([]byte("Hello"), sgsolve)
if result {
t.Errorf("Validating Guard successes")
}
return
}
t.Errorf("Failed SpamGaurd")
}

Zobrazit soubor

@ -1,9 +1,9 @@
package testing
import (
"git.mascherari.press/cwtch/peer"
"testing"
"time"
"git.mascherari.press/cwtch/peer"
)
func TestCwtchPeerIntegration(t *testing.T) {