diff options
Diffstat (limited to 'vendor/go.mau.fi/libsignal/state/record')
9 files changed, 1312 insertions, 0 deletions
diff --git a/vendor/go.mau.fi/libsignal/state/record/ChainState.go b/vendor/go.mau.fi/libsignal/state/record/ChainState.go new file mode 100644 index 00000000..dd07bf89 --- /dev/null +++ b/vendor/go.mau.fi/libsignal/state/record/ChainState.go @@ -0,0 +1,157 @@ +package record + +import ( + "go.mau.fi/libsignal/ecc" + "go.mau.fi/libsignal/kdf" + "go.mau.fi/libsignal/keys/chain" + "go.mau.fi/libsignal/keys/message" + "go.mau.fi/libsignal/util/bytehelper" +) + +// NewReceiverChainPair will return a new ReceiverChainPair object. +func NewReceiverChainPair(receiverChain *Chain, index int) *ReceiverChainPair { + return &ReceiverChainPair{ + ReceiverChain: receiverChain, + Index: index, + } +} + +// ReceiverChainPair is a structure for a receiver chain key and index number. +type ReceiverChainPair struct { + ReceiverChain *Chain + Index int +} + +// NewChain returns a new Chain structure for SessionState. +func NewChain(senderRatchetKeyPair *ecc.ECKeyPair, chainKey *chain.Key, + messageKeys []*message.Keys) *Chain { + + return &Chain{ + senderRatchetKeyPair: senderRatchetKeyPair, + chainKey: chainKey, + messageKeys: messageKeys, + } +} + +// NewChainFromStructure will return a new Chain with the given +// chain structure. +func NewChainFromStructure(structure *ChainStructure) (*Chain, error) { + // Alias to SliceToArray + getArray := bytehelper.SliceToArray + + // Build the sender ratchet key from bytes. + senderRatchetKeyPublic, err := ecc.DecodePoint(structure.SenderRatchetKeyPublic, 0) + if err != nil { + return nil, err + } + var senderRatchetKeyPrivate ecc.ECPrivateKeyable + if len(structure.SenderRatchetKeyPrivate) == 32 { + senderRatchetKeyPrivate = ecc.NewDjbECPrivateKey(getArray(structure.SenderRatchetKeyPrivate)) + } + senderRatchetKeyPair := ecc.NewECKeyPair(senderRatchetKeyPublic, senderRatchetKeyPrivate) + + // Build our message keys from the message key structures. + messageKeys := make([]*message.Keys, len(structure.MessageKeys)) + for i := range structure.MessageKeys { + messageKeys[i] = message.NewKeysFromStruct(structure.MessageKeys[i]) + } + + // Build our new chain state. + chainState := NewChain( + senderRatchetKeyPair, + chain.NewKeyFromStruct(structure.ChainKey, kdf.DeriveSecrets), + messageKeys, + ) + + return chainState, nil +} + +// ChainStructure is a serializeable structure for chain states. +type ChainStructure struct { + SenderRatchetKeyPublic []byte + SenderRatchetKeyPrivate []byte + ChainKey *chain.KeyStructure + MessageKeys []*message.KeysStructure +} + +// Chain is a structure used inside the SessionState that keeps +// track of an ongoing ratcheting chain for a session. +type Chain struct { + senderRatchetKeyPair *ecc.ECKeyPair + chainKey *chain.Key + messageKeys []*message.Keys +} + +// SenderRatchetKey returns the sender's EC keypair. +func (c *Chain) SenderRatchetKey() *ecc.ECKeyPair { + return c.senderRatchetKeyPair +} + +// SetSenderRatchetKey will set the chain state with the given EC +// key pair. +func (c *Chain) SetSenderRatchetKey(key *ecc.ECKeyPair) { + c.senderRatchetKeyPair = key +} + +// ChainKey will return the chain key in the chain state. +func (c *Chain) ChainKey() *chain.Key { + return c.chainKey +} + +// SetChainKey will set the chain state's chain key. +func (c *Chain) SetChainKey(key *chain.Key) { + c.chainKey = key +} + +// MessageKeys will return the message keys associated with the +// chain state. +func (c *Chain) MessageKeys() []*message.Keys { + return c.messageKeys +} + +// SetMessageKeys will set the chain state with the given message +// keys. +func (c *Chain) SetMessageKeys(keys []*message.Keys) { + c.messageKeys = keys +} + +// AddMessageKeys will append the chain state with the given +// message keys. +func (c *Chain) AddMessageKeys(keys *message.Keys) { + c.messageKeys = append(c.messageKeys, keys) +} + +// PopFirstMessageKeys will remove the first message key from +// the chain's list of message keys. +func (c *Chain) PopFirstMessageKeys() *message.Keys { + removed := c.messageKeys[0] + c.messageKeys = c.messageKeys[1:] + + return removed +} + +// structure returns a serializeable structure of the chain state. +func (c *Chain) structure() *ChainStructure { + // Alias to ArrayToSlice + getSlice := bytehelper.ArrayToSlice + + // Convert our message keys into a serializeable structure. + messageKeys := make([]*message.KeysStructure, len(c.messageKeys)) + for i := range c.messageKeys { + messageKeys[i] = message.NewStructFromKeys(c.messageKeys[i]) + } + + // Convert our sender ratchet key private + var senderRatchetKeyPrivate []byte + if c.senderRatchetKeyPair.PrivateKey() != nil { + senderRatchetKeyPrivate = getSlice(c.senderRatchetKeyPair.PrivateKey().Serialize()) + } + + // Build the chain structure. + return &ChainStructure{ + SenderRatchetKeyPublic: c.senderRatchetKeyPair.PublicKey().Serialize(), + SenderRatchetKeyPrivate: senderRatchetKeyPrivate, + ChainKey: chain.NewStructFromKey(c.chainKey), + MessageKeys: messageKeys, + } +} diff --git a/vendor/go.mau.fi/libsignal/state/record/Doc.go b/vendor/go.mau.fi/libsignal/state/record/Doc.go new file mode 100644 index 00000000..036419da --- /dev/null +++ b/vendor/go.mau.fi/libsignal/state/record/Doc.go @@ -0,0 +1,3 @@ +// Package record provides the state and record of an ongoing double +// ratchet session. +package record diff --git a/vendor/go.mau.fi/libsignal/state/record/PendingKeyExchangeState.go b/vendor/go.mau.fi/libsignal/state/record/PendingKeyExchangeState.go new file mode 100644 index 00000000..ac647215 --- /dev/null +++ b/vendor/go.mau.fi/libsignal/state/record/PendingKeyExchangeState.go @@ -0,0 +1,91 @@ +package record + +import ( + "go.mau.fi/libsignal/ecc" + "go.mau.fi/libsignal/keys/identity" + "go.mau.fi/libsignal/util/bytehelper" +) + +// NewPendingKeyExchange will return a new PendingKeyExchange object. +func NewPendingKeyExchange(sequence uint32, localBaseKeyPair, localRatchetKeyPair *ecc.ECKeyPair, + localIdentityKeyPair *identity.KeyPair) *PendingKeyExchange { + + return &PendingKeyExchange{ + sequence: sequence, + localBaseKeyPair: localBaseKeyPair, + localRatchetKeyPair: localRatchetKeyPair, + localIdentityKeyPair: localIdentityKeyPair, + } +} + +// NewPendingKeyExchangeFromStruct will return a PendingKeyExchange object from +// the given structure. This is used to get a deserialized pending prekey exchange +// fetched from persistent storage. +func NewPendingKeyExchangeFromStruct(structure *PendingKeyExchangeStructure) *PendingKeyExchange { + // Return nil if no structure was provided. + if structure == nil { + return nil + } + + // Alias the SliceToArray method. + getArray := bytehelper.SliceToArray + + // Convert the bytes in the given structure to ECC objects. + localBaseKeyPair := ecc.NewECKeyPair( + ecc.NewDjbECPublicKey(getArray(structure.LocalBaseKeyPublic)), + ecc.NewDjbECPrivateKey(getArray(structure.LocalBaseKeyPrivate)), + ) + localRatchetKeyPair := ecc.NewECKeyPair( + ecc.NewDjbECPublicKey(getArray(structure.LocalRatchetKeyPublic)), + ecc.NewDjbECPrivateKey(getArray(structure.LocalRatchetKeyPrivate)), + ) + localIdentityKeyPair := identity.NewKeyPair( + identity.NewKey(ecc.NewDjbECPublicKey(getArray(structure.LocalIdentityKeyPublic))), + ecc.NewDjbECPrivateKey(getArray(structure.LocalIdentityKeyPrivate)), + ) + + // Return the PendingKeyExchange with the deserialized keys. + return &PendingKeyExchange{ + sequence: structure.Sequence, + localBaseKeyPair: localBaseKeyPair, + localRatchetKeyPair: localRatchetKeyPair, + localIdentityKeyPair: localIdentityKeyPair, + } +} + +// PendingKeyExchangeStructure is a serializable structure for pending +// key exchanges. This structure is used for persistent storage of the +// key exchange state. +type PendingKeyExchangeStructure struct { + Sequence uint32 + LocalBaseKeyPublic []byte + LocalBaseKeyPrivate []byte + LocalRatchetKeyPublic []byte + LocalRatchetKeyPrivate []byte + LocalIdentityKeyPublic []byte + LocalIdentityKeyPrivate []byte +} + +// PendingKeyExchange is a structure for storing a pending +// key exchange for a session state. +type PendingKeyExchange struct { + sequence uint32 + localBaseKeyPair *ecc.ECKeyPair + localRatchetKeyPair *ecc.ECKeyPair + localIdentityKeyPair *identity.KeyPair +} + +// structre will return a serializable structure of a pending key exchange +// so it can be persistently stored. +func (p *PendingKeyExchange) structure() *PendingKeyExchangeStructure { + getSlice := bytehelper.ArrayToSlice + return &PendingKeyExchangeStructure{ + Sequence: p.sequence, + LocalBaseKeyPublic: getSlice(p.localBaseKeyPair.PublicKey().PublicKey()), + LocalBaseKeyPrivate: getSlice(p.localBaseKeyPair.PrivateKey().Serialize()), + LocalRatchetKeyPublic: getSlice(p.localRatchetKeyPair.PublicKey().PublicKey()), + LocalRatchetKeyPrivate: getSlice(p.localRatchetKeyPair.PrivateKey().Serialize()), + LocalIdentityKeyPublic: getSlice(p.localIdentityKeyPair.PublicKey().PublicKey().PublicKey()), + LocalIdentityKeyPrivate: getSlice(p.localIdentityKeyPair.PrivateKey().Serialize()), + } +} diff --git a/vendor/go.mau.fi/libsignal/state/record/PendingPreKeyState.go b/vendor/go.mau.fi/libsignal/state/record/PendingPreKeyState.go new file mode 100644 index 00000000..27a8878b --- /dev/null +++ b/vendor/go.mau.fi/libsignal/state/record/PendingPreKeyState.go @@ -0,0 +1,62 @@ +package record + +import ( + "go.mau.fi/libsignal/ecc" + "go.mau.fi/libsignal/util/optional" +) + +// NewPendingPreKey will return a new pending pre key object. +func NewPendingPreKey(preKeyID *optional.Uint32, signedPreKeyID uint32, + baseKey ecc.ECPublicKeyable) *PendingPreKey { + + return &PendingPreKey{ + preKeyID: preKeyID, + signedPreKeyID: signedPreKeyID, + baseKey: baseKey, + } +} + +// NewPendingPreKeyFromStruct will return a new pending prekey object from the +// given structure. +func NewPendingPreKeyFromStruct(preKey *PendingPreKeyStructure) (*PendingPreKey, error) { + baseKey, err := ecc.DecodePoint(preKey.BaseKey, 0) + if err != nil { + return nil, err + } + + pendingPreKey := NewPendingPreKey( + preKey.PreKeyID, + preKey.SignedPreKeyID, + baseKey, + ) + + return pendingPreKey, nil +} + +// PendingPreKeyStructure is a serializeable structure for pending +// prekeys. +type PendingPreKeyStructure struct { + PreKeyID *optional.Uint32 + SignedPreKeyID uint32 + BaseKey []byte +} + +// PendingPreKey is a structure for pending pre keys +// for a session state. +type PendingPreKey struct { + preKeyID *optional.Uint32 + signedPreKeyID uint32 + baseKey ecc.ECPublicKeyable +} + +// structure will return a serializeable structure of the pending prekey. +func (p *PendingPreKey) structure() *PendingPreKeyStructure { + if p != nil { + return &PendingPreKeyStructure{ + PreKeyID: p.preKeyID, + SignedPreKeyID: p.signedPreKeyID, + BaseKey: p.baseKey.Serialize(), + } + } + return nil +} diff --git a/vendor/go.mau.fi/libsignal/state/record/PreKeyRecord.go b/vendor/go.mau.fi/libsignal/state/record/PreKeyRecord.go new file mode 100644 index 00000000..dcd630d2 --- /dev/null +++ b/vendor/go.mau.fi/libsignal/state/record/PreKeyRecord.go @@ -0,0 +1,90 @@ +package record + +import ( + "go.mau.fi/libsignal/ecc" + "go.mau.fi/libsignal/util/bytehelper" + "go.mau.fi/libsignal/util/optional" +) + +// PreKeySerializer is an interface for serializing and deserializing +// PreKey objects into bytes. An implementation of this interface should be +// used to encode/decode the object into JSON, Protobuffers, etc. +type PreKeySerializer interface { + Serialize(preKey *PreKeyStructure) []byte + Deserialize(serialized []byte) (*PreKeyStructure, error) +} + +// NewPreKeyFromBytes will return a prekey record from the given bytes using the given serializer. +func NewPreKeyFromBytes(serialized []byte, serializer PreKeySerializer) (*PreKey, error) { + // Use the given serializer to decode the signal message. + preKeyStructure, err := serializer.Deserialize(serialized) + if err != nil { + return nil, err + } + + return NewPreKeyFromStruct(preKeyStructure, serializer) +} + +// NewPreKeyFromStruct returns a PreKey record using the given serializable structure. +func NewPreKeyFromStruct(structure *PreKeyStructure, serializer PreKeySerializer) (*PreKey, error) { + // Create the prekey record from the structure. + preKey := &PreKey{ + structure: *structure, + serializer: serializer, + } + + // Generate the ECC key from bytes. + publicKey := ecc.NewDjbECPublicKey(bytehelper.SliceToArray(structure.PublicKey)) + privateKey := ecc.NewDjbECPrivateKey(bytehelper.SliceToArray(structure.PrivateKey)) + keyPair := ecc.NewECKeyPair(publicKey, privateKey) + preKey.keyPair = keyPair + + return preKey, nil +} + +// NewPreKey record returns a new pre key record that can +// be stored in a PreKeyStore. +func NewPreKey(id uint32, keyPair *ecc.ECKeyPair, serializer PreKeySerializer) *PreKey { + return &PreKey{ + structure: PreKeyStructure{ + ID: id, + PublicKey: keyPair.PublicKey().Serialize(), + PrivateKey: bytehelper.ArrayToSlice(keyPair.PrivateKey().Serialize()), + }, + keyPair: keyPair, + serializer: serializer, + } +} + +// PreKeyStructure is a structure for serializing PreKey records. +type PreKeyStructure struct { + ID uint32 + PublicKey []byte + PrivateKey []byte +} + +// PreKey record is a structure for storing pre keys inside +// a PreKeyStore. +type PreKey struct { + structure PreKeyStructure + keyPair *ecc.ECKeyPair + serializer PreKeySerializer +} + +// ID returns the pre key record's id. +func (p *PreKey) ID() *optional.Uint32 { + // TODO: manually set this to empty if empty + return optional.NewOptionalUint32(p.structure.ID) +} + +// KeyPair returns the pre key record's key pair. +func (p *PreKey) KeyPair() *ecc.ECKeyPair { + return p.keyPair +} + +// Serialize uses the PreKey serializer to return the PreKey +// as serialized bytes. +func (p *PreKey) Serialize() []byte { + structure := p.structure + return p.serializer.Serialize(&structure) +} diff --git a/vendor/go.mau.fi/libsignal/state/record/SessionRecord.go b/vendor/go.mau.fi/libsignal/state/record/SessionRecord.go new file mode 100644 index 00000000..01e817e4 --- /dev/null +++ b/vendor/go.mau.fi/libsignal/state/record/SessionRecord.go @@ -0,0 +1,197 @@ +package record + +import ( + "bytes" +) + +// archivedStatesMaxLength describes how many previous session +// states we should keep track of. +const archivedStatesMaxLength int = 40 + +// SessionSerializer is an interface for serializing and deserializing +// a Signal Session into bytes. An implementation of this interface should be +// used to encode/decode the object into JSON, Protobuffers, etc. +type SessionSerializer interface { + Serialize(state *SessionStructure) []byte + Deserialize(serialized []byte) (*SessionStructure, error) +} + +// NewSessionFromBytes will return a Signal Session from the given +// bytes using the given serializer. +func NewSessionFromBytes(serialized []byte, serializer SessionSerializer, stateSerializer StateSerializer) (*Session, error) { + // Use the given serializer to decode the session. + sessionStructure, err := serializer.Deserialize(serialized) + if err != nil { + return nil, err + } + + return NewSessionFromStructure(sessionStructure, serializer, stateSerializer) +} + +// NewSession creates a new session record and uses the given session and state +// serializers to convert the object into storeable bytes. +func NewSession(serializer SessionSerializer, stateSerializer StateSerializer) *Session { + record := Session{ + sessionState: NewState(stateSerializer), + previousStates: []*State{}, + fresh: true, + serializer: serializer, + } + + return &record +} + +// NewSessionFromStructure will return a new Signal Session from the given +// session structure and serializer. +func NewSessionFromStructure(structure *SessionStructure, serializer SessionSerializer, + stateSerializer StateSerializer) (*Session, error) { + + // Build our previous states from structure. + previousStates := make([]*State, len(structure.PreviousStates)) + for i := range structure.PreviousStates { + var err error + previousStates[i], err = NewStateFromStructure(structure.PreviousStates[i], stateSerializer) + if err != nil { + return nil, err + } + } + + // Build our current state from structure. + sessionState, err := NewStateFromStructure(structure.SessionState, stateSerializer) + if err != nil { + return nil, err + } + + // Build and return our session. + session := &Session{ + previousStates: previousStates, + sessionState: sessionState, + serializer: serializer, + fresh: false, + } + + return session, nil +} + +// NewSessionFromState creates a new session record from the given +// session state. +func NewSessionFromState(sessionState *State, serializer SessionSerializer) *Session { + record := Session{ + sessionState: sessionState, + previousStates: []*State{}, + fresh: false, + serializer: serializer, + } + + return &record +} + +// SessionStructure is a public, serializeable structure for Signal +// Sessions. The states defined in the session are immuteable, as +// they should not be changed by anyone but the serializer. +type SessionStructure struct { + SessionState *StateStructure + PreviousStates []*StateStructure +} + +// Session encapsulates the state of an ongoing session. +type Session struct { + serializer SessionSerializer + sessionState *State + previousStates []*State + fresh bool +} + +// SetState sets the session record's current state to the given +// one. +func (r *Session) SetState(sessionState *State) { + r.sessionState = sessionState +} + +// IsFresh is used to determine if this is a brand new session +// or if a session record has already existed. +func (r *Session) IsFresh() bool { + return r.fresh +} + +// SessionState returns the session state object of the current +// session record. +func (r *Session) SessionState() *State { + return r.sessionState +} + +// PreviousSessionStates returns a list of all currently maintained +// "previous" session states. +func (r *Session) PreviousSessionStates() []*State { + return r.previousStates +} + +// HasSessionState will check this record to see if the sender's +// base key exists in the current and previous states. +func (r *Session) HasSessionState(version int, senderBaseKey []byte) bool { + // Ensure the session state version is identical to this one. + if r.sessionState.Version() == version && (bytes.Compare(senderBaseKey, r.sessionState.SenderBaseKey()) == 0) { + return true + } + + // Loop through all of our previous states and see if this + // exists in our state. + for i := range r.previousStates { + if r.previousStates[i].Version() == version && bytes.Compare(senderBaseKey, r.previousStates[i].SenderBaseKey()) == 0 { + return true + } + } + + return false +} + +// ArchiveCurrentState moves the current session state into the list +// of "previous" session states, and replaces the current session state +// with a fresh reset instance. +func (r *Session) ArchiveCurrentState() { + r.PromoteState(NewState(r.sessionState.serializer)) +} + +// PromoteState takes the given session state and replaces it with the +// current state, pushing the previous current state to "previousStates". +func (r *Session) PromoteState(promotedState *State) { + r.previousStates = r.prependStates(r.previousStates, r.sessionState) + r.sessionState = promotedState + + // Remove the last state if it has reached our maximum length + if len(r.previousStates) > archivedStatesMaxLength { + r.previousStates = r.removeLastState(r.previousStates) + } +} + +// Serialize will return the session as serialized bytes so it can be +// persistently stored. +func (r *Session) Serialize() []byte { + return r.serializer.Serialize(r.Structure()) +} + +// prependStates takes an array/slice of states and prepends it with +// the given session state. +func (r *Session) prependStates(states []*State, sessionState *State) []*State { + return append([]*State{sessionState}, states...) +} + +// removeLastState takes an array/slice of states and removes the +// last element from it. +func (r *Session) removeLastState(states []*State) []*State { + return states[:len(states)-1] +} + +// Structure will return a simple serializable session structure +// from the given structure. This is used for serialization to persistently +// store a session record. +func (r *Session) Structure() *SessionStructure { + previousStates := make([]*StateStructure, len(r.previousStates)) + for i := range r.previousStates { + previousStates[i] = r.previousStates[i].structure() + } + return &SessionStructure{ + SessionState: r.sessionState.structure(), + PreviousStates: previousStates, + } +} diff --git a/vendor/go.mau.fi/libsignal/state/record/SessionState.go b/vendor/go.mau.fi/libsignal/state/record/SessionState.go new file mode 100644 index 00000000..d0f61d5c --- /dev/null +++ b/vendor/go.mau.fi/libsignal/state/record/SessionState.go @@ -0,0 +1,531 @@ +package record + +import ( + "go.mau.fi/libsignal/ecc" + "go.mau.fi/libsignal/kdf" + "go.mau.fi/libsignal/keys/chain" + "go.mau.fi/libsignal/keys/identity" + "go.mau.fi/libsignal/keys/message" + "go.mau.fi/libsignal/keys/root" + "go.mau.fi/libsignal/keys/session" + "go.mau.fi/libsignal/logger" + "go.mau.fi/libsignal/util/errorhelper" + "go.mau.fi/libsignal/util/optional" +) + +const maxMessageKeys int = 2000 +const maxReceiverChains int = 5 + +// StateSerializer is an interface for serializing and deserializing +// a Signal State into bytes. An implementation of this interface should be +// used to encode/decode the object into JSON, Protobuffers, etc. +type StateSerializer interface { + Serialize(state *StateStructure) []byte + Deserialize(serialized []byte) (*StateStructure, error) +} + +// NewStateFromBytes will return a Signal State from the given +// bytes using the given serializer. +func NewStateFromBytes(serialized []byte, serializer StateSerializer) (*State, error) { + // Use the given serializer to decode the signal message. + stateStructure, err := serializer.Deserialize(serialized) + if err != nil { + return nil, err + } + + return NewStateFromStructure(stateStructure, serializer) +} + +// NewState returns a new session state. +func NewState(serializer StateSerializer) *State { + return &State{serializer: serializer} +} + +// NewStateFromStructure will return a new session state with the +// given state structure. +func NewStateFromStructure(structure *StateStructure, serializer StateSerializer) (*State, error) { + // Keep a list of errors, so they can be handled once. + errors := errorhelper.NewMultiError() + + // Convert our ecc keys from bytes into object form. + localIdentityPublic, err := ecc.DecodePoint(structure.LocalIdentityPublic, 0) + errors.Add(err) + remoteIdentityPublic, err := ecc.DecodePoint(structure.RemoteIdentityPublic, 0) + errors.Add(err) + senderBaseKey, err := ecc.DecodePoint(structure.SenderBaseKey, 0) + errors.Add(err) + var pendingPreKey *PendingPreKey + if structure.PendingPreKey != nil { + pendingPreKey, err = NewPendingPreKeyFromStruct(structure.PendingPreKey) + errors.Add(err) + } + senderChain, err := NewChainFromStructure(structure.SenderChain) + errors.Add(err) + + // Build our receiver chains from structure. + receiverChains := make([]*Chain, len(structure.ReceiverChains)) + for i := range structure.ReceiverChains { + receiverChains[i], err = NewChainFromStructure(structure.ReceiverChains[i]) + errors.Add(err) + } + + // Handle any errors. The first error will always be returned if there are multiple. + if errors.HasErrors() { + return nil, errors + } + + // Build our state object. + state := &State{ + localIdentityPublic: identity.NewKey(localIdentityPublic), + localRegistrationID: structure.LocalRegistrationID, + needsRefresh: structure.NeedsRefresh, + pendingKeyExchange: NewPendingKeyExchangeFromStruct(structure.PendingKeyExchange), + pendingPreKey: pendingPreKey, + previousCounter: structure.PreviousCounter, + receiverChains: receiverChains, + remoteIdentityPublic: identity.NewKey(remoteIdentityPublic), + remoteRegistrationID: structure.RemoteRegistrationID, + rootKey: root.NewKey(kdf.DeriveSecrets, structure.RootKey), + senderBaseKey: senderBaseKey, + senderChain: senderChain, + serializer: serializer, + sessionVersion: structure.SessionVersion, + } + + return state, nil +} + +// StateStructure is the structure of a session state. Fields are public +// to be used for serialization and deserialization. +type StateStructure struct { + LocalIdentityPublic []byte + LocalRegistrationID uint32 + NeedsRefresh bool + PendingKeyExchange *PendingKeyExchangeStructure + PendingPreKey *PendingPreKeyStructure + PreviousCounter uint32 + ReceiverChains []*ChainStructure + RemoteIdentityPublic []byte + RemoteRegistrationID uint32 + RootKey []byte + SenderBaseKey []byte + SenderChain *ChainStructure + SessionVersion int +} + +// State is a session state that contains the structure for +// all sessions. Session states are contained inside session records. +// The session state is implemented as a struct rather than protobuffers +// to allow other serialization methods. +type State struct { + localIdentityPublic *identity.Key + localRegistrationID uint32 + needsRefresh bool + pendingKeyExchange *PendingKeyExchange + pendingPreKey *PendingPreKey + previousCounter uint32 + receiverChains []*Chain + remoteIdentityPublic *identity.Key + remoteRegistrationID uint32 + rootKey *root.Key + senderBaseKey ecc.ECPublicKeyable + senderChain *Chain + serializer StateSerializer + sessionVersion int +} + +// SenderBaseKey returns the sender's base key in bytes. +func (s *State) SenderBaseKey() []byte { + if s.senderBaseKey == nil { + return nil + } + return s.senderBaseKey.Serialize() +} + +// SetSenderBaseKey sets the sender's base key with the given bytes. +func (s *State) SetSenderBaseKey(senderBaseKey []byte) { + s.senderBaseKey, _ = ecc.DecodePoint(senderBaseKey, 0) +} + +// Version returns the session's version. +func (s *State) Version() int { + return s.sessionVersion +} + +// SetVersion sets the session state's version number. +func (s *State) SetVersion(version int) { + s.sessionVersion = version +} + +// RemoteIdentityKey returns the identity key of the remote user. +func (s *State) RemoteIdentityKey() *identity.Key { + return s.remoteIdentityPublic +} + +// SetRemoteIdentityKey sets this session's identity key for the remote +// user. +func (s *State) SetRemoteIdentityKey(identityKey *identity.Key) { + s.remoteIdentityPublic = identityKey +} + +// LocalIdentityKey returns the session's identity key for the local +// user. +func (s *State) LocalIdentityKey() *identity.Key { + return s.localIdentityPublic +} + +// SetLocalIdentityKey sets the session's identity key for the local +// user. +func (s *State) SetLocalIdentityKey(identityKey *identity.Key) { + s.localIdentityPublic = identityKey +} + +// PreviousCounter returns the counter of the previous message. +func (s *State) PreviousCounter() uint32 { + return s.previousCounter +} + +// SetPreviousCounter sets the counter for the previous message. +func (s *State) SetPreviousCounter(previousCounter uint32) { + s.previousCounter = previousCounter +} + +// RootKey returns the root key for the session. +func (s *State) RootKey() session.RootKeyable { + return s.rootKey +} + +// SetRootKey sets the root key for the session. +func (s *State) SetRootKey(rootKey session.RootKeyable) { + s.rootKey = rootKey.(*root.Key) +} + +// SenderRatchetKey returns the public ratchet key of the sender. +func (s *State) SenderRatchetKey() ecc.ECPublicKeyable { + return s.senderChain.senderRatchetKeyPair.PublicKey() +} + +// SenderRatchetKeyPair returns the public/private ratchet key pair +// of the sender. +func (s *State) SenderRatchetKeyPair() *ecc.ECKeyPair { + return s.senderChain.senderRatchetKeyPair +} + +// HasReceiverChain will check to see if the session state has +// the given ephemeral key. +func (s *State) HasReceiverChain(senderEphemeral ecc.ECPublicKeyable) bool { + return s.receiverChain(senderEphemeral) != nil +} + +// HasSenderChain will check to see if the session state has a +// sender chain. +func (s *State) HasSenderChain() bool { + return s.senderChain != nil +} + +// receiverChain will loop through the session state's receiver chains +// and compare the given ephemeral key. If it is found, then the chain +// and index will be returned as a pair. +func (s *State) receiverChain(senderEphemeral ecc.ECPublicKeyable) *ReceiverChainPair { + receiverChains := s.receiverChains + + for i, receiverChain := range receiverChains { + chainSenderRatchetKey, err := ecc.DecodePoint(receiverChain.senderRatchetKeyPair.PublicKey().Serialize(), 0) + if err != nil { + logger.Error("Error getting receiverchain: ", err) + } + + // If the chainSenderRatchetKey equals our senderEphemeral key, return it. + if chainSenderRatchetKey.PublicKey() == senderEphemeral.PublicKey() { + return NewReceiverChainPair(receiverChain, i) + } + } + + return nil +} + +// ReceiverChainKey will use the given ephemeral key to generate a new +// chain key. +func (s *State) ReceiverChainKey(senderEphemeral ecc.ECPublicKeyable) *chain.Key { + receiverChainAndIndex := s.receiverChain(senderEphemeral) + receiverChain := receiverChainAndIndex.ReceiverChain + + if receiverChainAndIndex == nil || receiverChain == nil { + return nil + } + + return chain.NewKey( + kdf.DeriveSecrets, + receiverChain.chainKey.Key(), + receiverChain.chainKey.Index(), + ) +} + +// AddReceiverChain will add the given ratchet key and chain key to the session +// state. +func (s *State) AddReceiverChain(senderRatchetKey ecc.ECPublicKeyable, chainKey session.ChainKeyable) { + // Create a keypair structure with our sender ratchet key. + senderKey := ecc.NewECKeyPair(senderRatchetKey, nil) + + // Create a Chain state object that will hold our sender key, chain key, and + // message keys. + chain := NewChain(senderKey, chainKey.(*chain.Key), []*message.Keys{}) + + // Add the Chain state to our list of receiver chain states. + s.receiverChains = append(s.receiverChains, chain) + + // If our list of receiver chains is too big, delete the oldest entry. + if len(s.receiverChains) > maxReceiverChains { + i := 0 + s.receiverChains = append(s.receiverChains[:i], s.receiverChains[i+1:]...) + } +} + +// SetSenderChain will set the given ratchet key pair and chain key for this session +// state. +func (s *State) SetSenderChain(senderRatchetKeyPair *ecc.ECKeyPair, chainKey session.ChainKeyable) { + // Create a Chain state object that will hold our sender key, chain key, and + // message keys. + chain := NewChain(senderRatchetKeyPair, chainKey.(*chain.Key), []*message.Keys{}) + + // Set the sender chain. + s.senderChain = chain +} + +// SenderChainKey will return the chain key of the session state. +func (s *State) SenderChainKey() session.ChainKeyable { + chainKey := s.senderChain.chainKey + return chain.NewKey(kdf.DeriveSecrets, chainKey.Key(), chainKey.Index()) +} + +// SetSenderChainKey will set the chain key in the chain state for this session to +// the given chain key. +func (s *State) SetSenderChainKey(nextChainKey session.ChainKeyable) { + senderChain := s.senderChain + senderChain.SetChainKey(nextChainKey.(*chain.Key)) +} + +// HasMessageKeys returns true if we have message keys associated with the given +// sender key and counter. +func (s *State) HasMessageKeys(senderEphemeral ecc.ECPublicKeyable, counter uint32) bool { + // Get our chain state that has our chain key. + chainAndIndex := s.receiverChain(senderEphemeral) + receiverChain := chainAndIndex.ReceiverChain + + // If the chain is empty, we don't have any message keys. + if receiverChain == nil { + return false + } + + // Get our message keys from our receiver chain. + messageKeyList := receiverChain.MessageKeys() + + // Loop through our message keys and compare its index with the + // given counter. + for _, messageKey := range messageKeyList { + if messageKey.Index() == counter { + return true + } + } + + return false +} + +// RemoveMessageKeys removes the message key with the given sender key and +// counter. It will return the removed message key. +func (s *State) RemoveMessageKeys(senderEphemeral ecc.ECPublicKeyable, counter uint32) *message.Keys { + // Get our chain state that has our chain key. + chainAndIndex := s.receiverChain(senderEphemeral) + chainKey := chainAndIndex.ReceiverChain + + // If the chain is empty, we don't have any message keys. + if chainKey == nil { + return nil + } + + // Get our message keys from our receiver chain. + messageKeyList := chainKey.MessageKeys() + + // Loop through our message keys and compare its index with the + // given counter. When we find a match, remove it from our list. + var rmIndex int + for i, messageKey := range messageKeyList { + if messageKey.Index() == counter { + rmIndex = i + break + } + } + + // Retrive the message key + messageKey := chainKey.messageKeys[rmIndex] + + // Delete the message key from the given position. + chainKey.messageKeys = append(chainKey.messageKeys[:rmIndex], chainKey.messageKeys[rmIndex+1:]...) + + return message.NewKeys( + messageKey.CipherKey(), + messageKey.MacKey(), + messageKey.Iv(), + messageKey.Index(), + ) +} + +// SetMessageKeys will update the chain associated with the given sender key with +// the given message keys. +func (s *State) SetMessageKeys(senderEphemeral ecc.ECPublicKeyable, messageKeys *message.Keys) { + chainAndIndex := s.receiverChain(senderEphemeral) + chainState := chainAndIndex.ReceiverChain + + // Add the message keys to our chain state. + chainState.AddMessageKeys( + message.NewKeys( + messageKeys.CipherKey(), + messageKeys.MacKey(), + messageKeys.Iv(), + messageKeys.Index(), + ), + ) + + if len(chainState.MessageKeys()) > maxMessageKeys { + chainState.PopFirstMessageKeys() + } +} + +// SetReceiverChainKey sets the session's receiver chain key with the given chain key +// associated with the given senderEphemeral key. +func (s *State) SetReceiverChainKey(senderEphemeral ecc.ECPublicKeyable, chainKey session.ChainKeyable) { + chainAndIndex := s.receiverChain(senderEphemeral) + chainState := chainAndIndex.ReceiverChain + chainState.SetChainKey(chainKey.(*chain.Key)) +} + +// SetPendingKeyExchange will set the session's pending key exchange state to the given +// sequence and key pairs. +func (s *State) SetPendingKeyExchange(sequence uint32, ourBaseKey, ourRatchetKey *ecc.ECKeyPair, + ourIdentityKey *identity.KeyPair) { + + s.pendingKeyExchange = NewPendingKeyExchange( + sequence, + ourBaseKey, + ourRatchetKey, + ourIdentityKey, + ) +} + +// PendingKeyExchangeSequence will return the session's pending key exchange sequence +// number. +func (s *State) PendingKeyExchangeSequence() uint32 { + return s.pendingKeyExchange.sequence +} + +// PendingKeyExchangeBaseKeyPair will return the session's pending key exchange base keypair. +func (s *State) PendingKeyExchangeBaseKeyPair() *ecc.ECKeyPair { + return s.pendingKeyExchange.localBaseKeyPair +} + +// PendingKeyExchangeRatchetKeyPair will return the session's pending key exchange ratchet +// keypair. +func (s *State) PendingKeyExchangeRatchetKeyPair() *ecc.ECKeyPair { + return s.pendingKeyExchange.localRatchetKeyPair +} + +// PendingKeyExchangeIdentityKeyPair will return the session's pending key exchange identity +// keypair. +func (s *State) PendingKeyExchangeIdentityKeyPair() *identity.KeyPair { + return s.pendingKeyExchange.localIdentityKeyPair +} + +// HasPendingKeyExchange will return true if there is a valid pending key exchange waiting. +func (s *State) HasPendingKeyExchange() bool { + return s.pendingKeyExchange != nil +} + +// SetUnacknowledgedPreKeyMessage will return unacknowledged pre key message with the +// given key ids and base key. +func (s *State) SetUnacknowledgedPreKeyMessage(preKeyID *optional.Uint32, signedPreKeyID uint32, baseKey ecc.ECPublicKeyable) { + s.pendingPreKey = NewPendingPreKey( + preKeyID, + signedPreKeyID, + baseKey, + ) +} + +// HasUnacknowledgedPreKeyMessage will return true if this session has an unacknowledged +// pre key message. +func (s *State) HasUnacknowledgedPreKeyMessage() bool { + return s.pendingPreKey != nil +} + +// UnackPreKeyMessageItems will return the session's unacknowledged pre key messages. +func (s *State) UnackPreKeyMessageItems() (*UnackPreKeyMessageItems, error) { + preKeyID := s.pendingPreKey.preKeyID + signedPreKeyID := s.pendingPreKey.signedPreKeyID + baseKey, err := ecc.DecodePoint(s.pendingPreKey.baseKey.Serialize(), 0) + if err != nil { + return nil, err + } + return NewUnackPreKeyMessageItems(preKeyID, signedPreKeyID, baseKey), nil +} + +// ClearUnackPreKeyMessage will clear the session's pending pre key. +func (s *State) ClearUnackPreKeyMessage() { + s.pendingPreKey = nil +} + +// SetRemoteRegistrationID sets the remote user's registration id. +func (s *State) SetRemoteRegistrationID(registrationID uint32) { + s.remoteRegistrationID = registrationID +} + +// RemoteRegistrationID returns the remote user's registration id. +func (s *State) RemoteRegistrationID() uint32 { + return s.remoteRegistrationID +} + +// SetLocalRegistrationID sets the local user's registration id. +func (s *State) SetLocalRegistrationID(registrationID uint32) { + s.localRegistrationID = registrationID +} + +// LocalRegistrationID returns the local user's registration id. +func (s *State) LocalRegistrationID() uint32 { + return s.localRegistrationID +} + +// Serialize will return the state as bytes using the given serializer. +func (s *State) Serialize() []byte { + return s.serializer.Serialize(s.structure()) +} + +// structure will return a serializable structure of the +// the given state so it can be persistently stored. +func (s *State) structure() *StateStructure { + // Convert our receiver chains into a serializeable structure + receiverChains := make([]*ChainStructure, len(s.receiverChains)) + for i := range s.receiverChains { + receiverChains[i] = s.receiverChains[i].structure() + } + + // Convert our pending key exchange into a serializeable structure + var pendingKeyExchange *PendingKeyExchangeStructure + if s.pendingKeyExchange != nil { + pendingKeyExchange = s.pendingKeyExchange.structure() + } + + // Build and return our state structure. + return &StateStructure{ + LocalIdentityPublic: s.localIdentityPublic.Serialize(), + LocalRegistrationID: s.localRegistrationID, + NeedsRefresh: s.needsRefresh, + PendingKeyExchange: pendingKeyExchange, + PendingPreKey: s.pendingPreKey.structure(), + PreviousCounter: s.previousCounter, + ReceiverChains: receiverChains, + RemoteIdentityPublic: s.remoteIdentityPublic.Serialize(), + RemoteRegistrationID: s.remoteRegistrationID, + RootKey: s.rootKey.Bytes(), + SenderBaseKey: s.senderBaseKey.Serialize(), + SenderChain: s.senderChain.structure(), + SessionVersion: s.sessionVersion, + } +} diff --git a/vendor/go.mau.fi/libsignal/state/record/SignedPreKeyRecord.go b/vendor/go.mau.fi/libsignal/state/record/SignedPreKeyRecord.go new file mode 100644 index 00000000..f11ab088 --- /dev/null +++ b/vendor/go.mau.fi/libsignal/state/record/SignedPreKeyRecord.go @@ -0,0 +1,112 @@ +package record + +import ( + "go.mau.fi/libsignal/ecc" + "go.mau.fi/libsignal/util/bytehelper" +) + +// SignedPreKeySerializer is an interface for serializing and deserializing +// SignedPreKey objects into bytes. An implementation of this interface should be +// used to encode/decode the object into JSON, Protobuffers, etc. +type SignedPreKeySerializer interface { + Serialize(signedPreKey *SignedPreKeyStructure) []byte + Deserialize(serialized []byte) (*SignedPreKeyStructure, error) +} + +// NewSignedPreKeyFromBytes will return a signed prekey record from the given +// bytes using the given serializer. +func NewSignedPreKeyFromBytes(serialized []byte, serializer SignedPreKeySerializer) (*SignedPreKey, error) { + // Use the given serializer to decode the signal message. + signedPreKeyStructure, err := serializer.Deserialize(serialized) + if err != nil { + return nil, err + } + + return NewSignedPreKeyFromStruct(signedPreKeyStructure, serializer) +} + +// NewSignedPreKeyFromStruct returns a SignedPreKey record using the given +// serializable structure. +func NewSignedPreKeyFromStruct(structure *SignedPreKeyStructure, + serializer SignedPreKeySerializer) (*SignedPreKey, error) { + + // Create the signed prekey record from the structure. + signedPreKey := &SignedPreKey{ + structure: *structure, + serializer: serializer, + signature: bytehelper.SliceToArray64(structure.Signature), + } + + // Generate the ECC key from bytes. + publicKey := ecc.NewDjbECPublicKey(bytehelper.SliceToArray(structure.PublicKey)) + privateKey := ecc.NewDjbECPrivateKey(bytehelper.SliceToArray(structure.PrivateKey)) + keyPair := ecc.NewECKeyPair(publicKey, privateKey) + signedPreKey.keyPair = keyPair + + return signedPreKey, nil +} + +// NewSignedPreKey record creates a new signed pre key record +// with the given properties. +func NewSignedPreKey(id uint32, timestamp int64, keyPair *ecc.ECKeyPair, + sig [64]byte, serializer SignedPreKeySerializer) *SignedPreKey { + + return &SignedPreKey{ + structure: SignedPreKeyStructure{ + ID: id, + Timestamp: timestamp, + PublicKey: keyPair.PublicKey().Serialize(), + PrivateKey: bytehelper.ArrayToSlice(keyPair.PrivateKey().Serialize()), + Signature: bytehelper.ArrayToSlice64(sig), + }, + keyPair: keyPair, + signature: sig, + serializer: serializer, + } +} + +// SignedPreKeyStructure is a flat structure of a signed pre key, used +// for serialization and deserialization. +type SignedPreKeyStructure struct { + ID uint32 + PublicKey []byte + PrivateKey []byte + Signature []byte + Timestamp int64 +} + +// SignedPreKey record is a structure for storing a signed +// pre key in a SignedPreKey store. +type SignedPreKey struct { + structure SignedPreKeyStructure + keyPair *ecc.ECKeyPair + signature [64]byte + serializer SignedPreKeySerializer +} + +// ID returns the record's id. +func (s *SignedPreKey) ID() uint32 { + return s.structure.ID +} + +// Timestamp returns the record's timestamp +func (s *SignedPreKey) Timestamp() int64 { + return s.structure.Timestamp +} + +// KeyPair returns the signed pre key record's key pair. +func (s *SignedPreKey) KeyPair() *ecc.ECKeyPair { + return s.keyPair +} + +// Signature returns the record's signed prekey signature. +func (s *SignedPreKey) Signature() [64]byte { + return s.signature +} + +// Serialize uses the SignedPreKey serializer to return the SignedPreKey +// as serialized bytes. +func (s *SignedPreKey) Serialize() []byte { + structure := s.structure + return s.serializer.Serialize(&structure) +} diff --git a/vendor/go.mau.fi/libsignal/state/record/UnacknowledgedPreKey.go b/vendor/go.mau.fi/libsignal/state/record/UnacknowledgedPreKey.go new file mode 100644 index 00000000..61fe0644 --- /dev/null +++ b/vendor/go.mau.fi/libsignal/state/record/UnacknowledgedPreKey.go @@ -0,0 +1,69 @@ +package record + +import ( + "go.mau.fi/libsignal/ecc" + "go.mau.fi/libsignal/util/optional" +) + +// NewUnackPreKeyMessageItems returns message items that are unacknowledged. +func NewUnackPreKeyMessageItems(preKeyID *optional.Uint32, signedPreKeyID uint32, + baseKey ecc.ECPublicKeyable) *UnackPreKeyMessageItems { + + return &UnackPreKeyMessageItems{ + preKeyID: preKeyID, + signedPreKeyID: signedPreKeyID, + baseKey: baseKey, + } +} + +// NewUnackPreKeyMessageItemsFromStruct will return a new unacknowledged prekey +// message items object from the given structure. +func NewUnackPreKeyMessageItemsFromStruct(structure *UnackPreKeyMessageItemsStructure) *UnackPreKeyMessageItems { + baseKey, _ := ecc.DecodePoint(structure.BaseKey, 0) + return NewUnackPreKeyMessageItems( + structure.PreKeyID, + structure.SignedPreKeyID, + baseKey, + ) +} + +// UnackPreKeyMessageItemsStructure is a serializable structure for unackowledged +// prekey message items. +type UnackPreKeyMessageItemsStructure struct { + PreKeyID *optional.Uint32 + SignedPreKeyID uint32 + BaseKey []byte +} + +// UnackPreKeyMessageItems is a structure for messages that have not been +// acknowledged. +type UnackPreKeyMessageItems struct { + preKeyID *optional.Uint32 + signedPreKeyID uint32 + baseKey ecc.ECPublicKeyable +} + +// PreKeyID returns the prekey id of the unacknowledged message. +func (u *UnackPreKeyMessageItems) PreKeyID() *optional.Uint32 { + return u.preKeyID +} + +// SignedPreKeyID returns the signed prekey id of the unacknowledged message. +func (u *UnackPreKeyMessageItems) SignedPreKeyID() uint32 { + return u.signedPreKeyID +} + +// BaseKey returns the ECC public key of the unacknowledged message. +func (u *UnackPreKeyMessageItems) BaseKey() ecc.ECPublicKeyable { + return u.baseKey +} + +// structure will return a serializable base structure +// for unacknowledged prekey message items. +func (u *UnackPreKeyMessageItems) structure() *UnackPreKeyMessageItemsStructure { + return &UnackPreKeyMessageItemsStructure{ + PreKeyID: u.preKeyID, + SignedPreKeyID: u.signedPreKeyID, + BaseKey: u.baseKey.Serialize(), + } +} |