Documentation
¶
Index ¶
- Constants
- Variables
- func Chunker(ctx context.Context, r io.Reader, cfg *Config, batchMetadata []byte) (err error)
- func CreateHandleSenderCompletion(cfg *ServerConfig) (http.HandlerFunc, error)
- func CreateHandleSenderHandshake(cfg *ServerConfig) (http.HandlerFunc, error)
- func CreateHandleSenderPackage(cfg *ServerConfig) (http.HandlerFunc, error)
- func NewHTTPClient(certPEM []byte) (*http.Client, error)
- func Retrieve(ctx context.Context, requestorPubKey ed25519.PublicKey, batchID []byte, ...) (io.ReadCloser, []byte, error)
- type BatchSecrets
- type Config
- type ServerBatchStorageManager
- type ServerConfig
- type ServerSegmentStorageManager
- type ServerSharedSecretManager
- type ServerTransferToBatchManager
- type TransferMapping
Constants ¶
const ( // DefaultMaxRetries is the default maximum number of retry attempts for transient failures DefaultMaxRetries = 3 // DefaultRetryDelay is the default delay between retries when Retry-After header is not present DefaultRetryDelay = time.Second // DefaultMaxConcurrentSends is the default maximum number of concurrent package send goroutines DefaultMaxConcurrentSends = 10 )
const ( HeaderBatchID = "X-MFT-Batch-ID" HeaderSegmentID = "X-MFT-Segment-ID" HeaderBatchComplete = "X-MFT-Batch-Complete" )
Header names for HTTP requests
Variables ¶
var ( // ErrRetrieveNotAuthorised is returned when the requester is not authorised to retrieve batch data ErrRetrieveNotAuthorised = errors.New("requester is not authorised to retrieve batch data") // ErrReaderClosed is returned when Read is called on a closed batchReader ErrReaderClosed = errors.New("reader is closed") // ErrBatchIDMustNotBeEmpty is returned when an empty batchID is provided to Retrieve ErrBatchIDMustNotBeEmpty = errors.New("batchID must not be empty") )
var ( // ErrEndpointNotHTTPS is returned when the endpoint URL does not use HTTPS ErrEndpointNotHTTPS = errors.New("endpoint must use HTTPS scheme") // ErrEndpointInvalid is returned when the endpoint URL is malformed ErrEndpointInvalid = errors.New("endpoint URL is invalid") // ErrEndpointHasPath is returned when the endpoint URL contains path, query, or fragment ErrEndpointHasPath = errors.New("endpoint must not contain path, query, or fragment") // ErrHTTPRequestFailed is returned when an HTTP request fails ErrHTTPRequestFailed = errors.New("HTTP request failed") // ErrInvalidConfig is returned when no Config is provided ErrInvalidConfig = errors.New("Config details must be provided") // ErrInvalidSize is returned when Size is not positive ErrInvalidSize = errors.New("Size must be greater than zero") // ErrInvalidBatchID is returned when BatchID is empty ErrInvalidBatchID = errors.New("BatchID must not be empty") // ErrInvalidPrivKey is returned when PrivKey is nil or invalid length ErrInvalidPrivKey = errors.New("PrivKey must be a valid Ed25519 private key") // ErrInvalidServerPubKey is returned when ServerPubKey is nil or invalid length ErrInvalidServerPubKey = errors.New("ServerPubKey must be a valid Ed25519 public key") // ErrInvalidCodec is returned when Codec is nil ErrInvalidCodec = errors.New("Codec must not be nil") // ErrInvalidEndpoint is returned when Endpoint is empty ErrInvalidEndpoint = errors.New("Endpoint must be specified") // ErrRedirectAttempted is returned when the server attempts a redirect ErrRedirectAttempted = errors.New("redirects are not permitted") // ErrTLSRequired is returned when a non-TLS response is detected ErrTLSRequired = errors.New("TLS connection required") // ErrMaxRetriesExceeded is returned when retry attempts are exhausted ErrMaxRetriesExceeded = errors.New("maximum retry attempts exceeded") // ErrHandshakeFailed is returned when the server rejects the handshake ErrHandshakeFailed = errors.New("server rejected handshake") // ErrInvalidServerResponse is returned when the server response is malformed ErrInvalidServerResponse = errors.New("invalid server response format") // ErrServerSignatureInvalid is returned when the server signature verification fails ErrServerSignatureInvalid = errors.New("server signature verification failed") // ErrKeyGenerationFailed is returned when batch keypair generation fails ErrKeyGenerationFailed = errors.New("failed to generate batch keypair") // ErrECDHFailed is returned when ECDH shared secret computation fails ErrECDHFailed = errors.New("ECDH shared secret computation failed") // ErrMetadataTooLarge is returned when batch metadata exceeds the server's maximum allowed size ErrMetadataTooLarge = errors.New("batch metadata exceeds maximum allowed size") )
var ( // ErrServerConfigMustNotBeNil is returned when a nil ServerConfig is provided ErrServerConfigMustNotBeNil = errors.New("ServerConfig must not be nil") // ErrServerPrivKeyInvalid is returned when the server's Ed25519 private key is missing or invalid ErrServerPrivKeyInvalid = errors.New("ServerConfig.PrivKey must be a valid Ed25519 private key") // ErrAuthoriseSenderNil is returned when the AuthoriseSender callback is nil ErrAuthoriseSenderNil = errors.New("ServerConfig.AuthoriseSender must not be nil") // ErrSecretManagerNil is returned when the SecretManager is nil ErrSecretManagerNil = errors.New("ServerConfig.SecretManager must not be nil") // ErrSegmentManagerNil is returned when the SegmentManager is nil ErrSegmentManagerNil = errors.New("ServerConfig.SegmentManager must not be nil") // ErrBatchManagerNil is returned when the BatchManager is nil ErrBatchManagerNil = errors.New("ServerConfig.BatchManager must not be nil") // ErrInvalidCompletionPollWait is returned if the CompletionPollWait value is invalid ErrInvalidCompletionPollWait = errors.New("ServerConfig.CompletionPollWait must be less than or equal to ServerConfig.CompletionMaxWait") // ErrTransferManagerNil is returned when the TransferManager is nil ErrTransferManagerNil = errors.New("ServerConfig.TransferManager must not be nil") // ErrInvalidCompletionID is returned when the completion ID does not match ErrInvalidCompletionID = errors.New("completion ID verification failed") // ErrTransferNotFound is returned when a transfer mapping is not found ErrTransferNotFound = errors.New("transfer not found") // ErrAuthoriseRetrieverNil is returned when the AuthoriseRetriever callback is nil ErrAuthoriseRetrieverNil = errors.New("ServerConfig.AuthoriseRetriever must not be nil") // ErrInvalidPackagePath is returned when the PackageURLPath is an empty string ErrInvalidPackagePath = errors.New("ServerConfig.PackageURLPath must be specified") // ErrInvalidCompletionPath is returned when the CompletionURLPath is an empty string ErrInvalidCompletionPath = errors.New("ServerConfig.CompletionURLPath must be specified") // ErrPackageAndCompletionPathsMustDiffer is returned when the same value is used for both PackageURLPath and CompletionURLPath ErrPackageAndCompletionPathsMustDiffer = errors.New("ServerConfig has the same value for PackageURLPath and CompletionURLPath") )
var ErrInvalidCertPEM = errors.New("invalid certificate PEM data")
ErrInvalidCertPEM is returned when the provided certificate PEM data is invalid
Functions ¶
func Chunker ¶
Chunker reads from an io.Reader in chunks and sends serialized Package instances to the configured HTTPS endpoint.
Each Package contains:
- The provided BatchID to identify the overall data stream
- A unique segmentID (sequential, starting from 0) to identify the chunk's position
- Data processed through the provided Codec
- An Ed25519 signature for integrity verification
Each package is sent via HTTP POST to the endpoint with X-MFT-Batch-ID and X-MFT-Segment-ID headers (base64url-encoded). Processing stops if the endpoint does not return HTTP 200 OK (after retries for 429/503 are exhausted).
The client enforces TLS 1.2+, disallows redirects, and retries on transient failures (429 Too Many Requests, 503 Service Unavailable) using the Retry-After header.
The final chunk may contain fewer than Config.Size bytes if the reader's data is not evenly divisible by the chunk size.
The context can be used to cancel the operation. If the context is cancelled, the function returns the context's error.
func CreateHandleSenderCompletion ¶
func CreateHandleSenderCompletion(cfg *ServerConfig) (http.HandlerFunc, error)
CreateHandleSenderCompletion returns an http.HandlerFunc that processes batch completion requests from a remote sender (i.e. the request sent by sendCompletion in sender.go).
The handler expects an HTTP POST with the header:
X-MFT-Batch-Complete: base64(transfer_id).base64(num_segments).base64(completion_id).base64(signature)
where num_segments is an 8-byte big-endian uint64.
When the request body is empty (legacy mode):
signature = Ed25519Sign(sender_priv, SHA-512(transfer_id || num_segments || completion_id))
When the request body is non-empty (metadata mode):
signature = Ed25519Sign(sender_priv, SHA-512(transfer_id || num_segments || completion_id || body))
In metadata mode the body contains AES-256-GCM encrypted batch metadata (encrypted with the ECDH shared secret and password tag "completion"). After signature verification the handler decrypts the body and passes the plaintext metadata to BatchManager.Store.
The handler maps transfer_id to internal batch_id via TransferManager, verifies completion_id matches the stored value, retrieves BatchSecrets, verifies the sender's signature, then polls SegmentManager.SegmentCount until all segments have arrived (or CompletionMaxWait is exceeded). Once all segments are confirmed, the batch is persisted via BatchManager.Store and HTTP 200 is returned.
func CreateHandleSenderHandshake ¶
func CreateHandleSenderHandshake(cfg *ServerConfig) (http.HandlerFunc, error)
CreateHandleSenderHandshake returns an http.HandlerFunc that processes handshake requests from a remote sender (i.e. the request sent by sendHandshake in sender.go).
The handler expects an HTTP POST with Content-Type text/plain and body:
<batch_id>.<client_batch_x25519_pub>.<sender_ed25519_pub>.<chunk_size>.<signature>
where each part is base64url-encoded (no padding) and signature = Ed25519Sign(sender_priv, SHA-512(batch_id || client_batch_x25519_pub || sender_ed25519_pub || chunk_size)).
On success, it responds with HTTP 200 and body:
<batch_id>.<server_batch_x25519_pub>.<server_ed25519_pub>.<encrypted_blob>.<signature>
where signature = Ed25519Sign(server_priv, SHA-512(batch_id || server_batch_x25519_pub || server_ed25519_pub || encrypted_blob)).
The encrypted blob contains a unique transferID that maps to the batchID for the server, removing visibility of which subsequent Packages are associated with which batch from any observers in the transport layer. The blob also contains a completionID, which is sent to the server by the sender after all Packages have been sent (via the completion request), to allow the server to know that (a) all packages should have arrived (or are imminent) and (b) the correct sender sent the completion request. Finally the blob contains the size of data that can be within each Package, which the sender will then use rather than its preferred "chunk size".
The ECDH shared secret is computed and stored via cfg.SecretManager.
func CreateHandleSenderPackage ¶
func CreateHandleSenderPackage(cfg *ServerConfig) (http.HandlerFunc, error)
CreateHandleSenderPackage returns an http.HandlerFunc that processes segment package requests from a remote sender (i.e. the request sent by sendPackage in sender.go).
The handler expects an HTTP POST with:
- Content-Type: application/octet-stream
- Header X-MFT-Batch-ID: base64url-encoded transfer ID (maps to internal batch ID)
- Header X-MFT-Segment-ID: base64url-encoded segment ID
- Body: serialized Package bytes (version.meta.data.signature)
The handler maps the transfer ID to the internal batch ID via TransferManager, retrieves BatchSecrets, unpacks and verifies the package signature using the sender's public key, AES-GCM encrypts the segment data using the SegmentSecret, and stores it via cfg.SegmentManager under the internal batch ID.
func NewHTTPClient ¶
NewHTTPClient creates an HTTP client configured for secure communication:
- TLS 1.2 minimum
- No redirects allowed
- Optional: accepts a PEM-encoded certificate to add to the trusted root CAs (useful for self-signed certificates)
Returns an error if certPEM is provided but is not a valid PEM-encoded certificate.
func Retrieve ¶
func Retrieve(ctx context.Context, requestorPubKey ed25519.PublicKey, batchID []byte, cfg *ServerConfig) (io.ReadCloser, []byte, error)
Retrieve returns an io.ReadCloser that incrementally reconstructs the original data from stored batch segments, along with any batch metadata that was provided during the completion request (nil when absent). Each call to Read decrypts, verifies, and decodes the next segment on demand.
The caller must close the returned reader when finished.
Types ¶
type BatchSecrets ¶
type BatchSecrets struct {
// BatchID is the unique identifier the of data transfer from the sender
BatchID []byte
SharedSecret []byte
// SenderPubKey is a long term key identifying the sender
SenderPubKey ed25519.PublicKey
// SegmentSecret is a unique secret for the batch, used to encrypt segment data prior to storing in ServerSegmentStorageManager
SegmentSecret []byte
}
BatchSecrets provide the secrets needed to handle a single Batch
type Config ¶
type Config struct {
// Size is the maximum number of bytes to read per chunk
Size int
// BatchID identifies the overall data stream
BatchID []byte
// PrivKey is the Ed25519 private key used to sign each Package (sender's private key)
PrivKey ed25519.PrivateKey
// ServerPubKey is the Ed25519 public key of the server, used to verify handshake response
ServerPubKey ed25519.PublicKey
// Codec is the codec pipeline used to process each chunk's data
Codec datacodecs.Codec
// Endpoint is the remote HTTPS URL base to send packages to.
// Must use HTTPS scheme and must not contain path, query, or fragment.
Endpoint string
// MaxRetries is the maximum number of retry attempts for transient failures (429, 503)
// If zero, DefaultMaxRetries is used
MaxRetries int
// MaxConcurrentSends is the maximum number of concurrent package send goroutines
// If zero, DefaultMaxConcurrentSends is used
MaxConcurrentSends int
// ServerCertPEM is an optional PEM-encoded certificate to add to the trusted root CAs.
// Use this when connecting to a server with a self-signed certificate.
// If nil or empty, only system root CAs are trusted.
ServerCertPEM []byte
}
Config holds the configuration for the Chunker function.
type ServerBatchStorageManager ¶
type ServerBatchStorageManager interface {
// MetadataMaxSize returns the maximum size of metadata that can be stored by the manager
MetadataMaxSize(ctx context.Context) (int, error)
// Store persists the finalised number of segments in the batch.
// batchMetadata is optional decrypted metadata sent by the sender in the completion request (nil when absent).
Store(ctx context.Context, batchID []byte, numSegments int, batchMetadata []byte) error
// Retrieve returns the finalised number of segments and optional batch metadata.
// batchMetadata is nil when no metadata was provided during completion.
Retrieve(ctx context.Context, batchID []byte) (numSegments int, batchMetadata []byte, err error)
// Delete removes details of the batch
Delete(ctx context.Context, batchID []byte) error
}
ServerBatchStorageManager handles persistence and retrieval of the batch itself. Only once a batch can be retrieved from this manager can the batch data be assumed to fully exist in the remote location
type ServerConfig ¶
type ServerConfig struct {
// PrivKey is the server's Ed25519 private key used to sign handshake responses
PrivKey ed25519.PrivateKey
// AuthoriseSender returns true if the sender identified by the given Ed25519
// public key is permitted to initiate a batch transfer
AuthoriseSender func(ed25519.PublicKey) bool
// SecretManager handles persistence and retrieval of the shared secret created during the handshake
SecretManager ServerSharedSecretManager
// SegmentManager handles persistence and retrieval of segments of a batch
SegmentManager ServerSegmentStorageManager
// BatchManager handles persistence and retrieval of finalised batch metadata
BatchManager ServerBatchStorageManager
// CompletionMaxWait defines the duration a handler will wait for segments to have arrived (if not already available), prior to failing the completion request
CompletionMaxWait time.Duration
// CompletionPollWait defines the duration a handler will wait between checking for segment arrival, until CompletionMaxWait
CompletionPollWait time.Duration
// MaxSegmentSize is the maximum segment size (in bytes) the server will accept.
// If zero or negative, the server accepts whatever the client requests.
MaxSegmentSize int
// TransferManager maps opaque transferIDs to internal batchIDs
TransferManager ServerTransferToBatchManager
// AuthoriseRetriever returns true if the requester identified by the given Ed25519
// public key is permitted to retrieve batch data
AuthoriseRetriever func(ed25519.PublicKey) bool
// PackageURLPath is the path segment of a URL to which Package requests are to be sent, on the same host
PackageURLPath string
// CompletionURLPath is the path segment of a URL to which Completion requests are to be sent, on the same host
CompletionURLPath string
}
ServerConfig holds the configuration for server-side handlers.
type ServerSegmentStorageManager ¶
type ServerSegmentStorageManager interface {
// SegmentCount returns the number of segments currently in the store for a given batch
SegmentCount(ctx context.Context, batchID []byte) (int, error)
// List returns the segmentIDs for a given batch
List(ctx context.Context, batchID []byte) ([]byte, error)
// Store will persist the specified segment of the batch. The segment is encrypted using the BatchSecrets.SegmentSecret value, created during the handshake
Store(ctx context.Context, batchID []byte, segmentID []byte, encyptedSegment []byte) error
// Retrieve returns the specified segment of the batch. The returned data must be decrypted using the BatchSecrets.SegmentSecret value, created during the handshake
Retrieve(ctx context.Context, batchID []byte, segmentID []byte) ([]byte, error)
// Delete removes any segments associated with the batchID
Delete(ctx context.Context, batchID []byte) error
}
ServerSegmentStorageManager handles persistence and retrieval of segments of a batch, so that they can be transferred and stored concurrently The stored segment data will be encrypted using the BatchSecrets.SegmentSecret value, ensuring data security.
type ServerSharedSecretManager ¶
type ServerSharedSecretManager interface {
}
ServerSharedSecretManager handles persistence and retrieval of the shared secret established during the handshake.
type ServerTransferToBatchManager ¶
type ServerTransferToBatchManager interface {
Store(ctx context.Context, mapping *TransferMapping) error
Retrieve(ctx context.Context, transferID []byte) (*TransferMapping, error)
}
ServerTransferToBatchManager maps opaque transferIDs to internal batchIDs.
type TransferMapping ¶
TransferMapping holds the server-generated mapping from transferID to batchID.