rclone/vendor/storj.io/common/peertls/extensions/gob.go
2020-05-12 15:56:50 +00:00

232 lines
5.2 KiB
Go

// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package extensions
import (
"bytes"
"encoding/binary"
"io"
"math/bits"
"github.com/zeebo/errs"
)
const (
uint64Size = 8
firstCustomTypeID = 65
encFirstCustomTypeID = 130 // encoded 65
)
// hardcoded initial part of Revocation gob encoding, its constant until Revocation struct won't change,
// contains definition of Revocation struct with fields names and types.
// https://golang.org/pkg/encoding/gob/
var wireEncoding = []byte{
64, 255, 129, 3, 1, 1, 10, 82, 101, 118, 111, 99, 97, 116, 105, 111, 110, 1, 255, 130, 0,
1, 3, 1, 9, 84, 105, 109, 101, 115, 116, 97, 109, 112, 1, 4, 0, 1, 7, 75, 101, 121, 72,
97, 115, 104, 1, 10, 0, 1, 9, 83, 105, 103, 110, 97, 116, 117, 114, 101, 1, 10, 0, 0, 0,
}
type revocationEncoder struct {
value *bytes.Buffer
}
func (encoder *revocationEncoder) encode(revocation Revocation) ([]byte, error) {
encoder.value = new(bytes.Buffer)
encoder.encodeInt(firstCustomTypeID)
delta := uint64(1)
if revocation.Timestamp != 0 {
encoder.encodeUint(delta)
encoder.encodeInt(revocation.Timestamp)
} else {
delta++
}
if len(revocation.KeyHash) > 0 {
encoder.encodeUint(delta)
encoder.encodeUint(uint64(len(revocation.KeyHash)))
encoder.writeBytes(revocation.KeyHash)
delta = uint64(1)
} else {
delta++
}
if len(revocation.Signature) > 0 {
encoder.encodeUint(delta)
encoder.encodeUint(uint64(len(revocation.Signature)))
encoder.writeBytes(revocation.Signature)
}
encoder.encodeUint(0)
valueLength := encoder.value.Len()
encoder.encodeUint(uint64(valueLength))
value := encoder.value.Bytes()
lengthData := value[valueLength:]
valueData := value[:valueLength]
return append(wireEncoding, append(lengthData, valueData...)...), nil
}
func (encoder *revocationEncoder) encodeInt(i int64) {
var x uint64
if i < 0 {
x = uint64(^i<<1) | 1
} else {
x = uint64(i << 1)
}
encoder.encodeUint(x)
}
func (encoder *revocationEncoder) encodeUint(x uint64) {
if x <= 0x7F {
encoder.writeByte(uint8(x))
return
}
var stateBuf [1 + uint64Size]byte
binary.BigEndian.PutUint64(stateBuf[1:], x)
bc := bits.LeadingZeros64(x) >> 3 // 8 - bytelen(x)
stateBuf[bc] = uint8(bc - uint64Size) // and then we subtract 8 to get -bytelen(x)
encoder.writeBytes(stateBuf[bc : uint64Size+1])
}
func (encoder *revocationEncoder) writeByte(x byte) {
encoder.value.WriteByte(x)
}
func (encoder *revocationEncoder) writeBytes(x []byte) {
encoder.value.Write(x)
}
type revocationDecoder struct {
data *bytes.Buffer
}
func (decoder *revocationDecoder) decode(data []byte) (revocation Revocation, err error) {
decoder.data = bytes.NewBuffer(data)
wire := make([]byte, len(wireEncoding))
_, err = io.ReadFull(decoder.data, wire)
if err != nil {
return revocation, err
}
if !bytes.Equal(wire, wireEncoding) {
return revocation, ErrRevocation.New("invalid revocation encoding")
}
length, err := decoder.decodeUint()
if err != nil {
return revocation, err
}
if length != uint64(len(decoder.data.Bytes())) {
return revocation, ErrRevocation.New("invalid revocation encoding")
}
typeID, err := decoder.decodeUint()
if err != nil {
return revocation, err
}
if typeID != encFirstCustomTypeID {
return revocation, ErrRevocation.New("invalid revocation encoding")
}
index := uint64(0)
for {
field, err := decoder.decodeUint()
if err != nil {
return revocation, err
}
if field == 0 {
break
}
switch field + index {
case 1:
revocation.Timestamp, err = decoder.decodeInt()
if err != nil {
return revocation, err
}
case 2:
revocation.KeyHash, err = decoder.decodeByteArray()
if err != nil {
return revocation, err
}
case 3:
revocation.Signature, err = decoder.decodeByteArray()
if err != nil {
return revocation, err
}
default:
return revocation, errs.New("invalid field")
}
index += field
}
return revocation, nil
}
func (decoder *revocationDecoder) decodeUint() (x uint64, err error) {
b, err := decoder.data.ReadByte()
if err != nil {
return 0, err
}
if b <= 0x7f {
return uint64(b), nil
}
n := -int(int8(b))
if n > uint64Size {
return 0, errs.New("encoded unsigned integer out of range")
}
buf := make([]byte, n)
read, err := io.ReadFull(decoder.data, buf)
if err != nil {
return 0, err
}
if read < n {
return 0, errs.New("invalid uint data length %d: exceeds input size %d", n, len(buf))
}
// Don't need to check error; it's safe to loop regardless.
// Could check that the high byte is zero but it's not worth it.
for _, b := range buf {
x = x<<8 | uint64(b)
}
return x, nil
}
func (decoder *revocationDecoder) decodeInt() (int64, error) {
x, err := decoder.decodeUint()
if err != nil {
return 0, err
}
if x&1 != 0 {
return ^int64(x >> 1), nil
}
return int64(x >> 1), nil
}
func (decoder *revocationDecoder) decodeByteArray() ([]byte, error) {
length, err := decoder.decodeUint()
if err != nil {
return nil, err
}
n := int(length)
if uint64(n) != length || decoder.data.Len() < n {
return nil, errs.New("invalid array length: %d", length)
}
buf := make([]byte, n)
_, err = io.ReadFull(decoder.data, buf)
if err != nil {
return nil, err
}
return buf, nil
}