refactor to mautrix 0.17.x; update deps

This commit is contained in:
Aine
2024-02-11 20:47:04 +02:00
parent 0a9701f4c9
commit dd0ad4c245
237 changed files with 9091 additions and 3317 deletions

258
vendor/maunium.net/go/mautrix/crypto/goolm/olm/chain.go generated vendored Normal file
View File

@@ -0,0 +1,258 @@
package olm
import (
"fmt"
"maunium.net/go/mautrix/crypto/goolm"
"maunium.net/go/mautrix/crypto/goolm/crypto"
"maunium.net/go/mautrix/crypto/goolm/libolmpickle"
)
const (
chainKeySeed = 0x02
messageKeyLength = 32
)
// chainKey wraps the index and the public key
type chainKey struct {
Index uint32 `json:"index"`
Key crypto.Curve25519PublicKey `json:"key"`
}
// advance advances the chain
func (c *chainKey) advance() {
c.Key = crypto.HMACSHA256(c.Key, []byte{chainKeySeed})
c.Index++
}
// UnpickleLibOlm decodes the unencryted value and populates the chain key accordingly. It returns the number of bytes read.
func (r *chainKey) UnpickleLibOlm(value []byte) (int, error) {
curPos := 0
readBytes, err := r.Key.UnpickleLibOlm(value)
if err != nil {
return 0, err
}
curPos += readBytes
r.Index, readBytes, err = libolmpickle.UnpickleUInt32(value[curPos:])
if err != nil {
return 0, err
}
curPos += readBytes
return curPos, nil
}
// PickleLibOlm encodes the chain key into target. target has to have a size of at least PickleLen() and is written to from index 0.
// It returns the number of bytes written.
func (r chainKey) PickleLibOlm(target []byte) (int, error) {
if len(target) < r.PickleLen() {
return 0, fmt.Errorf("pickle chain key: %w", goolm.ErrValueTooShort)
}
written, err := r.Key.PickleLibOlm(target)
if err != nil {
return 0, fmt.Errorf("pickle chain key: %w", err)
}
written += libolmpickle.PickleUInt32(r.Index, target[written:])
return written, nil
}
// PickleLen returns the number of bytes the pickled chain key will have.
func (r chainKey) PickleLen() int {
length := r.Key.PickleLen()
length += libolmpickle.PickleUInt32Len(r.Index)
return length
}
// senderChain is a chain for sending messages
type senderChain struct {
RKey crypto.Curve25519KeyPair `json:"ratchet_key"`
CKey chainKey `json:"chain_key"`
IsSet bool `json:"set"`
}
// newSenderChain returns a sender chain initialized with chainKey and ratchet key pair.
func newSenderChain(key crypto.Curve25519PublicKey, ratchet crypto.Curve25519KeyPair) *senderChain {
return &senderChain{
RKey: ratchet,
CKey: chainKey{
Index: 0,
Key: key,
},
IsSet: true,
}
}
// advance advances the chain
func (s *senderChain) advance() {
s.CKey.advance()
}
// ratchetKey returns the ratchet key pair.
func (s senderChain) ratchetKey() crypto.Curve25519KeyPair {
return s.RKey
}
// chainKey returns the current chainKey.
func (s senderChain) chainKey() chainKey {
return s.CKey
}
// UnpickleLibOlm decodes the unencryted value and populates the chain accordingly. It returns the number of bytes read.
func (r *senderChain) UnpickleLibOlm(value []byte) (int, error) {
curPos := 0
readBytes, err := r.RKey.UnpickleLibOlm(value)
if err != nil {
return 0, err
}
curPos += readBytes
readBytes, err = r.CKey.UnpickleLibOlm(value[curPos:])
if err != nil {
return 0, err
}
curPos += readBytes
return curPos, nil
}
// PickleLibOlm encodes the chain into target. target has to have a size of at least PickleLen() and is written to from index 0.
// It returns the number of bytes written.
func (r senderChain) PickleLibOlm(target []byte) (int, error) {
if len(target) < r.PickleLen() {
return 0, fmt.Errorf("pickle sender chain: %w", goolm.ErrValueTooShort)
}
written, err := r.RKey.PickleLibOlm(target)
if err != nil {
return 0, fmt.Errorf("pickle sender chain: %w", err)
}
writtenChain, err := r.CKey.PickleLibOlm(target[written:])
if err != nil {
return 0, fmt.Errorf("pickle sender chain: %w", err)
}
written += writtenChain
return written, nil
}
// PickleLen returns the number of bytes the pickled chain will have.
func (r senderChain) PickleLen() int {
length := r.RKey.PickleLen()
length += r.CKey.PickleLen()
return length
}
// senderChain is a chain for receiving messages
type receiverChain struct {
RKey crypto.Curve25519PublicKey `json:"ratchet_key"`
CKey chainKey `json:"chain_key"`
}
// newReceiverChain returns a receiver chain initialized with chainKey and ratchet public key.
func newReceiverChain(chain crypto.Curve25519PublicKey, ratchet crypto.Curve25519PublicKey) *receiverChain {
return &receiverChain{
RKey: ratchet,
CKey: chainKey{
Index: 0,
Key: chain,
},
}
}
// advance advances the chain
func (s *receiverChain) advance() {
s.CKey.advance()
}
// ratchetKey returns the ratchet public key.
func (s receiverChain) ratchetKey() crypto.Curve25519PublicKey {
return s.RKey
}
// chainKey returns the current chainKey.
func (s receiverChain) chainKey() chainKey {
return s.CKey
}
// UnpickleLibOlm decodes the unencryted value and populates the chain accordingly. It returns the number of bytes read.
func (r *receiverChain) UnpickleLibOlm(value []byte) (int, error) {
curPos := 0
readBytes, err := r.RKey.UnpickleLibOlm(value)
if err != nil {
return 0, err
}
curPos += readBytes
readBytes, err = r.CKey.UnpickleLibOlm(value[curPos:])
if err != nil {
return 0, err
}
curPos += readBytes
return curPos, nil
}
// PickleLibOlm encodes the chain into target. target has to have a size of at least PickleLen() and is written to from index 0.
// It returns the number of bytes written.
func (r receiverChain) PickleLibOlm(target []byte) (int, error) {
if len(target) < r.PickleLen() {
return 0, fmt.Errorf("pickle sender chain: %w", goolm.ErrValueTooShort)
}
written, err := r.RKey.PickleLibOlm(target)
if err != nil {
return 0, fmt.Errorf("pickle sender chain: %w", err)
}
writtenChain, err := r.CKey.PickleLibOlm(target)
if err != nil {
return 0, fmt.Errorf("pickle sender chain: %w", err)
}
written += writtenChain
return written, nil
}
// PickleLen returns the number of bytes the pickled chain will have.
func (r receiverChain) PickleLen() int {
length := r.RKey.PickleLen()
length += r.CKey.PickleLen()
return length
}
// messageKey wraps the index and the key of a message
type messageKey struct {
Index uint32 `json:"index"`
Key []byte `json:"key"`
}
// UnpickleLibOlm decodes the unencryted value and populates the message key accordingly. It returns the number of bytes read.
func (m *messageKey) UnpickleLibOlm(value []byte) (int, error) {
curPos := 0
ratchetKey, readBytes, err := libolmpickle.UnpickleBytes(value, messageKeyLength)
if err != nil {
return 0, err
}
m.Key = ratchetKey
curPos += readBytes
keyID, readBytes, err := libolmpickle.UnpickleUInt32(value[:curPos])
if err != nil {
return 0, err
}
curPos += readBytes
m.Index = keyID
return curPos, nil
}
// PickleLibOlm encodes the message key into target. target has to have a size of at least PickleLen() and is written to from index 0.
// It returns the number of bytes written.
func (m messageKey) PickleLibOlm(target []byte) (int, error) {
if len(target) < m.PickleLen() {
return 0, fmt.Errorf("pickle message key: %w", goolm.ErrValueTooShort)
}
written := 0
if len(m.Key) != messageKeyLength {
written += libolmpickle.PickleBytes(make([]byte, messageKeyLength), target)
} else {
written += libolmpickle.PickleBytes(m.Key, target)
}
written += libolmpickle.PickleUInt32(m.Index, target[written:])
return written, nil
}
// PickleLen returns the number of bytes the pickled message key will have.
func (r messageKey) PickleLen() int {
length := libolmpickle.PickleBytesLen(make([]byte, messageKeyLength))
length += libolmpickle.PickleUInt32Len(r.Index)
return length
}

