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. ----------------------------------------------------------------------------->