serviceinfo

package
v0.0.0-...-09f1a81 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 1, 2025 License: Apache-2.0 Imports: 9 Imported by: 4

Documentation

Overview

Package serviceinfo handles FDO Service Info and Service Info Modules.

Index

Constants

View Source
const DefaultMTU = 1300

DefaultMTU for service info when Max(Owner|Device)ServiceInfoSz is null.

Variables

View Source
var ErrSizeTooSmall = errors.New("not enough size for chunk")

ErrSizeTooSmall indicates that a chunk could not be read due to insufficient max size.

Functions

func ArraySizeCBOR

func ArraySizeCBOR(arr []*KV) int64

ArraySizeCBOR returns the size of the service info slice once marshaled to CBOR.

func NewChunkInPipe

func NewChunkInPipe(buffers int) (*UnchunkReader, *ChunkWriter)

NewChunkInPipe creates a ChunkWriter and UnchunkReader pair. All chunks sent to the writer will be unchunked and emitted from the reader.

func NewChunkOutPipe

func NewChunkOutPipe(buffers int) (*ChunkReader, *UnchunkWriter)

NewChunkOutPipe creates a ChunkReader and UnchunkWriter pair. All service info sent to the writer will be chunked using the given MTU and emitted from the reader.

Types

type ChunkReader

type ChunkReader struct {
	// contains filtered or unexported fields
}

ChunkReader reads ServiceInfo chunked at some MTU.

func (*ChunkReader) Close

func (r *ChunkReader) Close() error

Close the reader if no more reads will be performed so that the Writer errors rather than deadlocks.

func (*ChunkReader) ReadChunk

func (r *ChunkReader) ReadChunk(size uint16) (*KV, error)

ReadChunk reads ServiceInfo chunked at some MTU. The values contain any number of logical ServiceInfos. When no more ServiceInfo will be available, an io.EOF error is returned.

type ChunkWriter

type ChunkWriter struct {
	// contains filtered or unexported fields
}

ChunkWriter expects ServiceInfo key-values to be written in the correct order and appropriate MTU. When all chunks have been written, it must be closed exactly once with either Close or CloseWithError.

func (*ChunkWriter) Close

func (w *ChunkWriter) Close() (err error)

Close must be called exactly once when all ServiceInfo have been written.

func (*ChunkWriter) CloseWithError

func (w *ChunkWriter) CloseWithError(err error) error

CloseWithError causes reads from the associated UnchunkReader to error with the given error.

func (*ChunkWriter) WriteChunk

func (w *ChunkWriter) WriteChunk(kv *KV) error

WriteChunk is called with chunked ServiceInfos.

type DeviceModule

type DeviceModule interface {
	// Transition sets the state of the module to active or inactive. Receive
	// and Respond will not be called unless Transition has been called at
	// least once and with the last input of true. Transition, as such, is only
	// a callback to allow setting up/tearing down state.
	Transition(active bool) error

	// Receive handles received service info. When any message is received
	// multiple times, the values will automatically be concatenated. For
	// cumulative data, this means that large values like files can be read
	// from a single io.Reader. For repetitive discrete objects, a CBOR decoder
	// should be applied to the io.Reader and a stream of objects can be read.
	//
	// The respond callback allows the module to send any number of service
	// info KVs. A writer is provided, as automatic chunking of messages larger
	// than the MTU will be performed. However, there is no automatic batching
	// of writes into a single KV, so each Write will result in at least one
	// service info KV being sent (possibly in a larger group of KVs per FDO
	// message).
	//
	// The yield callback will cause the next service info to be sent in a new
	// message, regardless of how much space is left in the current device
	// service info array.
	//
	// For manual chunking using yield, it may be desirable to know the MTU.
	// The full negotiated MTU (not the current space left from the MTU) can be
	// acquired from `ctx.Value(serviceinfo.MTUKey{}).(uint16)`.
	Receive(ctx context.Context, messageName string, messageBody io.Reader, respond func(message string) io.Writer, yield func()) error

	// Yield indicates that all service info key value pairs have been received
	// from the owner module, possibly across multiple messages with
	// IsMoreServiceInfo set.
	//
	// The respond and yield callbacks behave the same as for Receive.
	Yield(ctx context.Context, respond func(message string) io.Writer, yield func()) error
}

