mirror of
https://source.quilibrium.com/quilibrium/ceremonyclient.git
synced 2024-12-26 08:35:17 +00:00
6c567a04c1
* v1.4.20 base * add inmemory dev mock for hypergraph * add simple rdf + tr * Update config.go (#234) 2 of bootstrap nodes are going to be closed due to low performances. Will consider to replace with better specs. * go mod tidy * go mod tidy * bump name in readme --------- Co-authored-by: 0xOzgur <29779769+0xOzgur@users.noreply.github.com>
756 lines
19 KiB
Go
756 lines
19 KiB
Go
package channel
|
||
|
||
import (
|
||
"bytes"
|
||
"crypto/aes"
|
||
"crypto/cipher"
|
||
"crypto/rand"
|
||
"crypto/sha512"
|
||
"crypto/subtle"
|
||
"encoding/binary"
|
||
"encoding/json"
|
||
"fmt"
|
||
"sort"
|
||
|
||
"github.com/pkg/errors"
|
||
"golang.org/x/crypto/hkdf"
|
||
"source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves"
|
||
"source.quilibrium.com/quilibrium/monorepo/node/keys"
|
||
"source.quilibrium.com/quilibrium/monorepo/node/protobufs"
|
||
)
|
||
|
||
const TRIPLE_RATCHET_PROTOCOL_VERSION = 1
|
||
const TRIPLE_RATCHET_PROTOCOL = 2<<8 + TRIPLE_RATCHET_PROTOCOL_VERSION
|
||
|
||
type TripleRatchetRound int
|
||
|
||
const (
|
||
TRIPLE_RATCHET_ROUND_UNINITIALIZED = TripleRatchetRound(0)
|
||
TRIPLE_RATCHET_ROUND_INITIALIZED = TripleRatchetRound(1)
|
||
TRIPLE_RATCHET_ROUND_COMMITTED = TripleRatchetRound(2)
|
||
TRIPLE_RATCHET_ROUND_REVEALED = TripleRatchetRound(3)
|
||
TRIPLE_RATCHET_ROUND_RECONSTRUCTED = TripleRatchetRound(4)
|
||
)
|
||
|
||
// Note: If an HSM with raw primitive access becomes available, the raw crypto
|
||
// mechanisms should be refactored into calls in KeyManager and implemented
|
||
// through the driver
|
||
type TripleRatchetParticipant struct {
|
||
peerKey curves.Scalar
|
||
sendingEphemeralPrivateKey curves.Scalar
|
||
receivingEphemeralKeys map[string]curves.Scalar
|
||
receivingGroupKey curves.Point
|
||
curve curves.Curve
|
||
keyManager keys.KeyManager
|
||
rootKey []byte
|
||
sendingChainKey []byte
|
||
currentHeaderKey []byte
|
||
nextHeaderKey []byte
|
||
receivingChainKey map[string][]byte
|
||
currentSendingChainLength uint32
|
||
previousSendingChainLength uint32
|
||
currentReceivingChainLength map[string]uint32
|
||
previousReceivingChainLength map[string]uint32
|
||
peerIdMap map[string]int
|
||
idPeerMap map[int]*PeerInfo
|
||
skippedKeysMap map[string]map[string]map[uint32][]byte
|
||
peerChannels map[string]*DoubleRatchetParticipant
|
||
dkgRatchet *Feldman
|
||
}
|
||
|
||
type PeerInfo struct {
|
||
PublicKey curves.Point
|
||
IdentityPublicKey curves.Point
|
||
SignedPrePublicKey curves.Point
|
||
}
|
||
|
||
// Weak-mode synchronous group modification TR – this is not the asynchronous
|
||
// TR, does not ratchet group key automatically, know what your use case is
|
||
// before adopting this.
|
||
func NewTripleRatchetParticipant(
|
||
peers []*PeerInfo,
|
||
curve curves.Curve,
|
||
keyManager keys.KeyManager,
|
||
peerKey curves.Scalar,
|
||
identityKey curves.Scalar,
|
||
signedPreKey curves.Scalar,
|
||
) (
|
||
*TripleRatchetParticipant,
|
||
map[string]*protobufs.P2PChannelEnvelope,
|
||
error,
|
||
) {
|
||
participant := &TripleRatchetParticipant{}
|
||
participant.skippedKeysMap = make(map[string]map[string]map[uint32][]byte)
|
||
participant.receivingEphemeralKeys = make(map[string]curves.Scalar)
|
||
participant.receivingChainKey = make(map[string][]byte)
|
||
participant.peerChannels = make(map[string]*DoubleRatchetParticipant)
|
||
participant.keyManager = keyManager
|
||
participant.currentSendingChainLength = 0
|
||
participant.previousSendingChainLength = 0
|
||
participant.currentReceivingChainLength = make(map[string]uint32)
|
||
participant.previousReceivingChainLength = make(map[string]uint32)
|
||
|
||
peerBasis := append([]*PeerInfo{}, peers...)
|
||
peerBasis = append(peerBasis, &PeerInfo{
|
||
PublicKey: peerKey.Point().Generator().Mul(peerKey),
|
||
IdentityPublicKey: identityKey.Point().Generator().Mul(identityKey),
|
||
SignedPrePublicKey: signedPreKey.Point().Generator().Mul(signedPreKey),
|
||
})
|
||
sort.Slice(peerBasis, func(i, j int) bool {
|
||
return bytes.Compare(
|
||
peerBasis[i].PublicKey.ToAffineCompressed(),
|
||
peerBasis[j].PublicKey.ToAffineCompressed(),
|
||
) <= 0
|
||
})
|
||
|
||
initMessages := make(map[string]*protobufs.P2PChannelEnvelope)
|
||
|
||
peerIdMap := map[string]int{}
|
||
idPeerMap := map[int]*PeerInfo{}
|
||
sender := false
|
||
for i := 0; i < len(peerBasis); i++ {
|
||
peerIdMap[string(peerBasis[i].PublicKey.ToAffineCompressed())] = i + 1
|
||
idPeerMap[i+1] = peerBasis[i]
|
||
if bytes.Equal(
|
||
peerBasis[i].PublicKey.ToAffineCompressed(),
|
||
peerKey.Point().Generator().Mul(peerKey).ToAffineCompressed(),
|
||
) {
|
||
sender = true
|
||
} else {
|
||
participant.skippedKeysMap[string(
|
||
peerBasis[i].PublicKey.ToAffineCompressed(),
|
||
)] = make(map[string]map[uint32][]byte)
|
||
participant.currentReceivingChainLength[string(
|
||
peerBasis[i].PublicKey.ToAffineCompressed(),
|
||
)] = 0
|
||
participant.previousReceivingChainLength[string(
|
||
peerBasis[i].PublicKey.ToAffineCompressed(),
|
||
)] = 0
|
||
var sessionKey []byte
|
||
if sender {
|
||
sessionKey = SenderX3DH(
|
||
identityKey,
|
||
signedPreKey,
|
||
peerBasis[i].IdentityPublicKey,
|
||
peerBasis[i].SignedPrePublicKey,
|
||
96,
|
||
)
|
||
} else {
|
||
sessionKey = ReceiverX3DH(
|
||
identityKey,
|
||
signedPreKey,
|
||
peerBasis[i].IdentityPublicKey,
|
||
peerBasis[i].SignedPrePublicKey,
|
||
96,
|
||
)
|
||
}
|
||
|
||
var err error
|
||
participant.peerChannels[string(
|
||
peerBasis[i].PublicKey.ToAffineCompressed(),
|
||
)], err = NewDoubleRatchetParticipant(
|
||
sessionKey[:32],
|
||
sessionKey[32:64],
|
||
sessionKey[64:],
|
||
sender,
|
||
signedPreKey,
|
||
peerBasis[i].SignedPrePublicKey,
|
||
&curve,
|
||
keyManager,
|
||
)
|
||
if err != nil {
|
||
return nil, nil, errors.Wrap(err, "new triple ratchet participant")
|
||
}
|
||
if sender {
|
||
initMessages[string(peerBasis[i].PublicKey.ToAffineCompressed())], err =
|
||
participant.peerChannels[string(
|
||
peerBasis[i].PublicKey.ToAffineCompressed(),
|
||
)].RatchetEncrypt([]byte("init"))
|
||
if err != nil {
|
||
return nil, nil, errors.Wrap(err, "new triple ratchet participant")
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
feldman, err := NewFeldman(
|
||
2,
|
||
len(peers)+1,
|
||
peerIdMap[string(
|
||
peerKey.Point().Generator().Mul(peerKey).ToAffineCompressed(),
|
||
)],
|
||
curve.NewScalar().Random(rand.Reader),
|
||
curve,
|
||
curve.Point.Generator(),
|
||
)
|
||
if err != nil {
|
||
return nil, nil, errors.Wrap(err, "new triple ratchet participant")
|
||
}
|
||
|
||
participant.peerIdMap = peerIdMap
|
||
participant.idPeerMap = idPeerMap
|
||
participant.dkgRatchet = feldman
|
||
participant.curve = curve
|
||
participant.peerKey = peerKey
|
||
|
||
return participant, initMessages, nil
|
||
}
|
||
|
||
func (r *TripleRatchetParticipant) Initialize(
|
||
initMessages map[string]*protobufs.P2PChannelEnvelope,
|
||
) (map[string]*protobufs.P2PChannelEnvelope, error) {
|
||
for k, m := range initMessages {
|
||
msg, err := r.peerChannels[k].RatchetDecrypt(m)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "initialize")
|
||
}
|
||
|
||
if string(msg) != "init" {
|
||
return nil, errors.Wrap(errors.New("invalid init message"), "initialize")
|
||
}
|
||
}
|
||
|
||
if err := r.dkgRatchet.SamplePolynomial(); err != nil {
|
||
return nil, errors.Wrap(err, "initialize")
|
||
}
|
||
|
||
result, err := r.dkgRatchet.GetPolyFrags()
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "initialize")
|
||
}
|
||
|
||
resultMap := make(map[string]*protobufs.P2PChannelEnvelope)
|
||
for k, v := range result {
|
||
if r.idPeerMap[k].PublicKey.Equal(
|
||
r.peerKey.Point().Generator().Mul(r.peerKey),
|
||
) {
|
||
continue
|
||
}
|
||
|
||
envelope, err := r.peerChannels[string(
|
||
r.idPeerMap[k].PublicKey.ToAffineCompressed(),
|
||
)].RatchetEncrypt(v)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "initialize")
|
||
}
|
||
|
||
resultMap[string(r.idPeerMap[k].PublicKey.ToAffineCompressed())] = envelope
|
||
}
|
||
|
||
return resultMap, nil
|
||
}
|
||
|
||
func (r *TripleRatchetParticipant) ReceivePolyFrag(
|
||
peerId []byte,
|
||
frag *protobufs.P2PChannelEnvelope,
|
||
) (map[string]*protobufs.P2PChannelEnvelope, error) {
|
||
b, err := r.peerChannels[string(peerId)].RatchetDecrypt(frag)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "receive poly frag")
|
||
}
|
||
|
||
result, err := r.dkgRatchet.SetPolyFragForParty(
|
||
r.peerIdMap[string(peerId)],
|
||
b,
|
||
)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "receive poly frag")
|
||
}
|
||
|
||
if len(result) != 0 {
|
||
envelopes := make(map[string]*protobufs.P2PChannelEnvelope)
|
||
for k, c := range r.peerChannels {
|
||
envelope, err := c.RatchetEncrypt(result)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "receive poly frag")
|
||
}
|
||
envelopes[k] = envelope
|
||
}
|
||
|
||
return envelopes, errors.Wrap(err, "receive poly frag")
|
||
}
|
||
|
||
return nil, nil
|
||
}
|
||
|
||
func (r *TripleRatchetParticipant) ReceiveCommitment(
|
||
peerId []byte,
|
||
zkcommit *protobufs.P2PChannelEnvelope,
|
||
) (map[string]*protobufs.P2PChannelEnvelope, error) {
|
||
b, err := r.peerChannels[string(peerId)].RatchetDecrypt(zkcommit)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "receive commitment")
|
||
}
|
||
|
||
result, err := r.dkgRatchet.ReceiveCommitments(
|
||
r.peerIdMap[string(peerId)],
|
||
b,
|
||
)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "receive commitment")
|
||
}
|
||
|
||
d, err := json.Marshal(result)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "receive commitment")
|
||
}
|
||
|
||
if result != nil {
|
||
envelopes := make(map[string]*protobufs.P2PChannelEnvelope)
|
||
for k, c := range r.peerChannels {
|
||
envelope, err := c.RatchetEncrypt(d)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "receive commitment")
|
||
}
|
||
envelopes[k] = envelope
|
||
}
|
||
|
||
return envelopes, errors.Wrap(err, "receive poly frag")
|
||
}
|
||
|
||
return nil, nil
|
||
}
|
||
|
||
func (r *TripleRatchetParticipant) Recombine(
|
||
peerId []byte,
|
||
reveal *protobufs.P2PChannelEnvelope,
|
||
) error {
|
||
b, err := r.peerChannels[string(peerId)].RatchetDecrypt(reveal)
|
||
if err != nil {
|
||
return errors.Wrap(err, "recombine")
|
||
}
|
||
|
||
rev := &FeldmanReveal{}
|
||
if err = json.Unmarshal(b, rev); err != nil {
|
||
return errors.Wrap(err, "recombine")
|
||
}
|
||
|
||
done, err := r.dkgRatchet.Recombine(
|
||
r.peerIdMap[string(peerId)],
|
||
rev,
|
||
)
|
||
if err != nil {
|
||
return errors.Wrap(err, "recombine")
|
||
}
|
||
|
||
if !done {
|
||
return nil
|
||
}
|
||
|
||
sess := sha512.Sum512_256(r.dkgRatchet.PublicKeyBytes())
|
||
hash := hkdf.New(
|
||
sha512.New,
|
||
r.dkgRatchet.PublicKeyBytes(),
|
||
sess[:],
|
||
[]byte("quilibrium-triple-ratchet"),
|
||
)
|
||
rkck := make([]byte, 96)
|
||
if _, err := hash.Read(rkck[:]); err != nil {
|
||
return errors.Wrap(err, "recombine")
|
||
}
|
||
|
||
r.rootKey = rkck[:32]
|
||
r.currentHeaderKey = rkck[32:64]
|
||
r.nextHeaderKey = rkck[64:]
|
||
r.receivingGroupKey = r.dkgRatchet.PublicKey()
|
||
r.sendingEphemeralPrivateKey = r.curve.Scalar.Random(rand.Reader)
|
||
|
||
return nil
|
||
}
|
||
|
||
func (r *TripleRatchetParticipant) RatchetEncrypt(
|
||
message []byte,
|
||
) (*protobufs.P2PChannelEnvelope, error) {
|
||
envelope := &protobufs.P2PChannelEnvelope{
|
||
ProtocolIdentifier: TRIPLE_RATCHET_PROTOCOL,
|
||
MessageHeader: &protobufs.MessageCiphertext{},
|
||
MessageBody: &protobufs.MessageCiphertext{},
|
||
}
|
||
|
||
newChainKey, messageKey, aeadKey := ratchetKeys(r.sendingChainKey)
|
||
r.sendingChainKey = newChainKey
|
||
|
||
var err error
|
||
header := r.encodeHeader()
|
||
envelope.MessageHeader, err = r.encrypt(
|
||
header,
|
||
r.currentHeaderKey,
|
||
nil,
|
||
)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "could not encrypt header")
|
||
}
|
||
|
||
envelope.MessageBody, err = r.encrypt(
|
||
message,
|
||
messageKey,
|
||
append(append([]byte{}, aeadKey...), envelope.MessageHeader.Ciphertext...),
|
||
)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "could not encrypt message")
|
||
}
|
||
|
||
r.currentSendingChainLength++
|
||
|
||
return envelope, nil
|
||
}
|
||
|
||
func (r *TripleRatchetParticipant) RatchetDecrypt(
|
||
envelope *protobufs.P2PChannelEnvelope,
|
||
) ([]byte, error) {
|
||
plaintext, err := r.trySkippedMessageKeys(envelope)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "ratchet decrypt")
|
||
}
|
||
|
||
if plaintext != nil {
|
||
return plaintext, nil
|
||
}
|
||
|
||
header, shouldRatchet, err := r.decryptHeader(
|
||
envelope.MessageHeader,
|
||
r.currentHeaderKey,
|
||
)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "ratchet decrypt")
|
||
}
|
||
|
||
senderKey,
|
||
receivingEphemeralKey,
|
||
previousReceivingChainLength,
|
||
currentReceivingChainLength,
|
||
err := r.decodeHeader(header)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "ratchet decrypt")
|
||
}
|
||
|
||
if shouldRatchet {
|
||
if err := r.skipMessageKeys(
|
||
senderKey,
|
||
previousReceivingChainLength,
|
||
); err != nil {
|
||
return nil, errors.Wrap(err, "ratchet decrypt")
|
||
}
|
||
if err := r.ratchetReceiverEphemeralKeys(
|
||
senderKey,
|
||
receivingEphemeralKey,
|
||
); err != nil {
|
||
return nil, errors.Wrap(err, "ratchet decrypt")
|
||
}
|
||
}
|
||
|
||
if err := r.skipMessageKeys(
|
||
senderKey,
|
||
currentReceivingChainLength,
|
||
); err != nil {
|
||
return nil, errors.Wrap(err, "ratchet decrypt")
|
||
}
|
||
|
||
newChainKey, messageKey, aeadKey := ratchetKeys(
|
||
r.receivingChainKey[string(senderKey.ToAffineCompressed())],
|
||
)
|
||
r.receivingChainKey[string(senderKey.ToAffineCompressed())] = newChainKey
|
||
r.currentReceivingChainLength[string(senderKey.ToAffineCompressed())]++
|
||
|
||
plaintext, err = r.decrypt(
|
||
envelope.MessageBody,
|
||
messageKey,
|
||
append(
|
||
append([]byte{}, aeadKey...),
|
||
envelope.MessageHeader.Ciphertext...,
|
||
),
|
||
)
|
||
|
||
return plaintext, errors.Wrap(err, "ratchet decrypt")
|
||
}
|
||
|
||
func (r *TripleRatchetParticipant) ratchetSenderEphemeralKeys() error {
|
||
hash := hkdf.New(
|
||
sha512.New,
|
||
r.receivingGroupKey.Mul(
|
||
r.sendingEphemeralPrivateKey,
|
||
).ToAffineCompressed(),
|
||
r.rootKey,
|
||
[]byte("quilibrium-triple-ratchet"),
|
||
)
|
||
rkck2 := make([]byte, 96)
|
||
if _, err := hash.Read(rkck2[:]); err != nil {
|
||
return errors.Wrap(err, "failed ratcheting root key")
|
||
}
|
||
|
||
r.rootKey = rkck2[:32]
|
||
r.sendingChainKey = rkck2[32:64]
|
||
r.nextHeaderKey = rkck2[64:]
|
||
return nil
|
||
}
|
||
|
||
func (r *TripleRatchetParticipant) ratchetReceiverEphemeralKeys(
|
||
peerKey curves.Point,
|
||
newEphemeralKey curves.Scalar,
|
||
) error {
|
||
r.previousSendingChainLength = r.currentSendingChainLength
|
||
r.currentSendingChainLength = 0
|
||
r.currentReceivingChainLength[string(peerKey.ToAffineCompressed())] = 0
|
||
r.currentHeaderKey = r.nextHeaderKey
|
||
r.receivingEphemeralKeys[string(
|
||
peerKey.ToAffineCompressed(),
|
||
)] = newEphemeralKey
|
||
|
||
hash := hkdf.New(
|
||
sha512.New,
|
||
r.receivingGroupKey.Mul(
|
||
newEphemeralKey,
|
||
).ToAffineCompressed(),
|
||
r.rootKey,
|
||
[]byte("quilibrium-triple-ratchet"),
|
||
)
|
||
rkck := make([]byte, 96)
|
||
if _, err := hash.Read(rkck[:]); err != nil {
|
||
return errors.Wrap(err, "failed ratcheting root key")
|
||
}
|
||
|
||
r.rootKey = rkck[:32]
|
||
r.receivingChainKey[string(peerKey.ToAffineCompressed())] = rkck[32:64]
|
||
r.nextHeaderKey = rkck[64:]
|
||
r.sendingEphemeralPrivateKey = r.curve.NewScalar().Random(rand.Reader)
|
||
|
||
return nil
|
||
}
|
||
|
||
func (r *TripleRatchetParticipant) trySkippedMessageKeys(
|
||
envelope *protobufs.P2PChannelEnvelope,
|
||
) ([]byte, error) {
|
||
for receivingHeaderKey, skippedKeys := range r.skippedKeysMap {
|
||
header, _, err := r.decryptHeader(
|
||
envelope.MessageHeader,
|
||
[]byte(receivingHeaderKey),
|
||
)
|
||
|
||
if err == nil {
|
||
peerKey, _, _, current, err := r.decodeHeader(header)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "try skipped message keys")
|
||
}
|
||
|
||
messageKey := skippedKeys[string(
|
||
peerKey.ToAffineCompressed(),
|
||
)][current][:32]
|
||
aeadKey := skippedKeys[string(
|
||
peerKey.ToAffineCompressed(),
|
||
)][current][32:]
|
||
plaintext, err := r.decrypt(
|
||
envelope.MessageBody,
|
||
messageKey,
|
||
append(
|
||
append([]byte{}, aeadKey...),
|
||
envelope.MessageHeader.Ciphertext[:]...,
|
||
),
|
||
)
|
||
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "try skipped message keys")
|
||
}
|
||
|
||
delete(r.skippedKeysMap[string(
|
||
peerKey.ToAffineCompressed(),
|
||
)][receivingHeaderKey], current)
|
||
if len(r.skippedKeysMap[string(
|
||
peerKey.ToAffineCompressed(),
|
||
)][receivingHeaderKey]) == 0 {
|
||
delete(r.skippedKeysMap[string(
|
||
peerKey.ToAffineCompressed(),
|
||
)], receivingHeaderKey)
|
||
}
|
||
|
||
return plaintext, nil
|
||
}
|
||
}
|
||
|
||
return nil, nil
|
||
}
|
||
|
||
func (r *TripleRatchetParticipant) skipMessageKeys(
|
||
senderKey curves.Point,
|
||
until uint32,
|
||
) error {
|
||
if r.currentReceivingChainLength[string(
|
||
senderKey.ToAffineCompressed(),
|
||
)]+100 < until {
|
||
return errors.Wrap(errors.New("skip limit exceeded"), "skip message keys")
|
||
}
|
||
|
||
if r.receivingChainKey != nil {
|
||
for r.currentReceivingChainLength[string(
|
||
senderKey.ToAffineCompressed(),
|
||
)] < until {
|
||
newChainKey, messageKey, aeadKey := ratchetKeys(
|
||
r.receivingChainKey[string(
|
||
senderKey.ToAffineCompressed(),
|
||
)],
|
||
)
|
||
skippedKeys := r.skippedKeysMap[string(
|
||
senderKey.ToAffineCompressed(),
|
||
)][string(r.currentHeaderKey)]
|
||
if skippedKeys == nil {
|
||
r.skippedKeysMap[string(
|
||
senderKey.ToAffineCompressed(),
|
||
)][string(r.currentHeaderKey)] =
|
||
make(map[uint32][]byte)
|
||
}
|
||
|
||
skippedKeys[r.currentReceivingChainLength[string(
|
||
senderKey.ToAffineCompressed(),
|
||
)]] = append(
|
||
append([]byte{}, messageKey...),
|
||
aeadKey...,
|
||
)
|
||
r.receivingChainKey[string(
|
||
senderKey.ToAffineCompressed(),
|
||
)] = newChainKey
|
||
r.currentReceivingChainLength[string(
|
||
senderKey.ToAffineCompressed(),
|
||
)]++
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func (r *TripleRatchetParticipant) encodeHeader() []byte {
|
||
header := []byte{}
|
||
header = append(
|
||
header,
|
||
r.peerKey.Point().Generator().Mul(r.peerKey).ToAffineCompressed()...,
|
||
)
|
||
header = append(
|
||
header,
|
||
r.sendingEphemeralPrivateKey.Bytes()...,
|
||
)
|
||
header = binary.BigEndian.AppendUint32(header, r.previousSendingChainLength)
|
||
header = binary.BigEndian.AppendUint32(header, r.currentSendingChainLength)
|
||
return header
|
||
}
|
||
|
||
func (r *TripleRatchetParticipant) decryptHeader(
|
||
ciphertext *protobufs.MessageCiphertext,
|
||
receivingHeaderKey []byte,
|
||
) ([]byte, bool, error) {
|
||
header, err := r.decrypt(
|
||
ciphertext,
|
||
receivingHeaderKey,
|
||
nil,
|
||
)
|
||
if err != nil && subtle.ConstantTimeCompare(
|
||
r.currentHeaderKey,
|
||
receivingHeaderKey,
|
||
) == 1 {
|
||
if header, err = r.decrypt(
|
||
ciphertext,
|
||
r.nextHeaderKey,
|
||
nil,
|
||
); err != nil {
|
||
return nil, false, errors.Wrap(err, "could not decrypt header")
|
||
}
|
||
fmt.Println("should ratchet")
|
||
return header, true, nil
|
||
}
|
||
|
||
return header, false, errors.Wrap(err, "could not decrypt header")
|
||
}
|
||
|
||
func (r *TripleRatchetParticipant) decodeHeader(
|
||
header []byte,
|
||
) (curves.Point, curves.Scalar, uint32, uint32, error) {
|
||
if len(header) < 9 {
|
||
return nil, nil, 0, 0, errors.Wrap(
|
||
errors.New("malformed header"),
|
||
"decode header",
|
||
)
|
||
}
|
||
|
||
currentReceivingChainLength := binary.BigEndian.Uint32(header[len(header)-4:])
|
||
previousReceivingChainLength := binary.BigEndian.Uint32(
|
||
header[len(header)-8 : len(header)-4],
|
||
)
|
||
sender := header[:len(r.curve.Point.ToAffineCompressed())]
|
||
senderKey, err := r.curve.Point.FromAffineCompressed(sender)
|
||
if err != nil {
|
||
return nil, nil, 0, 0, errors.Wrap(err, "decode header")
|
||
}
|
||
|
||
receivingEphemeralKeyBytes := header[len(
|
||
r.curve.Point.ToAffineCompressed(),
|
||
) : len(header)-8]
|
||
receivingEphemeralKey, err := r.curve.Scalar.Clone().SetBytes(
|
||
receivingEphemeralKeyBytes,
|
||
)
|
||
|
||
return senderKey,
|
||
receivingEphemeralKey,
|
||
previousReceivingChainLength,
|
||
currentReceivingChainLength,
|
||
errors.Wrap(err, "decode header")
|
||
}
|
||
|
||
func (r *TripleRatchetParticipant) encrypt(
|
||
plaintext []byte,
|
||
key []byte,
|
||
associatedData []byte,
|
||
) (*protobufs.MessageCiphertext, error) {
|
||
iv := [12]byte{}
|
||
rand.Read(iv[:])
|
||
aesCipher, err := aes.NewCipher(key)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "encrypt")
|
||
}
|
||
|
||
gcm, err := cipher.NewGCM(aesCipher)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "encrypt")
|
||
}
|
||
|
||
ciphertext := &protobufs.MessageCiphertext{}
|
||
|
||
if associatedData == nil {
|
||
associatedData = make([]byte, 32)
|
||
if _, err := rand.Read(associatedData); err != nil {
|
||
return nil, errors.Wrap(err, "encrypt")
|
||
}
|
||
ciphertext.AssociatedData = associatedData
|
||
}
|
||
|
||
ciphertext.Ciphertext = gcm.Seal(nil, iv[:], plaintext, associatedData)
|
||
ciphertext.InitializationVector = iv[:]
|
||
|
||
return ciphertext, nil
|
||
}
|
||
|
||
func (r *TripleRatchetParticipant) decrypt(
|
||
ciphertext *protobufs.MessageCiphertext,
|
||
key []byte,
|
||
associatedData []byte,
|
||
) ([]byte, error) {
|
||
if associatedData == nil {
|
||
associatedData = ciphertext.AssociatedData
|
||
}
|
||
|
||
aesCipher, err := aes.NewCipher(key)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "decrypt")
|
||
}
|
||
|
||
gcm, err := cipher.NewGCM(aesCipher)
|
||
if err != nil {
|
||
return nil, errors.Wrap(err, "decrypt")
|
||
}
|
||
|
||
plaintext, err := gcm.Open(
|
||
nil,
|
||
ciphertext.InitializationVector,
|
||
ciphertext.Ciphertext,
|
||
associatedData,
|
||
)
|
||
|
||
return plaintext, errors.Wrap(err, "decrypt")
|
||
}
|