#include "open62541/transport_generated.h"
#include "ua_securechannel.h"
#include "ua_types_encoding_binary.h"
UA_StatusCode
UA_SecureChannel_generateLocalNonce(UA_SecureChannel *channel) {
const UA_SecurityPolicy *sp = channel->securityPolicy;
UA_CHECK_MEM(sp, return UA_STATUSCODE_BADINTERNALERROR);
UA_LOG_DEBUG_CHANNEL(sp->logger, channel, "Generating new local nonce");
size_t nonceLength = sp->nonceLength;
if(nonceLength == 0)
return UA_STATUSCODE_GOOD;
if(nonceLength < 32)
nonceLength = 32;
if(channel->localNonce.length != nonceLength) {
UA_ByteString_clear(&channel->localNonce);
UA_StatusCode res = UA_ByteString_allocBuffer(&channel->localNonce, nonceLength);
UA_CHECK_STATUS(res, return res);
}
channel->localNonce.data[0] = 'e';
channel->localNonce.data[1] = 'p';
channel->localNonce.data[2] = 'h';
return sp->generateNonce(sp, channel->channelContext, &channel->localNonce);
}
#define IKM_PREPEND_LENGTH(channel) \
((channel)->enhancedSecurity ? ((channel)->securityPolicy->nonceLength / 2) : 0)
static UA_StatusCode
prepareKeyInput(UA_SecureChannel *channel, const UA_ByteString *nonce,
UA_ByteString *outInput, UA_ByteString *outCombined) {
size_t ikmLen = IKM_PREPEND_LENGTH(channel);
if(ikmLen == 0) {
*outInput = *nonce;
*outCombined = UA_BYTESTRING_NULL;
return UA_STATUSCODE_GOOD;
}
UA_StatusCode res =
UA_ByteString_allocBuffer(outCombined, ikmLen + nonce->length);
if(res != UA_STATUSCODE_GOOD)
return res;
if(channel->currentIKM.length == ikmLen)
memcpy(outCombined->data, channel->currentIKM.data, ikmLen);
else
memset(outCombined->data, 0, ikmLen);
memcpy(outCombined->data + ikmLen, nonce->data, nonce->length);
*outInput = *outCombined;
return UA_STATUSCODE_GOOD;
}
static UA_StatusCode
captureIKMSlot(UA_SecureChannel *channel, const UA_ByteString *combined) {
size_t ikmLen = IKM_PREPEND_LENGTH(channel);
if(ikmLen == 0)
return UA_STATUSCODE_GOOD;
UA_ByteString_clear(&channel->currentIKM);
UA_ByteString ikmSlot = {ikmLen, combined->data};
return UA_ByteString_copy(&ikmSlot, &channel->currentIKM);
}
UA_StatusCode
UA_SecureChannel_generateLocalKeys(UA_SecureChannel *channel) {
const UA_SecurityPolicy *sp = channel->securityPolicy;
UA_CHECK_MEM(sp, return UA_STATUSCODE_BADINTERNALERROR);
UA_LOG_DEBUG_CHANNEL(sp->logger, channel, "Generating new local keys");
void *cc = channel->channelContext;
const UA_SecurityPolicyEncryptionAlgorithm *ea = &sp->symEncryptionAlgorithm;
UA_ByteString buf;
size_t encrKL = ea->getLocalKeyLength(sp, cc);
size_t encrBS = ea->getRemoteBlockSize(sp, cc);
size_t ivLen = ea->getLocalIvLength ? ea->getLocalIvLength(sp, cc) : encrBS;
size_t signKL = sp->symSignatureAlgorithm.getLocalKeyLength(sp, cc);
if(ivLen + signKL + encrKL == 0)
return UA_STATUSCODE_GOOD;
UA_StatusCode res = UA_ByteString_allocBuffer(&buf, ivLen + signKL + encrKL);
UA_CHECK_STATUS(res, return res);
UA_ByteString localSigningKey = {signKL, buf.data};
UA_ByteString localEncryptingKey = {encrKL, &buf.data[signKL]};
UA_ByteString localIv = {ivLen, &buf.data[signKL + encrKL]};
buf.data[0] = 0x00;
UA_ByteString secretInput, seedInput = channel->localNonce;
UA_ByteString secretCombined = UA_BYTESTRING_NULL;
res = prepareKeyInput(channel, &channel->remoteNonce,
&secretInput, &secretCombined);
UA_CHECK_STATUS(res, goto error);
res = sp->generateKey(sp, cc, &secretInput, &seedInput, &buf);
UA_CHECK_STATUS(res, goto error);
res |= sp->setLocalSymSigningKey(sp, cc, &localSigningKey);
res |= sp->setLocalSymEncryptingKey(sp, cc, &localEncryptingKey);
res |= sp->setLocalSymIv(sp, cc, &localIv);
error:
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_CHANNEL(sp->logger, channel,
"Could not generate local keys (%s)",
UA_StatusCode_name(res));
}
UA_ByteString_clear(&buf);
UA_ByteString_clear(&secretCombined);
return res;
}
UA_StatusCode
generateRemoteKeys(UA_SecureChannel *channel) {
const UA_SecurityPolicy *sp = channel->securityPolicy;
UA_CHECK_MEM(sp, return UA_STATUSCODE_BADINTERNALERROR);
UA_LOG_DEBUG_CHANNEL(sp->logger, channel, "Generating new remote keys");
void *cc = channel->channelContext;
const UA_SecurityPolicyEncryptionAlgorithm *ea = &sp->symEncryptionAlgorithm;
UA_ByteString buf;
size_t encrKL = ea->getRemoteKeyLength(sp, cc);
size_t encrBS = ea->getRemoteBlockSize(sp, cc);
size_t ivLen = ea->getLocalIvLength ? ea->getLocalIvLength(sp, cc) : encrBS;
size_t signKL = sp->symSignatureAlgorithm.getRemoteKeyLength(sp, cc);
if(ivLen + signKL + encrKL == 0)
return UA_STATUSCODE_GOOD;
UA_StatusCode res = UA_ByteString_allocBuffer(&buf, ivLen + signKL + encrKL);
UA_CHECK_STATUS(res, return res);
UA_ByteString remoteSigningKey = {signKL, buf.data};
UA_ByteString remoteEncryptingKey = {encrKL, &buf.data[signKL]};
UA_ByteString remoteIv = {ivLen, &buf.data[signKL + encrKL]};
buf.data[0] = 0x00;
UA_ByteString secretInput, seedInput = channel->remoteNonce;
UA_ByteString secretCombined = UA_BYTESTRING_NULL;
res = prepareKeyInput(channel, &channel->localNonce,
&secretInput, &secretCombined);
UA_CHECK_STATUS(res, goto error);
res = sp->generateKey(sp, cc, &secretInput, &seedInput, &buf);
UA_CHECK_STATUS(res, goto error);
res = captureIKMSlot(channel, &secretCombined);
if(res != UA_STATUSCODE_GOOD) {
res = UA_STATUSCODE_BADOUTOFMEMORY;
goto error;
}
res |= sp->setRemoteSymSigningKey(sp, cc, &remoteSigningKey);
res |= sp->setRemoteSymEncryptingKey(sp, cc, &remoteEncryptingKey);
res |= sp->setRemoteSymIv(sp, cc, &remoteIv);
error:
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_CHANNEL(sp->logger, channel,
"Could not generate remote keys (%s)",
UA_StatusCode_name(res));
}
UA_ByteString_clear(&buf);
UA_ByteString_clear(&secretCombined);
return res;
}
#ifdef UA_ENABLE_ENCRYPTION
static UA_StatusCode
hashCert(const UA_SecurityPolicy *sp, const UA_ByteString *cert,
UA_ByteString *hash) {
return UA_SecurityPolicy_hashCertificate(sp, cert, hash);
}
#else
static UA_StatusCode
hashCert(const UA_SecurityPolicy *sp, const UA_ByteString *cert,
UA_ByteString *hash) {
(void)sp; (void)cert; (void)hash;
return UA_STATUSCODE_BADINTERNALERROR;
}
#endif
#define UA_MAX_SIGDATA_CERTS 4
static UA_StatusCode
buildChannelBoundSignatureData(const UA_SecureChannel *channel,
const UA_ByteString *firstNonce,
const UA_ByteString *const *certs, size_t certCount,
const UA_ByteString *lastNonce, UA_ByteString *out) {
const UA_SecurityPolicy *sp = channel->securityPolicy;
if(!sp || !channel->enhancedSecurity || certCount > UA_MAX_SIGDATA_CERTS ||
channel->channelThumbprint.length == 0)
return UA_STATUSCODE_BADINTERNALERROR;
UA_ByteString hashes[UA_MAX_SIGDATA_CERTS];
for(size_t i = 0; i < UA_MAX_SIGDATA_CERTS; i++)
hashes[i] = UA_BYTESTRING_NULL;
UA_StatusCode res = UA_STATUSCODE_GOOD;
size_t hashesLen = 0;
for(size_t i = 0; i < certCount && res == UA_STATUSCODE_GOOD; i++) {
UA_ByteString leaf = getLeafCertificate(*certs[i]);
res = hashCert(sp, &leaf, &hashes[i]);
hashesLen += hashes[i].length;
}
if(res != UA_STATUSCODE_GOOD)
goto cleanup;
const UA_ByteString *tp = &channel->channelThumbprint;
res = UA_ByteString_allocBuffer(out, tp->length + firstNonce->length +
hashesLen + lastNonce->length);
if(res != UA_STATUSCODE_GOOD)
goto cleanup;
size_t o = 0;
memcpy(out->data + o, tp->data, tp->length); o += tp->length;
memcpy(out->data + o, firstNonce->data, firstNonce->length); o += firstNonce->length;
for(size_t i = 0; i < certCount; i++) {
memcpy(out->data + o, hashes[i].data, hashes[i].length);
o += hashes[i].length;
}
memcpy(out->data + o, lastNonce->data, lastNonce->length);
cleanup:
for(size_t i = 0; i < certCount; i++)
UA_ByteString_clear(&hashes[i]);
return res;
}
UA_StatusCode
UA_SecureChannel_buildCreateSessionSignatureData(
const UA_SecureChannel *channel, const UA_ByteString *clientNonce,
const UA_ByteString *serverNonce, const UA_ByteString *serverChannelCert,
const UA_ByteString *clientChannelCert, UA_ByteString *out) {
const UA_ByteString *certs[] = {serverChannelCert, clientChannelCert};
return buildChannelBoundSignatureData(channel, clientNonce, certs, 2,
serverNonce, out);
}
UA_StatusCode
UA_SecureChannel_buildActivateSessionSignatureData(
const UA_SecureChannel *channel, const UA_ByteString *serverNonce,
const UA_ByteString *clientNonce, const UA_ByteString *serverAppCert,
const UA_ByteString *serverChannelCert, const UA_ByteString *clientChannelCert,
UA_ByteString *out) {
const UA_ByteString *certs[] = {serverAppCert, serverChannelCert, clientChannelCert};
return buildChannelBoundSignatureData(channel, serverNonce, certs, 3,
clientNonce, out);
}
UA_StatusCode
UA_SecureChannel_buildUserTokenSignatureData(
const UA_SecureChannel *channel, const UA_ByteString *serverNonce,
const UA_ByteString *clientNonce, const UA_ByteString *serverAppCert,
const UA_ByteString *serverChannelCert, const UA_ByteString *clientAppCert,
const UA_ByteString *clientChannelCert, UA_ByteString *out) {
const UA_ByteString *certs[] = {serverAppCert, serverChannelCert,
clientAppCert, clientChannelCert};
return buildChannelBoundSignatureData(channel, serverNonce, certs, 4,
clientNonce, out);
}
#define UA_SECURECHANNEL_ASYMMETRIC_SECURITYHEADER_FIXED_LENGTH 12
size_t
calculateAsymAlgSecurityHeaderLength(const UA_SecureChannel *channel) {
const UA_SecurityPolicy *sp = channel->securityPolicy;
UA_CHECK_MEM(sp, return UA_STATUSCODE_BADINTERNALERROR);
size_t asymHeaderLength = UA_SECURECHANNEL_ASYMMETRIC_SECURITYHEADER_FIXED_LENGTH +
sp->policyUri.length;
if(channel->securityMode == UA_MESSAGESECURITYMODE_NONE)
return asymHeaderLength;
asymHeaderLength += 20;
asymHeaderLength += sp->localCertificate.length;
return asymHeaderLength;
}
UA_StatusCode
prependHeadersAsym(UA_SecureChannel *const channel, UA_Byte *header_pos,
const UA_Byte *buf_end, size_t totalLength,
size_t securityHeaderLength, UA_UInt32 requestId,
size_t *const encryptedLength) {
const UA_SecurityPolicy *sp = channel->securityPolicy;
UA_CHECK_MEM(sp, return UA_STATUSCODE_BADINTERNALERROR);
void *cc = channel->channelContext;
*encryptedLength = totalLength;
if(channel->securityMode != UA_MESSAGESECURITYMODE_NONE) {
size_t dataToEncryptLength = totalLength -
(UA_SECURECHANNEL_CHANNELHEADER_LENGTH + securityHeaderLength);
size_t plainTextBlockSize = sp->asymEncryptionAlgorithm.
getRemotePlainTextBlockSize(sp, cc);
size_t encryptedBlockSize = sp->asymEncryptionAlgorithm.
getRemoteBlockSize(sp, cc);
UA_assert(plainTextBlockSize > 0);
UA_assert(dataToEncryptLength % plainTextBlockSize == 0);
size_t blocks = dataToEncryptLength / plainTextBlockSize;
*encryptedLength = totalLength + blocks * (encryptedBlockSize - plainTextBlockSize);
}
UA_TcpMessageHeader messageHeader;
messageHeader.messageTypeAndChunkType = UA_MESSAGETYPE_OPN + UA_CHUNKTYPE_FINAL;
messageHeader.messageSize = (UA_UInt32)*encryptedLength;
UA_UInt32 secureChannelId = channel->securityToken.channelId;
UA_StatusCode res = UA_STATUSCODE_GOOD;
res |= UA_encodeBinaryInternal(&messageHeader,
&UA_TRANSPORT[UA_TRANSPORT_TCPMESSAGEHEADER],
&header_pos, &buf_end, NULL, NULL, NULL);
res |= UA_UInt32_encodeBinary(&secureChannelId, &header_pos, buf_end);
UA_CHECK_STATUS(res, return res);
UA_AsymmetricAlgorithmSecurityHeader asymHeader;
UA_AsymmetricAlgorithmSecurityHeader_init(&asymHeader);
asymHeader.securityPolicyUri = sp->policyUri;
if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGN ||
channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) {
asymHeader.senderCertificate = sp->localCertificate;
asymHeader.receiverCertificateThumbprint.length = 20;
asymHeader.receiverCertificateThumbprint.data = channel->remoteCertificateThumbprint;
}
res = UA_encodeBinaryInternal(&asymHeader,
&UA_TRANSPORT[UA_TRANSPORT_ASYMMETRICALGORITHMSECURITYHEADER],
&header_pos, &buf_end, NULL, NULL, NULL);
UA_CHECK_STATUS(res, return res);
UA_SequenceHeader seqHeader;
seqHeader.requestId = requestId;
seqHeader.sequenceNumber = UA_SecureChannel_nextSequenceNumber(channel);
res = UA_encodeBinaryInternal(&seqHeader, &UA_TRANSPORT[UA_TRANSPORT_SEQUENCEHEADER],
&header_pos, &buf_end, NULL, NULL, NULL);
return res;
}
void
hideBytesAsym(const UA_SecureChannel *channel, UA_Byte **buf_start,
const UA_Byte **buf_end) {
*buf_start += UA_SECURECHANNEL_CHANNELHEADER_LENGTH;
*buf_start += calculateAsymAlgSecurityHeaderLength(channel);
if(channel->securityMode == UA_MESSAGESECURITYMODE_NONE) {
*buf_start += UA_SECURECHANNEL_SEQUENCEHEADER_LENGTH;
return;
}
void *cc = channel->channelContext;
const UA_SecurityPolicy *sp = channel->securityPolicy;
size_t plainTextBlockSize =
sp->asymEncryptionAlgorithm.getRemotePlainTextBlockSize(sp, cc);
size_t encryptedBlockSize =
sp->asymEncryptionAlgorithm.getRemoteBlockSize(sp, cc);
size_t max_encrypted = (size_t)(*buf_end - *buf_start);
UA_assert(encryptedBlockSize > 0);
size_t max_blocks = max_encrypted / encryptedBlockSize;
size_t max_plaintext = max_blocks * plainTextBlockSize;
max_plaintext -= UA_SECURECHANNEL_SEQUENCEHEADER_LENGTH;
max_plaintext -= sp->asymSignatureAlgorithm.getLocalSignatureSize(sp, cc);
UA_Boolean extraPadding =
(sp->asymEncryptionAlgorithm.getRemoteKeyLength(sp, cc) > 2048);
max_plaintext -= (UA_LIKELY(!extraPadding)) ? 1u : 2u;
*buf_end = *buf_start + max_plaintext;
*buf_start += UA_SECURECHANNEL_SEQUENCEHEADER_LENGTH;
}
void
padChunk(UA_SecureChannel *channel,
const UA_SecurityPolicySignatureAlgorithm *sa,
const UA_SecurityPolicyEncryptionAlgorithm *ea,
const UA_Byte *start, UA_Byte **pos) {
UA_SecurityPolicy *sp = channel->securityPolicy;
void *cc = channel->channelContext;
const size_t bytesToWrite = (uintptr_t)*pos - (uintptr_t)start;
size_t signatureSize = sa->getLocalSignatureSize(sp, cc);
size_t plainTextBlockSize = ea->getRemotePlainTextBlockSize(sp, cc);
UA_Boolean extraPadding = (ea->getRemoteKeyLength(sp, cc) > 2048);
size_t paddingBytes = (UA_LIKELY(!extraPadding)) ? 1u : 2u;
UA_assert(plainTextBlockSize > 0);
size_t lastBlock = ((bytesToWrite + signatureSize + paddingBytes) % plainTextBlockSize);
size_t paddingLength = (lastBlock != 0) ? plainTextBlockSize - lastBlock : 0;
UA_assert((bytesToWrite + signatureSize +
paddingBytes + paddingLength) % plainTextBlockSize == 0);
UA_LOG_TRACE_CHANNEL(sp->logger, channel,
"Add %lu bytes of padding plus %lu padding size bytes",
(long unsigned int)paddingLength,
(long unsigned int)paddingBytes);
UA_Byte paddingByte = (UA_Byte)paddingLength;
for(size_t i = 0; i <= paddingLength; ++i) {
**pos = paddingByte;
++*pos;
}
if(extraPadding) {
**pos = (UA_Byte)(paddingLength >> 8u);
++*pos;
}
}
UA_StatusCode
signAndEncryptAsym(UA_SecureChannel *channel, size_t preSignLength,
UA_ByteString *buf, size_t securityHeaderLength,
size_t totalLength) {
if(channel->securityMode != UA_MESSAGESECURITYMODE_SIGN &&
channel->securityMode != UA_MESSAGESECURITYMODE_SIGNANDENCRYPT)
return UA_STATUSCODE_GOOD;
const UA_SecurityPolicy *sp = channel->securityPolicy;
void *cc = channel->channelContext;
UA_Boolean firstOPN = (channel->enhancedSecurity &&
channel->channelThumbprint.length == 0);
UA_ByteString *appendSig = NULL;
if(firstOPN && channel->firstRequestSignature.length > 0)
appendSig = &channel->firstRequestSignature;
size_t sigsize = sp->asymSignatureAlgorithm.getLocalSignatureSize(sp, cc);
UA_ByteString signature = {sigsize, buf->data + preSignLength};
UA_StatusCode retval;
if(appendSig) {
size_t bodyLen = preSignLength;
size_t extLen = bodyLen + appendSig->length;
UA_Byte *ext = (UA_Byte*)UA_malloc(extLen);
if(!ext)
return UA_STATUSCODE_BADOUTOFMEMORY;
memcpy(ext, buf->data, bodyLen);
memcpy(ext + bodyLen, appendSig->data, appendSig->length);
UA_ByteString dataToSignExt = {extLen, ext};
retval = sp->asymSignatureAlgorithm.sign(sp, cc, &dataToSignExt, &signature);
UA_free(ext);
} else {
const UA_ByteString dataToSign = {preSignLength, buf->data};
retval = sp->asymSignatureAlgorithm.sign(sp, cc, &dataToSign, &signature);
if(retval == UA_STATUSCODE_GOOD && firstOPN && signature.length > 0) {
UA_StatusCode clip =
UA_ByteString_copy(&signature, &channel->firstRequestSignature);
if(clip != UA_STATUSCODE_GOOD)
retval = clip;
}
}
UA_CHECK_STATUS(retval, return retval);
if(appendSig != NULL) {
UA_StatusCode tp = UA_ByteString_copy(&signature, &channel->channelThumbprint);
UA_CHECK_STATUS(tp, return tp);
UA_ByteString_clear(&channel->firstRequestSignature);
}
size_t unencrypted_length =
UA_SECURECHANNEL_CHANNELHEADER_LENGTH + securityHeaderLength;
UA_ByteString dataToEncrypt =
{totalLength - unencrypted_length, &buf->data[unencrypted_length]};
return sp->asymEncryptionAlgorithm.encrypt(sp, cc, &dataToEncrypt);
}
UA_StatusCode
signAndEncryptSym(UA_MessageContext *messageContext,
size_t preSigLength, size_t totalLength) {
const UA_SecureChannel *channel = messageContext->channel;
if(channel->securityMode == UA_MESSAGESECURITYMODE_NONE)
return UA_STATUSCODE_GOOD;
const UA_SecurityPolicy *sp = channel->securityPolicy;
void *cc = channel->channelContext;
if(UA_SecurityPolicy_isAead(sp)) {
if(sp->setMessageSecurityParameters) {
UA_ByteString aad;
aad.data = messageContext->messageBuffer.data;
aad.length = UA_SECURECHANNEL_CHANNELHEADER_LENGTH +
UA_SECURECHANNEL_SYMMETRIC_SECURITYHEADER_LENGTH;
UA_UInt32 prevSeqNo = channel->sendSequenceNumber >= 2 ?
(UA_UInt32)(channel->sendSequenceNumber - 2) : 0;
UA_StatusCode res = sp->setMessageSecurityParameters(
sp, cc, channel->securityToken.tokenId, prevSeqNo, &aad);
UA_CHECK_STATUS(res, return res);
}
if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) {
UA_ByteString dataToProcess;
dataToProcess.data = messageContext->messageBuffer.data +
UA_SECURECHANNEL_CHANNELHEADER_LENGTH +
UA_SECURECHANNEL_SYMMETRIC_SECURITYHEADER_LENGTH;
dataToProcess.length = totalLength -
(UA_SECURECHANNEL_CHANNELHEADER_LENGTH +
UA_SECURECHANNEL_SYMMETRIC_SECURITYHEADER_LENGTH);
return sp->symEncryptionAlgorithm.encrypt(sp, cc, &dataToProcess);
}
UA_ByteString dataToSign = messageContext->messageBuffer;
dataToSign.length = preSigLength;
UA_ByteString signature;
signature.length =
sp->symSignatureAlgorithm.getLocalSignatureSize(sp, cc);
signature.data = messageContext->buf_pos;
return sp->symSignatureAlgorithm.
sign(sp, cc, &dataToSign, &signature);
}
UA_ByteString dataToSign = messageContext->messageBuffer;
dataToSign.length = preSigLength;
UA_ByteString signature;
signature.length =
sp->symSignatureAlgorithm.getLocalSignatureSize(sp, cc);
signature.data = messageContext->buf_pos;
UA_StatusCode res = sp->symSignatureAlgorithm.
sign(sp, cc, &dataToSign, &signature);
UA_CHECK_STATUS(res, return res);
if(channel->securityMode != UA_MESSAGESECURITYMODE_SIGNANDENCRYPT)
return UA_STATUSCODE_GOOD;
UA_ByteString dataToEncrypt;
dataToEncrypt.data = messageContext->messageBuffer.data +
UA_SECURECHANNEL_CHANNELHEADER_LENGTH +
UA_SECURECHANNEL_SYMMETRIC_SECURITYHEADER_LENGTH;
dataToEncrypt.length = totalLength -
(UA_SECURECHANNEL_CHANNELHEADER_LENGTH +
UA_SECURECHANNEL_SYMMETRIC_SECURITYHEADER_LENGTH);
return sp->symEncryptionAlgorithm.encrypt(sp, cc, &dataToEncrypt);
}
void
setBufPos(UA_MessageContext *mc) {
mc->buf_pos = &mc->messageBuffer.data[UA_SECURECHANNEL_SYMMETRIC_HEADER_TOTALLENGTH];
mc->buf_end = &mc->messageBuffer.data[mc->messageBuffer.length];
if(mc->channel->securityMode == UA_MESSAGESECURITYMODE_NONE)
return;
const UA_SecureChannel *channel = mc->channel;
const UA_SecurityPolicy *sp = channel->securityPolicy;
void *cc = channel->channelContext;
if(UA_SecurityPolicy_isAead(sp)) {
size_t sigsize =
sp->symSignatureAlgorithm.getLocalSignatureSize(sp, cc);
mc->buf_end -= sigsize;
UA_LOG_TRACE_CHANNEL(sp->logger, channel,
"Prepare an AEAD symmetric message buffer of length %lu "
"with a usable maximum payload length of %lu",
(long unsigned)mc->messageBuffer.length,
(long unsigned)((uintptr_t)mc->buf_end -
(uintptr_t)mc->messageBuffer.data));
return;
}
size_t sigsize =
sp->symSignatureAlgorithm.getLocalSignatureSize(sp, cc);
size_t plainBlockSize =
sp->symEncryptionAlgorithm.getRemotePlainTextBlockSize(sp, cc);
UA_assert(sp->symEncryptionAlgorithm.getRemoteBlockSize(sp, cc) == plainBlockSize);
mc->buf_end -= sigsize;
UA_assert(plainBlockSize > 0);
mc->buf_end -= mc->messageBuffer.length % plainBlockSize;
if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) {
UA_Boolean extraPadding =
(sp->symEncryptionAlgorithm.getRemoteKeyLength(sp, cc) > 2048);
mc->buf_end -= (UA_LIKELY(!extraPadding)) ? 1 : 2;
}
UA_LOG_TRACE_CHANNEL(sp->logger, channel,
"Prepare a symmetric message buffer of length %lu "
"with a usable maximum payload length of %lu",
(long unsigned)mc->messageBuffer.length,
(long unsigned)((uintptr_t)mc->buf_end -
(uintptr_t)mc->messageBuffer.data));
}
static size_t
decodePadding(const UA_SecureChannel *channel,
const UA_SecurityPolicyEncryptionAlgorithm *encryptionAlgorithm,
const UA_ByteString *chunk, size_t sigsize) {
size_t paddingSize = chunk->data[chunk->length - sigsize - 1];
if(encryptionAlgorithm->getLocalKeyLength(channel->securityPolicy,
channel->channelContext) > 2048) {
paddingSize <<= 8u;
paddingSize += chunk->data[chunk->length - sigsize - 2];
paddingSize += 1;
}
return paddingSize + 1;
}
UA_StatusCode
decryptAndVerifyChunk(UA_SecureChannel *channel,
const UA_SecurityPolicySignatureAlgorithm *signatureAlgorithm,
const UA_SecurityPolicyEncryptionAlgorithm *encryptionAlgorithm,
UA_MessageType messageType, UA_ByteString *chunk,
size_t offset) {
UA_SecurityPolicy *sp = channel->securityPolicy;
void *cc = channel->channelContext;
UA_StatusCode res = UA_STATUSCODE_GOOD;
if(UA_SecurityPolicy_isAead(sp) &&
messageType != UA_MESSAGETYPE_OPN) {
if(sp->setMessageSecurityParameters) {
UA_ByteString aad = {offset, chunk->data};
size_t tokenOffset = UA_SECURECHANNEL_CHANNELHEADER_LENGTH;
UA_UInt32 msgTokenId = channel->securityToken.tokenId;
if(offset >= UA_SECURECHANNEL_MESSAGE_MIN_LENGTH)
UA_UInt32_decodeBinary(chunk, &tokenOffset, &msgTokenId);
res = sp->setMessageSecurityParameters(
sp, cc, msgTokenId,
channel->receiveSequenceNumber, &aad);
UA_CHECK_STATUS(res, return res);
}
if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) {
UA_ByteString cipher = {chunk->length - offset, chunk->data + offset};
res = encryptionAlgorithm->decrypt(sp, cc, &cipher);
UA_CHECK_STATUS(res,
UA_LOG_WARNING_CHANNEL(sp->logger, channel,
"AEAD decryption/verification failed");
return res);
chunk->length = cipher.length + offset;
} else if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGN) {
size_t sigsize = signatureAlgorithm->getRemoteSignatureSize(sp, cc);
UA_CHECK(sigsize < chunk->length,
return UA_STATUSCODE_BADSECURITYCHECKSFAILED);
const UA_ByteString content = {chunk->length - sigsize, chunk->data};
const UA_ByteString sig = {sigsize, chunk->data + chunk->length - sigsize};
res = signatureAlgorithm->verify(sp, cc, &content, &sig);
UA_CHECK_STATUS(res,
UA_LOG_WARNING_CHANNEL(sp->logger, channel,
"AEAD signature verification failed");
return res);
chunk->length -= sigsize;
}
UA_CHECK(offset + 9 < chunk->length,
UA_LOG_ERROR_CHANNEL(sp->logger, channel,
"AEAD message too short");
return UA_STATUSCODE_BADSECURITYCHECKSFAILED);
return UA_STATUSCODE_GOOD;
}
if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT ||
messageType == UA_MESSAGETYPE_OPN) {
UA_ByteString cipher = {chunk->length - offset, chunk->data + offset};
res = encryptionAlgorithm->decrypt(sp, cc, &cipher);
UA_CHECK_STATUS(res, return res);
chunk->length = cipher.length + offset;
}
if(channel->securityMode != UA_MESSAGESECURITYMODE_SIGN &&
channel->securityMode != UA_MESSAGESECURITYMODE_SIGNANDENCRYPT &&
messageType != UA_MESSAGETYPE_OPN)
return UA_STATUSCODE_GOOD;
UA_LOG_TRACE_CHANNEL(sp->logger, channel, "Verifying chunk signature");
size_t sigsize = signatureAlgorithm->getRemoteSignatureSize(sp, cc);
UA_CHECK(sigsize < chunk->length, return UA_STATUSCODE_BADSECURITYCHECKSFAILED);
const UA_ByteString content = {chunk->length - sigsize, chunk->data};
const UA_ByteString sig = {sigsize, chunk->data + chunk->length - sigsize};
UA_ByteString verifyContent = content;
UA_Byte *extendedBuf = NULL;
UA_Boolean capturedIncoming = false;
UA_Boolean firstOPN = (channel->enhancedSecurity &&
messageType == UA_MESSAGETYPE_OPN &&
channel->channelThumbprint.length == 0);
if(firstOPN) {
if(channel->firstRequestSignature.length == 0) {
UA_StatusCode clip = UA_ByteString_copy(&sig, &channel->firstRequestSignature);
UA_CHECK_STATUS(clip, return clip);
capturedIncoming = true;
} else {
extendedBuf = (UA_Byte*)UA_malloc(
content.length + channel->firstRequestSignature.length);
if(!extendedBuf) {
UA_ByteString_clear(&channel->firstRequestSignature);
return UA_STATUSCODE_BADOUTOFMEMORY;
}
memcpy(extendedBuf, content.data, content.length);
memcpy(extendedBuf + content.length,
channel->firstRequestSignature.data,
channel->firstRequestSignature.length);
verifyContent.data = extendedBuf;
verifyContent.length =
content.length + channel->firstRequestSignature.length;
}
}
res = signatureAlgorithm->verify(sp, cc, &verifyContent, &sig);
UA_free(extendedBuf);
if(firstOPN && !capturedIncoming) {
UA_ByteString_clear(&channel->firstRequestSignature);
}
UA_CHECK_STATUS(res, UA_LOG_WARNING_CHANNEL(sp->logger, channel,
"Could not verify the signature");
return res);
if(channel->enhancedSecurity && messageType == UA_MESSAGETYPE_OPN &&
!capturedIncoming && channel->channelThumbprint.length == 0) {
UA_StatusCode tp = UA_ByteString_copy(&sig, &channel->channelThumbprint);
UA_CHECK_STATUS(tp, return tp);
}
size_t padSize = 0;
if((messageType != UA_MESSAGETYPE_OPN &&
channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) ||
(messageType == UA_MESSAGETYPE_OPN &&
sp->policyType == UA_SECURITYPOLICYTYPE_RSA)) {
padSize = decodePadding(channel, encryptionAlgorithm, chunk, sigsize);
UA_LOG_TRACE_CHANNEL(sp->logger, channel, "Calculated padding size to be %lu",
(long unsigned)padSize);
}
UA_CHECK(offset + padSize + sigsize + 9 < chunk->length,
UA_LOG_ERROR_CHANNEL(sp->logger, channel, "Impossible padding value");
return UA_STATUSCODE_BADSECURITYCHECKSFAILED);
chunk->length -= (sigsize + padSize);
return UA_STATUSCODE_GOOD;
}
UA_StatusCode
checkAsymHeader(UA_SecureChannel *channel,
const UA_AsymmetricAlgorithmSecurityHeader *asymHeader) {
const UA_SecurityPolicy *sp = channel->securityPolicy;
if(!UA_String_equal(&sp->policyUri, &asymHeader->securityPolicyUri))
return UA_STATUSCODE_BADSECURITYPOLICYREJECTED;
return sp->compareCertThumbprint(sp, &asymHeader->receiverCertificateThumbprint);
}
UA_StatusCode
checkSymHeader(UA_SecureChannel *channel, const UA_UInt32 tokenId,
UA_DateTime nowMonotonic) {
UA_SecurityPolicy *sp = channel->securityPolicy;
(void)sp;
UA_StatusCode retval = UA_STATUSCODE_GOOD;
UA_ChannelSecurityToken *token = &channel->securityToken;
switch(channel->renewState) {
case UA_SECURECHANNELRENEWSTATE_NORMAL:
case UA_SECURECHANNELRENEWSTATE_SENT:
default:
break;
case UA_SECURECHANNELRENEWSTATE_NEWTOKEN_SERVER:
if(tokenId == channel->securityToken.tokenId)
break;
UA_CHECK(tokenId == channel->altSecurityToken.tokenId,
UA_LOG_ERROR_CHANNEL(sp->logger, channel, "Unknown SecurityToken");
return UA_STATUSCODE_BADSECURECHANNELTOKENUNKNOWN);
channel->renewState = UA_SECURECHANNELRENEWSTATE_NORMAL;
channel->securityToken = channel->altSecurityToken;
UA_ChannelSecurityToken_init(&channel->altSecurityToken);
retval |= UA_SecureChannel_generateLocalKeys(channel);
retval |= generateRemoteKeys(channel);
UA_CHECK_STATUS(retval, return retval);
break;
case UA_SECURECHANNELRENEWSTATE_NEWTOKEN_CLIENT:
if(tokenId == channel->altSecurityToken.tokenId) {
token = &channel->altSecurityToken;
break;
}
UA_CHECK(tokenId == channel->securityToken.tokenId,
UA_LOG_ERROR_CHANNEL(sp->logger, channel, "Unknown SecurityToken");
return UA_STATUSCODE_BADSECURECHANNELTOKENUNKNOWN);
channel->renewState = UA_SECURECHANNELRENEWSTATE_NORMAL;
UA_ChannelSecurityToken_init(&channel->altSecurityToken);
retval = generateRemoteKeys(channel);
UA_CHECK_STATUS(retval, return retval);
}
UA_DateTime timeout = token->createdAt + (token->revisedLifetime * UA_DATETIME_MSEC);
if(channel->state == UA_SECURECHANNELSTATE_OPEN && timeout < nowMonotonic) {
UA_LOG_ERROR_CHANNEL(sp->logger, channel, "SecurityToken timed out");
UA_SecureChannel_shutdown(channel, UA_SHUTDOWNREASON_TIMEOUT);
return UA_STATUSCODE_BADSECURECHANNELCLOSED;
}
return UA_STATUSCODE_GOOD;
}
UA_Boolean
UA_SecureChannel_checkTimeout(UA_SecureChannel *channel, UA_DateTime nowMonotonic) {
UA_DateTime timeout = channel->securityToken.createdAt +
(UA_DateTime)(channel->securityToken.revisedLifetime * UA_DATETIME_MSEC);
if(timeout < nowMonotonic &&
channel->renewState == UA_SECURECHANNELRENEWSTATE_NEWTOKEN_SERVER) {
channel->renewState = UA_SECURECHANNELRENEWSTATE_NORMAL;
channel->securityToken = channel->altSecurityToken;
UA_ChannelSecurityToken_init(&channel->altSecurityToken);
UA_SecureChannel_generateLocalKeys(channel);
generateRemoteKeys(channel);
timeout = channel->securityToken.createdAt +
(UA_DateTime)(channel->securityToken.revisedLifetime * UA_DATETIME_MSEC);
}
return (timeout < nowMonotonic);
}