megolm/megolm.go

126 lines
2.9 KiB
Go

package main
import (
"fmt"
)
const (
SHA256_BLOCK_LENGTH = 64
SHA256_OUTPUT_LENGTH = 32
MEGOLM_RATCHET_PART_LENGTH = 32
MEGOLM_RATCHET_PARTS = 4
MEGOLM_RATCHET_LENGTH = (MEGOLM_RATCHET_PARTS * MEGOLM_RATCHET_PART_LENGTH)
OLM_PROTOCOL_VERSION = 3
)
var HASH_KEY_SEEDS = [MEGOLM_RATCHET_PARTS]byte{0x00, 0x01, 0x02, 0x03}
type Megolm struct {
data [][]byte
counter int
}
func NewMegolm(initialData []byte, initialCounter int) (*Megolm, error) {
if len(initialData) != MEGOLM_RATCHET_LENGTH {
return nil, fmt.Errorf("megolm initial data must be %d bytes. Got %d.", MEGOLM_RATCHET_LENGTH, len(initialData))
}
data := make([][]byte, MEGOLM_RATCHET_PARTS)
for i := 0; i < MEGOLM_RATCHET_PARTS; i++ {
data[i] = make([]byte, MEGOLM_RATCHET_PART_LENGTH)
start := i * MEGOLM_RATCHET_PART_LENGTH
copy(data[i], initialData[start:start+MEGOLM_RATCHET_PART_LENGTH])
}
return &Megolm{
data: data,
counter: initialCounter,
}, nil
}
func (m *Megolm) Data() []byte {
data := make([]byte, MEGOLM_RATCHET_LENGTH)
for i := 0; i < MEGOLM_RATCHET_PARTS; i++ {
start := i * MEGOLM_RATCHET_PART_LENGTH
copy(data[start:start+MEGOLM_RATCHET_PART_LENGTH], m.data[i])
}
return data
}
func (m *Megolm) Advance() {
mask := 0x00FFFFFF
h := 0
m.counter++
/* figure out how much we need to rekey */
for h < MEGOLM_RATCHET_PARTS {
if m.counter&mask == 0 {
break
}
h++
mask >>= 8
}
// update R[h:3] based on h
for i := MEGOLM_RATCHET_PARTS - 1; i >= h; i-- {
m.rehashPart(h, i)
}
}
func (m *Megolm) AdvanceTo(advanceTo int) {
/* starting with R0, see if we need to update each part of the hash */
for j := 0; j < MEGOLM_RATCHET_PARTS; j++ {
shift := (MEGOLM_RATCHET_PARTS - j - 1) * 8
mask := 0xffffffff << shift
/* how many times do we need to rehash this part?
*
* '& 0xff' ensures we handle integer wraparound correctly
*/
steps :=
((advanceTo >> shift) - (m.counter >> shift)) & 0xff
if steps == 0 {
/* deal with the edge case where megolm->counter is slightly larger
* than advanceTo. This should only happen for R(0), and implies
* that advanceTo has wrapped around and we need to advance R(0)
* 256 times.
*/
if advanceTo < m.counter {
steps = 0x100
} else {
continue
}
}
/* for all but the last step, we can just bump R(j) without regard
* to R(j+1)...R(3).
*/
for steps > 1 {
m.rehashPart(j, j)
steps--
}
/* on the last step we also need to bump R(j+1)...R(3).
*
* (Theoretically, we could skip bumping R(j+2) if we're going to bump
* R(j+1) again, but the code to figure that out is a bit baroque and
* doesn't save us much).
*/
for k := 3; k >= j; k-- {
m.rehashPart(j, k)
}
m.counter = advanceTo & mask
}
}
func (m *Megolm) rehashPart(fromPart, toPart int) {
newPart := HMACSHA256(
m.data[fromPart],
[]byte{HASH_KEY_SEEDS[toPart]},
)
m.data[toPart] = newPart
}