From 82f2a0c65e0150a0aaee703a2e8c5cade37c015d Mon Sep 17 00:00:00 2001
From: Simon Bernard
Date: Tue, 22 Feb 2022 18:07:56 +0100
Subject: [PATCH] #1203: Add Oscore SecurityInfo support to Server and BS
Server demo
---
.../demo/LeshanBootstrapServerDemo.java | 5 +
.../demo/cli/LeshanBsServerDemoCLI.java | 5 +
.../components/wizard/ClientConfigDialog.vue | 4 +-
.../src/components/wizard/SecurityStep.vue | 46 +-----
.../webapp/src/plugins/icons.js | 2 +
.../webapp/src/views/Bootstrap.vue | 2 +-
.../json/JacksonSecurityDeserializer.java | 52 ++++++-
.../demo/json/JacksonSecuritySerializer.java | 14 ++
.../demo/json/servlet/SecurityServlet.java | 9 +-
.../src/components/security/OscoreInput.vue | 74 ++++++++++
.../components/security/SecurityInfoChip.vue | 8 +-
.../security/SecurityInfoDialog.vue | 26 ++--
.../components/security/SecurityInfoInput.vue | 137 ++++++++++--------
.../src/components/security/TlsInput.vue | 100 +++++++++++++
.../leshan/server/demo/LeshanServerDemo.java | 5 +
.../server/demo/cli/LeshanServerDemoCLI.java | 5 +
.../webapp/src/plugins/icons.js | 2 +
.../webapp/src/views/Security.vue | 10 ++
18 files changed, 389 insertions(+), 117 deletions(-)
create mode 100644 leshan-server-core-demo/webapp/src/components/security/OscoreInput.vue
create mode 100644 leshan-server-core-demo/webapp/src/components/security/TlsInput.vue
diff --git a/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/LeshanBootstrapServerDemo.java b/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/LeshanBootstrapServerDemo.java
index 664dbe92a5..52fc9d78bc 100755
--- a/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/LeshanBootstrapServerDemo.java
+++ b/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/LeshanBootstrapServerDemo.java
@@ -167,6 +167,11 @@ public static LeshanBootstrapServer createBsLeshanServer(LeshanBsServerDemoCLI c
builder.setConfigStore(bsConfigStore);
builder.setSecurityStore(new BootstrapSecurityStoreAdapter(securityStore));
+ // TODO OSCORE Temporary cli option to deactivate OSCORE
+ if (!cli.main.disableOscore) {
+ builder.setEnableOscore(true);
+ }
+
return builder.build();
}
diff --git a/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/cli/LeshanBsServerDemoCLI.java b/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/cli/LeshanBsServerDemoCLI.java
index 4d13c1cc72..eaea51dbd9 100644
--- a/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/cli/LeshanBsServerDemoCLI.java
+++ b/leshan-bsserver-demo/src/main/java/org/eclipse/leshan/server/bootstrap/demo/cli/LeshanBsServerDemoCLI.java
@@ -59,6 +59,11 @@ public static class BootstrapServerGeneralSection extends GeneralSection {
"Set the filename for the configuration.", //
"Default: ${DEFAULT-VALUE}" })
public String configFilename;
+
+ @Option(names = { "-no", "--disable-oscore" },
+ description = { //
+ "Disable experimental OSCORE feature." })
+ public Boolean disableOscore = false;
}
/* ********************************** DTLS Section ******************************** */
diff --git a/leshan-bsserver-demo/webapp/src/components/wizard/ClientConfigDialog.vue b/leshan-bsserver-demo/webapp/src/components/wizard/ClientConfigDialog.vue
index 0100a2901f..807a345441 100644
--- a/leshan-bsserver-demo/webapp/src/components/wizard/ClientConfigDialog.vue
+++ b/leshan-bsserver-demo/webapp/src/components/wizard/ClientConfigDialog.vue
@@ -250,7 +250,9 @@ export default {
}
// apply endpoint to security
- res.security.endpoint = res.endpoint
+ if (res.security) {
+ res.security.endpoint = res.endpoint;
+ }
return res;
},
diff --git a/leshan-bsserver-demo/webapp/src/components/wizard/SecurityStep.vue b/leshan-bsserver-demo/webapp/src/components/wizard/SecurityStep.vue
index 7e342ecf6e..98d19b60f1 100644
--- a/leshan-bsserver-demo/webapp/src/components/wizard/SecurityStep.vue
+++ b/leshan-bsserver-demo/webapp/src/components/wizard/SecurityStep.vue
@@ -22,23 +22,12 @@
a demo.
-
-
+
@@ -53,40 +42,19 @@ export default {
},
data() {
return {
- useDTLS: false,
- internalSecurityInfo: { tls: { mode: "psk", details: {} } },
+ internalSecurityInfo: {},
};
},
watch: {
value(v) {
if (!v) {
- this.useDTLS = false;
- this.internalSecurityInfo = { tls: { mode: "psk", details: {} } };
+ this.internalSecurityInfo = {};
} else {
- this.useDTLS = true;
this.internalSecurityInfo = v;
}
},
},
methods: {
- updateUseDTLS(useDTLS) {
- if (useDTLS) {
- this.$emit("input", this.internalSecurityInfo);
- this.resetValidation();
- this.$emit("update:valid", false);
- } else {
- this.$emit("input", null);
- this.$emit("update:valid", true);
- }
- },
- updateMode(mode) {
- this.internalSecurityInfo.tls.mode = mode;
- this.$emit("input", this.internalSecurityInfo);
- },
- updateDetails(details) {
- this.internalSecurityInfo.tls.details = details;
- this.$emit("input", this.internalSecurityInfo);
- },
resetValidation() {
this.$refs.form.resetValidation();
},
diff --git a/leshan-bsserver-demo/webapp/src/plugins/icons.js b/leshan-bsserver-demo/webapp/src/plugins/icons.js
index 877cb3df15..df929b1957 100644
--- a/leshan-bsserver-demo/webapp/src/plugins/icons.js
+++ b/leshan-bsserver-demo/webapp/src/plugins/icons.js
@@ -32,6 +32,7 @@ import {
mdiKeyPlus,
mdiLeadPencil,
mdiLockOpenRemove,
+ mdiLockOutline,
mdiMagnify,
mdiPlay,
mdiServerSecurity,
@@ -66,6 +67,7 @@ const _icons = {
mdiKeyPlus,
mdiLeadPencil,
mdiLockOpenRemove,
+ mdiLockOutline,
mdiMagnify,
mdiPlay,
mdiServerSecurity,
diff --git a/leshan-bsserver-demo/webapp/src/views/Bootstrap.vue b/leshan-bsserver-demo/webapp/src/views/Bootstrap.vue
index 86d69f2041..21c32e9c1f 100644
--- a/leshan-bsserver-demo/webapp/src/views/Bootstrap.vue
+++ b/leshan-bsserver-demo/webapp/src/views/Bootstrap.vue
@@ -209,7 +209,7 @@ export default {
},
onAdd(config) {
- if (config.security) {
+ if (config.security && (config.security.tls || config.security.oscore)) {
// if we have security we try to add security first
this.axios.put("api/security/clients/", config.security).then(() => {
this.addConfig(config);
diff --git a/leshan-server-core-demo/src/main/java/org/eclipse/leshan/server/core/demo/json/JacksonSecurityDeserializer.java b/leshan-server-core-demo/src/main/java/org/eclipse/leshan/server/core/demo/json/JacksonSecurityDeserializer.java
index fa5a7ba2f8..de872f7522 100644
--- a/leshan-server-core-demo/src/main/java/org/eclipse/leshan/server/core/demo/json/JacksonSecurityDeserializer.java
+++ b/leshan-server-core-demo/src/main/java/org/eclipse/leshan/server/core/demo/json/JacksonSecurityDeserializer.java
@@ -20,6 +20,7 @@
import java.security.GeneralSecurityException;
import java.security.PublicKey;
+import org.eclipse.leshan.core.oscore.OscoreSetting;
import org.eclipse.leshan.core.util.Hex;
import org.eclipse.leshan.core.util.SecurityUtil;
import org.eclipse.leshan.server.security.SecurityInfo;
@@ -49,8 +50,9 @@ public SecurityInfo deserialize(JsonParser p, DeserializationContext ctxt) throw
}
// handle dtls
+ JsonNode oTls = node.get("tls");
if (node.has("tls")) {
- JsonNode oTls = node.get("tls");
+
if (oTls.getNodeType() != JsonNodeType.OBJECT) {
throw new JsonParseException(p, "tls field should be a json object");
}
@@ -103,6 +105,54 @@ public SecurityInfo deserialize(JsonParser p, DeserializationContext ctxt) throw
throw new JsonParseException(p, "Invalid security info content");
}
}
+ // Handle Oscore
+ else if (node.has("oscore")) {
+ JsonNode oOscore = node.get("oscore");
+ if (oOscore.getNodeType() != JsonNodeType.OBJECT) {
+ throw new JsonParseException(p, "oscore field should be a json object");
+ }
+ // get Recipient ID
+ byte[] rid;
+ if (oOscore.has("rid")) {
+ rid = Hex.decodeHex(oOscore.get("rid").asText().toCharArray());
+ } else {
+ throw new JsonParseException(p, "Missing 'rid' field (Recipient ID)");
+ }
+
+ // get Sender ID
+ byte[] sid;
+ if (oOscore.has("sid")) {
+ sid = Hex.decodeHex(oOscore.get("sid").asText().toCharArray());
+ } else {
+ throw new JsonParseException(p, "Missing 'sid' field (Sender ID)");
+ }
+
+ // get Master secret
+ byte[] msec;
+ if (oOscore.has("msec")) {
+ msec = Hex.decodeHex(oOscore.get("msec").asText().toCharArray());
+ } else {
+ throw new JsonParseException(p, "Missing 'msec' field (Master Secret)");
+ }
+
+ // TODO OSCORE add support for msalt, aead and hkdf
+
+ // get Master salt
+ // byte[] msalt;
+ // if (oOscore.has("msalt")) {
+ // msalt = Hex.decodeHex(oOscore.get("msalt").asText().toCharArray());
+ // } else {
+ // msalt = new byte[0];
+ // }
+
+ // TODO OSCORE use dedicated enum / class for algorithm
+ // waiting use default value :
+ // master salf : empty string
+ // aead : AES_CCM_16_64_128("AES-CCM-16-64-128", 10, 128, 64),
+ // hkdf : HKDF_HMAC_SHA_256("HKDF-SHA-256", -10, 256, 0),
+
+ return SecurityInfo.newOscoreInfo(endpoint, new OscoreSetting(sid, rid, msec, 10, -10, null));
+ }
return null;
}
}
diff --git a/leshan-server-core-demo/src/main/java/org/eclipse/leshan/server/core/demo/json/JacksonSecuritySerializer.java b/leshan-server-core-demo/src/main/java/org/eclipse/leshan/server/core/demo/json/JacksonSecuritySerializer.java
index 540c3036e7..61b74c69e0 100644
--- a/leshan-server-core-demo/src/main/java/org/eclipse/leshan/server/core/demo/json/JacksonSecuritySerializer.java
+++ b/leshan-server-core-demo/src/main/java/org/eclipse/leshan/server/core/demo/json/JacksonSecuritySerializer.java
@@ -73,6 +73,20 @@ public void serialize(SecurityInfo securityInfo, JsonGenerator gen, SerializerPr
oTls.put("mode", "x509");
}
}
+ if (securityInfo.useOSCORE()) {
+ // handle OSCORE case :
+ ObjectNode oOscore = JsonNodeFactory.instance.objectNode();
+ oSecInfo.set("oscore", oOscore);
+ oOscore.put("rid", Hex.encodeHexString(securityInfo.getOscoreSetting().getRecipientId()));
+ oOscore.put("sid", Hex.encodeHexString(securityInfo.getOscoreSetting().getSenderId()));
+ oOscore.put("msec", Hex.encodeHexString(securityInfo.getOscoreSetting().getMasterSecret()));
+ // TODO OSCORE it should be possible to use an empty byte array for Master salf. (currently it failed)
+ if (securityInfo.getOscoreSetting().getMasterSalt() != null) {
+ oOscore.put("msalt", Hex.encodeHexString(securityInfo.getOscoreSetting().getMasterSalt()));
+ }
+ oOscore.put("aead", securityInfo.getOscoreSetting().getAeadAlgorithm().getValue());
+ oOscore.put("hkdf", securityInfo.getOscoreSetting().getHkdfAlgorithm().getValue());
+ }
gen.writeTree(oSecInfo);
}
}
diff --git a/leshan-server-core-demo/src/main/java/org/eclipse/leshan/server/core/demo/json/servlet/SecurityServlet.java b/leshan-server-core-demo/src/main/java/org/eclipse/leshan/server/core/demo/json/servlet/SecurityServlet.java
index 443e654718..42e8b147ff 100644
--- a/leshan-server-core-demo/src/main/java/org/eclipse/leshan/server/core/demo/json/servlet/SecurityServlet.java
+++ b/leshan-server-core-demo/src/main/java/org/eclipse/leshan/server/core/demo/json/servlet/SecurityServlet.java
@@ -115,7 +115,11 @@ protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws Se
} catch (JsonParseException e) {
LOG.warn("Could not parse request body", e);
resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
- resp.getWriter().append("Invalid request body").flush();
+ resp.getWriter().append("Invalid request body");
+ if (e.getMessage() != null) {
+ resp.getWriter().append(": ").append(e.getMessage());
+ }
+ resp.getWriter().flush();
} catch (RuntimeException e) {
LOG.warn("unexpected error for request " + req.getPathInfo(), e);
resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
@@ -176,8 +180,7 @@ protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws
resp.setStatus(HttpServletResponse.SC_OK);
} else {
resp.setContentType("application/json");
- resp.getOutputStream()
- .write("{\"message\":\"not_found\"}".getBytes(StandardCharsets.UTF_8));
+ resp.getOutputStream().write("{\"message\":\"not_found\"}".getBytes(StandardCharsets.UTF_8));
resp.setStatus(HttpServletResponse.SC_OK);
}
}
diff --git a/leshan-server-core-demo/webapp/src/components/security/OscoreInput.vue b/leshan-server-core-demo/webapp/src/components/security/OscoreInput.vue
new file mode 100644
index 0000000000..caa4fabd6b
--- /dev/null
+++ b/leshan-server-core-demo/webapp/src/components/security/OscoreInput.vue
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/leshan-server-core-demo/webapp/src/components/security/SecurityInfoChip.vue b/leshan-server-core-demo/webapp/src/components/security/SecurityInfoChip.vue
index dfdf873a71..2e80875e3a 100644
--- a/leshan-server-core-demo/webapp/src/components/security/SecurityInfoChip.vue
+++ b/leshan-server-core-demo/webapp/src/components/security/SecurityInfoChip.vue
@@ -11,13 +11,17 @@
* http://www.eclipse.org/org/documents/edl-v10.html.
----------------------------------------------------------------------------->
-
-
+
+
{{ modeIcon }}
{{ securityInfo.tls.mode }}
+
+ {{$icons.mdiLockOutline}}
+ oscore
+
diff --git a/leshan-server-core-demo/webapp/src/components/security/SecurityInfoDialog.vue b/leshan-server-core-demo/webapp/src/components/security/SecurityInfoDialog.vue
index dbafc9128c..d2093d146d 100644
--- a/leshan-server-core-demo/webapp/src/components/security/SecurityInfoDialog.vue
+++ b/leshan-server-core-demo/webapp/src/components/security/SecurityInfoDialog.vue
@@ -37,26 +37,22 @@
:disabled="editMode"
>
-
{{ editMode ? "Save" : "Add" }}
-
- Cancel
-
+ Cancel
@@ -82,6 +78,15 @@ export default {
this.$emit("input", value);
},
},
+ isValid: {
+ get() {
+ return (
+ this.securityInfo &&
+ (this.securityInfo.tls || this.securityInfo.oscore) &&
+ this.valid
+ );
+ },
+ },
},
watch: {
value(v) {
@@ -95,7 +100,10 @@ export default {
this.editMode = true;
} else {
// default value for creation
- this.securityInfo = { tls:{mode: "psk", details: {} }};
+ this.securityInfo = {
+ endpoint: "",
+ tls: { mode: "psk", details: {} },
+ };
this.editMode = false;
}
}
diff --git a/leshan-server-core-demo/webapp/src/components/security/SecurityInfoInput.vue b/leshan-server-core-demo/webapp/src/components/security/SecurityInfoInput.vue
index f222a68029..bb672f18d0 100644
--- a/leshan-server-core-demo/webapp/src/components/security/SecurityInfoInput.vue
+++ b/leshan-server-core-demo/webapp/src/components/security/SecurityInfoInput.vue
@@ -12,87 +12,102 @@
----------------------------------------------------------------------------->
diff --git a/leshan-server-demo/src/main/java/org/eclipse/leshan/server/demo/LeshanServerDemo.java b/leshan-server-demo/src/main/java/org/eclipse/leshan/server/demo/LeshanServerDemo.java
index 0049f06e1f..9db6f1ca92 100644
--- a/leshan-server-demo/src/main/java/org/eclipse/leshan/server/demo/LeshanServerDemo.java
+++ b/leshan-server-demo/src/main/java/org/eclipse/leshan/server/demo/LeshanServerDemo.java
@@ -216,6 +216,11 @@ public static LeshanServer createLeshanServer(LeshanServerDemoCLI cli) throws Ex
}
builder.setSecurityStore(securityStore);
+ // TODO OSCORE Temporary cli option to deactivate OSCORE
+ if (!cli.main.disableOscore) {
+ builder.setEnableOscore(true);
+ }
+
// Create LWM2M server
return builder.build();
}
diff --git a/leshan-server-demo/src/main/java/org/eclipse/leshan/server/demo/cli/LeshanServerDemoCLI.java b/leshan-server-demo/src/main/java/org/eclipse/leshan/server/demo/cli/LeshanServerDemoCLI.java
index ec03b75065..63f6d055db 100644
--- a/leshan-server-demo/src/main/java/org/eclipse/leshan/server/demo/cli/LeshanServerDemoCLI.java
+++ b/leshan-server-demo/src/main/java/org/eclipse/leshan/server/demo/cli/LeshanServerDemoCLI.java
@@ -77,6 +77,11 @@ public JedisPool convert(String value) throws Exception {
description = { //
"Publish leshan's services to DNS Service discovery." })
public Boolean mdns;
+
+ @Option(names = { "-no", "--disable-oscore" },
+ description = { //
+ "Disable experimental OSCORE feature." })
+ public Boolean disableOscore = false;
}
/* ********************************** DTLS Section ******************************** */
diff --git a/leshan-server-demo/webapp/src/plugins/icons.js b/leshan-server-demo/webapp/src/plugins/icons.js
index bca8a3702d..a3fa43fcc1 100644
--- a/leshan-server-demo/webapp/src/plugins/icons.js
+++ b/leshan-server-demo/webapp/src/plugins/icons.js
@@ -36,6 +36,7 @@ import {
mdiKeyPlus,
mdiLock,
mdiLockOpenRemove,
+ mdiLockOutline,
mdiMagnify,
mdiPencil,
mdiSelect,
@@ -78,6 +79,7 @@ const _icons = {
mdiKeyPlus,
mdiLock,
mdiLockOpenRemove,
+ mdiLockOutline,
mdiMagnify,
mdiPencil,
mdiSelect,
diff --git a/leshan-server-demo/webapp/src/views/Security.vue b/leshan-server-demo/webapp/src/views/Security.vue
index e4f32e88aa..8192844897 100644
--- a/leshan-server-demo/webapp/src/views/Security.vue
+++ b/leshan-server-demo/webapp/src/views/Security.vue
@@ -93,6 +93,16 @@
{{ item.endpoint }}
+
+ Sender ID:
+ {{ item.oscore.sid }}
+
+ Master Secret:{{ item.oscore.msec }}
+
+ Recipient ID:
+ {{ item.oscore.rid }}
+