A DeviceModule handles a single service info key's moduleName (key format: "moduleName:messageName"). "active" messages are automatically handled and do not result in a call to Receive method.

Chunking is applied automatically, so large responses may be written without custom chunking code. Responses larger than the MTU will cause the marshaled payload to be split across multiple bstrs. The data within the bstrs must be concatenated at the receiver in order to reconstruct the payload and guarantee a valid CBOR object. When chunking occurs, the device sends IsMoreServiceInfo = true.

Unchunking is automatic and applies the reverse process to the above. Note that this only occurs when IsMoreServiceInfo = true and the same message is sent multiple times in a row.

MessageBody must be fully read before writing a response unless the module implements UnsafeModule. Writing before fully consuming the reader will case the writer to fail.

Responses may be queued and not sent immediately, as the receive queue in TO2 may still be filling, potentially across multiple rounds of 68-69 messages. Sends only occur once the peer has stopped indicating IsMoreServiceInfo.

Any error returned will cause an ErrorMessage to be sent and TO2 will fail. If a warning should be logged, this must be done within the handler.

type Devmod

type Devmod struct {
	Os      string `devmod:"os,required"`
	Arch    string `devmod:"arch,required"`
	Version string `devmod:"version,required"`
	Device  string `devmod:"device,required"`
	Serial  []byte `devmod:"sn"`
	PathSep string `devmod:"pathsep"`
	FileSep string `devmod:"sep,required"`
	Newline string `devmod:"nl"`
	Temp    string `devmod:"tmp"`
	Dir     string `devmod:"dir"`
	ProgEnv string `devmod:"progenv"`
	Bin     string `devmod:"bin,required"`
	MudURL  string `devmod:"mudurl"`
}

Devmod implements the one required FDO ServiceInfo Module

