Files
LookingGlass/common/src/network/protocol.c
2026-11-06 14:37:37 +11:00

731 lines
26 KiB
C

/**
* Looking Glass
* Copyright © 2017-2026 The Looking Glass Authors
* https://looking-glass.io
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 59
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "common/network/protocol.h"
#include <limits.h>
#include <string.h>
static const LGNetEnvelopeFlags ENVELOPE_FLAGS =
LG_NET_ENVELOPE_RESPONSE |
LG_NET_ENVELOPE_FINAL |
LG_NET_ENVELOPE_RELIABLE |
LG_NET_ENVELOPE_DATAGRAM |
LG_NET_ENVELOPE_ACK_REQUIRED |
LG_NET_ENVELOPE_ERROR |
LG_NET_ENVELOPE_EARLY_DATA;
static const LGNetCapabilityFlags CAPABILITY_FLAGS =
LG_NET_CAPABILITY_REQUIRED |
LG_NET_CAPABILITY_SELECTED |
LG_NET_CAPABILITY_RELIABLE |
LG_NET_CAPABILITY_DATAGRAM |
LG_NET_CAPABILITY_BULK |
LG_NET_CAPABILITY_MULTI_CLIENT;
static bool envelopeValid(const LGNetEnvelope * envelope, bool encode)
{
if (!envelope || envelope->wireMajor != LG_NET_WIRE_VERSION_MAJOR ||
envelope->headerSize < LG_NET_ENVELOPE_WIRE_SIZE ||
(encode && envelope->wireMinor != LG_NET_WIRE_VERSION_MINOR) ||
(encode && envelope->headerSize != LG_NET_ENVELOPE_WIRE_SIZE) ||
!envelope->service || !envelope->messageType ||
!envelope->serviceVersion || !envelope->messageVersion ||
envelope->payloadLength > LG_NET_MAX_PAYLOAD_LENGTH)
return false;
return true;
}
static bool authModeKnown(LGNetAuthMode mode)
{
return mode == LG_NET_AUTH_NONE || mode == LG_NET_AUTH_PASSWORD;
}
static bool passwordAlgorithmKnown(LGNetPasswordAlgorithm algorithm)
{
return algorithm == LG_NET_PASSWORD_PBKDF2_HMAC_SHA256;
}
static LGNetParseResult fixedDecodeResult(
const LGNetReader * reader, size_t expected, size_t actual)
{
if (!reader || !lgNetReaderValid(reader))
return LG_NET_PARSE_INVALID_VALUE;
if (actual != expected || lgNetReaderConsumed(reader) != expected)
return LG_NET_PARSE_INVALID_LENGTH;
return LG_NET_PARSE_OK;
}
void lgNetEnvelopeInit(LGNetEnvelope * envelope, LGNetService service,
uint16_t messageType, uint16_t serviceVersion,
uint16_t messageVersion)
{
if (!envelope)
return;
memset(envelope, 0, sizeof(*envelope));
envelope->wireMajor = LG_NET_WIRE_VERSION_MAJOR;
envelope->wireMinor = LG_NET_WIRE_VERSION_MINOR;
envelope->headerSize = LG_NET_ENVELOPE_WIRE_SIZE;
envelope->service = service;
envelope->messageType = messageType;
envelope->serviceVersion = serviceVersion;
envelope->messageVersion = messageVersion;
}
bool lgNetEnvelopeEncode(void * data, size_t size,
const LGNetEnvelope * envelope)
{
if (!data || size < LG_NET_ENVELOPE_WIRE_SIZE ||
!envelopeValid(envelope, true) ||
envelope->flags & ~ENVELOPE_FLAGS)
return false;
LGNetWriter writer;
lgNetWriterInit(&writer, data, size);
return
lgNetWriterU32(&writer, LG_NET_WIRE_MAGIC) &&
lgNetWriterU16(&writer, envelope->wireMajor) &&
lgNetWriterU16(&writer, envelope->wireMinor) &&
lgNetWriterU16(&writer, envelope->headerSize) &&
lgNetWriterU16(&writer, envelope->service) &&
lgNetWriterU16(&writer, envelope->messageType) &&
lgNetWriterU16(&writer, envelope->serviceVersion) &&
lgNetWriterU16(&writer, envelope->messageVersion) &&
lgNetWriterU16(&writer, 0) &&
lgNetWriterU32(&writer, envelope->flags) &&
lgNetWriterU32(&writer, envelope->payloadLength) &&
lgNetWriterU32(&writer, 0) &&
lgNetWriterU64(&writer, envelope->sessionEpoch) &&
lgNetWriterU64(&writer, envelope->componentEpoch) &&
lgNetWriterU64(&writer, envelope->sequence) &&
lgNetWriterU64(&writer, envelope->requestID) &&
lgNetWriterSize(&writer) == LG_NET_ENVELOPE_WIRE_SIZE;
}
LGNetParseResult lgNetEnvelopeDecode(LGNetEnvelope * envelope,
const void * data, size_t size)
{
if (!envelope || !data)
return LG_NET_PARSE_INVALID_VALUE;
if (size < LG_NET_ENVELOPE_WIRE_SIZE)
return LG_NET_PARSE_TRUNCATED;
LGNetEnvelope decoded;
uint32_t magic;
LGNetReader reader;
memset(&decoded, 0, sizeof(decoded));
lgNetReaderInit(&reader, data, LG_NET_ENVELOPE_WIRE_SIZE);
if (!lgNetReaderU32(&reader, &magic) ||
!lgNetReaderU16(&reader, &decoded.wireMajor) ||
!lgNetReaderU16(&reader, &decoded.wireMinor) ||
!lgNetReaderU16(&reader, &decoded.headerSize) ||
!lgNetReaderU16(&reader, &decoded.service) ||
!lgNetReaderU16(&reader, &decoded.messageType) ||
!lgNetReaderU16(&reader, &decoded.serviceVersion) ||
!lgNetReaderU16(&reader, &decoded.messageVersion) ||
!lgNetReaderZero(&reader, 2) ||
!lgNetReaderU32(&reader, &decoded.flags) ||
!lgNetReaderU32(&reader, &decoded.payloadLength) ||
!lgNetReaderZero(&reader, 4) ||
!lgNetReaderU64(&reader, &decoded.sessionEpoch) ||
!lgNetReaderU64(&reader, &decoded.componentEpoch) ||
!lgNetReaderU64(&reader, &decoded.sequence) ||
!lgNetReaderU64(&reader, &decoded.requestID))
return LG_NET_PARSE_INVALID_HEADER;
if (magic != LG_NET_WIRE_MAGIC)
return LG_NET_PARSE_INVALID_MAGIC;
if (decoded.wireMajor != LG_NET_WIRE_VERSION_MAJOR)
return LG_NET_PARSE_INVALID_VERSION;
if (decoded.headerSize < LG_NET_ENVELOPE_WIRE_SIZE)
return LG_NET_PARSE_INVALID_HEADER;
if (decoded.headerSize > size)
return LG_NET_PARSE_TRUNCATED;
if (!envelopeValid(&decoded, false) || decoded.flags & ~ENVELOPE_FLAGS)
return LG_NET_PARSE_INVALID_VALUE;
*envelope = decoded;
return LG_NET_PARSE_OK;
}
bool lgNetPacketSize(const LGNetEnvelope * envelope, size_t * size)
{
if (!size || !envelopeValid(envelope, false) ||
envelope->headerSize > SIZE_MAX - envelope->payloadLength)
return false;
*size = (size_t)envelope->headerSize + envelope->payloadLength;
return true;
}
bool lgNetPacketEncode(void * data, size_t size,
const LGNetEnvelope * envelope, const void * payload)
{
uint8_t header[LG_NET_ENVELOPE_WIRE_SIZE];
size_t wireSize;
if (!data || !envelope || (envelope->payloadLength && !payload) ||
!lgNetPacketSize(envelope, &wireSize) || wireSize > size ||
!lgNetEnvelopeEncode(header, sizeof(header), envelope))
return false;
if (envelope->payloadLength)
memmove((uint8_t *)data + envelope->headerSize, payload,
envelope->payloadLength);
memcpy(data, header, sizeof(header));
return true;
}
LGNetParseResult lgNetPacketDecode(LGNetPacketView * packet,
const void * data, size_t size)
{
if (!packet)
return LG_NET_PARSE_INVALID_VALUE;
LGNetPacketView decoded;
memset(&decoded, 0, sizeof(decoded));
const LGNetParseResult result = lgNetEnvelopeDecode(
&decoded.envelope, data, size);
if (result != LG_NET_PARSE_OK)
return result;
if (!lgNetPacketSize(&decoded.envelope, &decoded.wireSize))
return LG_NET_PARSE_INVALID_LENGTH;
if (decoded.wireSize > size)
return LG_NET_PARSE_TRUNCATED;
decoded.payload = (const uint8_t *)data + decoded.envelope.headerSize;
decoded.payloadLength = decoded.envelope.payloadLength;
*packet = decoded;
return LG_NET_PARSE_OK;
}
bool lgNetServiceKnown(LGNetService service)
{
return service >= LG_NET_SERVICE_CORE && service <= LG_NET_SERVICE_USB;
}
bool lgNetMessageKnown(LGNetService service, uint16_t messageType)
{
if (!messageType)
return false;
switch (service)
{
case LG_NET_SERVICE_CORE:
return messageType <= LG_NET_CORE_MESSAGE_ERROR;
case LG_NET_SERVICE_RECOVERY:
return messageType <= LG_NET_RECOVERY_MESSAGE_STATUS;
case LG_NET_SERVICE_VIDEO:
return messageType <= LG_NET_VIDEO_MESSAGE_STATUS;
case LG_NET_SERVICE_CURSOR:
return messageType <= LG_NET_CURSOR_MESSAGE_STATUS;
case LG_NET_SERVICE_INPUT:
return messageType <= LG_NET_INPUT_MESSAGE_KEYBOARD_LEDS;
case LG_NET_SERVICE_AUDIO:
return messageType <= LG_NET_AUDIO_MESSAGE_STATE_ACK;
case LG_NET_SERVICE_CLIPBOARD:
return messageType <= LG_NET_CLIPBOARD_MESSAGE_STATUS;
case LG_NET_SERVICE_FILE:
return messageType <= LG_NET_FILE_MESSAGE_STATUS;
case LG_NET_SERVICE_USB:
return messageType <= LG_NET_USB_MESSAGE_STATUS;
}
return false;
}
void lgNetHelloInit(LGNetHello * hello, LGNetRole role)
{
if (!hello)
return;
memset(hello, 0, sizeof(*hello));
hello->role = role;
hello->protocolMin = LG_NET_PROTOCOL_VERSION_MIN;
hello->protocolMax = LG_NET_PROTOCOL_VERSION_CURRENT;
hello->authModes = LG_NET_AUTH_MODES_DEFAULT;
hello->maxPayloadLength = LG_NET_MAX_PAYLOAD_LENGTH;
}
bool lgNetHelloValid(const LGNetHello * hello)
{
return hello &&
(hello->role == LG_NET_ROLE_CLIENT || hello->role == LG_NET_ROLE_SERVER) &&
hello->protocolMin && hello->protocolMin <= hello->protocolMax &&
hello->authModes &&
hello->maxPayloadLength &&
hello->maxPayloadLength <= LG_NET_MAX_PAYLOAD_LENGTH &&
hello->maxDatagramPayload <= hello->maxPayloadLength &&
hello->capabilityCount <= LG_NET_MAX_CAPABILITY_RECORDS;
}
bool lgNetHelloEncode(void * data, size_t size, const LGNetHello * hello)
{
if (!data || size < LG_NET_HELLO_WIRE_SIZE || !lgNetHelloValid(hello))
return false;
LGNetWriter writer;
lgNetWriterInit(&writer, data, size);
return
lgNetWriterU16(&writer, hello->role) &&
lgNetWriterU16(&writer, hello->protocolMin) &&
lgNetWriterU16(&writer, hello->protocolMax) &&
lgNetWriterU16(&writer, 0) &&
lgNetWriterU32(&writer, hello->authModes) &&
lgNetWriterU32(&writer, hello->maxPayloadLength) &&
lgNetWriterU32(&writer, hello->maxDatagramPayload) &&
lgNetWriterU32(&writer, hello->capabilityCount) &&
lgNetWriterBytes(&writer, hello->nonce, sizeof(hello->nonce)) &&
lgNetWriterSize(&writer) == LG_NET_HELLO_WIRE_SIZE;
}
LGNetParseResult lgNetHelloDecode(LGNetHello * hello,
const void * data, size_t size)
{
if (!hello || !data)
return LG_NET_PARSE_INVALID_VALUE;
if (size < LG_NET_HELLO_WIRE_SIZE)
return LG_NET_PARSE_TRUNCATED;
LGNetHello decoded;
LGNetReader reader;
memset(&decoded, 0, sizeof(decoded));
lgNetReaderInit(&reader, data, size);
if (!lgNetReaderU16(&reader, &decoded.role) ||
!lgNetReaderU16(&reader, &decoded.protocolMin) ||
!lgNetReaderU16(&reader, &decoded.protocolMax) ||
!lgNetReaderZero(&reader, 2) ||
!lgNetReaderU32(&reader, &decoded.authModes) ||
!lgNetReaderU32(&reader, &decoded.maxPayloadLength) ||
!lgNetReaderU32(&reader, &decoded.maxDatagramPayload) ||
!lgNetReaderU32(&reader, &decoded.capabilityCount) ||
!lgNetReaderBytes(&reader, decoded.nonce,
sizeof(decoded.nonce)))
return LG_NET_PARSE_INVALID_VALUE;
const LGNetParseResult result = fixedDecodeResult(
&reader, LG_NET_HELLO_WIRE_SIZE, size);
if (result != LG_NET_PARSE_OK)
return result;
if (!lgNetHelloValid(&decoded))
return LG_NET_PARSE_INVALID_VALUE;
*hello = decoded;
return LG_NET_PARSE_OK;
}
void lgNetHelloAckInit(LGNetHelloAck * hello, uint64_t sessionEpoch)
{
if (!hello)
return;
memset(hello, 0, sizeof(*hello));
hello->protocolVersion = LG_NET_PROTOCOL_VERSION_CURRENT;
hello->authMode = LG_NET_AUTH_MODE_DEFAULT;
hello->maxPayloadLength = LG_NET_MAX_PAYLOAD_LENGTH;
hello->sessionEpoch = sessionEpoch;
}
bool lgNetHelloAckValid(const LGNetHelloAck * hello)
{
return hello &&
hello->protocolVersion >= LG_NET_PROTOCOL_VERSION_MIN &&
hello->protocolVersion <= LG_NET_PROTOCOL_VERSION_MAX &&
authModeKnown(hello->authMode) &&
!(hello->flags & ~LG_NET_HELLO_ACK_RESUMED) &&
hello->maxPayloadLength &&
hello->maxPayloadLength <= LG_NET_MAX_PAYLOAD_LENGTH &&
hello->maxDatagramPayload <= hello->maxPayloadLength &&
hello->capabilityCount <= LG_NET_MAX_CAPABILITY_RECORDS &&
hello->sessionEpoch;
}
bool lgNetHelloAckEncode(
void * data, size_t size, const LGNetHelloAck * hello)
{
if (!data || size < LG_NET_HELLO_ACK_WIRE_SIZE ||
!lgNetHelloAckValid(hello))
return false;
LGNetWriter writer;
lgNetWriterInit(&writer, data, size);
return
lgNetWriterU16(&writer, hello->protocolVersion) &&
lgNetWriterU16(&writer, hello->authMode) &&
lgNetWriterU32(&writer, hello->flags) &&
lgNetWriterU32(&writer, hello->maxPayloadLength) &&
lgNetWriterU32(&writer, hello->maxDatagramPayload) &&
lgNetWriterU32(&writer, hello->capabilityCount) &&
lgNetWriterU32(&writer, 0) &&
lgNetWriterU64(&writer, hello->sessionEpoch) &&
lgNetWriterBytes(&writer, hello->nonce, sizeof(hello->nonce)) &&
lgNetWriterSize(&writer) == LG_NET_HELLO_ACK_WIRE_SIZE;
}
LGNetParseResult lgNetHelloAckDecode(LGNetHelloAck * hello,
const void * data, size_t size)
{
if (!hello || !data)
return LG_NET_PARSE_INVALID_VALUE;
if (size < LG_NET_HELLO_ACK_WIRE_SIZE)
return LG_NET_PARSE_TRUNCATED;
LGNetHelloAck decoded;
LGNetReader reader;
memset(&decoded, 0, sizeof(decoded));
lgNetReaderInit(&reader, data, size);
if (!lgNetReaderU16(&reader, &decoded.protocolVersion) ||
!lgNetReaderU16(&reader, &decoded.authMode) ||
!lgNetReaderU32(&reader, &decoded.flags) ||
!lgNetReaderU32(&reader, &decoded.maxPayloadLength) ||
!lgNetReaderU32(&reader, &decoded.maxDatagramPayload) ||
!lgNetReaderU32(&reader, &decoded.capabilityCount) ||
!lgNetReaderZero(&reader, 4) ||
!lgNetReaderU64(&reader, &decoded.sessionEpoch) ||
!lgNetReaderBytes(&reader, decoded.nonce,
sizeof(decoded.nonce)))
return LG_NET_PARSE_INVALID_VALUE;
const LGNetParseResult result = fixedDecodeResult(
&reader, LG_NET_HELLO_ACK_WIRE_SIZE, size);
if (result != LG_NET_PARSE_OK)
return result;
if (!lgNetHelloAckValid(&decoded))
return LG_NET_PARSE_INVALID_VALUE;
*hello = decoded;
return LG_NET_PARSE_OK;
}
bool lgNetCapabilityValid(const LGNetCapability * capability)
{
if (!capability || !capability->service ||
!capability->versionMin ||
capability->versionMin > capability->versionMax ||
capability->flags & ~CAPABILITY_FLAGS ||
!capability->maxPayloadLength ||
capability->maxPayloadLength > LG_NET_MAX_PAYLOAD_LENGTH ||
capability->maxDatagramPayload > capability->maxPayloadLength)
return false;
if ((capability->flags & LG_NET_CAPABILITY_SELECTED) &&
capability->versionMin != capability->versionMax)
return false;
if (!!(capability->flags & LG_NET_CAPABILITY_DATAGRAM) !=
!!capability->maxDatagramPayload)
return false;
return true;
}
bool lgNetCapabilityEncode(void * data, size_t size,
const LGNetCapability * capability)
{
if (!data || size < LG_NET_CAPABILITY_WIRE_SIZE ||
!lgNetCapabilityValid(capability))
return false;
LGNetWriter writer;
lgNetWriterInit(&writer, data, size);
return
lgNetWriterU16(&writer, capability->service) &&
lgNetWriterU16(&writer, capability->versionMin) &&
lgNetWriterU16(&writer, capability->versionMax) &&
lgNetWriterU16(&writer, capability->flags) &&
lgNetWriterU64(&writer, capability->features) &&
lgNetWriterU32(&writer, capability->maxPayloadLength) &&
lgNetWriterU32(&writer, capability->maxDatagramPayload) &&
lgNetWriterU16(&writer, capability->maxBidirectionalStreams) &&
lgNetWriterU16(&writer, capability->maxUnidirectionalStreams) &&
lgNetWriterU32(&writer, 0) &&
lgNetWriterSize(&writer) == LG_NET_CAPABILITY_WIRE_SIZE;
}
LGNetParseResult lgNetCapabilityDecode(LGNetCapability * capability,
const void * data, size_t size)
{
if (!capability || !data)
return LG_NET_PARSE_INVALID_VALUE;
if (size < LG_NET_CAPABILITY_WIRE_SIZE)
return LG_NET_PARSE_TRUNCATED;
LGNetCapability decoded;
LGNetReader reader;
memset(&decoded, 0, sizeof(decoded));
lgNetReaderInit(&reader, data, size);
if (!lgNetReaderU16(&reader, &decoded.service) ||
!lgNetReaderU16(&reader, &decoded.versionMin) ||
!lgNetReaderU16(&reader, &decoded.versionMax) ||
!lgNetReaderU16(&reader, &decoded.flags) ||
!lgNetReaderU64(&reader, &decoded.features) ||
!lgNetReaderU32(&reader, &decoded.maxPayloadLength) ||
!lgNetReaderU32(&reader, &decoded.maxDatagramPayload) ||
!lgNetReaderU16(&reader, &decoded.maxBidirectionalStreams) ||
!lgNetReaderU16(&reader, &decoded.maxUnidirectionalStreams) ||
!lgNetReaderZero(&reader, 4))
return LG_NET_PARSE_INVALID_VALUE;
const LGNetParseResult result = fixedDecodeResult(
&reader, LG_NET_CAPABILITY_WIRE_SIZE, size);
if (result != LG_NET_PARSE_OK)
return result;
if (!lgNetCapabilityValid(&decoded))
return LG_NET_PARSE_INVALID_VALUE;
*capability = decoded;
return LG_NET_PARSE_OK;
}
size_t lgNetPasswordChallengeSize(
const LGNetPasswordChallenge * challenge)
{
if (!lgNetPasswordChallengeValid(challenge))
return 0;
return LG_NET_PASSWORD_CHALLENGE_HEADER_SIZE +
challenge->saltLength + challenge->challengeLength;
}
bool lgNetPasswordChallengeValid(
const LGNetPasswordChallenge * challenge)
{
return challenge && passwordAlgorithmKnown(challenge->algorithm) &&
challenge->rounds && challenge->saltLength &&
challenge->saltLength <= LG_NET_PASSWORD_SALT_MAX_LENGTH &&
challenge->challengeLength &&
challenge->challengeLength <= LG_NET_PASSWORD_CHALLENGE_MAX_LENGTH;
}
bool lgNetPasswordChallengeEncode(void * data, size_t size,
const LGNetPasswordChallenge * challenge)
{
const size_t required = lgNetPasswordChallengeSize(challenge);
if (!data || !required || size < required)
return false;
LGNetWriter writer;
lgNetWriterInit(&writer, data, size);
return
lgNetWriterU16(&writer, challenge->algorithm) &&
lgNetWriterU16(&writer, 0) &&
lgNetWriterU32(&writer, challenge->rounds) &&
lgNetWriterU16(&writer, challenge->saltLength) &&
lgNetWriterU16(&writer, challenge->challengeLength) &&
lgNetWriterU32(&writer, 0) &&
lgNetWriterBytes(&writer, challenge->salt,
challenge->saltLength) &&
lgNetWriterBytes(&writer, challenge->challenge,
challenge->challengeLength) &&
lgNetWriterSize(&writer) == required;
}
LGNetParseResult lgNetPasswordChallengeDecode(
LGNetPasswordChallenge * challenge, const void * data, size_t size)
{
if (!challenge || !data)
return LG_NET_PARSE_INVALID_VALUE;
if (size < LG_NET_PASSWORD_CHALLENGE_HEADER_SIZE)
return LG_NET_PARSE_TRUNCATED;
LGNetPasswordChallenge decoded;
LGNetReader reader;
memset(&decoded, 0, sizeof(decoded));
lgNetReaderInit(&reader, data, size);
if (!lgNetReaderU16(&reader, &decoded.algorithm) ||
!lgNetReaderZero(&reader, 2) ||
!lgNetReaderU32(&reader, &decoded.rounds) ||
!lgNetReaderU16(&reader, &decoded.saltLength) ||
!lgNetReaderU16(&reader, &decoded.challengeLength) ||
!lgNetReaderZero(&reader, 4))
return LG_NET_PARSE_INVALID_VALUE;
if (decoded.saltLength > LG_NET_PASSWORD_SALT_MAX_LENGTH ||
decoded.challengeLength > LG_NET_PASSWORD_CHALLENGE_MAX_LENGTH)
return LG_NET_PARSE_INVALID_LENGTH;
const size_t expected = LG_NET_PASSWORD_CHALLENGE_HEADER_SIZE +
decoded.saltLength + decoded.challengeLength;
if (size < expected)
return LG_NET_PARSE_TRUNCATED;
if (size != expected ||
!lgNetReaderBytes(&reader, decoded.salt, decoded.saltLength) ||
!lgNetReaderBytes(&reader, decoded.challenge,
decoded.challengeLength))
return LG_NET_PARSE_INVALID_LENGTH;
if (!lgNetPasswordChallengeValid(&decoded))
return LG_NET_PARSE_INVALID_VALUE;
*challenge = decoded;
return LG_NET_PARSE_OK;
}
size_t lgNetPasswordResponseSize(const LGNetPasswordResponse * response)
{
if (!lgNetPasswordResponseValid(response))
return 0;
return LG_NET_PASSWORD_RESPONSE_HEADER_SIZE +
response->nonceLength + response->proofLength;
}
bool lgNetPasswordResponseValid(const LGNetPasswordResponse * response)
{
return response && passwordAlgorithmKnown(response->algorithm) &&
response->nonceLength &&
response->nonceLength <= LG_NET_PASSWORD_NONCE_MAX_LENGTH &&
response->proofLength &&
response->proofLength <= LG_NET_PASSWORD_PROOF_MAX_LENGTH;
}
bool lgNetPasswordResponseEncode(void * data, size_t size,
const LGNetPasswordResponse * response)
{
const size_t required = lgNetPasswordResponseSize(response);
if (!data || !required || size < required)
return false;
LGNetWriter writer;
lgNetWriterInit(&writer, data, size);
return
lgNetWriterU16(&writer, response->algorithm) &&
lgNetWriterU16(&writer, response->nonceLength) &&
lgNetWriterU16(&writer, response->proofLength) &&
lgNetWriterU16(&writer, 0) &&
lgNetWriterU32(&writer, 0) &&
lgNetWriterBytes(&writer, response->nonce,
response->nonceLength) &&
lgNetWriterBytes(&writer, response->proof,
response->proofLength) &&
lgNetWriterSize(&writer) == required;
}
LGNetParseResult lgNetPasswordResponseDecode(
LGNetPasswordResponse * response, const void * data, size_t size)
{
if (!response || !data)
return LG_NET_PARSE_INVALID_VALUE;
if (size < LG_NET_PASSWORD_RESPONSE_HEADER_SIZE)
return LG_NET_PARSE_TRUNCATED;
LGNetPasswordResponse decoded;
LGNetReader reader;
memset(&decoded, 0, sizeof(decoded));
lgNetReaderInit(&reader, data, size);
if (!lgNetReaderU16(&reader, &decoded.algorithm) ||
!lgNetReaderU16(&reader, &decoded.nonceLength) ||
!lgNetReaderU16(&reader, &decoded.proofLength) ||
!lgNetReaderZero(&reader, 2) ||
!lgNetReaderZero(&reader, 4))
return LG_NET_PARSE_INVALID_VALUE;
if (decoded.nonceLength > LG_NET_PASSWORD_NONCE_MAX_LENGTH ||
decoded.proofLength > LG_NET_PASSWORD_PROOF_MAX_LENGTH)
return LG_NET_PARSE_INVALID_LENGTH;
const size_t expected = LG_NET_PASSWORD_RESPONSE_HEADER_SIZE +
decoded.nonceLength + decoded.proofLength;
if (size < expected)
return LG_NET_PARSE_TRUNCATED;
if (size != expected ||
!lgNetReaderBytes(&reader, decoded.nonce, decoded.nonceLength) ||
!lgNetReaderBytes(&reader, decoded.proof, decoded.proofLength))
return LG_NET_PARSE_INVALID_LENGTH;
if (!lgNetPasswordResponseValid(&decoded))
return LG_NET_PARSE_INVALID_VALUE;
*response = decoded;
return LG_NET_PARSE_OK;
}
size_t lgNetAuthResultSize(const LGNetAuthResult * result)
{
if (!lgNetAuthResultValid(result))
return 0;
return LG_NET_AUTH_RESULT_HEADER_SIZE + result->proofLength;
}
bool lgNetAuthResultValid(const LGNetAuthResult * result)
{
return result &&
result->result >= LG_NET_AUTH_RESULT_ACCEPTED &&
result->result <= LG_NET_AUTH_RESULT_ERROR &&
result->proofLength <= LG_NET_PASSWORD_PROOF_MAX_LENGTH;
}
bool lgNetAuthResultEncode(
void * data, size_t size, const LGNetAuthResult * result)
{
const size_t required = lgNetAuthResultSize(result);
if (!data || !required || size < required)
return false;
LGNetWriter writer;
lgNetWriterInit(&writer, data, size);
return
lgNetWriterU16(&writer, result->result) &&
lgNetWriterU16(&writer, result->proofLength) &&
lgNetWriterU32(&writer, result->retryAfterMs) &&
lgNetWriterU32(&writer, 0) &&
lgNetWriterBytes(&writer, result->proof,
result->proofLength) &&
lgNetWriterSize(&writer) == required;
}
LGNetParseResult lgNetAuthResultDecode(LGNetAuthResult * result,
const void * data, size_t size)
{
if (!result || !data)
return LG_NET_PARSE_INVALID_VALUE;
if (size < LG_NET_AUTH_RESULT_HEADER_SIZE)
return LG_NET_PARSE_TRUNCATED;
LGNetAuthResult decoded;
LGNetReader reader;
memset(&decoded, 0, sizeof(decoded));
lgNetReaderInit(&reader, data, size);
if (!lgNetReaderU16(&reader, &decoded.result) ||
!lgNetReaderU16(&reader, &decoded.proofLength) ||
!lgNetReaderU32(&reader, &decoded.retryAfterMs) ||
!lgNetReaderZero(&reader, 4))
return LG_NET_PARSE_INVALID_VALUE;
if (decoded.proofLength > LG_NET_PASSWORD_PROOF_MAX_LENGTH)
return LG_NET_PARSE_INVALID_LENGTH;
const size_t expected = LG_NET_AUTH_RESULT_HEADER_SIZE +
decoded.proofLength;
if (size < expected)
return LG_NET_PARSE_TRUNCATED;
if (size != expected ||
!lgNetReaderBytes(&reader, decoded.proof, decoded.proofLength))
return LG_NET_PARSE_INVALID_LENGTH;
if (!lgNetAuthResultValid(&decoded))
return LG_NET_PARSE_INVALID_VALUE;
*result = decoded;
return LG_NET_PARSE_OK;
}