func TestStreamData(t *testing.T) { var serverPrivate, clientPrivate, serverPublic, clientPublic [32]byte randBytes(serverPrivate[:]) randBytes(clientPrivate[:]) curve25519.ScalarBaseMult(&serverPublic, &serverPrivate) curve25519.ScalarBaseMult(&clientPublic, &clientPrivate) x, y := NewBiDiPipe() client := NewClient(x, &clientPrivate, &clientPublic, &serverPublic) server := NewServer(y, &serverPrivate) clientComplete := make(chan bool) go func() { defer x.Close() err := client.Handshake() if err != nil { panic(err) } if _, err = client.Write(nil); err != nil { panic(err) } if _, err = client.Write([]byte("hello")); err != nil { panic(err) } if _, err = client.Write([]byte("world")); err != nil { panic(err) } if _, err = client.Write(make([]byte, 20*1024)); err != nil { panic(err) } close(clientComplete) }() serverComplete := make(chan bool) go func() { defer y.Close() err := server.Handshake() if err != nil { panic(err) } h := sha256.New() if _, err := io.Copy(h, server); err != nil { panic(err) } if h.Sum(nil)[0] != 0xec { panic("bad data received") } close(serverComplete) }() <-clientComplete <-serverComplete }
// FillKeyExchange sets elements of kx with key exchange information from the // ratchet. func (r *Ratchet) FillKeyExchange(kx *pond.KeyExchange) error { if r.kxPrivate0 == nil || r.kxPrivate1 == nil { return errors.New("ratchet: handshake already complete") } var public0, public1 [32]byte curve25519.ScalarBaseMult(&public0, r.kxPrivate0) curve25519.ScalarBaseMult(&public1, r.kxPrivate1) kx.Dh = public0[:] kx.Dh1 = public1[:] return nil }
func TestBox(t *testing.T) { var privateKey1, privateKey2 [32]byte for i := range privateKey1[:] { privateKey1[i] = 1 } for i := range privateKey2[:] { privateKey2[i] = 2 } var publicKey1 [32]byte curve25519.ScalarBaseMult(&publicKey1, &privateKey1) var message [64]byte for i := range message[:] { message[i] = 3 } var nonce [24]byte for i := range nonce[:] { nonce[i] = 4 } box := Seal(nil, message[:], &nonce, &publicKey1, &privateKey2) // expected was generated using the C implementation of NaCl. expected, _ := hex.DecodeString("78ea30b19d2341ebbdba54180f821eec265cf86312549bea8a37652a8bb94f07b78a73ed1708085e6ddd0e943bbdeb8755079a37eb31d86163ce241164a47629c0539f330b4914cd135b3855bc2a2dfc") if !bytes.Equal(box, expected) { t.Fatalf("box didn't match, got\n%x\n, expected\n%x", box, expected) } }
func UnmarshalKeyExchange(rand io.Reader, meetingPlace MeetingPlace, serialised []byte) (*KeyExchange, error) { var p panda_proto.KeyExchange if err := proto.Unmarshal(serialised, &p); err != nil { return nil, err } sharedSecret, ok := newSharedSecret(p.SharedSecret) if !ok { return nil, errors.New("panda: invalid shared secret in serialised key exchange") } kx := &KeyExchange{ rand: rand, meetingPlace: meetingPlace, status: p.GetStatus(), sharedSecret: sharedSecret, serialised: serialised, kxBytes: p.KeyExchangeBytes, message1: p.Message1, message2: p.Message2, } copy(kx.key[:], p.Key) copy(kx.meeting1[:], p.Meeting1) copy(kx.meeting2[:], p.Meeting2) copy(kx.sharedKey[:], p.SharedKey) copy(kx.dhPrivate[:], p.DhPrivate) curve25519.ScalarBaseMult(&kx.dhPublic, &kx.dhPrivate) return kx, nil }
func (c *client) dialServer(server string, useRandomIdentity bool) (*transport.Conn, error) { identity := &c.identity identityPublic := &c.identityPublic if useRandomIdentity { var randomIdentity [32]byte c.randBytes(randomIdentity[:]) var randomIdentityPublic [32]byte curve25519.ScalarBaseMult(&randomIdentityPublic, &randomIdentity) identity = &randomIdentity identityPublic = &randomIdentityPublic } serverIdentity, host, err := parseServer(server, c.testing) if err != nil { return nil, err } var tor proxy.Dialer if c.testing { tor = proxy.Direct } else { tor = c.torDialer() } rawConn, err := tor.Dial("tcp", host) if err != nil { return nil, err } conn := transport.NewClient(rawConn, identity, identityPublic, serverIdentity) if err := conn.Handshake(); err != nil { return nil, err } return conn, nil }
func NewTestServer(setup func(dir string)) *TestServer { listener, err := net.ListenTCP("tcp", &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1)}) if err != nil { panic(err) } dir, err := ioutil.TempDir("", "servertest") if err != nil { panic(err) } if setup != nil { setup(dir) } testServer := &TestServer{ listener: listener, addr: listener.Addr().(*net.TCPAddr), dir: dir, server: NewServer(dir, true), } io.ReadFull(rand.Reader, testServer.identity[:]) curve25519.ScalarBaseMult(&testServer.identityPublic, &testServer.identity) go testServer.Loop() return testServer }
// GenerateKey returns an appropriate private and public key pair // for securing messages. func GenerateKey() (priv *[PrivateKeySize]byte, pub *[PublicKeySize]byte, ok bool) { var priv1 [32]byte var priv2 [32]byte var pub1 [32]byte var pub2 [32]byte _, err := io.ReadFull(PRNG, priv1[:]) if err != nil { return } _, err = io.ReadFull(PRNG, priv2[:]) if err != nil { return } h := sha3.NewKeccak384() h.Write(priv1[:]) digest := h.Sum(nil) copy(priv1[:], digest) priv1[0] &= 248 priv1[31] &= 127 priv1[31] |= 64 h.Reset() h.Write(priv2[:]) digest = h.Sum(nil) copy(priv2[:], digest) priv2[0] &= 248 priv2[31] &= 127 priv2[31] |= 64 curve25519.ScalarBaseMult(&pub1, &priv1) curve25519.ScalarBaseMult(&pub2, &priv2) priv = new([PrivateKeySize]byte) copy(priv[:32], priv1[:]) copy(priv[32:], priv2[:]) pub = new([PublicKeySize]byte) copy(pub[:32], pub1[:]) copy(pub[32:], pub2[:]) secretbox.Zero(priv1[:]) secretbox.Zero(priv2[:]) ok = true return }
// Pubkey returns the associated public key for the supplied private key. func (k *Private) Pubkey() *Public { var pub [32]byte priv := [32]byte(*k) // Performs ScalarBaseMult on the supplied private key, returning the public key curve25519.ScalarBaseMult(&pub, &priv) public := Public(pub) return &public }
func (c *client) doCreateAccount() error { _, _, err := parseServer(c.server, c.dev) if err != nil { return err } if !c.dev { // Check that Tor is running. testConn, err := net.Dial("tcp", c.torAddress) if err != nil { return errors.New("Failed to connect to local Tor: " + err.Error()) } testConn.Close() } c.ui.Actions() <- SetText{name: "status", text: "Generating keys..."} c.ui.Signal() c.randBytes(c.identity[:]) curve25519.ScalarBaseMult(&c.identityPublic, &c.identity) c.ui.Actions() <- SetText{name: "status", text: "Connecting..."} c.ui.Signal() conn, err := c.dialServer(c.server, false) if err != nil { return err } defer conn.Close() c.ui.Actions() <- SetText{name: "status", text: "Requesting new account..."} c.ui.Signal() c.generation = uint32(c.randId()) request := new(pond.Request) request.NewAccount = &pond.NewAccount{ Generation: proto.Uint32(c.generation), Group: c.groupPriv.Group.Marshal(), } if err := conn.WriteProto(request); err != nil { return err } reply := new(pond.Reply) if err := conn.ReadProto(reply); err != nil { return err } if err := replyToError(reply); err != nil { return err } c.ui.Actions() <- SetText{name: "status", text: "Done"} c.ui.Signal() return nil }
// GetKeyExchangeMaterial returns key exchange information from the // ratchet. func (r *Ratchet) GetKeyExchangeMaterial() (kx *KeyExchange, err error) { if r.kxPrivate0 == nil || r.kxPrivate1 == nil { return new(KeyExchange), errors.New("ratchet: handshake already complete") } var public0, public1, myIdentity [32]byte curve25519.ScalarBaseMult(&public0, r.kxPrivate0) curve25519.ScalarBaseMult(&public1, r.kxPrivate1) curve25519.ScalarBaseMult(&myIdentity, &r.myIdentityPrivate) kx = &KeyExchange{ IdentityPublic: toSlice(myIdentity), Dh: toSlice(public0), Dh1: toSlice(public1), } return }
func TestHandshake(t *testing.T) { var serverPrivate, clientPrivate, serverPublic, clientPublic [32]byte randBytes(serverPrivate[:]) randBytes(clientPrivate[:]) curve25519.ScalarBaseMult(&serverPublic, &serverPrivate) curve25519.ScalarBaseMult(&clientPublic, &clientPrivate) clientError, serverError := runHandshake(&clientPrivate, &clientPublic, &serverPrivate, &serverPublic) if clientError != nil || serverError != nil { t.Fatalf("handshake failed: client:'%s' server:'%s'", clientError, serverError) } serverPublic[0] ^= 0x40 clientError, serverError = runHandshake(&clientPrivate, &clientPublic, &serverPrivate, &serverPublic) if clientError == nil && serverError == nil { t.Fatal("bad handshake succeeded") } }
func pairedRatchet() (a, b *Ratchet) { var privA, pubA, privB, pubB [32]byte io.ReadFull(rand.Reader, privA[:]) io.ReadFull(rand.Reader, privB[:]) curve25519.ScalarBaseMult(&pubA, &privA) curve25519.ScalarBaseMult(&pubB, &privB) // These are the "Ed25519" public keys for the two parties. Of course, // they're not actually valid Ed25519 keys but that doesn't matter // here. var aSigningPublic, bSigningPublic [32]byte io.ReadFull(rand.Reader, aSigningPublic[:]) io.ReadFull(rand.Reader, bSigningPublic[:]) a, b = New(rand.Reader), New(rand.Reader) a.Now = nowFunc b.Now = nowFunc a.MyIdentityPrivate = &privA b.MyIdentityPrivate = &privB a.TheirIdentityPublic = &pubB b.TheirIdentityPublic = &pubA a.MySigningPublic = &aSigningPublic b.MySigningPublic = &bSigningPublic a.TheirSigningPublic = &bSigningPublic b.TheirSigningPublic = &aSigningPublic kxA, kxB := new(pond.KeyExchange), new(pond.KeyExchange) if err := a.FillKeyExchange(kxA); err != nil { panic(err) } if err := b.FillKeyExchange(kxB); err != nil { panic(err) } if err := a.CompleteKeyExchange(kxB, true); err != nil { panic(err) } if err := b.CompleteKeyExchange(kxA, true); err != nil { panic(err) } return }
func (e *curve25519ECDH) GenerateKey(rand io.Reader) (crypto.PrivateKey, crypto.PublicKey, error) { var pub, priv [32]byte var err error _, err = io.ReadFull(rand, priv[:]) if err != nil { return nil, nil, err } curve25519.ScalarBaseMult(&pub, &priv) return &priv, &pub, nil }
// GenerateKey generates a new public/private key pair suitable for use with // Seal and Open. func GenerateKey(rand io.Reader) (publicKey, privateKey *[32]byte, err error) { publicKey = new([32]byte) privateKey = new([32]byte) _, err = io.ReadFull(rand, privateKey[:]) if err != nil { publicKey = nil privateKey = nil return } curve25519.ScalarBaseMult(publicKey, privateKey) return }
func (noise255) GenerateKey(random io.Reader) (Key, error) { var pubKey, privKey [32]byte if random == nil { random = rand.Reader } if _, err := io.ReadFull(random, privKey[:]); err != nil { return Key{}, err } privKey[0] &= 248 privKey[31] &= 127 privKey[31] |= 64 curve25519.ScalarBaseMult(&pubKey, &privKey) return Key{Private: privKey[:], Public: pubKey[:]}, nil }
// Encrypt acts like append() but appends an encrypted version of msg to out. func (r *Ratchet) Encrypt(out, msg []byte) []byte { if r.ratchet { r.randBytes(r.sendRatchetPrivate[:]) copy(r.sendHeaderKey[:], r.nextSendHeaderKey[:]) var sharedKey, keyMaterial [32]byte curve25519.ScalarMult(&sharedKey, &r.sendRatchetPrivate, &r.recvRatchetPublic) sha := sha256.New() sha.Write(rootKeyUpdateLabel) sha.Write(r.rootKey[:]) sha.Write(sharedKey[:]) if r.v2 { sha.Sum(keyMaterial[:0]) h := hmac.New(sha256.New, keyMaterial[:]) deriveKey(&r.rootKey, rootKeyLabel, h) deriveKey(&r.nextSendHeaderKey, sendHeaderKeyLabel, h) deriveKey(&r.sendChainKey, chainKeyLabel, h) } else { sha.Sum(r.rootKey[:0]) h := hmac.New(sha256.New, r.rootKey[:]) deriveKey(&r.nextSendHeaderKey, sendHeaderKeyLabel, h) deriveKey(&r.sendChainKey, chainKeyLabel, h) } r.prevSendCount, r.sendCount = r.sendCount, 0 r.ratchet = false } h := hmac.New(sha256.New, r.sendChainKey[:]) var messageKey [32]byte deriveKey(&messageKey, messageKeyLabel, h) deriveKey(&r.sendChainKey, chainKeyStepLabel, h) var sendRatchetPublic [32]byte curve25519.ScalarBaseMult(&sendRatchetPublic, &r.sendRatchetPrivate) var header [headerSize]byte var headerNonce, messageNonce [24]byte r.randBytes(headerNonce[:]) r.randBytes(messageNonce[:]) binary.LittleEndian.PutUint32(header[0:4], r.sendCount) binary.LittleEndian.PutUint32(header[4:8], r.prevSendCount) copy(header[8:], sendRatchetPublic[:]) copy(header[nonceInHeaderOffset:], messageNonce[:]) out = append(out, headerNonce[:]...) out = secretbox.Seal(out, header[:], &headerNonce, &r.sendHeaderKey) r.sendCount++ return secretbox.Seal(out, msg, &messageNonce, &messageKey) }
func (c *client) sendAck(msg *InboxMessage) { // First, see if we can merge this ack with a message to the same // contact that is pending transmission. c.queueMutex.Lock() for _, queuedMsg := range c.queue { if queuedMsg.sending { continue } if msg.from == queuedMsg.to && !queuedMsg.revocation { proto := queuedMsg.message proto.AlsoAck = append(proto.AlsoAck, msg.message.GetId()) if !tooLarge(queuedMsg) { c.queueMutex.Unlock() c.log.Printf("ACK merged with queued message.") // All done. return } proto.AlsoAck = proto.AlsoAck[:len(proto.AlsoAck)-1] if len(proto.AlsoAck) == 0 { proto.AlsoAck = nil } } } c.queueMutex.Unlock() to := c.contacts[msg.from] var myNextDH []byte if to.ratchet == nil { var nextDHPub [32]byte curve25519.ScalarBaseMult(&nextDHPub, &to.currentDHPrivate) myNextDH = nextDHPub[:] } id := c.randId() err := c.send(to, &pond.Message{ Id: proto.Uint64(id), Time: proto.Int64(time.Now().Unix()), Body: make([]byte, 0), BodyEncoding: pond.Message_RAW.Enum(), MyNextDh: myNextDH, InReplyTo: msg.message.Id, SupportedVersion: proto.Int32(protoVersion), }) if err != nil { c.log.Errorf("Error sending message: %s", err) } }
func TestCurve25519Conversion(t *testing.T) { public, private, _ := ed25519.GenerateKey(rand.Reader) var curve25519Public, curve25519Public2, curve25519Private [32]byte PrivateKeyToCurve25519(&curve25519Private, private) curve25519.ScalarBaseMult(&curve25519Public, &curve25519Private) if !PublicKeyToCurve25519(&curve25519Public2, public) { t.Fatalf("PublicKeyToCurve25519 failed") } if !bytes.Equal(curve25519Public[:], curve25519Public2[:]) { t.Errorf("Values didn't match: curve25519 produced %x, conversion produced %x", curve25519Public[:], curve25519Public2[:]) } }
func TestHandshake(t *testing.T) { var serverPrivate, clientPrivate, serverPublic, clientPublic [32]byte if _, err := io.ReadFull(rand.Reader, serverPrivate[:]); err != nil { t.Fatalf("reading from rand: %s", err) } if _, err := io.ReadFull(rand.Reader, clientPrivate[:]); err != nil { t.Fatalf("reading from rand: %s", err) } curve25519.ScalarBaseMult(&serverPublic, &serverPrivate) curve25519.ScalarBaseMult(&clientPublic, &clientPrivate) clientError, serverError := runHandshake(&clientPrivate, &clientPublic, &serverPrivate, &serverPublic) if clientError != nil || serverError != nil { t.Fatalf("handshake failed: client:'%s' server:'%s'", clientError, serverError) } serverPublic[0] ^= 0x40 clientError, serverError = runHandshake(&clientPrivate, &clientPublic, &serverPrivate, &serverPublic) if clientError == nil && serverError == nil { t.Fatal("bad handshake succeeded") } }
func (c *client) sendAck(msg *InboxMessage) { to := c.contacts[msg.from] var nextDHPub [32]byte curve25519.ScalarBaseMult(&nextDHPub, &to.currentDHPrivate) id := c.randId() err := c.send(to, &pond.Message{ Id: proto.Uint64(id), Time: proto.Int64(time.Now().Unix()), Body: make([]byte, 0), BodyEncoding: pond.Message_RAW.Enum(), MyNextDh: nextDHPub[:], InReplyTo: msg.message.Id, SupportedVersion: proto.Int32(protoVersion), }) if err != nil { c.log.Errorf("Error sending message: %s", err) } }
func TestElligator(t *testing.T) { var publicKey, publicKey2, publicKey3, representative, privateKey [32]byte for i := 0; i < 1000; i++ { rand.Reader.Read(privateKey[:]) if !ScalarBaseMult(&publicKey, &representative, &privateKey) { continue } RepresentativeToPublicKey(&publicKey2, &representative) if !bytes.Equal(publicKey[:], publicKey2[:]) { t.Fatal("The resulting public key doesn't match the initial one.") } curve25519.ScalarBaseMult(&publicKey3, &privateKey) if !bytes.Equal(publicKey[:], publicKey3[:]) { t.Fatal("The public key doesn't match the value that curve25519 produced.") } } }
func NewKeyExchange(rand io.Reader, meetingPlace MeetingPlace, sharedSecret *SharedSecret, kxBytes []byte) (*KeyExchange, error) { if 24 /* nonce */ +4 /* length */ +len(kxBytes)+secretbox.Overhead > meetingPlace.Padding() { return nil, errors.New("panda: key exchange too large for meeting place") } kx := &KeyExchange{ Log: func(format string, args ...interface{}) {}, rand: rand, meetingPlace: meetingPlace, status: panda_proto.KeyExchange_INIT, sharedSecret: sharedSecret, kxBytes: kxBytes, } if _, err := io.ReadFull(kx.rand, kx.dhPrivate[:]); err != nil { return nil, err } curve25519.ScalarBaseMult(&kx.dhPublic, &kx.dhPrivate) kx.updateSerialised() return kx, nil }
func (c *client) dialServer(server string, useRandomIdentity bool) (*transport.Conn, error) { identity := &c.identity identityPublic := &c.identityPublic if useRandomIdentity { var randomIdentity [32]byte c.randBytes(randomIdentity[:]) var randomIdentityPublic [32]byte curve25519.ScalarBaseMult(&randomIdentityPublic, &randomIdentity) identity = &randomIdentity identityPublic = &randomIdentityPublic } serverIdentity, host, err := parseServer(server, c.dev) if err != nil { return nil, err } var tor proxy.Dialer if c.dev { tor = proxy.Direct } else { tor = c.torDialer() } rawConn, err := tor.Dial("tcp", host) if err != nil { return nil, err } // Sometimes Tor holds the connection open but we never receive // anything so we add a 60 second deadline. rawConn.SetDeadline(time.Now().Add(60 * time.Second)) conn := transport.NewClient(rawConn, identity, identityPublic, serverIdentity) if err := conn.Handshake(); err != nil { return nil, err } return conn, nil }
func (c *client) newKeyExchange(contact *Contact) { var err error c.randBytes(contact.lastDHPrivate[:]) c.randBytes(contact.currentDHPrivate[:]) var pub [32]byte curve25519.ScalarBaseMult(&pub, &contact.lastDHPrivate) if contact.groupKey, err = c.groupPriv.NewMember(c.rand); err != nil { panic(err) } kx := &pond.KeyExchange{ PublicKey: c.pub[:], IdentityPublic: c.identityPublic[:], Server: proto.String(c.server), Dh: pub[:], Group: contact.groupKey.Group.Marshal(), GroupKey: contact.groupKey.Marshal(), Generation: proto.Uint32(c.generation), } kxBytes, err := proto.Marshal(kx) if err != nil { panic(err) } sig := ed25519.Sign(&c.priv, kxBytes) kxs := &pond.SignedKeyExchange{ Signed: kxBytes, Signature: sig[:], } if contact.kxsBytes, err = proto.Marshal(kxs); err != nil { panic(err) } }
func (c *Conn) Handshake() error { var ephemeralPrivate, ephemeralPublic, ephemeralShared [32]byte if _, err := io.ReadFull(rand.Reader, ephemeralPrivate[:]); err != nil { return err } curve25519.ScalarBaseMult(&ephemeralPublic, &ephemeralPrivate) if _, err := c.write(ephemeralPublic[:]); err != nil { return err } var theirEphemeralPublic [32]byte if n, err := c.read(theirEphemeralPublic[:]); err != nil || n != len(theirEphemeralPublic) { if err == nil { err = shortMessageError } return err } handshakeHash := sha256.New() if c.isServer { handshakeHash.Write(theirEphemeralPublic[:]) handshakeHash.Write(ephemeralPublic[:]) } else { handshakeHash.Write(ephemeralPublic[:]) handshakeHash.Write(theirEphemeralPublic[:]) } curve25519.ScalarMult(&ephemeralShared, &ephemeralPrivate, &theirEphemeralPublic) c.setupKeys(&ephemeralShared) if c.isServer { return c.handshakeServer(handshakeHash, &theirEphemeralPublic) } return c.handshakeClient(handshakeHash, &ephemeralPrivate) }
func (c *client) unmarshal(state *disk.State) error { c.server = *state.Server if len(state.Identity) != len(c.identity) { return errors.New("client: identity is wrong length in State") } copy(c.identity[:], state.Identity) curve25519.ScalarBaseMult(&c.identityPublic, &c.identity) group, ok := new(bbssig.Group).Unmarshal(state.Group) if !ok { return errors.New("client: failed to unmarshal group") } c.groupPriv, ok = new(bbssig.PrivateKey).Unmarshal(group, state.GroupPrivate) if !ok { return errors.New("client: failed to unmarshal group private key") } if len(state.Private) != len(c.priv) { return errors.New("client: failed to unmarshal private key") } copy(c.priv[:], state.Private) if len(state.Public) != len(c.pub) { return errors.New("client: failed to unmarshal public key") } copy(c.pub[:], state.Public) c.generation = *state.Generation if state.LastErasureStorageTime != nil { c.lastErasureStorageTime = time.Unix(*state.LastErasureStorageTime, 0) } for _, prevGroupPriv := range state.PreviousGroupPrivateKeys { group, ok := new(bbssig.Group).Unmarshal(prevGroupPriv.Group) if !ok { return errors.New("client: failed to unmarshal previous group") } priv, ok := new(bbssig.PrivateKey).Unmarshal(group, prevGroupPriv.GroupPrivate) if !ok { return errors.New("client: failed to unmarshal previous group private key") } c.prevGroupPrivs = append(c.prevGroupPrivs, previousGroupPrivateKey{ priv: priv, expired: time.Unix(*prevGroupPriv.Expired, 0), }) } for _, cont := range state.Contacts { contact := &Contact{ id: *cont.Id, name: *cont.Name, kxsBytes: cont.KeyExchangeBytes, pandaKeyExchange: cont.PandaKeyExchange, pandaResult: cont.GetPandaError(), revokedUs: cont.GetRevokedUs(), } c.registerId(contact.id) c.contacts[contact.id] = contact if contact.groupKey, ok = new(bbssig.MemberKey).Unmarshal(c.groupPriv.Group, cont.GroupKey); !ok { return errors.New("client: failed to unmarshal group member key") } copy(contact.lastDHPrivate[:], cont.LastPrivate) copy(contact.currentDHPrivate[:], cont.CurrentPrivate) if cont.Ratchet != nil { contact.ratchet = c.newRatchet(contact) if err := contact.ratchet.Unmarshal(cont.Ratchet); err != nil { return err } } if cont.IsPending != nil && *cont.IsPending { contact.isPending = true continue } theirGroup, ok := new(bbssig.Group).Unmarshal(cont.TheirGroup) if !ok { return errors.New("client: failed to unmarshal their group") } if contact.myGroupKey, ok = new(bbssig.MemberKey).Unmarshal(theirGroup, cont.MyGroupKey); !ok { return errors.New("client: failed to unmarshal my group key") } if cont.TheirServer == nil { return errors.New("client: contact missing server") } contact.theirServer = *cont.TheirServer if len(cont.TheirPub) != len(contact.theirPub) { return errors.New("client: contact missing public key") } copy(contact.theirPub[:], cont.TheirPub) if len(cont.TheirIdentityPublic) != len(contact.theirIdentityPublic) { return errors.New("client: contact missing identity public key") } copy(contact.theirIdentityPublic[:], cont.TheirIdentityPublic) copy(contact.theirLastDHPublic[:], cont.TheirLastPublic) copy(contact.theirCurrentDHPublic[:], cont.TheirCurrentPublic) for _, prevTag := range cont.PreviousTags { contact.previousTags = append(contact.previousTags, previousTag{ tag: prevTag.Tag, expired: time.Unix(*prevTag.Expired, 0), }) } // For now we'll have to do this conditionally until everyone // has updated local state. if cont.Generation != nil { contact.generation = *cont.Generation } if cont.SupportedVersion != nil { contact.supportedVersion = *cont.SupportedVersion } } now := c.Now() for _, m := range state.Inbox { msg := &InboxMessage{ id: *m.Id, from: *m.From, receivedTime: time.Unix(*m.ReceivedTime, 0), acked: *m.Acked, read: *m.Read, sealed: m.Sealed, retained: m.GetRetained(), exposureTime: now, } c.registerId(msg.id) if len(m.Message) > 0 { msg.message = new(pond.Message) if err := proto.Unmarshal(m.Message, msg.message); err != nil { return errors.New("client: corrupt message in inbox: " + err.Error()) } } c.inbox = append(c.inbox, msg) } for _, m := range state.Outbox { msg := &queuedMessage{ id: *m.Id, to: *m.To, server: *m.Server, created: time.Unix(*m.Created, 0), } c.registerId(msg.id) if len(m.Message) > 0 { msg.message = new(pond.Message) if err := proto.Unmarshal(m.Message, msg.message); err != nil { return errors.New("client: corrupt message in outbox: " + err.Error()) } } if m.Sent != nil { msg.sent = time.Unix(*m.Sent, 0) } if m.Acked != nil { msg.acked = time.Unix(*m.Acked, 0) } if len(m.Request) != 0 { msg.request = new(pond.Request) if err := proto.Unmarshal(m.Request, msg.request); err != nil { return errors.New("client: corrupt request in outbox: " + err.Error()) } } msg.revocation = m.GetRevocation() if msg.revocation && len(msg.server) == 0 { // There was a bug in some versions where revoking a // pending contact would result in a revocation message // with an empty server. msg.server = c.server } c.outbox = append(c.outbox, msg) if msg.sent.IsZero() && (msg.to == 0 || !c.contacts[msg.to].revokedUs) { // This message hasn't been sent yet. c.enqueue(msg) } } for _, m := range state.Drafts { draft := &Draft{ id: *m.Id, body: *m.Body, attachments: m.Attachments, detachments: m.Detachments, created: time.Unix(*m.Created, 0), } c.registerId(draft.id) if m.To != nil { draft.to = *m.To } if m.InReplyTo != nil { draft.inReplyTo = *m.InReplyTo } c.drafts[draft.id] = draft } return nil }
func main() { flag.Parse() if len(*makeAnnounce) > 0 { msgBytes, err := ioutil.ReadFile(*makeAnnounce) if err != nil { panic(err) } announce := &pond.Message{ Id: proto.Uint64(0), Time: proto.Int64(time.Now().Unix()), Body: msgBytes, MyNextDh: []byte{}, BodyEncoding: pond.Message_RAW.Enum(), } announceBytes, err := proto.Marshal(announce) if err != nil { panic(err) } os.Stdout.Write(announceBytes) return } if len(*baseDirectory) == 0 { log.Fatalf("Must give --base-directory") return } configPath := filepath.Join(*baseDirectory, configFilename) var identity [32]byte if *initFlag { if err := os.MkdirAll(*baseDirectory, 0700); err != nil { log.Fatalf("Failed to create base directory: %s", err) return } if _, err := io.ReadFull(rand.Reader, identity[:]); err != nil { log.Fatalf("Failed to read random bytes: %s", err) return } if err := ioutil.WriteFile(filepath.Join(*baseDirectory, identityFilename), identity[:], 0600); err != nil { log.Fatalf("Failed to write identity file: %s", err) return } defaultConfig := &protos.Config{ Port: proto.Uint32(uint32(*port)), } configFile, err := os.OpenFile(configPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { log.Fatalf("Failed to create config file: %s", err) } proto.MarshalText(configFile, defaultConfig) configFile.Close() } identityBytes, err := ioutil.ReadFile(filepath.Join(*baseDirectory, identityFilename)) if err != nil { log.Print("Use --init to setup a new base directory") log.Fatalf("Failed to read identity file: %s", err) return } if len(identityBytes) != 32 { log.Fatalf("Identity file is not 32 bytes long") return } copy(identity[:], identityBytes) config := new(protos.Config) configBytes, err := ioutil.ReadFile(configPath) if err != nil { log.Fatalf("No config file found") } if err := proto.UnmarshalText(string(configBytes), config); err != nil { log.Fatalf("Failed to parse config: %s", err) } ip := net.IPv4(127, 0, 0, 1) // IPv4 loopback interface if config.Address != nil { if ip = net.ParseIP(*config.Address); ip == nil { log.Fatalf("Failed to parse address from config: %s", ip) } } listenAddr := net.TCPAddr{ IP: ip, Port: int(*config.Port), } listener, err := net.ListenTCP("tcp", &listenAddr) if err != nil { log.Fatalf("Failed to listen on port: %s", err) } var identityPublic [32]byte curve25519.ScalarBaseMult(&identityPublic, &identity) identityString := strings.Replace(base32.StdEncoding.EncodeToString(identityPublic[:]), "=", "", -1) log.Printf("Started. Listening on port %d with identity %s", listener.Addr().(*net.TCPAddr).Port, identityString) server := NewServer(*baseDirectory, config.GetAllowRegistration()) if *lifelineFd > -1 { lifeline := os.NewFile(uintptr(*lifelineFd), "lifeline") go func() { var buf [1]byte lifeline.Read(buf[:]) os.Exit(255) }() } for { conn, err := listener.Accept() if err != nil { log.Printf("Error accepting connection: %s", err) continue } go handleConnection(server, conn, &identity) } }
func (c *client) loadUI() error { c.ui.initUI() c.torAddress = "127.0.0.1:9050" // default for dev mode. if !c.dev && !c.detectTor() { if err := c.ui.torPromptUI(); err != nil { return err } } c.ui.loadingUI() stateFile := &disk.StateFile{ Path: c.stateFilename, Rand: c.rand, Log: func(format string, args ...interface{}) { c.log.Printf(format, args...) }, } var newAccount bool var err error if c.stateLock, err = stateFile.Lock(false /* don't create */); err == nil && c.stateLock == nil { c.ui.errorUI("State file locked by another process. Waiting for lock.", false) c.log.Errorf("Waiting for locked state file") for { if c.stateLock, err = stateFile.Lock(false /* don't create */); c.stateLock != nil { break } if err := c.ui.sleepUI(1 * time.Second); err != nil { return err } } } else if err == nil { } else if os.IsNotExist(err) { newAccount = true } else { c.ui.errorUI(err.Error(), true) if err := c.ui.ShutdownAndSuspend(); err != nil { return err } } if newAccount { pub, priv, err := ed25519.GenerateKey(rand.Reader) if err != nil { panic(err) } copy(c.priv[:], priv[:]) copy(c.pub[:], pub[:]) if c.disableV2Ratchet { c.randBytes(c.identity[:]) } else { extra25519.PrivateKeyToCurve25519(&c.identity, priv) } curve25519.ScalarBaseMult(&c.identityPublic, &c.identity) c.groupPriv, err = bbssig.GenerateGroup(rand.Reader) if err != nil { panic(err) } pw, err := c.ui.createPassphraseUI() if err != nil { return err } c.ui.createErasureStorage(pw, stateFile) if err := c.ui.createAccountUI(); err != nil { return err } newAccount = true } else { // First try with zero key. err := c.loadState(stateFile, "") for err == disk.BadPasswordError { // That didn't work, try prompting for a key. err = c.ui.keyPromptUI(stateFile) } if err == errInterrupted { return err } if err != nil { // Fatal error loading state. Abort. c.ui.errorUI(err.Error(), true) if err := c.ui.ShutdownAndSuspend(); err != nil { return err } } } if newAccount { c.stateLock, err = stateFile.Lock(true /* create */) if err != nil { err = errors.New("Failed to create state file: " + err.Error()) } else if c.stateLock == nil { err = errors.New("Failed to obtain lock on created state file") } if err != nil { c.ui.errorUI(err.Error(), true) if err := c.ui.ShutdownAndSuspend(); err != nil { return err } } c.lastErasureStorageTime = time.Now() } c.writerChan = make(chan disk.NewState) c.writerDone = make(chan struct{}) c.fetchNowChan = make(chan chan bool, 1) // Start disk and network workers. go stateFile.StartWriter(c.writerChan, c.writerDone) go c.transact() if newAccount { c.save() } // Start any pending key exchanges. for _, contact := range c.contacts { if len(contact.pandaKeyExchange) == 0 { continue } c.pandaWaitGroup.Add(1) contact.pandaShutdownChan = make(chan struct{}) go c.runPANDA(contact.pandaKeyExchange, contact.id, contact.name, contact.pandaShutdownChan) } c.ui.mainUI() return nil }
func (c *cliClient) processCommand(cmd interface{}) (shouldQuit bool) { // First commands that might start a subprocess that needs terminal // control. switch cmd.(type) { case composeCommand: if contact, ok := c.currentObj.(*Contact); ok { c.compose(contact, nil, nil) } else { c.Printf("%s Select contact first\n", termWarnPrefix) } case contactsCommand: c.showContacts() case editCommand: if draft, ok := c.currentObj.(*Draft); ok { c.compose(nil, draft, nil) } else { c.Printf("%s Select draft first\n", termWarnPrefix) } case replyCommand: msg, ok := c.currentObj.(*InboxMessage) if !ok { c.Printf("%s Select inbox message first\n", termWarnPrefix) return } c.compose(c.contacts[msg.from], nil, msg) default: goto Handle } return Handle: // The command won't need to start subprocesses with terminal control // so we can start watching for Ctrl-C again. c.termWrapper.Restart() switch cmd := cmd.(type) { case clearCommand: c.Printf("\x1b[2J") case helpCommand: if cmd.ShowAll { c.input.showHelp(0, true) return } switch c.currentObj.(type) { case *Contact: c.input.showHelp(contextContact, false) case *Draft: c.input.showHelp(contextDraft, false) case *InboxMessage: c.input.showHelp(contextInbox, false) case *queuedMessage: c.input.showHelp(contextOutbox, false) default: c.input.showHelp(0, false) } case tagCommand: if len(cmd.tag) == 0 { c.showState() return } cliId, ok := cliIdFromString(cmd.tag) if !ok { c.Printf("%s Bad tag\n", termWarnPrefix) return } for _, msg := range c.inbox { if msg.cliId == cliId { c.setCurrentObject(msg) return } } for _, msg := range c.outbox { if msg.cliId == cliId { c.setCurrentObject(msg) return } } for _, msg := range c.drafts { if msg.cliId == cliId { c.setCurrentObject(msg) return } } for _, contact := range c.contacts { if contact.cliId == cliId { c.setCurrentObject(contact) return } } c.Printf("%s Unknown tag\n", termWarnPrefix) case logCommand: n := 15 if l := len(c.log.entries); l < n { n = l } for _, entry := range c.log.entries[len(c.log.entries)-n:] { c.Printf("%s (%s) %s\n", termHeaderPrefix, entry.Format(logTimeFormat), terminalEscape(entry.s, false)) } case transactNowCommand: c.Printf("%s Triggering immediate network transaction.\n", termPrefix) select { case c.fetchNowChan <- nil: default: } case closeCommand: c.setCurrentObject(nil) case quitCommand: c.ShutdownAndSuspend() c.Printf("Goodbye!\n") shouldQuit = true return case deleteCommand: if c.currentObj == nil { c.Printf("%s Select object first\n", termWarnPrefix) return } switch o := c.currentObj.(type) { case *Draft: delete(c.drafts, o.id) c.save() c.setCurrentObject(nil) case *Contact: c.maybeDeleteContact(o) // maybeDeleteContact may need confirmation so // setCurrentObject is handled in there. default: c.Printf("%s Cannot delete current object\n", termWarnPrefix) } case sendCommand: draft, ok := c.currentObj.(*Draft) if !ok { c.Printf("%s Select draft first\n", termWarnPrefix) return } to := c.contacts[draft.to] var myNextDH []byte if to.ratchet == nil { var nextDHPub [32]byte curve25519.ScalarBaseMult(&nextDHPub, &to.currentDHPrivate) myNextDH = nextDHPub[:] } if len(draft.body) == 0 { // Zero length bodies are ACKs. draft.body = " " } id := c.randId() var inReplyTo *uint64 if r := draft.inReplyTo; r != 0 { inReplyTo = proto.Uint64(r) } err := c.send(to, &pond.Message{ Id: proto.Uint64(id), Time: proto.Int64(c.Now().Unix()), Body: []byte(draft.body), BodyEncoding: pond.Message_RAW.Enum(), InReplyTo: inReplyTo, MyNextDh: myNextDH, Files: draft.attachments, DetachedFiles: draft.detachments, SupportedVersion: proto.Int32(protoVersion), }) if err != nil { c.log.Errorf("%s Error sending: %s\n", termErrPrefix, err) return } if draft.inReplyTo != 0 { for _, msg := range c.inbox { if msg.message != nil && msg.message.GetId() == draft.inReplyTo { msg.acked = true break } } } delete(c.drafts, draft.id) c.setCurrentObject(nil) for _, msg := range c.outbox { if msg.id == id { if msg.cliId == invalidCliId { msg.cliId = c.newCliId() } c.Printf("%s Created new outbox entry %s%s%s\n", termInfoPrefix, termCliIdStart, msg.cliId.String(), termReset) c.setCurrentObject(msg) c.showQueueState() break } } c.save() case abortCommand: msg, ok := c.currentObj.(*queuedMessage) if !ok { c.Printf("%s Select outbox message first\n", termErrPrefix) return } c.queueMutex.Lock() index := c.indexOfQueuedMessage(msg) if index == -1 || msg.sending { c.queueMutex.Unlock() c.Printf("%s Too Late to Abort!\n", termErrPrefix) return } c.removeQueuedMessage(index) c.queueMutex.Unlock() c.deleteOutboxMsg(msg.id) draft := c.outboxToDraft(msg) c.drafts[draft.id] = draft if draft.cliId == invalidCliId { draft.cliId = c.newCliId() } c.Printf("%s Aborted sending %s%s%s and moved to Drafts as %s%s%s\n", termInfoPrefix, termCliIdStart, msg.cliId.String(), termReset, termCliIdStart, draft.cliId.String(), termReset) c.save() c.setCurrentObject(draft) case ackCommand: msg, ok := c.currentObj.(*InboxMessage) if !ok { c.Printf("%s Select inbox message first\n", termWarnPrefix) return } if msg.acked { c.Printf("%s Message has already been acknowledged\n", termWarnPrefix) return } msg.acked = true c.sendAck(msg) c.showQueueState() case showCommand: if c.currentObj == nil { c.Printf("Select object first\n") return } switch o := c.currentObj.(type) { case *queuedMessage: c.showOutbox(o) case *InboxMessage: c.showInbox(o) case *Draft: c.showDraft(o) case *Contact: c.showContact(o) default: c.Printf("%s Cannot show the current object\n", termWarnPrefix) } case showOutboxSummaryCommand: c.showOutboxSummary() case showInboxSummaryCommand: c.showInboxSummary() case showDraftsSummaryCommand: c.showDraftsSummary() case showQueueStateCommand: c.showQueueState() case statusCommand: c.showState() case attachCommand: draft, ok := c.currentObj.(*Draft) if !ok { c.Printf("%s Select draft first\n", termWarnPrefix) } contents, size, err := openAttachment(cmd.Filename) if err != nil { c.Printf("%s Failed to open file: %s\n", termErrPrefix, terminalEscape(err.Error(), false)) return } if size > 0 { c.Printf("%s File is too large (%d bytes) to attach. Use the 'upload' or 'save-encrypted' commands to encrypt the file, include just the keys in the message and either upload or save the ciphertext\n", termErrPrefix, size) return } base := filepath.Base(cmd.Filename) a := &pond.Message_Attachment{ Filename: proto.String(base), Contents: contents, } draft.attachments = append(draft.attachments, a) c.Printf("%s Attached '%s' (%d bytes)\n", termPrefix, terminalEscape(base, false), len(contents)) c.printDraftSize(draft) case uploadCommand: draft, ok := c.currentObj.(*Draft) if !ok { c.Printf("%s Select draft first\n", termWarnPrefix) return } base := filepath.Base(cmd.Filename) id := c.randId() c.Printf("%s Padding, encrypting and uploading '%s' to home server (Ctrl-C to abort):\n", termPrefix, terminalEscape(base, false)) cancelThunk := c.startUpload(id, cmd.Filename) if detachment, ok := c.runBackgroundProcess(id, cancelThunk); ok { draft.detachments = append(draft.detachments, detachment) } case downloadCommand: msg, ok := c.currentObj.(*InboxMessage) if !ok { c.Printf("%s Select inbox message\n", termWarnPrefix) return } i, ok := c.prepareSubobjectCommand(cmd.Number, len(msg.message.DetachedFiles), "detachment") if !ok { return } id := c.randId() c.Printf("%s Downloading and decrypting detachment (Ctrl-C to abort):\n", termPrefix) cancelThunk := c.startDownload(id, cmd.Filename, msg.message.DetachedFiles[i]) c.runBackgroundProcess(id, cancelThunk) case saveCommand: msg, ok := c.currentObj.(*InboxMessage) if !ok { c.Printf("%s Select inbox message\n", termWarnPrefix) return } i, ok := c.prepareSubobjectCommand(cmd.Number, len(msg.message.Files), "attachment") if !ok { return } if err := ioutil.WriteFile(cmd.Filename, msg.message.Files[i].GetContents(), 0600); err != nil { c.Printf("%s Failed to write file: %s\n", termErrPrefix, terminalEscape(err.Error(), false)) } else { c.Printf("%s Wrote file\n", termPrefix) } case removeCommand: draft, ok := c.currentObj.(*Draft) if !ok { c.Printf("%s Select draft first\n", termWarnPrefix) } i, ok := c.prepareSubobjectCommand(cmd.Number, len(draft.attachments)+len(draft.detachments), "attachment") if !ok { return } if i < len(draft.attachments) { draft.attachments = append(draft.attachments[:i], draft.attachments[i+1:]...) return } i -= len(draft.attachments) draft.detachments = append(draft.detachments[:i], draft.detachments[i+1:]...) case newContactCommand: for _, contact := range c.contacts { if contact.name == cmd.Name { c.Printf("%s A contact with that name already exists.\n", termErrPrefix) return } } c.Printf("Enter shared secret with contact, or hit enter to generate, print and use a random one\n") sharedSecret, err := c.term.ReadPassword("secret: ") if err != nil { panic(err) } if len(sharedSecret) == 0 { var secret [16]byte c.randBytes(secret[:]) sharedSecret = fmt.Sprintf("%x", secret[:]) c.Printf("%s Shared secret: %s\n", termPrefix, sharedSecret) } contact := &Contact{ name: cmd.Name, isPending: true, id: c.randId(), cliId: c.newCliId(), } c.newKeyExchange(contact) stack := &panda.CardStack{ NumDecks: 1, } secret := panda.SharedSecret{ Secret: sharedSecret, Cards: *stack, } mp := c.newMeetingPlace() c.contacts[contact.id] = contact kx, err := panda.NewKeyExchange(c.rand, mp, &secret, contact.kxsBytes) if err != nil { panic(err) } kx.Testing = c.testing contact.pandaKeyExchange = kx.Marshal() contact.kxsBytes = nil c.save() c.pandaWaitGroup.Add(1) contact.pandaShutdownChan = make(chan struct{}) go c.runPANDA(contact.pandaKeyExchange, contact.id, contact.name, contact.pandaShutdownChan) c.Printf("%s Key exchange running in background.\n", termPrefix) case renameCommand: if contact, ok := c.currentObj.(*Contact); ok { c.renameContact(contact, cmd.NewName) } else { c.Printf("%s Select contact first\n", termWarnPrefix) } default: panic(fmt.Sprintf("Unhandled command: %#v", cmd)) } return }
func runScript(t *testing.T, s script) { server := NewTestServer(s.setupDir) defer server.Close() identities := make([][32]byte, s.numPlayers) publicIdentities := make([][32]byte, s.numPlayers) for i := range identities { io.ReadFull(rand.Reader, identities[i][:]) curve25519.ScalarBaseMult(&publicIdentities[i], &identities[i]) } groupPrivateKeys := make([]*bbssig.PrivateKey, s.numPlayersWithAccounts) for i := range groupPrivateKeys { var err error groupPrivateKeys[i], err = bbssig.GenerateGroup(rand.Reader) if err != nil { panic(err) } conn := server.Dial(&identities[i], &publicIdentities[i]) if err := conn.WriteProto(&pond.Request{ NewAccount: &pond.NewAccount{ Generation: proto.Uint32(0), Group: groupPrivateKeys[i].Group.Marshal(), }, }); err != nil { t.Fatal(err) } reply := new(pond.Reply) if err := conn.ReadProto(reply); err != nil { t.Fatalf("Error while reading reply from server: %s", err) } if reply.AccountCreated == nil { t.Fatalf("Failed to create 1st account: %s", err) } conn.Close() } state := &scriptState{ identities: identities, publicIdentities: publicIdentities, groupPrivateKeys: groupPrivateKeys, testServer: server, } for _, a := range s.actions { conn := server.Dial(&identities[a.player], &publicIdentities[a.player]) req := a.request if a.buildRequest != nil { req = a.buildRequest(state) } if err := conn.WriteProto(req); err != nil { t.Fatal(err) } reply := new(pond.Reply) if err := conn.ReadProto(reply); err != nil { t.Fatal(err) } if a.validate != nil { a.validate(t, reply) } if len(a.payload) > 0 { _, err := conn.Write(a.payload) if err != nil { t.Fatalf("Failed to write payload: %s", err) } } if a.payloadSize > 0 { fromServer := make([]byte, a.payloadSize) if _, err := io.ReadFull(conn, fromServer); err != nil { t.Errorf("Failed to read payload: %s", err) } if a.validatePayload != nil { a.validatePayload(t, fromServer) } } conn.Close() } }