diff --git a/core/src/main/java/com/netflix/msl/msg/MessageHeader.java b/core/src/main/java/com/netflix/msl/msg/MessageHeader.java index 7ab1261d..388d3aac 100644 --- a/core/src/main/java/com/netflix/msl/msg/MessageHeader.java +++ b/core/src/main/java/com/netflix/msl/msg/MessageHeader.java @@ -115,6 +115,8 @@ public class MessageHeader extends Header { private static final long MILLISECONDS_PER_SECOND = 1000; // Message header data. + /** Key sender. */ + private static final String KEY_SENDER = "sender"; /** Key timestamp. */ private static final String KEY_TIMESTAMP = "timestamp"; /** Key message ID. */ @@ -268,6 +270,20 @@ public MessageHeader(final MslContext ctx, final EntityAuthenticationData entity } if (!encrypted && headerData.userAuthData != null) throw new MslInternalException("User authentication data cannot be included if the message is not encrypted."); + + // Older MSL stacks expect the sender if a master token is being used. + // + // If the local entity does not know its entity identity, then use the + // empty string. This will work except for the case where the old MSL + // stack is receiving a message for which it is also the issuer of the + // master token. That scenario will continue to fail. + final String sender; + if (masterToken != null) { + final String localIdentity = ctx.getEntityAuthenticationData(null).getIdentity(); + sender = (localIdentity != null) ? localIdentity : ""; + } else { + sender = null; + } this.entityAuthData = (masterToken == null) ? entityAuthData : null; this.masterToken = masterToken; @@ -341,6 +357,7 @@ public MessageHeader(final MslContext ctx, final EntityAuthenticationData entity try { final MslEncoderFactory encoder = ctx.getMslEncoderFactory(); headerdata = encoder.createObject(); + if (sender != null) headerdata.put(KEY_SENDER, sender); headerdata.put(KEY_TIMESTAMP, this.timestamp); headerdata.put(KEY_MESSAGE_ID, this.messageId); headerdata.put(KEY_NON_REPLAYABLE, this.nonReplayableId != null); diff --git a/core/src/main/javascript/msg/MessageHeader.js b/core/src/main/javascript/msg/MessageHeader.js index 34472ada..0ffb5209 100644 --- a/core/src/main/javascript/msg/MessageHeader.js +++ b/core/src/main/javascript/msg/MessageHeader.js @@ -100,6 +100,12 @@ var MILLISECONDS_PER_SECOND = 1000; // Message header data. + /** + * Key sender. + * @const + * @type {string} + */ + var KEY_SENDER = "sender"; /** * Key timestamp. * @const @@ -513,161 +519,191 @@ */ init: function init(ctx, entityAuthData, masterToken, headerData, peerData, creationData, callback) { var self = this; - AsyncExecutor(callback, function() { - // Message ID must be within range. - if (headerData.messageId < 0 || headerData.messageId > MslConstants.MAX_LONG_VALUE) - throw new MslInternalException("Message ID " + headerData.messageId + " is out of range."); - - // Message entity must be provided. - if (!entityAuthData && !masterToken) - throw new MslInternalException("Message entity authentication data or master token must be provided."); - - // Do not allow user authentication data to be included if the message - // will not be encrypted. - var encrypted; - if (masterToken) { - encrypted = true; + if (creationData) { + // Ignore the sender. + construct(null); } else { - var scheme = entityAuthData.scheme; - encrypted = scheme.encrypts; - } - if (!encrypted && headerData.userAuthData) - throw new MslInternalException("User authentication data cannot be included if the message is not encrypted."); - - entityAuthData = (!masterToken) ? entityAuthData : null; - var nonReplayableId = headerData.nonReplayableId; - var renewable = headerData.renewable; - var handshake = headerData.handshake; - var capabilities = headerData.capabilities; - var messageId = headerData.messageId; - var keyRequestData = (headerData.keyRequestData) ? headerData.keyRequestData : []; - var keyResponseData = headerData.keyResponseData; - var userAuthData = headerData.userAuthData; - var userIdToken = headerData.userIdToken; - var serviceTokens = (headerData.serviceTokens) ? headerData.serviceTokens : []; - var peerMasterToken, peerUserIdToken, peerServiceTokens; - if (ctx.isPeerToPeer()) { - peerMasterToken = peerData.peerMasterToken; - peerUserIdToken = peerData.peerUserIdToken; - peerServiceTokens = (peerData.peerServiceTokens) ? peerData.peerServiceTokens : []; - } else { - peerMasterToken = null; - peerUserIdToken = null; - peerServiceTokens = []; - } - - // Grab token verification master tokens. - var tokenVerificationMasterToken, peerTokenVerificationMasterToken; - if (keyResponseData) { - // The key response data is used for token verification in a - // trusted services network and peer token verification in a peer- - // to-peer network. - if (!ctx.isPeerToPeer()) { - tokenVerificationMasterToken = keyResponseData.masterToken; - peerTokenVerificationMasterToken = peerMasterToken; + // Older MSL stacks expect the sender if a master token is being used. + // + // If the local entity does not know its entity identity, then use the + // empty string. This will work except for the case where the old MSL + // stack is receiving a message for which it is also the issuer of the + // master token. That scenario will continue to fail. + if (masterToken) { + ctx.getEntityAuthenticationData(null, { + result: function(ead) { + AsyncExecutor(callback, function() { + var localIdentity = ead.getIdentity(); + var sender = (localIdentity) ? localIdentity : ""; + construct(sender); + }, self); + }, + error: callback.error, + }); } else { - tokenVerificationMasterToken = masterToken; - peerTokenVerificationMasterToken = keyResponseData.masterToken; + construct(null); } - } else { - tokenVerificationMasterToken = masterToken; - peerTokenVerificationMasterToken = peerMasterToken; } + }, self); - // Check token combinations. - if (userIdToken && (!tokenVerificationMasterToken || !userIdToken.isBoundTo(tokenVerificationMasterToken))) - throw new MslInternalException("User ID token must be bound to a master token."); - if (peerUserIdToken && (!peerTokenVerificationMasterToken || !peerUserIdToken.isBoundTo(peerTokenVerificationMasterToken))) - throw new MslInternalException("Peer user ID token must be bound to a peer master token."); - - // All service tokens must be unbound or if bound, bound to the - // provided tokens. - serviceTokens.forEach(function(serviceToken) { - if (serviceToken.isMasterTokenBound() && (!tokenVerificationMasterToken || !serviceToken.isBoundTo(tokenVerificationMasterToken))) - throw new MslInternalException("Master token bound service tokens must be bound to the provided master token."); - if (serviceToken.isUserIdTokenBound() && (!userIdToken || !serviceToken.isBoundTo(userIdToken))) - throw new MslInternalException("User ID token bound service tokens must be bound to the provided user ID token."); - }, this); - peerServiceTokens.forEach(function(peerServiceToken) { - if (peerServiceToken.isMasterTokenBound() && (!peerTokenVerificationMasterToken || !peerServiceToken.isBoundTo(peerTokenVerificationMasterToken))) - throw new MslInternalException("Master token bound peer service tokens must be bound to the provided peer master token."); - if (peerServiceToken.isUserIdTokenBound() && (!peerUserIdToken || !peerServiceToken.isBoundTo(peerUserIdToken))) - throw new MslInternalException("User ID token bound peer service tokens must be bound to the provided peer user ID token."); - }, this); - - // Create the header data. - var user, timestampSeconds, headerdata, messageCryptoContext; - if (!creationData) { - // Grab the user. - user = (userIdToken) ? userIdToken.user : null; + function construct(sender) { + AsyncExecutor(callback, function() { + // Message ID must be within range. + if (headerData.messageId < 0 || headerData.messageId > MslConstants.MAX_LONG_VALUE) + throw new MslInternalException("Message ID " + headerData.messageId + " is out of range."); + + // Message entity must be provided. + if (!entityAuthData && !masterToken) + throw new MslInternalException("Message entity authentication data or master token must be provided."); - // Set the creation timestamp. - timestampSeconds = parseInt(ctx.getTime() / MILLISECONDS_PER_SECOND); - - // Construct the header data. - try { - var encoder = ctx.getMslEncoderFactory(); - headerdata = encoder.createObject(); - headerdata.put(KEY_TIMESTAMP, timestampSeconds); - headerdata.put(KEY_MESSAGE_ID, messageId); - headerdata.put(KEY_NON_REPLAYABLE, (typeof nonReplayableId === 'number')); - if (typeof nonReplayableId === 'number') headerdata.put(KEY_NON_REPLAYABLE_ID, nonReplayableId); - headerdata.put(KEY_RENEWABLE, renewable); - headerdata.put(KEY_HANDSHAKE, handshake); - if (capabilities) headerdata.put(KEY_CAPABILITIES, capabilities); - if (keyRequestData.length > 0) headerdata.put(KEY_KEY_REQUEST_DATA, keyRequestData); - if (keyResponseData) headerdata.put(KEY_KEY_RESPONSE_DATA, keyResponseData); - if (userAuthData) headerdata.put(KEY_USER_AUTHENTICATION_DATA, userAuthData); - if (userIdToken) headerdata.put(KEY_USER_ID_TOKEN, userIdToken); - if (serviceTokens.length > 0) headerdata.put(KEY_SERVICE_TOKENS, serviceTokens); - if (peerMasterToken) headerdata.put(KEY_PEER_MASTER_TOKEN, peerMasterToken); - if (peerUserIdToken) headerdata.put(KEY_PEER_USER_ID_TOKEN, peerUserIdToken); - if (peerServiceTokens.length > 0) headerdata.put(KEY_PEER_SERVICE_TOKENS, peerServiceTokens); - } catch (e) { - if (e instanceof MslEncoderException) { - throw new MslEncodingException(MslError.MSL_ENCODE_ERROR, "headerdata", e) - .setMasterToken(masterToken) - .setEntityAuthenticationData(entityAuthData) - .setUserIdToken(peerUserIdToken) - .setUserAuthenticationData(userAuthData) - .setMessageId(messageId); + // Do not allow user authentication data to be included if the message + // will not be encrypted. + var encrypted; + if (masterToken) { + encrypted = true; + } else { + var scheme = entityAuthData.scheme; + encrypted = scheme.encrypts; + } + if (!encrypted && headerData.userAuthData) + throw new MslInternalException("User authentication data cannot be included if the message is not encrypted."); + + entityAuthData = (!masterToken) ? entityAuthData : null; + var nonReplayableId = headerData.nonReplayableId; + var renewable = headerData.renewable; + var handshake = headerData.handshake; + var capabilities = headerData.capabilities; + var messageId = headerData.messageId; + var keyRequestData = (headerData.keyRequestData) ? headerData.keyRequestData : []; + var keyResponseData = headerData.keyResponseData; + var userAuthData = headerData.userAuthData; + var userIdToken = headerData.userIdToken; + var serviceTokens = (headerData.serviceTokens) ? headerData.serviceTokens : []; + var peerMasterToken, peerUserIdToken, peerServiceTokens; + if (ctx.isPeerToPeer()) { + peerMasterToken = peerData.peerMasterToken; + peerUserIdToken = peerData.peerUserIdToken; + peerServiceTokens = (peerData.peerServiceTokens) ? peerData.peerServiceTokens : []; + } else { + peerMasterToken = null; + peerUserIdToken = null; + peerServiceTokens = []; + } + + // Grab token verification master tokens. + var tokenVerificationMasterToken, peerTokenVerificationMasterToken; + if (keyResponseData) { + // The key response data is used for token verification in a + // trusted services network and peer token verification in a peer- + // to-peer network. + if (!ctx.isPeerToPeer()) { + tokenVerificationMasterToken = keyResponseData.masterToken; + peerTokenVerificationMasterToken = peerMasterToken; + } else { + tokenVerificationMasterToken = masterToken; + peerTokenVerificationMasterToken = keyResponseData.masterToken; } - throw e; + } else { + tokenVerificationMasterToken = masterToken; + peerTokenVerificationMasterToken = peerMasterToken; } - - // Get the correct crypto context. - try { - messageCryptoContext = getMessageCryptoContext(ctx, entityAuthData, masterToken); - } catch (e) { - if (e instanceof MslException) { - e.setMasterToken(masterToken); - e.setEntityAuthenticationData(entityAuthData); - e.setUserIdToken(userIdToken); - e.setUserAuthenticationData(userAuthData); - e.setMessageId(messageId); + + // Check token combinations. + if (userIdToken && (!tokenVerificationMasterToken || !userIdToken.isBoundTo(tokenVerificationMasterToken))) + throw new MslInternalException("User ID token must be bound to a master token."); + if (peerUserIdToken && (!peerTokenVerificationMasterToken || !peerUserIdToken.isBoundTo(peerTokenVerificationMasterToken))) + throw new MslInternalException("Peer user ID token must be bound to a peer master token."); + + // All service tokens must be unbound or if bound, bound to the + // provided tokens. + serviceTokens.forEach(function(serviceToken) { + if (serviceToken.isMasterTokenBound() && (!tokenVerificationMasterToken || !serviceToken.isBoundTo(tokenVerificationMasterToken))) + throw new MslInternalException("Master token bound service tokens must be bound to the provided master token."); + if (serviceToken.isUserIdTokenBound() && (!userIdToken || !serviceToken.isBoundTo(userIdToken))) + throw new MslInternalException("User ID token bound service tokens must be bound to the provided user ID token."); + }, this); + peerServiceTokens.forEach(function(peerServiceToken) { + if (peerServiceToken.isMasterTokenBound() && (!peerTokenVerificationMasterToken || !peerServiceToken.isBoundTo(peerTokenVerificationMasterToken))) + throw new MslInternalException("Master token bound peer service tokens must be bound to the provided peer master token."); + if (peerServiceToken.isUserIdTokenBound() && (!peerUserIdToken || !peerServiceToken.isBoundTo(peerUserIdToken))) + throw new MslInternalException("User ID token bound peer service tokens must be bound to the provided peer user ID token."); + }, this); + + // Create the header data. + var user, timestampSeconds, headerdata, messageCryptoContext; + if (!creationData) { + // Grab the user. + user = (userIdToken) ? userIdToken.user : null; + + // Set the creation timestamp. + timestampSeconds = parseInt(ctx.getTime() / MILLISECONDS_PER_SECOND); + + // Construct the header data. + try { + var encoder = ctx.getMslEncoderFactory(); + headerdata = encoder.createObject(); + if (typeof sender === 'string') headerdata.put(KEY_SENDER, sender); + headerdata.put(KEY_TIMESTAMP, timestampSeconds); + headerdata.put(KEY_MESSAGE_ID, messageId); + headerdata.put(KEY_NON_REPLAYABLE, (typeof nonReplayableId === 'number')); + if (typeof nonReplayableId === 'number') headerdata.put(KEY_NON_REPLAYABLE_ID, nonReplayableId); + headerdata.put(KEY_RENEWABLE, renewable); + headerdata.put(KEY_HANDSHAKE, handshake); + if (capabilities) headerdata.put(KEY_CAPABILITIES, capabilities); + if (keyRequestData.length > 0) headerdata.put(KEY_KEY_REQUEST_DATA, keyRequestData); + if (keyResponseData) headerdata.put(KEY_KEY_RESPONSE_DATA, keyResponseData); + if (userAuthData) headerdata.put(KEY_USER_AUTHENTICATION_DATA, userAuthData); + if (userIdToken) headerdata.put(KEY_USER_ID_TOKEN, userIdToken); + if (serviceTokens.length > 0) headerdata.put(KEY_SERVICE_TOKENS, serviceTokens); + if (peerMasterToken) headerdata.put(KEY_PEER_MASTER_TOKEN, peerMasterToken); + if (peerUserIdToken) headerdata.put(KEY_PEER_USER_ID_TOKEN, peerUserIdToken); + if (peerServiceTokens.length > 0) headerdata.put(KEY_PEER_SERVICE_TOKENS, peerServiceTokens); + } catch (e) { + if (e instanceof MslEncoderException) { + throw new MslEncodingException(MslError.MSL_ENCODE_ERROR, "headerdata", e) + .setMasterToken(masterToken) + .setEntityAuthenticationData(entityAuthData) + .setUserIdToken(peerUserIdToken) + .setUserAuthenticationData(userAuthData) + .setMessageId(messageId); + } + throw e; } - throw e; + + // Get the correct crypto context. + try { + messageCryptoContext = getMessageCryptoContext(ctx, entityAuthData, masterToken); + } catch (e) { + if (e instanceof MslException) { + e.setMasterToken(masterToken); + e.setEntityAuthenticationData(entityAuthData); + e.setUserIdToken(userIdToken); + e.setUserAuthenticationData(userAuthData); + e.setMessageId(messageId); + } + throw e; + } + } else { + user = creationData.user; + timestampSeconds = creationData.timestampSeconds; + headerdata = creationData.headerdata; + messageCryptoContext = creationData.messageCryptoContext; } - } else { - user = creationData.user; - timestampSeconds = creationData.timestampSeconds; - headerdata = creationData.headerdata; - messageCryptoContext = creationData.messageCryptoContext; - } - - // The properties. - var props = buildProperties(ctx, entityAuthData, masterToken, headerdata, - timestampSeconds, messageId, nonReplayableId, - renewable, handshake, - capabilities, keyRequestData, keyResponseData, - userAuthData, userIdToken, - serviceTokens, - peerMasterToken, peerUserIdToken, peerServiceTokens, - user, messageCryptoContext); - Object.defineProperties(this, props); - return this; - }, self); + + // The properties. + var props = buildProperties(ctx, entityAuthData, masterToken, headerdata, + timestampSeconds, messageId, nonReplayableId, + renewable, handshake, + capabilities, keyRequestData, keyResponseData, + userAuthData, userIdToken, + serviceTokens, + peerMasterToken, peerUserIdToken, peerServiceTokens, + user, messageCryptoContext); + Object.defineProperties(this, props); + return this; + }, self); + } }, /** diff --git a/core/src/main/javascript/package.json b/core/src/main/javascript/package.json index 4fd35360..e7e10ff6 100644 --- a/core/src/main/javascript/package.json +++ b/core/src/main/javascript/package.json @@ -1,6 +1,6 @@ { "name": "msl-core", - "version": "1.1221.0", + "version": "1.1221.1", "description": "Message Security Layer", "keywords": [ "msl", diff --git a/integ-tests/src/test/javascript/package.json b/integ-tests/src/test/javascript/package.json index 836fc093..e3934a9b 100644 --- a/integ-tests/src/test/javascript/package.json +++ b/integ-tests/src/test/javascript/package.json @@ -1,6 +1,6 @@ { "name": "msl-integ-tests-exec", - "version": "1.1221.0", + "version": "1.1221.1", "description": "Message Security Layer Integration Tests Execution", "keywords": [ "msl", @@ -21,8 +21,8 @@ "lint": "find . -type f -name '*.js' ! -regex '.*/node_modules/.*' | xargs jshint --verbose" }, "dependencies": { - "msl-core": "^1.1221.0", - "msl-tests": "^1.1221.0" + "msl-core": "file:../../../../core/src/main/javascript/msl-core-1.1221.1.tgz", + "msl-tests": "file:../../../../tests/src/main/javascript/msl-tests-1.1221.1.tgz" }, "devDependencies": { "ec-key": "0.0.2", diff --git a/tests/src/main/javascript/package.json b/tests/src/main/javascript/package.json index c1e85eca..8734f017 100644 --- a/tests/src/main/javascript/package.json +++ b/tests/src/main/javascript/package.json @@ -1,6 +1,6 @@ { "name": "msl-tests", - "version": "1.1221.0", + "version": "1.1221.1", "description": "Message Security Layer Tests", "keywords": [ "msl", @@ -42,7 +42,7 @@ ] }, "dependencies": { - "msl-core": "^1.1221.0" + "msl-core": "^1.1221.1" }, "devDependencies": { "ec-key": "0.0.2", diff --git a/tests/src/test/javascript/package.json b/tests/src/test/javascript/package.json index c6eebd6f..0ebdb6a1 100644 --- a/tests/src/test/javascript/package.json +++ b/tests/src/test/javascript/package.json @@ -1,6 +1,6 @@ { "name": "msl-tests-exec", - "version": "1.1221.0", + "version": "1.1221.1", "description": "Message Security Layer Tests Execution", "keywords": [ "msl", @@ -20,8 +20,8 @@ "test": "find . -type f -name '*.js' ! -regex '.*/node_modules/.*' | xargs -n1 jasmine-node --matchall --forceexit --captureExceptions ./node_modules/msl-tests/lib/jasmine/msl-helpers.js" }, "dependencies": { - "msl-core": "^1.1221.0", - "msl-tests": "^1.1221.0" + "msl-core": "file:../../../../core/src/main/javascript/msl-core-1.1221.1.tgz", + "msl-tests": "file:../../main/javascript/msl-tests-1.1221.1.tgz" }, "devDependencies": { "ec-key": "0.0.2",