┌─────────────────┬───────────┬───────────────┬─────────────────────────────────────────────────────────────┐
│devmod:active    │ Required  │ bool          │ Indicates the module is active. Devmod is required on       │
│                 │           │               │ all devices                                                 │
├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤
│devmod:os        │ Required  │ tstr          │ OS name (e.g., Linux)                                       │
├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤
│devmod:arch      │ Required  │ tstr          │ Architecture name / instruction set (e.g., X86_64)          │
├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤
│devmod:version   │ Required  │ tstr          │ Version of OS (e.g., “Ubuntu* 16.0.4LTS”)                   │
├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤
│devmod:device    │ Required  │ tstr          │ Model specifier for this FIDO Device Onboard Device,        │
│                 │           │               │ manufacturer specific                                       │
├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤
│devmod:sn        │ Optional  │ tstr/bstr     │ Serial number for this FIDO Device Onboard Device,          │
│                 │           │               │ manufacturer specific                                       │
├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤
│devmod:pathsep   │ Optional  │ tstr          │ Filename path separator, between the directory and          │
│                 │           │               │ sub-directory (e.g., ‘/’ or ‘\’)                            │
├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤
│devmod:sep       │ Required  │ tstr          │ Filename separator, that works to make lists of file        │
│                 │           │               │ names (e.g., ‘:’ or ‘;’)                                    │
├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤
│devmod:nl        │ Optional  │ tstr          │ Newline sequence (e.g., a tstr of length 1 containing       │
│                 │           │               │ U+000A; a tstr of length 2 containing U+000D followed       │
│                 │           │               │ by U+000A)                                                  │
├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤
│devmod:tmp       │ Optional  │ tstr          │ Location of temporary directory, including terminating      │
│                 │           │               │ file separator (e.g., “/tmp”)                               │
├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤
│devmod:dir       │ Optional  │ tstr          │ Location of suggested installation directory, including     │
│                 │           │               │ terminating file separator (e.g., “.” or “/home/fdo” or     │
│                 │           │               │ “c:\Program Files\fdo”)                                     │
├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤
│devmod:progenv   │ Optional  │ tstr          │ Programming environment. See Table 3-22 (e.g.,              │
│                 │           │               │ “bin:java:py3:py2”)                                         │
├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤
│devmod:bin       │ Required  │ tstr          │ Either the same value as “arch”, or a list of machine       │
│                 │           │               │ formats that can be interpreted by this device, in          │
│                 │           │               │ preference order, separated by the “sep” value (e.g.,       │
│                 │           │               │ “x86:X86_64”)                                               │
├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤
│devmod:mudurl    │ Optional  │ tstr          │ URL for the Manufacturer Usage Description file that        │
│                 │           │               │ relates to this device                                      │
├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤
│devmod:nummodules│ Required  │ uint          │ Number of modules supported by this FIDO Device Onboard     │
│                 │           │               │ Device                                                      │
├─────────────────┼───────────┼───────────────┼─────────────────────────────────────────────────────────────┤
│devmod:modules   │ Required  │ [uint, uint,  │ Enumerates the modules supported by this FIDO Device        │
│                 │           │ tstr1, tstr2, │ Onboard Device. The first element is an integer from        │
│                 │           │ ...]          │ zero to devmod:nummodules. The second element is the        │
│                 │           │               │ number of module names to return The subsequent elements    │
│                 │           │               │ are module names. During the initial Device ServiceInfo,    │
│                 │           │               │ the device sends the complete list of modules to the Owner. │
│                 │           │               │ If the list is long, it might require more than one         │
│                 │           │               │ ServiceInfo message.                                        │
└─────────────────┴───────────┴───────────────┴─────────────────────────────────────────────────────────────┘

func (*Devmod) Validate

func (d *Devmod) Validate() error

Validate checks that all required fields are not their zero value.

func (*Devmod) Write

func (d *Devmod) Write(ctx context.Context, deviceModules map[string]DeviceModule, mtu uint16, w *UnchunkWriter)

Write the devmod messages.

type DevmodModulesChunk

type DevmodModulesChunk struct {
	Start   int
	Len     int
	Modules []string
}

DevmodModulesChunk is the CBOR array value used in devmod:modules messages. Instead of representing it as an []any, it provides a more typed interface, knowing that the array will always contain [int, int, [string...]].

func (DevmodModulesChunk) MarshalCBOR

func (c DevmodModulesChunk) MarshalCBOR() ([]byte, error)

MarshalCBOR implements cbor.Marshaler.

func (*DevmodModulesChunk) UnmarshalCBOR

func (c *DevmodModulesChunk) UnmarshalCBOR(data []byte) error

UnmarshalCBOR implements cbor.Unmarshaler.

type KV

type KV struct {
	Key string
	Val []byte
}

KV is a ServiceInfoKV structure.

func (*KV) Size

func (kv *KV) Size() uint16

Size calculates the number of bytes once marshaled to CBOR.

func (*KV) String

func (kv *KV) String() string

type MTUKey

type MTUKey struct{}

MTUKey is the context key for the uint16 MTU value. See the description of DeviceModule.Receive.

type OwnerModule

type OwnerModule interface {
	// HandleInfo is called once for each service info KV received from the
	// device.
	HandleInfo(ctx context.Context, messageName string, messageBody io.Reader) error

	// ProduceInfo is called once for each TO2.DeviceServiceInfo, after
	// HandleInfo is called for each service info KV, unless the device
	// indicated IsMoreServiceInfo.
	//
	// If `blockPeer` is true, the owner service will indicate
	// IsMoreServiceInfo to keep the device from sending service info in the
	// next exchange. If `moduleDone` is true, then IsMoreServiceInfo will not
	// be set true, regardless of the value of `more`, and this module will no
	// longer be used in the TO2 protocol.
	ProduceInfo(ctx context.Context, producer *Producer) (blockPeer, moduleDone bool, _ error)
}