432
vendor/maunium.net/go/mautrix/crypto/goolm/olm/olm.go generated vendored Normal file
View File

@@ -0,0 +1,432 @@
// olm provides the ratchet used by the olm protocol
package olm
import (
"fmt"
"io"
"maunium.net/go/mautrix/crypto/goolm"
"maunium.net/go/mautrix/crypto/goolm/cipher"
"maunium.net/go/mautrix/crypto/goolm/crypto"
"maunium.net/go/mautrix/crypto/goolm/libolmpickle"
"maunium.net/go/mautrix/crypto/goolm/message"
"maunium.net/go/mautrix/crypto/goolm/utilities"
)
const (
olmPickleVersion uint8 = 1
)
const (
maxReceiverChains = 5
maxSkippedMessageKeys = 40
protocolVersion = 3
messageKeySeed = 0x01
maxMessageGap = 2000
sharedKeyLength = 32
)
// KdfInfo has the infos used for the kdf
var KdfInfo = struct {
Root []byte
Ratchet []byte
}{
Root: []byte("OLM_ROOT"),
Ratchet: []byte("OLM_RATCHET"),
}
var RatchetCipher = cipher.NewAESSHA256([]byte("OLM_KEYS"))
// Ratchet represents the olm ratchet as described in
//
// https://gitlab.matrix.org/matrix-org/olm/-/blob/master/docs/olm.md
type Ratchet struct {
// The root key is used to generate chain keys from the ephemeral keys.
// A new root_key is derived each time a new chain is started.
RootKey crypto.Curve25519PublicKey `json:"root_key"`
// The sender chain is used to send messages. Each time a new ephemeral
// key is received from the remote server we generate a new sender chain
// with a new ephemeral key when we next send a message.
SenderChains senderChain `json:"sender_chain"`
// The receiver chain is used to decrypt received messages. We store the
// last few chains so we can decrypt any out of order messages we haven't
// received yet.
// New chains are prepended for easier access.
ReceiverChains []receiverChain `json:"receiver_chains"`
// Storing the keys of missed messages for future use.
// The order of the elements is not important.
SkippedMessageKeys []skippedMessageKey `json:"skipped_message_keys"`
}
// New creates a new ratchet, setting the kdfInfos and cipher.
func New() *Ratchet {
r := &Ratchet{}
return r
}
// InitializeAsBob initializes this ratchet from a receiving point of view (only first message).
func (r *Ratchet) InitializeAsBob(sharedSecret []byte, theirRatchetKey crypto.Curve25519PublicKey) error {
derivedSecretsReader := crypto.HKDFSHA256(sharedSecret, nil, KdfInfo.Root)
derivedSecrets := make([]byte, 2*sharedKeyLength)
if _, err := io.ReadFull(derivedSecretsReader, derivedSecrets); err != nil {
return err
}
r.RootKey = derivedSecrets[0:sharedKeyLength]
newReceiverChain := newReceiverChain(derivedSecrets[sharedKeyLength:], theirRatchetKey)
r.ReceiverChains = append([]receiverChain{*newReceiverChain}, r.ReceiverChains...)
return nil
}
// InitializeAsAlice initializes this ratchet from a sending point of view (only first message).
func (r *Ratchet) InitializeAsAlice(sharedSecret []byte, ourRatchetKey crypto.Curve25519KeyPair) error {
derivedSecretsReader := crypto.HKDFSHA256(sharedSecret, nil, KdfInfo.Root)
derivedSecrets := make([]byte, 2*sharedKeyLength)
if _, err := io.ReadFull(derivedSecretsReader, derivedSecrets); err != nil {
return err
}
r.RootKey = derivedSecrets[0:sharedKeyLength]
newSenderChain := newSenderChain(derivedSecrets[sharedKeyLength:], ourRatchetKey)
r.SenderChains = *newSenderChain
return nil
}
// Encrypt encrypts the message in a message.Message with MAC. If reader is nil, crypto/rand is used for key generations.
func (r *Ratchet) Encrypt(plaintext []byte, reader io.Reader) ([]byte, error) {
var err error
if !r.SenderChains.IsSet {
newRatchetKey, err := crypto.Curve25519GenerateKey(reader)
if err != nil {
return nil, err
}
newChainKey, err := r.advanceRootKey(newRatchetKey, r.ReceiverChains[0].ratchetKey())
if err != nil {
return nil, err
}
newSenderChain := newSenderChain(newChainKey, newRatchetKey)
r.SenderChains = *newSenderChain
}
messageKey := r.createMessageKeys(r.SenderChains.chainKey())
r.SenderChains.advance()
encryptedText, err := RatchetCipher.Encrypt(messageKey.Key, plaintext)
if err != nil {
return nil, fmt.Errorf("cipher encrypt: %w", err)
}
message := &message.Message{}
message.Version = protocolVersion
message.Counter = messageKey.Index
message.RatchetKey = r.SenderChains.ratchetKey().PublicKey
message.Ciphertext = encryptedText
//creating the mac is done in encode
output, err := message.EncodeAndMAC(messageKey.Key, RatchetCipher)
if err != nil {
return nil, err
}
return output, nil
}
// Decrypt decrypts the ciphertext and verifies the MAC. If reader is nil, crypto/rand is used for key generations.
func (r *Ratchet) Decrypt(input []byte) ([]byte, error) {
message := &message.Message{}
//The mac is not verified here, as we do not know the key yet
err := message.Decode(input)
if err != nil {
return nil, err
}
if message.Version != protocolVersion {
return nil, fmt.Errorf("decrypt: %w", goolm.ErrWrongProtocolVersion)
}
if !message.HasCounter || len(message.RatchetKey) == 0 || len(message.Ciphertext) == 0 {
return nil, fmt.Errorf("decrypt: %w", goolm.ErrBadMessageFormat)
}
var receiverChainFromMessage *receiverChain
for curChainIndex := range r.ReceiverChains {
if r.ReceiverChains[curChainIndex].ratchetKey().Equal(message.RatchetKey) {
receiverChainFromMessage = &r.ReceiverChains[curChainIndex]
break
}
}
var result []byte
if receiverChainFromMessage == nil {
//Advancing the chain is done in this method
result, err = r.decryptForNewChain(message, input)
if err != nil {
return nil, err
}
} else if receiverChainFromMessage.chainKey().Index > message.Counter {
// No need to advance the chain
// Chain already advanced beyond the key for this message
// Check if the message keys are in the skipped key list.
foundSkippedKey := false
for curSkippedIndex := range r.SkippedMessageKeys {
if message.Counter == r.SkippedMessageKeys[curSkippedIndex].MKey.Index {
// Found the key for this message. Check the MAC.
verified, err := message.VerifyMACInline(r.SkippedMessageKeys[curSkippedIndex].MKey.Key, RatchetCipher, input)
if err != nil {
return nil, err
}
if !verified {
return nil, fmt.Errorf("decrypt from skipped message keys: %w", goolm.ErrBadMAC)
}
result, err = RatchetCipher.Decrypt(r.SkippedMessageKeys[curSkippedIndex].MKey.Key, message.Ciphertext)
if err != nil {
return nil, fmt.Errorf("cipher decrypt: %w", err)
}
if len(result) != 0 {
// Remove the key from the skipped keys now that we've
// decoded the message it corresponds to.
r.SkippedMessageKeys[curSkippedIndex] = r.SkippedMessageKeys[len(r.SkippedMessageKeys)-1]
r.SkippedMessageKeys = r.SkippedMessageKeys[:len(r.SkippedMessageKeys)-1]
}
foundSkippedKey = true
}
}
if !foundSkippedKey {
return nil, fmt.Errorf("decrypt: %w", goolm.ErrMessageKeyNotFound)
}
} else {
//Advancing the chain is done in this method
result, err = r.decryptForExistingChain(receiverChainFromMessage, message, input)
if err != nil {
return nil, err
}
}
return result, nil
}
// advanceRootKey created the next root key and returns the next chainKey
func (r *Ratchet) advanceRootKey(newRatchetKey crypto.Curve25519KeyPair, oldRatchetKey crypto.Curve25519PublicKey) (crypto.Curve25519PublicKey, error) {
sharedSecret, err := newRatchetKey.SharedSecret(oldRatchetKey)
if err != nil {
return nil, err
}
derivedSecretsReader := crypto.HKDFSHA256(sharedSecret, r.RootKey, KdfInfo.Ratchet)
derivedSecrets := make([]byte, 2*sharedKeyLength)
if _, err := io.ReadFull(derivedSecretsReader, derivedSecrets); err != nil {
return nil, err
}
r.RootKey = derivedSecrets[:sharedKeyLength]
return derivedSecrets[sharedKeyLength:], nil
}
// createMessageKeys returns the messageKey derived from the chainKey
func (r Ratchet) createMessageKeys(chainKey chainKey) messageKey {
res := messageKey{}
res.Key = crypto.HMACSHA256(chainKey.Key, []byte{messageKeySeed})
res.Index = chainKey.Index
return res
}
// decryptForExistingChain returns the decrypted message by using the chain. The MAC of the rawMessage is verified.
func (r *Ratchet) decryptForExistingChain(chain *receiverChain, message *message.Message, rawMessage []byte) ([]byte, error) {
if message.Counter < chain.CKey.Index {
return nil, fmt.Errorf("decrypt: %w", goolm.ErrChainTooHigh)
}
// Limit the number of hashes we're prepared to compute
if message.Counter-chain.CKey.Index > maxMessageGap {
return nil, fmt.Errorf("decrypt from existing chain: %w", goolm.ErrMsgIndexTooHigh)
}
for chain.CKey.Index < message.Counter {
messageKey := r.createMessageKeys(chain.chainKey())
skippedKey := skippedMessageKey{
MKey: messageKey,
RKey: chain.ratchetKey(),
}
r.SkippedMessageKeys = append(r.SkippedMessageKeys, skippedKey)
chain.advance()
}
messageKey := r.createMessageKeys(chain.chainKey())
chain.advance()
verified, err := message.VerifyMACInline(messageKey.Key, RatchetCipher, rawMessage)
if err != nil {
return nil, err
}
if !verified {
return nil, fmt.Errorf("decrypt from existing chain: %w", goolm.ErrBadMAC)
}
return RatchetCipher.Decrypt(messageKey.Key, message.Ciphertext)
}
// decryptForNewChain returns the decrypted message by creating a new chain and advancing the root key.
func (r *Ratchet) decryptForNewChain(message *message.Message, rawMessage []byte) ([]byte, error) {
// They shouldn't move to a new chain until we've sent them a message
// acknowledging the last one
if !r.SenderChains.IsSet {
return nil, fmt.Errorf("decrypt for new chain: %w", goolm.ErrProtocolViolation)
}
// Limit the number of hashes we're prepared to compute
if message.Counter > maxMessageGap {
return nil, fmt.Errorf("decrypt for new chain: %w", goolm.ErrMsgIndexTooHigh)
}
newChainKey, err := r.advanceRootKey(r.SenderChains.ratchetKey(), message.RatchetKey)
if err != nil {
return nil, err
}
newChain := newReceiverChain(newChainKey, message.RatchetKey)
r.ReceiverChains = append([]receiverChain{*newChain}, r.ReceiverChains...)
/*
They have started using a new ephemeral ratchet key.
We needed to derive a new set of chain keys.
We can discard our previous ephemeral ratchet key.
We will generate a new key when we send the next message.
*/
r.SenderChains = senderChain{}
decrypted, err := r.decryptForExistingChain(&r.ReceiverChains[0], message, rawMessage)
if err != nil {
return nil, err
}
return decrypted, nil
}
// PickleAsJSON returns a ratchet as a base64 string encrypted using the supplied key. The unencrypted representation of the Account is in JSON format.
func (r Ratchet) PickleAsJSON(key []byte) ([]byte, error) {
return utilities.PickleAsJSON(r, olmPickleVersion, key)
}
// UnpickleAsJSON updates a ratchet by a base64 encrypted string using the supplied key. The unencrypted representation has to be in JSON format.
func (r *Ratchet) UnpickleAsJSON(pickled, key []byte) error {
return utilities.UnpickleAsJSON(r, pickled, key, olmPickleVersion)
}
// UnpickleLibOlm decodes the unencryted value and populates the Ratchet accordingly. It returns the number of bytes read.
func (r *Ratchet) UnpickleLibOlm(value []byte, includesChainIndex bool) (int, error) {
//read ratchet data
curPos := 0
readBytes, err := r.RootKey.UnpickleLibOlm(value)
if err != nil {
return 0, err
}
curPos += readBytes
countSenderChains, readBytes, err := libolmpickle.UnpickleUInt32(value[curPos:]) //Length of sender chain
if err != nil {
return 0, err
}
curPos += readBytes
for i := uint32(0); i < countSenderChains; i++ {
if i == 0 {
//only first is stored
readBytes, err := r.SenderChains.UnpickleLibOlm(value[curPos:])
if err != nil {
return 0, err
}
curPos += readBytes
r.SenderChains.IsSet = true
} else {
dummy := senderChain{}
readBytes, err := dummy.UnpickleLibOlm(value[curPos:])
if err != nil {
return 0, err
}
curPos += readBytes
}
}
countReceivChains, readBytes, err := libolmpickle.UnpickleUInt32(value[curPos:]) //Length of recevier chain
if err != nil {
return 0, err
}
curPos += readBytes
r.ReceiverChains = make([]receiverChain, countReceivChains)
for i := uint32(0); i < countReceivChains; i++ {
readBytes, err := r.ReceiverChains[i].UnpickleLibOlm(value[curPos:])
if err != nil {
return 0, err
}
curPos += readBytes
}
countSkippedMessageKeys, readBytes, err := libolmpickle.UnpickleUInt32(value[curPos:]) //Length of skippedMessageKeys
if err != nil {
return 0, err
}
curPos += readBytes
r.SkippedMessageKeys = make([]skippedMessageKey, countSkippedMessageKeys)
for i := uint32(0); i < countSkippedMessageKeys; i++ {
readBytes, err := r.SkippedMessageKeys[i].UnpickleLibOlm(value[curPos:])
if err != nil {
return 0, err
}
curPos += readBytes
}
// pickle v 0x80000001 includes a chain index; pickle v1 does not.
if includesChainIndex {
_, readBytes, err := libolmpickle.UnpickleUInt32(value[curPos:])
if err != nil {
return 0, err
}
curPos += readBytes
}
return curPos, nil
}
// PickleLibOlm encodes the ratchet into target. target has to have a size of at least PickleLen() and is written to from index 0.
// It returns the number of bytes written.
func (r Ratchet) PickleLibOlm(target []byte) (int, error) {
if len(target) < r.PickleLen() {
return 0, fmt.Errorf("pickle ratchet: %w", goolm.ErrValueTooShort)
}
written, err := r.RootKey.PickleLibOlm(target)
if err != nil {
return 0, fmt.Errorf("pickle ratchet: %w", err)
}
if r.SenderChains.IsSet {
written += libolmpickle.PickleUInt32(1, target[written:]) //Length of sender chain, always 1
writtenSender, err := r.SenderChains.PickleLibOlm(target[written:])
if err != nil {
return 0, fmt.Errorf("pickle ratchet: %w", err)
}
written += writtenSender
} else {
written += libolmpickle.PickleUInt32(0, target[written:]) //Length of sender chain
}
written += libolmpickle.PickleUInt32(uint32(len(r.ReceiverChains)), target[written:])
for _, curChain := range r.ReceiverChains {
writtenChain, err := curChain.PickleLibOlm(target[written:])
if err != nil {
return 0, fmt.Errorf("pickle ratchet: %w", err)
}
written += writtenChain
}
written += libolmpickle.PickleUInt32(uint32(len(r.SkippedMessageKeys)), target[written:])
for _, curChain := range r.SkippedMessageKeys {
writtenChain, err := curChain.PickleLibOlm(target[written:])
if err != nil {
return 0, fmt.Errorf("pickle ratchet: %w", err)
}
written += writtenChain
}
return written, nil
}
// PickleLen returns the actual number of bytes the pickled ratchet will have.
func (r Ratchet) PickleLen() int {
length := r.RootKey.PickleLen()
if r.SenderChains.IsSet {
length += libolmpickle.PickleUInt32Len(1)
length += r.SenderChains.PickleLen()
} else {
length += libolmpickle.PickleUInt32Len(0)
}
length += libolmpickle.PickleUInt32Len(uint32(len(r.ReceiverChains)))
length += len(r.ReceiverChains) * receiverChain{}.PickleLen()
length += libolmpickle.PickleUInt32Len(uint32(len(r.SkippedMessageKeys)))
length += len(r.SkippedMessageKeys) * skippedMessageKey{}.PickleLen()
return length
}
// PickleLen returns the minimum number of bytes the pickled ratchet must have.
func (r Ratchet) PickleLenMin() int {
length := r.RootKey.PickleLen()
length += libolmpickle.PickleUInt32Len(0)
length += libolmpickle.PickleUInt32Len(0)
length += libolmpickle.PickleUInt32Len(0)
return length
}

