/** * Returns the sender's entity identity. The identity will be unknown if the local entity is a * trusted network client and the message was sent by a trusted network server using the local * entity's master token. * * @return the sender's entity identity or null if unknown. * @throws MslCryptoException if there is a crypto error accessing the entity identity; */ public String getIdentity() throws MslCryptoException { final MessageHeader messageHeader = getMessageHeader(); if (messageHeader != null) { final MasterToken masterToken = messageHeader.getMasterToken(); if (masterToken != null) return masterToken.getIdentity(); return messageHeader.getEntityAuthenticationData().getIdentity(); } final ErrorHeader errorHeader = getErrorHeader(); return errorHeader.getEntityAuthenticationData().getIdentity(); }
/** * Construct a new message input stream. The header is parsed. * * <p>If key request data is provided and a matching key response data is found in the message * header the key exchange will be performed to process the message payloads. * * <p>Service tokens will be decrypted and verified with the provided crypto contexts identified * by token name. A default crypto context may be provided by using the empty string as the token * name; if a token name is not explcitly mapped onto a crypto context, the default crypto context * will be used. * * @param ctx MSL context. * @param source MSL input stream. * @param charset input stream character set encoding. * @param keyRequestData key request data to use when processing key response data. * @param cryptoContexts the map of service token names onto crypto contexts used to decrypt and * verify service tokens. * @throws MslEncodingException if there is an error parsing the message. * @throws MslCryptoException if there is an error decrypting or verifying the header or creating * the message payload crypto context. * @throws MslEntityAuthException if unable to create the entity authentication data. * @throws MslUserAuthException if unable to create the user authentication data. * @throws MslMessageException if the message master token is expired and the message is not * renewable. * @throws MslMasterTokenException if the master token is not trusted and needs to be or if it has * been revoked. * @throws MslUserIdTokenException if the user ID token has been revoked. * @throws MslKeyExchangeException if there is an error with the key request data or key response * data or the key exchange scheme is not supported. * @throws MslMessageException if the message master token is expired and the message is not * renewable. * @throws MslException if the message does not contain an entity authentication data or a master * token, or a token is improperly bound to another token. */ public MessageInputStream( final MslContext ctx, final InputStream source, final Charset charset, final Set<KeyRequestData> keyRequestData, final Map<String, ICryptoContext> cryptoContexts) throws MslEncodingException, MslEntityAuthException, MslCryptoException, MslUserAuthException, MslMessageException, MslKeyExchangeException, MslMasterTokenException, MslUserIdTokenException, MslMessageException, MslException { // Parse the header. this.source = source; this.tokener = new JSONTokener(new InputStreamReader(source, charset)); final JSONObject jo; try { if (!this.tokener.more()) throw new MslEncodingException(MslError.MESSAGE_DATA_MISSING); final Object o = this.tokener.nextValue(); if (!(o instanceof JSONObject)) throw new MslEncodingException(MslError.MESSAGE_FORMAT_ERROR); jo = (JSONObject) o; } catch (final JSONException e) { throw new MslEncodingException(MslError.JSON_PARSE_ERROR, "header", e); } this.header = Header.parseHeader(ctx, jo, cryptoContexts); try { // For error messages there are no key exchange or payload crypto // contexts. if (this.header instanceof ErrorHeader) { this.keyxCryptoContext = null; this.cryptoContext = null; return; } // Grab the key exchange crypto context, if any. final MessageHeader messageHeader = (MessageHeader) this.header; this.keyxCryptoContext = getKeyxCryptoContext(ctx, messageHeader, keyRequestData); // In peer-to-peer mode or in trusted network mode with no key // exchange the payload crypto context equals the header crypto // context. if (ctx.isPeerToPeer() || this.keyxCryptoContext == null) this.cryptoContext = messageHeader.getCryptoContext(); // Otherwise the payload crypto context equals the key exchange // crypto context. else this.cryptoContext = this.keyxCryptoContext; // If this is a handshake message but it is not renewable or does // not contain key request data then reject the message. if (messageHeader.isHandshake() && (!messageHeader.isRenewable() || messageHeader.getKeyRequestData().isEmpty())) { throw new MslMessageException( MslError.HANDSHAKE_DATA_MISSING, messageHeader.toJSONString()); } // If I am in peer-to-peer mode or the master token is verified // (i.e. issued by the local entity which is therefore a trusted // network server) then perform the master token checks. final MasterToken masterToken = messageHeader.getMasterToken(); if (masterToken != null && (ctx.isPeerToPeer() || masterToken.isVerified())) { // If the master token has been revoked then reject the // message. final TokenFactory factory = ctx.getTokenFactory(); final MslError revoked = factory.isMasterTokenRevoked(ctx, masterToken); if (revoked != null) throw new MslMasterTokenException(revoked, masterToken); // If the user ID token has been revoked then reject the // message. We know the master token is not null and that it is // verified so we assume the user ID token is as well. final UserIdToken userIdToken = messageHeader.getUserIdToken(); if (userIdToken != null) { final MslError uitRevoked = factory.isUserIdTokenRevoked(ctx, masterToken, userIdToken); if (uitRevoked != null) throw new MslUserIdTokenException(uitRevoked, userIdToken); } // If the master token is expired... if (masterToken.isExpired(null)) { // If the message is not renewable or does not contain key // request data then reject the message. if (!messageHeader.isRenewable() || messageHeader.getKeyRequestData().isEmpty()) throw new MslMessageException(MslError.MESSAGE_EXPIRED, messageHeader.toJSONString()); // If the master token will not be renewed by the token // factory then reject the message. // // This throws an exception if the master token is not // renewable. final MslError notRenewable = factory.isMasterTokenRenewable(ctx, masterToken); if (notRenewable != null) throw new MslMessageException( notRenewable, "Master token is expired and not renewable."); } } // TODO: This is the old non-replayable logic for backwards // compatibility. It should be removed once all MSL stacks have // migrated to the newer non-replayable ID logic. // // If the message is non-replayable (it is not from a trusted // network server). if (messageHeader.isNonReplayable()) { // ...and not also renewable with key request data and a // master token then reject the message. if (!messageHeader.isRenewable() || messageHeader.getKeyRequestData().isEmpty() || masterToken == null) { throw new MslMessageException( MslError.INCOMPLETE_NONREPLAYABLE_MESSAGE, messageHeader.toJSONString()); } // If the message does not have the newest master token // then notify the sender. final TokenFactory factory = ctx.getTokenFactory(); if (!factory.isNewestMasterToken(ctx, masterToken)) throw new MslMessageException(MslError.MESSAGE_REPLAYED, messageHeader.toJSONString()); } // If the message is non-replayable (it is not from a trusted // network server). final Long nonReplayableId = messageHeader.getNonReplayableId(); if (nonReplayableId != null) { // ...and does not include a master token then reject the // message. if (masterToken == null) throw new MslMessageException( MslError.INCOMPLETE_NONREPLAYABLE_MESSAGE, messageHeader.toJSONString()); // If the non-replayable ID is not accepted then notify the // sender. final TokenFactory factory = ctx.getTokenFactory(); final MslError replayed = factory.acceptNonReplayableId(ctx, masterToken, nonReplayableId); if (replayed != null) throw new MslMessageException(replayed, messageHeader.toJSONString()); } } catch (final MslException e) { if (this.header instanceof MessageHeader) { final MessageHeader messageHeader = (MessageHeader) this.header; e.setEntity(messageHeader.getMasterToken()); e.setEntity(messageHeader.getEntityAuthenticationData()); e.setUser(messageHeader.getUserIdToken()); e.setUser(messageHeader.getUserAuthenticationData()); e.setMessageId(messageHeader.getMessageId()); } else { final ErrorHeader errorHeader = (ErrorHeader) this.header; e.setEntity(errorHeader.getEntityAuthenticationData()); e.setMessageId(errorHeader.getMessageId()); } throw e; } }