Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
GH-1402:Fix Invalid datatype of Ciphersuite resource in BootstrapConfig
Co-authored-by: Simon Bernard <sbernard@sierrawireless.com>
  • Loading branch information
Warmek and sbernard31 committed Mar 3, 2023
1 parent fd5c9c0 commit 8f0db24
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 4 deletions.
5 changes: 5 additions & 0 deletions leshan-server-core/pom.xml
Expand Up @@ -42,6 +42,11 @@ Contributors:
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
Expand Down
Expand Up @@ -291,7 +291,6 @@ public static class ServerSecurity {
/**
* The Object ID of the OSCORE Object Instance that holds the OSCORE configuration to be used by the LWM2M
* Client to the LWM2M Server associated with this Security object.
*
*/
public Integer oscoreSecurityMode;

Expand Down Expand Up @@ -341,7 +340,7 @@ public static class ServerSecurity {
* <p>
* Since Security v1.1
*/
public ULong cipherSuite = null;
public List<CipherSuiteId> cipherSuite = null;

@Override
public String toString() {
Expand Down Expand Up @@ -465,6 +464,62 @@ public String toString() {
}
}

public static class CipherSuiteId {

private final byte firstByte;
private final byte secondByte;

/**
* Create {@link CipherSuiteId} from Cipher Suite value defined at IANA.
* <p>
* Possible values are described in the
* <a href="https://iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4"> registry</a>.
* <p>
* This doesn't mean that Leshan supports all IANA registered Cipher Suites.
*
* @param firstByte of Cipher Suite (e.g. {@code 0xC0} for {@code TLS_PSK_WITH_AES_128_CCM_8})
* @param secondByte of Cipher Suite (e.g. {@code 0xA8} for {@code TLS_PSK_WITH_AES_128_CCM_8})
*/
public CipherSuiteId(byte firstByte, byte secondByte) {
this.firstByte = firstByte;
this.secondByte = secondByte;
}

/**
* Create {@link CipherSuiteId} from ULong value of "DTLS/TLS Ciphersuite" resource (Id:16) from "Security"
* Object (Id:0).
* <p>
* As an example, the {@code TLS_PSK_WITH_AES_128_CCM_8} Cipher Suite is represented with the following string
* "{@code 0xC0,0xA8}". To form an integer value the two values are concatenated. In this example, the value is
* {@code 0xc0a8} for {@code 49320}.
*/
public CipherSuiteId(ULong valueFromSecurityObject) {
if (valueFromSecurityObject.intValue() < 0 || valueFromSecurityObject.intValue() > 65535)
throw new IllegalArgumentException("ULong value MUST be a 16-bit unsigned integer (max value : 65535)");
this.firstByte = (byte) ((valueFromSecurityObject.intValue() >> 8) & 0xFF);
this.secondByte = (byte) ((valueFromSecurityObject.intValue()) & 0xFF);
}

/**
* @return Value used in "DTLS/TLS Ciphersuite" resource (Id:16) from "Security" Object (Id:0).
* <p>
* The two bytes from Cipher Suite id are concatenated into integer value. As an example bytes
* "{@code 0xC0,0xA8}" will be concatenated into {@code 0xc0a8} which in decimal notation is
* {@code 49320}.
*/
public ULong getValueForSecurityObject() {
return ULong.valueOf((Byte.toUnsignedInt(firstByte) << 8) | Byte.toUnsignedInt(secondByte));
}

/**
* @return String representing IANA Cipher Suite Value.
*/
@Override
public String toString() {
return String.format("0x%02X,0x%02X", firstByte, secondByte);
}
}

@Override
public String toString() {
return String.format("BootstrapConfig [servers=%s, security=%s, acls=%s, oscore=%s]", servers, security, acls,
Expand Down
Expand Up @@ -18,7 +18,9 @@

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

Expand All @@ -36,6 +38,7 @@
import org.eclipse.leshan.core.request.BootstrapWriteRequest;
import org.eclipse.leshan.core.request.ContentFormat;
import org.eclipse.leshan.core.response.LwM2mResponse;
import org.eclipse.leshan.core.util.datatype.ULong;
import org.eclipse.leshan.server.bootstrap.BootstrapConfig.ACLConfig;
import org.eclipse.leshan.server.bootstrap.BootstrapConfig.OscoreObject;
import org.eclipse.leshan.server.bootstrap.BootstrapConfig.ServerConfig;
Expand Down Expand Up @@ -79,8 +82,14 @@ public static LwM2mObjectInstance toSecurityInstance(int instanceId, ServerSecur
resources.add(LwM2mSingleResource.newStringResource(14, securityConfig.sni));
if (securityConfig.certificateUsage != null)
resources.add(LwM2mSingleResource.newUnsignedIntegerResource(15, securityConfig.certificateUsage.code));
if (securityConfig.cipherSuite != null)
resources.add(LwM2mSingleResource.newUnsignedIntegerResource(16, securityConfig.cipherSuite));
if (securityConfig.cipherSuite != null) {
Map<Integer, ULong> ciperSuiteULong = new HashMap<>();
int i = 0;
for (BootstrapConfig.CipherSuiteId cipherSuiteId : securityConfig.cipherSuite) {
ciperSuiteULong.put(i++, cipherSuiteId.getValueForSecurityObject());
}
resources.add(LwM2mMultipleResource.newUnsignedIntegerResource(16, ciperSuiteULong));
}
if (securityConfig.oscoreSecurityMode != null) {
resources.add(LwM2mSingleResource.newObjectLinkResource(17,
new ObjectLink(21, securityConfig.oscoreSecurityMode)));
Expand Down
@@ -0,0 +1,92 @@
/*******************************************************************************
* Copyright (c) 2016 Sierra Wireless and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.html.
*
* Contributors:
* Bartosz Stolarczyk
* Orange Polska S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.leshan.server.bootstrap;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;

import org.eclipse.leshan.core.util.Hex;
import org.eclipse.leshan.core.util.datatype.ULong;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;

class BootstrapConfigTest {

@ParameterizedTest
@CsvSource(value = { // given 2 bytes, expected ToString()
"C0A8 | 0xC0,0xA8", // TLS_PSK_WITH_AES_128_CCM_8
"0003 | 0x00,0x03", // TLS_RSA_EXPORT_WITH_RC4_40_MD5
}, delimiter = '|')
public void test_toString(String input, String expectedResult) {
// Given 2 bytes
byte[] decoded = Hex.decodeHex(input.toCharArray());

// Create CipherSuiteId
BootstrapConfig.CipherSuiteId cipherSuiteId = new BootstrapConfig.CipherSuiteId(decoded[0], decoded[1]);

// Assert if bytes were correctly phrased
assertEquals(expectedResult, cipherSuiteId.toString());
}

@ParameterizedTest
@CsvSource(value = { // given ulong, expected ToString()
"49320 | 0xC0,0xA8", // TLS_PSK_WITH_AES_128_CCM_8
" 0 | 0x00,0x00", // test to lower limit
"65535 | 0xFF,0xFF", // test to upper limit

}, delimiter = '|')

public void test_create_new_CipherSuiteId_from_ULong(String input, String expectedResult) {
// Create CipherSuiteId with ULong
BootstrapConfig.CipherSuiteId cipherSuiteId = new BootstrapConfig.CipherSuiteId(ULong.valueOf(input));

// Assert if ULong was correctly phrased
assertEquals(expectedResult, cipherSuiteId.toString());
}

@ParameterizedTest
@ValueSource(strings = { //
"49320", // TLS_PSK_WITH_AES_128_CCM_8
"0", // test to lower limit
"65535", // test to upper limit
})
public void test_getValueForSecurityObject(String input) {
// Create example ULong
ULong testValue = ULong.valueOf(input);

// Create cipherSuiteId from ULong
BootstrapConfig.CipherSuiteId cipherSuiteId = new BootstrapConfig.CipherSuiteId(testValue);

// Check if getValueForSecurityObject() returns input ULong
assertEquals(testValue, cipherSuiteId.getValueForSecurityObject());
}

@ParameterizedTest
@ValueSource(strings = { //
"2147483647", // max 32-bit signed Integer
"2147483648", // max 32-bit signed Integer + 1
"65536"// max 16-bit signed Integer +1
})
public void test_error_thrown_for_invalid_ulong(String invalidULong) {
// Create ULong from String
ULong testValue = ULong.valueOf(invalidULong);

// Try to create CipherSuiteId with ULong outside of range
assertThrowsExactly(IllegalArgumentException.class, () -> new BootstrapConfig.CipherSuiteId(testValue));
}
}

0 comments on commit 8f0db24

Please sign in to comment.