State is now and interface and it's what New now returns
This commit is contained in:
parent
487ae8bdeb
commit
302d30e0a8
48
state.go
48
state.go
|
@ -3,9 +3,9 @@ package doubleratchet
|
|||
// TODO: For Bob to be able to send messages right after he initiated a session it's required
|
||||
// to populate his sending chain with the shared secret. Should it be the same secret both
|
||||
// parties agreed upon before the communication or should it be a separate key?
|
||||
// TODO: When the new public key should be reset?
|
||||
// TODO: Max chain length? What happens when N in message header closes in on overflowing? Perform Ratchet step?
|
||||
|
||||
// TODO
|
||||
// Any Double Ratchet message encrypted using Alice's initial sending chain can serve as
|
||||
// an "initial ciphertext" for X3DH. To deal with the possibility of lost or out-of-order messages,
|
||||
// a recommended pattern is for Alice to repeatedly send the same X3DH initial message prepended
|
||||
|
@ -15,16 +15,20 @@ import (
|
|||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
// Maximum number of message keys that can be skipped in a single chain.
|
||||
MaxSkip = 1000
|
||||
)
|
||||
// State of the party involved in The Double Ratchet Algorithm.
|
||||
type State interface {
|
||||
// RatchetEncrypt performs a symmetric-key ratchet step, then encrypts the message with
|
||||
// the resulting message key.
|
||||
RatchetEncrypt(plaintext []byte, ad AssociatedData) Message
|
||||
|
||||
// RatchetDecrypt is called to decrypt messages.
|
||||
RatchetDecrypt(m Message, ad AssociatedData) ([]byte, error)
|
||||
}
|
||||
|
||||
// State is a state of the party involved in The Double Ratchet message exchange.
|
||||
// Operations on this object are NOT THREAD-SAFE, make sure they're done in sequence.
|
||||
// TODO: Store skipper separately.
|
||||
// TODO: Store state separately?
|
||||
type State struct {
|
||||
type state struct {
|
||||
// 32-byte root key. Both parties MUST agree on this key before starting a ratchet session.
|
||||
RK [32]byte
|
||||
|
||||
|
@ -54,16 +58,16 @@ type State struct {
|
|||
Crypto Crypto
|
||||
}
|
||||
|
||||
// New creates State with the shared key and public key of the other party initiating the session.
|
||||
// New creates state with the shared key and public key of the other party initiating the session.
|
||||
// If this party initiates the session, pubKey must be nil.
|
||||
func New(sharedKey [32]byte, opts ...Option) (*State, error) {
|
||||
if len(sharedKey) == 0 {
|
||||
return nil, fmt.Errorf("sharedKey must be set")
|
||||
func New(sharedKey [32]byte, opts ...Option) (State, error) {
|
||||
if sharedKey == [32]byte{} {
|
||||
return nil, fmt.Errorf("sharedKey must be non-zero")
|
||||
}
|
||||
s := &State{
|
||||
s := &state{
|
||||
RK: sharedKey,
|
||||
MkSkipped: make(map[string][32]byte),
|
||||
MaxSkip: MaxSkip,
|
||||
MaxSkip: 1000,
|
||||
Crypto: DefaultCrypto{},
|
||||
}
|
||||
|
||||
|
@ -83,11 +87,11 @@ func New(sharedKey [32]byte, opts ...Option) (*State, error) {
|
|||
}
|
||||
|
||||
// Option is a constructor option.
|
||||
type Option func(*State) error
|
||||
type Option func(*state) error
|
||||
|
||||
// WithRemoteKey specifies the remote public key for the sending chain.
|
||||
func WithRemoteKey(dhRemotePubKey [32]byte) Option {
|
||||
return func(s *State) error {
|
||||
return func(s *state) error {
|
||||
s.DHr = dhRemotePubKey
|
||||
s.RK, s.CKs = s.Crypto.KdfRK(s.RK, s.Crypto.DH(s.DHs, s.DHr))
|
||||
return nil
|
||||
|
@ -96,7 +100,7 @@ func WithRemoteKey(dhRemotePubKey [32]byte) Option {
|
|||
|
||||
// WithMaxSkip specifies the maximum number of skipped message in a single chain.
|
||||
func WithMaxSkip(maxSkip int) Option {
|
||||
return func(s *State) error {
|
||||
return func(s *state) error {
|
||||
if maxSkip < 0 {
|
||||
return fmt.Errorf("maxSkip must be non-negative")
|
||||
}
|
||||
|
@ -107,7 +111,7 @@ func WithMaxSkip(maxSkip int) Option {
|
|||
|
||||
// RatchetEncrypt performs a symmetric-key ratchet step, then encrypts the message with
|
||||
// the resulting message key.
|
||||
func (s *State) RatchetEncrypt(plaintext []byte, ad AssociatedData) Message {
|
||||
func (s *state) RatchetEncrypt(plaintext []byte, ad AssociatedData) Message {
|
||||
var mk [32]byte
|
||||
s.CKs, mk = s.Crypto.KdfCK(s.CKs)
|
||||
h := MessageHeader{
|
||||
|
@ -124,7 +128,7 @@ func (s *State) RatchetEncrypt(plaintext []byte, ad AssociatedData) Message {
|
|||
}
|
||||
|
||||
// RatchetDecrypt is called to decrypt messages.
|
||||
func (s *State) RatchetDecrypt(m Message, ad AssociatedData) ([]byte, error) {
|
||||
func (s *state) RatchetDecrypt(m Message, ad AssociatedData) ([]byte, error) {
|
||||
plaintext, err := s.trySkippedMessageKeys(m, ad)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't decrypt skipped message: %s", err)
|
||||
|
@ -148,7 +152,7 @@ func (s *State) RatchetDecrypt(m Message, ad AssociatedData) ([]byte, error) {
|
|||
}
|
||||
|
||||
// trySkippedMessageKeys tries to decrypt the message with a skipped message key.
|
||||
func (s *State) trySkippedMessageKeys(m Message, ad AssociatedData) ([]byte, error) {
|
||||
func (s *state) trySkippedMessageKeys(m Message, ad AssociatedData) ([]byte, error) {
|
||||
skippedKey := s.skippedKey(m.Header.DH[:], m.Header.N)
|
||||
if mk, ok := s.MkSkipped[skippedKey]; ok {
|
||||
delete(s.MkSkipped, skippedKey)
|
||||
|
@ -159,14 +163,14 @@ func (s *State) trySkippedMessageKeys(m Message, ad AssociatedData) ([]byte, err
|
|||
}
|
||||
|
||||
// skippedKey forms a key for a skipped message.
|
||||
func (s *State) skippedKey(dh []byte, n uint) string {
|
||||
func (s *state) skippedKey(dh []byte, n uint) string {
|
||||
// TODO: More compact representation.
|
||||
nByte := []byte(fmt.Sprintf("_%d", n))
|
||||
return string(append(dh, nByte...))
|
||||
}
|
||||
|
||||
// skipMessageKeys skips message keys in the current receiving chain.
|
||||
func (s *State) skipMessageKeys(until uint) {
|
||||
func (s *state) skipMessageKeys(until uint) {
|
||||
// TODO: What is it?..
|
||||
if s.Nr+s.MaxSkip < until {
|
||||
// TODO: Return error.
|
||||
|
@ -185,7 +189,7 @@ func (s *State) skipMessageKeys(until uint) {
|
|||
}
|
||||
|
||||
// dhRatchet performs a single ratchet step.
|
||||
func (s *State) dhRatchet(mh MessageHeader) error {
|
||||
func (s *state) dhRatchet(mh MessageHeader) error {
|
||||
// TODO: Discard state changes in case of error.
|
||||
var err error
|
||||
|
||||
|
|
Loading…
Reference in New Issue