#include "ua_util_internal.h"
typedef struct {
UA_NodeId typeId;
UA_Byte encodingMask;
UA_UInt32 length;
UA_String securityPolicyUri;
UA_ByteString certificate;
UA_DateTime signingTime;
UA_UInt16 keyDataLen;
UA_ByteString senderPublicKey;
UA_ByteString receiverPublicKey;
UA_ByteString nonce;
UA_ByteString secret;
UA_Byte* signature;
} UA_EccEncryptedSecretStruct;
static void
UA_EccEncryptedSecretStruct_init(UA_EccEncryptedSecretStruct* es) {
memset(es, 0, sizeof(UA_EccEncryptedSecretStruct));
}
static void
UA_EccEncryptedSecretStruct_clear(UA_EccEncryptedSecretStruct* es) {
UA_String_clear(&es->securityPolicyUri);
UA_ByteString_clear(&es->certificate);
UA_ByteString_clear(&es->senderPublicKey);
UA_ByteString_clear(&es->receiverPublicKey);
UA_ByteString_clear(&es->nonce);
UA_ByteString_clear(&es->secret);
UA_EccEncryptedSecretStruct_init(es);
}
static size_t
UA_EccEncryptedSecret_getCommonHeaderSize(const UA_EccEncryptedSecretStruct* src) {
size_t len = 0;
len += UA_calcSizeBinary(&src->typeId, &UA_TYPES[UA_TYPES_NODEID], NULL);
len += UA_calcSizeBinary(&src->encodingMask, &UA_TYPES[UA_TYPES_BYTE], NULL);
len += UA_calcSizeBinary(&src->length, &UA_TYPES[UA_TYPES_UINT32], NULL);
len += UA_calcSizeBinary(&src->securityPolicyUri, &UA_TYPES[UA_TYPES_STRING], NULL);
len += UA_calcSizeBinary(&src->certificate, &UA_TYPES[UA_TYPES_BYTESTRING], NULL);
len += UA_calcSizeBinary(&src->signingTime, &UA_TYPES[UA_TYPES_DATETIME], NULL);
len += UA_calcSizeBinary(&src->keyDataLen, &UA_TYPES[UA_TYPES_UINT16], NULL);
return len;
}
static size_t
UA_EccEncryptedSecret_getPolicyHeaderSize(const UA_EccEncryptedSecretStruct* src) {
size_t len = 0;
len += UA_calcSizeBinary(&src->senderPublicKey, &UA_TYPES[UA_TYPES_BYTESTRING], NULL);
len += UA_calcSizeBinary(&src->receiverPublicKey, &UA_TYPES[UA_TYPES_BYTESTRING], NULL);
return len;
}
static UA_StatusCode
UA_EccEncryptedSecret_serializeCommonHeader(const UA_EccEncryptedSecretStruct *src,
UA_Byte** bufPos, const UA_Byte* bufEnd) {
UA_UInt32 length32 = (UA_UInt32)src->length;
UA_StatusCode ret = UA_STATUSCODE_GOOD;
ret |= UA_NodeId_encodeBinary(&src->typeId, bufPos, bufEnd);
ret |= UA_Byte_encodeBinary(&src->encodingMask, bufPos, bufEnd);
ret |= UA_UInt32_encodeBinary(&length32, bufPos, bufEnd);
ret |= UA_String_encodeBinary(&src->securityPolicyUri, bufPos, bufEnd);
ret |= UA_ByteString_encodeBinary(&src->certificate, bufPos, bufEnd);
ret |= UA_DateTime_encodeBinary(&src->signingTime, bufPos, bufEnd);
ret |= UA_UInt16_encodeBinary(&src->keyDataLen, bufPos, bufEnd);
return ret;
}
static UA_StatusCode
UA_EccEncryptedSecret_serializePolicyHeader(const UA_EccEncryptedSecretStruct *src,
UA_Byte** bufPos, const UA_Byte* bufEnd) {
UA_StatusCode ret = UA_STATUSCODE_GOOD;
ret |= UA_ByteString_encodeBinary(&src->senderPublicKey, bufPos, bufEnd);
ret |= UA_ByteString_encodeBinary(&src->receiverPublicKey, bufPos, bufEnd);
return ret;
}
static UA_StatusCode
UA_EccEncryptedSecret_deserializeCommonHeader(UA_EccEncryptedSecret *src,
UA_EccEncryptedSecretStruct *dest,
size_t* offset) {
UA_StatusCode ret = UA_STATUSCODE_GOOD;
ret |= UA_NodeId_decodeBinary(src, offset, &dest->typeId);
ret |= UA_Byte_decodeBinary(src, offset, &dest->encodingMask);
ret |= UA_UInt32_decodeBinary(src, offset, &dest->length);
ret |= UA_String_decodeBinary(src, offset, &dest->securityPolicyUri);
ret |= UA_ByteString_decodeBinary(src, offset, &dest->certificate);
ret |= UA_DateTime_decodeBinary(src, offset, &dest->signingTime);
ret |= UA_UInt16_decodeBinary(src, offset, &dest->keyDataLen);
return ret;
}
static UA_StatusCode
UA_EccEncryptedSecret_deserializePolicyHeader(UA_EccEncryptedSecret *src,
UA_EccEncryptedSecretStruct *dest,
size_t* offset) {
UA_StatusCode ret = UA_STATUSCODE_GOOD;
ret |= UA_ByteString_decodeBinary(src, offset, &dest->senderPublicKey);
ret |= UA_ByteString_decodeBinary(src, offset, &dest->receiverPublicKey);
return ret;
}
UA_StatusCode
encryptUserIdentityTokenEcc(UA_Logger *logger, UA_SecureChannel *channel,
const UA_SecurityPolicy *sp, void *spContext,
UA_ByteString *tokenData,
const UA_ByteString serverSessionNonce,
const UA_ByteString serverEphemeralPubKey) {
UA_Boolean aead = UA_SecurityPolicy_isAead(sp);
size_t symKeyLen = sp->symEncryptionAlgorithm.getLocalKeyLength(sp, spContext);
size_t ivLen = (aead && sp->symEncryptionAlgorithm.getLocalIvLength) ?
sp->symEncryptionAlgorithm.getLocalIvLength(sp, spContext) :
sp->symEncryptionAlgorithm.getRemoteBlockSize(sp, spContext);
size_t tagLen = aead ?
sp->symSignatureAlgorithm.getLocalSignatureSize(sp, spContext) : 0;
size_t sigLen = sp->asymSignatureAlgorithm.getRemoteSignatureSize(sp, spContext);
UA_assert(symKeyLen > 0 && ivLen > 0);
UA_EccEncryptedSecretStruct secret;
UA_EccEncryptedSecretStruct_init(&secret);
secret.typeId = UA_NS0ID(ECCENCRYPTEDSECRET);
secret.encodingMask = 0x01;
secret.signingTime = UA_DateTime_now();
UA_StatusCode retval = UA_STATUSCODE_GOOD;
retval |= UA_String_copy(&sp->policyUri, &secret.securityPolicyUri);
retval |= UA_ByteString_copy(&sp->localCertificate, &secret.certificate);
retval |= UA_ByteString_copy(&serverEphemeralPubKey, &secret.receiverPublicKey);
retval |= UA_ByteString_copy(&serverSessionNonce, &secret.nonce);
retval |= UA_ByteString_copy(tokenData, &secret.secret);
if(retval != UA_STATUSCODE_GOOD) {
UA_EccEncryptedSecretStruct_clear(&secret);
return retval;
}
size_t ephKeyLen = sp->nonceLength;
retval = UA_ByteString_allocBuffer(&secret.senderPublicKey, ephKeyLen);
if(retval != UA_STATUSCODE_GOOD) {
UA_EccEncryptedSecretStruct_clear(&secret);
return retval;
}
secret.senderPublicKey.data[0] = 'e';
secret.senderPublicKey.data[1] = 'p';
secret.senderPublicKey.data[2] = 'h';
retval = sp->generateNonce(sp, spContext, &secret.senderPublicKey);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_CHANNEL(logger, channel,
"EccEncryptedSecret: Failed to generate local ephemeral key");
UA_EccEncryptedSecretStruct_clear(&secret);
return retval;
}
size_t blockSize = aead ? 16 : ivLen;
size_t baseLen = UA_ByteString_calcSizeBinary(&secret.nonce) +
UA_ByteString_calcSizeBinary(&secret.secret);
size_t modLen = (baseLen + 2) % blockSize;
size_t paddingCount = (modLen == 0) ? 0 : (blockSize - modLen);
if(paddingCount + secret.secret.length < blockSize)
paddingCount += blockSize;
size_t encryptedLength = baseLen + paddingCount + 2;
size_t signatureLen = sp->asymSignatureAlgorithm.
getLocalSignatureSize(sp, spContext);
secret.keyDataLen = (UA_UInt16)
UA_EccEncryptedSecret_getPolicyHeaderSize(&secret);
size_t totalLength = encryptedLength + tagLen + secret.keyDataLen +
UA_EccEncryptedSecret_getCommonHeaderSize(&secret) + signatureLen;
size_t headerPrefix =
UA_calcSizeBinary(&secret.typeId, &UA_TYPES[UA_TYPES_NODEID], NULL) +
UA_calcSizeBinary(&secret.encodingMask, &UA_TYPES[UA_TYPES_BYTE], NULL) +
UA_calcSizeBinary(&secret.length, &UA_TYPES[UA_TYPES_UINT32], NULL);
secret.length = (UA_UInt32)(totalLength - headerPrefix);
UA_ByteString symEncKeyMaterial;
retval = UA_ByteString_allocBuffer(&symEncKeyMaterial, symKeyLen+ivLen);
if(retval != UA_STATUSCODE_GOOD) {
UA_EccEncryptedSecretStruct_clear(&secret);
return retval;
}
symEncKeyMaterial.data[0] = 0x03;
symEncKeyMaterial.data[1] = 0x03;
symEncKeyMaterial.data[2] = 0x04;
retval = sp->generateKey(sp, spContext, &secret.receiverPublicKey,
&secret.senderPublicKey, &symEncKeyMaterial);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_CHANNEL(logger, channel,
"EccEncryptedSecret: Failed to derive key material");
UA_ByteString_clear(&symEncKeyMaterial);
UA_EccEncryptedSecretStruct_clear(&secret);
return retval;
}
UA_ByteString encKey = {symKeyLen, symEncKeyMaterial.data};
UA_ByteString iv = {ivLen, &symEncKeyMaterial.data[symKeyLen]};
retval |= sp->setLocalSymEncryptingKey(sp, spContext, &encKey);
retval |= sp->setLocalSymIv(sp, spContext, &iv);
UA_ByteString_clear(&symEncKeyMaterial);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_CHANNEL(logger, channel,
"EccEncryptedSecret: Failed to set EncryptingKey/"
"IV in the SecurityPolicy");
UA_EccEncryptedSecretStruct_clear(&secret);
return retval;
}
UA_ByteString output;
retval = UA_ByteString_allocBuffer(&output, totalLength);
if(retval != UA_STATUSCODE_GOOD) {
UA_EccEncryptedSecretStruct_clear(&secret);
return retval;
}
UA_Byte* bufPos = output.data;
UA_Byte* bufEnd = output.data + output.length;
retval |= UA_EccEncryptedSecret_serializeCommonHeader(&secret, &bufPos, bufEnd);
retval |= UA_EccEncryptedSecret_serializePolicyHeader(&secret, &bufPos, bufEnd);
UA_Byte* payloadPos = bufPos;
retval |= UA_ByteString_encodeBinary(&secret.nonce, &bufPos, bufEnd);
retval |= UA_ByteString_encodeBinary(&secret.secret, &bufPos, bufEnd);
UA_Byte pad = (UA_Byte)(paddingCount & 0xFF);
for(size_t i = 0; i < paddingCount; i++) {
*bufPos = pad;
bufPos++;
}
UA_UInt16 paddingCount16 = (UA_UInt16)paddingCount;
retval |= UA_UInt16_encodeBinary(&paddingCount16, &bufPos, bufEnd);
if(retval != UA_STATUSCODE_GOOD) {
UA_EccEncryptedSecretStruct_clear(&secret);
UA_ByteString_clear(&output);
return retval;
}
UA_ByteString payload = {(size_t)(bufPos - payloadPos) + tagLen, payloadPos};
if(aead) {
UA_ByteString aad = {(size_t)(payloadPos - output.data), output.data};
retval |= sp->setMessageSecurityParameters(sp, spContext, 0, 0, &aad);
}
retval |= sp->symEncryptionAlgorithm.encrypt(sp, spContext, &payload);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_CHANNEL(logger, channel,
"EccEncryptedSecret: Failed to encrypt the payload");
UA_EccEncryptedSecretStruct_clear(&secret);
UA_ByteString_clear(&output);
return retval;
}
bufPos += tagLen;
UA_assert(bufPos + sigLen == bufEnd);
UA_ByteString sigContent = {(size_t)(bufPos - output.data), output.data};
UA_ByteString signature = {sigLen, bufPos};
retval = sp->asymSignatureAlgorithm.sign(sp, spContext, &sigContent, &signature);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_CHANNEL(logger, channel,
"EccEncryptedSecret: Failed to sign the EccEncryptedSecret");
UA_EccEncryptedSecretStruct_clear(&secret);
UA_ByteString_clear(&output);
return retval;
}
UA_ByteString_clear(tokenData);
*tokenData = output;
UA_EccEncryptedSecretStruct_clear(&secret);
return UA_STATUSCODE_GOOD;
}
UA_StatusCode
decryptUserTokenEcc(UA_Logger *logger, UA_SecureChannel *channel,
const UA_SecurityPolicy *sp, void *spContext,
UA_ByteString sessionServerNonce,
UA_EccEncryptedSecret *es) {
UA_assert(UA_SecurityPolicy_isEcc(sp));
UA_StatusCode res = UA_STATUSCODE_GOOD;
UA_EccEncryptedSecretStruct esd;
UA_EccEncryptedSecretStruct_init(&esd);
UA_ByteString symEncKeyMaterial = UA_BYTESTRING_NULL;
UA_ByteString pass = UA_BYTESTRING_NULL;
size_t offset = 0;
res = UA_EccEncryptedSecret_deserializeCommonHeader(es, &esd, &offset);
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_CHANNEL(logger, channel, "EccEncryptedSecret: "
"Failed to decode the common header");
goto cleanecc;
}
UA_NodeId eccTypeId = UA_NS0ID(ECCENCRYPTEDSECRET);
if(!UA_NodeId_equal(&eccTypeId, &esd.typeId) ||
esd.encodingMask != 0x01 ||
!UA_String_equal(&esd.securityPolicyUri, &sp->policyUri)) {
UA_LOG_ERROR_CHANNEL(logger, channel, "EccEncryptedSecret: "
"Inconsistent common header");
res = UA_STATUSCODE_BADSECURITYCHECKSFAILED;
goto cleanecc;
}
size_t headerPrefix =
UA_calcSizeBinary(&esd.typeId, &UA_TYPES[UA_TYPES_NODEID], NULL) +
UA_calcSizeBinary(&esd.encodingMask, &UA_TYPES[UA_TYPES_BYTE], NULL) +
UA_calcSizeBinary(&esd.length, &UA_TYPES[UA_TYPES_UINT32], NULL);
size_t endOfSecret = (size_t)esd.length + headerPrefix;
if(endOfSecret > es->length || endOfSecret <= offset) {
UA_LOG_ERROR_CHANNEL(logger, channel, "EccEncryptedSecret: "
"Inconsistent Length field");
res = UA_STATUSCODE_BADDECODINGERROR;
goto cleanecc;
}
size_t sigLen = sp->asymSignatureAlgorithm.getRemoteSignatureSize(sp, spContext);
size_t signedDataLen = endOfSecret - sigLen;
UA_ByteString signedData = {signedDataLen, es->data};
UA_ByteString signature = {sigLen, &es->data[signedDataLen]};
res = sp->asymSignatureAlgorithm.verify(sp, spContext, &signedData, &signature);
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_CHANNEL(logger, channel, "EccEncryptedSecret: "
"Signature verification failed");
goto cleanecc;
}
size_t oldoffset = offset;
res = UA_EccEncryptedSecret_deserializePolicyHeader(es, &esd, &offset);
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_CHANNEL(logger, channel, "EccEncryptedSecret: "
"Failed to decode the policy header");
goto cleanecc;
}
if(esd.keyDataLen != (UA_UInt16)(offset - oldoffset)) {
UA_LOG_ERROR_CHANNEL(logger, channel, "EccEncryptedSecret: "
"Inconstent KeyDataLength");
res = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
goto cleanecc;
}
if(endOfSecret <= offset + sigLen) {
UA_LOG_ERROR_CHANNEL(logger, channel, "EccEncryptedSecret: "
"Inconstent payload / signature length");
res = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
goto cleanecc;
}
UA_ByteString payload = {endOfSecret - offset - sigLen, es->data + offset};
UA_Boolean aead = UA_SecurityPolicy_isAead(sp);
size_t symKeyLen = sp->symEncryptionAlgorithm.getRemoteKeyLength(sp, spContext);
size_t ivLen = (aead && sp->symEncryptionAlgorithm.getLocalIvLength) ?
sp->symEncryptionAlgorithm.getLocalIvLength(sp, spContext) :
sp->symEncryptionAlgorithm.getRemoteBlockSize(sp, spContext);
res = UA_ByteString_allocBuffer(&symEncKeyMaterial, symKeyLen+ivLen);
if(res != UA_STATUSCODE_GOOD)
goto cleanecc;
symEncKeyMaterial.data[0] = 0x03;
symEncKeyMaterial.data[1] = 0x03;
symEncKeyMaterial.data[2] = 0x04;
res = sp->generateKey(sp, spContext, &esd.receiverPublicKey,
&esd.senderPublicKey, &symEncKeyMaterial);
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_CHANNEL(logger, channel, "EccEncryptedSecret: "
"Failed to derive key material");
goto cleanecc;
}
UA_ByteString encKey = {symKeyLen, symEncKeyMaterial.data};
UA_ByteString iv = {ivLen, &symEncKeyMaterial.data[symKeyLen]};
res |= sp->setRemoteSymEncryptingKey(sp, spContext, &encKey);
res |= sp->setRemoteSymIv(sp, spContext, &iv);
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_CHANNEL(logger, channel, "EccEncryptedSecret: "
"Failed to set IV/RemoteSymEncryptingKey");
goto cleanecc;
}
if(aead) {
UA_ByteString aad = {(size_t)(payload.data - es->data), es->data};
res = sp->setMessageSecurityParameters(sp, spContext, 0, 0, &aad);
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_CHANNEL(logger, channel, "EccEncryptedSecret: "
"Failed to set AEAD parameters");
goto cleanecc;
}
}
res = sp->symEncryptionAlgorithm.decrypt(sp, spContext, &payload);
if(res != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR_CHANNEL(logger, channel, "EccEncryptedSecret: "
"Failed to decrypt the payload");
goto cleanecc;
}
offset = 0;
UA_ByteString nonce;
UA_StatusCode ret = UA_STATUSCODE_GOOD;
ret |= UA_ByteString_decodeBinary(&payload, &offset, &nonce);
ret |= UA_ByteString_decodeBinary(&payload, &offset, &pass);
if(ret != UA_STATUSCODE_GOOD) {
UA_ByteString_clear(&nonce);
UA_LOG_ERROR_CHANNEL(logger, channel, "EccEncryptedSecret: "
"Failed to decode the payload");
goto cleanecc;
}
UA_Boolean nonceMatch = UA_ByteString_equal(&sessionServerNonce, &nonce);
UA_ByteString_clear(&nonce);
if(!nonceMatch) {
UA_LOG_ERROR_CHANNEL(logger, channel, "EccEncryptedSecret: "
"Nonce does not match");
res = UA_STATUSCODE_BADSECURITYCHECKSFAILED;
goto cleanecc;
}
UA_UInt16 paddingSize = 0;
size_t paddingOffset = payload.length - 2;
UA_UInt16_decodeBinary(&payload, &paddingOffset, &paddingSize);
UA_Byte padd = (UA_Byte)paddingSize;
for(; offset < payload.length-2; offset++) {
if(payload.data[offset] != padd) {
UA_LOG_ERROR_CHANNEL(logger, channel,
"EccEncryptedSecret: Inconsistent padding");
res = UA_STATUSCODE_BADSECURITYCHECKSFAILED;
goto cleanecc;
}
}
memcpy(es->data, pass.data, pass.length);
es->length = pass.length;
cleanecc:
UA_EccEncryptedSecretStruct_clear(&esd);
UA_ByteString_clear(&symEncKeyMaterial);
UA_ByteString_clear(&pass);
return res;
}
UA_StatusCode
encryptSecretLegacy(const UA_SecurityPolicy *sp, void *spContext,
const UA_ByteString serverSessionNonce,
UA_ByteString *tokenData) {
size_t plainTextBlockSize = sp->asymEncryptionAlgorithm.
getRemotePlainTextBlockSize(sp, spContext);
size_t encryptedBlockSize = sp->asymEncryptionAlgorithm.
getRemoteBlockSize(sp, spContext);
UA_UInt32 length =
(UA_UInt32)(tokenData->length + serverSessionNonce.length);
UA_UInt32 totalLength = length + 4;
size_t blocks = totalLength / plainTextBlockSize;
if(totalLength % plainTextBlockSize != 0)
blocks++;
size_t encryptedLength = blocks * encryptedBlockSize;
UA_ByteString encrypted;
UA_StatusCode res = UA_ByteString_allocBuffer(&encrypted, encryptedLength);
if(res != UA_STATUSCODE_GOOD)
return res;
UA_Byte *pos = encrypted.data;
const UA_Byte *end = &encrypted.data[encrypted.length];
res = UA_UInt32_encodeBinary(&length, &pos, end);
memcpy(pos, tokenData->data, tokenData->length);
memcpy(&pos[tokenData->length], serverSessionNonce.data,
serverSessionNonce.length);
UA_assert(res == UA_STATUSCODE_GOOD);
size_t paddedLength = plainTextBlockSize * blocks;
for(size_t i = totalLength; i < paddedLength; i++)
encrypted.data[i] = 0;
encrypted.length = paddedLength;
res = sp->asymEncryptionAlgorithm.encrypt(sp, spContext, &encrypted);
if(res != UA_STATUSCODE_GOOD) {
UA_ByteString_clear(&encrypted);
return res;
}
encrypted.length = encryptedLength;
UA_ByteString_clear(tokenData);
*tokenData = encrypted;
return UA_STATUSCODE_GOOD;
}
UA_StatusCode
decryptSecretLegacy(const UA_SecurityPolicy *sp, void *spContext,
const UA_ByteString serverSessionNonce,
UA_ByteString *tokenData) {
UA_UInt32 secretLen = 0;
UA_ByteString secret, tokenNonce;
size_t tokenpos = 0;
size_t offset = 0;
const UA_SecurityPolicyEncryptionAlgorithm *asymEnc = &sp->asymEncryptionAlgorithm;
UA_StatusCode res = UA_STATUSCODE_BADIDENTITYTOKENINVALID;
if(UA_ByteString_copy(tokenData, &secret) != UA_STATUSCODE_GOOD ||
asymEnc->decrypt(sp, spContext, &secret) != UA_STATUSCODE_GOOD)
goto cleanup;
if(UA_UInt32_decodeBinary(&secret, &offset, &secretLen) != UA_STATUSCODE_GOOD)
goto cleanup;
if(secret.length < sizeof(UA_UInt32) + serverSessionNonce.length ||
secret.length < sizeof(UA_UInt32) + secretLen ||
secretLen < serverSessionNonce.length)
goto cleanup;
for(size_t i = sizeof(UA_UInt32) + secretLen; i < secret.length; i++) {
if(secret.data[i] != 0)
goto cleanup;
}
tokenpos = sizeof(UA_UInt32) + secretLen - serverSessionNonce.length;
tokenNonce.length = serverSessionNonce.length;
tokenNonce.data = &secret.data[tokenpos];
if(!UA_ByteString_equal(&serverSessionNonce, &tokenNonce))
goto cleanup;
size_t outLen = secretLen - serverSessionNonce.length;
memcpy(tokenData->data, &secret.data[sizeof(UA_UInt32)], outLen);
tokenData->length = outLen;
res = UA_STATUSCODE_GOOD;
cleanup:
UA_ByteString_clear(&secret);
return res;
}