penguin/homekey-go

homekey-go

internal/provision/tlv.go

raw ยท 1396 bytes

// SPDX-License-Identifier: Apache-2.0
package provision

import "fmt"

const MaxMessage = 4096

type Fields map[byte][]byte

// DecodeTLV accepts a single TLV8 object. Adjacent 255-byte fragments are
// reassembled; ambiguous duplicate fields and list separators are rejected.
func DecodeTLV(b []byte) (Fields, error) {
	if len(b) > MaxMessage {
		return nil, fmt.Errorf("TLV exceeds limit")
	}
	out := make(Fields)
	var previous byte
	previousLen := -1
	for len(b) != 0 {
		if len(b) < 2 {
			return nil, fmt.Errorf("truncated TLV header")
		}
		t, n := b[0], int(b[1])
		b = b[2:]
		if t == 0xff || len(b) < n {
			return nil, fmt.Errorf("invalid TLV field")
		}
		if _, exists := out[t]; exists && (previous != t || previousLen != 255) {
			return nil, fmt.Errorf("duplicate TLV field")
		}
		out[t] = append(out[t], b[:n]...)
		// Preserve the presence of an explicitly empty field.
		if out[t] == nil {
			out[t] = []byte{}
		}
		previous, previousLen, b = t, n, b[n:]
	}
	return out, nil
}

func TLV(t byte, value []byte) []byte {
	var out []byte
	for len(value) > 255 {
		out = append(out, t, 255)
		out = append(out, value[:255]...)
		value = value[255:]
	}
	out = append(out, t, byte(len(value)))
	return append(out, value...)
}

func one(f Fields, t byte) (byte, error) {
	if len(f[t]) != 1 {
		return 0, fmt.Errorf("missing or invalid one-byte field %d", t)
	}
	return f[t][0], nil
}