- internal/{address,identity,protocol,tce,transport,verify} -> pkg/ so
external Go projects can import the verified core; invariant tests
updated for the new paths
- Config.TrustProxy: key rate limiting by X-Forwarded-For when the relay
sits behind a reverse proxy (off by default, header never trusted
otherwise)
- examples/service + examples/approve: complete passwordless login round
trip (mint request -> wallet approves -> local verify), run live in CI
- docs/SERVICE-GUIDE.md: the integration recipe
287 lines
7.2 KiB
Go
287 lines
7.2 KiB
Go
package tce
|
|
|
|
import (
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// Decoder reads canonical TCE bytes.
|
|
//
|
|
// The decoder is strict: it accepts only the exact byte sequence the encoder
|
|
// would produce for a given object, and treats every other input as an error.
|
|
// In particular it never skips a field it does not understand. Skipping would
|
|
// mean two implementations computed different meanings for the same signed
|
|
// bytes while both saw a valid signature, so an old verifier could approve an
|
|
// object whose actual content it never examined.
|
|
//
|
|
// Every length prefix is checked against the remaining input and against the
|
|
// field's maximum before any allocation, so a hostile length cannot cause a
|
|
// large allocation or a long loop.
|
|
type Decoder struct {
|
|
buf []byte
|
|
off int
|
|
}
|
|
|
|
// NewDecoder returns a decoder reading b. The slice is not copied; the caller
|
|
// must not modify it while decoding.
|
|
func NewDecoder(b []byte) *Decoder { return &Decoder{buf: b} }
|
|
|
|
// Offset returns the current read position.
|
|
func (d *Decoder) Offset() int { return d.off }
|
|
|
|
// Remaining returns the number of unread bytes.
|
|
func (d *Decoder) Remaining() int { return len(d.buf) - d.off }
|
|
|
|
// End asserts that the input is fully consumed.
|
|
//
|
|
// Trailing bytes are an error rather than ignored data: an object with extra
|
|
// bytes appended has a different byte string, and therefore a different object
|
|
// ID and a different signature, from the object it appears to contain.
|
|
func (d *Decoder) End() error {
|
|
if d.off != len(d.buf) {
|
|
return ErrTrailing
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Header reads and validates the magic, object tag and object version.
|
|
func (d *Decoder) Header() (ObjectTag, error) {
|
|
if d.Remaining() < MagicLen {
|
|
return TagReserved, ErrTruncated
|
|
}
|
|
if string(d.buf[d.off:d.off+MagicLen]) != Magic {
|
|
return TagReserved, ErrMagic
|
|
}
|
|
d.off += MagicLen
|
|
|
|
if d.Remaining() < 1 {
|
|
return TagReserved, ErrTruncated
|
|
}
|
|
tag := ObjectTag(d.buf[d.off])
|
|
d.off++
|
|
if !knownTag(tag) {
|
|
return TagReserved, ErrObjectTag
|
|
}
|
|
|
|
ver, err := d.Uvarint()
|
|
if err != nil {
|
|
return TagReserved, fieldErr("version", err)
|
|
}
|
|
if ver != Version {
|
|
return TagReserved, ErrVersion
|
|
}
|
|
return tag, nil
|
|
}
|
|
|
|
// Uvarint reads a canonical LEB128 unsigned varint.
|
|
//
|
|
// A multi-byte encoding whose final group is zero is a longer spelling of a
|
|
// shorter value and is rejected, because permitting it would give the same
|
|
// number two encodings and therefore the same object two byte strings.
|
|
func (d *Decoder) Uvarint() (uint64, error) {
|
|
var n uint64
|
|
var shift uint
|
|
start := d.off
|
|
for {
|
|
if d.off >= len(d.buf) {
|
|
return 0, ErrTruncated
|
|
}
|
|
if d.off-start >= MaxUvarintBytes {
|
|
return 0, ErrUvarint
|
|
}
|
|
b := d.buf[d.off]
|
|
d.off++
|
|
|
|
if shift >= 64 || (shift == 63 && b > 1) {
|
|
return 0, ErrOverflow
|
|
}
|
|
n |= uint64(b&0x7f) << shift
|
|
|
|
if b&0x80 == 0 {
|
|
if d.off-start > 1 && b == 0x00 {
|
|
return 0, ErrNonMinimal
|
|
}
|
|
return n, nil
|
|
}
|
|
shift += 7
|
|
}
|
|
}
|
|
|
|
// RawBytes reads a length-prefixed byte string, enforcing maxLen.
|
|
//
|
|
// The returned slice aliases the decoder's buffer. Callers that retain the
|
|
// data must copy it; the object constructors in the protocol package do.
|
|
func (d *Decoder) RawBytes(maxLen int) ([]byte, error) {
|
|
n, err := d.Uvarint()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Compare against the remaining input first so that an absurd length is
|
|
// rejected before it is compared with anything else.
|
|
if n > uint64(d.Remaining()) {
|
|
return nil, ErrTruncated
|
|
}
|
|
if n > uint64(maxLen) {
|
|
return nil, ErrTooLong
|
|
}
|
|
b := d.buf[d.off : d.off+int(n)]
|
|
d.off += int(n)
|
|
return b, nil
|
|
}
|
|
|
|
// FixedBytes reads a length-prefixed byte string of exactly want bytes.
|
|
func (d *Decoder) FixedBytes(want int) ([]byte, error) {
|
|
b, err := d.RawBytes(want)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(b) != want {
|
|
return nil, ErrFieldSize
|
|
}
|
|
return b, nil
|
|
}
|
|
|
|
// String reads a length-prefixed UTF-8 string and validates it.
|
|
func (d *Decoder) String(maxLen int) (string, error) {
|
|
b, err := d.RawBytes(maxLen)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if !utf8.Valid(b) {
|
|
return "", ErrUTF8
|
|
}
|
|
s := string(b)
|
|
for _, r := range s {
|
|
if r <= 0x1f || r == 0x7f || (r >= 0x80 && r <= 0x9f) {
|
|
return "", ErrControlChar
|
|
}
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
// Identity reads an identity field and returns a copy of the public key.
|
|
//
|
|
// The key is copied so that a decoded object does not alias the input buffer,
|
|
// which means a caller cannot alter an identity after the object containing it
|
|
// has been verified.
|
|
func (d *Decoder) Identity() ([]byte, error) {
|
|
ver, err := d.Uvarint()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ver != AddressVersion {
|
|
return nil, ErrAddressVersion
|
|
}
|
|
b, err := d.FixedBytes(PubKeySize)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]byte, PubKeySize)
|
|
copy(out, b)
|
|
return out, nil
|
|
}
|
|
|
|
// Timestamp reads a timestamp and validates its range.
|
|
func (d *Decoder) Timestamp(allowZero bool) (uint64, error) {
|
|
ts, err := d.Uvarint()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if err := ValidateTimestamp(ts, allowZero); err != nil {
|
|
return 0, err
|
|
}
|
|
return ts, nil
|
|
}
|
|
|
|
// Value reads a typed value.
|
|
//
|
|
// Reserved and unknown tags are rejected. This is the opposite of the
|
|
// "ignore what you do not understand" rule common in extensible formats, and
|
|
// it is deliberate: in a signed protocol, skipping an unrecognised value means
|
|
// the verifier's view of the object differs from the signer's.
|
|
func (d *Decoder) Value() (Value, error) {
|
|
if d.Remaining() < 1 {
|
|
return Value{}, ErrTruncated
|
|
}
|
|
tag := ValueTag(d.buf[d.off])
|
|
d.off++
|
|
|
|
switch tag {
|
|
case ValNull:
|
|
return Null(), nil
|
|
case ValFalse:
|
|
return Bool(false), nil
|
|
case ValTrue:
|
|
return Bool(true), nil
|
|
case ValString:
|
|
s, err := d.String(MaxStringValue)
|
|
if err != nil {
|
|
return Value{}, err
|
|
}
|
|
return String(s), nil
|
|
case ValNumber:
|
|
b, err := d.RawBytes(MaxNumberToken)
|
|
if err != nil {
|
|
return Value{}, err
|
|
}
|
|
tok := string(b)
|
|
// A number must arrive in canonical form. Accepting "1.0" here would
|
|
// mean two byte strings encoded the same value.
|
|
if !IsCanonicalNumber(tok) {
|
|
return Value{}, ErrNumberFormat
|
|
}
|
|
return Number(tok), nil
|
|
default:
|
|
return Value{}, ErrValueTag
|
|
}
|
|
}
|
|
|
|
// Map reads a map, enforcing ascending key order, key uniqueness, the key
|
|
// grammar and the entry limit.
|
|
func (d *Decoder) Map(minEntries int) (map[string]Value, error) {
|
|
n, err := d.Uvarint()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if n > MaxMapEntries {
|
|
return nil, ErrTooLong
|
|
}
|
|
if n < uint64(minEntries) {
|
|
return nil, ErrEmptyMap
|
|
}
|
|
// Each entry costs at least two bytes, so a count larger than the
|
|
// remaining input cannot be satisfied. Checking this before allocating
|
|
// prevents a small input from reserving a large map.
|
|
if n > uint64(d.Remaining()) {
|
|
return nil, ErrTruncated
|
|
}
|
|
|
|
m := make(map[string]Value, n)
|
|
prev := ""
|
|
for i := uint64(0); i < n; i++ {
|
|
kb, err := d.RawBytes(MaxKeyLen)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
key := string(kb)
|
|
if err := ValidateKey(key); err != nil {
|
|
return nil, err
|
|
}
|
|
if i > 0 {
|
|
switch {
|
|
case key == prev:
|
|
return nil, ErrDuplicateKey
|
|
case key < prev:
|
|
// Out-of-order entries parse, but would give one map two
|
|
// encodings, so they are rejected.
|
|
return nil, ErrKeyOrder
|
|
}
|
|
}
|
|
v, err := d.Value()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
m[key] = v
|
|
prev = key
|
|
}
|
|
return m, nil
|
|
}
|