OwnerModule implements a service info module.

type Producer

type Producer struct {
	// contains filtered or unexported fields
}

Producer allows an owner service info module to produce service info either with auto-chunking (not yet implemented) or manually.

func NewProducer

func NewProducer(moduleName string, mtu uint16) *Producer

NewProducer creates a new producer instance for the given MTU.

func (*Producer) Available

func (p *Producer) Available(messageName string) int

Available returns the remaining space available for a message body in bytes. If the next service info will not fit in the remaining bytes, then the module should return and on the next ProduceInfo the full MTU will be available.

func (*Producer) ServiceInfo

func (p *Producer) ServiceInfo() []*KV

ServiceInfo returns all ServiceInfo, guaranteed to fit within the MTU.

func (*Producer) WriteChunk

func (p *Producer) WriteChunk(messageName string, messageBody []byte) error

WriteChunk queues a single service info. If messageBody is larger than the bytes available, WriteChunk will fail and no service info will be queued.

type UnchunkReader

type UnchunkReader struct {
	// contains filtered or unexported fields
}

UnchunkReader gets a new io.Reader for each logical ServiceInfo.

func (*UnchunkReader) NextServiceInfo

func (r *UnchunkReader) NextServiceInfo() (key string, val io.ReadCloser, ok bool)

NextServiceInfo gets a new io.Reader for a logical ServiceInfo. The reader contains the unchunked value of the ServiceInfo so that the consumer does not need to be aware of the MTU.

type UnchunkWriter

type UnchunkWriter struct {
	// contains filtered or unexported fields
}

UnchunkWriter is used for writing unchunked logical ServiceInfos. Each ServiceInfo is written by first calling NextServiceInfo to give it a module and message name, then the full body is written via zero or more calls to Write.

func (*UnchunkWriter) Close

func (w *UnchunkWriter) Close() error

Close is called when all ServiceInfos have been written and no further calls to Next or Write will be made.

func (*UnchunkWriter) CloseWithError

func (w *UnchunkWriter) CloseWithError(err error) error

CloseWithError causes reads from the associated ChunkReader to error with the given error.

func (*UnchunkWriter) ForceNewMessage

func (w *UnchunkWriter) ForceNewMessage() error

ForceNewMessage causes the next (*ChunkReader).ReadChunk to return ErrSizeTooSmall. This in turn forces the next KV to be put into a new ServiceInfo message.

This method facilitates implementing custom chunking conventions, provided that the MTU used for automatic chunking at the client level is known to the implementer.

func (*UnchunkWriter) NextServiceInfo

func (w *UnchunkWriter) NextServiceInfo(moduleName, messageName string) error

NextServiceInfo must be called once before each logical ServiceInfo.

func (*UnchunkWriter) Write

func (w *UnchunkWriter) Write(p []byte) (n int, err error)

Write may be called any number of times to write the contents of a ServiceInfo value, but it must be preceded by a call to Next and must be succeeded by a call to either Next or Close.

type UnknownModule

type UnknownModule struct{}

UnknownModule handles receiving and responding to service info for an inactive or missing module.

Section 3.8.3.1 says to ignore all messages for unknown modules, except message=active, which should respond CBOR false. The exceception to the exception is that devmod should always return active=true.

func (UnknownModule) Receive

func (m UnknownModule) Receive(_ context.Context, _ string, messageBody io.Reader, _ func(string) io.Writer, _ func()) error

Receive implements DeviceModule.

func (UnknownModule) Transition

func (m UnknownModule) Transition(bool) error

Transition implements DeviceModule.

func (UnknownModule) Yield

func (m UnknownModule) Yield(ctx context.Context, respond func(message string) io.Writer, yield func()) error

Yield implements DeviceModule.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL