use of com.quorum.tessera.partyinfo.model.PartyInfo in project tessera by ConsenSys.
the class PartyInfoParserTest method from.
@Test
public void from() {
PartyInfo result = partyInfoParser.from(dataOne);
assertThat(result).isNotNull();
assertThat(result.getUrl()).isEqualTo("http://localhost:8000");
assertThat(result.getRecipients()).hasSize(1);
final Recipient recipient = result.getRecipients().iterator().next();
assertThat(recipient.getUrl()).isEqualTo("http://localhost:8001");
assertThat(recipient.getKey()).isNotNull();
assertThat(result.getParties()).hasSize(1);
assertThat(result.getParties()).containsExactly(new Party("http://localhost:8001"));
}
use of com.quorum.tessera.partyinfo.model.PartyInfo in project tessera by ConsenSys.
the class PartyInfoParserTest method multiplePartiesParses.
@Test
public void multiplePartiesParses() {
final PartyInfo result = partyInfoParser.from(dataTwo);
assertThat(result.getParties()).containsExactlyInAnyOrder(new Party("https://127.0.0.5:9005/"), new Party("https://127.0.0.3:9003/"), new Party("https://127.0.0.1:9001/"), new Party("https://127.0.0.7:9007/"), new Party("https://127.0.0.6:9006/"), new Party("https://127.0.0.4:9004/"), new Party("https://127.0.0.2:9002/"));
assertThat(result.getRecipients()).containsExactlyInAnyOrder(Recipient.of(toKey("ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc="), "https://127.0.0.7:9007/"), Recipient.of(toKey("BULeR8JyUWhiuuCMU/HLA0Q5pzkYT+cHII3ZKBey3Bo="), "https://127.0.0.1:9001/"), Recipient.of(toKey("QfeDAys9MPDs2XHExtc84jKGHxZg/aj52DTh0vtA3Xc="), "https://127.0.0.2:9002/"), Recipient.of(toKey("1iTZde/ndBHvzhcl7V68x44Vx7pl8nwx9LqnM/AfJUg="), "https://127.0.0.3:9003/"), Recipient.of(toKey("UfNSeSGySeKg11DVNEnqrUtxYRVor4+CvluI8tVv62Y="), "https://127.0.0.6:9006/"), Recipient.of(toKey("oNspPPgszVUFw0qmGFfWwh1uxVUXgvBxleXORHj07g8="), "https://127.0.0.4:9004/"), Recipient.of(toKey("R56gy4dn24YOjwyesTczYa8m5xhP6hF2uTMCju/1xkY="), "https://127.0.0.5:9005/"));
}
use of com.quorum.tessera.partyinfo.model.PartyInfo in project tessera by ConsenSys.
the class PartyInfoParserTest method toUsingSameInfoFromFixture.
@Test
public void toUsingSameInfoFromFixture() {
final PartyInfo partyInfo = partyInfoParser.from(dataOne);
final byte[] result = partyInfoParser.to(partyInfo);
final ByteBuffer byteBuffer = ByteBuffer.wrap(result);
assertThat(result).isNotEmpty();
assertThat(byteBuffer.getLong()).isEqualTo(21L);
byte[] urlData = new byte[21];
byteBuffer.get(urlData);
final String url = new String(urlData);
assertThat(url).isEqualTo(partyInfo.getUrl());
long numberOfRecipients = byteBuffer.getLong();
assertThat(numberOfRecipients).isEqualTo(1L);
long keyByteLength = byteBuffer.getLong();
assertThat(keyByteLength).isEqualTo(32L);
final byte[] keyData = new byte[32];
byteBuffer.get(keyData);
assertThat(keyData).hasSize(32).isEqualTo(partyInfo.getRecipients().iterator().next().getKey().getKeyBytes());
long recipientUrlLength = byteBuffer.getLong();
assertThat(recipientUrlLength).isEqualTo(21L);
byte[] recipientUrlData = new byte[21];
byteBuffer.get(recipientUrlData);
String recipientUrl = new String(recipientUrlData);
assertThat(recipientUrl).isEqualTo(partyInfo.getRecipients().iterator().next().getUrl());
long partyCount = byteBuffer.getLong();
assertThat(partyCount).isEqualTo(1L);
long partyUrlLength = byteBuffer.getLong();
assertThat(partyUrlLength).isEqualTo(22L);
byte[] partyUrlData = new byte[22];
byteBuffer.get(partyUrlData);
String partyUrl = new String(partyUrlData);
assertThat(partyUrl).isEqualTo(partyInfo.getParties().iterator().next().getUrl());
}
use of com.quorum.tessera.partyinfo.model.PartyInfo in project tessera by ConsenSys.
the class PartyInfoResource method partyInfo.
/**
* Update the local partyinfo store with the encoded partyinfo included in the request.
*
* @param payload The encoded partyinfo information pushed by the caller
* @return an empty 200 OK Response if the local node is using remote key validation; a 200 OK
* Response wrapping an encoded partyinfo that contains only the local node's URL if not using
* remote key validation; a 500 Internal Server Error if remote key validation fails
*/
@Operation(summary = "/partyinfo", operationId = "broadcastPartyInfo", description = "broadcast partyinfo information to server")
@ApiResponse(responseCode = "200", description = "server successfully updated its party info", content = @Content(array = @ArraySchema(schema = @Schema(description = "empty if server is using remote key validation, else is encoded partyinfo object containing only the server's URL", type = "string", format = "byte"))))
@ApiResponse(responseCode = "500", description = "Validation failed (if server is using remote key validation)")
@POST
@Consumes(MediaType.APPLICATION_OCTET_STREAM)
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response partyInfo(@RequestBody(required = true, description = "partyinfo object") final byte[] payload, @HeaderParam(Constants.API_VERSION_HEADER) @Parameter(description = "client's supported API versions", array = @ArraySchema(schema = @Schema(type = "string"))) final List<String> headers) {
final PartyInfo partyInfo = partyInfoParser.from(payload);
final Set<String> versions = Optional.ofNullable(headers).orElse(emptyList()).stream().filter(Objects::nonNull).flatMap(v -> Arrays.stream(v.split(","))).collect(Collectors.toSet());
final NodeInfo nodeInfo = NodeInfoUtil.from(partyInfo, versions);
LOGGER.debug("Received PartyInfo from {}", partyInfo.getUrl());
if (!enableKeyValidation) {
LOGGER.debug("Key validation not enabled, passing PartyInfo through");
discovery.onUpdate(nodeInfo);
partyInfo.getParties().stream().map(Party::getUrl).map(NodeUri::create).map(NodeUri::asURI).forEach(partyStore::store);
// create an empty party info object with our URL to send back
// this is used by older versions (before 0.10.0), but we don't want to give any info back
final PartyInfo emptyInfo = new PartyInfo(discovery.getCurrent().getUrl(), emptySet(), emptySet());
final byte[] returnData = partyInfoParser.to(emptyInfo);
return Response.ok(returnData).build();
}
final PublicKey localPublicKey = enclave.defaultPublicKey();
final Predicate<Recipient> isValidRecipient = r -> {
try {
LOGGER.debug("Validating key {} for peer {}", r.getKey(), r.getUrl());
final String dataToEncrypt = UUID.randomUUID().toString();
final EncodedPayload encodedPayload = enclave.encryptPayload(dataToEncrypt.getBytes(), localPublicKey, List.of(r.getKey()), PrivacyMetadata.Builder.forStandardPrivate().build());
final byte[] encodedPayloadBytes = payloadEncoder.encode(encodedPayload);
try (Response response = restClient.target(r.getUrl()).path("partyinfo").path("validate").request().post(Entity.entity(encodedPayloadBytes, MediaType.APPLICATION_OCTET_STREAM))) {
LOGGER.debug("Response code {} from peer {}", response.getStatus(), r.getUrl());
final String responseData = response.readEntity(String.class);
final boolean isValid = Objects.equals(responseData, dataToEncrypt);
if (!isValid) {
LOGGER.warn("Validation of key {} for peer {} failed. Key and peer will not be added to local partyinfo.", r.getKey(), r.getUrl());
LOGGER.debug("Response from {} was {}", r.getUrl(), responseData);
}
return isValid;
}
// Assume any and all exceptions to mean invalid. enclave bubbles up nacl array out of
// bounds when calculating shared key from invalid data
} catch (Exception ex) {
LOGGER.debug(null, ex);
return false;
}
};
final String partyInfoSender = partyInfo.getUrl();
final Predicate<Recipient> isSender = r -> NodeUri.create(r.getUrl()).equals(NodeUri.create(partyInfoSender));
// Validate caller and treat no valid certs as security issue.
final Set<com.quorum.tessera.partyinfo.node.Recipient> validatedSendersKeys = partyInfo.getRecipients().stream().filter(isSender.and(isValidRecipient)).map(r -> com.quorum.tessera.partyinfo.node.Recipient.of(r.getKey(), r.getUrl())).collect(Collectors.toSet());
LOGGER.debug("Validated keys for peer {}: {}", partyInfoSender, validatedSendersKeys);
if (validatedSendersKeys.isEmpty()) {
throw new SecurityException("No validated keys found for peer " + partyInfoSender);
}
// End validation stuff
final NodeInfo reducedNodeInfo = NodeInfo.Builder.create().withUrl(partyInfoSender).withSupportedApiVersions(versions).withRecipients(validatedSendersKeys).build();
discovery.onUpdate(reducedNodeInfo);
partyInfo.getParties().stream().map(Party::getUrl).map(NodeUri::create).map(NodeUri::asURI).forEach(partyStore::store);
return Response.ok().build();
}
use of com.quorum.tessera.partyinfo.model.PartyInfo in project tessera by ConsenSys.
the class PartyInfoResourceTest method partyInfoExceptionIfValidationFailsWith400.
@Test
public void partyInfoExceptionIfValidationFailsWith400() {
final int validateResponseCode = 400;
final String validateResponseMsg = null;
String url = "http://www.bogus.com";
PublicKey myKey = PublicKey.from("myKey".getBytes());
PublicKey recipientKey = PublicKey.from("recipientKey".getBytes());
String message = "I love sparrows";
byte[] payload = message.getBytes();
Recipient recipient = Recipient.of(recipientKey, url);
Set<Recipient> recipientList = Collections.singleton(recipient);
PartyInfo partyInfo = new PartyInfo(url, recipientList, Collections.emptySet());
when(partyInfoParser.from(payload)).thenReturn(partyInfo);
when(enclave.defaultPublicKey()).thenReturn(myKey);
when(partyInfoParser.to(partyInfo)).thenReturn(payload);
EncodedPayload encodedPayload = mock(EncodedPayload.class);
when(enclave.encryptPayload(any(byte[].class), any(PublicKey.class), anyList(), any(PrivacyMetadata.class))).thenReturn(encodedPayload);
when(payloadEncoder.encode(encodedPayload)).thenReturn(payload);
WebTarget webTarget = mock(WebTarget.class);
when(restClient.target(url)).thenReturn(webTarget);
when(webTarget.path(anyString())).thenReturn(webTarget);
Invocation.Builder invocationBuilder = mock(Invocation.Builder.class);
when(webTarget.request()).thenReturn(invocationBuilder);
Response response = mock(Response.class);
when(response.getStatus()).thenReturn(validateResponseCode);
doAnswer((invocation) -> validateResponseMsg).when(response).readEntity(String.class);
when(invocationBuilder.post(any(Entity.class))).thenReturn(response);
try {
partyInfoResource.partyInfo(payload, List.of("v1", "v2"));
failBecauseExceptionWasNotThrown(SecurityException.class);
} catch (SecurityException ex) {
verify(partyInfoParser).from(payload);
verify(enclave).defaultPublicKey();
verify(enclave).encryptPayload(any(byte[].class), any(PublicKey.class), anyList(), any(PrivacyMetadata.class));
verify(payloadEncoder).encode(encodedPayload);
verify(restClient).target(url);
}
}
Aggregations