Procházet zdrojové kódy

Client: Add support for encrypted UserTokenPolicies

Julius Pfrommer před 6 roky
rodič
revize
30a14e8bd7
1 změnil soubory, kde provedl 110 přidání a 0 odebrání
  1. 110 0
      src/client/ua_client_connect.c

+ 110 - 0
src/client/ua_client_connect.c

@@ -347,6 +347,106 @@ signActivateSessionRequest(UA_SecureChannel *channel,
     return retval;
 }
 
+UA_StatusCode
+encryptUserIdentityToken(UA_Client *client, const UA_String *userTokenSecurityPolicy,
+                         UA_ExtensionObject *userIdentityToken) {
+    UA_IssuedIdentityToken *iit = NULL;
+    UA_UserNameIdentityToken *unit = NULL;
+    UA_ByteString *tokenData;
+    if(userIdentityToken->content.decoded.type == &UA_TYPES[UA_TYPES_ISSUEDIDENTITYTOKEN]) {
+        iit = (UA_IssuedIdentityToken*)userIdentityToken->content.decoded.data;
+        tokenData = &iit->tokenData;
+    } else if(userIdentityToken->content.decoded.type == &UA_TYPES[UA_TYPES_USERNAMEIDENTITYTOKEN]) {
+        unit = (UA_UserNameIdentityToken*)userIdentityToken->content.decoded.data;
+        tokenData = &unit->password;
+    } else {
+        return UA_STATUSCODE_GOOD;
+    }
+
+    /* No encryption */
+    const UA_String none = UA_STRING("http://opcfoundation.org/UA/SecurityPolicy#None");
+    if(userTokenSecurityPolicy->length == 0 ||
+       UA_String_equal(userTokenSecurityPolicy, &none)) {
+        return UA_STATUSCODE_GOOD;
+    }
+
+    UA_SecurityPolicy *sp = getSecurityPolicy(client, *userTokenSecurityPolicy);
+    if(!sp) {
+        UA_LOG_WARNING(&client->config.logger, UA_LOGCATEGORY_NETWORK,
+                       "Could not find the required SecurityPolicy for the UserToken");
+        return UA_STATUSCODE_BADSECURITYPOLICYREJECTED;
+    }
+
+    /* Create a temp channel context */
+
+    void *channelContext;
+    UA_StatusCode retval = sp->channelModule.
+        newContext(sp, &client->config.endpoint.serverCertificate, &channelContext);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_LOG_WARNING(&client->config.logger, UA_LOGCATEGORY_NETWORK,
+                       "Could not instantiate the SecurityPolicy for the UserToken");
+        return UA_STATUSCODE_BADINTERNALERROR;
+    }
+    
+    /* Compute the encrypted length (at least one byte padding) */
+    size_t plainTextBlockSize = sp->asymmetricModule.cryptoModule.
+        encryptionAlgorithm.getRemotePlainTextBlockSize(sp, channelContext);
+    UA_UInt32 length = (UA_UInt32)(tokenData->length + client->channel.remoteNonce.length);
+    UA_UInt32 totalLength = length + 4; /* Including the length field */
+    size_t blocks = totalLength / plainTextBlockSize;
+    if(totalLength  % plainTextBlockSize != 0)
+        blocks++;
+    size_t overHead =
+        UA_SecurityPolicy_getRemoteAsymEncryptionBufferLengthOverhead(sp, channelContext,
+                                                                      blocks * plainTextBlockSize);
+
+    /* Allocate memory for encryption overhead */
+    UA_ByteString encrypted;
+    retval = UA_ByteString_allocBuffer(&encrypted, (blocks * plainTextBlockSize) + overHead);
+    if(retval != UA_STATUSCODE_GOOD) {
+        sp->channelModule.deleteContext(channelContext);
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    }
+
+    UA_Byte *pos = encrypted.data;
+    const UA_Byte *end = &encrypted.data[encrypted.length];
+    UA_UInt32_encodeBinary(&length, &pos, end);
+    memcpy(pos, tokenData->data, tokenData->length);
+    memcpy(&pos[tokenData->length], client->channel.remoteNonce.data,
+           client->channel.remoteNonce.length);
+
+    /* Add padding
+     *
+     * 7.36.2.2 Legacy Encrypted Token Secret Format: A Client should not add any
+     * padding after the secret. If a Client adds padding then all bytes shall
+     * be zero. A Server shall check for padding added by Clients and ensure
+     * that all padding bytes are zeros. */
+    size_t paddedLength = plainTextBlockSize * blocks;
+    for(size_t i = totalLength; i < paddedLength; i++)
+        encrypted.data[i] = 0;
+    encrypted.length = paddedLength;
+
+    retval = sp->asymmetricModule.cryptoModule.encryptionAlgorithm.encrypt(sp, channelContext,
+                                                                           &encrypted);
+    encrypted.length = (blocks * plainTextBlockSize) + overHead;
+
+    if(iit) {
+        retval |= UA_String_copy(&sp->asymmetricModule.cryptoModule.encryptionAlgorithm.uri,
+                                 &iit->encryptionAlgorithm);
+    } else {
+        retval |= UA_String_copy(&sp->asymmetricModule.cryptoModule.encryptionAlgorithm.uri,
+                                 &unit->encryptionAlgorithm);
+    }
+
+    UA_ByteString_deleteMembers(tokenData);
+    *tokenData = encrypted;
+
+    /* Delete the temp channel context */
+    sp->channelModule.deleteContext(channelContext);
+
+    return retval;
+}
+
 static UA_StatusCode
 activateSession(UA_Client *client) {
     UA_ActivateSessionRequest request;
@@ -376,6 +476,16 @@ activateSession(UA_Client *client) {
     retval |= UA_String_copy(&client->config.userTokenPolicy.policyId,
                              (UA_String*)request.userIdentityToken.content.decoded.data);
 
+    /* Encrypt the UserIdentityToken */
+    const UA_String *userTokenPolicy = &client->channel.securityPolicy->policyUri;
+    if(client->config.userTokenPolicy.securityPolicyUri.length > 0)
+        userTokenPolicy = &client->config.userTokenPolicy.securityPolicyUri;
+    retval |= encryptUserIdentityToken(client, userTokenPolicy, &request.userIdentityToken);
+    if(retval != UA_STATUSCODE_GOOD) {
+        UA_ActivateSessionRequest_deleteMembers(&request);
+        return retval;
+    }
+
     /* This function call is to prepare a client signature */
     if(client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGN ||
        client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) {