summaryrefslogtreecommitdiffstats
path: root/vendor/go.mau.fi/libsignal/session/Session.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/go.mau.fi/libsignal/session/Session.go')
-rw-r--r--vendor/go.mau.fi/libsignal/session/Session.go272
1 files changed, 272 insertions, 0 deletions
diff --git a/vendor/go.mau.fi/libsignal/session/Session.go b/vendor/go.mau.fi/libsignal/session/Session.go
new file mode 100644
index 00000000..aafac760
--- /dev/null
+++ b/vendor/go.mau.fi/libsignal/session/Session.go
@@ -0,0 +1,272 @@
+// Package session provides the methods necessary to build sessions
+package session
+
+import (
+ "fmt"
+
+ "go.mau.fi/libsignal/ecc"
+ "go.mau.fi/libsignal/keys/prekey"
+ "go.mau.fi/libsignal/logger"
+ "go.mau.fi/libsignal/protocol"
+ "go.mau.fi/libsignal/ratchet"
+ "go.mau.fi/libsignal/serialize"
+ "go.mau.fi/libsignal/signalerror"
+ "go.mau.fi/libsignal/state/record"
+ "go.mau.fi/libsignal/state/store"
+ "go.mau.fi/libsignal/util/medium"
+ "go.mau.fi/libsignal/util/optional"
+)
+
+// NewBuilder constructs a session builder.
+func NewBuilder(sessionStore store.Session, preKeyStore store.PreKey,
+ signedStore store.SignedPreKey, identityStore store.IdentityKey,
+ remoteAddress *protocol.SignalAddress, serializer *serialize.Serializer) *Builder {
+
+ builder := Builder{
+ sessionStore: sessionStore,
+ preKeyStore: preKeyStore,
+ signedPreKeyStore: signedStore,
+ identityKeyStore: identityStore,
+ remoteAddress: remoteAddress,
+ serializer: serializer,
+ }
+
+ return &builder
+}
+
+// NewBuilderFromSignal Store constructs a session builder using a
+// SignalProtocol Store.
+func NewBuilderFromSignal(signalStore store.SignalProtocol,
+ remoteAddress *protocol.SignalAddress, serializer *serialize.Serializer) *Builder {
+
+ builder := Builder{
+ sessionStore: signalStore,
+ preKeyStore: signalStore,
+ signedPreKeyStore: signalStore,
+ identityKeyStore: signalStore,
+ remoteAddress: remoteAddress,
+ serializer: serializer,
+ }
+
+ return &builder
+}
+
+// Builder is responsible for setting up encrypted sessions.
+// Once a session has been established, SessionCipher can be
+// used to encrypt/decrypt messages in that session.
+//
+// Sessions are built from one of three different vectors:
+// * PreKeyBundle retrieved from a server.
+// * PreKeySignalMessage received from a client.
+// * KeyExchangeMessage sent to or received from a client.
+//
+// Sessions are constructed per recipientId + deviceId tuple.
+// Remote logical users are identified by their recipientId,
+// and each logical recipientId can have multiple physical
+// devices.
+type Builder struct {
+ sessionStore store.Session
+ preKeyStore store.PreKey
+ signedPreKeyStore store.SignedPreKey
+ identityKeyStore store.IdentityKey
+ remoteAddress *protocol.SignalAddress
+ serializer *serialize.Serializer
+}
+
+// Process builds a new session from a session record and pre
+// key signal message.
+func (b *Builder) Process(sessionRecord *record.Session, message *protocol.PreKeySignalMessage) (unsignedPreKeyID *optional.Uint32, err error) {
+
+ // Check to see if the keys are trusted.
+ theirIdentityKey := message.IdentityKey()
+ if !(b.identityKeyStore.IsTrustedIdentity(b.remoteAddress, theirIdentityKey)) {
+ return nil, signalerror.ErrUntrustedIdentity
+ }
+
+ // Use version 3 of the signal/axolotl protocol.
+ unsignedPreKeyID, err = b.processV3(sessionRecord, message)
+ if err != nil {
+ return nil, err
+ }
+
+ // Save the identity key to our identity store.
+ b.identityKeyStore.SaveIdentity(b.remoteAddress, theirIdentityKey)
+
+ // Return the unsignedPreKeyID
+ return unsignedPreKeyID, nil
+}
+
+// ProcessV3 builds a new session from a session record and pre key
+// signal message. After a session is constructed in this way, the embedded
+// SignalMessage can be decrypted.
+func (b *Builder) processV3(sessionRecord *record.Session,
+ message *protocol.PreKeySignalMessage) (unsignedPreKeyID *optional.Uint32, err error) {
+
+ logger.Debug("Processing message with PreKeyID: ", message.PreKeyID())
+ // Check to see if we've already set up a session for this V3 message.
+ sessionExists := sessionRecord.HasSessionState(
+ message.MessageVersion(),
+ message.BaseKey().Serialize(),
+ )
+ if sessionExists {
+ logger.Debug("We've already setup a session for this V3 message, letting bundled message fall through...")
+ return optional.NewEmptyUint32(), nil
+ }
+
+ // Load our signed prekey from our signed prekey store.
+ ourSignedPreKeyRecord := b.signedPreKeyStore.LoadSignedPreKey(message.SignedPreKeyID())
+ if ourSignedPreKeyRecord == nil {
+ return nil, fmt.Errorf("%w with ID %d", signalerror.ErrNoSignedPreKey, message.SignedPreKeyID())
+ }
+ ourSignedPreKey := ourSignedPreKeyRecord.KeyPair()
+
+ // Build the parameters of the session.
+ parameters := ratchet.NewEmptyReceiverParameters()
+ parameters.SetTheirBaseKey(message.BaseKey())
+ parameters.SetTheirIdentityKey(message.IdentityKey())
+ parameters.SetOurIdentityKeyPair(b.identityKeyStore.GetIdentityKeyPair())
+ parameters.SetOurSignedPreKey(ourSignedPreKey)
+ parameters.SetOurRatchetKey(ourSignedPreKey)
+
+ // Set our one time pre key with the one from our prekey store
+ // if the message contains a valid pre key id
+ if !message.PreKeyID().IsEmpty {
+ oneTimePreKey := b.preKeyStore.LoadPreKey(message.PreKeyID().Value)
+ if oneTimePreKey == nil {
+ return nil, fmt.Errorf("%w with ID %d", signalerror.ErrNoOneTimeKeyFound, message.PreKeyID().Value)
+ }
+ parameters.SetOurOneTimePreKey(oneTimePreKey.KeyPair())
+ } else {
+ parameters.SetOurOneTimePreKey(nil)
+ }
+
+ // If this is a fresh record, archive our current state.
+ if !sessionRecord.IsFresh() {
+ sessionRecord.ArchiveCurrentState()
+ }
+
+ ///////// Initialize our session /////////
+ sessionState := sessionRecord.SessionState()
+ derivedKeys, sessionErr := ratchet.CalculateReceiverSession(parameters)
+ if sessionErr != nil {
+ return nil, sessionErr
+ }
+ sessionState.SetVersion(protocol.CurrentVersion)
+ sessionState.SetRemoteIdentityKey(parameters.TheirIdentityKey())
+ sessionState.SetLocalIdentityKey(parameters.OurIdentityKeyPair().PublicKey())
+ sessionState.SetSenderChain(parameters.OurRatchetKey(), derivedKeys.ChainKey)
+ sessionState.SetRootKey(derivedKeys.RootKey)
+
+ // Set the session's registration ids and base key
+ sessionState.SetLocalRegistrationID(b.identityKeyStore.GetLocalRegistrationId())
+ sessionState.SetRemoteRegistrationID(message.RegistrationID())
+ sessionState.SetSenderBaseKey(message.BaseKey().Serialize())
+
+ // Remove the PreKey from our store and return the message prekey id if it is valid.
+ if message.PreKeyID() != nil && message.PreKeyID().Value != medium.MaxValue {
+ return message.PreKeyID(), nil
+ }
+ return nil, nil
+}
+
+// ProcessBundle builds a new session from a PreKeyBundle retrieved
+// from a server.
+func (b *Builder) ProcessBundle(preKey *prekey.Bundle) error {
+ // Check to see if the keys are trusted.
+ if !(b.identityKeyStore.IsTrustedIdentity(b.remoteAddress, preKey.IdentityKey())) {
+ return signalerror.ErrUntrustedIdentity
+ }
+
+ // Check to see if the bundle has a signed pre key.
+ if preKey.SignedPreKey() == nil {
+ return signalerror.ErrNoSignedPreKey
+ }
+
+ // Verify the signature of the pre key
+ preKeyPublic := preKey.IdentityKey().PublicKey()
+ preKeyBytes := preKey.SignedPreKey().Serialize()
+ preKeySignature := preKey.SignedPreKeySignature()
+ if !ecc.VerifySignature(preKeyPublic, preKeyBytes, preKeySignature) {
+ return signalerror.ErrInvalidSignature
+ }
+
+ // Load our session and generate keys.
+ sessionRecord := b.sessionStore.LoadSession(b.remoteAddress)
+ ourBaseKey, err := ecc.GenerateKeyPair()
+ if err != nil {
+ return err
+ }
+ theirSignedPreKey := preKey.SignedPreKey()
+ theirOneTimePreKey := preKey.PreKey()
+ theirOneTimePreKeyID := preKey.PreKeyID()
+
+ // Build the parameters of the session
+ parameters := ratchet.NewEmptySenderParameters()
+ parameters.SetOurBaseKey(ourBaseKey)
+ parameters.SetOurIdentityKey(b.identityKeyStore.GetIdentityKeyPair())
+ parameters.SetTheirIdentityKey(preKey.IdentityKey())
+ parameters.SetTheirSignedPreKey(theirSignedPreKey)
+ parameters.SetTheirRatchetKey(theirSignedPreKey)
+ parameters.SetTheirOneTimePreKey(theirOneTimePreKey)
+
+ // If this is a fresh record, archive our current state.
+ if !sessionRecord.IsFresh() {
+ sessionRecord.ArchiveCurrentState()
+ }
+
+ ///////// Initialize our session /////////
+ sessionState := sessionRecord.SessionState()
+ derivedKeys, sessionErr := ratchet.CalculateSenderSession(parameters)
+ if sessionErr != nil {
+ return sessionErr
+ }
+ // Generate an ephemeral "ratchet" key that will be advertised to
+ // the receiving user.
+ sendingRatchetKey, keyErr := ecc.GenerateKeyPair()
+ if keyErr != nil {
+ return keyErr
+ }
+ sendingChain, chainErr := derivedKeys.RootKey.CreateChain(
+ parameters.TheirRatchetKey(),
+ sendingRatchetKey,
+ )
+ if chainErr != nil {
+ return chainErr
+ }
+
+ // Calculate the sender session.
+ sessionState.SetVersion(protocol.CurrentVersion)
+ sessionState.SetRemoteIdentityKey(parameters.TheirIdentityKey())
+ sessionState.SetLocalIdentityKey(parameters.OurIdentityKey().PublicKey())
+ sessionState.AddReceiverChain(parameters.TheirRatchetKey(), derivedKeys.ChainKey.Current())
+ sessionState.SetSenderChain(sendingRatchetKey, sendingChain.ChainKey)
+ sessionState.SetRootKey(sendingChain.RootKey)
+
+ // Update our session record with the unackowledged prekey message
+ sessionState.SetUnacknowledgedPreKeyMessage(
+ theirOneTimePreKeyID,
+ preKey.SignedPreKeyID(),
+ ourBaseKey.PublicKey(),
+ )
+
+ // Set the local registration ID based on the registration id in our identity key store.
+ sessionState.SetLocalRegistrationID(
+ b.identityKeyStore.GetLocalRegistrationId(),
+ )
+
+ // Set the remote registration ID based on the given prekey bundle registrationID.
+ sessionState.SetRemoteRegistrationID(
+ preKey.RegistrationID(),
+ )
+
+ // Set the sender base key in our session record state.
+ sessionState.SetSenderBaseKey(
+ ourBaseKey.PublicKey().Serialize(),
+ )
+
+ // Store the session in our session store and save the identity in our identity store.
+ b.sessionStore.StoreSession(b.remoteAddress, sessionRecord)
+ b.identityKeyStore.SaveIdentity(b.remoteAddress, preKey.IdentityKey())
+
+ return nil
+}