Skip to content

Commit

Permalink
#537: Add RPK API for bootstrap server
Browse files Browse the repository at this point in the history
  • Loading branch information
sbernard31 committed Aug 16, 2018
1 parent 1690036 commit 2cf53cc
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,21 @@

package org.eclipse.leshan.integration.tests;

import java.math.BigInteger;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.security.AlgorithmParameters;
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECParameterSpec;
import java.security.spec.ECPoint;
import java.security.spec.ECPrivateKeySpec;
import java.security.spec.ECPublicKeySpec;
import java.security.spec.KeySpec;
import java.util.Arrays;
import java.util.List;

Expand All @@ -39,14 +51,48 @@
import org.eclipse.leshan.server.security.BootstrapSecurityStore;
import org.eclipse.leshan.server.security.EditableSecurityStore;
import org.eclipse.leshan.server.security.SecurityInfo;
import org.eclipse.leshan.util.Hex;

/**
* Helper for running a server and executing a client against it.
*
*/
public class BootstrapIntegrationTestHelper extends SecureIntegrationTestHelper {

LeshanBootstrapServer bootstrapServer;
public LeshanBootstrapServer bootstrapServer;
public final PublicKey bootstrapServerPublicKey;
public final PrivateKey bootstrapServerPrivateKey;

public BootstrapIntegrationTestHelper() {
super();

// create bootstrap server credentials
try {
// Get point values
byte[] publicX = Hex
.decodeHex("fb136894878a9696d45fdb04506b9eb49ddcfba71e4e1b4ce23d5c3ac382d6b4".toCharArray());
byte[] publicY = Hex
.decodeHex("3deed825e808f8ed6a9a74ff6bd24e3d34b1c0c5fc253422f7febadbdc9cb9e6".toCharArray());
byte[] privateS = Hex
.decodeHex("35a8303e67a7e99d06552a0f8f6c8f1bf91a174396f4fad6211ae227e890da11".toCharArray());

// Get Elliptic Curve Parameter spec for secp256r1
AlgorithmParameters algoParameters = AlgorithmParameters.getInstance("EC");
algoParameters.init(new ECGenParameterSpec("secp256r1"));
ECParameterSpec parameterSpec = algoParameters.getParameterSpec(ECParameterSpec.class);

// Create key specs
KeySpec publicKeySpec = new ECPublicKeySpec(new ECPoint(new BigInteger(publicX), new BigInteger(publicY)),
parameterSpec);
KeySpec privateKeySpec = new ECPrivateKeySpec(new BigInteger(privateS), parameterSpec);

// Get keys
bootstrapServerPublicKey = KeyFactory.getInstance("EC").generatePublic(publicKeySpec);
bootstrapServerPrivateKey = KeyFactory.getInstance("EC").generatePrivate(privateKeySpec);
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}

public void createBootstrapServer(BootstrapSecurityStore securityStore, BootstrapStore bootstrapStore) {
if (bootstrapStore == null) {
Expand Down Expand Up @@ -91,6 +137,8 @@ public BootstrapConfig getBootstrap(String endpoint, Identity deviceIdentity) {
builder.setSecurityStore(securityStore);
builder.setLocalAddress(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
builder.setLocalSecureAddress(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
builder.setPrivateKey(bootstrapServerPrivateKey);
builder.setPublicKey(bootstrapServerPublicKey);

bootstrapServer = builder.build();
}
Expand Down Expand Up @@ -120,6 +168,16 @@ public void createPSKClient(String pskIdentity, byte[] pskKey) {
createClient(security);
}

@Override
public void createRPKClient() {
String bsUrl = "coaps://" + bootstrapServer.getSecuredAddress().getHostString() + ":"
+ bootstrapServer.getSecuredAddress().getPort();
Security security = Security.rpkBootstrap(bsUrl, clientPublicKey.getEncoded(), clientPrivateKey.getEncoded(),
bootstrapServerPublicKey.getEncoded());

createClient(security);
}

private void createClient(Security security) {
ObjectsInitializer initializer = new ObjectsInitializer();

Expand All @@ -137,26 +195,32 @@ private void createClient(Security security) {
setupClientMonitoring();
}

public BootstrapSecurityStore bsSecurityStore() {
public BootstrapSecurityStore bsSecurityStore(final SecurityMode mode) {

return new BootstrapSecurityStore() {
@Override
public SecurityInfo getByIdentity(String identity) {
if (BootstrapIntegrationTestHelper.GOOD_PSK_ID.equals(identity)) {
return pskSecurityInfo();
} else {
return null;
if (mode == SecurityMode.PSK) {
if (BootstrapIntegrationTestHelper.GOOD_PSK_ID.equals(identity)) {
return pskSecurityInfo();
}
}
return null;
}

@Override
public List<SecurityInfo> getAllByEndpoint(String endpoint) {
if (getCurrentEndpoint().equals(endpoint)) {
SecurityInfo info = pskSecurityInfo();
return Arrays.asList(info);
} else {
return Arrays.asList();
SecurityInfo info;
if (mode == SecurityMode.PSK) {
info = pskSecurityInfo();
return Arrays.asList(info);
} else if (mode == SecurityMode.RPK) {
info = rpkSecurityInfo();
return Arrays.asList(info);
}
}
return Arrays.asList();
}
};
}
Expand All @@ -167,6 +231,11 @@ public SecurityInfo pskSecurityInfo() {
return info;
}

public SecurityInfo rpkSecurityInfo() {
SecurityInfo info = SecurityInfo.newRawPublicKeyInfo(getCurrentEndpoint(), clientPublicKey);
return info;
}

private BootstrapSecurityStore dummyBsSecurityStore() {
return new BootstrapSecurityStore() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import static org.eclipse.leshan.integration.tests.SecureIntegrationTestHelper.*;

import org.eclipse.leshan.SecurityMode;
import org.eclipse.leshan.server.security.NonUniqueSecurityInfoException;
import org.eclipse.leshan.server.security.SecurityInfo;
import org.junit.After;
Expand Down Expand Up @@ -63,13 +64,13 @@ public void bootstrap() {
}

@Test
public void bootstrapSecure() {
public void bootstrapSecureWithPSK() {
// Create DM Server without security & start it
helper.createServer();
helper.server.start();

// Create and start bootstrap server
helper.createBootstrapServer(helper.bsSecurityStore());
helper.createBootstrapServer(helper.bsSecurityStore(SecurityMode.PSK));
helper.bootstrapServer.start();

// Create PSK Client and check it is not already registered
Expand All @@ -85,17 +86,17 @@ public void bootstrapSecure() {
}

@Test
public void bootstrapSecureWithBadCredentials() {
public void bootstrapSecureWithBadPSKKey() {
// Create DM Server without security & start it
helper.createServer();
helper.server.start();

// Create and start bootstrap server
helper.createBootstrapServer(helper.bsSecurityStore());
helper.createBootstrapServer(helper.bsSecurityStore(SecurityMode.PSK));
helper.bootstrapServer.start();

// Create PSK Client with bad credentials and check it is not already registered
helper.createPSKClient(GOOD_PSK_ID, BAD_PSK_KEY);
helper.createRPKClient();
helper.assertClientNotRegisterered();

// Start it and wait for registration
Expand All @@ -106,6 +107,28 @@ public void bootstrapSecureWithBadCredentials() {
helper.assertClientNotRegisterered();
}

@Test
public void bootstrapSecureWithRPK() {
// Create DM Server without security & start it
helper.createServer();
helper.server.start();

// Create and start bootstrap server
helper.createBootstrapServer(helper.bsSecurityStore(SecurityMode.RPK));
helper.bootstrapServer.start();

// Create RPK Client and check it is not already registered
helper.createRPKClient();
helper.assertClientNotRegisterered();

// Start it and wait for registration
helper.client.start();
helper.waitForRegistrationAtServerSide(5000);

// check the client is registered
helper.assertClientRegisterered();
}

@Test
public void bootstrapToPSKServer() throws NonUniqueSecurityInfoException {
// Create DM Server & start it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package org.eclipse.leshan.server.californium;

import java.net.InetSocketAddress;
import java.security.PrivateKey;
import java.security.PublicKey;

import org.eclipse.californium.core.network.CoapEndpoint;
import org.eclipse.californium.core.network.config.NetworkConfig;
Expand Down Expand Up @@ -57,6 +59,9 @@ public class LeshanBootstrapServerBuilder {
private NetworkConfig coapConfig;
private Builder dtlsConfigBuilder;

private PublicKey publicKey;
private PrivateKey privateKey;

private EndpointFactory endpointFactory;

private boolean noSecuredEndpoint;
Expand Down Expand Up @@ -115,6 +120,25 @@ public LeshanBootstrapServerBuilder setLocalSecureAddress(String hostname, int p
return this;
}

/**
* <p>
* Set the {@link PublicKey} of the server which will be used for RawPublicKey DTLS authentication.
* </p>
* This should be used for RPK support only.
*/
public LeshanBootstrapServerBuilder setPublicKey(PublicKey publicKey) {
this.publicKey = publicKey;
return this;
}

/**
* Set the {@link PrivateKey} of the server which will be used for RawPublicKey(RPK).
*/
public LeshanBootstrapServerBuilder setPrivateKey(PrivateKey privateKey) {
this.privateKey = privateKey;
return this;
}

/**
* <p>
* Set the address for secured CoAP Server (Using DTLS).
Expand Down Expand Up @@ -244,6 +268,25 @@ public LeshanBootstrapServer build() {
if (incompleteConfig.getStaleConnectionThreshold() == null)
dtlsConfigBuilder.setStaleConnectionThreshold(coapConfig.getLong(Keys.MAX_PEER_INACTIVITY_PERIOD));

// check conflict for private key
if (privateKey != null) {
if (incompleteConfig.getPrivateKey() != null && !incompleteConfig.getPrivateKey().equals(privateKey)) {
throw new IllegalStateException(String.format(
"Configuration conflict between LeshanBuilder and DtlsConnectorConfig.Builder for private key: %s != %s",
privateKey, incompleteConfig.getPrivateKey()));
}

if (publicKey != null) {
if (incompleteConfig.getPublicKey() != null && !incompleteConfig.getPublicKey().equals(publicKey)) {
throw new IllegalStateException(String.format(
"Configuration conflict between LeshanBuilder and DtlsConnectorConfig.Builder for public key: %s != %s",
publicKey, incompleteConfig.getPublicKey()));
}

dtlsConfigBuilder.setIdentity(privateKey, publicKey);
}
}

// Deactivate SNI by default
// TODO should we support SNI ?
if (incompleteConfig.isSniEnabled() == null) {
Expand Down

0 comments on commit 2cf53cc

Please sign in to comment.