Skip to content

Commit

Permalink
Support client bootstrapping to receive OSCORE security information
Browse files Browse the repository at this point in the history
This commit enables the client to receive OSCORE security context
information from the bootstrap server during the bootstrapping
process. The bootstrap server web UI was updated to accept settings
for OSCORE under the LWM2M Server tab. Some changes were done in
the client side code to support this functionality. Finally, an
OSCORE server identity is now generated and set.

Signed-off-by: Rikard Höglund <rikard.hoglund@ri.se>
  • Loading branch information
rikard-sics authored and sbernard31 committed Jan 26, 2021
1 parent 4488653 commit 0d45552
Show file tree
Hide file tree
Showing 12 changed files with 201 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -97,20 +97,24 @@ public Iterator<SecurityInfo> getAllByEndpoint(String endpoint) {
// TODO this should be done via OSCORE store ?
// Extract OSCORE security info
if (bsConfig != null && bsConfig.oscore != null && !bsConfig.oscore.isEmpty()) {
LOG.trace("Extracting OSCORE security info for endpoint {}", endpoint);
LOG.trace("Looking for OSCORE security info for endpoint {}", endpoint);

// First find the context for this endpoint
for (Map.Entry<Integer, BootstrapConfig.OscoreObject> oscoreEntry : bsConfig.oscore.entrySet()) {
OscoreObject value = oscoreEntry.getValue();
for (ServerSecurity security : bsConfig.security.values()) {
// Only find contexts for BS-Client connections
Integer oscoreInstanceId = security.oscoreSecurityMode;
if (security.bootstrapServer && oscoreInstanceId != null) {
OscoreObject oscoreObject = bsConfig.oscore.get(oscoreInstanceId);

HashMapCtxDB db = OscoreHandler.getContextDB();
byte[] rid = Hex.decodeHex(value.oscoreRecipientId.toCharArray());
OSCoreCtx ctx = db.getContext(rid);
HashMapCtxDB db = OscoreHandler.getContextDB();
byte[] rid = Hex.decodeHex(oscoreObject.oscoreRecipientId.toCharArray());
OSCoreCtx ctx = db.getContext(rid);

// Create the security info (will re-add the context to the db)
SecurityInfo securityInfo = SecurityInfo.newOSCoreInfo(endpoint, ctx);
// Create the security info (will re-add the context to the db)
SecurityInfo securityInfo = SecurityInfo.newOSCoreInfo(endpoint, ctx);

return Arrays.asList(securityInfo).iterator();
return Arrays.asList(securityInfo).iterator();
}
}
}

Expand Down
15 changes: 15 additions & 0 deletions leshan-bsserver-demo/src/main/resources/webapp/js/bsconfigstore.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ var configFromRestToUI = function(config){
if (!newConfig.dm){
newConfig.dm.push({security:security});
}

// add oscore object (if any) to dm
var oscoreObjectInstanceId = security.oscoreSecurityMode;
var oscore = config.oscore[oscoreObjectInstanceId];
if(oscore){
newConfig.dm.push({oscore:oscore});
}
}
}
return newConfig;
Expand All @@ -41,18 +48,26 @@ var configsFromRestToUI = function(configs){
//convert config from UI to rest API format:
var configFromUIToRest = function(config){
var newConfig = {servers:{}, security:{}, oscore:{}};
var writingOscore = false;
for (var i = 0; i < config.bs.length; i++) {
var bs = config.bs[i];
newConfig.security[i] = bs.security;
newConfig.oscore[i] = bs.oscore;
writingOscore |= (bs.oscore != null);
}
for (var j = 0; j < config.dm.length; j++) {
var dm = config.dm[j];
newConfig.security[i+j] = dm.security;
delete dm.security;
newConfig.oscore[i+j] = dm.oscore;
writingOscore |= (dm.oscore != null);
delete dm.oscore;
newConfig.servers[j] = dm;
}
newConfig.toDelete = ["/0", "/1"]
if(writingOscore) {
newConfig.toDelete.push("/21");
}
return newConfig;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<securityconfig-input ref="lwserver" onchange={update} show={activetab.lwserver}
securi={ "coaps://" + location.hostname + ":5684" }
unsecuri= { "coap://" + location.hostname + ":5683" }
secmode = { {no_sec:true, psk:true, rpk:true, x509:true} }
secmode = { {no_sec:true, psk:true, rpk:true, x509:true, oscore:true} }
></securityconfig-input>
</div>
<div>
Expand Down Expand Up @@ -96,7 +96,7 @@

if(bsserver.secmode === "OSCORE") {
var bsserverOscore = bsserver.oscore;
var oscore =
var bsOscore =
{
oscoreMasterSecret : bsserverOscore.masterSecret,
oscoreSenderId : bsserverOscore.senderId,
Expand All @@ -109,6 +109,21 @@
bsserver.secmode = "NO_SEC"; // act as no_sec from here
}

if(lwserver.secmode === "OSCORE") {
var lwserverOscore = lwserver.oscore;
var dmOscore =
{
oscoreMasterSecret : lwserverOscore.masterSecret,
oscoreSenderId : lwserverOscore.senderId,
oscoreRecipientId : lwserverOscore.recipientId,
oscoreAeadAlgorithm : lwserverOscore.aeadAlgorithm,
oscoreHmacAlgorithm : lwserverOscore.hkdfAlgorithm,
oscoreMasterSalt : lwserverOscore.masterSalt,
}
var dmOscoreSecurityMode = 1; // link to dm oscore object
lwserver.secmode = "NO_SEC"; // act as no_sec from here
}

// add config to the store
bsConfigStore.add(endpoint.value, {
dm:[{
Expand All @@ -129,8 +144,10 @@
smsBindingKeyParam : [ ],
smsBindingKeySecret : [ ],
smsSecurityMode : "NO_SEC",
uri : lwserver.uri
}
uri : lwserver.uri,
oscoreSecurityMode : dmOscoreSecurityMode
},
oscore : dmOscore
}],
bs:[{
security : {
Expand All @@ -148,7 +165,7 @@
uri : bsserver.uri,
oscoreSecurityMode : bsOscoreSecurityMode
},
oscore
oscore : bsOscore
}]
});
$('#bootstrap-modal').modal('hide');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
</div>
</td>
<td>
<div each={ config.dm }>
<div each={ config.dm } if={ security }>
<p>
<strong>{security.uri}</strong><br/>
security mode : {security.securityMode}<br/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.List;

import org.eclipse.californium.core.CoapServer;
import org.eclipse.californium.core.Utils;
import org.eclipse.californium.core.network.CoapEndpoint;
import org.eclipse.californium.core.network.Endpoint;
import org.eclipse.californium.core.network.config.NetworkConfig;
Expand Down Expand Up @@ -192,8 +193,13 @@ public boolean isTrusted(RawPublicKeyIdentity id) {
}

currentEndpoint = endpointFactory.createUnsecuredEndpoint(localAddress, coapConfig, null, db);
// TODO OSCORE: Should be visible in identity if OSCORE is used
serverIdentity = Identity.unsecure(serverInfo.getAddress());

// Build server identity for OSCORE
String sidString = Utils.toHexString(serverInfo.senderId).replace("[", "").replace("]", "").toLowerCase();
String ridString = Utils.toHexString(serverInfo.recipientId).replace("[", "").replace("]", "")
.toLowerCase();
String oscoreIdentity = "sid=" + sidString + ",rid=" + ridString;
serverIdentity = Identity.oscoreOnly(serverInfo.getAddress(), oscoreIdentity);
} else {
currentEndpoint = endpointFactory.createUnsecuredEndpoint(localAddress, coapConfig, null, null);
serverIdentity = Identity.unsecure(serverInfo.getAddress());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,20 +92,31 @@ public static ServersInfo getInfo(Map<Integer, LwM2mObjectEnabler> objectEnabler
info.serverUri = new URI((String) security.getResource(SEC_SERVER_URI).getValue());
info.secureMode = getSecurityMode(security);

// find instance id of the associated oscore object (if any)
ObjectLink oscoreObjLink = (ObjectLink) security.getResource(SEC_OSCORE_SECURITY_MODE).getValue();
int oscoreObjectInstanceId = oscoreObjLink.getObjectInstanceId();

if (!oscoreObjLink.isNullLink() && oscoreObjLink.getObjectId() != OSCORE) {
LOG.warn("The security object's 'OSCORE Security Mode' links to an incorrect object type.");
// find associated oscore instance (if any)
LwM2mObjectInstance oscoreInstance = null;
ObjectLink oscoreObjLink = (ObjectLink) security.getResource(SEC_OSCORE_SECURITY_MODE)
.getValue();
if (oscoreObjLink != null && !oscoreObjLink.isNullLink()) {
if (oscoreObjLink.getObjectId() != OSCORE) {
LOG.warn(
"Invalid Security info for bootstrap server : 'OSCORE Security Mode' does not link to OSCORE Object but to {} object.",
oscoreObjLink.getObjectId());
} else {
if (oscores == null) {
LOG.warn(
"Invalid Security info for bootstrap server : OSCORE object enabler is not available.");
} else {
oscoreInstance = oscores.getInstance(oscoreObjLink.getObjectInstanceId());
if (oscoreInstance == null) {
LOG.warn(
"Invalid Security info for bootstrap server : OSCORE instance {} does not exist.",
oscoreObjLink.getObjectInstanceId());
}
}
}
}

boolean useOscore = oscoreObjLink.getObjectId() == OSCORE;
if (useOscore) {
// get corresponding oscore object
LwM2mObjectInstance oscoreInstance = oscores.getInstance(oscoreObjectInstanceId);
LOG.trace("Bootstrap connection is using OSCORE.");

if (oscoreInstance != null) {
info.useOscore = true;
info.masterSecret = getMasterSecret(oscoreInstance);
info.senderId = getSenderId(oscoreInstance);
Expand Down Expand Up @@ -135,20 +146,31 @@ public static ServersInfo getInfo(Map<Integer, LwM2mObjectEnabler> objectEnabler
info.serverId = (long) security.getResource(SEC_SERVER_ID).getValue();
info.secureMode = getSecurityMode(security);

// find instance id of the associated oscore object (if any)
// find associated oscore instance (if any)
LwM2mObjectInstance oscoreInstance = null;
ObjectLink oscoreObjLink = (ObjectLink) security.getResource(SEC_OSCORE_SECURITY_MODE).getValue();
int oscoreObjectInstanceId = oscoreObjLink.getObjectInstanceId();

if (!oscoreObjLink.isNullLink() && oscoreObjLink.getObjectId() != OSCORE) {
LOG.warn("The security object's 'OSCORE Security Mode' links to an incorrect object type.");
if (oscoreObjLink != null && !oscoreObjLink.isNullLink()) {
if (oscoreObjLink.getObjectId() != OSCORE) {
LOG.warn(
"Invalid Security info for LWM2M server {} : 'OSCORE Security Mode' does not link to OSCORE Object but to {} object.",
info.serverUri, oscoreObjLink.getObjectId());
} else {
if (oscores == null) {
LOG.warn(
"Invalid Security info for LWM2M server {}: OSCORE object enabler is not available.",
info.serverUri);
} else {
oscoreInstance = oscores.getInstance(oscoreObjLink.getObjectInstanceId());
if (oscoreInstance == null) {
LOG.warn(
"Invalid Security info for LWM2M server {} : OSCORE instance {} does not exist.",
info.serverUri, oscoreObjLink.getObjectInstanceId());
}
}
}
}

boolean useOscore = oscoreObjLink.getObjectId() == OSCORE;
if (useOscore) {
// get corresponding oscore object
LwM2mObjectInstance oscoreInstance = oscores.getInstance(oscoreObjectInstanceId);
LOG.trace("Registration connection is using OSCORE.");

if (oscoreInstance != null) {
info.useOscore = true;
info.masterSecret = getMasterSecret(oscoreInstance);
info.senderId = getSenderId(oscoreInstance);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -605,14 +605,14 @@ public static void main(final String[] args) {
// Get models folder
String modelsFolderPath = cl.getOptionValue("m");

// TODO OSCORE : OSCoreCoapStack should be create in Default endpoint factory
HashMapCtxDB db = OscoreHandler.getContextDB();
OSCoreCoapStackFactory.useAsDefault(db);

// Set parameters controlling OSCORE usage
OSCoreSettings oscoreSettings = null;
if (cl.hasOption("msec")) {

HashMapCtxDB db = OscoreHandler.getContextDB();
// TODO OSCORE : OSCoreCoapStack should be create in Default endpoint factory
OSCoreCoapStackFactory.useAsDefault(db);

// Parse OSCORE related command line parameters

String mastersecretStr = cl.getOptionValue("msec");
Expand Down Expand Up @@ -744,6 +744,7 @@ public static void createAndStartClient(String endpoint, String localAddress, in
initializer.setInstancesForObject(SECURITY, noSecBootstap(serverURI));
initializer.setClassForObject(SERVER, Server.class);
}
initializer.setClassForObject(OSCORE, Oscore.class);
} else {
if (pskIdentity != null) {
initializer.setInstancesForObject(SECURITY, psk(serverURI, 123, pskIdentity, pskKey));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*
* Contributors:
* Sierra Wireless - initial API and implementation
* Rikard Höglund (RISE) - additions to support OSCORE
*******************************************************************************/
package org.eclipse.leshan.server.californium.bootstrap;

Expand All @@ -24,11 +25,15 @@
import org.eclipse.californium.core.coap.CoAP.Type;
import org.eclipse.californium.core.coap.Request;
import org.eclipse.californium.core.server.resources.CoapExchange;
import org.eclipse.californium.oscore.HashMapCtxDB;
import org.eclipse.californium.oscore.OSCoreCtx;
import org.eclipse.californium.oscore.OSException;
import org.eclipse.leshan.core.californium.LwM2mCoapResource;
import org.eclipse.leshan.core.request.BootstrapRequest;
import org.eclipse.leshan.core.request.Identity;
import org.eclipse.leshan.core.response.BootstrapResponse;
import org.eclipse.leshan.core.response.SendableResponse;
import org.eclipse.leshan.server.OscoreHandler;
import org.eclipse.leshan.server.bootstrap.BootstrapHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -53,6 +58,24 @@ public void handlePOST(CoapExchange exchange) {
Request request = exchange.advanced().getRequest();
LOG.trace("POST received : {}", request);

// TODO OSCORE : should we really need to do this ?
// Check if this incoming request is using OSCORE
if (exchange.advanced().getRequest().getOptions().getOscore() != null) {
LOG.trace("Client bootstrapped using OSCORE");

// Update the URI of the associated OSCORE Context with the client's URI
// So the server can send requests to the client
HashMapCtxDB db = OscoreHandler.getContextDB();
OSCoreCtx clientCtx = db.getContext(exchange.advanced().getCryptographicContextID());

try {
db.addContext(request.getScheme() + "://"
+ request.getSourceContext().getPeerAddress().getHostString().toString(), clientCtx);
} catch (OSException e) {
LOG.error("Failed to update OSCORE Context for registering client.", request, e);
}
}

// The LW M2M spec (section 8.2) mandates the usage of Confirmable
// messages
if (!Type.CON.equals(request.getType())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ public class BootstrapConfig implements Serializable {
*/
public Map<Integer, ACLConfig> acls = new HashMap<>();

// TODO OSCORE : add some javadoc
/**
* Map indexed by OSCORE Object Instance Id. Key is the OSCORE Object Instance to write.
*/
public Map<Integer, OscoreObject> oscore = new HashMap<>();

/** Server Configuration (object 1) as defined in LWM2M 1.0.x TS. */
Expand Down Expand Up @@ -225,6 +227,11 @@ public static class ServerSecurity implements Serializable {
* Bootstrap-Server Account lifetime is infinite.
*/
public Integer bootstrapServerAccountTimeout = 0;
/**
* The Object ID of the OSCORE Object Instance that holds the OSCORE configuration to be used by the LWM2M
* Client to the LWM2M Server associated with this Security object.
*
*/
public Integer oscoreSecurityMode;

@Override
Expand Down
Loading

0 comments on commit 0d45552

Please sign in to comment.