xochimilco

package
v0.0.0-...-941dc1b Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 7, 2024 License: GPL-3.0 Imports: 13 Imported by: 0

Documentation

Overview

Package xochimilco provides an usable API for end-to-end encrypted communication based on the "Signal Protocol".

The "Signal Protocol" refers to the Extended Triple Diffie-Hellman (X3DH) key agreement protocol paired with the Double Ratchet algorithm. Both are implemented and exposed in this repository's subdirectories. For implementation details please refer there.

Example
// In this example, Alice and Bob can exchange messages over some chat
// protocol. Furthermore, they already know each other's public key.
alicePub, alicePriv, _ := ed25519.GenerateKey(nil)
bobPub, bobPriv, _ := ed25519.GenerateKey(nil)

alice := Session{
	IdentityKey: alicePriv,
	VerifyPeer: func(peer ed25519.PublicKey) (valid bool) {
		return peer.Equal(bobPub)
	},
}
bob := Session{
	IdentityKey: bobPriv,
	VerifyPeer: func(peer ed25519.PublicKey) (valid bool) {
		return peer.Equal(alicePub)
	},
}

// Alice starts by offering Bob to upgrade the connection.
offerMsg, err := alice.Offer()
if err != nil {
	panic(err)
}
fmt.Printf("A->B\tOFFER\t%s\n", offerMsg)

// Bob acknowledges Alice's offer.
ackMsg, err := bob.Acknowledge(offerMsg)
if err != nil {
	panic(err)
}
fmt.Printf("B-A\tACK\t%s\n", ackMsg)

// Alice evaluates Bob's acknowledgement. This SHOULD be `isEstablished`.
isEstablished, _, _, err := alice.Receive(ackMsg)
if err != nil {
	panic(err)
} else if !isEstablished {
	panic("invalid message")
}

// Now we have an established connection.
// Let's exchange some very important messages.
dataMsgAlice1, err := alice.Send([]byte("hello bob"))
if err != nil {
	panic(err)
}
dataMsgAlice2, err := alice.Send([]byte("how are you?"))
if err != nil {
	panic(err)
}

// Ops, the messages were reorder on the wired.
fmt.Printf("A->B\tDATA\t%s", dataMsgAlice2)
fmt.Printf("A->B\tDATA\t%s", dataMsgAlice1)

_, _, plaintextAlice2, err := bob.Receive(dataMsgAlice2)
if err != nil {
	panic(err)
}
fmt.Printf("B\tRECV\t%s", plaintextAlice2)

_, _, plaintextAlice1, err := bob.Receive(dataMsgAlice2)
if err != nil {
	panic(err)
}
fmt.Printf("B\tRECV\t%s", plaintextAlice1)

// Bob also sends an answer.
dataMsgBob, err := bob.Send([]byte("hej alice!"))
if err != nil {
	panic(err)
}
fmt.Printf("B->A\tDATA\t%s", dataMsgBob)

_, _, plaintextBob, err := alice.Receive(dataMsgBob)
if err != nil {
	panic(err)
}
fmt.Printf("A\tRECV\t%s", plaintextBob)

// Finally, Alice closes her Session...
closeMsg, err := alice.Close()
if err != nil {
	panic(err)
}
fmt.Printf("A->B\tCLOSE\t%s", closeMsg)

// ...and tells Bob to do the same.
_, isClosed, _, err := bob.Receive(closeMsg)
if err != nil {
	panic(err)
} else if !isClosed {
	panic("invalid message")
}

_, err = bob.Close()
if err != nil {
	panic(err)
}
Output:

Index

Examples

Constants

View Source
const (

	// Prefix indicates the beginning of an encoded message.
	Prefix string = "!RAT!"

	// Suffix indicates the end of an encoded message.
	Suffix string = "!CHT!"
)

Variables

This section is empty.

Functions

func Seal

func Seal(m encoding.BinaryMarshaler, key *[32]byte) (out sealedMessage, err error)

Types

type Msg

type Msg interface{ interface{ ID() []byte } }

func Parse

func Parse(in string) (Msg, error)

type Session

type Session struct {
	// LocalUUID is a unique identifier for the session. Provided in the offer.
	LocalUUID  []byte
	RemoteUUID []byte

	Me string

	// IdentityKey is this node's private Ed25519 identity key.
	//
	// This will only be used within the X3DH key agreement protocol. The other
	// party might want to verify this key's public part.
	IdentityKey ed25519.PrivateKey

	// VerifyPeer is a callback during session initialization to verify the
	// other party's public key.
	//
	// To determine when a key is correct is out of Xochimilco's scope. The key
	// might be either exchanged over another secure channel or a trust on first
	// use (TOFU) principle might be used.
	VerifyPeer func(peer ed25519.PublicKey) (valid bool)
	// contains filtered or unexported fields
}

Session between two parties to exchange encrypted messages.

Each party creates a new Session variable configured with their private long time identity key and a function callback to verify the other party's public identity key.

The active party must start by offering to "upgrade" the current channel (Offer). Afterwards, the other party must confirm this step (Acknowledge). Once the first party finally receives the acknowledgement (Receive), the connection is established.

Now both parties can create encrypted messages directed to the other (Send). Furthermore, the Session can be closed again (Close). Incoming messages can be inspected and the payload extracted, if present (Receive).

func (*Session) Acknowledge

func (sess *Session) Acknowledge(offerMsg string) (ackMsg string, err error)

Acknowledge to establish an encrypted Session.

This method MUST be called by the passive party (Bob) with the active party's (Alice's) offer message. The created acknowledge message MUST be send back.

At this point, this passive part is able to send and receive messages.

func (*Session) Active

func (sess *Session) Active() bool

Active returns true if the session has been activated.

func (*Session) Close

func (sess *Session) Close() (closeMsg string, err error)

Close this Session and tell the other party to do the same.

This resets the internal state. Thus, the same Session might be reused.

func (*Session) MarshalBinary

func (sess *Session) MarshalBinary() ([]byte, error)

func (*Session) Offer

func (sess *Session) Offer() (offerMsg string, err error)

Offer to establish an encrypted Session.

This method MUST be called initially by the active resp. opening party (Alice) once. The other party will hopefully Acknowledge this message.

func (*Session) OfferSealed

func (sess *Session) OfferSealed(k *[32]byte) (offerMsg string, err error)

func (*Session) Receive

func (sess *Session) Receive(msg string) (isEstablished, isClosed bool, plaintext []byte, err error)

Receive an incoming message.

All messages except the passive party's initial offer message MUST be passed to this method. The multiple return fields indicate this message's kind.

If the active party receives its first (acknowledge) message, this Session will be established; isEstablished. If the other party has signaled to close the Session, isClosed is set. This Session MUST then also be closed down. In case of an incoming encrypted message, the plaintext field holds its decrypted plaintext value. Of course, there might also be an error.

func (*Session) ReceiveMsg

func (sess *Session) ReceiveMsg(msg Msg) (isEstablished, isClosed bool, plaintext []byte, err error)

func (*Session) Send

func (sess *Session) Send(plaintext []byte) (dataMsg string, err error)

Send a message to the other party. The given plaintext byte array will be embedded in an encrypted message.

This method is allowed to be called after the initial handshake, Offer resp. Acknowledge.

func (*Session) UnmarshalBinary

func (sess *Session) UnmarshalBinary(b []byte) error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL