Commit bf76e0e2db48e13d70035aaf3eaf61ef8bf67959

Authored by Andrew Shvayka
Committed by GitHub
2 parents e06faae3 e3fa441d

Merge pull request #4631 from thingsboard/feature/lwm2m-certificate-verifier

Lwm2m certificate verifier
Showing 60 changed files with 3340 additions and 420 deletions

Too many changes to show.

To preserve performance only 60 of 66 files are displayed.

... ... @@ -17,6 +17,7 @@ package org.thingsboard.server.controller;
17 17
18 18 import com.fasterxml.jackson.databind.ObjectMapper;
19 19 import lombok.extern.slf4j.Slf4j;
  20 +import org.eclipse.leshan.core.SecurityMode;
20 21 import org.springframework.security.access.prepost.PreAuthorize;
21 22 import org.springframework.web.bind.annotation.PathVariable;
22 23 import org.springframework.web.bind.annotation.RequestBody;
... ... @@ -46,9 +47,11 @@ public class Lwm2mController extends BaseController {
46 47 @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
47 48 @RequestMapping(value = "/lwm2m/deviceProfile/bootstrap/{securityMode}/{bootstrapServerIs}", method = RequestMethod.GET)
48 49 @ResponseBody
49   - public ServerSecurityConfig getLwm2mBootstrapSecurityInfo(@PathVariable("securityMode") String securityMode,
  50 + public ServerSecurityConfig getLwm2mBootstrapSecurityInfo(@PathVariable("securityMode") String strSecurityMode,
50 51 @PathVariable("bootstrapServerIs") boolean bootstrapServer) throws ThingsboardException {
  52 + checkNotNull(strSecurityMode);
51 53 try {
  54 + SecurityMode securityMode = SecurityMode.valueOf(strSecurityMode);
52 55 return lwM2MServerSecurityInfoRepository.getServerSecurityInfo(securityMode, bootstrapServer);
53 56 } catch (Exception e) {
54 57 throw handleException(e);
... ...
... ... @@ -18,6 +18,7 @@ package org.thingsboard.server.service.lwm2m;
18 18
19 19 import lombok.RequiredArgsConstructor;
20 20 import lombok.extern.slf4j.Slf4j;
  21 +import org.eclipse.leshan.core.SecurityMode;
21 22 import org.eclipse.leshan.core.util.Hex;
22 23 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
23 24 import org.springframework.stereotype.Service;
... ... @@ -25,7 +26,6 @@ import org.thingsboard.server.common.data.lwm2m.ServerSecurityConfig;
25 26 import org.thingsboard.server.transport.lwm2m.config.LwM2MSecureServerConfig;
26 27 import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportBootstrapConfig;
27 28 import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
28   -import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
29 29
30 30 import java.math.BigInteger;
31 31 import java.security.AlgorithmParameters;
... ... @@ -55,17 +55,16 @@ public class LwM2MServerSecurityInfoRepository {
55 55 * @param bootstrapServer
56 56 * @return ServerSecurityConfig more value is default: Important - port, host, publicKey
57 57 */
58   - public ServerSecurityConfig getServerSecurityInfo(String securityMode, boolean bootstrapServer) {
59   - LwM2MSecurityMode lwM2MSecurityMode = LwM2MSecurityMode.fromSecurityMode(securityMode.toLowerCase());
60   - ServerSecurityConfig result = getServerSecurityConfig(bootstrapServer ? bootstrapConfig : serverConfig, lwM2MSecurityMode);
  58 + public ServerSecurityConfig getServerSecurityInfo(SecurityMode securityMode, boolean bootstrapServer) {
  59 + ServerSecurityConfig result = getServerSecurityConfig(bootstrapServer ? bootstrapConfig : serverConfig, securityMode);
61 60 result.setBootstrapServerIs(bootstrapServer);
62 61 return result;
63 62 }
64 63
65   - private ServerSecurityConfig getServerSecurityConfig(LwM2MSecureServerConfig serverConfig, LwM2MSecurityMode mode) {
  64 + private ServerSecurityConfig getServerSecurityConfig(LwM2MSecureServerConfig serverConfig, SecurityMode securityMode) {
66 65 ServerSecurityConfig bsServ = new ServerSecurityConfig();
67 66 bsServ.setServerId(serverConfig.getId());
68   - switch (mode) {
  67 + switch (securityMode) {
69 68 case NO_SEC:
70 69 bsServ.setHost(serverConfig.getHost());
71 70 bsServ.setPort(serverConfig.getPort());
... ...
... ... @@ -643,6 +643,7 @@ transport:
643 643 private_encoded: "${LWM2M_SERVER_PRIVATE_ENCODED:308193020100301306072a8648ce3d020106082a8648ce3d030107047930770201010420dc774b309e547ceb48fee547e104ce201a9c48c449dc5414cd04e7f5cf05f67ba00a06082a8648ce3d030107a1440342000405064b9e6762dd8d8b8a52355d7b4d8b9a3d64e6d2ee277d76c248861353f3585eeb1838e4f9e37b31fa347aef5ce3431eb54e0a2506910c5e0298817445721b}"
644 644 # Only Certificate_x509:
645 645 alias: "${LWM2M_KEYSTORE_ALIAS_SERVER:server}"
  646 + skip_validity_check_for_client_cert: "${TB_LWM2M_SERVER_SECURITY_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}"
646 647 bootstrap:
647 648 enable: "${LWM2M_ENABLED_BS:true}"
648 649 id: "${LWM2M_SERVER_ID_BS:111}"
... ...
... ... @@ -22,6 +22,7 @@ import io.jsonwebtoken.Claims;
22 22 import io.jsonwebtoken.Header;
23 23 import io.jsonwebtoken.Jwt;
24 24 import io.jsonwebtoken.Jwts;
  25 +import lombok.Getter;
25 26 import lombok.extern.slf4j.Slf4j;
26 27 import org.apache.commons.lang3.RandomStringUtils;
27 28 import org.apache.commons.lang3.StringUtils;
... ... @@ -120,7 +121,7 @@ public abstract class AbstractWebTest {
120 121 protected String refreshToken;
121 122 protected String username;
122 123
123   - private TenantId tenantId;
  124 + protected TenantId tenantId;
124 125
125 126 @SuppressWarnings("rawtypes")
126 127 private HttpMessageConverter mappingJackson2HttpMessageConverter;
... ...
... ... @@ -32,7 +32,8 @@ import java.util.Arrays;
32 32 "org.thingsboard.server.transport.*.attributes.updates.sql.*Test",
33 33 "org.thingsboard.server.transport.*.attributes.request.sql.*Test",
34 34 "org.thingsboard.server.transport.*.claim.sql.*Test",
35   - "org.thingsboard.server.transport.*.provision.sql.*Test"
  35 + "org.thingsboard.server.transport.*.provision.sql.*Test",
  36 + "org.thingsboard.server.transport.lwm2m.*Test"
36 37 })
37 38 public class TransportSqlTestSuite {
38 39
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m;
  17 +
  18 +import com.fasterxml.jackson.core.type.TypeReference;
  19 +import org.apache.commons.io.IOUtils;
  20 +import org.eclipse.leshan.core.util.Hex;
  21 +import org.junit.After;
  22 +import org.junit.Assert;
  23 +import org.junit.Before;
  24 +import org.thingsboard.common.util.JacksonUtil;
  25 +import org.thingsboard.server.common.data.DeviceProfile;
  26 +import org.thingsboard.server.common.data.DeviceProfileProvisionType;
  27 +import org.thingsboard.server.common.data.DeviceProfileType;
  28 +import org.thingsboard.server.common.data.DeviceTransportType;
  29 +import org.thingsboard.server.common.data.ResourceType;
  30 +import org.thingsboard.server.common.data.TbResource;
  31 +import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration;
  32 +import org.thingsboard.server.common.data.device.profile.DeviceProfileData;
  33 +import org.thingsboard.server.common.data.device.profile.DisabledDeviceProfileProvisionConfiguration;
  34 +import org.thingsboard.server.common.data.device.profile.Lwm2mDeviceProfileTransportConfiguration;
  35 +import org.thingsboard.server.controller.AbstractWebsocketTest;
  36 +import org.thingsboard.server.controller.TbTestWebSocketClient;
  37 +import org.thingsboard.server.dao.service.DaoSqlTest;
  38 +
  39 +import java.io.IOException;
  40 +import java.io.InputStream;
  41 +import java.math.BigInteger;
  42 +import java.security.AlgorithmParameters;
  43 +import java.security.GeneralSecurityException;
  44 +import java.security.KeyFactory;
  45 +import java.security.KeyStore;
  46 +import java.security.PrivateKey;
  47 +import java.security.PublicKey;
  48 +import java.security.cert.Certificate;
  49 +import java.security.cert.X509Certificate;
  50 +import java.security.spec.ECGenParameterSpec;
  51 +import java.security.spec.ECParameterSpec;
  52 +import java.security.spec.ECPoint;
  53 +import java.security.spec.ECPrivateKeySpec;
  54 +import java.security.spec.ECPublicKeySpec;
  55 +import java.security.spec.KeySpec;
  56 +import java.util.Base64;
  57 +import java.util.concurrent.Executors;
  58 +import java.util.concurrent.ScheduledExecutorService;
  59 +
  60 +@DaoSqlTest
  61 +public class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
  62 +
  63 + protected DeviceProfile deviceProfile;
  64 + protected ScheduledExecutorService executor;
  65 + protected TbTestWebSocketClient wsClient;
  66 +
  67 + protected final PublicKey clientPublicKey; // client public key used for RPK
  68 + protected final PrivateKey clientPrivateKey; // client private key used for RPK
  69 + protected final PublicKey serverPublicKey; // server public key used for RPK
  70 + protected final PrivateKey serverPrivateKey; // server private key used for RPK
  71 +
  72 + // client private key used for X509
  73 + protected final PrivateKey clientPrivateKeyFromCert;
  74 + // server private key used for X509
  75 + protected final PrivateKey serverPrivateKeyFromCert;
  76 + // client certificate signed by rootCA with a good CN (CN start by leshan_integration_test)
  77 + protected final X509Certificate clientX509Cert;
  78 + // client certificate signed by rootCA but with bad CN (CN does not start by leshan_integration_test)
  79 + protected final X509Certificate clientX509CertWithBadCN;
  80 + // client certificate self-signed with a good CN (CN start by leshan_integration_test)
  81 + protected final X509Certificate clientX509CertSelfSigned;
  82 + // client certificate signed by another CA (not rootCA) with a good CN (CN start by leshan_integration_test)
  83 + protected final X509Certificate clientX509CertNotTrusted;
  84 + // server certificate signed by rootCA
  85 + protected final X509Certificate serverX509Cert;
  86 + // self-signed server certificate
  87 + protected final X509Certificate serverX509CertSelfSigned;
  88 + // rootCA used by the server
  89 + protected final X509Certificate rootCAX509Cert;
  90 + // certificates trustedby the server (should contain rootCA)
  91 + protected final Certificate[] trustedCertificates = new Certificate[1];
  92 +
  93 + public AbstractLwM2MIntegrationTest() {
  94 +// create client credentials
  95 + try {
  96 + // Get point values
  97 + byte[] publicX = Hex
  98 + .decodeHex("89c048261979208666f2bfb188be1968fc9021c416ce12828c06f4e314c167b5".toCharArray());
  99 + byte[] publicY = Hex
  100 + .decodeHex("cbf1eb7587f08e01688d9ada4be859137ca49f79394bad9179326b3090967b68".toCharArray());
  101 + byte[] privateS = Hex
  102 + .decodeHex("e67b68d2aaeb6550f19d98cade3ad62b39532e02e6b422e1f7ea189dabaea5d2".toCharArray());
  103 +
  104 + // Get Elliptic Curve Parameter spec for secp256r1
  105 + AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
  106 + algoParameters.init(new ECGenParameterSpec("secp256r1"));
  107 + ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
  108 +
  109 + // Create key specs
  110 + KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
  111 + parameterSpec);
  112 + KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec);
  113 +
  114 + // Get keys
  115 + clientPublicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
  116 + clientPrivateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);
  117 +
  118 + // Get certificates from key store
  119 + char[] clientKeyStorePwd = "client".toCharArray();
  120 + KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
  121 + try (InputStream clientKeyStoreFile = this.getClass().getClassLoader().getResourceAsStream("lwm2m/credentials/clientKeyStore.jks")) {
  122 + clientKeyStore.load(clientKeyStoreFile, clientKeyStorePwd);
  123 + }
  124 +
  125 + clientPrivateKeyFromCert = (PrivateKey) clientKeyStore.getKey("client", clientKeyStorePwd);
  126 + clientX509Cert = (X509Certificate) clientKeyStore.getCertificate("client");
  127 + clientX509CertWithBadCN = (X509Certificate) clientKeyStore.getCertificate("client_bad_cn");
  128 + clientX509CertSelfSigned = (X509Certificate) clientKeyStore.getCertificate("client_self_signed");
  129 + clientX509CertNotTrusted = (X509Certificate) clientKeyStore.getCertificate("client_not_trusted");
  130 + } catch (GeneralSecurityException | IOException e) {
  131 + throw new RuntimeException(e);
  132 + }
  133 +
  134 + // create server credentials
  135 + try {
  136 + // Get point values
  137 + byte[] publicX = Hex
  138 + .decodeHex("fcc28728c123b155be410fc1c0651da374fc6ebe7f96606e90d927d188894a73".toCharArray());
  139 + byte[] publicY = Hex
  140 + .decodeHex("d2ffaa73957d76984633fc1cc54d0b763ca0559a9dff9706e9f4557dacc3f52a".toCharArray());
  141 + byte[] privateS = Hex
  142 + .decodeHex("1dae121ba406802ef07c193c1ee4df91115aabd79c1ed7f4c0ef7ef6a5449400".toCharArray());
  143 +
  144 + // Get Elliptic Curve Parameter spec for secp256r1
  145 + AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
  146 + algoParameters.init(new ECGenParameterSpec("secp256r1"));
  147 + ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);
  148 +
  149 + // Create key specs
  150 + KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
  151 + parameterSpec);
  152 + KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec);
  153 +
  154 + // Get keys
  155 + serverPublicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
  156 + serverPrivateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);
  157 +
  158 + // Get certificates from key store
  159 + char[] serverKeyStorePwd = "server".toCharArray();
  160 + KeyStore serverKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
  161 + try (InputStream serverKeyStoreFile = this.getClass().getClassLoader().getResourceAsStream("lwm2m/credentials/serverKeyStore.jks")) {
  162 + serverKeyStore.load(serverKeyStoreFile, serverKeyStorePwd);
  163 + }
  164 +
  165 + serverPrivateKeyFromCert = (PrivateKey) serverKeyStore.getKey("server", serverKeyStorePwd);
  166 + rootCAX509Cert = (X509Certificate) serverKeyStore.getCertificate("rootCA");
  167 + serverX509Cert = (X509Certificate) serverKeyStore.getCertificate("server");
  168 + serverX509CertSelfSigned = (X509Certificate) serverKeyStore.getCertificate("server_self_signed");
  169 + trustedCertificates[0] = rootCAX509Cert;
  170 + } catch (GeneralSecurityException | IOException e) {
  171 + throw new RuntimeException(e);
  172 + }
  173 + }
  174 +
  175 + @Before
  176 + public void beforeTest() throws Exception {
  177 + executor = Executors.newScheduledThreadPool(10);
  178 + loginTenantAdmin();
  179 +
  180 + String[] resources = new String[]{"1.xml", "2.xml", "3.xml"};
  181 + for (String resourceName : resources) {
  182 + TbResource lwModel = new TbResource();
  183 + lwModel.setResourceType(ResourceType.LWM2M_MODEL);
  184 + lwModel.setTitle(resourceName);
  185 + lwModel.setFileName(resourceName);
  186 + lwModel.setTenantId(tenantId);
  187 + byte[] bytes = IOUtils.toByteArray(AbstractLwM2MIntegrationTest.class.getClassLoader().getResourceAsStream("lwm2m/" + resourceName));
  188 + lwModel.setData(Base64.getEncoder().encodeToString(bytes));
  189 + lwModel = doPostWithTypedResponse("/api/resource", lwModel, new TypeReference<>() {
  190 + });
  191 + Assert.assertNotNull(lwModel);
  192 + }
  193 + wsClient = buildAndConnectWebSocketClient();
  194 + }
  195 +
  196 + protected void createDeviceProfile(String transportConfiguration) throws Exception {
  197 + deviceProfile = new DeviceProfile();
  198 +
  199 + deviceProfile.setName("LwM2M");
  200 + deviceProfile.setType(DeviceProfileType.DEFAULT);
  201 + deviceProfile.setTenantId(tenantId);
  202 + deviceProfile.setTransportType(DeviceTransportType.LWM2M);
  203 + deviceProfile.setProvisionType(DeviceProfileProvisionType.DISABLED);
  204 + deviceProfile.setDescription(deviceProfile.getName());
  205 +
  206 + DeviceProfileData deviceProfileData = new DeviceProfileData();
  207 + deviceProfileData.setConfiguration(new DefaultDeviceProfileConfiguration());
  208 + deviceProfileData.setProvisionConfiguration(new DisabledDeviceProfileProvisionConfiguration(null));
  209 + deviceProfileData.setTransportConfiguration(JacksonUtil.fromString(transportConfiguration, Lwm2mDeviceProfileTransportConfiguration.class));
  210 + deviceProfile.setProfileData(deviceProfileData);
  211 +
  212 + deviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class);
  213 + Assert.assertNotNull(deviceProfile);
  214 + }
  215 +
  216 + @After
  217 + public void after() {
  218 + executor.shutdownNow();
  219 + wsClient.close();
  220 + }
  221 +
  222 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m;
  17 +
  18 +import org.eclipse.californium.core.network.config.NetworkConfig;
  19 +import org.eclipse.leshan.client.object.Security;
  20 +import org.jetbrains.annotations.NotNull;
  21 +import org.junit.Assert;
  22 +import org.junit.Test;
  23 +import org.thingsboard.common.util.JacksonUtil;
  24 +import org.thingsboard.server.common.data.Device;
  25 +import org.thingsboard.server.common.data.query.EntityData;
  26 +import org.thingsboard.server.common.data.query.EntityDataPageLink;
  27 +import org.thingsboard.server.common.data.query.EntityDataQuery;
  28 +import org.thingsboard.server.common.data.query.EntityKey;
  29 +import org.thingsboard.server.common.data.query.EntityKeyType;
  30 +import org.thingsboard.server.common.data.query.SingleEntityFilter;
  31 +import org.thingsboard.server.common.data.security.DeviceCredentials;
  32 +import org.thingsboard.server.common.data.security.DeviceCredentialsType;
  33 +import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper;
  34 +import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;
  35 +import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
  36 +import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd;
  37 +import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient;
  38 +import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
  39 +import org.thingsboard.server.common.data.device.credentials.lwm2m.NoSecClientCredentials;
  40 +
  41 +import java.util.Collections;
  42 +import java.util.List;
  43 +
  44 +import static org.eclipse.leshan.client.object.Security.noSec;
  45 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  46 +
  47 +public class NoSecLwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
  48 +
  49 + protected final String TRANSPORT_CONFIGURATION = "{\n" +
  50 + " \"type\": \"LWM2M\",\n" +
  51 + " \"observeAttr\": {\n" +
  52 + " \"keyName\": {\n" +
  53 + " \"/3_1.0/0/9\": \"batteryLevel\"\n" +
  54 + " },\n" +
  55 + " \"observe\": [],\n" +
  56 + " \"attribute\": [\n" +
  57 + " ],\n" +
  58 + " \"telemetry\": [\n" +
  59 + " \"/3_1.0/0/9\"\n" +
  60 + " ],\n" +
  61 + " \"attributeLwm2m\": {}\n" +
  62 + " },\n" +
  63 + " \"bootstrap\": {\n" +
  64 + " \"servers\": {\n" +
  65 + " \"binding\": \"UQ\",\n" +
  66 + " \"shortId\": 123,\n" +
  67 + " \"lifetime\": 300,\n" +
  68 + " \"notifIfDisabled\": true,\n" +
  69 + " \"defaultMinPeriod\": 1\n" +
  70 + " },\n" +
  71 + " \"lwm2mServer\": {\n" +
  72 + " \"host\": \"localhost\",\n" +
  73 + " \"port\": 5685,\n" +
  74 + " \"serverId\": 123,\n" +
  75 + " \"securityMode\": \"NO_SEC\",\n" +
  76 + " \"serverPublicKey\": \"\",\n" +
  77 + " \"bootstrapServerIs\": false,\n" +
  78 + " \"clientHoldOffTime\": 1,\n" +
  79 + " \"bootstrapServerAccountTimeout\": 0\n" +
  80 + " },\n" +
  81 + " \"bootstrapServer\": {\n" +
  82 + " \"host\": \"localhost\",\n" +
  83 + " \"port\": 5687,\n" +
  84 + " \"serverId\": 111,\n" +
  85 + " \"securityMode\": \"NO_SEC\",\n" +
  86 + " \"serverPublicKey\": \"\",\n" +
  87 + " \"bootstrapServerIs\": true,\n" +
  88 + " \"clientHoldOffTime\": 1,\n" +
  89 + " \"bootstrapServerAccountTimeout\": 0\n" +
  90 + " }\n" +
  91 + " },\n" +
  92 + " \"clientLwM2mSettings\": {\n" +
  93 + " \"clientOnlyObserveAfterConnect\": 1\n" +
  94 + " }\n" +
  95 + "}";
  96 +
  97 + private final int port = 5685;
  98 + private final Security security = noSec("coap://localhost:" + port, 123);
  99 + private final NetworkConfig coapConfig = new NetworkConfig().setString("COAP_PORT", Integer.toString(port));
  100 +
  101 + @NotNull
  102 + private Device createDevice(String deviceAEndpoint) throws Exception {
  103 + Device device = new Device();
  104 + device.setName("Device A");
  105 + device.setDeviceProfileId(deviceProfile.getId());
  106 + device.setTenantId(tenantId);
  107 + device = doPost("/api/device", device, Device.class);
  108 + Assert.assertNotNull(device);
  109 +
  110 + DeviceCredentials deviceCredentials =
  111 + doGet("/api/device/" + device.getId().getId().toString() + "/credentials", DeviceCredentials.class);
  112 + Assert.assertEquals(device.getId(), deviceCredentials.getDeviceId());
  113 + deviceCredentials.setCredentialsType(DeviceCredentialsType.LWM2M_CREDENTIALS);
  114 +
  115 + LwM2MCredentials noSecCredentials = new LwM2MCredentials();
  116 + NoSecClientCredentials clientCredentials = new NoSecClientCredentials();
  117 + clientCredentials.setEndpoint(deviceAEndpoint);
  118 + noSecCredentials.setClient(clientCredentials);
  119 + deviceCredentials.setCredentialsValue(JacksonUtil.toString(noSecCredentials));
  120 + doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk());
  121 + return device;
  122 + }
  123 +
  124 + @Test
  125 + public void testConnectAndObserveTelemetry() throws Exception {
  126 + createDeviceProfile(TRANSPORT_CONFIGURATION);
  127 +
  128 + String deviceAEndpoint = "deviceAEndpoint";
  129 +
  130 + Device device = createDevice(deviceAEndpoint);
  131 +
  132 + SingleEntityFilter sef = new SingleEntityFilter();
  133 + sef.setSingleEntity(device.getId());
  134 + LatestValueCmd latestCmd = new LatestValueCmd();
  135 + latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "batteryLevel")));
  136 + EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null),
  137 + Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
  138 +
  139 + EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null);
  140 + TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper();
  141 + wrapper.setEntityDataCmds(Collections.singletonList(cmd));
  142 +
  143 + wsClient.send(mapper.writeValueAsString(wrapper));
  144 + wsClient.waitForReply();
  145 +
  146 + wsClient.registerWaitForUpdate();
  147 + LwM2MTestClient client = new LwM2MTestClient(executor, deviceAEndpoint);
  148 + client.init(security, coapConfig);
  149 + String msg = wsClient.waitForUpdate();
  150 +
  151 + EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);
  152 + Assert.assertEquals(1, update.getCmdId());
  153 + List<EntityData> eData = update.getUpdate();
  154 + Assert.assertNotNull(eData);
  155 + Assert.assertEquals(1, eData.size());
  156 + Assert.assertEquals(device.getId(), eData.get(0).getEntityId());
  157 + Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES));
  158 + var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("batteryLevel");
  159 + Assert.assertEquals(42, Long.parseLong(tsValue.getValue()));
  160 + client.destroy();
  161 + }
  162 +
  163 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m;
  17 +
  18 +import org.eclipse.californium.core.network.config.NetworkConfig;
  19 +import org.eclipse.leshan.client.object.Security;
  20 +import org.jetbrains.annotations.NotNull;
  21 +import org.junit.Assert;
  22 +import org.junit.Test;
  23 +import org.thingsboard.common.util.JacksonUtil;
  24 +import org.thingsboard.server.common.data.Device;
  25 +import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredentials;
  26 +import org.thingsboard.server.common.data.query.EntityData;
  27 +import org.thingsboard.server.common.data.query.EntityDataPageLink;
  28 +import org.thingsboard.server.common.data.query.EntityDataQuery;
  29 +import org.thingsboard.server.common.data.query.EntityKey;
  30 +import org.thingsboard.server.common.data.query.EntityKeyType;
  31 +import org.thingsboard.server.common.data.query.SingleEntityFilter;
  32 +import org.thingsboard.server.common.data.security.DeviceCredentials;
  33 +import org.thingsboard.server.common.data.security.DeviceCredentialsType;
  34 +import org.thingsboard.server.common.transport.util.SslUtil;
  35 +import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper;
  36 +import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;
  37 +import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
  38 +import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd;
  39 +import org.thingsboard.server.transport.lwm2m.client.LwM2MTestClient;
  40 +import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
  41 +
  42 +import java.util.Collections;
  43 +import java.util.List;
  44 +
  45 +import static org.eclipse.leshan.client.object.Security.x509;
  46 +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
  47 +
  48 +public class X509LwM2MIntegrationTest extends AbstractLwM2MIntegrationTest {
  49 +
  50 + protected final String TRANSPORT_CONFIGURATION = "{\n" +
  51 + " \"type\": \"LWM2M\",\n" +
  52 + " \"observeAttr\": {\n" +
  53 + " \"keyName\": {\n" +
  54 + " \"/3_1.0/0/9\": \"batteryLevel\"\n" +
  55 + " },\n" +
  56 + " \"observe\": [],\n" +
  57 + " \"attribute\": [\n" +
  58 + " ],\n" +
  59 + " \"telemetry\": [\n" +
  60 + " \"/3_1.0/0/9\"\n" +
  61 + " ],\n" +
  62 + " \"attributeLwm2m\": {}\n" +
  63 + " },\n" +
  64 + " \"bootstrap\": {\n" +
  65 + " \"servers\": {\n" +
  66 + " \"binding\": \"UQ\",\n" +
  67 + " \"shortId\": 123,\n" +
  68 + " \"lifetime\": 300,\n" +
  69 + " \"notifIfDisabled\": true,\n" +
  70 + " \"defaultMinPeriod\": 1\n" +
  71 + " },\n" +
  72 + " \"lwm2mServer\": {\n" +
  73 + " \"host\": \"localhost\",\n" +
  74 + " \"port\": 5686,\n" +
  75 + " \"serverId\": 123,\n" +
  76 + " \"serverPublicKey\": \"\",\n" +
  77 + " \"bootstrapServerIs\": false,\n" +
  78 + " \"clientHoldOffTime\": 1,\n" +
  79 + " \"bootstrapServerAccountTimeout\": 0\n" +
  80 + " },\n" +
  81 + " \"bootstrapServer\": {\n" +
  82 + " \"host\": \"localhost\",\n" +
  83 + " \"port\": 5687,\n" +
  84 + " \"serverId\": 111,\n" +
  85 + " \"securityMode\": \"NO_SEC\",\n" +
  86 + " \"serverPublicKey\": \"\",\n" +
  87 + " \"bootstrapServerIs\": true,\n" +
  88 + " \"clientHoldOffTime\": 1,\n" +
  89 + " \"bootstrapServerAccountTimeout\": 0\n" +
  90 + " }\n" +
  91 + " },\n" +
  92 + " \"clientLwM2mSettings\": {\n" +
  93 + " \"clientOnlyObserveAfterConnect\": 1\n" +
  94 + " }\n" +
  95 + "}";
  96 +
  97 +
  98 + private final int port = 5686;
  99 + private final NetworkConfig coapConfig = new NetworkConfig().setString("COAP_SECURE_PORT", Integer.toString(port));
  100 + private final String endpoint = "deviceAEndpoint";
  101 + private final String serverUri = "coaps://localhost:" + port;
  102 +
  103 + @NotNull
  104 + private Device createDevice(X509ClientCredentials clientCredentials) throws Exception {
  105 + Device device = new Device();
  106 + device.setName("Device A");
  107 + device.setDeviceProfileId(deviceProfile.getId());
  108 + device.setTenantId(tenantId);
  109 + device = doPost("/api/device", device, Device.class);
  110 + Assert.assertNotNull(device);
  111 +
  112 + DeviceCredentials deviceCredentials =
  113 + doGet("/api/device/" + device.getId().getId().toString() + "/credentials", DeviceCredentials.class);
  114 + Assert.assertEquals(device.getId(), deviceCredentials.getDeviceId());
  115 + deviceCredentials.setCredentialsType(DeviceCredentialsType.LWM2M_CREDENTIALS);
  116 +
  117 + LwM2MCredentials credentials = new LwM2MCredentials();
  118 +
  119 + credentials.setClient(clientCredentials);
  120 +
  121 + deviceCredentials.setCredentialsValue(JacksonUtil.toString(credentials));
  122 + doPost("/api/device/credentials", deviceCredentials).andExpect(status().isOk());
  123 + return device;
  124 + }
  125 +
  126 + @Test
  127 + public void testConnectAndObserveTelemetry() throws Exception {
  128 + createDeviceProfile(TRANSPORT_CONFIGURATION);
  129 + X509ClientCredentials credentials = new X509ClientCredentials();
  130 + credentials.setEndpoint(endpoint);
  131 + Device device = createDevice(credentials);
  132 +
  133 + SingleEntityFilter sef = new SingleEntityFilter();
  134 + sef.setSingleEntity(device.getId());
  135 + LatestValueCmd latestCmd = new LatestValueCmd();
  136 + latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "batteryLevel")));
  137 + EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null),
  138 + Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
  139 +
  140 + EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null);
  141 + TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper();
  142 + wrapper.setEntityDataCmds(Collections.singletonList(cmd));
  143 +
  144 + wsClient.send(mapper.writeValueAsString(wrapper));
  145 + wsClient.waitForReply();
  146 +
  147 + wsClient.registerWaitForUpdate();
  148 + LwM2MTestClient client = new LwM2MTestClient(executor, endpoint);
  149 + Security security = x509(serverUri, 123, clientX509Cert.getEncoded(), clientPrivateKeyFromCert.getEncoded(), serverX509Cert.getEncoded());
  150 + client.init(security, coapConfig);
  151 + String msg = wsClient.waitForUpdate();
  152 +
  153 + EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);
  154 + Assert.assertEquals(1, update.getCmdId());
  155 + List<EntityData> eData = update.getUpdate();
  156 + Assert.assertNotNull(eData);
  157 + Assert.assertEquals(1, eData.size());
  158 + Assert.assertEquals(device.getId(), eData.get(0).getEntityId());
  159 + Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES));
  160 + var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("batteryLevel");
  161 + Assert.assertEquals(42, Long.parseLong(tsValue.getValue()));
  162 + client.destroy();
  163 + }
  164 +
  165 + @Test
  166 + public void testConnectWithCertAndObserveTelemetry() throws Exception {
  167 + createDeviceProfile(TRANSPORT_CONFIGURATION);
  168 + X509ClientCredentials credentials = new X509ClientCredentials();
  169 + credentials.setEndpoint(endpoint);
  170 + credentials.setCert(SslUtil.getCertificateString(clientX509CertNotTrusted));
  171 + Device device = createDevice(credentials);
  172 +
  173 + SingleEntityFilter sef = new SingleEntityFilter();
  174 + sef.setSingleEntity(device.getId());
  175 + LatestValueCmd latestCmd = new LatestValueCmd();
  176 + latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "batteryLevel")));
  177 + EntityDataQuery edq = new EntityDataQuery(sef, new EntityDataPageLink(1, 0, null, null),
  178 + Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
  179 +
  180 + EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null);
  181 + TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper();
  182 + wrapper.setEntityDataCmds(Collections.singletonList(cmd));
  183 +
  184 + wsClient.send(mapper.writeValueAsString(wrapper));
  185 + wsClient.waitForReply();
  186 +
  187 + wsClient.registerWaitForUpdate();
  188 + LwM2MTestClient client = new LwM2MTestClient(executor, endpoint);
  189 +
  190 + Security security = x509(serverUri, 123, clientX509CertNotTrusted.getEncoded(), clientPrivateKeyFromCert.getEncoded(), serverX509Cert.getEncoded());
  191 +
  192 + client.init(security, coapConfig);
  193 + String msg = wsClient.waitForUpdate();
  194 +
  195 + EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);
  196 + Assert.assertEquals(1, update.getCmdId());
  197 + List<EntityData> eData = update.getUpdate();
  198 + Assert.assertNotNull(eData);
  199 + Assert.assertEquals(1, eData.size());
  200 + Assert.assertEquals(device.getId(), eData.get(0).getEntityId());
  201 + Assert.assertNotNull(eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES));
  202 + var tsValue = eData.get(0).getLatest().get(EntityKeyType.TIME_SERIES).get("batteryLevel");
  203 + Assert.assertEquals(42, Long.parseLong(tsValue.getValue()));
  204 + client.destroy();
  205 + }
  206 +
  207 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.client;
  17 +
  18 +import lombok.Data;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.eclipse.californium.core.network.config.NetworkConfig;
  21 +import org.eclipse.californium.elements.Connector;
  22 +import org.eclipse.californium.scandium.DTLSConnector;
  23 +import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
  24 +import org.eclipse.californium.scandium.dtls.ClientHandshaker;
  25 +import org.eclipse.californium.scandium.dtls.DTLSSession;
  26 +import org.eclipse.californium.scandium.dtls.HandshakeException;
  27 +import org.eclipse.californium.scandium.dtls.Handshaker;
  28 +import org.eclipse.californium.scandium.dtls.ResumingClientHandshaker;
  29 +import org.eclipse.californium.scandium.dtls.ResumingServerHandshaker;
  30 +import org.eclipse.californium.scandium.dtls.ServerHandshaker;
  31 +import org.eclipse.californium.scandium.dtls.SessionAdapter;
  32 +import org.eclipse.leshan.client.californium.LeshanClient;
  33 +import org.eclipse.leshan.client.californium.LeshanClientBuilder;
  34 +import org.eclipse.leshan.client.engine.DefaultRegistrationEngineFactory;
  35 +import org.eclipse.leshan.client.object.Security;
  36 +import org.eclipse.leshan.client.object.Server;
  37 +import org.eclipse.leshan.client.observer.LwM2mClientObserver;
  38 +import org.eclipse.leshan.client.resource.ObjectsInitializer;
  39 +import org.eclipse.leshan.client.servers.ServerIdentity;
  40 +import org.eclipse.leshan.core.ResponseCode;
  41 +import org.eclipse.leshan.core.californium.DefaultEndpointFactory;
  42 +import org.eclipse.leshan.core.model.LwM2mModel;
  43 +import org.eclipse.leshan.core.model.ObjectLoader;
  44 +import org.eclipse.leshan.core.model.ObjectModel;
  45 +import org.eclipse.leshan.core.model.StaticModel;
  46 +import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
  47 +import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder;
  48 +import org.eclipse.leshan.core.request.BindingMode;
  49 +import org.eclipse.leshan.core.request.BootstrapRequest;
  50 +import org.eclipse.leshan.core.request.DeregisterRequest;
  51 +import org.eclipse.leshan.core.request.RegisterRequest;
  52 +import org.eclipse.leshan.core.request.UpdateRequest;
  53 +
  54 +import java.util.ArrayList;
  55 +import java.util.List;
  56 +import java.util.concurrent.ScheduledExecutorService;
  57 +
  58 +import static org.eclipse.leshan.core.LwM2mId.DEVICE;
  59 +import static org.eclipse.leshan.core.LwM2mId.SECURITY;
  60 +import static org.eclipse.leshan.core.LwM2mId.SERVER;
  61 +
  62 +@Slf4j
  63 +@Data
  64 +public class LwM2MTestClient {
  65 +
  66 + private final ScheduledExecutorService executor;
  67 + private final String endpoint;
  68 + private LeshanClient client;
  69 +
  70 + public void init(Security security, NetworkConfig coapConfig) {
  71 + String[] resources = new String[]{"0.xml", "1.xml", "2.xml", "3.xml"};
  72 + List<ObjectModel> models = new ArrayList<>();
  73 + for (String resourceName : resources) {
  74 + models.addAll(ObjectLoader.loadDdfFile(LwM2MTestClient.class.getClassLoader().getResourceAsStream("lwm2m/" + resourceName), resourceName));
  75 + }
  76 + LwM2mModel model = new StaticModel(models);
  77 + ObjectsInitializer initializer = new ObjectsInitializer(model);
  78 + initializer.setInstancesForObject(SECURITY, security);
  79 + initializer.setInstancesForObject(SERVER, new Server(123, 300, BindingMode.U, false));
  80 + initializer.setInstancesForObject(DEVICE, new SimpleLwM2MDevice());
  81 +
  82 + DtlsConnectorConfig.Builder dtlsConfig = new DtlsConnectorConfig.Builder();
  83 + dtlsConfig.setRecommendedCipherSuitesOnly(true);
  84 +
  85 + DefaultRegistrationEngineFactory engineFactory = new DefaultRegistrationEngineFactory();
  86 + engineFactory.setReconnectOnUpdate(false);
  87 + engineFactory.setResumeOnConnect(true);
  88 +
  89 + DefaultEndpointFactory endpointFactory = new DefaultEndpointFactory(endpoint) {
  90 + @Override
  91 + protected Connector createSecuredConnector(DtlsConnectorConfig dtlsConfig) {
  92 +
  93 + return new DTLSConnector(dtlsConfig) {
  94 + @Override
  95 + protected void onInitializeHandshaker(Handshaker handshaker) {
  96 + handshaker.addSessionListener(new SessionAdapter() {
  97 +
  98 + @Override
  99 + public void handshakeStarted(Handshaker handshaker) throws HandshakeException {
  100 + if (handshaker instanceof ServerHandshaker) {
  101 + log.info("DTLS Full Handshake initiated by server : STARTED ...");
  102 + } else if (handshaker instanceof ResumingServerHandshaker) {
  103 + log.info("DTLS abbreviated Handshake initiated by server : STARTED ...");
  104 + } else if (handshaker instanceof ClientHandshaker) {
  105 + log.info("DTLS Full Handshake initiated by client : STARTED ...");
  106 + } else if (handshaker instanceof ResumingClientHandshaker) {
  107 + log.info("DTLS abbreviated Handshake initiated by client : STARTED ...");
  108 + }
  109 + }
  110 +
  111 + @Override
  112 + public void sessionEstablished(Handshaker handshaker, DTLSSession establishedSession)
  113 + throws HandshakeException {
  114 + if (handshaker instanceof ServerHandshaker) {
  115 + log.info("DTLS Full Handshake initiated by server : SUCCEED, handshaker {}", handshaker);
  116 + } else if (handshaker instanceof ResumingServerHandshaker) {
  117 + log.info("DTLS abbreviated Handshake initiated by server : SUCCEED, handshaker {}", handshaker);
  118 + } else if (handshaker instanceof ClientHandshaker) {
  119 + log.info("DTLS Full Handshake initiated by client : SUCCEED, handshaker {}", handshaker);
  120 + } else if (handshaker instanceof ResumingClientHandshaker) {
  121 + log.info("DTLS abbreviated Handshake initiated by client : SUCCEED, handshaker {}", handshaker);
  122 + }
  123 + }
  124 +
  125 + @Override
  126 + public void handshakeFailed(Handshaker handshaker, Throwable error) {
  127 + /** get cause */
  128 + String cause;
  129 + if (error != null) {
  130 + if (error.getMessage() != null) {
  131 + cause = error.getMessage();
  132 + } else {
  133 + cause = error.getClass().getName();
  134 + }
  135 + } else {
  136 + cause = "unknown cause";
  137 + }
  138 +
  139 + if (handshaker instanceof ServerHandshaker) {
  140 + log.info("DTLS Full Handshake initiated by server : FAILED [{}]", cause);
  141 + } else if (handshaker instanceof ResumingServerHandshaker) {
  142 + log.info("DTLS abbreviated Handshake initiated by server : FAILED [{}]", cause);
  143 + } else if (handshaker instanceof ClientHandshaker) {
  144 + log.info("DTLS Full Handshake initiated by client : FAILED [{}]", cause);
  145 + } else if (handshaker instanceof ResumingClientHandshaker) {
  146 + log.info("DTLS abbreviated Handshake initiated by client : FAILED [{}]", cause);
  147 + }
  148 + }
  149 + });
  150 + }
  151 + };
  152 + }
  153 + };
  154 +
  155 + LeshanClientBuilder builder = new LeshanClientBuilder(endpoint);
  156 + builder.setLocalAddress("0.0.0.0", 11000);
  157 + builder.setObjects(initializer.createAll());
  158 + builder.setCoapConfig(coapConfig);
  159 + builder.setDtlsConfig(dtlsConfig);
  160 + builder.setRegistrationEngineFactory(engineFactory);
  161 + builder.setEndpointFactory(endpointFactory);
  162 + builder.setSharedExecutor(executor);
  163 + builder.setDecoder(new DefaultLwM2mNodeDecoder(true));
  164 + builder.setEncoder(new DefaultLwM2mNodeEncoder(true));
  165 + client = builder.build();
  166 +
  167 + LwM2mClientObserver observer = new LwM2mClientObserver() {
  168 + @Override
  169 + public void onBootstrapStarted(ServerIdentity bsserver, BootstrapRequest request) {
  170 + log.info("ClientObserver -> onBootstrapStarted...");
  171 + }
  172 +
  173 + @Override
  174 + public void onBootstrapSuccess(ServerIdentity bsserver, BootstrapRequest request) {
  175 + log.info("ClientObserver -> onBootstrapSuccess...");
  176 + }
  177 +
  178 + @Override
  179 + public void onBootstrapFailure(ServerIdentity bsserver, BootstrapRequest request, ResponseCode responseCode, String errorMessage, Exception cause) {
  180 + log.info("ClientObserver -> onBootstrapFailure...");
  181 + }
  182 +
  183 + @Override
  184 + public void onBootstrapTimeout(ServerIdentity bsserver, BootstrapRequest request) {
  185 + log.info("ClientObserver -> onBootstrapTimeout...");
  186 + }
  187 +
  188 + @Override
  189 + public void onRegistrationStarted(ServerIdentity server, RegisterRequest request) {
  190 +// log.info("ClientObserver -> onRegistrationStarted... EndpointName [{}]", request.getEndpointName());
  191 + }
  192 +
  193 + @Override
  194 + public void onRegistrationSuccess(ServerIdentity server, RegisterRequest request, String registrationID) {
  195 + log.info("ClientObserver -> onRegistrationSuccess... EndpointName [{}] [{}]", request.getEndpointName(), registrationID);
  196 + }
  197 +
  198 + @Override
  199 + public void onRegistrationFailure(ServerIdentity server, RegisterRequest request, ResponseCode responseCode, String errorMessage, Exception cause) {
  200 + log.info("ClientObserver -> onRegistrationFailure... ServerIdentity [{}]", server);
  201 + }
  202 +
  203 + @Override
  204 + public void onRegistrationTimeout(ServerIdentity server, RegisterRequest request) {
  205 + log.info("ClientObserver -> onRegistrationTimeout... RegisterRequest [{}]", request);
  206 + }
  207 +
  208 + @Override
  209 + public void onUpdateStarted(ServerIdentity server, UpdateRequest request) {
  210 +// log.info("ClientObserver -> onUpdateStarted... UpdateRequest [{}]", request);
  211 + }
  212 +
  213 + @Override
  214 + public void onUpdateSuccess(ServerIdentity server, UpdateRequest request) {
  215 +// log.info("ClientObserver -> onUpdateSuccess... UpdateRequest [{}]", request);
  216 + }
  217 +
  218 + @Override
  219 + public void onUpdateFailure(ServerIdentity server, UpdateRequest request, ResponseCode responseCode, String errorMessage, Exception cause) {
  220 +
  221 + }
  222 +
  223 + @Override
  224 + public void onUpdateTimeout(ServerIdentity server, UpdateRequest request) {
  225 +
  226 + }
  227 +
  228 + @Override
  229 + public void onDeregistrationStarted(ServerIdentity server, DeregisterRequest request) {
  230 + log.info("ClientObserver ->onDeregistrationStarted... DeregisterRequest [{}]", request.getRegistrationId());
  231 +
  232 + }
  233 +
  234 + @Override
  235 + public void onDeregistrationSuccess(ServerIdentity server, DeregisterRequest request) {
  236 + log.info("ClientObserver ->onDeregistrationSuccess... DeregisterRequest [{}]", request.getRegistrationId());
  237 +
  238 + }
  239 +
  240 + @Override
  241 + public void onDeregistrationFailure(ServerIdentity server, DeregisterRequest request, ResponseCode responseCode, String errorMessage, Exception cause) {
  242 + log.info("ClientObserver ->onDeregistrationFailure... DeregisterRequest [{}] [{}]", request.getRegistrationId(), request.getRegistrationId());
  243 + }
  244 +
  245 + @Override
  246 + public void onDeregistrationTimeout(ServerIdentity server, DeregisterRequest request) {
  247 + log.info("ClientObserver ->onDeregistrationTimeout... DeregisterRequest [{}] [{}]", request.getRegistrationId(), request.getRegistrationId());
  248 + }
  249 + };
  250 + this.client.addObserver(observer);
  251 +
  252 + client.start();
  253 + }
  254 +
  255 + public void destroy() {
  256 + client.destroy(true);
  257 + }
  258 +
  259 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.client;
  17 +
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.eclipse.leshan.client.resource.BaseInstanceEnabler;
  20 +import org.eclipse.leshan.client.servers.ServerIdentity;
  21 +import org.eclipse.leshan.core.model.ObjectModel;
  22 +import org.eclipse.leshan.core.model.ResourceModel;
  23 +import org.eclipse.leshan.core.node.LwM2mResource;
  24 +import org.eclipse.leshan.core.response.ExecuteResponse;
  25 +import org.eclipse.leshan.core.response.ReadResponse;
  26 +import org.eclipse.leshan.core.response.WriteResponse;
  27 +
  28 +import javax.security.auth.Destroyable;
  29 +import java.text.SimpleDateFormat;
  30 +import java.util.Arrays;
  31 +import java.util.Calendar;
  32 +import java.util.HashMap;
  33 +import java.util.List;
  34 +import java.util.Map;
  35 +import java.util.Random;
  36 +import java.util.TimeZone;
  37 +
  38 +@Slf4j
  39 +public class SimpleLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
  40 +
  41 +
  42 + private static final Random RANDOM = new Random();
  43 + private static final List<Integer> supportedResources = Arrays.asList(0, 1, 2, 3
  44 +// , 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21
  45 + );
  46 +
  47 + @Override
  48 + public ReadResponse read(ServerIdentity identity, int resourceid) {
  49 + if (!identity.isSystem())
  50 + log.info("Read on Device resource /{}/{}/{}", getModel().id, getId(), resourceid);
  51 + switch (resourceid) {
  52 + case 0:
  53 + return ReadResponse.success(resourceid, getManufacturer());
  54 + case 1:
  55 + return ReadResponse.success(resourceid, getModelNumber());
  56 + case 2:
  57 + return ReadResponse.success(resourceid, getSerialNumber());
  58 + case 3:
  59 + return ReadResponse.success(resourceid, getFirmwareVersion());
  60 + case 9:
  61 + return ReadResponse.success(resourceid, getBatteryLevel());
  62 + case 10:
  63 + return ReadResponse.success(resourceid, getMemoryFree());
  64 + case 11:
  65 + Map<Integer, Long> errorCodes = new HashMap<>();
  66 + errorCodes.put(0, getErrorCode());
  67 + return ReadResponse.success(resourceid, errorCodes, ResourceModel.Type.INTEGER);
  68 + case 14:
  69 + return ReadResponse.success(resourceid, getUtcOffset());
  70 + case 15:
  71 + return ReadResponse.success(resourceid, getTimezone());
  72 + case 16:
  73 + return ReadResponse.success(resourceid, getSupportedBinding());
  74 + case 17:
  75 + return ReadResponse.success(resourceid, getDeviceType());
  76 + case 18:
  77 + return ReadResponse.success(resourceid, getHardwareVersion());
  78 + case 19:
  79 + return ReadResponse.success(resourceid, getSoftwareVersion());
  80 + case 20:
  81 + return ReadResponse.success(resourceid, getBatteryStatus());
  82 + case 21:
  83 + return ReadResponse.success(resourceid, getMemoryTotal());
  84 + default:
  85 + return super.read(identity, resourceid);
  86 + }
  87 + }
  88 +
  89 + @Override
  90 + public ExecuteResponse execute(ServerIdentity identity, int resourceid, String params) {
  91 + String withParams = null;
  92 + if (params != null && params.length() != 0) {
  93 + withParams = " with params " + params;
  94 + }
  95 + log.info("Execute on Device resource /{}/{}/{} {}", getModel().id, getId(), resourceid, withParams != null ? withParams : "");
  96 + return ExecuteResponse.success();
  97 + }
  98 +
  99 + @Override
  100 + public WriteResponse write(ServerIdentity identity, int resourceid, LwM2mResource value) {
  101 + log.info("Write on Device resource /{}/{}/{}", getModel().id, getId(), resourceid);
  102 +
  103 + switch (resourceid) {
  104 + case 13:
  105 + return WriteResponse.notFound();
  106 + case 14:
  107 + setUtcOffset((String) value.getValue());
  108 + fireResourcesChange(resourceid);
  109 + return WriteResponse.success();
  110 + case 15:
  111 + setTimezone((String) value.getValue());
  112 + fireResourcesChange(resourceid);
  113 + return WriteResponse.success();
  114 + default:
  115 + return super.write(identity, resourceid, value);
  116 + }
  117 + }
  118 +
  119 + private String getManufacturer() {
  120 + return "Leshan Demo Device";
  121 + }
  122 +
  123 + private String getModelNumber() {
  124 + return "Model 500";
  125 + }
  126 +
  127 + private String getSerialNumber() {
  128 + return "LT-500-000-0001";
  129 + }
  130 +
  131 + private String getFirmwareVersion() {
  132 + return "1.0.0";
  133 + }
  134 +
  135 + private long getErrorCode() {
  136 + return 0;
  137 + }
  138 +
  139 + private int getBatteryLevel() {
  140 + return 42;
  141 + }
  142 +
  143 + private long getMemoryFree() {
  144 + return Runtime.getRuntime().freeMemory() / 1024;
  145 + }
  146 +
  147 + private String utcOffset = new SimpleDateFormat("X").format(Calendar.getInstance().getTime());
  148 +
  149 + private String getUtcOffset() {
  150 + return utcOffset;
  151 + }
  152 +
  153 + private void setUtcOffset(String t) {
  154 + utcOffset = t;
  155 + }
  156 +
  157 + private String timeZone = TimeZone.getDefault().getID();
  158 +
  159 + private String getTimezone() {
  160 + return timeZone;
  161 + }
  162 +
  163 + private void setTimezone(String t) {
  164 + timeZone = t;
  165 + }
  166 +
  167 + private String getSupportedBinding() {
  168 + return "U";
  169 + }
  170 +
  171 + private String getDeviceType() {
  172 + return "Demo";
  173 + }
  174 +
  175 + private String getHardwareVersion() {
  176 + return "1.0.1";
  177 + }
  178 +
  179 + private String getSoftwareVersion() {
  180 + return "1.0.2";
  181 + }
  182 +
  183 + private int getBatteryStatus() {
  184 + return RANDOM.nextInt(7);
  185 + }
  186 +
  187 + private long getMemoryTotal() {
  188 + return Runtime.getRuntime().totalMemory() / 1024;
  189 + }
  190 +
  191 + @Override
  192 + public List<Integer> getAvailableResourceIds(ObjectModel model) {
  193 + return supportedResources;
  194 + }
  195 +
  196 + @Override
  197 + public void destroy() {
  198 + }
  199 +}
... ...
  1 +transport.lwm2m.security.key_store=lwm2m/credentials/serverKeyStore.jks
  2 +transport.lwm2m.security.key_store_password=server
  3 +edges.enabled=true
\ No newline at end of file
... ...
... ... @@ -9,13 +9,15 @@
9 9
10 10 <!-- <logger name="org.thingsboard.server.service.subscription" level="TRACE"/>-->
11 11 <logger name="org.thingsboard.server.controller.TbTestWebSocketClient" level="INFO"/>
12   - <logger name="org.thingsboard.server" level="WARN"/>
  12 + <logger name="org.thingsboard.server" level="DEBUG"/>
13 13 <logger name="org.springframework" level="WARN"/>
14 14 <logger name="org.springframework.boot.test" level="WARN"/>
15 15 <logger name="org.apache.cassandra" level="WARN"/>
16 16 <logger name="org.cassandraunit" level="INFO"/>
  17 + <logger name="org.eclipse.leshan" level="TRACE"/>
17 18
18   - <root level="WARN">
  19 +
  20 + <root level="INFO">
19 21 <appender-ref ref="console"/>
20 22 </root>
21 23
... ...
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +
  3 +<!--
  4 +FILE INFORMATION
  5 +
  6 +OMA Permanent Document
  7 + File: OMA-SUP-XML_0-V1_2-20201110-A.xml
  8 + Path: http://www.openmobilealliance.org/release/ObjLwM2M_Security/
  9 +
  10 +OMNA LwM2M Registry
  11 + Path: https://github.com/OpenMobileAlliance/lwm2m-registry
  12 + Name: 0.xml
  13 +
  14 +NORMATIVE INFORMATION
  15 +
  16 + Information about this file can be found in the latest revision of
  17 +
  18 + OMA-TS-LightweightM2M_Core-V1_2
  19 +
  20 + This is available at http://www.openmobilealliance.org/release/LightweightM2M/
  21 +
  22 + Send comments to https://github.com/OpenMobileAlliance/OMA_LwM2M_for_Developers/issues
  23 +
  24 +LEGAL DISCLAIMER
  25 +
  26 + Copyright 2020 Open Mobile Alliance.
  27 +
  28 + Redistribution and use in source and binary forms, with or without
  29 + modification, are permitted provided that the following conditions
  30 + are met:
  31 +
  32 + 1. Redistributions of source code must retain the above copyright
  33 + notice, this list of conditions and the following disclaimer.
  34 + 2. Redistributions in binary form must reproduce the above copyright
  35 + notice, this list of conditions and the following disclaimer in the
  36 + documentation and/or other materials provided with the distribution.
  37 + 3. Neither the name of the copyright holder nor the names of its
  38 + contributors may be used to endorse or promote products derived
  39 + from this software without specific prior written permission.
  40 +
  41 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  42 + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  43 + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  44 + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  45 + COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  46 + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  47 + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  48 + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  49 + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  50 + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  51 + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  52 + POSSIBILITY OF SUCH DAMAGE.
  53 +
  54 + The above license is used as a license under copyright only. Please
  55 + reference the OMA IPR Policy for patent licensing terms:
  56 + https://www.omaspecworks.org/about/intellectual-property-rights/
  57 +
  58 +-->
  59 +
  60 +<LWM2M xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.openmobilealliance.org/tech/profiles/LWM2M-v1_1.xsd">
  61 + <Object ObjectType="MODefinition">
  62 + <Name>LWM2M Security</Name>
  63 + <Description1><![CDATA[This LwM2M Object provides the keying material of a LwM2M Client appropriate to access a specified LwM2M Server. One Object Instance SHOULD address a LwM2M Bootstrap-Server.
  64 +These LwM2M Object Resources MUST only be changed by a LwM2M Bootstrap-Server or Bootstrap from Smartcard and MUST NOT be accessible by any other LwM2M Server.]]></Description1>
  65 + <ObjectID>0</ObjectID>
  66 + <ObjectURN>urn:oma:lwm2m:oma:0:1.2</ObjectURN>
  67 + <LWM2MVersion>1.1</LWM2MVersion>
  68 + <ObjectVersion>1.2</ObjectVersion>
  69 + <MultipleInstances>Multiple</MultipleInstances>
  70 + <Mandatory>Mandatory</Mandatory>
  71 + <Resources>
  72 + <Item ID="0">
  73 + <Name>LWM2M Server URI</Name>
  74 + <Operations></Operations>
  75 + <MultipleInstances>Single</MultipleInstances>
  76 + <Mandatory>Mandatory</Mandatory>
  77 + <Type>String</Type>
  78 + <RangeEnumeration>0..255</RangeEnumeration>
  79 + <Units></Units>
  80 + <Description><![CDATA[Uniquely identifies the LwM2M Server or LwM2M Bootstrap-Server. The format of the CoAP URI is defined in Section 6 of RFC 7252.]]></Description>
  81 + </Item>
  82 + <Item ID="1">
  83 + <Name>Bootstrap-Server</Name>
  84 + <Operations></Operations>
  85 + <MultipleInstances>Single</MultipleInstances>
  86 + <Mandatory>Mandatory</Mandatory>
  87 + <Type>Boolean</Type>
  88 + <RangeEnumeration></RangeEnumeration>
  89 + <Units></Units>
  90 + <Description><![CDATA[Determines if the current instance concerns a LwM2M Bootstrap-Server (true) or a standard LwM2M Server (false)]]></Description>
  91 + </Item>
  92 + <Item ID="2">
  93 + <Name>Security Mode</Name>
  94 + <Operations></Operations>
  95 + <MultipleInstances>Single</MultipleInstances>
  96 + <Mandatory>Mandatory</Mandatory>
  97 + <Type>Integer</Type>
  98 + <RangeEnumeration>0..4</RangeEnumeration>
  99 + <Units></Units>
  100 + <Description><![CDATA[Determines which security mode is used
  101 +0: Pre-Shared Key mode
  102 +1: Raw Public Key mode
  103 +2: Certificate mode
  104 +3: NoSec mode
  105 +4: Certificate mode with EST]]></Description>
  106 + </Item>
  107 + <Item ID="3">
  108 + <Name>Public Key or Identity</Name>
  109 + <Operations></Operations>
  110 + <MultipleInstances>Single</MultipleInstances>
  111 + <Mandatory>Mandatory</Mandatory>
  112 + <Type>Opaque</Type>
  113 + <RangeEnumeration></RangeEnumeration>
  114 + <Units></Units>
  115 + <Description><![CDATA[Stores the LwM2M Client's certificate, public key (RPK mode) or PSK Identity (PSK mode).]]></Description>
  116 + </Item>
  117 + <Item ID="4">
  118 + <Name>Server Public Key</Name>
  119 + <Operations></Operations>
  120 + <MultipleInstances>Single</MultipleInstances>
  121 + <Mandatory>Mandatory</Mandatory>
  122 + <Type>Opaque</Type>
  123 + <RangeEnumeration></RangeEnumeration>
  124 + <Units></Units>
  125 + <Description><![CDATA[Stores the LwM2M Server's, respectively LwM2M Bootstrap-Server's, certificate, public key (RPK mode) or trust anchor. The Certificate Mode Resource determines the content of this resource.]]></Description>
  126 + </Item>
  127 + <Item ID="5">
  128 + <Name>Secret Key</Name>
  129 + <Operations></Operations>
  130 + <MultipleInstances>Single</MultipleInstances>
  131 + <Mandatory>Mandatory</Mandatory>
  132 + <Type>Opaque</Type>
  133 + <RangeEnumeration></RangeEnumeration>
  134 + <Units></Units>
  135 + <Description><![CDATA[Stores the secret key (PSK mode) or private key (RPK or certificate mode).]]></Description>
  136 + </Item>
  137 + <Item ID="6">
  138 + <Name>SMS Security Mode</Name>
  139 + <Operations></Operations>
  140 + <MultipleInstances>Single</MultipleInstances>
  141 + <Mandatory>Optional</Mandatory>
  142 + <Type>Integer</Type>
  143 + <RangeEnumeration>0..255</RangeEnumeration>
  144 + <Units></Units>
  145 + <Description><![CDATA[Determines which SMS security mode is used:
  146 +0: Reserved for future use
  147 +1: DTLS mode (Device terminated) PSK mode assumed
  148 +2: Secure Packet Structure mode (Smartcard terminated)
  149 +3: NoSec mode
  150 +4: Reserved mode (DTLS mode with multiplexing Security Association support)
  151 +5-203 : Reserved for future use
  152 +204-255: Proprietary modes]]></Description>
  153 + </Item>
  154 + <Item ID="7">
  155 + <Name>SMS Binding Key Parameters</Name>
  156 + <Operations></Operations>
  157 + <MultipleInstances>Single</MultipleInstances>
  158 + <Mandatory>Optional</Mandatory>
  159 + <Type>Opaque</Type>
  160 + <RangeEnumeration>6</RangeEnumeration>
  161 + <Units></Units>
  162 + <Description><![CDATA[Stores the KIc, KID, SPI and TAR.]]></Description>
  163 + </Item>
  164 + <Item ID="8">
  165 + <Name>SMS Binding Secret Key(s)</Name>
  166 + <Operations></Operations>
  167 + <MultipleInstances>Single</MultipleInstances>
  168 + <Mandatory>Optional</Mandatory>
  169 + <Type>Opaque</Type>
  170 + <RangeEnumeration>16,32,48</RangeEnumeration>
  171 + <Units></Units>
  172 + <Description><![CDATA[Stores the values of the key(s) for the SMS binding.]]></Description>
  173 + </Item>
  174 + <Item ID="9">
  175 + <Name>LwM2M Server SMS Number</Name>
  176 + <Operations></Operations>
  177 + <MultipleInstances>Single</MultipleInstances>
  178 + <Mandatory>Optional</Mandatory>
  179 + <Type>String</Type>
  180 + <RangeEnumeration></RangeEnumeration>
  181 + <Units></Units>
  182 + <Description><![CDATA[MSISDN used by the LwM2M Client to send messages to the LwM2M Server via the SMS binding.]]></Description>
  183 + </Item>
  184 + <Item ID="10">
  185 + <Name>Short Server ID</Name>
  186 + <Operations></Operations>
  187 + <MultipleInstances>Single</MultipleInstances>
  188 + <Mandatory>Optional</Mandatory>
  189 + <Type>Integer</Type>
  190 + <RangeEnumeration>1..65534</RangeEnumeration>
  191 + <Units></Units>
  192 + <Description><![CDATA[This identifier uniquely identifies each LwM2M Server configured for the LwM2M Client.
  193 +This Resource MUST be set when the Bootstrap-Server Resource has a value of 'false'.
  194 +The values ID:0 and ID:65535 values MUST NOT be used for identifying the LwM2M Server.]]></Description>
  195 + </Item>
  196 + <Item ID="11">
  197 + <Name>Client Hold Off Time</Name>
  198 + <Operations></Operations>
  199 + <MultipleInstances>Single</MultipleInstances>
  200 + <Mandatory>Optional</Mandatory>
  201 + <Type>Integer</Type>
  202 + <RangeEnumeration></RangeEnumeration>
  203 + <Units>s</Units>
  204 + <Description><![CDATA[The number of seconds to wait before initiating a Client Initiated Bootstrap once the LwM2M Client has determined it should initiate this bootstrap mode.
  205 +In case client initiated bootstrap is supported by the LwM2M Client, this resource MUST be supported. This information is relevant for use with a Bootstrap-Server only.]]></Description>
  206 + </Item>
  207 + <Item ID="12">
  208 + <Name>Bootstrap-Server Account Timeout</Name>
  209 + <Operations></Operations>
  210 + <MultipleInstances>Single</MultipleInstances>
  211 + <Mandatory>Optional</Mandatory>
  212 + <Type>Integer</Type>
  213 + <RangeEnumeration></RangeEnumeration>
  214 + <Units>s</Units>
  215 + <Description><![CDATA[The LwM2M Client MUST purge the LwM2M Bootstrap-Server Account after the timeout value given by this resource. The lowest timeout value is 1.
  216 +If the value is set to 0, or if this resource is not instantiated, the Bootstrap-Server Account lifetime is infinite.]]></Description>
  217 + </Item>
  218 + <Item ID="13">
  219 + <Name>Matching Type</Name>
  220 + <Operations></Operations>
  221 + <MultipleInstances>Single</MultipleInstances>
  222 + <Mandatory>Optional</Mandatory>
  223 + <Type>Integer</Type>
  224 + <RangeEnumeration>0..3</RangeEnumeration>
  225 + <Units></Units>
  226 + <Description><![CDATA[The Matching Type Resource specifies how the certificate or raw public key in in the Server Public Key is presented. Four values are currently defined:
  227 + 0: Exact match. This is the default value and also corresponds to the functionality of LwM2M v1.0. Hence, if this resource is not present then the content of the Server Public Key Resource corresponds to this value.
  228 + 1: SHA-256 hash [RFC6234]
  229 + 2: SHA-384 hash [RFC6234]
  230 + 3: SHA-512 hash [RFC6234]]]></Description>
  231 + </Item>
  232 + <Item ID="14">
  233 + <Name>SNI</Name>
  234 + <Operations></Operations>
  235 + <MultipleInstances>Single</MultipleInstances>
  236 + <Mandatory>Optional</Mandatory>
  237 + <Type>String</Type>
  238 + <RangeEnumeration></RangeEnumeration>
  239 + <Units></Units>
  240 + <Description><![CDATA[This resource holds the value of the Server Name Indication (SNI) value to be used during the TLS handshake. When this resource is present then the LwM2M Server URI acts as the address of the service while the SNI value is used for matching a presented certificate, or PSK identity.]]></Description>
  241 + </Item>
  242 + <Item ID="15">
  243 + <Name>Certificate Usage</Name>
  244 + <Operations></Operations>
  245 + <MultipleInstances>Single</MultipleInstances>
  246 + <Mandatory>Optional</Mandatory>
  247 + <Type>Integer</Type>
  248 + <RangeEnumeration>0..3</RangeEnumeration>
  249 + <Units></Units>
  250 + <Description><![CDATA[The Certificate Usage Resource specifies the semantic of the certificate or
  251 + raw public key stored in the Server Public Key Resource, which is used to match
  252 + the certificate presented in the TLS/DTLS handshake. The currently defined values are
  253 + 0 for "CA constraint", 1 for "service certificate constraint", 2 for "trust anchor
  254 + assertion", and 3 for "domain-issued certificate". When this resource is absent,
  255 + value (3) for domain issued certificate mode is assumed. More details about the
  256 + semantic of each value can be found in the security consideration section of the
  257 + LwM2M specification.]]></Description>
  258 + </Item>
  259 + <Item ID="16">
  260 + <Name>DTLS/TLS Ciphersuite</Name>
  261 + <Operations></Operations>
  262 + <MultipleInstances>Multiple</MultipleInstances>
  263 + <Mandatory>Optional</Mandatory>
  264 + <Type>Integer</Type>
  265 + <RangeEnumeration></RangeEnumeration>
  266 + <Units></Units>
  267 + <Description><![CDATA[When this resource is present it instructs the TLS/DTLS client to propose the indicated ciphersuite(s) in the ClientHello of the handshake. A ciphersuite is indicated as a 32-bit integer value. The IANA TLS ciphersuite registry is maintained at https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml. As an example, the TLS_PSK_WITH_AES_128_CCM_8 ciphersuite is represented with the following string "0xC0,0xA8". To form an integer value the two values are concatenated. In this example, the value is 0xc0a8 or 49320.]]></Description>
  268 + </Item>
  269 + <Item ID="17"><Name>OSCORE Security Mode</Name>
  270 + <Operations></Operations>
  271 + <MultipleInstances>Single</MultipleInstances>
  272 + <Mandatory>Optional</Mandatory>
  273 + <Type>Objlnk</Type>
  274 + <RangeEnumeration></RangeEnumeration>
  275 + <Units></Units>
  276 + <Description><![CDATA[If this resource is defined, it provides a link to the OSCORE Object Instance and OSCORE MUST be used by the LwM2M Client with the linked OSCORE Object Instance.]]></Description>
  277 + </Item>
  278 + <Item ID="18">
  279 + <Name>Groups To Use by Client</Name>
  280 + <Operations></Operations>
  281 + <MultipleInstances>Multiple</MultipleInstances>
  282 + <Mandatory>Optional</Mandatory>
  283 + <Type>Integer</Type>
  284 + <RangeEnumeration>0..65535</RangeEnumeration>
  285 + <Units></Units>
  286 + <Description><![CDATA[If this resource is defined, it indicates what groups the LwM2M Client should use with a LwM2M Server/LwM2M Bootstrap-Server (ordered from most preferred to least preferred). Resource instance 0 indicates the most preferred group. The values are taken from Section 4.2.7 of RFC 8446. An example is secp256r1 (0x0017).]]></Description>
  287 + </Item>
  288 + <Item ID="19">
  289 + <Name>Signature Algorithms Supported by Server</Name>
  290 + <Operations></Operations>
  291 + <MultipleInstances>Multiple</MultipleInstances>
  292 + <Mandatory>Optional</Mandatory>
  293 + <Type>Integer</Type>
  294 + <RangeEnumeration>0..65535</RangeEnumeration>
  295 + <Units></Units>
  296 + <Description><![CDATA[If this resource is defined, it indicates what signature algorithms the LwM2M Server/LwM2M Bootstrap-Server supports. The values are taken from Section 4.2.3 of RFC 8446. An example is ecdsa_secp256r1_sha256(0x0403).]]></Description>
  297 + </Item>
  298 + <Item ID="20"><Name>Signature Algorithms To Use by Client</Name>
  299 + <Operations></Operations>
  300 + <MultipleInstances>Multiple</MultipleInstances>
  301 + <Mandatory>Optional</Mandatory>
  302 + <Type>Integer</Type>
  303 + <RangeEnumeration>0..65535</RangeEnumeration>
  304 + <Units></Units>
  305 + <Description><![CDATA[If this resource is defined, it indicates what signature algorithms the LwM2M Client should use with a LwM2M Server/LwM2M Bootstrap-Server (ordered from most preferred to least preferred). Resource instance 0 indicates the most preferred group. The values are taken from Section 4.2.3 of RFC 8446. An example is ecdsa_secp256r1_sha256(0x0403).]]></Description>
  306 + </Item>
  307 + <Item ID="21">
  308 + <Name>Signature Algorithm Certs Supported by Server</Name>
  309 + <Operations></Operations>
  310 + <MultipleInstances>Multiple</MultipleInstances>
  311 + <Mandatory>Optional</Mandatory>
  312 + <Type>Integer</Type>
  313 + <RangeEnumeration>0..65535</RangeEnumeration>
  314 + <Units></Units>
  315 + <Description><![CDATA[If this resource is defined, it indicates what certificate-specific signature algorithms the the LwM2M Server/LwM2M Bootstrap-Server supports. The values are taken from Section 4.2.3 of RFC 8446. An example is ecdsa_secp256r1_sha256(0x0403).]]></Description>
  316 + </Item>
  317 + <Item ID="22">
  318 + <Name>TLS 1.3 Features To Use by Client</Name>
  319 + <Operations></Operations>
  320 + <MultipleInstances>Single</MultipleInstances>
  321 + <Mandatory>Optional</Mandatory>
  322 + <Type>Integer</Type>
  323 + <RangeEnumeration>0..65535</RangeEnumeration>
  324 + <Units></Units>
  325 + <Description><![CDATA[If this resource is defined, it indicates which features the LwM2M Client should use with the respective LwM2M Server/LwM2M Bootstrap-Server. The bitmask values listed below are defined. A bit value of '0' means the feature should not be used. bit(0) - PSK Plain, bit(1) - 0-RTT, bit(2) - PSK with PFS, bit(3) - Certificate-based Authentication. Bit(4) to bit(31) are reserved.]]></Description>
  326 + </Item>
  327 + <Item ID="23">
  328 + <Name>TLS Extensions Supported by Server</Name>
  329 + <Operations></Operations>
  330 + <MultipleInstances>Single</MultipleInstances>
  331 + <Mandatory>Optional</Mandatory>
  332 + <Type>Integer</Type>
  333 + <RangeEnumeration>0..65535</RangeEnumeration>
  334 + <Units></Units>
  335 + <Description><![CDATA[If this resource is defined, it indicates what extensions the LwM2M Server/LwM2M Bootstrap-Server supports in form of a bitmap. The following values are defined: bit(0) - Server Name Indication (RFC 6066), bit (1) - Max Fragment Length (RFC 6066), bit (2) - Status Request (RFC 6066), bit (3) - Heartbeat (RFC 6520), bit (4) - Application Layer Protocol Negotiation (RFC 7301), bit (5) - Signed Certificate Timestamp (RFC 6962), bit (6) - Certificate Compression (draft-ietf-tls-certificate-compression), bit (7) - Record Size Limit (RFC 8449), bit (8) - Ticket Pinning (draft-ietf-tls-pinning-ticket), bit (9) - Certificate Authorities (RFC 8446), bit (10) - OID Filters (RFC 8446), bit (11) - Post Handshake Auth (RFC 8446), bit (12) - Connection ID (draft-ietf-tls-dtls-connection-id/draft-ietf-tls-dtls13). Bit(13) to bit(31) are reserved. ]]></Description>
  336 + </Item>
  337 + <Item ID="24">
  338 + <Name>TLS Extensions To Use by Client</Name>
  339 + <Operations></Operations>
  340 + <MultipleInstances>Single</MultipleInstances>
  341 + <Mandatory>Optional</Mandatory>
  342 + <Type>Integer</Type>
  343 + <RangeEnumeration>0..65535</RangeEnumeration>
  344 + <Units></Units>
  345 + <Description><![CDATA[If this resource is defined, it indicates what extensions the LwM2M Client should use with the LwM2M Server/LwM2M Bootstrap-Server in form of a bitmap. The following values are defined: bit(0) - Server Name Indication (RFC 6066), bit (1) - Max Fragment Length (RFC 6066), bit (2) - Status Request (RFC 6066), bit (3) - Heartbeat (RFC 6520), bit (4) - Application Layer Protocol Negotiation (RFC 7301), bit (5) - Signed Certificate Timestamp (RFC 6962), bit (6) - Certificate Compression (draft-ietf-tls-certificate-compression), bit (7) - Record Size Limit (RFC 8449), bit (8) - Ticket Pinning (draft-ietf-tls-pinning-ticket), bit (9) - Certificate Authorities (RFC 8446), bit (10) - OID Filters (RFC 8446), bit (11) - Post Handshake Auth (RFC 8446), bit (12) - Connection ID (draft-ietf-tls-dtls-connection-id/draft-ietf-tls-dtls13). Bit(13) to bit(31) are reserved. ]]></Description>
  346 + </Item>
  347 + <Item ID="25">
  348 + <Name>Secondary LwM2M Server URI</Name>
  349 + <Operations></Operations>
  350 + <MultipleInstances>Multiple</MultipleInstances>
  351 + <Mandatory>Optional</Mandatory>
  352 + <Type>String</Type>
  353 + <RangeEnumeration>0..255</RangeEnumeration>
  354 + <Units></Units>
  355 + <Description><![CDATA[If this resource is present then the LwM2M Server URI in the Security Object, Resource ID 0, is augmented with information about further LwM2M Server URIs that can be used with the same security information found in the LwM2M Security Object. This is useful when a LwM2M Server is reachable via two different transport bindings (i.e. URIs). For example when the same server is reachable with two different URIs, such as a "coaps" and a "coaps+tcp" URI scheme.]]></Description>
  356 + </Item>
  357 + <Item ID="26"><Name>MQTT Server</Name>
  358 + <Operations></Operations>
  359 + <MultipleInstances>Single</MultipleInstances>
  360 + <Mandatory>Optional</Mandatory>
  361 + <Type>Objlnk</Type>
  362 + <RangeEnumeration></RangeEnumeration>
  363 + <Units></Units>
  364 + <Description><![CDATA[If this resource is defined, it provides a link to a MQTT Server Object Instance, which offers additional configuration information for use with this MQTT server. This Resource is used only when the URI scheme in the LwM2M Server URI Resource indicates the use of MQTT.]]></Description>
  365 + </Item>
  366 + <Item ID="27"><Name>LwM2M COSE Security</Name>
  367 + <Operations></Operations>
  368 + <MultipleInstances>Multiple</MultipleInstances>
  369 + <Mandatory>Optional</Mandatory>
  370 + <Type>Objlnk</Type>
  371 + <RangeEnumeration></RangeEnumeration>
  372 + <Units></Units>
  373 + <Description><![CDATA[If this resource is defined, it provides a links to LwM2M COSE Object Instances, which contain security-relevant configuration information for use with COSE.]]></Description>
  374 + </Item>
  375 + <Item ID="28"><Name>RDS Destination Port</Name>
  376 + <Operations></Operations>
  377 + <MultipleInstances>Single</MultipleInstances>
  378 + <Mandatory>Optional</Mandatory>
  379 + <Type>Integer</Type>
  380 + <RangeEnumeration>0..15</RangeEnumeration>
  381 + <Units></Units>
  382 + <Description><![CDATA[This resource provides the default RDS Destination Port Number (as defined in 3GPP TS 24.250) to use for contacting the LwM2M or Bootstrap Server when communicating through the SCEF across the Non-IP binding.]]></Description>
  383 + </Item>
  384 + <Item ID="29"><Name>RDS Source Port</Name>
  385 + <Operations></Operations>
  386 + <MultipleInstances>Single</MultipleInstances>
  387 + <Mandatory>Optional</Mandatory>
  388 + <Type>Integer</Type>
  389 + <RangeEnumeration>0..15</RangeEnumeration>
  390 + <Units></Units>
  391 + <Description><![CDATA[This resource provides the default RDS Source Port Number (as defined in 3GPP TS 24.250) to use for contacting the LwM2M or Bootstrap Server when communicating through the SCEF across the Non-IP binding.]]></Description>
  392 + </Item>
  393 + <Item ID="30"><Name>RDS Application ID</Name>
  394 + <Operations></Operations>
  395 + <MultipleInstances>Single</MultipleInstances>
  396 + <Mandatory>Optional</Mandatory>
  397 + <Type>String</Type>
  398 + <RangeEnumeration></RangeEnumeration>
  399 + <Units></Units>
  400 + <Description><![CDATA[This resource provides the Application ID (as defined in 3GPP TS 24.250) to use for querying the SCEF for the source and destination port numbers for contacting the LwM2M or Bootstrap Server when communicating through the SCEF across the Non-IP binding.]]></Description>
  401 + </Item>
  402 + </Resources>
  403 + <Description2><![CDATA[]]></Description2>
  404 + </Object>
  405 +</LWM2M>
... ...
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +
  3 +<!--
  4 +FILE INFORMATION
  5 +
  6 +OMA Permanent Document
  7 + File: OMA-SUP-XML_1-V1_2-20201110-A.xml
  8 + Path: http://www.openmobilealliance.org/release/ObjLwM2M_Server/
  9 +
  10 +OMNA LwM2M Registry
  11 + Path: https://github.com/OpenMobileAlliance/lwm2m-registry
  12 + Name: 1.xml
  13 +
  14 +NORMATIVE INFORMATION
  15 +
  16 + Information about this file can be found in the latest revision of
  17 +
  18 + OMA-TS-LightweightM2M_Core-V1_2
  19 +
  20 + This is available at http://www.openmobilealliance.org/release/LightweightM2M/
  21 +
  22 + Send comments to https://github.com/OpenMobileAlliance/OMA_LwM2M_for_Developers/issues
  23 +
  24 +LEGAL DISCLAIMER
  25 +
  26 + Copyright 2020 Open Mobile Alliance.
  27 +
  28 + Redistribution and use in source and binary forms, with or without
  29 + modification, are permitted provided that the following conditions
  30 + are met:
  31 +
  32 + 1. Redistributions of source code must retain the above copyright
  33 + notice, this list of conditions and the following disclaimer.
  34 + 2. Redistributions in binary form must reproduce the above copyright
  35 + notice, this list of conditions and the following disclaimer in the
  36 + documentation and/or other materials provided with the distribution.
  37 + 3. Neither the name of the copyright holder nor the names of its
  38 + contributors may be used to endorse or promote products derived
  39 + from this software without specific prior written permission.
  40 +
  41 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  42 + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  43 + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  44 + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  45 + COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  46 + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  47 + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  48 + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  49 + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  50 + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  51 + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  52 + POSSIBILITY OF SUCH DAMAGE.
  53 +
  54 + The above license is used as a license under copyright only. Please
  55 + reference the OMA IPR Policy for patent licensing terms:
  56 + https://www.omaspecworks.org/about/intellectual-property-rights/
  57 +
  58 +-->
  59 +
  60 +<LWM2M xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.openmobilealliance.org/tech/profiles/LWM2M-v1_1.xsd">
  61 + <Object ObjectType="MODefinition">
  62 + <Name>LwM2M Server</Name>
  63 + <Description1><![CDATA[This LwM2M Objects provides the data related to a LwM2M Server. A Bootstrap-Server has no such an Object Instance associated to it.]]></Description1>
  64 + <ObjectID>1</ObjectID>
  65 + <ObjectURN>urn:oma:lwm2m:oma:1:1.2</ObjectURN>
  66 + <LWM2MVersion>1.2</LWM2MVersion>
  67 + <ObjectVersion>1.2</ObjectVersion>
  68 + <MultipleInstances>Multiple</MultipleInstances>
  69 + <Mandatory>Mandatory</Mandatory>
  70 + <Resources>
  71 + <Item ID="0">
  72 + <Name>Short Server ID</Name>
  73 + <Operations>R</Operations>
  74 + <MultipleInstances>Single</MultipleInstances>
  75 + <Mandatory>Mandatory</Mandatory>
  76 + <Type>Integer</Type>
  77 + <RangeEnumeration>1..65534</RangeEnumeration>
  78 + <Units></Units>
  79 + <Description><![CDATA[Used as link to associate server Object Instance.]]></Description>
  80 + </Item>
  81 + <Item ID="1">
  82 + <Name>Lifetime</Name>
  83 + <Operations>RW</Operations>
  84 + <MultipleInstances>Single</MultipleInstances>
  85 + <Mandatory>Mandatory</Mandatory>
  86 + <Type>Integer</Type>
  87 + <RangeEnumeration></RangeEnumeration>
  88 + <Units>s</Units>
  89 + <Description><![CDATA[Specify the lifetime of the registration in seconds (see Client Registration Interface). If the value is set to 0, the lifetime is infinite.]]></Description>
  90 + </Item>
  91 + <Item ID="2">
  92 + <Name>Default Minimum Period</Name>
  93 + <Operations>RW</Operations>
  94 + <MultipleInstances>Single</MultipleInstances>
  95 + <Mandatory>Optional</Mandatory>
  96 + <Type>Integer</Type>
  97 + <RangeEnumeration></RangeEnumeration>
  98 + <Units>s</Units>
  99 + <Description><![CDATA[The default value the LwM2M Client should use for the Minimum Period of an Observation in the absence of this parameter being included in an Observation.
  100 +If this Resource doesn’t exist, the default value is 0.]]></Description>
  101 + </Item>
  102 + <Item ID="3">
  103 + <Name>Default Maximum Period</Name>
  104 + <Operations>RW</Operations>
  105 + <MultipleInstances>Single</MultipleInstances>
  106 + <Mandatory>Optional</Mandatory>
  107 + <Type>Integer</Type>
  108 + <RangeEnumeration></RangeEnumeration>
  109 + <Units>s</Units>
  110 + <Description><![CDATA[The default value the LwM2M Client should use for the Maximum Period of an Observation in the absence of this parameter being included in an Observation.]]></Description>
  111 + </Item>
  112 + <Item ID="4">
  113 + <Name>Disable</Name>
  114 + <Operations>E</Operations>
  115 + <MultipleInstances>Single</MultipleInstances>
  116 + <Mandatory>Optional</Mandatory>
  117 + <Type></Type>
  118 + <RangeEnumeration></RangeEnumeration>
  119 + <Units></Units>
  120 + <Description><![CDATA[If this Resource is executed, this LwM2M Server Object is disabled for a certain period defined in the Disabled Timeout Resource. After receiving "Execute" operation, LwM2M Client MUST send response of the operation and perform de-registration process, and underlying network connection between the Client and Server MUST be disconnected to disable the LwM2M Server account.
  121 +After the above process, the LwM2M Client MUST NOT send any message to the Server and ignore all the messages from the LwM2M Server for the period.]]></Description>
  122 + </Item>
  123 + <Item ID="5">
  124 + <Name>Disable Timeout</Name>
  125 + <Operations>RW</Operations>
  126 + <MultipleInstances>Single</MultipleInstances>
  127 + <Mandatory>Optional</Mandatory>
  128 + <Type>Integer</Type>
  129 + <RangeEnumeration></RangeEnumeration>
  130 + <Units>s</Units>
  131 + <Description><![CDATA[A period to disable the Server. After this period, the LwM2M Client MUST perform registration process to the Server. If this Resource is not set, a default timeout value is 86400 (1 day).]]></Description>
  132 + </Item>
  133 + <Item ID="6">
  134 + <Name>Notification Storing When Disabled or Offline</Name>
  135 + <Operations>RW</Operations>
  136 + <MultipleInstances>Single</MultipleInstances>
  137 + <Mandatory>Mandatory</Mandatory>
  138 + <Type>Boolean</Type>
  139 + <RangeEnumeration></RangeEnumeration>
  140 + <Units></Units>
  141 + <Description><![CDATA[If true, the LwM2M Client stores "Notify" operations to the LwM2M Server while the LwM2M Server account is disabled or the LwM2M Client is offline. After the LwM2M Server account is enabled or the LwM2M Client is online, the LwM2M Client reports the stored "Notify" operations to the Server.
  142 +If false, the LwM2M Client discards all the "Notify" operations or temporarily disables the Observe function while the LwM2M Server is disabled or the LwM2M Client is offline.
  143 +The default value is true.
  144 +The maximum number of storing Notifications per Server is up to the implementation.]]></Description>
  145 + </Item>
  146 + <Item ID="7">
  147 + <Name>Binding</Name>
  148 + <Operations>RW</Operations>
  149 + <MultipleInstances>Single</MultipleInstances>
  150 + <Mandatory>Mandatory</Mandatory>
  151 + <Type>String</Type>
  152 + <RangeEnumeration></RangeEnumeration>
  153 + <Units></Units>
  154 + <Description><![CDATA[The possible values are those listed in the LwM2M Core Specification. This Resource defines the transport binding configured for the LwM2M Client.
  155 +If the LwM2M Client supports the binding specified in this Resource, the LwM2M Client MUST use that transport for the Current Binding Mode.]]></Description>
  156 + </Item>
  157 + <Item ID="8">
  158 + <Name>Registration Update Trigger</Name>
  159 + <Operations>E</Operations>
  160 + <MultipleInstances>Single</MultipleInstances>
  161 + <Mandatory>Mandatory</Mandatory>
  162 + <Type></Type>
  163 + <RangeEnumeration></RangeEnumeration>
  164 + <Units></Units>
  165 + <Description><![CDATA[If this Resource is executed the LwM2M Client MUST perform an "Update" operation with this LwM2M Server. The LwM2M Client can use a transport binding supported in the Current Binding Mode, Preferred Transport resource or the transport specified as an argument in the Registration Update Trigger.]]></Description>
  166 + </Item>
  167 + <Item ID="9">
  168 + <Name>Bootstrap-Request Trigger</Name>
  169 + <Operations>E</Operations>
  170 + <MultipleInstances>Single</MultipleInstances>
  171 + <Mandatory>Optional</Mandatory>
  172 + <Type></Type>
  173 + <RangeEnumeration></RangeEnumeration>
  174 + <Units></Units>
  175 + <Description><![CDATA[When this Resource is executed the LwM2M Client MUST initiate a "Client Initiated Bootstrap" procedure in using the LwM2M Bootstrap-Server Account.]]></Description>
  176 + </Item>
  177 + <Item ID="10">
  178 + <Name>APN Link</Name>
  179 + <Operations>RW</Operations>
  180 + <MultipleInstances>Single</MultipleInstances>
  181 + <Mandatory>Optional</Mandatory>
  182 + <Type>Objlnk</Type>
  183 + <RangeEnumeration></RangeEnumeration>
  184 + <Units></Units>
  185 + <Description><![CDATA[If this resource is defined, it provides a link to the APN connection profile Object Instance (OMNA registered Object ID:11) to be used to communicate with this server.]]></Description>
  186 + </Item>
  187 + <Item ID="11">
  188 + <Name>TLS-DTLS Alert Code</Name>
  189 + <Operations>R</Operations>
  190 + <MultipleInstances>Single</MultipleInstances>
  191 + <Mandatory>Optional</Mandatory>
  192 + <Type>Integer</Type>
  193 + <RangeEnumeration>0..255</RangeEnumeration>
  194 + <Units></Units>
  195 + <Description><![CDATA[If this resource is defined, it contains the most recent TLS / DTLS alert message received from the LwM2M Server respective represented by the AlertDescription defined in Section 7.2 of RFC 5246. This resource set by the LwM2M Client may help the LwM2M Bootstrap-Server to determine the cause of TLS/DTLS connection failure with the respective LwM2M Server.]]></Description>
  196 + </Item>
  197 + <Item ID="12">
  198 + <Name>Last Bootstrapped</Name>
  199 + <Operations>R</Operations>
  200 + <MultipleInstances>Single</MultipleInstances>
  201 + <Mandatory>Optional</Mandatory>
  202 + <Type>Time</Type>
  203 + <RangeEnumeration></RangeEnumeration>
  204 + <Units></Units>
  205 + <Description><![CDATA[If this resource is defined, it represents the last time that the bootstrap server updated this LwM2M Server Account. The LwM2M Client is responsible for updating this value. When the Bootstrap Server detects that this LwM2M Server Account is "out-of-date", the Bootstrap Server can update the LwM2M Server Account as represented by the LwM2M Server object instance.]]></Description>
  206 + </Item>
  207 + <Item ID="13">
  208 + <Name>Registration Priority Order</Name>
  209 + <Operations>R</Operations>
  210 + <MultipleInstances>Single</MultipleInstances>
  211 + <Mandatory>Optional</Mandatory>
  212 + <Type>Integer</Type>
  213 + <RangeEnumeration></RangeEnumeration>
  214 + <Units></Units>
  215 + <Description><![CDATA[The LwM2M Client sequences the LwM2M Server registrations in increasing order of this value. If this value is not defined, registration attempts to this server are not impacted by other server registrations.]]></Description>
  216 + </Item>
  217 + <Item ID="14">
  218 + <Name>Initial Registration Delay Timer</Name>
  219 + <Operations>RW</Operations>
  220 + <MultipleInstances>Single</MultipleInstances>
  221 + <Mandatory>Optional</Mandatory>
  222 + <Type>Integer</Type>
  223 + <RangeEnumeration></RangeEnumeration>
  224 + <Units>s</Units>
  225 + <Description><![CDATA[The delay, in seconds, before registration is attempted for this LwM2M Server based upon the completion of registration of the previous LwM2M Server in the registration order. This is only applied until the first successful registration after a successful bootstrapping sequence.]]></Description>
  226 + </Item>
  227 + <Item ID="15">
  228 + <Name>Registration Failure Block</Name>
  229 + <Operations>R</Operations>
  230 + <MultipleInstances>Single</MultipleInstances>
  231 + <Mandatory>Optional</Mandatory>
  232 + <Type>Boolean</Type>
  233 + <RangeEnumeration></RangeEnumeration>
  234 + <Units></Units>
  235 + <Description><![CDATA[When set to true and registration to this LwM2M server fails, the LwM2M Client blocks registration to other servers in the order. When set to false, the LwM2M Client proceeds with registration to the next server in the order.]]></Description>
  236 + </Item>
  237 + <Item ID="16">
  238 + <Name>Bootstrap on Registration Failure</Name>
  239 + <Operations>R</Operations>
  240 + <MultipleInstances>Single</MultipleInstances>
  241 + <Mandatory>Optional</Mandatory>
  242 + <Type>Boolean</Type>
  243 + <RangeEnumeration></RangeEnumeration>
  244 + <Units></Units>
  245 + <Description><![CDATA[If set to true, this indicates that the LwM2M Client should re-bootstrap when either registration is explicitly rejected by the LwM2M Server or registration is considered as failing as dictated by the other resource settings. If set to false, the LwM2M Client will continue with the registration attempts as dictated by the other resource settings.]]></Description>
  246 + </Item>
  247 + <Item ID="17">
  248 + <Name>Communication Retry Count</Name>
  249 + <Operations>RW</Operations>
  250 + <MultipleInstances>Single</MultipleInstances>
  251 + <Mandatory>Optional</Mandatory>
  252 + <Type>Integer</Type>
  253 + <RangeEnumeration></RangeEnumeration>
  254 + <Units></Units>
  255 + <Description><![CDATA[The number of successive communication attempts before which a communication sequence is considered as failed.]]></Description>
  256 + </Item>
  257 + <Item ID="18">
  258 + <Name>Communication Retry Timer</Name>
  259 + <Operations>RW</Operations>
  260 + <MultipleInstances>Single</MultipleInstances>
  261 + <Mandatory>Optional</Mandatory>
  262 + <Type>Integer</Type>
  263 + <RangeEnumeration></RangeEnumeration>
  264 + <Units>s</Units>
  265 + <Description><![CDATA[The delay, in seconds, between successive communication attempts in a communication sequence. This value is multiplied by two to the power of the communication retry attempt minus one (2**(retry attempt-1)) to create an exponential back-off.]]></Description>
  266 + </Item>
  267 + <Item ID="19">
  268 + <Name>Communication Sequence Delay Timer</Name>
  269 + <Operations>RW</Operations>
  270 + <MultipleInstances>Single</MultipleInstances>
  271 + <Mandatory>Optional</Mandatory>
  272 + <Type>Integer</Type>
  273 + <RangeEnumeration></RangeEnumeration>
  274 + <Units>s</Units>
  275 + <Description><![CDATA[The delay, in seconds, between successive communication sequences. A communication sequence is defined as the exhaustion of the Communication Retry Count and Communication Retry Timer values. A communication sequence can be applied to server registrations or bootstrapping attempts. MAX_VALUE means do not perform another communication sequence.]]></Description>
  276 + </Item>
  277 + <Item ID="20">
  278 + <Name>Communication Sequence Retry Count</Name>
  279 + <Operations>RW</Operations>
  280 + <MultipleInstances>Single</MultipleInstances>
  281 + <Mandatory>Optional</Mandatory>
  282 + <Type>Integer</Type>
  283 + <RangeEnumeration></RangeEnumeration>
  284 + <Units></Units>
  285 + <Description><![CDATA[The number of successive communication sequences before which a registration attempt is considered as failed.]]></Description>
  286 + </Item>
  287 + <Item ID="21">
  288 + <Name>Trigger</Name>
  289 + <Operations>RW</Operations>
  290 + <MultipleInstances>Single</MultipleInstances>
  291 + <Mandatory>Optional</Mandatory>
  292 + <Type>Boolean</Type>
  293 + <RangeEnumeration></RangeEnumeration>
  294 + <Units></Units>
  295 + <Description><![CDATA[Using the Trigger Resource a LwM2M Client can indicate whether it is reachable over SMS (value set to 'true') or not (value set to 'false'). The default value (resource not present) is 'false'. When set to 'true' the LwM2M Server MAY, for example, request the LwM2M Client to perform operations, such as the "Update" operation by sending an "Execute" operation on "Registration Update Trigger" Resource via SMS. No SMS response is expected for such a message.]]></Description>
  296 + </Item>
  297 + <Item ID="22">
  298 + <Name>Preferred Transport</Name>
  299 + <Operations>RW</Operations>
  300 + <MultipleInstances>Single</MultipleInstances>
  301 + <Mandatory>Optional</Mandatory>
  302 + <Type>String</Type>
  303 + <RangeEnumeration>The possible values are those listed in the LwM2M Core Specification</RangeEnumeration>
  304 + <Units></Units>
  305 + <Description><![CDATA[Only a single transport binding SHALL be present. When the LwM2M client supports multiple transports, it MAY use this transport to initiate a connection. This resource can also be used to switch between multiple transports e.g. a non-IP device can switch to UDP transport to perform firmware updates.]]></Description>
  306 + </Item>
  307 + <Item ID="23"><Name>Mute Send</Name>
  308 + <Operations>RW</Operations>
  309 + <MultipleInstances>Single</MultipleInstances>
  310 + <Mandatory>Optional</Mandatory>
  311 + <Type>Boolean</Type>
  312 + <RangeEnumeration></RangeEnumeration>
  313 + <Units></Units>
  314 + <Description><![CDATA[If true or the Resource is not present, the LwM2M Client Send command capability is de-activated.
  315 +If false, the LwM2M Client Send Command capability is activated.]]></Description>
  316 + </Item>
  317 + <Item ID="24">
  318 + <Name>Alternate APN Links</Name>
  319 + <Operations>RW</Operations>
  320 + <MultipleInstances>Multiple</MultipleInstances>
  321 + <Mandatory>Optional</Mandatory>
  322 + <Type>Objlnk</Type>
  323 + <RangeEnumeration></RangeEnumeration>
  324 + <Units></Units>
  325 + <Description><![CDATA[If this resource is defined, it provides links to alternate APN connection profile Object Instance (OMNA registered Object ID:11) to be used to communicate with this server if Resource 10 has configuration conflicts.]]></Description>
  326 + </Item>
  327 + <Item ID="25">
  328 + <Name>Supported Server Versions</Name>
  329 + <Operations>RW</Operations>
  330 + <MultipleInstances>Multiple</MultipleInstances>
  331 + <Mandatory>Optional</Mandatory>
  332 + <Type>String</Type>
  333 + <RangeEnumeration></RangeEnumeration>
  334 + <Units></Units>
  335 + <Description><![CDATA[This resource provides the supported enabler versions of the server to the client as a set of strings. Format for each string is 1*DIGIT"."1*DIGIT"."1*DIGIT where the third DIGIT is optional.]]></Description>
  336 + </Item>
  337 + <Item ID="26">
  338 + <Name>Default Notification Mode</Name>
  339 + <Operations>RW</Operations>
  340 + <MultipleInstances>Single</MultipleInstances>
  341 + <Mandatory>Optional</Mandatory>
  342 + <Type>Integer</Type>
  343 + <RangeEnumeration>0..1</RangeEnumeration>
  344 + <Units></Units>
  345 + <Description><![CDATA[This resource indicates the default mode for observations to be sent: 0 = Non-Confirmable, 1 = Confirmable.]]></Description>
  346 + </Item>
  347 + <Item ID="27">
  348 + <Name>Profile ID Hash Algorithm</Name>
  349 + <Operations>RW</Operations>
  350 + <MultipleInstances>Single</MultipleInstances>
  351 + <Mandatory>Optional</Mandatory>
  352 + <Type>Integer</Type>
  353 + <RangeEnumeration>0..255</RangeEnumeration>
  354 + <Units/>
  355 + <Description><![CDATA[If this resource is defined, it contains the hash algorithm the LwM2M Server would prefer the LwM2M Client to use with the dynamically generated mode of creating Profile IDs. The numerical ID value of the 'Suite Identifiers' registered by RFC 6920 is used in this Resource.]]></Description>
  356 + </Item>
  357 + </Resources>
  358 + <Description2></Description2>
  359 + </Object>
  360 +</LWM2M>
... ...
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +
  3 +<!--
  4 +FILE INFORMATION
  5 +
  6 +OMA Permanent Document
  7 + File: OMA-SUP-XML_2-V1_1-20201110-A.xml
  8 + Path: http://www.openmobilealliance.org/release/ObjLwM2M_ACL/
  9 +
  10 +OMNA LwM2M Registry
  11 + Path: https://github.com/OpenMobileAlliance/lwm2m-registry
  12 + Name: 2.xml
  13 +
  14 +NORMATIVE INFORMATION
  15 +
  16 + Information about this file can be found in the latest revision of
  17 +
  18 + OMA-TS-LightweightM2M_Core-V1_2
  19 +
  20 + This is available at http://www.openmobilealliance.org/release/LightweightM2M/
  21 +
  22 + Send comments to https://github.com/OpenMobileAlliance/OMA_LwM2M_for_Developers/issues
  23 +
  24 +LEGAL DISCLAIMER
  25 +
  26 + Copyright 2020 Open Mobile Alliance.
  27 +
  28 + Redistribution and use in source and binary forms, with or without
  29 + modification, are permitted provided that the following conditions
  30 + are met:
  31 +
  32 + 1. Redistributions of source code must retain the above copyright
  33 + notice, this list of conditions and the following disclaimer.
  34 + 2. Redistributions in binary form must reproduce the above copyright
  35 + notice, this list of conditions and the following disclaimer in the
  36 + documentation and/or other materials provided with the distribution.
  37 + 3. Neither the name of the copyright holder nor the names of its
  38 + contributors may be used to endorse or promote products derived
  39 + from this software without specific prior written permission.
  40 +
  41 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  42 + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  43 + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  44 + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  45 + COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  46 + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  47 + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  48 + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  49 + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  50 + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  51 + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  52 + POSSIBILITY OF SUCH DAMAGE.
  53 +
  54 + The above license is used as a license under copyright only. Please
  55 + reference the OMA IPR Policy for patent licensing terms:
  56 + https://www.omaspecworks.org/about/intellectual-property-rights/
  57 +-->
  58 +
  59 +<LWM2M xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.openmobilealliance.org/tech/profiles/LWM2M.xsd">
  60 + <Object ObjectType="MODefinition">
  61 + <Name>LwM2M Access Control</Name>
  62 + <Description1><![CDATA[Access Control Object is used to check whether the LwM2M Server has access right for performing an operation.]]></Description1>
  63 + <ObjectID>2</ObjectID>
  64 + <ObjectURN>urn:oma:lwm2m:oma:2:1.1</ObjectURN>
  65 + <LWM2MVersion>1.0</LWM2MVersion>
  66 + <ObjectVersion>1.1</ObjectVersion>
  67 + <MultipleInstances>Multiple</MultipleInstances>
  68 + <Mandatory>Optional</Mandatory>
  69 + <Resources>
  70 + <Item ID="0">
  71 + <Name>Object ID</Name>
  72 + <Operations>R</Operations>
  73 + <MultipleInstances>Single</MultipleInstances>
  74 + <Mandatory>Mandatory</Mandatory>
  75 + <Type>Integer</Type>
  76 + <RangeEnumeration>1..65534</RangeEnumeration>
  77 + <Units></Units>
  78 + <Description><![CDATA[Resources 0 and 1 point to the Object Instance for which the Instances of the ACL Resource of that Access Control Object Instance are applicable.]]></Description>
  79 + </Item>
  80 + <Item ID="1">
  81 + <Name>Object Instance ID</Name>
  82 + <Operations>R</Operations>
  83 + <MultipleInstances>Single</MultipleInstances>
  84 + <Mandatory>Mandatory</Mandatory>
  85 + <Type>Integer</Type>
  86 + <RangeEnumeration>0..65535</RangeEnumeration>
  87 + <Units></Units>
  88 + <Description><![CDATA[See above]]></Description>
  89 + </Item>
  90 + <Item ID="2">
  91 + <Name>ACL</Name>
  92 + <Operations>RW</Operations>
  93 + <MultipleInstances>Multiple</MultipleInstances>
  94 + <Mandatory>Optional</Mandatory>
  95 + <Type>Integer</Type>
  96 + <RangeEnumeration>0..31</RangeEnumeration>
  97 + <Units></Units>
  98 + <Description><![CDATA[The Resource Instance ID MUST be the Short Server ID of a certain LwM2M Server for which associated access rights are contained in the Resource Instance value.
  99 +The Resource Instance ID 0 is a specific ID, determining the ACL Instance which contains the default access rights.
  100 +Each bit set in the Resource Instance value, grants an access right to the LwM2M Server to the corresponding operation.
  101 +The bit order is specified as below.
  102 +1st LSB: R(Read, Observe, Write-Attributes)
  103 +2nd LSB: W(Write)
  104 +3rd LSB: E(Execute)
  105 +4th LSB: D(Delete)
  106 +5th LSB: C(Create)
  107 +Other bits are reserved for future use.]]></Description>
  108 + </Item>
  109 + <Item ID="3">
  110 + <Name>Access Control Owner</Name>
  111 + <Operations>RW</Operations>
  112 + <MultipleInstances>Single</MultipleInstances>
  113 + <Mandatory>Mandatory</Mandatory>
  114 + <Type>Integer</Type>
  115 + <RangeEnumeration>0..65535</RangeEnumeration>
  116 + <Units></Units>
  117 + <Description><![CDATA[Short Server ID of a certain LwM2M Server; only such an LwM2M Server can manage the Resources of this Object Instance.
  118 +The specific value MAX_ID=65535 means this Access Control Object Instance is created and modified during a Bootstrap phase only.]]></Description>
  119 + </Item>
  120 + </Resources>
  121 + <Description2><![CDATA[]]></Description2>
  122 + </Object>
  123 +</LWM2M>
... ...
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +
  3 +<!--
  4 +FILE INFORMATION
  5 +
  6 +OMA Permanent Document
  7 + File: OMA-SUP-XML_3-V1_2-20201110-A.xml
  8 + Path: http://www.openmobilealliance.org/release/ObjLwM2M_Device/
  9 +
  10 +OMNA LwM2M Registry
  11 + Path: https://github.com/OpenMobileAlliance/lwm2m-registry
  12 + Name: 3.xml
  13 +
  14 +NORMATIVE INFORMATION
  15 +
  16 + Information about this file can be found in the latest revision of
  17 +
  18 + OMA-TS-LightweightM2M_Core-V1_2
  19 +
  20 + This is available at http://www.openmobilealliance.org/release/LightweightM2M/
  21 +
  22 + Send comments to https://github.com/OpenMobileAlliance/OMA_LwM2M_for_Developers/issues
  23 +
  24 +LEGAL DISCLAIMER
  25 +
  26 + Copyright 2020 Open Mobile Alliance.
  27 +
  28 + Redistribution and use in source and binary forms, with or without
  29 + modification, are permitted provided that the following conditions
  30 + are met:
  31 +
  32 + 1. Redistributions of source code must retain the above copyright
  33 + notice, this list of conditions and the following disclaimer.
  34 + 2. Redistributions in binary form must reproduce the above copyright
  35 + notice, this list of conditions and the following disclaimer in the
  36 + documentation and/or other materials provided with the distribution.
  37 + 3. Neither the name of the copyright holder nor the names of its
  38 + contributors may be used to endorse or promote products derived
  39 + from this software without specific prior written permission.
  40 +
  41 + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  42 + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  43 + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  44 + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  45 + COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  46 + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  47 + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  48 + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  49 + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  50 + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  51 + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  52 + POSSIBILITY OF SUCH DAMAGE.
  53 +
  54 + The above license is used as a license under copyright only. Please
  55 + reference the OMA IPR Policy for patent licensing terms:
  56 + https://www.omaspecworks.org/about/intellectual-property-rights/
  57 +
  58 +-->
  59 +
  60 +<LWM2M xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.openmobilealliance.org/tech/profiles/LWM2M-v1_1.xsd">
  61 + <Object ObjectType="MODefinition">
  62 + <Name>Device</Name>
  63 + <Description1><![CDATA[This LwM2M Object provides a range of device related information which can be queried by the LwM2M Server, and a device reboot and factory reset function.]]></Description1>
  64 + <ObjectID>3</ObjectID>
  65 + <ObjectURN>urn:oma:lwm2m:oma:3:1.0</ObjectURN>
  66 + <LWM2MVersion>1.1</LWM2MVersion>
  67 + <ObjectVersion>1.0</ObjectVersion>
  68 + <MultipleInstances>Single</MultipleInstances>
  69 + <Mandatory>Mandatory</Mandatory>
  70 + <Resources>
  71 + <Item ID="0">
  72 + <Name>Manufacturer</Name>
  73 + <Operations>R</Operations>
  74 + <MultipleInstances>Single</MultipleInstances>
  75 + <Mandatory>Optional</Mandatory>
  76 + <Type>String</Type>
  77 + <RangeEnumeration></RangeEnumeration>
  78 + <Units></Units>
  79 + <Description><![CDATA[Human readable manufacturer name]]></Description>
  80 + </Item>
  81 + <Item ID="1">
  82 + <Name>Model Number</Name>
  83 + <Operations>R</Operations>
  84 + <MultipleInstances>Single</MultipleInstances>
  85 + <Mandatory>Optional</Mandatory>
  86 + <Type>String</Type>
  87 + <RangeEnumeration></RangeEnumeration>
  88 + <Units></Units>
  89 + <Description><![CDATA[A model identifier (manufacturer specified string)]]></Description>
  90 + </Item>
  91 + <Item ID="2">
  92 + <Name>Serial Number</Name>
  93 + <Operations>R</Operations>
  94 + <MultipleInstances>Single</MultipleInstances>
  95 + <Mandatory>Optional</Mandatory>
  96 + <Type>String</Type>
  97 + <RangeEnumeration></RangeEnumeration>
  98 + <Units></Units>
  99 + <Description><![CDATA[Serial Number]]></Description>
  100 + </Item>
  101 + <Item ID="3">
  102 + <Name>Firmware Version</Name>
  103 + <Operations>R</Operations>
  104 + <MultipleInstances>Single</MultipleInstances>
  105 + <Mandatory>Optional</Mandatory>
  106 + <Type>String</Type>
  107 + <RangeEnumeration></RangeEnumeration>
  108 + <Units></Units>
  109 + <Description><![CDATA[Current firmware version of the Device.The Firmware Management function could rely on this resource.]]></Description>
  110 + </Item>
  111 + <Item ID="4">
  112 + <Name>Reboot</Name>
  113 + <Operations>E</Operations>
  114 + <MultipleInstances>Single</MultipleInstances>
  115 + <Mandatory>Mandatory</Mandatory>
  116 + <Type></Type>
  117 + <RangeEnumeration></RangeEnumeration>
  118 + <Units></Units>
  119 + <Description><![CDATA[Reboot the LwM2M Device to restore the Device from unexpected firmware failure.]]></Description>
  120 + </Item>
  121 + <Item ID="5">
  122 + <Name>Factory Reset</Name>
  123 + <Operations>E</Operations>
  124 + <MultipleInstances>Single</MultipleInstances>
  125 + <Mandatory>Optional</Mandatory>
  126 + <Type></Type>
  127 + <RangeEnumeration></RangeEnumeration>
  128 + <Units></Units>
  129 + <Description><![CDATA[Perform factory reset of the LwM2M Device to make the LwM2M Device to go through initial deployment sequence where provisioning and bootstrap sequence is performed. This requires client ensuring post factory reset to have minimal information to allow it to carry out one of the bootstrap methods specified in section 5.2.3.
  130 +When this Resource is executed, "De-register" operation MAY be sent to the LwM2M Server(s) before factory reset of the LwM2M Device.]]></Description>
  131 + </Item>
  132 + <Item ID="6">
  133 + <Name>Available Power Sources</Name>
  134 + <Operations>R</Operations>
  135 + <MultipleInstances>Multiple</MultipleInstances>
  136 + <Mandatory>Optional</Mandatory>
  137 + <Type>Integer</Type>
  138 + <RangeEnumeration>0..7</RangeEnumeration>
  139 + <Units></Units>
  140 + <Description><![CDATA[0: DC power
  141 +1: Internal Battery
  142 +2: External Battery
  143 +3: Fuel Cell
  144 +4: Power over Ethernet
  145 +5: USB
  146 +6: AC (Mains) power
  147 +7: Solar
  148 +The same Resource Instance ID MUST be used to associate a given Power Source (Resource ID:6) with its Present Voltage (Resource ID:7) and its Present Current (Resource ID:8)]]></Description>
  149 + </Item>
  150 + <Item ID="7">
  151 + <Name>Power Source Voltage</Name>
  152 + <Operations>R</Operations>
  153 + <MultipleInstances>Multiple</MultipleInstances>
  154 + <Mandatory>Optional</Mandatory>
  155 + <Type>Integer</Type>
  156 + <RangeEnumeration></RangeEnumeration>
  157 + <Units></Units>
  158 + <Description><![CDATA[Present voltage for each Available Power Sources Resource Instance. The unit used for this resource is in mV.]]></Description>
  159 + </Item>
  160 + <Item ID="8">
  161 + <Name>Power Source Current</Name>
  162 + <Operations>R</Operations>
  163 + <MultipleInstances>Multiple</MultipleInstances>
  164 + <Mandatory>Optional</Mandatory>
  165 + <Type>Integer</Type>
  166 + <RangeEnumeration></RangeEnumeration>
  167 + <Units></Units>
  168 + <Description><![CDATA[Present current for each Available Power Source. The unit used for this resource is in mA.]]></Description>
  169 + </Item>
  170 + <Item ID="9">
  171 + <Name>Battery Level</Name>
  172 + <Operations>R</Operations>
  173 + <MultipleInstances>Single</MultipleInstances>
  174 + <Mandatory>Optional</Mandatory>
  175 + <Type>Integer</Type>
  176 + <RangeEnumeration>0..100</RangeEnumeration>
  177 + <Units>/100</Units>
  178 + <Description><![CDATA[Contains the current battery level as a percentage (with a range from 0 to 100). This value is only valid for the Device internal Battery if present (one Available Power Sources Resource Instance is 1).]]></Description>
  179 + </Item>
  180 + <Item ID="10">
  181 + <Name>Memory Free</Name>
  182 + <Operations>R</Operations>
  183 + <MultipleInstances>Single</MultipleInstances>
  184 + <Mandatory>Optional</Mandatory>
  185 + <Type>Integer</Type>
  186 + <RangeEnumeration></RangeEnumeration>
  187 + <Units></Units>
  188 + <Description><![CDATA[Estimated current available amount of storage space which can store data and software in the LwM2M Device (expressed in kilobytes). Note: 1 kilobyte corresponds to 1000 bytes.]]></Description>
  189 + </Item>
  190 + <Item ID="11">
  191 + <Name>Error Code</Name>
  192 + <Operations>R</Operations>
  193 + <MultipleInstances>Multiple</MultipleInstances>
  194 + <Mandatory>Mandatory</Mandatory>
  195 + <Type>Integer</Type>
  196 + <RangeEnumeration>0..32</RangeEnumeration>
  197 + <Units></Units>
  198 + <Description><![CDATA[0=No error
  199 +1=Low battery power
  200 +2=External power supply off
  201 +3=GPS module failure
  202 +4=Low received signal strength
  203 +5=Out of memory
  204 +6=SMS failure
  205 +7=IP connectivity failure
  206 +8=Peripheral malfunction
  207 +9..15=Reserved for future use
  208 +16..32=Device specific error codes
  209 +
  210 +When the single Device Object Instance is initiated, there is only one error code Resource Instance whose value is equal to 0 that means no error. When the first error happens, the LwM2M Client changes error code Resource Instance to any non-zero value to indicate the error type. When any other error happens, a new error code Resource Instance is created. When an error associated with a Resource Instance is no longer present, that Resource Instance is deleted. When the single existing error is no longer present, the LwM2M Client returns to the original no error state where Instance 0 has value 0.
  211 +This error code Resource MAY be observed by the LwM2M Server. How to deal with LwM2M Client’s error report depends on the policy of the LwM2M Server. Error codes in between 16 and 32 are specific to the Device and may have different meanings among implementations.]]></Description>
  212 + </Item>
  213 + <Item ID="12">
  214 + <Name>Reset Error Code</Name>
  215 + <Operations>E</Operations>
  216 + <MultipleInstances>Single</MultipleInstances>
  217 + <Mandatory>Optional</Mandatory>
  218 + <Type></Type>
  219 + <RangeEnumeration></RangeEnumeration>
  220 + <Units></Units>
  221 + <Description><![CDATA[Delete all error code Resource Instances and create only one zero-value error code that implies no error, then re-evaluate all error conditions and update and create Resources Instances to capture all current error conditions.]]></Description>
  222 + </Item>
  223 + <Item ID="13">
  224 + <Name>Current Time</Name>
  225 + <Operations>RW</Operations>
  226 + <MultipleInstances>Single</MultipleInstances>
  227 + <Mandatory>Optional</Mandatory>
  228 + <Type>Time</Type>
  229 + <RangeEnumeration></RangeEnumeration>
  230 + <Units></Units>
  231 + <Description><![CDATA[Current UNIX time of the LwM2M Client.
  232 +The LwM2M Client should be responsible to increase this time value as every second elapses.
  233 +The LwM2M Server is able to write this Resource to make the LwM2M Client synchronized with the LwM2M Server.]]></Description>
  234 + </Item>
  235 + <Item ID="14">
  236 + <Name>UTC Offset</Name>
  237 + <Operations>RW</Operations>
  238 + <MultipleInstances>Single</MultipleInstances>
  239 + <Mandatory>Optional</Mandatory>
  240 + <Type>String</Type>
  241 + <RangeEnumeration></RangeEnumeration>
  242 + <Units></Units>
  243 + <Description><![CDATA[Indicates the UTC offset currently in effect for this LwM2M Device. UTC+X [ISO 8601].]]></Description>
  244 + </Item>
  245 + <Item ID="15">
  246 + <Name>Timezone</Name>
  247 + <Operations>RW</Operations>
  248 + <MultipleInstances>Single</MultipleInstances>
  249 + <Mandatory>Optional</Mandatory>
  250 + <Type>String</Type>
  251 + <RangeEnumeration></RangeEnumeration>
  252 + <Units></Units>
  253 + <Description><![CDATA[Indicates in which time zone the LwM2M Device is located, in IANA Timezone (TZ) database format.]]></Description>
  254 + </Item>
  255 + <Item ID="16">
  256 + <Name>Supported Binding and Modes</Name>
  257 + <Operations>R</Operations>
  258 + <MultipleInstances>Single</MultipleInstances>
  259 + <Mandatory>Mandatory</Mandatory>
  260 + <Type>String</Type>
  261 + <RangeEnumeration></RangeEnumeration>
  262 + <Units></Units>
  263 + <Description><![CDATA[Indicates which bindings and modes are supported in the LwM2M Client. The possible values are those listed in the LwM2M Core Specification.]]></Description>
  264 + </Item>
  265 + <Item ID="17"><Name>Device Type</Name>
  266 + <Operations>R</Operations>
  267 + <MultipleInstances>Single</MultipleInstances>
  268 + <Mandatory>Optional</Mandatory>
  269 + <Type>String</Type>
  270 + <RangeEnumeration></RangeEnumeration>
  271 + <Units></Units>
  272 + <Description><![CDATA[Type of the device (manufacturer specified string: e.g. smart meters / dev Class / ...)]]></Description>
  273 + </Item>
  274 + <Item ID="18"><Name>Hardware Version</Name>
  275 + <Operations>R</Operations>
  276 + <MultipleInstances>Single</MultipleInstances>
  277 + <Mandatory>Optional</Mandatory>
  278 + <Type>String</Type>
  279 + <RangeEnumeration></RangeEnumeration>
  280 + <Units></Units>
  281 + <Description><![CDATA[Current hardware version of the device]]></Description>
  282 + </Item>
  283 + <Item ID="19"><Name>Software Version</Name>
  284 + <Operations>R</Operations>
  285 + <MultipleInstances>Single</MultipleInstances>
  286 + <Mandatory>Optional</Mandatory>
  287 + <Type>String</Type>
  288 + <RangeEnumeration></RangeEnumeration>
  289 + <Units></Units>
  290 + <Description><![CDATA[Current software version of the device (manufacturer specified string). On elaborated LwM2M device, SW could be split in 2 parts: a firmware one and a higher level software on top.
  291 +Both pieces of Software are together managed by LwM2M Firmware Update Object (Object ID 5)]]></Description>
  292 + </Item>
  293 + <Item ID="20"><Name>Battery Status</Name>
  294 + <Operations>R</Operations>
  295 + <MultipleInstances>Single</MultipleInstances>
  296 + <Mandatory>Optional</Mandatory>
  297 + <Type>Integer</Type>
  298 + <RangeEnumeration>0..6</RangeEnumeration>
  299 + <Units></Units>
  300 + <Description><![CDATA[This value is only valid for the Device Internal Battery if present (one Available Power Sources Resource Instance value is 1).
  301 +Battery
  302 +Status Meaning Description
  303 +0 Normal The battery is operating normally and not on power.
  304 +1 Charging The battery is currently charging.
  305 +2 Charge Complete The battery is fully charged and still on power.
  306 +3 Damaged The battery has some problem.
  307 +4 Low Battery The battery is low on charge.
  308 +5 Not Installed The battery is not installed.
  309 +6 Unknown The battery information is not available.]]></Description>
  310 + </Item>
  311 + <Item ID="21"><Name>Memory Total</Name>
  312 + <Operations>R</Operations>
  313 + <MultipleInstances>Single</MultipleInstances>
  314 + <Mandatory>Optional</Mandatory>
  315 + <Type>Integer</Type>
  316 + <RangeEnumeration></RangeEnumeration>
  317 + <Units></Units>
  318 + <Description><![CDATA[Total amount of storage space which can store data and software in the LwM2M Device (expressed in kilobytes). Note: 1 kilobyte corresponds to 1000 bytes.]]></Description>
  319 + </Item>
  320 + <Item ID="22"><Name>ExtDevInfo</Name>
  321 + <Operations>R</Operations>
  322 + <MultipleInstances>Multiple</MultipleInstances>
  323 + <Mandatory>Optional</Mandatory>
  324 + <Type>Objlnk</Type>
  325 + <RangeEnumeration></RangeEnumeration>
  326 + <Units></Units>
  327 + <Description><![CDATA[Reference to external "Device" object instance containing information. For example, such an external device can be a Host Device, which is a device into which the Device containing the LwM2M client is embedded. This Resource may be used to retrieve information about the Host Device.]]></Description>
  328 + </Item></Resources>
  329 + <Description2></Description2>
  330 + </Object>
  331 +</LWM2M>
... ...
... ... @@ -145,7 +145,6 @@ public class TbCoapDtlsCertificateVerifier implements NewAdvancedCertificateVeri
145 145
146 146 @Override
147 147 public void setResultHandler(HandshakeResultHandler resultHandler) {
148   - // empty implementation
149 148 }
150 149
151 150 public ConcurrentMap<String, TbCoapDtlsSessionInfo> getTbCoapDtlsSessionIdsMap() {
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.device.credentials.lwm2m;
  17 +
  18 +import lombok.Getter;
  19 +import lombok.NoArgsConstructor;
  20 +import lombok.Setter;
  21 +
  22 +@Getter
  23 +@Setter
  24 +@NoArgsConstructor
  25 +public abstract class AbstractLwM2MClientCredentials implements LwM2MClientCredentials {
  26 + private String endpoint;
  27 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.device.credentials.lwm2m;
  17 +
  18 +import lombok.SneakyThrows;
  19 +import org.apache.commons.codec.binary.Hex;
  20 +
  21 +public abstract class HasKey extends AbstractLwM2MClientCredentials {
  22 + private byte[] key;
  23 +
  24 + @SneakyThrows
  25 + public void setKey(String key) {
  26 + if (key != null) {
  27 + this.key = Hex.decodeHex(key.toLowerCase().toCharArray());
  28 + }
  29 + }
  30 +
  31 + public byte[] getKey() {
  32 + return key;
  33 + }
  34 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.device.credentials.lwm2m;
  17 +
  18 +import com.fasterxml.jackson.annotation.JsonIgnore;
  19 +import com.fasterxml.jackson.annotation.JsonSubTypes;
  20 +import com.fasterxml.jackson.annotation.JsonTypeInfo;
  21 +
  22 +@JsonTypeInfo(
  23 + use = JsonTypeInfo.Id.NAME,
  24 + property = "securityConfigClientMode")
  25 +@JsonSubTypes({
  26 + @JsonSubTypes.Type(value = NoSecClientCredentials.class, name = "NO_SEC"),
  27 + @JsonSubTypes.Type(value = PSKClientCredentials.class, name = "PSK"),
  28 + @JsonSubTypes.Type(value = RPKClientCredentials.class, name = "RPK"),
  29 + @JsonSubTypes.Type(value = X509ClientCredentials.class, name = "X509")})
  30 +public interface LwM2MClientCredentials {
  31 +
  32 + @JsonIgnore
  33 + LwM2MSecurityMode getSecurityConfigClientMode();
  34 +
  35 + String getEndpoint();
  36 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.device.credentials.lwm2m;
  17 +
  18 +public enum LwM2MSecurityMode {
  19 + PSK, RPK, X509, NO_SEC;
  20 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.device.credentials.lwm2m;
  17 +
  18 +public class NoSecClientCredentials extends AbstractLwM2MClientCredentials {
  19 +
  20 + @Override
  21 + public LwM2MSecurityMode getSecurityConfigClientMode() {
  22 + return LwM2MSecurityMode.NO_SEC;
  23 + }
  24 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.device.credentials.lwm2m;
  17 +
  18 +import lombok.Getter;
  19 +import lombok.Setter;
  20 +
  21 +@Getter
  22 +@Setter
  23 +public class PSKClientCredentials extends HasKey {
  24 + private String identity;
  25 +
  26 + @Override
  27 + public LwM2MSecurityMode getSecurityConfigClientMode() {
  28 + return LwM2MSecurityMode.PSK;
  29 + }
  30 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.device.credentials.lwm2m;
  17 +
  18 +public class RPKClientCredentials extends HasKey {
  19 +
  20 + @Override
  21 + public LwM2MSecurityMode getSecurityConfigClientMode() {
  22 + return LwM2MSecurityMode.RPK;
  23 + }
  24 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.common.data.device.credentials.lwm2m;
  17 +
  18 +import lombok.Getter;
  19 +import lombok.Setter;
  20 +
  21 +@Getter
  22 +@Setter
  23 +public class X509ClientCredentials extends AbstractLwM2MClientCredentials {
  24 + private String cert;
  25 +
  26 + @Override
  27 + public LwM2MSecurityMode getSecurityConfigClientMode() {
  28 + return LwM2MSecurityMode.X509;
  29 + }
  30 +}
... ...
common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapService.java renamed from common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/bootstrap/LwM2MTransportBootstrapServerConfiguration.java
... ... @@ -65,7 +65,8 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.g
65 65 @Component
66 66 @ConditionalOnExpression("('${service.type:null}'=='tb-transport' && '${transport.lwm2m.enabled:false}'=='true' && '${transport.lwm2m.bootstrap.enable:false}'=='true') || ('${service.type:null}'=='monolith' && '${transport.lwm2m.enabled:false}'=='true'&& '${transport.lwm2m.bootstrap.enable:false}'=='true')")
67 67 @RequiredArgsConstructor
68   -public class LwM2MTransportBootstrapServerConfiguration {
  68 +//TODO: @ybondarenko please refactor this to be similar to DefaultLwM2mTransportService
  69 +public class LwM2MTransportBootstrapService {
69 70 private PublicKey publicKey;
70 71 private PrivateKey privateKey;
71 72 private boolean pskMode = false;
... ...
... ... @@ -73,17 +73,17 @@ public class LwM2MBootstrapConfig {
73 73 configBs.servers.put(0, server0);
74 74 /* Security Configuration (object 0) as defined in LWM2M 1.0.x TS. Bootstrap instance = 0 */
75 75 this.bootstrapServer.setBootstrapServerIs(true);
76   - configBs.security.put(0, setServerSecuruty(this.bootstrapServer.getHost(), this.bootstrapServer.getPort(), this.bootstrapServer.isBootstrapServerIs(), this.bootstrapServer.getSecurityMode(), this.bootstrapServer.getClientPublicKeyOrId(), this.bootstrapServer.getServerPublicKey(), this.bootstrapServer.getClientSecretKey(), this.bootstrapServer.getServerId()));
  76 + configBs.security.put(0, setServerSecurity(this.bootstrapServer.getHost(), this.bootstrapServer.getPort(), this.bootstrapServer.isBootstrapServerIs(), this.bootstrapServer.getSecurityMode(), this.bootstrapServer.getClientPublicKeyOrId(), this.bootstrapServer.getServerPublicKey(), this.bootstrapServer.getClientSecretKey(), this.bootstrapServer.getServerId()));
77 77 /* Security Configuration (object 0) as defined in LWM2M 1.0.x TS. Server instance = 1 */
78   - configBs.security.put(1, setServerSecuruty(this.lwm2mServer.getHost(), this.lwm2mServer.getPort(), this.lwm2mServer.isBootstrapServerIs(), this.lwm2mServer.getSecurityMode(), this.lwm2mServer.getClientPublicKeyOrId(), this.lwm2mServer.getServerPublicKey(), this.lwm2mServer.getClientSecretKey(), this.lwm2mServer.getServerId()));
  78 + configBs.security.put(1, setServerSecurity(this.lwm2mServer.getHost(), this.lwm2mServer.getPort(), this.lwm2mServer.isBootstrapServerIs(), this.lwm2mServer.getSecurityMode(), this.lwm2mServer.getClientPublicKeyOrId(), this.lwm2mServer.getServerPublicKey(), this.lwm2mServer.getClientSecretKey(), this.lwm2mServer.getServerId()));
79 79 return configBs;
80 80 }
81 81
82   - private BootstrapConfig.ServerSecurity setServerSecuruty(String host, Integer port, boolean bootstrapServer, String securityMode, String clientPublicKey, String serverPublicKey, String secretKey, int serverId) {
  82 + private BootstrapConfig.ServerSecurity setServerSecurity(String host, Integer port, boolean bootstrapServer, SecurityMode securityMode, String clientPublicKey, String serverPublicKey, String secretKey, int serverId) {
83 83 BootstrapConfig.ServerSecurity serverSecurity = new BootstrapConfig.ServerSecurity();
84 84 serverSecurity.uri = "coaps://" + host + ":" + Integer.toString(port);
85 85 serverSecurity.bootstrapServer = bootstrapServer;
86   - serverSecurity.securityMode = SecurityMode.valueOf(securityMode);
  86 + serverSecurity.securityMode = securityMode;
87 87 serverSecurity.publicKeyOrId = setPublicKeyOrId(clientPublicKey, securityMode);
88 88 serverSecurity.serverPublicKey = (serverPublicKey != null && !serverPublicKey.isEmpty()) ? Hex.decodeHex(serverPublicKey.toCharArray()) : new byte[]{};
89 89 serverSecurity.secretKey = (secretKey != null && !secretKey.isEmpty()) ? Hex.decodeHex(secretKey.toCharArray()) : new byte[]{};
... ... @@ -91,9 +91,9 @@ public class LwM2MBootstrapConfig {
91 91 return serverSecurity;
92 92 }
93 93
94   - private byte[] setPublicKeyOrId(String publicKeyOrIdStr, String securityMode) {
  94 + private byte[] setPublicKeyOrId(String publicKeyOrIdStr, SecurityMode securityMode) {
95 95 return (publicKeyOrIdStr == null || publicKeyOrIdStr.isEmpty()) ? new byte[]{} :
96   - SecurityMode.valueOf(securityMode).equals(SecurityMode.PSK) ? publicKeyOrIdStr.getBytes(StandardCharsets.UTF_8) :
  96 + SecurityMode.PSK.equals(securityMode) ? publicKeyOrIdStr.getBytes(StandardCharsets.UTF_8) :
97 97 Hex.decodeHex(publicKeyOrIdStr.toCharArray());
98 98 }
99 99 }
... ...
... ... @@ -31,7 +31,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
31 31 import org.springframework.stereotype.Service;
32 32 import org.thingsboard.server.gen.transport.TransportProtos;
33 33 import org.thingsboard.server.transport.lwm2m.secure.EndpointSecurityInfo;
34   -import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
35 34 import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
36 35 import org.thingsboard.server.transport.lwm2m.server.LwM2mSessionMsgListener;
37 36 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
... ... @@ -74,7 +73,7 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
74 73 @Override
75 74 public List<SecurityInfo> getAllByEndpoint(String endPoint) {
76 75 EndpointSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfo(endPoint, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP);
77   - if (store.getBootstrapJsonCredential() != null && store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) {
  76 + if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) {
78 77 /* add value to store from BootstrapJson */
79 78 this.setBootstrapConfigScurityInfo(store);
80 79 BootstrapConfig bsConfigNew = store.getBootstrapConfig();
... ... @@ -98,13 +97,13 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
98 97 @Override
99 98 public SecurityInfo getByIdentity(String identity) {
100 99 EndpointSecurityInfo store = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfo(identity, LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP);
101   - if (store.getBootstrapJsonCredential() != null && store.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) {
  100 + if (store.getBootstrapCredentialConfig() != null && store.getSecurityMode() != null) {
102 101 /* add value to store from BootstrapJson */
103 102 this.setBootstrapConfigScurityInfo(store);
104 103 BootstrapConfig bsConfig = store.getBootstrapConfig();
105 104 if (bsConfig.security != null) {
106 105 try {
107   - bootstrapConfigStore.add(store.getEndPoint(), bsConfig);
  106 + bootstrapConfigStore.add(store.getEndpoint(), bsConfig);
108 107 } catch (InvalidConfigurationException e) {
109 108 log.error("", e);
110 109 }
... ... @@ -119,29 +118,29 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
119 118 LwM2MBootstrapConfig lwM2MBootstrapConfig = this.getParametersBootstrap(store);
120 119 if (lwM2MBootstrapConfig != null) {
121 120 /* Security info */
122   - switch (SecurityMode.valueOf(lwM2MBootstrapConfig.getBootstrapServer().getSecurityMode())) {
  121 + switch (lwM2MBootstrapConfig.getBootstrapServer().getSecurityMode()) {
123 122 /* Use RPK only */
124 123 case PSK:
125   - store.setSecurityInfo(SecurityInfo.newPreSharedKeyInfo(store.getEndPoint(),
  124 + store.setSecurityInfo(SecurityInfo.newPreSharedKeyInfo(store.getEndpoint(),
126 125 lwM2MBootstrapConfig.getBootstrapServer().getClientPublicKeyOrId(),
127 126 Hex.decodeHex(lwM2MBootstrapConfig.getBootstrapServer().getClientSecretKey().toCharArray())));
128   - store.setSecurityMode(SecurityMode.PSK.code);
  127 + store.setSecurityMode(SecurityMode.PSK);
129 128 break;
130 129 case RPK:
131 130 try {
132   - store.setSecurityInfo(SecurityInfo.newRawPublicKeyInfo(store.getEndPoint(),
  131 + store.setSecurityInfo(SecurityInfo.newRawPublicKeyInfo(store.getEndpoint(),
133 132 SecurityUtil.publicKey.decode(Hex.decodeHex(lwM2MBootstrapConfig.getBootstrapServer().getClientPublicKeyOrId().toCharArray()))));
134   - store.setSecurityMode(SecurityMode.RPK.code);
  133 + store.setSecurityMode(SecurityMode.RPK);
135 134 break;
136 135 } catch (IOException | GeneralSecurityException e) {
137   - log.error("Unable to decode Client public key for [{}] [{}]", store.getEndPoint(), e.getMessage());
  136 + log.error("Unable to decode Client public key for [{}] [{}]", store.getEndpoint(), e.getMessage());
138 137 }
139 138 case X509:
140   - store.setSecurityInfo(SecurityInfo.newX509CertInfo(store.getEndPoint()));
141   - store.setSecurityMode(SecurityMode.X509.code);
  139 + store.setSecurityInfo(SecurityInfo.newX509CertInfo(store.getEndpoint()));
  140 + store.setSecurityMode(SecurityMode.X509);
142 141 break;
143 142 case NO_SEC:
144   - store.setSecurityMode(SecurityMode.NO_SEC.code);
  143 + store.setSecurityMode(SecurityMode.NO_SEC);
145 144 store.setSecurityInfo(null);
146 145 break;
147 146 default:
... ... @@ -153,10 +152,9 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
153 152
154 153 private LwM2MBootstrapConfig getParametersBootstrap(EndpointSecurityInfo store) {
155 154 try {
156   - JsonObject bootstrapJsonCredential = store.getBootstrapJsonCredential();
157   - if (bootstrapJsonCredential != null) {
  155 + LwM2MBootstrapConfig lwM2MBootstrapConfig = store.getBootstrapCredentialConfig();
  156 + if (lwM2MBootstrapConfig != null) {
158 157 ObjectMapper mapper = new ObjectMapper();
159   - LwM2MBootstrapConfig lwM2MBootstrapConfig = mapper.readValue(bootstrapJsonCredential.toString(), LwM2MBootstrapConfig.class);
160 158 JsonObject bootstrapObject = getBootstrapParametersFromThingsboard(store.getDeviceProfile());
161 159 lwM2MBootstrapConfig.servers = mapper.readValue(bootstrapObject.get(SERVERS).toString(), LwM2MBootstrapServers.class);
162 160 LwM2MServerBootstrap profileServerBootstrap = mapper.readValue(bootstrapObject.get(BOOTSTRAP_SERVER).toString(), LwM2MServerBootstrap.class);
... ... @@ -167,22 +165,22 @@ public class LwM2MBootstrapSecurityStore implements BootstrapSecurityStore {
167 165 if (this.getValidatedSecurityMode(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap, lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer)) {
168 166 lwM2MBootstrapConfig.bootstrapServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.bootstrapServer, profileServerBootstrap);
169 167 lwM2MBootstrapConfig.lwm2mServer = new LwM2MServerBootstrap(lwM2MBootstrapConfig.lwm2mServer, profileLwm2mServer);
170   - String logMsg = String.format("%s: getParametersBootstrap: %s Access connect client with bootstrap server.", LOG_LW2M_INFO, store.getEndPoint());
  168 + String logMsg = String.format("%s: getParametersBootstrap: %s Access connect client with bootstrap server.", LOG_LW2M_INFO, store.getEndpoint());
171 169 helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LW2M_TELEMETRY, logMsg), sessionInfo);
172 170 return lwM2MBootstrapConfig;
173 171 } else {
174   - log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndPoint());
175   - log.error("{} getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", LOG_LW2M_ERROR, store.getEndPoint());
176   - String logMsg = String.format("%s: getParametersBootstrap: %s Different values SecurityMode between of client and profile.", LOG_LW2M_ERROR, store.getEndPoint());
  172 + log.error(" [{}] Different values SecurityMode between of client and profile.", store.getEndpoint());
  173 + log.error("{} getParametersBootstrap: [{}] Different values SecurityMode between of client and profile.", LOG_LW2M_ERROR, store.getEndpoint());
  174 + String logMsg = String.format("%s: getParametersBootstrap: %s Different values SecurityMode between of client and profile.", LOG_LW2M_ERROR, store.getEndpoint());
177 175 helper.sendParametersOnThingsboardTelemetry(helper.getKvStringtoThingsboard(LOG_LW2M_TELEMETRY, logMsg), sessionInfo);
178 176 return null;
179 177 }
180 178 }
181 179 } catch (JsonProcessingException e) {
182   - log.error("Unable to decode Json or Certificate for [{}] [{}]", store.getEndPoint(), e.getMessage());
  180 + log.error("Unable to decode Json or Certificate for [{}] [{}]", store.getEndpoint(), e.getMessage());
183 181 return null;
184 182 }
185   - log.error("Unable to decode Json or Certificate for [{}]", store.getEndPoint());
  183 + log.error("Unable to decode Json or Certificate for [{}]", store.getEndpoint());
186 184 return null;
187 185 }
188 186
... ...
... ... @@ -32,7 +32,7 @@ public class LwM2MServerBootstrap {
32 32 String host = "0.0.0.0";
33 33 Integer port = 0;
34 34
35   - String securityMode = SecurityMode.NO_SEC.name();
  35 + SecurityMode securityMode = SecurityMode.NO_SEC;
36 36
37 37 Integer serverId = 123;
38 38 boolean bootstrapServerIs = false;
... ...
... ... @@ -156,7 +156,7 @@ public class LwM2MTransportServerConfig implements LwM2MSecureServerConfig {
156 156 keyStoreValue = KeyStore.getInstance(keyStoreType);
157 157 keyStoreValue.load(inKeyStore, keyStorePassword == null ? null : keyStorePassword.toCharArray());
158 158 } catch (Exception e) {
159   - log.trace("Unable to lookup LwM2M keystore. Reason: {}, {}" , uri, e.getMessage());
  159 + log.info("Unable to lookup LwM2M keystore. Reason: {}, {}" , uri, e.getMessage());
160 160 }
161 161 }
162 162 }
... ...
... ... @@ -15,24 +15,23 @@
15 15 */
16 16 package org.thingsboard.server.transport.lwm2m.secure;
17 17
18   -import com.google.gson.JsonObject;
19 18 import lombok.Data;
  19 +import org.eclipse.leshan.core.SecurityMode;
20 20 import org.eclipse.leshan.server.bootstrap.BootstrapConfig;
21 21 import org.eclipse.leshan.server.security.SecurityInfo;
22 22 import org.thingsboard.server.common.data.DeviceProfile;
23   -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
24   -
25   -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.DEFAULT_MODE;
  23 +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
  24 +import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapConfig;
26 25
27 26 @Data
28 27 public class EndpointSecurityInfo {
29   - private ValidateDeviceCredentialsResponseMsg msg;
  28 + private ValidateDeviceCredentialsResponse msg;
30 29 private SecurityInfo securityInfo;
31   - private int securityMode = DEFAULT_MODE.code;
  30 + private SecurityMode securityMode;
32 31
33 32 /** bootstrap */
34 33 private DeviceProfile deviceProfile;
35   - private JsonObject bootstrapJsonCredential;
36   - private String endPoint;
  34 + private LwM2MBootstrapConfig bootstrapCredentialConfig;
  35 + private String endpoint;
37 36 private BootstrapConfig bootstrapConfig;
38 37 }
... ...
... ... @@ -33,16 +33,6 @@ import java.util.Arrays;
33 33 @Slf4j
34 34 public class LWM2MGenerationPSkRPkECC {
35 35
36   - public LWM2MGenerationPSkRPkECC(Integer dtlsMode) {
37   - switch (LwM2MSecurityMode.fromSecurityMode(dtlsMode)) {
38   - case PSK:
39   - generationPSkKey();
40   - break;
41   - case RPK:
42   - generationRPKECCKey();
43   - }
44   - }
45   -
46 36 public LWM2MGenerationPSkRPkECC() {
47 37 generationPSkKey();
48 38 generationRPKECCKey();
... ... @@ -102,12 +92,12 @@ public class LWM2MGenerationPSkRPkECC {
102 92 /* Get Curves params */
103 93 String privHex = Hex.encodeHexString(privKey.getEncoded());
104 94 log.info("\nCreating new RPK for the next start... \n" +
105   - " Public Key (Hex): [{}]\n" +
106   - " Private Key (Hex): [{}]" +
107   - " public_x : [{}] \n" +
108   - " public_y : [{}] \n" +
109   - " private_encode : [{}] \n" +
110   - " Elliptic Curve parameters : [{}] \n",
  95 + " Public Key (Hex): [{}]\n" +
  96 + " Private Key (Hex): [{}]" +
  97 + " public_x : [{}] \n" +
  98 + " public_y : [{}] \n" +
  99 + " private_encode : [{}] \n" +
  100 + " Elliptic Curve parameters : [{}] \n",
111 101 Hex.encodeHexString(pubKey.getEncoded()),
112 102 privHex,
113 103 Hex.encodeHexString(x),
... ...
1   -/**
2   - * Copyright © 2016-2021 The Thingsboard Authors
3   - *
4   - * Licensed under the Apache License, Version 2.0 (the "License");
5   - * you may not use this file except in compliance with the License.
6   - * You may obtain a copy of the License at
7   - *
8   - * http://www.apache.org/licenses/LICENSE-2.0
9   - *
10   - * Unless required by applicable law or agreed to in writing, software
11   - * distributed under the License is distributed on an "AS IS" BASIS,
12   - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   - * See the License for the specific language governing permissions and
14   - * limitations under the License.
15   - */
16   -package org.thingsboard.server.transport.lwm2m.secure;
17   -
18   -public enum LwM2MSecurityMode {
19   -
20   - PSK(0, "psk"),
21   - RPK(1, "rpk"),
22   - X509(2, "x509"),
23   - NO_SEC(3, "no_sec"),
24   - X509_EST(4, "x509_est"),
25   - REDIS(7, "redis"),
26   - DEFAULT_MODE(255, "default_mode");
27   -
28   - public int code;
29   - public String subEndpoint;
30   -
31   - LwM2MSecurityMode(int code, String subEndpoint) {
32   - this.code = code;
33   - this.subEndpoint = subEndpoint;
34   - }
35   -
36   - public static LwM2MSecurityMode fromSecurityMode(long code) {
37   - return fromSecurityMode((int) code);
38   - }
39   -
40   - public static LwM2MSecurityMode fromSecurityMode(int code) {
41   - for (LwM2MSecurityMode sm : LwM2MSecurityMode.values()) {
42   - if (sm.code == code) {
43   - return sm;
44   - }
45   - }
46   - throw new IllegalArgumentException(String.format("Unsupported security code : %d", code));
47   - }
48   -
49   -
50   - public static LwM2MSecurityMode fromSecurityMode(String subEndpoint) {
51   - for (LwM2MSecurityMode sm : LwM2MSecurityMode.values()) {
52   - if (sm.subEndpoint.equals(subEndpoint)) {
53   - return sm;
54   - }
55   - }
56   - throw new IllegalArgumentException(String.format("Unsupported security subEndpoint : %d", subEndpoint));
57   - }
58   -}
... ... @@ -15,33 +15,36 @@
15 15 */
16 16 package org.thingsboard.server.transport.lwm2m.secure;
17 17
18   -import com.google.gson.JsonObject;
19 18 import lombok.RequiredArgsConstructor;
20 19 import lombok.extern.slf4j.Slf4j;
21   -import org.eclipse.leshan.core.util.Hex;
22 20 import org.eclipse.leshan.core.util.SecurityUtil;
23 21 import org.eclipse.leshan.server.security.SecurityInfo;
24 22 import org.springframework.stereotype.Component;
25   -import org.thingsboard.server.common.data.DeviceProfile;
  23 +import org.thingsboard.common.util.JacksonUtil;
  24 +import org.thingsboard.server.common.data.StringUtils;
  25 +import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode;
26 26 import org.thingsboard.server.common.transport.TransportServiceCallback;
27   -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
  27 +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
28 28 import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg;
29 29 import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
30 30 import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
  31 +import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredentials;
  32 +import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
  33 +import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredentials;
  34 +import org.thingsboard.server.common.data.device.credentials.lwm2m.RPKClientCredentials;
31 35 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
32 36 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
33 37
34 38 import java.io.IOException;
35 39 import java.security.GeneralSecurityException;
36 40 import java.security.PublicKey;
37   -import java.util.Optional;
38 41 import java.util.concurrent.CountDownLatch;
39 42 import java.util.concurrent.TimeUnit;
40 43
41   -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC;
42   -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.PSK;
43   -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.RPK;
44   -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.X509;
  44 +import static org.eclipse.leshan.core.SecurityMode.NO_SEC;
  45 +import static org.eclipse.leshan.core.SecurityMode.PSK;
  46 +import static org.eclipse.leshan.core.SecurityMode.RPK;
  47 +import static org.eclipse.leshan.core.SecurityMode.X509;
45 48
46 49 @Slf4j
47 50 @Component
... ... @@ -52,19 +55,17 @@ public class LwM2mCredentialsSecurityInfoValidator {
52 55 private final LwM2mTransportContext context;
53 56 private final LwM2MTransportServerConfig config;
54 57
55   -
56 58 public EndpointSecurityInfo getEndpointSecurityInfo(String endpoint, LwM2mTransportUtil.LwM2mTypeServer keyValue) {
57 59 CountDownLatch latch = new CountDownLatch(1);
58 60 final EndpointSecurityInfo[] resultSecurityStore = new EndpointSecurityInfo[1];
59 61 context.getTransportService().process(ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(endpoint).build(),
60 62 new TransportServiceCallback<>() {
61 63 @Override
62   - public void onSuccess(ValidateDeviceCredentialsResponseMsg msg) {
63   - String credentialsBody = msg.getCredentialsBody();
  64 + public void onSuccess(ValidateDeviceCredentialsResponse msg) {
  65 + String credentialsBody = msg.getCredentials();
64 66 resultSecurityStore[0] = createSecurityInfo(endpoint, credentialsBody, keyValue);
65 67 resultSecurityStore[0].setMsg(msg);
66   - Optional<DeviceProfile> deviceProfileOpt = LwM2mTransportUtil.decode(msg.getProfileBody().toByteArray());
67   - deviceProfileOpt.ifPresent(profile -> resultSecurityStore[0].setDeviceProfile(profile));
  68 + resultSecurityStore[0].setDeviceProfile(msg.getDeviceProfile());
68 69 latch.countDown();
69 70 }
70 71
... ... @@ -92,39 +93,32 @@ public class LwM2mCredentialsSecurityInfoValidator {
92 93 */
93 94 private EndpointSecurityInfo createSecurityInfo(String endpoint, String jsonStr, LwM2mTransportUtil.LwM2mTypeServer keyValue) {
94 95 EndpointSecurityInfo result = new EndpointSecurityInfo();
95   - JsonObject objectMsg = LwM2mTransportUtil.validateJson(jsonStr);
96   - if (objectMsg != null && !objectMsg.isJsonNull()) {
97   - JsonObject object = (objectMsg.has(keyValue.type) && !objectMsg.get(keyValue.type).isJsonNull()) ? objectMsg.get(keyValue.type).getAsJsonObject() : null;
98   - /**
99   - * Only PSK
100   - */
101   - String endpointPsk = (objectMsg.has("client")
102   - && objectMsg.get("client").getAsJsonObject().has("endpoint")
103   - && objectMsg.get("client").getAsJsonObject().get("endpoint").isJsonPrimitive()) ? objectMsg.get("client").getAsJsonObject().get("endpoint").getAsString() : null;
104   - endpoint = (endpointPsk == null || endpointPsk.isEmpty()) ? endpoint : endpointPsk;
105   - if (object != null && !object.isJsonNull()) {
106   - if (keyValue.equals(LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP)) {
107   - result.setBootstrapJsonCredential(object);
108   - result.setEndPoint(endpoint);
109   - result.setSecurityMode(LwM2MSecurityMode.fromSecurityMode(object.get("bootstrapServer").getAsJsonObject().get("securityMode").getAsString().toLowerCase()).code);
110   - } else {
111   - LwM2MSecurityMode lwM2MSecurityMode = LwM2MSecurityMode.fromSecurityMode(object.get("securityConfigClientMode").getAsString().toLowerCase());
112   - switch (lwM2MSecurityMode) {
113   - case NO_SEC:
114   - createClientSecurityInfoNoSec(result);
115   - break;
116   - case PSK:
117   - createClientSecurityInfoPSK(result, endpoint, object);
118   - break;
119   - case RPK:
120   - createClientSecurityInfoRPK(result, endpoint, object);
121   - break;
122   - case X509:
123   - createClientSecurityInfoX509(result, endpoint);
124   - break;
125   - default:
126   - break;
127   - }
  96 + LwM2MCredentials credentials = JacksonUtil.fromString(jsonStr, LwM2MCredentials.class);
  97 + if (credentials != null) {
  98 + if (keyValue.equals(LwM2mTransportUtil.LwM2mTypeServer.BOOTSTRAP)) {
  99 + result.setBootstrapCredentialConfig(credentials.getBootstrap());
  100 + if (LwM2MSecurityMode.PSK.equals(credentials.getClient().getSecurityConfigClientMode())) {
  101 + PSKClientCredentials pskClientConfig = (PSKClientCredentials) credentials.getClient();
  102 + endpoint = StringUtils.isNotEmpty(pskClientConfig.getEndpoint()) ? pskClientConfig.getEndpoint() : endpoint;
  103 + }
  104 + result.setEndpoint(endpoint);
  105 + result.setSecurityMode(credentials.getBootstrap().getBootstrapServer().getSecurityMode());
  106 + } else {
  107 + switch (credentials.getClient().getSecurityConfigClientMode()) {
  108 + case NO_SEC:
  109 + createClientSecurityInfoNoSec(result);
  110 + break;
  111 + case PSK:
  112 + createClientSecurityInfoPSK(result, endpoint, credentials.getClient());
  113 + break;
  114 + case RPK:
  115 + createClientSecurityInfoRPK(result, endpoint, credentials.getClient());
  116 + break;
  117 + case X509:
  118 + createClientSecurityInfoX509(result, endpoint, credentials.getClient());
  119 + break;
  120 + default:
  121 + break;
128 122 }
129 123 }
130 124 }
... ... @@ -133,19 +127,18 @@ public class LwM2mCredentialsSecurityInfoValidator {
133 127
134 128 private void createClientSecurityInfoNoSec(EndpointSecurityInfo result) {
135 129 result.setSecurityInfo(null);
136   - result.setSecurityMode(NO_SEC.code);
  130 + result.setSecurityMode(NO_SEC);
137 131 }
138 132
139   - private void createClientSecurityInfoPSK(EndpointSecurityInfo result, String endpoint, JsonObject object) {
140   - /** PSK Deserialization */
141   - String identity = (object.has("identity") && object.get("identity").isJsonPrimitive()) ? object.get("identity").getAsString() : null;
142   - if (identity != null && !identity.isEmpty()) {
  133 + private void createClientSecurityInfoPSK(EndpointSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) {
  134 + PSKClientCredentials pskConfig = (PSKClientCredentials) clientCredentialsConfig;
  135 + if (StringUtils.isNotEmpty(pskConfig.getIdentity())) {
143 136 try {
144   - byte[] key = (object.has("key") && object.get("key").isJsonPrimitive()) ? Hex.decodeHex(object.get("key").getAsString().toCharArray()) : null;
145   - if (key != null && key.length > 0) {
  137 + if (pskConfig.getKey() != null && pskConfig.getKey().length > 0) {
  138 + endpoint = StringUtils.isNotEmpty(pskConfig.getEndpoint()) ? pskConfig.getEndpoint() : endpoint;
146 139 if (endpoint != null && !endpoint.isEmpty()) {
147   - result.setSecurityInfo(SecurityInfo.newPreSharedKeyInfo(endpoint, identity, key));
148   - result.setSecurityMode(PSK.code);
  140 + result.setSecurityInfo(SecurityInfo.newPreSharedKeyInfo(endpoint, pskConfig.getIdentity(), pskConfig.getKey()));
  141 + result.setSecurityMode(PSK);
149 142 }
150 143 }
151 144 } catch (IllegalArgumentException e) {
... ... @@ -156,13 +149,13 @@ public class LwM2mCredentialsSecurityInfoValidator {
156 149 }
157 150 }
158 151
159   - private void createClientSecurityInfoRPK(EndpointSecurityInfo result, String endpoint, JsonObject object) {
  152 + private void createClientSecurityInfoRPK(EndpointSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) {
  153 + RPKClientCredentials rpkConfig = (RPKClientCredentials) clientCredentialsConfig;
160 154 try {
161   - if (object.has("key") && object.get("key").isJsonPrimitive()) {
162   - byte[] rpkkey = Hex.decodeHex(object.get("key").getAsString().toLowerCase().toCharArray());
163   - PublicKey key = SecurityUtil.publicKey.decode(rpkkey);
  155 + if (rpkConfig.getKey() != null) {
  156 + PublicKey key = SecurityUtil.publicKey.decode(rpkConfig.getKey());
164 157 result.setSecurityInfo(SecurityInfo.newRawPublicKeyInfo(endpoint, key));
165   - result.setSecurityMode(RPK.code);
  158 + result.setSecurityMode(RPK);
166 159 } else {
167 160 log.error("Missing RPK key");
168 161 }
... ... @@ -171,8 +164,8 @@ public class LwM2mCredentialsSecurityInfoValidator {
171 164 }
172 165 }
173 166
174   - private void createClientSecurityInfoX509(EndpointSecurityInfo result, String endpoint) {
  167 + private void createClientSecurityInfoX509(EndpointSecurityInfo result, String endpoint, LwM2MClientCredentials clientCredentialsConfig) {
175 168 result.setSecurityInfo(SecurityInfo.newX509CertInfo(endpoint));
176   - result.setSecurityMode(X509.code);
  169 + result.setSecurityMode(X509);
177 170 }
178 171 }
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.secure;
  17 +
  18 +import lombok.RequiredArgsConstructor;
  19 +import org.eclipse.leshan.core.request.Identity;
  20 +import org.eclipse.leshan.core.request.UplinkRequest;
  21 +import org.eclipse.leshan.server.registration.Registration;
  22 +import org.eclipse.leshan.server.security.Authorizer;
  23 +import org.eclipse.leshan.server.security.SecurityChecker;
  24 +import org.eclipse.leshan.server.security.SecurityInfo;
  25 +import org.springframework.stereotype.Component;
  26 +import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
  27 +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
  28 +import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore;
  29 +import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2mSecurityStore;
  30 +
  31 +@Component
  32 +@RequiredArgsConstructor
  33 +@TbLwM2mTransportComponent
  34 +public class TbLwM2MAuthorizer implements Authorizer {
  35 +
  36 + private final TbLwM2MDtlsSessionStore sessionStorage;
  37 + private final TbLwM2mSecurityStore securityStore;
  38 + private final SecurityChecker securityChecker = new SecurityChecker();
  39 + private final LwM2mClientContext clientContext;
  40 +
  41 + @Override
  42 + public Registration isAuthorized(UplinkRequest<?> request, Registration registration, Identity senderIdentity) {
  43 + if (senderIdentity.isX509()) {
  44 + TbX509DtlsSessionInfo sessionInfo = sessionStorage.get(registration.getEndpoint());
  45 + if (sessionInfo != null) {
  46 + if (sessionInfo.getX509CommonName().endsWith(senderIdentity.getX509CommonName())) {
  47 + clientContext.registerClient(registration, sessionInfo.getCredentials());
  48 + // X509 certificate is valid and matches endpoint.
  49 + return registration;
  50 + } else {
  51 + // X509 certificate is not valid.
  52 + return null;
  53 + }
  54 + }
  55 + // If session info is not found, this may be the trusted certificate, so we still need to check all other options below.
  56 + }
  57 + SecurityInfo expectedSecurityInfo = null;
  58 + if (securityStore != null) {
  59 + expectedSecurityInfo = securityStore.getByEndpoint(registration.getEndpoint());
  60 + }
  61 + if (securityChecker.checkSecurityInfo(registration.getEndpoint(), senderIdentity, expectedSecurityInfo)) {
  62 + return registration;
  63 + } else {
  64 + return null;
  65 + }
  66 + }
  67 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.secure;
  17 +
  18 +import lombok.RequiredArgsConstructor;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.eclipse.californium.elements.util.CertPathUtil;
  21 +import org.eclipse.californium.scandium.dtls.AlertMessage;
  22 +import org.eclipse.californium.scandium.dtls.CertificateMessage;
  23 +import org.eclipse.californium.scandium.dtls.CertificateType;
  24 +import org.eclipse.californium.scandium.dtls.CertificateVerificationResult;
  25 +import org.eclipse.californium.scandium.dtls.ConnectionId;
  26 +import org.eclipse.californium.scandium.dtls.DTLSSession;
  27 +import org.eclipse.californium.scandium.dtls.HandshakeException;
  28 +import org.eclipse.californium.scandium.dtls.HandshakeResultHandler;
  29 +import org.eclipse.californium.scandium.dtls.x509.NewAdvancedCertificateVerifier;
  30 +import org.eclipse.californium.scandium.dtls.x509.StaticCertificateVerifier;
  31 +import org.eclipse.californium.scandium.util.ServerNames;
  32 +import org.springframework.beans.factory.annotation.Value;
  33 +import org.springframework.stereotype.Component;
  34 +import org.springframework.util.StringUtils;
  35 +import org.thingsboard.common.util.JacksonUtil;
  36 +import org.thingsboard.server.common.data.DeviceProfile;
  37 +import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MSecurityMode;
  38 +import org.thingsboard.server.common.msg.EncryptionUtil;
  39 +import org.thingsboard.server.common.transport.TransportService;
  40 +import org.thingsboard.server.common.transport.TransportServiceCallback;
  41 +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
  42 +import org.thingsboard.server.common.transport.util.SslUtil;
  43 +import org.thingsboard.server.gen.transport.TransportProtos;
  44 +import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
  45 +import org.thingsboard.server.transport.lwm2m.secure.credentials.LwM2MCredentials;
  46 +import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredentials;
  47 +import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore;
  48 +
  49 +import javax.annotation.PostConstruct;
  50 +import javax.security.auth.x500.X500Principal;
  51 +import java.security.PublicKey;
  52 +import java.security.cert.CertPath;
  53 +import java.security.cert.CertificateEncodingException;
  54 +import java.security.cert.CertificateExpiredException;
  55 +import java.security.cert.CertificateNotYetValidException;
  56 +import java.security.cert.X509Certificate;
  57 +import java.util.Arrays;
  58 +import java.util.List;
  59 +import java.util.concurrent.CountDownLatch;
  60 +import java.util.concurrent.TimeUnit;
  61 +
  62 +@Slf4j
  63 +@Component
  64 +@RequiredArgsConstructor
  65 +public class TbLwM2MDtlsCertificateVerifier implements NewAdvancedCertificateVerifier {
  66 +
  67 + private final TransportService transportService;
  68 + private final TbLwM2MDtlsSessionStore sessionStorage;
  69 + private final LwM2MTransportServerConfig config;
  70 +
  71 + @SuppressWarnings("deprecation")
  72 + private StaticCertificateVerifier staticCertificateVerifier;
  73 +
  74 + @Value("${transport.lwm2m.server.security.skip_validity_check_for_client_cert:false}")
  75 + private boolean skipValidityCheckForClientCert;
  76 +
  77 + @Override
  78 + public List<CertificateType> getSupportedCertificateType() {
  79 + return Arrays.asList(CertificateType.X_509, CertificateType.RAW_PUBLIC_KEY);
  80 + }
  81 +
  82 + @PostConstruct
  83 + public void init() {
  84 + try {
  85 + /* by default trust all */
  86 + X509Certificate[] trustedCertificates = new X509Certificate[0];
  87 + if (config.getKeyStoreValue() != null) {
  88 + X509Certificate rootCAX509Cert = (X509Certificate) config.getKeyStoreValue().getCertificate(config.getRootCertificateAlias());
  89 + if (rootCAX509Cert != null) {
  90 + trustedCertificates = new X509Certificate[1];
  91 + trustedCertificates[0] = rootCAX509Cert;
  92 + }
  93 + }
  94 + staticCertificateVerifier = new StaticCertificateVerifier(trustedCertificates);
  95 + } catch (Exception e) {
  96 + log.info("Failed to initialize the ");
  97 + }
  98 + }
  99 +
  100 + @Override
  101 + public CertificateVerificationResult verifyCertificate(ConnectionId cid, ServerNames serverName, Boolean clientUsage, boolean truncateCertificatePath, CertificateMessage message, DTLSSession session) {
  102 + CertPath certChain = message.getCertificateChain();
  103 + if (certChain == null) {
  104 + //We trust all RPK on this layer, and use TbLwM2MAuthorizer
  105 + PublicKey publicKey = message.getPublicKey();
  106 + return new CertificateVerificationResult(cid, publicKey, null);
  107 + } else {
  108 + try {
  109 + boolean x509CredentialsFound = false;
  110 + CertPath certpath = message.getCertificateChain();
  111 + X509Certificate[] chain = certpath.getCertificates().toArray(new X509Certificate[0]);
  112 + for (X509Certificate cert : chain) {
  113 + try {
  114 + if (!skipValidityCheckForClientCert) {
  115 + cert.checkValidity();
  116 + }
  117 +
  118 + String strCert = SslUtil.getCertificateString(cert);
  119 + String sha3Hash = EncryptionUtil.getSha3Hash(strCert);
  120 + final ValidateDeviceCredentialsResponse[] deviceCredentialsResponse = new ValidateDeviceCredentialsResponse[1];
  121 + CountDownLatch latch = new CountDownLatch(1);
  122 + transportService.process(TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg.newBuilder().setCredentialsId(sha3Hash).build(),
  123 + new TransportServiceCallback<>() {
  124 + @Override
  125 + public void onSuccess(ValidateDeviceCredentialsResponse msg) {
  126 + if (!StringUtils.isEmpty(msg.getCredentials())) {
  127 + deviceCredentialsResponse[0] = msg;
  128 + }
  129 + latch.countDown();
  130 + }
  131 +
  132 + @Override
  133 + public void onError(Throwable e) {
  134 + log.error(e.getMessage(), e);
  135 + latch.countDown();
  136 + }
  137 + });
  138 + if (latch.await(10, TimeUnit.SECONDS)) {
  139 + ValidateDeviceCredentialsResponse msg = deviceCredentialsResponse[0];
  140 + if (msg != null && org.thingsboard.server.common.data.StringUtils.isNotEmpty(msg.getCredentials())) {
  141 + LwM2MCredentials credentials = JacksonUtil.fromString(msg.getCredentials(), LwM2MCredentials.class);
  142 + if(!credentials.getClient().getSecurityConfigClientMode().equals(LwM2MSecurityMode.X509)){
  143 + continue;
  144 + }
  145 + X509ClientCredentials config = (X509ClientCredentials) credentials.getClient();
  146 + String certBody = config.getCert();
  147 + String endpoint = config.getEndpoint();
  148 + if (strCert.equals(certBody)) {
  149 + x509CredentialsFound = true;
  150 + DeviceProfile deviceProfile = msg.getDeviceProfile();
  151 + if (msg.hasDeviceInfo() && deviceProfile != null) {
  152 + sessionStorage.put(endpoint, new TbX509DtlsSessionInfo(cert.getSubjectX500Principal().getName(), msg));
  153 + break;
  154 + }
  155 + } else {
  156 + log.trace("[{}][{}] Certificate mismatch. Expected: {}, Actual: {}", endpoint, sha3Hash, strCert, certBody);
  157 + }
  158 + }
  159 + }
  160 + } catch (InterruptedException |
  161 + CertificateEncodingException |
  162 + CertificateExpiredException |
  163 + CertificateNotYetValidException e) {
  164 + log.error(e.getMessage(), e);
  165 + }
  166 + }
  167 + if (!x509CredentialsFound) {
  168 + if (staticCertificateVerifier != null) {
  169 + staticCertificateVerifier.verifyCertificate(message, session);
  170 + } else {
  171 + AlertMessage alert = new AlertMessage(AlertMessage.AlertLevel.FATAL, AlertMessage.AlertDescription.INTERNAL_ERROR,
  172 + session.getPeer());
  173 + throw new HandshakeException("x509 verification not enabled!", alert);
  174 + }
  175 + }
  176 + return new CertificateVerificationResult(cid, certpath, null);
  177 + } catch (HandshakeException e) {
  178 + log.trace("Certificate validation failed!", e);
  179 + return new CertificateVerificationResult(cid, e, null);
  180 + }
  181 + }
  182 + }
  183 +
  184 + @Override
  185 + public List<X500Principal> getAcceptedIssuers() {
  186 + return CertPathUtil.toSubjects(null);
  187 + }
  188 +
  189 + @Override
  190 + public void setResultHandler(HandshakeResultHandler resultHandler) {
  191 +
  192 + }
  193 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.secure;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
  20 +
  21 +@Data
  22 +public class TbX509DtlsSessionInfo {
  23 +
  24 + private final String x509CommonName;
  25 + private final ValidateDeviceCredentialsResponse credentials;
  26 +
  27 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.secure.credentials;
  17 +
  18 +import lombok.Data;
  19 +import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredentials;
  20 +import org.thingsboard.server.transport.lwm2m.bootstrap.secure.LwM2MBootstrapConfig;
  21 +
  22 +@Data
  23 +public class LwM2MCredentials {
  24 + private LwM2MClientCredentials client;
  25 + private LwM2MBootstrapConfig bootstrap;
  26 +}
... ...
... ... @@ -63,6 +63,7 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientProfile;
63 63 import org.thingsboard.server.transport.lwm2m.server.client.Lwm2mClientRpcRequest;
64 64 import org.thingsboard.server.transport.lwm2m.server.client.ResultsAddKeyValueProto;
65 65 import org.thingsboard.server.transport.lwm2m.server.client.ResultsAnalyzerParameters;
  66 +import org.thingsboard.server.transport.lwm2m.server.store.TbLwM2MDtlsSessionStore;
66 67 import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
67 68
68 69 import javax.annotation.PostConstruct;
... ... @@ -133,13 +134,15 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
133 134 public final LwM2mTransportServerHelper helper;
134 135 private final LwM2MJsonAdaptor adaptor;
135 136 private final LwM2mClientContext clientContext;
136   - public final LwM2mTransportRequest lwM2mTransportRequest;
  137 + private final LwM2mTransportRequest lwM2mTransportRequest;
  138 + private final TbLwM2MDtlsSessionStore sessionStore;
  139 +
137 140
138 141 public DefaultLwM2MTransportMsgHandler(TransportService transportService, LwM2MTransportServerConfig config, LwM2mTransportServerHelper helper,
139 142 LwM2mClientContext clientContext,
140 143 @Lazy LwM2mTransportRequest lwM2mTransportRequest,
141 144 FirmwareDataCache firmwareDataCache,
142   - LwM2mTransportContext context, LwM2MJsonAdaptor adaptor) {
  145 + LwM2mTransportContext context, LwM2MJsonAdaptor adaptor, TbLwM2MDtlsSessionStore sessionStore) {
143 146 this.transportService = transportService;
144 147 this.config = config;
145 148 this.helper = helper;
... ... @@ -148,6 +151,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
148 151 this.firmwareDataCache = firmwareDataCache;
149 152 this.context = context;
150 153 this.adaptor = adaptor;
  154 + this.sessionStore = sessionStore;
151 155 }
152 156
153 157 @PostConstruct
... ... @@ -245,7 +249,8 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
245 249 try {
246 250 this.setCancelObservationsAll(registration);
247 251 this.sendLogsToThingsboard(LOG_LW2M_INFO + ": Client unRegistration", registration.getId());
248   - this.closeClientSession(registration); ;
  252 + this.closeClientSession(registration);
  253 + ;
249 254 } catch (Throwable t) {
250 255 log.error("[{}] endpoint [{}] error Unable un registration.", registration.getEndpoint(), t);
251 256 this.sendLogsToThingsboard(LOG_LW2M_ERROR + String.format(": Client Unable un Registration, %s", t.getMessage()), registration.getId());
... ... @@ -257,6 +262,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
257 262 SessionInfoProto sessionInfo = this.getSessionInfoOrCloseSession(registration);
258 263 if (sessionInfo != null) {
259 264 transportService.deregisterSession(sessionInfo);
  265 + sessionStore.remove(registration.getEndpoint());
260 266 this.doCloseSession(sessionInfo);
261 267 clientContext.removeClientByRegistrationId(registration.getId());
262 268 log.info("Client close session: [{}] unReg [{}] name [{}] profile ", registration.getId(), registration.getEndpoint(), sessionInfo.getDeviceType());
... ... @@ -326,7 +332,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
326 332 } else if (response.getContent() instanceof LwM2mObjectInstance) {
327 333 value = lwM2MClient.instanceToString((LwM2mObjectInstance) response.getContent(), this.converter, pathIdVer);
328 334 } else if (response.getContent() instanceof LwM2mResource) {
329   - value = lwM2MClient.resourceToString ((LwM2mResource) response.getContent(), this.converter, pathIdVer);
  335 + value = lwM2MClient.resourceToString((LwM2mResource) response.getContent(), this.converter, pathIdVer);
330 336 }
331 337 String msg = String.format("%s: type operation %s path - %s value - %s", LOG_LW2M_INFO,
332 338 READ, pathIdVer, value);
... ... @@ -711,16 +717,15 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
711 717 */
712 718 private void updateResourcesValue(Registration registration, LwM2mResource lwM2mResource, String path) {
713 719 LwM2mClient lwM2MClient = clientContext.getOrRegister(registration);
714   - if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.config
715   - .getModelProvider())) {
  720 + if (lwM2MClient.saveResourceValue(path, lwM2mResource, this.config.getModelProvider())) {
716 721 /** version != null
717 722 * set setClient_fw_info... = value
718 723 **/
719 724 if (lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) {
720   - lwM2MClient.getFwUpdate().initReadValue(this, path);
  725 + lwM2MClient.getFwUpdate().initReadValue(this, lwM2mTransportRequest, path);
721 726 }
722 727 if (lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) {
723   - lwM2MClient.getSwUpdate().initReadValue(this, path);
  728 + lwM2MClient.getSwUpdate().initReadValue(this, lwM2mTransportRequest, path);
724 729 }
725 730
726 731 /**
... ... @@ -737,13 +742,13 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
737 742 && (convertPathFromObjectIdToIdVer(FW_RESULT_ID, registration).equals(path))) {
738 743 if (DOWNLOADED.name().equals(lwM2MClient.getFwUpdate().getStateUpdate())
739 744 && lwM2MClient.getFwUpdate().conditionalFwExecuteStart()) {
740   - lwM2MClient.getFwUpdate().executeFwSwWare();
  745 + lwM2MClient.getFwUpdate().executeFwSwWare(this, lwM2mTransportRequest);
741 746 } else if (UPDATING.name().equals(lwM2MClient.getFwUpdate().getStateUpdate())
742 747 && lwM2MClient.getFwUpdate().conditionalFwExecuteAfterSuccess()) {
743   - lwM2MClient.getFwUpdate().finishFwSwUpdate(true);
  748 + lwM2MClient.getFwUpdate().finishFwSwUpdate(this, true);
744 749 } else if (UPDATING.name().equals(lwM2MClient.getFwUpdate().getStateUpdate())
745 750 && lwM2MClient.getFwUpdate().conditionalFwExecuteAfterError()) {
746   - lwM2MClient.getFwUpdate().finishFwSwUpdate(false);
  751 + lwM2MClient.getFwUpdate().finishFwSwUpdate(this, false);
747 752 }
748 753 }
749 754
... ... @@ -762,13 +767,13 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
762 767 && (convertPathFromObjectIdToIdVer(SW_RESULT_ID, registration).equals(path))) {
763 768 if (DOWNLOADED.name().equals(lwM2MClient.getSwUpdate().getStateUpdate())
764 769 && lwM2MClient.getSwUpdate().conditionalSwUpdateExecute()) {
765   - lwM2MClient.getSwUpdate().executeFwSwWare();
  770 + lwM2MClient.getSwUpdate().executeFwSwWare(this, lwM2mTransportRequest);
766 771 } else if (UPDATING.name().equals(lwM2MClient.getSwUpdate().getStateUpdate())
767 772 && lwM2MClient.getSwUpdate().conditionalSwExecuteAfterSuccess()) {
768   - lwM2MClient.getSwUpdate().finishFwSwUpdate(true);
  773 + lwM2MClient.getSwUpdate().finishFwSwUpdate(this, true);
769 774 } else if (UPDATING.name().equals(lwM2MClient.getSwUpdate().getStateUpdate())
770 775 && lwM2MClient.getSwUpdate().conditionalSwExecuteAfterError()) {
771   - lwM2MClient.getSwUpdate().finishFwSwUpdate(false);
  776 + lwM2MClient.getSwUpdate().finishFwSwUpdate(this, false);
772 777 }
773 778 }
774 779 Set<String> paths = new HashSet<>();
... ... @@ -1422,7 +1427,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
1422 1427 lwM2MClient.getFwUpdate().setCurrentVersion(response.getVersion());
1423 1428 lwM2MClient.getFwUpdate().setCurrentTitle(response.getTitle());
1424 1429 lwM2MClient.getFwUpdate().setCurrentId(new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())).getId());
1425   - lwM2MClient.getFwUpdate().sendReadObserveInfo(serviceImpl);
  1430 + lwM2MClient.getFwUpdate().sendReadObserveInfo(lwM2mTransportRequest);
1426 1431 } else {
1427 1432 log.trace("Firmware [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString());
1428 1433 }
... ... @@ -1451,7 +1456,7 @@ public class DefaultLwM2MTransportMsgHandler implements LwM2mTransportMsgHandler
1451 1456 lwM2MClient.getSwUpdate().setCurrentVersion(response.getVersion());
1452 1457 lwM2MClient.getSwUpdate().setCurrentTitle(response.getTitle());
1453 1458 lwM2MClient.getSwUpdate().setCurrentId(new FirmwareId(new UUID(response.getFirmwareIdMSB(), response.getFirmwareIdLSB())).getId());
1454   - lwM2MClient.getSwUpdate().sendReadObserveInfo(serviceImpl);
  1459 + lwM2MClient.getSwUpdate().sendReadObserveInfo(lwM2mTransportRequest);
1455 1460 } else {
1456 1461 log.trace("Software [{}] [{}]", lwM2MClient.getDeviceName(), response.getResponseStatus().toString());
1457 1462 }
... ...
... ... @@ -18,6 +18,7 @@ package org.thingsboard.server.transport.lwm2m.server;
18 18 import lombok.RequiredArgsConstructor;
19 19 import lombok.extern.slf4j.Slf4j;
20 20 import org.eclipse.californium.scandium.config.DtlsConnectorConfig;
  21 +import org.eclipse.californium.scandium.dtls.cipher.CipherSuite;
21 22 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeDecoder;
22 23 import org.eclipse.leshan.core.node.codec.DefaultLwM2mNodeEncoder;
23 24 import org.eclipse.leshan.core.util.Hex;
... ... @@ -25,15 +26,15 @@ import org.eclipse.leshan.server.californium.LeshanServer;
25 26 import org.eclipse.leshan.server.californium.LeshanServerBuilder;
26 27 import org.eclipse.leshan.server.californium.registration.CaliforniumRegistrationStore;
27 28 import org.eclipse.leshan.server.model.LwM2mModelProvider;
28   -import org.eclipse.leshan.server.security.DefaultAuthorizer;
29 29 import org.eclipse.leshan.server.security.EditableSecurityStore;
30   -import org.eclipse.leshan.server.security.SecurityChecker;
31 30 import org.springframework.stereotype.Component;
32 31 import org.thingsboard.common.util.ThingsBoardThreadFactory;
33 32 import org.thingsboard.server.common.data.StringUtils;
34 33 import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
35 34 import org.thingsboard.server.transport.lwm2m.config.LwM2MTransportServerConfig;
36 35 import org.thingsboard.server.transport.lwm2m.secure.LWM2MGenerationPSkRPkECC;
  36 +import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MAuthorizer;
  37 +import org.thingsboard.server.transport.lwm2m.secure.TbLwM2MDtlsCertificateVerifier;
37 38 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
38 39 import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
39 40
... ... @@ -42,7 +43,6 @@ import javax.annotation.PreDestroy;
42 43 import java.math.BigInteger;
43 44 import java.security.AlgorithmParameters;
44 45 import java.security.KeyFactory;
45   -import java.security.KeyStoreException;
46 46 import java.security.NoSuchAlgorithmException;
47 47 import java.security.PrivateKey;
48 48 import java.security.PublicKey;
... ... @@ -73,9 +73,10 @@ import static org.thingsboard.server.transport.lwm2m.server.LwM2mNetworkConfig.g
73 73 @RequiredArgsConstructor
74 74 public class DefaultLwM2mTransportService implements LwM2MTransportService {
75 75
  76 + public static final CipherSuite[] RPK_OR_X509_CIPHER_SUITES = {TLS_PSK_WITH_AES_128_CCM_8, TLS_PSK_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256};
  77 + public static final CipherSuite[] PSK_CIPHER_SUITES = {TLS_PSK_WITH_AES_128_CCM_8, TLS_PSK_WITH_AES_128_CBC_SHA256};
76 78 private PublicKey publicKey;
77 79 private PrivateKey privateKey;
78   - private boolean pskMode = false;
79 80
80 81 private final LwM2mTransportContext context;
81 82 private final LwM2MTransportServerConfig config;
... ... @@ -84,7 +85,8 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
84 85 private final CaliforniumRegistrationStore registrationStore;
85 86 private final EditableSecurityStore securityStore;
86 87 private final LwM2mClientContext lwM2mClientContext;
87   - private ScheduledExecutorService registrationStoreExecutor;
  88 + private final TbLwM2MDtlsCertificateVerifier certificateVerifier;
  89 + private final TbLwM2MAuthorizer authorizer;
88 90
89 91 private LeshanServer server;
90 92
... ... @@ -116,8 +118,6 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
116 118 }
117 119
118 120 private LeshanServer getLhServer() {
119   - this.registrationStoreExecutor = Executors.newScheduledThreadPool(this.config.getRegistrationStorePoolSize(), ThingsBoardThreadFactory.forName("LwM2M registrationStore"));
120   -
121 121 LeshanServerBuilder builder = new LeshanServerBuilder();
122 122 builder.setLocalAddress(config.getHost(), config.getPort());
123 123 builder.setLocalSecureAddress(config.getSecureHost(), config.getSecurePort());
... ... @@ -125,10 +125,6 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
125 125 /* Use a magic converter to support bad type send by the UI. */
126 126 builder.setEncoder(new DefaultLwM2mNodeEncoder(LwM2mValueConverterImpl.getInstance()));
127 127
128   - /* InMemoryRegistrationStore(ScheduledExecutorService schedExecutor, long cleanPeriodInSec) */
129   -//// InMemoryRegistrationStore registrationStore = new InMemoryRegistrationStore(this.registrationStoreExecutor, this.config.getCleanPeriodInSec());
130   -// builder.setRegistrationStore(registrationStore);
131   -
132 128 /* Create CoAP Config */
133 129 builder.setCoapConfig(getCoapConfig(config.getPort(), config.getSecurePort()));
134 130
... ... @@ -137,9 +133,6 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
137 133 config.setModelProvider(modelProvider);
138 134 builder.setObjectModelProvider(modelProvider);
139 135
140   - /* Create credentials */
141   - this.setServerWithCredentials(builder);
142   -
143 136 /* Set securityStore with new registrationStore */
144 137 builder.setSecurityStore(securityStore);
145 138 builder.setRegistrationStore(registrationStore);
... ... @@ -151,18 +144,8 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
151 144 dtlsConfig.setServerOnly(true);
152 145 dtlsConfig.setRecommendedSupportedGroupsOnly(config.isRecommendedSupportedGroups());
153 146 dtlsConfig.setRecommendedCipherSuitesOnly(config.isRecommendedCiphers());
154   - if (this.pskMode) {
155   - dtlsConfig.setSupportedCipherSuites(
156   - TLS_PSK_WITH_AES_128_CCM_8,
157   - TLS_PSK_WITH_AES_128_CBC_SHA256);
158   - } else {
159   - dtlsConfig.setSupportedCipherSuites(
160   - TLS_PSK_WITH_AES_128_CCM_8,
161   - TLS_PSK_WITH_AES_128_CBC_SHA256,
162   - TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
163   - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256);
164   - }
165   -
  147 + /* Create credentials */
  148 + this.setServerWithCredentials(builder, dtlsConfig);
166 149
167 150 /* Set DTLS Config */
168 151 builder.setDtlsConfig(dtlsConfig);
... ... @@ -171,40 +154,21 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
171 154 return builder.build();
172 155 }
173 156
174   - private void setServerWithCredentials(LeshanServerBuilder builder) {
175   - try {
176   - if (config.getKeyStoreValue() != null) {
177   - if (this.setBuilderX509(builder)) {
178   - X509Certificate rootCAX509Cert = (X509Certificate) config.getKeyStoreValue().getCertificate(config.getRootCertificateAlias());
179   - if (rootCAX509Cert != null) {
180   - X509Certificate[] trustedCertificates = new X509Certificate[1];
181   - trustedCertificates[0] = rootCAX509Cert;
182   - builder.setTrustedCertificates(trustedCertificates);
183   - } else {
184   - /* by default trust all */
185   - builder.setTrustedCertificates(new X509Certificate[0]);
186   - }
187   - /* Set securityStore with registrationStore*/
188   - builder.setAuthorizer(new DefaultAuthorizer(securityStore, new SecurityChecker() {
189   - @Override
190   - protected boolean matchX509Identity(String endpoint, String receivedX509CommonName,
191   - String expectedX509CommonName) {
192   - return endpoint.startsWith(expectedX509CommonName);
193   - }
194   - }));
195   - }
196   - } else if (this.setServerRPK(builder)) {
197   - this.infoPramsUri("RPK");
198   - this.infoParamsServerKey(this.publicKey, this.privateKey);
199   - } else {
200   - /* by default trust all */
201   - builder.setTrustedCertificates(new X509Certificate[0]);
202   - log.info("Unable to load X509 files for LWM2MServer");
203   - this.pskMode = true;
204   - this.infoPramsUri("PSK");
205   - }
206   - } catch (KeyStoreException ex) {
207   - log.error("[{}] Unable to load X509 files server", ex.getMessage());
  157 + private void setServerWithCredentials(LeshanServerBuilder builder, DtlsConnectorConfig.Builder dtlsConfig) {
  158 + if (config.getKeyStoreValue() != null && this.setBuilderX509(builder)) {
  159 + dtlsConfig.setAdvancedCertificateVerifier(certificateVerifier);
  160 + builder.setAuthorizer(authorizer);
  161 + dtlsConfig.setSupportedCipherSuites(RPK_OR_X509_CIPHER_SUITES);
  162 + } else if (this.setServerRPK(builder)) {
  163 + this.infoPramsUri("RPK");
  164 + this.infoParamsServerKey(this.publicKey, this.privateKey);
  165 + dtlsConfig.setSupportedCipherSuites(RPK_OR_X509_CIPHER_SUITES);
  166 + } else {
  167 + /* by default trust all */
  168 + builder.setTrustedCertificates(new X509Certificate[0]);
  169 + log.info("Unable to load X509 files for LWM2MServer");
  170 + dtlsConfig.setSupportedCipherSuites(PSK_CIPHER_SUITES);
  171 + this.infoPramsUri("PSK");
208 172 }
209 173 }
210 174
... ... @@ -250,7 +214,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
250 214
251 215 private boolean setServerRPK(LeshanServerBuilder builder) {
252 216 try {
253   - this.generateKeyForRPK();
  217 + this.loadOrGenerateRPKKeys();
254 218 if (this.publicKey != null && this.publicKey.getEncoded().length > 0 &&
255 219 this.privateKey != null && this.privateKey.getEncoded().length > 0) {
256 220 builder.setPublicKey(this.publicKey);
... ... @@ -263,7 +227,7 @@ public class DefaultLwM2mTransportService implements LwM2MTransportService {
263 227 return false;
264 228 }
265 229
266   - private void generateKeyForRPK() throws NoSuchAlgorithmException, InvalidParameterSpecException, InvalidKeySpecException {
  230 + private void loadOrGenerateRPKKeys() throws NoSuchAlgorithmException, InvalidParameterSpecException, InvalidKeySpecException {
267 231 /* Get Elliptic Curve Parameter spec for secp256r1 */
268 232 AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
269 233 algoParameters.init(new ECGenParameterSpec("secp256r1"));
... ...
... ... @@ -104,7 +104,7 @@ public class LwM2mTransportRequest {
104 104 private final LwM2mTransportContext context;
105 105 private final LwM2MTransportServerConfig config;
106 106 private final LwM2mClientContext lwM2mClientContext;
107   - private final DefaultLwM2MTransportMsgHandler serviceImpl;
  107 + private final DefaultLwM2MTransportMsgHandler handler;
108 108
109 109 @PostConstruct
110 110 public void init() {
... ... @@ -150,7 +150,7 @@ public class LwM2mTransportRequest {
150 150 Lwm2mClientRpcRequest rpcRequestClone = (Lwm2mClientRpcRequest) rpcRequest.clone();
151 151 if (rpcRequestClone != null) {
152 152 String errorMsg = String.format("Path %s params is not valid", targetIdVer);
153   - serviceImpl.sentRpcRequest(rpcRequestClone, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR);
  153 + handler.sentRpcRequest(rpcRequestClone, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR);
154 154 rpcRequest = null;
155 155 }
156 156 }
... ... @@ -159,12 +159,12 @@ public class LwM2mTransportRequest {
159 159 if (rpcRequest != null) {
160 160 ResourceModel resourceModel = lwM2MClient.getResourceModel(targetIdVer, this.config.getModelProvider());
161 161 String errorMsg = resourceModel == null ? String.format("Path %s not found in object version", targetIdVer) : "SendRequest - null";
162   - serviceImpl.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
  162 + handler.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
163 163 }
164 164 }
165 165 } else if (rpcRequest != null) {
166 166 String errorMsg = String.format("Path %s not found in object version", targetIdVer);
167   - serviceImpl.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
  167 + handler.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
168 168 }
169 169 } else if (OBSERVE_READ_ALL.name().equals(typeOper.name()) || DISCOVER_All.name().equals(typeOper.name())) {
170 170 Set<String> paths;
... ... @@ -177,11 +177,11 @@ public class LwM2mTransportRequest {
177 177 paths = Arrays.stream(objectLinks).map(Link::toString).collect(Collectors.toUnmodifiableSet());
178 178 String msg = String.format("%s: type operation %s paths - %s", LOG_LW2M_INFO,
179 179 typeOper.name(), paths);
180   - serviceImpl.sendLogsToThingsboard(msg, registration.getId());
  180 + handler.sendLogsToThingsboard(msg, registration.getId());
181 181 }
182 182 if (rpcRequest != null) {
183 183 String valueMsg = String.format("Paths - %s", paths);
184   - serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE);
  184 + handler.sentRpcRequest(rpcRequest, CONTENT.name(), valueMsg, LOG_LW2M_VALUE);
185 185 }
186 186 } else if (OBSERVE_CANCEL.name().equals(typeOper.name())) {
187 187 int observeCancelCnt = context.getServer().getObservationService().cancelObservations(registration);
... ... @@ -192,10 +192,10 @@ public class LwM2mTransportRequest {
192 192 } catch (Exception e) {
193 193 String msg = String.format("%s: type operation %s %s", LOG_LW2M_ERROR,
194 194 typeOper.name(), e.getMessage());
195   - serviceImpl.sendLogsToThingsboard(msg, registration.getId());
  195 + handler.sendLogsToThingsboard(msg, registration.getId());
196 196 if (rpcRequest != null) {
197 197 String errorMsg = String.format("Path %s type operation %s %s", targetIdVer, typeOper.name(), e.getMessage());
198   - serviceImpl.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
  198 + handler.sentRpcRequest(rpcRequest, NOT_FOUND.getName(), errorMsg, LOG_LW2M_ERROR);
199 199 }
200 200 }
201 201 }
... ... @@ -318,31 +318,31 @@ public class LwM2mTransportRequest {
318 318 context.getServer().send(registration, request, timeoutInMs, (ResponseCallback<?>) response -> {
319 319
320 320 if (!lwM2MClient.isInit()) {
321   - lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
  321 + lwM2MClient.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
322 322 }
323 323 if (CoAP.ResponseCode.isSuccess(((Response) response.getCoapResponse()).getCode())) {
324 324 this.handleResponse(registration, request.getPath().toString(), response, request, rpcRequest);
325 325 } else {
326 326 String msg = String.format("%s: SendRequest %s: CoapCode - %s Lwm2m code - %d name - %s Resource path - %s", LOG_LW2M_ERROR, request.getClass().getName().toString(),
327 327 ((Response) response.getCoapResponse()).getCode(), response.getCode().getCode(), response.getCode().getName(), request.getPath().toString());
328   - serviceImpl.sendLogsToThingsboard(msg, registration.getId());
  328 + handler.sendLogsToThingsboard(msg, registration.getId());
329 329 log.error("[{}] [{}], [{}] - [{}] [{}] error SendRequest", request.getClass().getName().toString(), registration.getEndpoint(),
330 330 ((Response) response.getCoapResponse()).getCode(), response.getCode(), request.getPath().toString());
331 331 if (!lwM2MClient.isInit()) {
332   - lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
  332 + lwM2MClient.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
333 333 }
334 334 /** Not Found */
335 335 if (rpcRequest != null) {
336   - serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), response.getErrorMessage(), LOG_LW2M_ERROR);
  336 + handler.sentRpcRequest(rpcRequest, response.getCode().getName(), response.getErrorMessage(), LOG_LW2M_ERROR);
337 337 }
338 338 /** Not Found
339 339 set setClient_fw_info... = empty
340 340 **/
341 341 if (lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) {
342   - lwM2MClient.getFwUpdate().initReadValue(serviceImpl, request.getPath().toString());
  342 + lwM2MClient.getFwUpdate().initReadValue(handler, this, request.getPath().toString());
343 343 }
344 344 if (lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) {
345   - lwM2MClient.getSwUpdate().initReadValue(serviceImpl, request.getPath().toString());
  345 + lwM2MClient.getSwUpdate().initReadValue(handler, this, request.getPath().toString());
346 346 }
347 347 if (request.getPath().toString().equals(FW_PACKAGE_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) {
348 348 this.afterWriteFwSWUpdateError(registration, request, response.getErrorMessage());
... ... @@ -356,10 +356,10 @@ public class LwM2mTransportRequest {
356 356 set setClient_fw_info... = empty
357 357 **/
358 358 if (lwM2MClient.getFwUpdate().isInfoFwSwUpdate()) {
359   - lwM2MClient.getFwUpdate().initReadValue(serviceImpl, request.getPath().toString());
  359 + lwM2MClient.getFwUpdate().initReadValue(handler, this, request.getPath().toString());
360 360 }
361 361 if (lwM2MClient.getSwUpdate().isInfoFwSwUpdate()) {
362   - lwM2MClient.getSwUpdate().initReadValue(serviceImpl, request.getPath().toString());
  362 + lwM2MClient.getSwUpdate().initReadValue(handler, this, request.getPath().toString());
363 363 }
364 364 if (request.getPath().toString().equals(FW_PACKAGE_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) {
365 365 this.afterWriteFwSWUpdateError(registration, request, e.getMessage());
... ... @@ -368,14 +368,14 @@ public class LwM2mTransportRequest {
368 368 this.afterExecuteFwSwUpdateError(registration, request, e.getMessage());
369 369 }
370 370 if (!lwM2MClient.isInit()) {
371   - lwM2MClient.initReadValue(this.serviceImpl, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
  371 + lwM2MClient.initReadValue(this.handler, convertPathFromObjectIdToIdVer(request.getPath().toString(), registration));
372 372 }
373 373 String msg = String.format("%s: SendRequest %s: Resource path - %s msg error - %s",
374 374 LOG_LW2M_ERROR, request.getClass().getName().toString(), request.getPath().toString(), e.getMessage());
375   - serviceImpl.sendLogsToThingsboard(msg, registration.getId());
  375 + handler.sendLogsToThingsboard(msg, registration.getId());
376 376 log.error("[{}] [{}] - [{}] error SendRequest", request.getClass().getName().toString(), request.getPath().toString(), e.toString());
377 377 if (rpcRequest != null) {
378   - serviceImpl.sentRpcRequest(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR);
  378 + handler.sentRpcRequest(rpcRequest, CoAP.CodeClass.ERROR_RESPONSE.name(), e.getMessage(), LOG_LW2M_ERROR);
379 379 }
380 380 });
381 381 }
... ... @@ -417,11 +417,11 @@ public class LwM2mTransportRequest {
417 417 String patn = "/" + objectId + "/" + instanceId + "/" + resourceId;
418 418 String msg = String.format(LOG_LW2M_ERROR + ": NumberFormatException: Resource path - %s type - %s value - %s msg error - %s SendRequest to Client",
419 419 patn, type, value, e.toString());
420   - serviceImpl.sendLogsToThingsboard(msg, registration.getId());
  420 + handler.sendLogsToThingsboard(msg, registration.getId());
421 421 log.error("Path: [{}] type: [{}] value: [{}] errorMsg: [{}]]", patn, type, value, e.toString());
422 422 if (rpcRequest != null) {
423 423 String errorMsg = String.format("NumberFormatException: Resource path - %s type - %s value - %s", patn, type, value);
424   - serviceImpl.sentRpcRequest(rpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR);
  424 + handler.sentRpcRequest(rpcRequest, BAD_REQUEST.getName(), errorMsg, LOG_LW2M_ERROR);
425 425 }
426 426 return null;
427 427 }
... ... @@ -450,17 +450,17 @@ public class LwM2mTransportRequest {
450 450 String pathIdVer = convertPathFromObjectIdToIdVer(path, registration);
451 451 String msgLog = "";
452 452 if (response instanceof ReadResponse) {
453   - serviceImpl.onUpdateValueAfterReadResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest);
  453 + handler.onUpdateValueAfterReadResponse(registration, pathIdVer, (ReadResponse) response, rpcRequest);
454 454 } else if (response instanceof DeleteResponse) {
455 455 log.warn("[{}] Path [{}] DeleteResponse 5_Send", pathIdVer, response);
456 456 } else if (response instanceof DiscoverResponse) {
457 457 String discoverValue = Link.serialize(((DiscoverResponse)response).getObjectLinks());
458 458 msgLog = String.format("%s: type operation: %s path: %s value: %s",
459 459 LOG_LW2M_INFO, DISCOVER.name(), request.getPath().toString(), discoverValue);
460   - serviceImpl.sendLogsToThingsboard(msgLog, registration.getId());
  460 + handler.sendLogsToThingsboard(msgLog, registration.getId());
461 461 log.warn("DiscoverResponse: [{}]", (DiscoverResponse) response);
462 462 if (rpcRequest != null) {
463   - serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), discoverValue, LOG_LW2M_VALUE);
  463 + handler.sentRpcRequest(rpcRequest, response.getCode().getName(), discoverValue, LOG_LW2M_VALUE);
464 464 }
465 465 } else if (response instanceof ExecuteResponse) {
466 466 log.warn("[{}] Path [{}] ExecuteResponse 7_Send", pathIdVer, response);
... ... @@ -469,16 +469,16 @@ public class LwM2mTransportRequest {
469 469 } else if (response instanceof WriteResponse) {
470 470 log.warn("[{}] Path [{}] WriteResponse 9_Send", pathIdVer, response);
471 471 this.infoWriteResponse(registration, response, request);
472   - serviceImpl.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request);
  472 + handler.onWriteResponseOk(registration, pathIdVer, (WriteRequest) request);
473 473 }
474 474 if (rpcRequest != null) {
475 475 if (response instanceof ExecuteResponse
476 476 || response instanceof WriteAttributesResponse
477 477 || response instanceof DeleteResponse) {
478 478 rpcRequest.setInfoMsg(null);
479   - serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, null);
  479 + handler.sentRpcRequest(rpcRequest, response.getCode().getName(), null, null);
480 480 } else if (response instanceof WriteResponse) {
481   - serviceImpl.sentRpcRequest(rpcRequest, response.getCode().getName(), null, LOG_LW2M_INFO);
  481 + handler.sentRpcRequest(rpcRequest, response.getCode().getName(), null, LOG_LW2M_INFO);
482 482 }
483 483 }
484 484 }
... ... @@ -511,7 +511,7 @@ public class LwM2mTransportRequest {
511 511 LOG_LW2M_INFO, response.getCode().getCode(), request.getPath().toString(), value);
512 512 }
513 513 if (msg != null) {
514   - serviceImpl.sendLogsToThingsboard(msg, registration.getId());
  514 + handler.sendLogsToThingsboard(msg, registration.getId());
515 515 if (request.getPath().toString().equals(FW_PACKAGE_ID) || request.getPath().toString().equals(SW_PACKAGE_ID)) {
516 516 this.afterWriteSuccessFwSwUpdate(registration, request);
517 517 }
... ... @@ -530,11 +530,11 @@ public class LwM2mTransportRequest {
530 530 LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
531 531 if (request.getPath().toString().equals(FW_PACKAGE_ID) && lwM2MClient.getFwUpdate() != null) {
532 532 lwM2MClient.getFwUpdate().setStateUpdate(DOWNLOADED.name());
533   - lwM2MClient.getFwUpdate().sendLogs(WRITE_REPLACE.name(), LOG_LW2M_INFO, null);
  533 + lwM2MClient.getFwUpdate().sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null);
534 534 }
535 535 if (request.getPath().toString().equals(SW_PACKAGE_ID) && lwM2MClient.getSwUpdate() != null) {
536 536 lwM2MClient.getSwUpdate().setStateUpdate(DOWNLOADED.name());
537   - lwM2MClient.getSwUpdate().sendLogs(WRITE_REPLACE.name(), LOG_LW2M_INFO, null);
  537 + lwM2MClient.getSwUpdate().sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null);
538 538 }
539 539 }
540 540
... ... @@ -545,30 +545,30 @@ public class LwM2mTransportRequest {
545 545 LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
546 546 if (request.getPath().toString().equals(FW_PACKAGE_ID) && lwM2MClient.getFwUpdate() != null) {
547 547 lwM2MClient.getFwUpdate().setStateUpdate(FAILED.name());
548   - lwM2MClient.getFwUpdate().sendLogs(WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError);
  548 + lwM2MClient.getFwUpdate().sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError);
549 549 }
550 550 if (request.getPath().toString().equals(SW_PACKAGE_ID) && lwM2MClient.getSwUpdate() != null) {
551 551 lwM2MClient.getSwUpdate().setStateUpdate(FAILED.name());
552   - lwM2MClient.getSwUpdate().sendLogs(WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError);
  552 + lwM2MClient.getSwUpdate().sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_ERROR, msgError);
553 553 }
554 554 }
555 555
556 556 private void afterExecuteFwSwUpdateError(Registration registration, DownlinkRequest request, String msgError) {
557 557 LwM2mClient lwM2MClient = this.lwM2mClientContext.getClientByRegistrationId(registration.getId());
558 558 if (request.getPath().toString().equals(FW_UPDATE_ID) && lwM2MClient.getFwUpdate() != null) {
559   - lwM2MClient.getFwUpdate().sendLogs(EXECUTE.name(), LOG_LW2M_ERROR, msgError);
  559 + lwM2MClient.getFwUpdate().sendLogs(handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError);
560 560 }
561 561 if (request.getPath().toString().equals(SW_INSTALL_ID) && lwM2MClient.getSwUpdate() != null) {
562   - lwM2MClient.getSwUpdate().sendLogs(EXECUTE.name(), LOG_LW2M_ERROR, msgError);
  562 + lwM2MClient.getSwUpdate().sendLogs(handler, EXECUTE.name(), LOG_LW2M_ERROR, msgError);
563 563 }
564 564 }
565 565
566 566 private void afterObserveCancel(Registration registration, int observeCancelCnt, String observeCancelMsg, Lwm2mClientRpcRequest rpcRequest) {
567   - serviceImpl.sendLogsToThingsboard(observeCancelMsg, registration.getId());
  567 + handler.sendLogsToThingsboard(observeCancelMsg, registration.getId());
568 568 log.warn("[{}]", observeCancelMsg);
569 569 if (rpcRequest != null) {
570 570 rpcRequest.setInfoMsg(String.format("Count: %d", observeCancelCnt));
571   - serviceImpl.sentRpcRequest(rpcRequest, CONTENT.name(), null, LOG_LW2M_INFO);
  571 + handler.sentRpcRequest(rpcRequest, CONTENT.name(), null, LOG_LW2M_INFO);
572 572 }
573 573 }
574 574 }
... ...
... ... @@ -41,6 +41,7 @@ import org.eclipse.leshan.core.node.codec.CodecException;
41 41 import org.eclipse.leshan.core.request.ContentFormat;
42 42 import org.springframework.stereotype.Component;
43 43 import org.thingsboard.server.common.transport.TransportServiceCallback;
  44 +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
44 45 import org.thingsboard.server.gen.transport.TransportProtos;
45 46 import org.thingsboard.server.gen.transport.TransportProtos.PostAttributeMsg;
46 47 import org.thingsboard.server.gen.transport.TransportProtos.PostTelemetryMsg;
... ... @@ -106,21 +107,21 @@ public class LwM2mTransportServerHelper {
106 107 /**
107 108 * @return - sessionInfo after access connect client
108 109 */
109   - public SessionInfoProto getValidateSessionInfo(TransportProtos.ValidateDeviceCredentialsResponseMsg msg, long mostSignificantBits, long leastSignificantBits) {
  110 + public SessionInfoProto getValidateSessionInfo(ValidateDeviceCredentialsResponse msg, long mostSignificantBits, long leastSignificantBits) {
110 111 return SessionInfoProto.newBuilder()
111 112 .setNodeId(context.getNodeId())
112 113 .setSessionIdMSB(mostSignificantBits)
113 114 .setSessionIdLSB(leastSignificantBits)
114   - .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB())
115   - .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB())
116   - .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB())
117   - .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB())
118   - .setCustomerIdMSB(msg.getDeviceInfo().getCustomerIdMSB())
119   - .setCustomerIdLSB(msg.getDeviceInfo().getCustomerIdLSB())
  115 + .setDeviceIdMSB(msg.getDeviceInfo().getDeviceId().getId().getMostSignificantBits())
  116 + .setDeviceIdLSB(msg.getDeviceInfo().getDeviceId().getId().getLeastSignificantBits())
  117 + .setTenantIdMSB(msg.getDeviceInfo().getTenantId().getId().getMostSignificantBits())
  118 + .setTenantIdLSB(msg.getDeviceInfo().getTenantId().getId().getLeastSignificantBits())
  119 + .setCustomerIdMSB(msg.getDeviceInfo().getCustomerId().getId().getMostSignificantBits())
  120 + .setCustomerIdLSB(msg.getDeviceInfo().getCustomerId().getId().getLeastSignificantBits())
120 121 .setDeviceName(msg.getDeviceInfo().getDeviceName())
121 122 .setDeviceType(msg.getDeviceInfo().getDeviceType())
122   - .setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileIdLSB())
123   - .setDeviceProfileIdMSB(msg.getDeviceInfo().getDeviceProfileIdMSB())
  123 + .setDeviceProfileIdMSB(msg.getDeviceInfo().getDeviceProfileId().getId().getMostSignificantBits())
  124 + .setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileId().getId().getLeastSignificantBits())
124 125 .build();
125 126 }
126 127
... ...
... ... @@ -105,7 +105,7 @@ public class LwM2mTransportUtil {
105 105 public static final long DEFAULT_TIMEOUT = 2 * 60 * 1000L; // 2min in ms
106 106
107 107 public static final String
108   - LOG_LW2M_TELEMETRY = "logLwm2m";
  108 + LOG_LW2M_TELEMETRY = "LwM2MLog";
109 109 public static final String LOG_LW2M_INFO = "info";
110 110 public static final String LOG_LW2M_ERROR = "error";
111 111 public static final String LOG_LW2M_WARN = "warn";
... ...
... ... @@ -31,10 +31,10 @@ import org.eclipse.leshan.server.registration.Registration;
31 31 import org.eclipse.leshan.server.security.SecurityInfo;
32 32 import org.thingsboard.server.common.data.Device;
33 33 import org.thingsboard.server.common.data.DeviceProfile;
  34 +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
34 35 import org.thingsboard.server.common.data.firmware.FirmwareType;
35 36 import org.thingsboard.server.gen.transport.TransportProtos.SessionInfoProto;
36 37 import org.thingsboard.server.gen.transport.TransportProtos.TsKvProto;
37   -import org.thingsboard.server.gen.transport.TransportProtos.ValidateDeviceCredentialsResponseMsg;
38 38 import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MTransportMsgHandler;
39 39 import org.thingsboard.server.transport.lwm2m.server.LwM2mQueuedRequest;
40 40 import org.thingsboard.server.transport.lwm2m.utils.LwM2mValueConverterImpl;
... ... @@ -89,7 +89,9 @@ public class LwM2mClient implements Cloneable {
89 89 @Getter
90 90 @Setter
91 91 private Registration registration;
92   - private ValidateDeviceCredentialsResponseMsg credentialsResponse;
  92 +
  93 + private ValidateDeviceCredentialsResponse credentials;
  94 +
93 95 @Getter
94 96 private final Map<String, ResourceValue> resources;
95 97 @Getter
... ... @@ -106,11 +108,11 @@ public class LwM2mClient implements Cloneable {
106 108 return super.clone();
107 109 }
108 110
109   - public LwM2mClient(String nodeId, String endpoint, String identity, SecurityInfo securityInfo, ValidateDeviceCredentialsResponseMsg credentialsResponse, UUID profileId, UUID sessionId) {
  111 + public LwM2mClient(String nodeId, String endpoint, String identity, SecurityInfo securityInfo, ValidateDeviceCredentialsResponse credentials, UUID profileId, UUID sessionId) {
110 112 this.endpoint = endpoint;
111 113 this.identity = identity;
112 114 this.securityInfo = securityInfo;
113   - this.credentialsResponse = credentialsResponse;
  115 + this.credentials = credentials;
114 116 this.delayedRequests = new ConcurrentHashMap<>();
115 117 this.pendingReadRequests = new CopyOnWriteArrayList<>();
116 118 this.resources = new ConcurrentHashMap<>();
... ... @@ -118,10 +120,11 @@ public class LwM2mClient implements Cloneable {
118 120 this.sessionId = sessionId;
119 121 this.init = false;
120 122 this.queuedRequests = new ConcurrentLinkedQueue<>();
  123 +
121 124 this.fwUpdate = new LwM2mFwSwUpdate(this, FirmwareType.FIRMWARE);
122 125 this.swUpdate = new LwM2mFwSwUpdate(this, FirmwareType.SOFTWARE);
123   - if (this.credentialsResponse != null && this.credentialsResponse.hasDeviceInfo()) {
124   - this.session = createSession(nodeId, sessionId, credentialsResponse);
  126 + if (this.credentials != null && this.credentials.hasDeviceInfo()) {
  127 + this.session = createSession(nodeId, sessionId, credentials);
125 128 this.deviceId = new UUID(session.getDeviceIdMSB(), session.getDeviceIdLSB());
126 129 this.profileId = new UUID(session.getDeviceProfileIdMSB(), session.getDeviceProfileIdLSB());
127 130 this.deviceName = session.getDeviceName();
... ... @@ -154,21 +157,21 @@ public class LwM2mClient implements Cloneable {
154 157 builder.setDeviceType(this.deviceProfileName);
155 158 }
156 159
157   - private SessionInfoProto createSession(String nodeId, UUID sessionId, ValidateDeviceCredentialsResponseMsg msg) {
  160 + private SessionInfoProto createSession(String nodeId, UUID sessionId, ValidateDeviceCredentialsResponse msg) {
158 161 return SessionInfoProto.newBuilder()
159 162 .setNodeId(nodeId)
160 163 .setSessionIdMSB(sessionId.getMostSignificantBits())
161 164 .setSessionIdLSB(sessionId.getLeastSignificantBits())
162   - .setDeviceIdMSB(msg.getDeviceInfo().getDeviceIdMSB())
163   - .setDeviceIdLSB(msg.getDeviceInfo().getDeviceIdLSB())
164   - .setTenantIdMSB(msg.getDeviceInfo().getTenantIdMSB())
165   - .setTenantIdLSB(msg.getDeviceInfo().getTenantIdLSB())
166   - .setCustomerIdMSB(msg.getDeviceInfo().getCustomerIdMSB())
167   - .setCustomerIdLSB(msg.getDeviceInfo().getCustomerIdLSB())
  165 + .setDeviceIdMSB(msg.getDeviceInfo().getDeviceId().getId().getMostSignificantBits())
  166 + .setDeviceIdLSB(msg.getDeviceInfo().getDeviceId().getId().getLeastSignificantBits())
  167 + .setTenantIdMSB(msg.getDeviceInfo().getTenantId().getId().getMostSignificantBits())
  168 + .setTenantIdLSB(msg.getDeviceInfo().getTenantId().getId().getLeastSignificantBits())
  169 + .setCustomerIdMSB(msg.getDeviceInfo().getCustomerId().getId().getMostSignificantBits())
  170 + .setCustomerIdLSB(msg.getDeviceInfo().getCustomerId().getId().getLeastSignificantBits())
168 171 .setDeviceName(msg.getDeviceInfo().getDeviceName())
169 172 .setDeviceType(msg.getDeviceInfo().getDeviceType())
170   - .setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileIdLSB())
171   - .setDeviceProfileIdMSB(msg.getDeviceInfo().getDeviceProfileIdMSB())
  173 + .setDeviceProfileIdMSB(msg.getDeviceInfo().getDeviceProfileId().getId().getMostSignificantBits())
  174 + .setDeviceProfileIdLSB(msg.getDeviceInfo().getDeviceProfileId().getId().getLeastSignificantBits())
172 175 .build();
173 176 }
174 177
... ... @@ -188,7 +191,7 @@ public class LwM2mClient implements Cloneable {
188 191 }
189 192 }
190 193
191   - public Object getResourceValue (String pathRezIdVer, String pathRezId) {
  194 + public Object getResourceValue(String pathRezIdVer, String pathRezId) {
192 195 String pathRez = pathRezIdVer == null ? convertPathFromObjectIdToIdVer(pathRezId, this.registration) : pathRezIdVer;
193 196 if (this.resources.get(pathRez) != null) {
194 197 return this.resources.get(pathRez).getLwM2mResource().isMultiInstances() ?
... ...
... ... @@ -17,6 +17,7 @@ package org.thingsboard.server.transport.lwm2m.server.client;
17 17
18 18 import org.eclipse.leshan.server.registration.Registration;
19 19 import org.thingsboard.server.common.data.DeviceProfile;
  20 +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
20 21 import org.thingsboard.server.gen.transport.TransportProtos;
21 22
22 23 import java.util.Collection;
... ... @@ -59,4 +60,6 @@ public interface LwM2mClientContext {
59 60 Set<String> getSupportedIdVerInClient(Registration registration);
60 61
61 62 LwM2mClient getClientByDeviceId(UUID deviceId);
  63 +
  64 + void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials);
62 65 }
... ...
... ... @@ -22,10 +22,10 @@ import org.eclipse.leshan.server.registration.Registration;
22 22 import org.eclipse.leshan.server.security.EditableSecurityStore;
23 23 import org.springframework.stereotype.Service;
24 24 import org.thingsboard.server.common.data.DeviceProfile;
  25 +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
25 26 import org.thingsboard.server.gen.transport.TransportProtos;
26 27 import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
27 28 import org.thingsboard.server.transport.lwm2m.secure.EndpointSecurityInfo;
28   -import org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode;
29 29 import org.thingsboard.server.transport.lwm2m.secure.LwM2mCredentialsSecurityInfoValidator;
30 30 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext;
31 31 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
... ... @@ -38,7 +38,7 @@ import java.util.Set;
38 38 import java.util.UUID;
39 39 import java.util.concurrent.ConcurrentHashMap;
40 40
41   -import static org.thingsboard.server.transport.lwm2m.secure.LwM2MSecurityMode.NO_SEC;
  41 +import static org.eclipse.leshan.core.SecurityMode.NO_SEC;
42 42 import static org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil.convertPathFromObjectIdToIdVer;
43 43
44 44 @Slf4j
... ... @@ -112,7 +112,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
112 112 @Override
113 113 public LwM2mClient fetchClientByEndpoint(String endpoint) {
114 114 EndpointSecurityInfo securityInfo = lwM2MCredentialsSecurityInfoValidator.getEndpointSecurityInfo(endpoint, LwM2mTransportUtil.LwM2mTypeServer.CLIENT);
115   - if (securityInfo.getSecurityMode() < LwM2MSecurityMode.DEFAULT_MODE.code) {
  115 + if (securityInfo.getSecurityMode() != null) {
116 116 if (securityInfo.getDeviceProfile() != null) {
117 117 UUID profileUuid = profileUpdate(securityInfo.getDeviceProfile())!= null ?
118 118 securityInfo.getDeviceProfile().getUuidId() : null;
... ... @@ -125,7 +125,7 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
125 125 client = new LwM2mClient(context.getNodeId(), securityInfo.getSecurityInfo().getEndpoint(),
126 126 securityInfo.getSecurityInfo().getIdentity(), securityInfo.getSecurityInfo(),
127 127 securityInfo.getMsg(), profileUuid, UUID.randomUUID());
128   - } else if (securityInfo.getSecurityMode() == NO_SEC.code) {
  128 + } else if (NO_SEC.equals(securityInfo.getSecurityMode())) {
129 129 client = new LwM2mClient(context.getNodeId(), endpoint,
130 130 null, null,
131 131 securityInfo.getMsg(), profileUuid, UUID.randomUUID());
... ... @@ -143,6 +143,14 @@ public class LwM2mClientContextImpl implements LwM2mClientContext {
143 143 }
144 144
145 145 @Override
  146 + public void registerClient(Registration registration, ValidateDeviceCredentialsResponse credentials) {
  147 + LwM2mClient client = new LwM2mClient(context.getNodeId(), registration.getEndpoint(), null, null, credentials, credentials.getDeviceProfile().getUuidId(), UUID.randomUUID());
  148 + lwM2mClientsByEndpoint.put(registration.getEndpoint(), client);
  149 + lwM2mClientsByRegistrationId.put(registration.getId(), client);
  150 + profileUpdate(credentials.getDeviceProfile());
  151 + }
  152 +
  153 + @Override
146 154 public Collection<LwM2mClient> getLwM2mClients() {
147 155 return lwM2mClientsByEndpoint.values();
148 156 }
... ...
... ... @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.firmware.FirmwareType;
24 24 import org.thingsboard.server.common.data.firmware.FirmwareUpdateStatus;
25 25 import org.thingsboard.server.gen.transport.TransportProtos;
26 26 import org.thingsboard.server.transport.lwm2m.server.DefaultLwM2MTransportMsgHandler;
  27 +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportRequest;
27 28 import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportUtil;
28 29
29 30 import java.util.ArrayList;
... ... @@ -97,7 +98,6 @@ public class LwM2mFwSwUpdate {
97 98 @Setter
98 99 private volatile boolean infoFwSwUpdate = false;
99 100 private final FirmwareType type;
100   - private DefaultLwM2MTransportMsgHandler serviceImpl;
101 101 @Getter
102 102 LwM2mClient lwM2MClient;
103 103 @Getter
... ... @@ -113,7 +113,7 @@ public class LwM2mFwSwUpdate {
113 113 }
114 114
115 115 private void initPathId() {
116   - if (FIRMWARE.equals(this.type) ) {
  116 + if (FIRMWARE.equals(this.type)) {
117 117 this.pathPackageId = FW_PACKAGE_ID;
118 118 this.pathStateId = FW_STATE_ID;
119 119 this.pathResultId = FW_RESULT_ID;
... ... @@ -121,7 +121,7 @@ public class LwM2mFwSwUpdate {
121 121 this.pathVerId = FW_VER_ID;
122 122 this.pathInstallId = FW_UPDATE_ID;
123 123 this.wUpdate = FW_UPDATE;
124   - } else if (SOFTWARE.equals(this.type) ) {
  124 + } else if (SOFTWARE.equals(this.type)) {
125 125 this.pathPackageId = SW_PACKAGE_ID;
126 126 this.pathStateId = SW_UPDATE_STATE_ID;
127 127 this.pathResultId = SW_RESULT_ID;
... ... @@ -133,8 +133,7 @@ public class LwM2mFwSwUpdate {
133 133 }
134 134 }
135 135
136   - public void initReadValue(DefaultLwM2MTransportMsgHandler serviceImpl, String pathIdVer) {
137   - if (this.serviceImpl == null) this.serviceImpl = serviceImpl;
  136 + public void initReadValue(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request, String pathIdVer) {
138 137 if (pathIdVer != null) {
139 138 this.pendingInfoRequestsStart.remove(pathIdVer);
140 139 }
... ... @@ -144,7 +143,7 @@ public class LwM2mFwSwUpdate {
144 143 boolean conditionalStart = this.type.equals(FIRMWARE) ? this.conditionalFwUpdateStart() :
145 144 this.conditionalSwUpdateStart();
146 145 if (conditionalStart) {
147   - this.writeFwSwWare();
  146 + this.writeFwSwWare(handler, request);
148 147 }
149 148 }
150 149 }
... ... @@ -154,26 +153,26 @@ public class LwM2mFwSwUpdate {
154 153 * Send FsSw to Lwm2mClient:
155 154 * before operation Write: fw_state = DOWNLOADING
156 155 */
157   - private void writeFwSwWare() {
  156 + private void writeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) {
158 157 this.stateUpdate = FirmwareUpdateStatus.DOWNLOADING.name();
159 158 // this.observeStateUpdate();
160   - this.sendLogs(WRITE_REPLACE.name(), LOG_LW2M_INFO, null);
  159 + this.sendLogs(handler, WRITE_REPLACE.name(), LOG_LW2M_INFO, null);
161 160 int chunkSize = 0;
162 161 int chunk = 0;
163   - byte[] firmwareChunk = this.serviceImpl.firmwareDataCache.get(this.currentId.toString(), chunkSize, chunk);
  162 + byte[] firmwareChunk = handler.firmwareDataCache.get(this.currentId.toString(), chunkSize, chunk);
164 163 String targetIdVer = convertPathFromObjectIdToIdVer(this.pathPackageId, this.lwM2MClient.getRegistration());
165   - this.serviceImpl.lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE.getName(),
166   - firmwareChunk, this.serviceImpl.config.getTimeout(), null);
  164 + request.sendAllRequest(lwM2MClient.getRegistration(), targetIdVer, WRITE_REPLACE, ContentFormat.OPAQUE.getName(),
  165 + firmwareChunk, handler.config.getTimeout(), null);
167 166 }
168 167
169   - public void sendLogs(String typeOper, String typeInfo, String msgError) {
170   - this.sendSateOnThingsboard();
  168 + public void sendLogs(DefaultLwM2MTransportMsgHandler handler, String typeOper, String typeInfo, String msgError) {
  169 + this.sendSateOnThingsBoard(handler);
171 170 String msg = String.format("%s: %s, %s, pkgVer: %s: pkgName - %s state - %s.",
172 171 typeInfo, this.wUpdate, typeOper, this.currentVersion, this.currentTitle, this.stateUpdate);
173 172 if (LOG_LW2M_ERROR.equals(typeInfo)) {
174 173 msg = String.format("%s Error: %s", msg, msgError);
175 174 }
176   - serviceImpl.sendLogsToThingsboard(msg, lwM2MClient.getRegistration().getId());
  175 + handler.sendLogsToThingsboard(msg, lwM2MClient.getRegistration().getId());
177 176 }
178 177
179 178
... ... @@ -182,11 +181,11 @@ public class LwM2mFwSwUpdate {
182 181 * fw_state/sw_state = UPDATING
183 182 * send execute
184 183 */
185   - public void executeFwSwWare() {
186   - this.setStateUpdate(UPDATING.name());
187   - this.sendLogs(EXECUTE.name(), LOG_LW2M_INFO, null);
188   - this.serviceImpl.lwM2mTransportRequest.sendAllRequest(this.lwM2MClient.getRegistration(), this.pathInstallId, EXECUTE, ContentFormat.TLV.getName(),
189   - null, 0, null);
  184 + public void executeFwSwWare(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) {
  185 + this.setStateUpdate(UPDATING.name());
  186 + this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null);
  187 + request.sendAllRequest(this.lwM2MClient.getRegistration(), this.pathInstallId, EXECUTE, ContentFormat.TLV.getName(),
  188 + null, 0, null);
190 189 }
191 190
192 191
... ... @@ -219,7 +218,7 @@ public class LwM2mFwSwUpdate {
219 218 */
220 219 public boolean conditionalFwExecuteStart() {
221 220 Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
222   - return LwM2mTransportUtil.UpdateResultFw.INITIAL.code == updateResult;
  221 + return LwM2mTransportUtil.UpdateResultFw.INITIAL.code == updateResult;
223 222 }
224 223
225 224 /**
... ... @@ -228,7 +227,7 @@ public class LwM2mFwSwUpdate {
228 227 */
229 228 public boolean conditionalFwExecuteAfterSuccess() {
230 229 Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
231   - return LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code == updateResult;
  230 + return LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code == updateResult;
232 231 }
233 232
234 233 /**
... ... @@ -237,7 +236,7 @@ public class LwM2mFwSwUpdate {
237 236 */
238 237 public boolean conditionalFwExecuteAfterError() {
239 238 Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
240   - return LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code < updateResult;
  239 + return LwM2mTransportUtil.UpdateResultFw.UPDATE_SUCCESSFULLY.code < updateResult;
241 240 }
242 241
243 242 /**
... ... @@ -283,21 +282,20 @@ public class LwM2mFwSwUpdate {
283 282 * --- send to telemetry ( key - this is name Update Result in model) (
284 283 * -- fw_state/sw_state = FAILED
285 284 */
286   - public void finishFwSwUpdate(boolean success) {
  285 + public void finishFwSwUpdate(DefaultLwM2MTransportMsgHandler handler, boolean success) {
287 286 Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
288 287 String value = FIRMWARE.equals(this.type) ? LwM2mTransportUtil.UpdateResultFw.fromUpdateResultFwByCode(updateResult.intValue()).type :
289 288 LwM2mTransportUtil.UpdateResultSw.fromUpdateResultSwByCode(updateResult.intValue()).type;
290   - String key = splitCamelCaseString((String) this.lwM2MClient.getResourceName (null, this.pathResultId));
  289 + String key = splitCamelCaseString((String) this.lwM2MClient.getResourceName(null, this.pathResultId));
291 290 if (success) {
292 291 this.stateUpdate = FirmwareUpdateStatus.UPDATED.name();
293   - this.sendLogs(EXECUTE.name(), LOG_LW2M_INFO, null);
294   - }
295   - else {
  292 + this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_INFO, null);
  293 + } else {
296 294 this.stateUpdate = FirmwareUpdateStatus.FAILED.name();
297   - this.sendLogs(EXECUTE.name(), LOG_LW2M_ERROR, value);
  295 + this.sendLogs(handler, EXECUTE.name(), LOG_LW2M_ERROR, value);
298 296 }
299   - this.serviceImpl.helper.sendParametersOnThingsboardTelemetry(
300   - this.serviceImpl.helper.getKvStringtoThingsboard(key, value), this.lwM2MClient.getSession());
  297 + handler.helper.sendParametersOnThingsboardTelemetry(
  298 + handler.helper.getKvStringtoThingsboard(key, value), this.lwM2MClient.getSession());
301 299 }
302 300
303 301 /**
... ... @@ -306,40 +304,40 @@ public class LwM2mFwSwUpdate {
306 304 */
307 305 public boolean conditionalSwExecuteAfterSuccess() {
308 306 Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
309   - return LwM2mTransportUtil.UpdateResultSw.SUCCESSFULLY_INSTALLED.code == updateResult;
  307 + return LwM2mTransportUtil.UpdateResultSw.SUCCESSFULLY_INSTALLED.code == updateResult;
310 308 }
  309 +
311 310 /**
312 311 * After operation Execute success inspection Update Result :
313 312 * >= 50 - error "NOT_ENOUGH_STORAGE"
314 313 */
315 314 public boolean conditionalSwExecuteAfterError() {
316 315 Long updateResult = (Long) this.lwM2MClient.getResourceValue(null, this.pathResultId);
317   - return LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code <= updateResult;
  316 + return LwM2mTransportUtil.UpdateResultSw.NOT_ENOUGH_STORAGE.code <= updateResult;
318 317 }
319 318
320   - private void observeStateUpdate() {
321   - this.serviceImpl.lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(),
  319 + private void observeStateUpdate(DefaultLwM2MTransportMsgHandler handler, LwM2mTransportRequest request) {
  320 + request.sendAllRequest(lwM2MClient.getRegistration(),
322 321 convertPathFromObjectIdToIdVer(this.pathStateId, this.lwM2MClient.getRegistration()), OBSERVE,
323 322 null, null, 0, null);
324   - this.serviceImpl.lwM2mTransportRequest.sendAllRequest(lwM2MClient.getRegistration(),
  323 + request.sendAllRequest(lwM2MClient.getRegistration(),
325 324 convertPathFromObjectIdToIdVer(this.pathResultId, this.lwM2MClient.getRegistration()), OBSERVE,
326 325 null, null, 0, null);
327 326 }
328 327
329   - public void sendSateOnThingsboard() {
  328 + public void sendSateOnThingsBoard(DefaultLwM2MTransportMsgHandler handler) {
330 329 if (StringUtils.trimToNull(this.stateUpdate) != null) {
331 330 List<TransportProtos.KeyValueProto> result = new ArrayList<>();
332 331 TransportProtos.KeyValueProto.Builder kvProto = TransportProtos.KeyValueProto.newBuilder().setKey(getAttributeKey(this.type, STATE));
333 332 kvProto.setType(TransportProtos.KeyValueType.STRING_V).setStringV(stateUpdate);
334 333 result.add(kvProto.build());
335   - this.serviceImpl.helper.sendParametersOnThingsboardTelemetry(result,
336   - this.serviceImpl.getSessionInfoOrCloseSession(this.lwM2MClient.getRegistration()));
  334 + handler.helper.sendParametersOnThingsboardTelemetry(result,
  335 + handler.getSessionInfoOrCloseSession(this.lwM2MClient.getRegistration()));
337 336 }
338 337 }
339 338
340   - public void sendReadObserveInfo(DefaultLwM2MTransportMsgHandler serviceImpl) {
  339 + public void sendReadObserveInfo(LwM2mTransportRequest request) {
341 340 this.infoFwSwUpdate = true;
342   - this.serviceImpl = this.serviceImpl == null ? serviceImpl : this.serviceImpl;
343 341 this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer(
344 342 this.pathVerId, this.lwM2MClient.getRegistration()));
345 343 this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer(
... ... @@ -349,7 +347,7 @@ public class LwM2mFwSwUpdate {
349 347 this.pendingInfoRequestsStart.add(convertPathFromObjectIdToIdVer(
350 348 this.pathResultId, this.lwM2MClient.getRegistration()));
351 349 this.pendingInfoRequestsStart.forEach(pathIdVer -> {
352   - this.serviceImpl.lwM2mTransportRequest.sendAllRequest(this.lwM2MClient.getRegistration(), pathIdVer, OBSERVE, ContentFormat.TLV.getName(),
  350 + request.sendAllRequest(this.lwM2MClient.getRegistration(), pathIdVer, OBSERVE, ContentFormat.TLV.getName(),
353 351 null, 0, null);
354 352 });
355 353
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.store;
  17 +
  18 +import org.thingsboard.server.transport.lwm2m.secure.TbX509DtlsSessionInfo;
  19 +
  20 +import java.util.concurrent.ConcurrentHashMap;
  21 +
  22 +public class TbL2M2MDtlsSessionInMemoryStore implements TbLwM2MDtlsSessionStore {
  23 +
  24 + private final ConcurrentHashMap<String, TbX509DtlsSessionInfo> store = new ConcurrentHashMap<>();
  25 +
  26 + @Override
  27 + public void put(String endpoint, TbX509DtlsSessionInfo msg) {
  28 + store.put(endpoint, msg);
  29 + }
  30 +
  31 + @Override
  32 + public TbX509DtlsSessionInfo get(String endpoint) {
  33 + return store.get(endpoint);
  34 + }
  35 +
  36 + @Override
  37 + public void remove(String endpoint) {
  38 + store.remove(endpoint);
  39 + }
  40 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.store;
  17 +
  18 +import com.fasterxml.jackson.databind.JsonNode;
  19 +import org.springframework.data.redis.connection.RedisConnectionFactory;
  20 +import org.thingsboard.common.util.JacksonUtil;
  21 +import org.thingsboard.server.transport.lwm2m.secure.TbX509DtlsSessionInfo;
  22 +
  23 +public class TbLwM2MDtlsSessionRedisStore implements TbLwM2MDtlsSessionStore {
  24 +
  25 + private static final String SESSION_EP = "SESSION#EP#";
  26 + RedisConnectionFactory connectionFactory;
  27 +
  28 + public TbLwM2MDtlsSessionRedisStore(RedisConnectionFactory redisConnectionFactory) {
  29 + this.connectionFactory = redisConnectionFactory;
  30 + }
  31 +
  32 + @Override
  33 + public void put(String endpoint, TbX509DtlsSessionInfo msg) {
  34 + try (var c = connectionFactory.getConnection()) {
  35 + var msgJson = JacksonUtil.convertValue(msg, JsonNode.class);
  36 + if (msgJson != null) {
  37 + c.set(getKey(endpoint), msgJson.toString().getBytes());
  38 + } else {
  39 + throw new RuntimeException("Problem with serialization of message: " + msg.toString());
  40 + }
  41 + }
  42 + }
  43 +
  44 + @Override
  45 + public TbX509DtlsSessionInfo get(String endpoint) {
  46 + try (var c = connectionFactory.getConnection()) {
  47 + var data = c.get(getKey(endpoint));
  48 + if (data != null) {
  49 + return JacksonUtil.fromString(new String(data), TbX509DtlsSessionInfo.class);
  50 + } else {
  51 + return null;
  52 + }
  53 + }
  54 + }
  55 +
  56 + @Override
  57 + public void remove(String endpoint) {
  58 + try (var c = connectionFactory.getConnection()) {
  59 + c.del(getKey(endpoint));
  60 + }
  61 + }
  62 +
  63 + private byte[] getKey(String endpoint) {
  64 + return (SESSION_EP + endpoint).getBytes();
  65 + }
  66 +}
... ...
  1 +/**
  2 + * Copyright © 2016-2021 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.transport.lwm2m.server.store;
  17 +
  18 +
  19 +import org.thingsboard.server.transport.lwm2m.secure.TbX509DtlsSessionInfo;
  20 +
  21 +public interface TbLwM2MDtlsSessionStore {
  22 +
  23 + void put(String endpoint, TbX509DtlsSessionInfo msg);
  24 +
  25 + TbX509DtlsSessionInfo get(String endpoint);
  26 +
  27 + void remove(String endpoint);
  28 +
  29 +}
... ...
... ... @@ -20,12 +20,16 @@ import org.eclipse.leshan.server.security.EditableSecurityStore;
20 20 import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
21 21 import org.eclipse.leshan.server.security.SecurityInfo;
22 22 import org.eclipse.leshan.server.security.SecurityStoreListener;
  23 +import org.springframework.stereotype.Component;
  24 +import org.thingsboard.server.queue.util.TbLwM2mTransportComponent;
23 25 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient;
24 26 import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
25 27
26 28 import java.util.Collection;
27 29
28 30 @Slf4j
  31 +@Component
  32 +@TbLwM2mTransportComponent
29 33 public class TbLwM2mSecurityStore implements EditableSecurityStore {
30 34
31 35 private final LwM2mClientContext clientContext;
... ...
... ... @@ -60,4 +60,10 @@ public class TbLwM2mStoreFactory {
60 60 new TbLwM2mRedisSecurityStore(redisConfiguration.get().redisConnectionFactory()) : new InMemorySecurityStore());
61 61 }
62 62
  63 + @Bean
  64 + private TbLwM2MDtlsSessionStore sessionStore() {
  65 + return redisConfiguration.isPresent() && useRedis ?
  66 + new TbLwM2MDtlsSessionRedisStore(redisConfiguration.get().redisConnectionFactory()) : new TbL2M2MDtlsSessionInMemoryStore();
  67 + }
  68 +
63 69 }
... ...
... ... @@ -80,7 +80,7 @@ public interface TransportService {
80 80 TransportServiceCallback<ValidateDeviceCredentialsResponse> callback);
81 81
82 82 void process(ValidateDeviceLwM2MCredentialsRequestMsg msg,
83   - TransportServiceCallback<ValidateDeviceCredentialsResponseMsg> callback);
  83 + TransportServiceCallback<ValidateDeviceCredentialsResponse> callback);
84 84
85 85 void process(GetOrCreateDeviceFromGatewayRequestMsg msg,
86 86 TransportServiceCallback<GetOrCreateDeviceFromGatewayResponse> callback);
... ...
... ... @@ -226,19 +226,29 @@ public class JsonConverter {
226 226 }
227 227
228 228 private static KeyValueProto buildNumericKeyValueProto(JsonPrimitive value, String key) {
229   - if (value.getAsString().contains(".")) {
230   - return KeyValueProto.newBuilder()
231   - .setKey(key)
232   - .setType(KeyValueType.DOUBLE_V)
233   - .setDoubleV(value.getAsDouble())
234   - .build();
  229 + String valueAsString = value.getAsString();
  230 + KeyValueProto.Builder builder = KeyValueProto.newBuilder().setKey(key);
  231 + if (valueAsString.contains("e") || valueAsString.contains("E")) {
  232 + //TODO: correct value conversion. We should make sure that if the value can't fit into Long or Double, we should send String
  233 + var bd = new BigDecimal(valueAsString);
  234 + if (bd.stripTrailingZeros().scale() <= 0) {
  235 + try {
  236 + return builder.setType(KeyValueType.LONG_V).setLongV(bd.longValueExact()).build();
  237 + } catch (ArithmeticException e) {
  238 + return builder.setType(KeyValueType.DOUBLE_V).setDoubleV(bd.doubleValue()).build();
  239 + }
  240 + } else {
  241 + return builder.setType(KeyValueType.DOUBLE_V).setDoubleV(bd.doubleValue()).build();
  242 + }
  243 + } else if (valueAsString.contains(".")) {
  244 + return builder.setType(KeyValueType.DOUBLE_V).setDoubleV(value.getAsDouble()).build();
235 245 } else {
236 246 try {
237 247 long longValue = Long.parseLong(value.getAsString());
238   - return KeyValueProto.newBuilder().setKey(key).setType(KeyValueType.LONG_V)
239   - .setLongV(longValue).build();
  248 + return builder.setType(KeyValueType.LONG_V).setLongV(longValue).build();
240 249 } catch (NumberFormatException e) {
241   - throw new JsonSyntaxException("Big integer values are not supported!");
  250 + //TODO: correct value conversion. We should make sure that if the value can't fit into Long or Double, we should send String
  251 + return builder.setType(KeyValueType.DOUBLE_V).setDoubleV(new BigDecimal(valueAsString).doubleValue()).build();
242 252 }
243 253 }
244 254 }
... ... @@ -252,6 +262,7 @@ public class JsonConverter {
252 262 String valueAsString = value.getAsString();
253 263 String key = valueEntry.getKey();
254 264 if (valueAsString.contains("e") || valueAsString.contains("E")) {
  265 + //TODO: correct value conversion. We should make sure that if the value can't fit into Long or Double, we should send String
255 266 var bd = new BigDecimal(valueAsString);
256 267 if (bd.stripTrailingZeros().scale() <= 0) {
257 268 try {
... ... @@ -269,7 +280,8 @@ public class JsonConverter {
269 280 long longValue = Long.parseLong(value.getAsString());
270 281 result.add(new LongDataEntry(key, longValue));
271 282 } catch (NumberFormatException e) {
272   - throw new JsonSyntaxException("Big integer values are not supported!");
  283 + //TODO: correct value conversion. We should make sure that if the value can't fit into Long or Double, we should send String
  284 + result.add(new DoubleDataEntry(key, new BigDecimal(valueAsString).doubleValue()));
273 285 }
274 286 }
275 287 }
... ...
... ... @@ -348,11 +348,25 @@ public class DefaultTransportService implements TransportService {
348 348 }
349 349
350 350 @Override
351   - public void process(TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg msg, TransportServiceCallback<TransportProtos.ValidateDeviceCredentialsResponseMsg> callback) {
352   - log.trace("Processing msg: {}", msg);
353   - TbProtoQueueMsg<TransportApiRequestMsg> protoMsg = new TbProtoQueueMsg<>(UUID.randomUUID(), TransportApiRequestMsg.newBuilder().setValidateDeviceLwM2MCredentialsRequestMsg(msg).build());
354   - AsyncCallbackTemplate.withCallback(transportApiRequestTemplate.send(protoMsg),
355   - response -> callback.onSuccess(response.getValue().getValidateCredResponseMsg()), callback::onError, transportCallbackExecutor);
  351 + public void process(TransportProtos.ValidateDeviceLwM2MCredentialsRequestMsg requestMsg, TransportServiceCallback<ValidateDeviceCredentialsResponse> callback) {
  352 + log.trace("Processing msg: {}", requestMsg);
  353 + TbProtoQueueMsg<TransportApiRequestMsg> protoMsg = new TbProtoQueueMsg<>(UUID.randomUUID(), TransportApiRequestMsg.newBuilder().setValidateDeviceLwM2MCredentialsRequestMsg(requestMsg).build());
  354 + ListenableFuture<ValidateDeviceCredentialsResponse> response = Futures.transform(transportApiRequestTemplate.send(protoMsg), tmp -> {
  355 + TransportProtos.ValidateDeviceCredentialsResponseMsg msg = tmp.getValue().getValidateCredResponseMsg();
  356 + ValidateDeviceCredentialsResponse.ValidateDeviceCredentialsResponseBuilder result = ValidateDeviceCredentialsResponse.builder();
  357 + if (msg.hasDeviceInfo()) {
  358 + result.credentials(msg.getCredentialsBody());
  359 + TransportDeviceInfo tdi = getTransportDeviceInfo(msg.getDeviceInfo());
  360 + result.deviceInfo(tdi);
  361 + ByteString profileBody = msg.getProfileBody();
  362 + if (!profileBody.isEmpty()) {
  363 + DeviceProfile profile = deviceProfileCache.getOrCreate(tdi.getDeviceProfileId(), profileBody);
  364 + result.deviceProfile(profile);
  365 + }
  366 + }
  367 + return result.build();
  368 + }, MoreExecutors.directExecutor());
  369 + AsyncCallbackTemplate.withCallback(response, callback::onSuccess, callback::onError, transportCallbackExecutor);
356 370 }
357 371
358 372 @Override
... ... @@ -372,7 +386,7 @@ public class DefaultTransportService implements TransportService {
372 386 TransportDeviceInfo tdi = getTransportDeviceInfo(msg.getDeviceInfo());
373 387 result.deviceInfo(tdi);
374 388 ByteString profileBody = msg.getProfileBody();
375   - if (profileBody != null && !profileBody.isEmpty()) {
  389 + if (!profileBody.isEmpty()) {
376 390 DeviceProfile profile = deviceProfileCache.getOrCreate(tdi.getDeviceProfileId(), profileBody);
377 391 if (transportType != DeviceTransportType.DEFAULT
378 392 && profile != null && profile.getTransportType() != DeviceTransportType.DEFAULT && profile.getTransportType() != transportType) {
... ...
... ... @@ -21,6 +21,8 @@ import org.junit.runner.RunWith;
21 21 import org.mockito.junit.MockitoJUnitRunner;
22 22 import org.thingsboard.server.common.transport.adaptor.JsonConverter;
23 23
  24 +import java.util.ArrayList;
  25 +
24 26 @RunWith(MockitoJUnitRunner.class)
25 27 public class JsonConverterTest {
26 28
... ... @@ -39,6 +41,12 @@ public class JsonConverterTest {
39 41 }
40 42
41 43 @Test
  44 + public void testParseAttributesBigDecimalAsLong() {
  45 + var result = new ArrayList<>(JsonConverter.convertToAttributes(JSON_PARSER.parse("{\"meterReadingDelta\": 1E1}")));
  46 + Assert.assertEquals(10L, result.get(0).getLongValue().get().longValue());
  47 + }
  48 +
  49 + @Test
42 50 public void testParseAsDouble() {
43 51 var result = JsonConverter.convertToTelemetry(JSON_PARSER.parse("{\"meterReadingDelta\": 1.1}"), 0L);
44 52 Assert.assertEquals(1.1, result.get(0L).get(0).getDoubleValue().get(), 0.0);
... ...
... ... @@ -16,6 +16,7 @@
16 16 package org.thingsboard.server.dao.device;
17 17
18 18
  19 +import com.fasterxml.jackson.databind.node.ObjectNode;
19 20 import lombok.extern.slf4j.Slf4j;
20 21 import org.hibernate.exception.ConstraintViolationException;
21 22 import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -23,8 +24,12 @@ import org.springframework.cache.annotation.CacheEvict;
23 24 import org.springframework.cache.annotation.Cacheable;
24 25 import org.springframework.stereotype.Service;
25 26 import org.springframework.util.StringUtils;
  27 +import org.thingsboard.common.util.JacksonUtil;
26 28 import org.thingsboard.server.common.data.Device;
27 29 import org.thingsboard.server.common.data.device.credentials.BasicMqttCredentials;
  30 +import org.thingsboard.server.common.data.device.credentials.lwm2m.LwM2MClientCredentials;
  31 +import org.thingsboard.server.common.data.device.credentials.lwm2m.PSKClientCredentials;
  32 +import org.thingsboard.server.common.data.device.credentials.lwm2m.X509ClientCredentials;
28 33 import org.thingsboard.server.common.data.id.DeviceId;
29 34 import org.thingsboard.server.common.data.id.EntityId;
30 35 import org.thingsboard.server.common.data.id.TenantId;
... ... @@ -33,7 +38,6 @@ import org.thingsboard.server.common.msg.EncryptionUtil;
33 38 import org.thingsboard.server.dao.entity.AbstractEntityService;
34 39 import org.thingsboard.server.dao.exception.DataValidationException;
35 40 import org.thingsboard.server.dao.service.DataValidator;
36   -import org.thingsboard.common.util.JacksonUtil;
37 41
38 42 import static org.thingsboard.server.common.data.CacheConstants.DEVICE_CREDENTIALS_CACHE;
39 43 import static org.thingsboard.server.dao.service.Validator.validateId;
... ... @@ -76,7 +80,7 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen
76 80 }
77 81
78 82 private DeviceCredentials saveOrUpdate(TenantId tenantId, DeviceCredentials deviceCredentials) {
79   - if(deviceCredentials.getCredentialsType() == null){
  83 + if (deviceCredentials.getCredentialsType() == null) {
80 84 throw new DataValidationException("Device credentials type should be specified");
81 85 }
82 86 switch (deviceCredentials.getCredentialsType()) {
... ... @@ -131,7 +135,6 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen
131 135 deviceCredentials.setCredentialsValue(JacksonUtil.toString(mqttCredentials));
132 136 }
133 137
134   -
135 138 private void formatCertData(DeviceCredentials deviceCredentials) {
136 139 String cert = EncryptionUtil.trimNewLines(deviceCredentials.getCredentialsValue());
137 140 String sha3Hash = EncryptionUtil.getSha3Hash(cert);
... ... @@ -140,7 +143,49 @@ public class DeviceCredentialsServiceImpl extends AbstractEntityService implemen
140 143 }
141 144
142 145 private void formatSimpleLwm2mCredentials(DeviceCredentials deviceCredentials) {
  146 + LwM2MClientCredentials clientCredentials;
  147 + ObjectNode json;
  148 + try {
  149 + json = JacksonUtil.fromString(deviceCredentials.getCredentialsValue(), ObjectNode.class);
  150 + if (json == null) {
  151 + throw new IllegalArgumentException();
  152 + }
  153 + clientCredentials = JacksonUtil.convertValue(json.get("client"), LwM2MClientCredentials.class);
  154 + if (clientCredentials == null) {
  155 + throw new IllegalArgumentException();
  156 + }
  157 + } catch (IllegalArgumentException e) {
  158 + throw new DataValidationException("Invalid credentials body for LwM2M credentials!");
  159 + }
  160 +
  161 + String credentialsId = null;
143 162
  163 + switch (clientCredentials.getSecurityConfigClientMode()) {
  164 + case NO_SEC:
  165 + case RPK:
  166 + credentialsId = clientCredentials.getEndpoint();
  167 + break;
  168 + case PSK:
  169 + credentialsId = ((PSKClientCredentials) clientCredentials).getIdentity();
  170 + break;
  171 + case X509:
  172 + X509ClientCredentials x509Config = (X509ClientCredentials) clientCredentials;
  173 + if (x509Config.getCert() != null) {
  174 + String cert = EncryptionUtil.trimNewLines(x509Config.getCert());
  175 + String sha3Hash = EncryptionUtil.getSha3Hash(cert);
  176 + x509Config.setCert(cert);
  177 + ((ObjectNode) json.get("client")).put("cert", cert);
  178 + deviceCredentials.setCredentialsValue(JacksonUtil.toString(json));
  179 + credentialsId = sha3Hash;
  180 + } else {
  181 + credentialsId = x509Config.getEndpoint();
  182 + }
  183 + break;
  184 + }
  185 + if (credentialsId == null) {
  186 + throw new DataValidationException("Invalid credentials body for LwM2M credentials!");
  187 + }
  188 + deviceCredentials.setCredentialsId(credentialsId);
144 189 }
145 190
146 191 @Override
... ...
... ... @@ -75,32 +75,7 @@
75 75 ('device.client-id-or-user-name-necessary' | translate) : ''"></tb-error>
76 76 </section>
77 77 <div *ngIf="deviceCredentialsFormGroup.get('credentialsType').value === deviceCredentialsType.LWM2M_CREDENTIALS">
78   - <mat-form-field class="mat-block">
79   - <mat-label translate>device.lwm2m-key</mat-label>
80   - <input matInput type="text" formControlName="credentialsId" required>
81   - <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsId').hasError('required')">
82   - {{ 'device.lwm2m-key-required' | translate }}
83   - </mat-error>
84   - </mat-form-field>
85   - <mat-form-field class="mat-block">
86   - <mat-label translate>device.lwm2m-value</mat-label>
87   - <textarea matInput formControlName="credentialsValue" rows="10" required
88   - [matTooltip]="lwm2mCredentialsValueTooltip(deviceCredentialsFormGroup.get('credentialsValue').hasError('jsonError'))"
89   - matTooltipPosition="above"
90   - ></textarea>
91   - <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsValue').hasError('required')">
92   - {{ 'device.lwm2m-value-required' | translate }}
93   - </mat-error>
94   - <mat-error *ngIf="deviceCredentialsFormGroup.get('credentialsValue').hasError('jsonError')">
95   - {{ 'device.lwm2m-value-format-error' | translate }}
96   - </mat-error>
97   - <div mat-dialog-actions fxLayoutAlign="center center">
98   - <button mat-raised-button color="primary"
99   - (click)="openSecurityInfoLwM2mDialog($event)"
100   - >
101   - {{'device.lwm2m-value-edit' | translate }}
102   - </button>
103   - </div>
104   - </mat-form-field>
  78 + <tb-security-config-lwm2m formControlName="credentialsValue">
  79 + </tb-security-config-lwm2m>
105 80 </div>
106 81 </section>
... ...