View File

@@ -0,0 +1,55 @@
package olm
import (
"fmt"
"maunium.net/go/mautrix/crypto/goolm"
"maunium.net/go/mautrix/crypto/goolm/crypto"
)
// skippedMessageKey stores a skipped message key
type skippedMessageKey struct {
RKey crypto.Curve25519PublicKey `json:"ratchet_key"`
MKey messageKey `json:"message_key"`
}
// UnpickleLibOlm decodes the unencryted value and populates the chain accordingly. It returns the number of bytes read.
func (r *skippedMessageKey) UnpickleLibOlm(value []byte) (int, error) {
curPos := 0
readBytes, err := r.RKey.UnpickleLibOlm(value)
if err != nil {
return 0, err
}
curPos += readBytes
readBytes, err = r.MKey.UnpickleLibOlm(value[curPos:])
if err != nil {
return 0, err
}
curPos += readBytes
return curPos, nil
}
// PickleLibOlm encodes the chain into target. target has to have a size of at least PickleLen() and is written to from index 0.
// It returns the number of bytes written.
func (r skippedMessageKey) PickleLibOlm(target []byte) (int, error) {
if len(target) < r.PickleLen() {
return 0, fmt.Errorf("pickle sender chain: %w", goolm.ErrValueTooShort)
}
written, err := r.RKey.PickleLibOlm(target)
if err != nil {
return 0, fmt.Errorf("pickle sender chain: %w", err)
}
writtenChain, err := r.MKey.PickleLibOlm(target)
if err != nil {
return 0, fmt.Errorf("pickle sender chain: %w", err)
}
written += writtenChain
return written, nil
}
// PickleLen returns the number of bytes the pickled chain will have.
func (r skippedMessageKey) PickleLen() int {
length := r.RKey.PickleLen()
length += r.MKey.PickleLen()
return length
}