RequestDecryptor.java
/*******************************************************************************
* Copyright (c) 2019 RISE SICS and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.html.
*
* Contributors:
* Joakim Brorsson
* Ludwig Seitz (RISE SICS)
* Tobias Andersson (RISE SICS)
* Rikard Höglund (RISE SICS)
*
******************************************************************************/
package org.eclipse.californium.oscore;
import java.io.ByteArrayInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.eclipse.californium.core.coap.OptionSet;
import org.eclipse.californium.core.coap.Request;
import org.eclipse.californium.core.network.serialization.UdpDataParser;
import org.eclipse.californium.cose.Encrypt0Message;
import org.eclipse.californium.core.coap.CoAP;
import org.eclipse.californium.core.coap.CoAP.Code;
import org.eclipse.californium.core.coap.CoAP.ResponseCode;
import org.eclipse.californium.elements.util.DatagramReader;
import org.eclipse.californium.oscore.group.GroupDynamicContextDerivation;
import com.upokecenter.cbor.CBORObject;
import com.upokecenter.cbor.CBORType;
import org.eclipse.californium.cose.HeaderKeys;
/**
*
* Decrypts an OSCORE encrypted Request.
*
*/
public class RequestDecryptor extends Decryptor {
/**
* The logger
*/
private static final Logger LOGGER = LoggerFactory.getLogger(RequestDecryptor.class);
/**
* @param db the context database used
* @param request the request to decrypt
* @param ctx the OSCore context
*
* @return the decrypted request
*
* @throws CoapOSException if decryption fails
*/
public static Request decrypt(OSCoreCtxDB db, Request request, OSCoreCtx ctx) throws CoapOSException {
discardEOptions(request);
byte[] protectedData = request.getPayload();
Encrypt0Message enc;
OptionSet uOptions = request.getOptions();
try {
enc = decompression(protectedData, request);
} catch (OSException e) {
LOGGER.error(ErrorDescriptions.FAILED_TO_DECODE_COSE);
throw new CoapOSException(ErrorDescriptions.FAILED_TO_DECODE_COSE, ResponseCode.BAD_OPTION);
}
CBORObject kid = enc.findAttribute(HeaderKeys.KID);
if (kid == null || !kid.getType().equals(CBORType.ByteString)) {
LOGGER.error(ErrorDescriptions.MISSING_KID);
throw new CoapOSException(ErrorDescriptions.FAILED_TO_DECODE_COSE, ResponseCode.BAD_OPTION);
}
byte[] rid = kid.GetByteString();
// Retrieve Context ID (kid context)
CBORObject kidContext = enc.findAttribute(CBORObject.FromObject(10));
byte[] contextID = null;
if (kidContext != null) {
contextID = kidContext.GetByteString();
}
// Perform context re-derivation procedure if triggered or ongoing
try {
ctx = ContextRederivation.incomingRequest(db, ctx, contextID, rid);
} catch (OSException e) {
LOGGER.error(ErrorDescriptions.CONTEXT_REGENERATION_FAILED);
throw new CoapOSException(ErrorDescriptions.CONTEXT_REGENERATION_FAILED, ResponseCode.BAD_REQUEST);
}
// Attempt Group OSCORE dynamic context generation
if (ctx == null) {
ctx = GroupDynamicContextDerivation.derive(db, rid, contextID);
}
if (ctx == null) {
LOGGER.error(ErrorDescriptions.CONTEXT_NOT_FOUND);
throw new CoapOSException(ErrorDescriptions.CONTEXT_NOT_FOUND, ResponseCode.UNAUTHORIZED);
}
byte[] plaintext;
try {
plaintext = decryptAndDecode(enc, request, ctx, null);
} catch (OSException e) {
//First check for replay exceptions
if (e.getMessage().equals(ErrorDescriptions.REPLAY_DETECT)) {
LOGGER.error(ErrorDescriptions.REPLAY_DETECT);
throw new CoapOSException(ErrorDescriptions.REPLAY_DETECT, ResponseCode.UNAUTHORIZED);
} else if (e.getMessage().equals(ErrorDescriptions.COUNTERSIGNATURE_CHECK_FAILED)) {
//Check for countersignature verification failure
LOGGER.error(ErrorDescriptions.COUNTERSIGNATURE_CHECK_FAILED);
throw new CoapOSException(ErrorDescriptions.COUNTERSIGNATURE_CHECK_FAILED, ResponseCode.BAD_REQUEST);
}
//Otherwise return generic error message
LOGGER.error(ErrorDescriptions.DECRYPTION_FAILED);
throw new CoapOSException(ErrorDescriptions.DECRYPTION_FAILED, ResponseCode.BAD_REQUEST);
}
//Check if parsing of request plaintext succeeds
try {
DatagramReader reader = new DatagramReader(new ByteArrayInputStream(plaintext));
ctx.setCoAPCode(Code.valueOf(reader.read(CoAP.MessageFormat.CODE_BITS)));
// resets option so eOptions gets priority during parse
request.setOptions(EMPTY);
new UdpDataParser().parseOptionsAndPayload(reader, request);
} catch (Exception e) {
LOGGER.error(ErrorDescriptions.DECRYPTION_FAILED);
throw new CoapOSException(ErrorDescriptions.DECRYPTION_FAILED, ResponseCode.BAD_REQUEST);
}
OptionSet eOptions = request.getOptions();
eOptions = OptionJuggle.merge(eOptions, uOptions);
request.setOptions(eOptions);
// Associate the Token with the context used
db.addContext(request.getToken(), ctx);
//Set information about the OSCORE context used in the endpoint context of this request
OSCoreEndpointContextInfo.receivingRequest(ctx, request);
return OptionJuggle.setRealCodeRequest(request, ctx.getCoAPCode());
}
}