/** * 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(); }
/** * Return the crypto context resulting from key response data contained in the provided header. * * <p>The {@link MslException}s thrown by this method will not have the entity or user set. * * @param ctx MSL context. * @param header header. * @param keyRequestData key request data for key exchange. * @return the crypto context or null if the header does not contain key response data or is for * an error message. * @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 MslCryptoException if the crypto context cannot be created. * @throws MslEncodingException if there is an error parsing the JSON. * @throws MslMasterTokenException if the master token is not trusted and needs to be. * @throws MslEntityAuthException if there is a problem with the master token identity. */ private static ICryptoContext getKeyxCryptoContext( final MslContext ctx, final MessageHeader header, final Set<KeyRequestData> keyRequestData) throws MslCryptoException, MslKeyExchangeException, MslEncodingException, MslMasterTokenException, MslEntityAuthException { // Pull the header data. final MessageHeader messageHeader = (MessageHeader) header; final MasterToken masterToken = messageHeader.getMasterToken(); final KeyResponseData keyResponse = messageHeader.getKeyResponseData(); // If there is no key response data then return null. if (keyResponse == null) return null; // If the key response data master token is decrypted then use the // master token keys to create the crypto context. final MasterToken keyxMasterToken = keyResponse.getMasterToken(); if (keyxMasterToken.isDecrypted()) return new SessionCryptoContext(ctx, keyxMasterToken); // Perform the key exchange. final KeyExchangeScheme responseScheme = keyResponse.getKeyExchangeScheme(); final KeyExchangeFactory factory = ctx.getKeyExchangeFactory(responseScheme); if (factory == null) throw new MslKeyExchangeException(MslError.KEYX_FACTORY_NOT_FOUND, responseScheme.name()); // Attempt the key exchange but if it fails then try with the next // key request data before giving up. MslException keyxException = null; final Iterator<KeyRequestData> keyRequests = keyRequestData.iterator(); while (keyRequests.hasNext()) { final KeyRequestData keyRequest = keyRequests.next(); final KeyExchangeScheme requestScheme = keyRequest.getKeyExchangeScheme(); // Skip incompatible key request data. if (!responseScheme.equals(requestScheme)) continue; try { return factory.getCryptoContext(ctx, keyRequest, keyResponse, masterToken); } catch (final MslKeyExchangeException e) { if (!keyRequests.hasNext()) throw e; keyxException = e; } catch (final MslEncodingException e) { if (!keyRequests.hasNext()) throw e; keyxException = e; } catch (final MslMasterTokenException e) { if (!keyRequests.hasNext()) throw e; keyxException = e; } catch (final MslEntityAuthException e) { if (!keyRequests.hasNext()) throw e; keyxException = e; } } // We did not perform a successful key exchange. If we caught an // exception then throw that exception now. if (keyxException != null) { if (keyxException instanceof MslKeyExchangeException) throw (MslKeyExchangeException) keyxException; if (keyxException instanceof MslEncodingException) throw (MslEncodingException) keyxException; if (keyxException instanceof MslMasterTokenException) throw (MslMasterTokenException) keyxException; if (keyxException instanceof MslEntityAuthException) throw (MslEntityAuthException) keyxException; throw new MslInternalException( "Unexpected exception caught during key exchange.", keyxException); } // If we did not perform a successful key exchange then the // payloads will not decrypt properly. Throw an exception. throw new MslKeyExchangeException( MslError.KEYX_RESPONSE_REQUEST_MISMATCH, Arrays.toString(keyRequestData.toArray())); }
/** * 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; } }