summaryrefslogtreecommitdiffstats
path: root/vendor/go.mau.fi/libsignal/groups
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/go.mau.fi/libsignal/groups')
-rw-r--r--vendor/go.mau.fi/libsignal/groups/GroupCipher.go141
-rw-r--r--vendor/go.mau.fi/libsignal/groups/GroupSessionBuilder.go84
-rw-r--r--vendor/go.mau.fi/libsignal/groups/ratchet/Doc.go3
-rw-r--r--vendor/go.mau.fi/libsignal/groups/ratchet/SenderChainKey.go68
-rw-r--r--vendor/go.mau.fi/libsignal/groups/ratchet/SenderMessageKey.go89
-rw-r--r--vendor/go.mau.fi/libsignal/groups/state/record/Doc.go2
-rw-r--r--vendor/go.mau.fi/libsignal/groups/state/record/SenderKeyRecord.go149
-rw-r--r--vendor/go.mau.fi/libsignal/groups/state/record/SenderKeyState.go186
-rw-r--r--vendor/go.mau.fi/libsignal/groups/state/store/Doc.go3
-rw-r--r--vendor/go.mau.fi/libsignal/groups/state/store/SenderKeyStore.go11
10 files changed, 736 insertions, 0 deletions
diff --git a/vendor/go.mau.fi/libsignal/groups/GroupCipher.go b/vendor/go.mau.fi/libsignal/groups/GroupCipher.go
new file mode 100644
index 00000000..b821f3c3
--- /dev/null
+++ b/vendor/go.mau.fi/libsignal/groups/GroupCipher.go
@@ -0,0 +1,141 @@
+package groups
+
+import (
+ "fmt"
+
+ "go.mau.fi/libsignal/cipher"
+ "go.mau.fi/libsignal/ecc"
+ "go.mau.fi/libsignal/groups/ratchet"
+ "go.mau.fi/libsignal/groups/state/record"
+ "go.mau.fi/libsignal/groups/state/store"
+ "go.mau.fi/libsignal/protocol"
+ "go.mau.fi/libsignal/signalerror"
+)
+
+// NewGroupCipher will return a new group message cipher that can be used for
+// encrypt/decrypt operations.
+func NewGroupCipher(builder *SessionBuilder, senderKeyID *protocol.SenderKeyName,
+ senderKeyStore store.SenderKey) *GroupCipher {
+
+ return &GroupCipher{
+ senderKeyID: senderKeyID,
+ senderKeyStore: senderKeyStore,
+ sessionBuilder: builder,
+ }
+}
+
+// GroupCipher is the main entry point for group encrypt/decrypt operations.
+// Once a session has been established, this can be used for
+// all encrypt/decrypt operations within that session.
+type GroupCipher struct {
+ senderKeyID *protocol.SenderKeyName
+ senderKeyStore store.SenderKey
+ sessionBuilder *SessionBuilder
+}
+
+// Encrypt will take the given message in bytes and return encrypted bytes.
+func (c *GroupCipher) Encrypt(plaintext []byte) (protocol.GroupCiphertextMessage, error) {
+ // Load the sender key based on id from our store.
+ keyRecord := c.senderKeyStore.LoadSenderKey(c.senderKeyID)
+ senderKeyState, err := keyRecord.SenderKeyState()
+ if err != nil {
+ return nil, err
+ }
+
+ // Get the message key from the senderkey state.
+ senderKey, err := senderKeyState.SenderChainKey().SenderMessageKey()
+ if err != nil {
+ return nil, err
+ }
+
+ // Encrypt the plaintext.
+ ciphertext, err := cipher.EncryptCbc(senderKey.Iv(), senderKey.CipherKey(), plaintext)
+ if err != nil {
+ return nil, err
+ }
+
+ senderKeyMessage := protocol.NewSenderKeyMessage(
+ senderKeyState.KeyID(),
+ senderKey.Iteration(),
+ ciphertext,
+ senderKeyState.SigningKey().PrivateKey(),
+ c.sessionBuilder.serializer.SenderKeyMessage,
+ )
+
+ senderKeyState.SetSenderChainKey(senderKeyState.SenderChainKey().Next())
+ c.senderKeyStore.StoreSenderKey(c.senderKeyID, keyRecord)
+
+ return senderKeyMessage, nil
+}
+
+// Decrypt decrypts the given message using an existing session that
+// is stored in the senderKey store.
+func (c *GroupCipher) Decrypt(senderKeyMessage *protocol.SenderKeyMessage) ([]byte, error) {
+ keyRecord := c.senderKeyStore.LoadSenderKey(c.senderKeyID)
+
+ if keyRecord.IsEmpty() {
+ return nil, fmt.Errorf("%w for %s in %s", signalerror.ErrNoSenderKeyForUser, c.senderKeyID.Sender().String(), c.senderKeyID.GroupID())
+ }
+
+ // Get the senderkey state by id.
+ senderKeyState, err := keyRecord.GetSenderKeyStateByID(senderKeyMessage.KeyID())
+ if err != nil {
+ return nil, err
+ }
+
+ // Verify the signature of the senderkey message.
+ verified := c.verifySignature(senderKeyState.SigningKey().PublicKey(), senderKeyMessage)
+ if !verified {
+ return nil, signalerror.ErrSenderKeyStateVerificationFailed
+ }
+
+ senderKey, err := c.getSenderKey(senderKeyState, senderKeyMessage.Iteration())
+ if err != nil {
+ return nil, err
+ }
+
+ // Decrypt the message ciphertext.
+ plaintext, err := cipher.DecryptCbc(senderKey.Iv(), senderKey.CipherKey(), senderKeyMessage.Ciphertext())
+ if err != nil {
+ return nil, err
+ }
+
+ // Store the sender key by id.
+ c.senderKeyStore.StoreSenderKey(c.senderKeyID, keyRecord)
+
+ return plaintext, nil
+}
+
+// verifySignature will verify the signature of the senderkey message with
+// the given public key.
+func (c *GroupCipher) verifySignature(signingPubKey ecc.ECPublicKeyable,
+ senderKeyMessage *protocol.SenderKeyMessage) bool {
+
+ return ecc.VerifySignature(signingPubKey, senderKeyMessage.Serialize(), senderKeyMessage.Signature())
+}
+
+func (c *GroupCipher) getSenderKey(senderKeyState *record.SenderKeyState, iteration uint32) (*ratchet.SenderMessageKey, error) {
+ senderChainKey := senderKeyState.SenderChainKey()
+ if senderChainKey.Iteration() > iteration {
+ if senderKeyState.HasSenderMessageKey(iteration) {
+ return senderKeyState.RemoveSenderMessageKey(iteration), nil
+ }
+ return nil, fmt.Errorf("%w (current: %d, received: %d)", signalerror.ErrOldCounter, senderChainKey.Iteration(), iteration)
+ }
+
+ if iteration-senderChainKey.Iteration() > 2000 {
+ return nil, signalerror.ErrTooFarIntoFuture
+ }
+
+ for senderChainKey.Iteration() < iteration {
+ senderMessageKey, err := senderChainKey.SenderMessageKey()
+ if err != nil {
+ return nil, err
+ }
+ senderKeyState.AddSenderMessageKey(senderMessageKey)
+ senderChainKey = senderChainKey.Next()
+ }
+
+ senderKeyState.SetSenderChainKey(senderChainKey.Next())
+ return senderChainKey.SenderMessageKey()
+}
diff --git a/vendor/go.mau.fi/libsignal/groups/GroupSessionBuilder.go b/vendor/go.mau.fi/libsignal/groups/GroupSessionBuilder.go
new file mode 100644
index 00000000..2a5569b7
--- /dev/null
+++ b/vendor/go.mau.fi/libsignal/groups/GroupSessionBuilder.go
@@ -0,0 +1,84 @@
+// Package groups is responsible for setting up group SenderKey encrypted sessions.
+// Once a session has been established, GroupCipher can be used to encrypt/decrypt
+// messages in that session.
+//
+// The built sessions are unidirectional: they can be used either for sending or
+// for receiving, but not both. Sessions are constructed per (groupId + senderId +
+// deviceId) tuple. Remote logical users are identified by their senderId, and each
+// logical recipientId can have multiple physical devices.
+package groups
+
+import (
+ "go.mau.fi/libsignal/groups/state/record"
+ "go.mau.fi/libsignal/groups/state/store"
+ "go.mau.fi/libsignal/protocol"
+ "go.mau.fi/libsignal/serialize"
+ "go.mau.fi/libsignal/util/keyhelper"
+)
+
+// NewGroupSessionBuilder will return a new group session builder.
+func NewGroupSessionBuilder(senderKeyStore store.SenderKey,
+ serializer *serialize.Serializer) *SessionBuilder {
+
+ return &SessionBuilder{
+ senderKeyStore: senderKeyStore,
+ serializer: serializer,
+ }
+}
+
+// SessionBuilder is a structure for building group sessions.
+type SessionBuilder struct {
+ senderKeyStore store.SenderKey
+ serializer *serialize.Serializer
+}
+
+// Process will process an incoming group message and set up the corresponding
+// session for it.
+func (b *SessionBuilder) Process(senderKeyName *protocol.SenderKeyName,
+ msg *protocol.SenderKeyDistributionMessage) {
+
+ senderKeyRecord := b.senderKeyStore.LoadSenderKey(senderKeyName)
+ if senderKeyRecord == nil {
+ senderKeyRecord = record.NewSenderKey(b.serializer.SenderKeyRecord, b.serializer.SenderKeyState)
+ }
+ senderKeyRecord.AddSenderKeyState(msg.ID(), msg.Iteration(), msg.ChainKey(), msg.SignatureKey())
+ b.senderKeyStore.StoreSenderKey(senderKeyName, senderKeyRecord)
+}
+
+// Create will create a new group session for the given name.
+func (b *SessionBuilder) Create(senderKeyName *protocol.SenderKeyName) (*protocol.SenderKeyDistributionMessage, error) {
+ // Load the senderkey by name
+ senderKeyRecord := b.senderKeyStore.LoadSenderKey(senderKeyName)
+
+ // If the record is empty, generate new keys.
+ if senderKeyRecord == nil || senderKeyRecord.IsEmpty() {
+ senderKeyRecord = record.NewSenderKey(b.serializer.SenderKeyRecord, b.serializer.SenderKeyState)
+ signingKey, err := keyhelper.GenerateSenderSigningKey()
+ if err != nil {
+ return nil, err
+ }
+ senderKeyRecord.SetSenderKeyState(
+ keyhelper.GenerateSenderKeyID(), 0,
+ keyhelper.GenerateSenderKey(),
+ signingKey,
+ )
+ b.senderKeyStore.StoreSenderKey(senderKeyName, senderKeyRecord)
+ }
+
+ // Get the senderkey state.
+ state, err := senderKeyRecord.SenderKeyState()
+ if err != nil {
+ return nil, err
+ }
+
+ // Create the group message to return.
+ senderKeyDistributionMessage := protocol.NewSenderKeyDistributionMessage(
+ state.KeyID(),
+ state.SenderChainKey().Iteration(),
+ state.SenderChainKey().Seed(),
+ state.SigningKey().PublicKey(),
+ b.serializer.SenderKeyDistributionMessage,
+ )
+
+ return senderKeyDistributionMessage, nil
+}
diff --git a/vendor/go.mau.fi/libsignal/groups/ratchet/Doc.go b/vendor/go.mau.fi/libsignal/groups/ratchet/Doc.go
new file mode 100644
index 00000000..6d374465
--- /dev/null
+++ b/vendor/go.mau.fi/libsignal/groups/ratchet/Doc.go
@@ -0,0 +1,3 @@
+// Package ratchet provides the methods necessary to establish a ratchet
+// session for group messaging.
+package ratchet
diff --git a/vendor/go.mau.fi/libsignal/groups/ratchet/SenderChainKey.go b/vendor/go.mau.fi/libsignal/groups/ratchet/SenderChainKey.go
new file mode 100644
index 00000000..a9530143
--- /dev/null
+++ b/vendor/go.mau.fi/libsignal/groups/ratchet/SenderChainKey.go
@@ -0,0 +1,68 @@
+package ratchet
+
+import (
+ "crypto/hmac"
+ "crypto/sha256"
+)
+
+var messageKeySeed = []byte{0x01}
+var chainKeySeed = []byte{0x02}
+
+// NewSenderChainKey will return a new SenderChainKey.
+func NewSenderChainKey(iteration uint32, chainKey []byte) *SenderChainKey {
+ return &SenderChainKey{
+ iteration: iteration,
+ chainKey: chainKey,
+ }
+}
+
+// NewSenderChainKeyFromStruct will return a new chain key object from the
+// given serializeable structure.
+func NewSenderChainKeyFromStruct(structure *SenderChainKeyStructure) *SenderChainKey {
+ return &SenderChainKey{
+ iteration: structure.Iteration,
+ chainKey: structure.ChainKey,
+ }
+}
+
+// NewStructFromSenderChainKeys returns a serializeable structure of chain keys.
+func NewStructFromSenderChainKey(key *SenderChainKey) *SenderChainKeyStructure {
+ return &SenderChainKeyStructure{
+ Iteration: key.iteration,
+ ChainKey: key.chainKey,
+ }
+}
+
+// SenderChainKeyStructure is a serializeable structure of SenderChainKeys.
+type SenderChainKeyStructure struct {
+ Iteration uint32
+ ChainKey []byte
+}
+
+type SenderChainKey struct {
+ iteration uint32
+ chainKey []byte
+}
+
+func (k *SenderChainKey) Iteration() uint32 {
+ return k.iteration
+}
+
+func (k *SenderChainKey) SenderMessageKey() (*SenderMessageKey, error) {
+ return NewSenderMessageKey(k.iteration, k.getDerivative(messageKeySeed, k.chainKey))
+}
+
+func (k *SenderChainKey) Next() *SenderChainKey {
+ return NewSenderChainKey(k.iteration+1, k.getDerivative(chainKeySeed, k.chainKey))
+}
+
+func (k *SenderChainKey) Seed() []byte {
+ return k.chainKey
+}
+
+func (k *SenderChainKey) getDerivative(seed []byte, key []byte) []byte {
+ mac := hmac.New(sha256.New, key[:])
+ mac.Write(seed)
+
+ return mac.Sum(nil)
+}
diff --git a/vendor/go.mau.fi/libsignal/groups/ratchet/SenderMessageKey.go b/vendor/go.mau.fi/libsignal/groups/ratchet/SenderMessageKey.go
new file mode 100644
index 00000000..724059f2
--- /dev/null
+++ b/vendor/go.mau.fi/libsignal/groups/ratchet/SenderMessageKey.go
@@ -0,0 +1,89 @@
+package ratchet
+
+import (
+ "go.mau.fi/libsignal/kdf"
+ "go.mau.fi/libsignal/util/bytehelper"
+)
+
+// KdfInfo is optional bytes to include in deriving secrets with KDF.
+const KdfInfo string = "WhisperGroup"
+
+// NewSenderMessageKey will return a new sender message key using the given
+// iteration and seed.
+func NewSenderMessageKey(iteration uint32, seed []byte) (*SenderMessageKey, error) {
+ derivative, err := kdf.DeriveSecrets(seed, nil, []byte(KdfInfo), 48)
+ if err != nil {
+ return nil, err
+ }
+
+ // Split our derived secrets into 2 parts
+ parts := bytehelper.Split(derivative, 16, 32)
+
+ // Build the message key.
+ senderKeyMessage := &SenderMessageKey{
+ iteration: iteration,
+ seed: seed,
+ iv: parts[0],
+ cipherKey: parts[1],
+ }
+
+ return senderKeyMessage, nil
+}
+
+// NewSenderMessageKeyFromStruct will return a new message key object from the
+// given serializeable structure.
+func NewSenderMessageKeyFromStruct(structure *SenderMessageKeyStructure) *SenderMessageKey {
+ return &SenderMessageKey{
+ iteration: structure.Iteration,
+ iv: structure.IV,
+ cipherKey: structure.CipherKey,
+ seed: structure.Seed,
+ }
+}
+
+// NewStructFromSenderMessageKey returns a serializeable structure of message keys.
+func NewStructFromSenderMessageKey(key *SenderMessageKey) *SenderMessageKeyStructure {
+ return &SenderMessageKeyStructure{
+ CipherKey: key.cipherKey,
+ Iteration: key.iteration,
+ IV: key.iv,
+ Seed: key.seed,
+ }
+}
+
+// SenderMessageKeyStructure is a serializeable structure of SenderMessageKeys.
+type SenderMessageKeyStructure struct {
+ Iteration uint32
+ IV []byte
+ CipherKey []byte
+ Seed []byte
+}
+
+// SenderMessageKey is a structure for sender message keys used in group
+// messaging.
+type SenderMessageKey struct {
+ iteration uint32
+ iv []byte
+ cipherKey []byte
+ seed []byte
+}
+
+// Iteration will return the sender message key's iteration.
+func (k *SenderMessageKey) Iteration() uint32 {
+ return k.iteration
+}
+
+// Iv will return the sender message key's initialization vector.
+func (k *SenderMessageKey) Iv() []byte {
+ return k.iv
+}
+
+// CipherKey will return the key in bytes.
+func (k *SenderMessageKey) CipherKey() []byte {
+ return k.cipherKey
+}
+
+// Seed will return the sender message key's seed.
+func (k *SenderMessageKey) Seed() []byte {
+ return k.seed
+}
diff --git a/vendor/go.mau.fi/libsignal/groups/state/record/Doc.go b/vendor/go.mau.fi/libsignal/groups/state/record/Doc.go
new file mode 100644
index 00000000..5a7d7307
--- /dev/null
+++ b/vendor/go.mau.fi/libsignal/groups/state/record/Doc.go
@@ -0,0 +1,2 @@
+// Package record provides the state and record of a group session.
+package record
diff --git a/vendor/go.mau.fi/libsignal/groups/state/record/SenderKeyRecord.go b/vendor/go.mau.fi/libsignal/groups/state/record/SenderKeyRecord.go
new file mode 100644
index 00000000..64d59068
--- /dev/null
+++ b/vendor/go.mau.fi/libsignal/groups/state/record/SenderKeyRecord.go
@@ -0,0 +1,149 @@
+package record
+
+import (
+ "fmt"
+
+ "go.mau.fi/libsignal/ecc"
+ "go.mau.fi/libsignal/signalerror"
+)
+
+const maxStates = 5
+
+// SenderKeySerializer is an interface for serializing and deserializing
+// SenderKey objects into bytes. An implementation of this interface should be
+// used to encode/decode the object into JSON, Protobuffers, etc.
+type SenderKeySerializer interface {
+ Serialize(preKey *SenderKeyStructure) []byte
+ Deserialize(serialized []byte) (*SenderKeyStructure, error)
+}
+
+// NewSenderKeyFromBytes will return a prekey record from the given bytes using the given serializer.
+func NewSenderKeyFromBytes(serialized []byte, serializer SenderKeySerializer,
+ stateSerializer SenderKeyStateSerializer) (*SenderKey, error) {
+
+ // Use the given serializer to decode the senderkey record
+ senderKeyStructure, err := serializer.Deserialize(serialized)
+ if err != nil {
+ return nil, err
+ }
+
+ return NewSenderKeyFromStruct(senderKeyStructure, serializer, stateSerializer)
+}
+
+// NewSenderKeyFromStruct returns a SenderKey record using the given serializable structure.
+func NewSenderKeyFromStruct(structure *SenderKeyStructure, serializer SenderKeySerializer,
+ stateSerializer SenderKeyStateSerializer) (*SenderKey, error) {
+
+ // Build our sender key states from structure.
+ senderKeyStates := make([]*SenderKeyState, len(structure.SenderKeyStates))
+ for i := range structure.SenderKeyStates {
+ var err error
+ senderKeyStates[i], err = NewSenderKeyStateFromStructure(structure.SenderKeyStates[i], stateSerializer)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ // Build and return our session.
+ senderKey := &SenderKey{
+ senderKeyStates: senderKeyStates,
+ serializer: serializer,
+ }
+
+ return senderKey, nil
+
+}
+
+// NewSenderKey record returns a new sender key record that can
+// be stored in a SenderKeyStore.
+func NewSenderKey(serializer SenderKeySerializer,
+ stateSerializer SenderKeyStateSerializer) *SenderKey {
+
+ return &SenderKey{
+ senderKeyStates: []*SenderKeyState{},
+ serializer: serializer,
+ stateSerializer: stateSerializer,
+ }
+}
+
+// SenderKeyStructure is a structure for serializing SenderKey records.
+type SenderKeyStructure struct {
+ SenderKeyStates []*SenderKeyStateStructure
+}
+
+// SenderKey record is a structure for storing pre keys inside
+// a SenderKeyStore.
+type SenderKey struct {
+ senderKeyStates []*SenderKeyState
+ serializer SenderKeySerializer
+ stateSerializer SenderKeyStateSerializer
+}
+
+// SenderKeyState will return the first sender key state in the record's
+// list of sender key states.
+func (k *SenderKey) SenderKeyState() (*SenderKeyState, error) {
+ if len(k.senderKeyStates) > 0 {
+ return k.senderKeyStates[0], nil
+ }
+ return nil, signalerror.ErrNoSenderKeyStatesInRecord
+}
+
+// GetSenderKeyStateByID will return the sender key state with the given
+// key id.
+func (k *SenderKey) GetSenderKeyStateByID(keyID uint32) (*SenderKeyState, error) {
+ for i := 0; i < len(k.senderKeyStates); i++ {
+ if k.senderKeyStates[i].KeyID() == keyID {
+ return k.senderKeyStates[i], nil
+ }
+ }
+
+ return nil, fmt.Errorf("%w %d", signalerror.ErrNoSenderKeyStateForID, keyID)
+}
+
+// IsEmpty will return false if there is more than one state in this
+// senderkey record.
+func (k *SenderKey) IsEmpty() bool {
+ return len(k.senderKeyStates) == 0
+}
+
+// AddSenderKeyState will add a new state to this senderkey record with the given
+// id, iteration, chainkey, and signature key.
+func (k *SenderKey) AddSenderKeyState(id uint32, iteration uint32,
+ chainKey []byte, signatureKey ecc.ECPublicKeyable) {
+
+ newState := NewSenderKeyStateFromPublicKey(id, iteration, chainKey, signatureKey, k.stateSerializer)
+ k.senderKeyStates = append([]*SenderKeyState{newState}, k.senderKeyStates...)
+
+ if len(k.senderKeyStates) > maxStates {
+ k.senderKeyStates = k.senderKeyStates[:len(k.senderKeyStates)-1]
+ }
+}
+
+// SetSenderKeyState will replace the current senderkey states with the given
+// senderkey state.
+func (k *SenderKey) SetSenderKeyState(id uint32, iteration uint32,
+ chainKey []byte, signatureKey *ecc.ECKeyPair) {
+
+ newState := NewSenderKeyState(id, iteration, chainKey, signatureKey, k.stateSerializer)
+ k.senderKeyStates = make([]*SenderKeyState, 0, maxStates/2)
+ k.senderKeyStates = append(k.senderKeyStates, newState)
+}
+
+// Serialize will return the record as serialized bytes so it can be
+// persistently stored.
+func (k *SenderKey) Serialize() []byte {
+ return k.serializer.Serialize(k.Structure())
+}
+
+// Structure will return a simple serializable record structure.
+// This is used for serialization to persistently
+// store a session record.
+func (k *SenderKey) Structure() *SenderKeyStructure {
+ senderKeyStates := make([]*SenderKeyStateStructure, len(k.senderKeyStates))
+ for i := range k.senderKeyStates {
+ senderKeyStates[i] = k.senderKeyStates[i].structure()
+ }
+ return &SenderKeyStructure{
+ SenderKeyStates: senderKeyStates,
+ }
+}
diff --git a/vendor/go.mau.fi/libsignal/groups/state/record/SenderKeyState.go b/vendor/go.mau.fi/libsignal/groups/state/record/SenderKeyState.go
new file mode 100644
index 00000000..e3187c30
--- /dev/null
+++ b/vendor/go.mau.fi/libsignal/groups/state/record/SenderKeyState.go
@@ -0,0 +1,186 @@
+package record
+
+import (
+ "go.mau.fi/libsignal/ecc"
+ "go.mau.fi/libsignal/groups/ratchet"
+ "go.mau.fi/libsignal/util/bytehelper"
+)
+
+const maxMessageKeys = 2000
+
+// SenderKeyStateSerializer 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 SenderKeyStateSerializer interface {
+ Serialize(state *SenderKeyStateStructure) []byte
+ Deserialize(serialized []byte) (*SenderKeyStateStructure, error)
+}
+
+// NewSenderKeyStateFromBytes will return a Signal State from the given
+// bytes using the given serializer.
+func NewSenderKeyStateFromBytes(serialized []byte, serializer SenderKeyStateSerializer) (*SenderKeyState, error) {
+ // Use the given serializer to decode the signal message.
+ stateStructure, err := serializer.Deserialize(serialized)
+ if err != nil {
+ return nil, err
+ }
+
+ return NewSenderKeyStateFromStructure(stateStructure, serializer)
+}
+
+// NewSenderKeyState returns a new SenderKeyState.
+func NewSenderKeyState(keyID uint32, iteration uint32, chainKey []byte,
+ signatureKey *ecc.ECKeyPair, serializer SenderKeyStateSerializer) *SenderKeyState {
+
+ return &SenderKeyState{
+ keys: make([]*ratchet.SenderMessageKey, 0, maxMessageKeys/2),
+ keyID: keyID,
+ senderChainKey: ratchet.NewSenderChainKey(iteration, chainKey),
+ signingKeyPair: signatureKey,
+ serializer: serializer,
+ }
+}
+
+// NewSenderKeyStateFromPublicKey returns a new SenderKeyState with the given publicKey.
+func NewSenderKeyStateFromPublicKey(keyID uint32, iteration uint32, chainKey []byte,
+ signatureKey ecc.ECPublicKeyable, serializer SenderKeyStateSerializer) *SenderKeyState {
+
+ keyPair := ecc.NewECKeyPair(signatureKey, nil)
+
+ return &SenderKeyState{
+ keys: make([]*ratchet.SenderMessageKey, 0, maxMessageKeys/2),
+ keyID: keyID,
+ senderChainKey: ratchet.NewSenderChainKey(iteration, chainKey),
+ signingKeyPair: keyPair,
+ serializer: serializer,
+ }
+}
+
+// NewSenderKeyStateFromStructure will return a new session state with the
+// given state structure. This structure is given back from an
+// implementation of the sender key state serializer.
+func NewSenderKeyStateFromStructure(structure *SenderKeyStateStructure,
+ serializer SenderKeyStateSerializer) (*SenderKeyState, error) {
+
+ // Convert our ecc keys from bytes into object form.
+ signingKeyPublic, err := ecc.DecodePoint(structure.SigningKeyPublic, 0)
+ if err != nil {
+ return nil, err
+ }
+ signingKeyPrivate := ecc.NewDjbECPrivateKey(bytehelper.SliceToArray(structure.SigningKeyPrivate))
+
+ // Build our sender message keys from structure
+ senderMessageKeys := make([]*ratchet.SenderMessageKey, len(structure.Keys))
+ for i := range structure.Keys {
+ senderMessageKeys[i] = ratchet.NewSenderMessageKeyFromStruct(structure.Keys[i])
+ }
+
+ // Build our state object.
+ state := &SenderKeyState{
+ keys: senderMessageKeys,
+ keyID: structure.KeyID,
+ senderChainKey: ratchet.NewSenderChainKeyFromStruct(structure.SenderChainKey),
+ signingKeyPair: ecc.NewECKeyPair(signingKeyPublic, signingKeyPrivate),
+ serializer: serializer,
+ }
+
+ return state, nil
+}
+
+// SenderKeyStateStructure is a serializeable structure of SenderKeyState.
+type SenderKeyStateStructure struct {
+ Keys []*ratchet.SenderMessageKeyStructure
+ KeyID uint32
+ SenderChainKey *ratchet.SenderChainKeyStructure
+ SigningKeyPrivate []byte
+ SigningKeyPublic []byte
+}
+
+// SenderKeyState is a structure for maintaining a senderkey session state.
+type SenderKeyState struct {
+ keys []*ratchet.SenderMessageKey
+ keyID uint32
+ senderChainKey *ratchet.SenderChainKey
+ signingKeyPair *ecc.ECKeyPair
+ serializer SenderKeyStateSerializer
+}
+
+// SigningKey returns the signing key pair of the sender key state.
+func (k *SenderKeyState) SigningKey() *ecc.ECKeyPair {
+ return k.signingKeyPair
+}
+
+// SenderChainKey returns the sender chain key of the state.
+func (k *SenderKeyState) SenderChainKey() *ratchet.SenderChainKey {
+ return k.senderChainKey
+}
+
+// KeyID returns the state's key id.
+func (k *SenderKeyState) KeyID() uint32 {
+ return k.keyID
+}
+
+// HasSenderMessageKey will return true if the state has a key with the
+// given iteration.
+func (k *SenderKeyState) HasSenderMessageKey(iteration uint32) bool {
+ for i := 0; i < len(k.keys); i++ {
+ if k.keys[i].Iteration() == iteration {
+ return true
+ }
+ }
+ return false
+}
+
+// AddSenderMessageKey will add the given sender message key to the state.
+func (k *SenderKeyState) AddSenderMessageKey(senderMsgKey *ratchet.SenderMessageKey) {
+ k.keys = append(k.keys, senderMsgKey)
+
+ if len(k.keys) > maxMessageKeys {
+ k.keys = k.keys[1:]
+ }
+}
+
+// SetSenderChainKey will set the state's sender chain key with the given key.
+func (k *SenderKeyState) SetSenderChainKey(senderChainKey *ratchet.SenderChainKey) {
+ k.senderChainKey = senderChainKey
+}
+
+// RemoveSenderMessageKey will remove the key in this state with the given iteration number.
+func (k *SenderKeyState) RemoveSenderMessageKey(iteration uint32) *ratchet.SenderMessageKey {
+ for i := 0; i < len(k.keys); i++ {
+ if k.keys[i].Iteration() == iteration {
+ removed := k.keys[i]
+ k.keys = append(k.keys[0:i], k.keys[i+1:]...)
+ return removed
+ }
+ }
+
+ return nil
+}
+
+// Serialize will return the state as bytes using the given serializer.
+func (k *SenderKeyState) Serialize() []byte {
+ return k.serializer.Serialize(k.structure())
+}
+
+// structure will return a serializable structure of the
+// the given state so it can be persistently stored.
+func (k *SenderKeyState) structure() *SenderKeyStateStructure {
+ // Convert our sender message keys into a serializeable structure
+ keys := make([]*ratchet.SenderMessageKeyStructure, len(k.keys))
+ for i := range k.keys {
+ keys[i] = ratchet.NewStructFromSenderMessageKey(k.keys[i])
+ }
+
+ // Build and return our state structure.
+ s := &SenderKeyStateStructure{
+ Keys: keys,
+ KeyID: k.keyID,
+ SenderChainKey: ratchet.NewStructFromSenderChainKey(k.senderChainKey),
+ SigningKeyPublic: k.signingKeyPair.PublicKey().Serialize(),
+ }
+ if k.signingKeyPair.PrivateKey() != nil {
+ s.SigningKeyPrivate = bytehelper.ArrayToSlice(k.signingKeyPair.PrivateKey().Serialize())
+ }
+ return s
+}
diff --git a/vendor/go.mau.fi/libsignal/groups/state/store/Doc.go b/vendor/go.mau.fi/libsignal/groups/state/store/Doc.go
new file mode 100644
index 00000000..8a23b446
--- /dev/null
+++ b/vendor/go.mau.fi/libsignal/groups/state/store/Doc.go
@@ -0,0 +1,3 @@
+// Package store provides the storage interfaces for storing group sender
+// key records.
+package store
diff --git a/vendor/go.mau.fi/libsignal/groups/state/store/SenderKeyStore.go b/vendor/go.mau.fi/libsignal/groups/state/store/SenderKeyStore.go
new file mode 100644
index 00000000..a068df7c
--- /dev/null
+++ b/vendor/go.mau.fi/libsignal/groups/state/store/SenderKeyStore.go
@@ -0,0 +1,11 @@
+package store
+
+import (
+ "go.mau.fi/libsignal/groups/state/record"
+ "go.mau.fi/libsignal/protocol"
+)
+
+type SenderKey interface {
+ StoreSenderKey(senderKeyName *protocol.SenderKeyName, keyRecord *record.SenderKey)
+ LoadSenderKey(senderKeyName *protocol.SenderKeyName) *record.SenderKey
+}