mirror of
https://github.com/rclone/rclone.git
synced 2024-12-11 17:51:12 +01:00
232 lines
5.2 KiB
Go
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
|
|
}
|