diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/auth/CertificateGenerator.java b/service/src/main/java/org/whispersystems/textsecuregcm/auth/CertificateGenerator.java index 20f6c01e9..9e24671a9 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/auth/CertificateGenerator.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/auth/CertificateGenerator.java @@ -28,18 +28,20 @@ public class CertificateGenerator { this.serverCertificate = ServerCertificate.parseFrom(serverCertificate); } - public byte[] createFor(Account account, Device device) throws IOException, InvalidKeyException { - byte[] certificate = SenderCertificate.Certificate.newBuilder() - .setSender(account.getNumber()) - .setSenderUuid(account.getUuid().toString()) - .setSenderDevice(Math.toIntExact(device.getId())) - .setExpires(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(expiresDays)) - .setIdentityKey(ByteString.copyFrom(Base64.decode(account.getIdentityKey()))) - .setSigner(serverCertificate) - .build() - .toByteArray(); + public byte[] createFor(Account account, Device device, boolean includeUuid) throws IOException, InvalidKeyException { + SenderCertificate.Certificate.Builder builder = SenderCertificate.Certificate.newBuilder() + .setSender(account.getNumber()) + .setSenderDevice(Math.toIntExact(device.getId())) + .setExpires(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(expiresDays)) + .setIdentityKey(ByteString.copyFrom(Base64.decode(account.getIdentityKey()))) + .setSigner(serverCertificate); - byte[] signature = Curve.calculateSignature(privateKey, certificate); + if (includeUuid) { + builder.setSenderUuid(account.getUuid().toString()); + } + + byte[] certificate = builder.build().toByteArray(); + byte[] signature = Curve.calculateSignature(privateKey, certificate); return SenderCertificate.newBuilder() .setCertificate(ByteString.copyFrom(certificate)) diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/CertificateController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/CertificateController.java index 9ac66cf1e..127531cd4 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/CertificateController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/CertificateController.java @@ -11,14 +11,17 @@ import org.whispersystems.textsecuregcm.util.Util; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.io.IOException; import java.security.InvalidKeyException; +import java.util.Optional; import io.dropwizard.auth.Auth; +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") @Path("/v1/certificate") public class CertificateController { @@ -34,14 +37,17 @@ public class CertificateController { @GET @Produces(MediaType.APPLICATION_JSON) @Path("/delivery") - public DeliveryCertificate getDeliveryCertificate(@Auth Account account) throws IOException, InvalidKeyException { + public DeliveryCertificate getDeliveryCertificate(@Auth Account account, + @QueryParam("includeUuid") Optional includeUuid) + throws IOException, InvalidKeyException + { if (!account.getAuthenticatedDevice().isPresent()) throw new AssertionError(); if (Util.isEmpty(account.getIdentityKey())) { throw new WebApplicationException(Response.Status.BAD_REQUEST); } - return new DeliveryCertificate(certificateGenerator.createFor(account, account.getAuthenticatedDevice().get())); + return new DeliveryCertificate(certificateGenerator.createFor(account, account.getAuthenticatedDevice().get(), includeUuid.orElse(false))); } } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/CertificateControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/CertificateControllerTest.java index 81239758d..3b0d9eab2 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/CertificateControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/CertificateControllerTest.java @@ -25,6 +25,7 @@ import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider; import io.dropwizard.testing.junit.ResourceTestRule; import static junit.framework.TestCase.assertTrue; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; public class CertificateControllerTest { @@ -64,6 +65,31 @@ public class CertificateControllerTest { .get(DeliveryCertificate.class); + SenderCertificate certificateHolder = SenderCertificate.parseFrom(certificateObject.getCertificate()); + SenderCertificate.Certificate certificate = SenderCertificate.Certificate.parseFrom(certificateHolder.getCertificate()); + + ServerCertificate serverCertificateHolder = certificate.getSigner(); + ServerCertificate.Certificate serverCertificate = ServerCertificate.Certificate.parseFrom(serverCertificateHolder.getCertificate()); + + assertTrue(Curve.verifySignature(Curve.decodePoint(serverCertificate.getKey().toByteArray(), 0), certificateHolder.getCertificate().toByteArray(), certificateHolder.getSignature().toByteArray())); + assertTrue(Curve.verifySignature(Curve.decodePoint(Base64.decode(caPublicKey), 0), serverCertificateHolder.getCertificate().toByteArray(), serverCertificateHolder.getSignature().toByteArray())); + + assertEquals(certificate.getSender(), AuthHelper.VALID_NUMBER); + assertEquals(certificate.getSenderDevice(), 1L); + assertFalse(certificate.hasSenderUuid()); + assertTrue(Arrays.equals(certificate.getIdentityKey().toByteArray(), Base64.decode(AuthHelper.VALID_IDENTITY))); + } + + @Test + public void testValidCertificateWithUuid() throws Exception { + DeliveryCertificate certificateObject = resources.getJerseyTest() + .target("/v1/certificate/delivery") + .queryParam("includeUuid", "true") + .request() + .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD)) + .get(DeliveryCertificate.class); + + SenderCertificate certificateHolder = SenderCertificate.parseFrom(certificateObject.getCertificate()); SenderCertificate.Certificate certificate = SenderCertificate.Certificate.parseFrom(certificateHolder.getCertificate());