Validate registration IDs
This commit is contained in:
		
							parent
							
								
									8b95bb0c03
								
							
						
					
					
						commit
						f46842c6c9
					
				| 
						 | 
					@ -8,7 +8,6 @@ import static org.whispersystems.textsecuregcm.metrics.MetricsUtil.name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import io.dropwizard.auth.Auth;
 | 
					import io.dropwizard.auth.Auth;
 | 
				
			||||||
import io.micrometer.core.instrument.Metrics;
 | 
					import io.micrometer.core.instrument.Metrics;
 | 
				
			||||||
import io.micrometer.core.instrument.Tags;
 | 
					 | 
				
			||||||
import io.swagger.v3.oas.annotations.Operation;
 | 
					import io.swagger.v3.oas.annotations.Operation;
 | 
				
			||||||
import io.swagger.v3.oas.annotations.Parameter;
 | 
					import io.swagger.v3.oas.annotations.Parameter;
 | 
				
			||||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
 | 
					import io.swagger.v3.oas.annotations.responses.ApiResponse;
 | 
				
			||||||
| 
						 | 
					@ -228,10 +227,6 @@ public class AccountController {
 | 
				
			||||||
    final Account account = disabledPermittedAuth.getAccount();
 | 
					    final Account account = disabledPermittedAuth.getAccount();
 | 
				
			||||||
    final byte deviceId = disabledPermittedAuth.getAuthenticatedDevice().getId();
 | 
					    final byte deviceId = disabledPermittedAuth.getAuthenticatedDevice().getId();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!AccountsManager.validNewAccountAttributes(attributes)) {
 | 
					 | 
				
			||||||
      Metrics.counter(INVALID_REGISTRATION_ID, Tags.of(UserAgentTagUtil.getPlatformTag(userAgent))).increment();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    final Account updatedAccount = accounts.update(account, a -> {
 | 
					    final Account updatedAccount = accounts.update(account, a -> {
 | 
				
			||||||
      a.getDevice(deviceId).ifPresent(d -> {
 | 
					      a.getDevice(deviceId).ifPresent(d -> {
 | 
				
			||||||
        d.setFetchesMessages(attributes.getFetchesMessages());
 | 
					        d.setFetchesMessages(attributes.getFetchesMessages());
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -107,10 +107,6 @@ public class RegistrationController {
 | 
				
			||||||
    final String password = authorizationHeader.getPassword();
 | 
					    final String password = authorizationHeader.getPassword();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    RateLimiter.adaptLegacyException(() -> rateLimiters.getRegistrationLimiter().validate(number));
 | 
					    RateLimiter.adaptLegacyException(() -> rateLimiters.getRegistrationLimiter().validate(number));
 | 
				
			||||||
    if (!AccountsManager.validNewAccountAttributes(registrationRequest.accountAttributes())) {
 | 
					 | 
				
			||||||
      Metrics.counter(INVALID_ACCOUNT_ATTRS_COUNTER_NAME, Tags.of(UserAgentTagUtil.getPlatformTag(userAgent))).increment();
 | 
					 | 
				
			||||||
      throw new WebApplicationException(Response.status(422, "account attributes invalid").build());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final PhoneVerificationRequest.VerificationType verificationType = phoneVerificationTokenManager.verify(number,
 | 
					    final PhoneVerificationRequest.VerificationType verificationType = phoneVerificationTokenManager.verify(number,
 | 
				
			||||||
        registrationRequest);
 | 
					        registrationRequest);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,12 +4,15 @@
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
package org.whispersystems.textsecuregcm.entities;
 | 
					package org.whispersystems.textsecuregcm.entities;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static org.whispersystems.textsecuregcm.util.RegistrationIdValidator.validRegistrationId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.fasterxml.jackson.annotation.JsonProperty;
 | 
					import com.fasterxml.jackson.annotation.JsonProperty;
 | 
				
			||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 | 
					import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 | 
				
			||||||
import com.google.common.annotations.VisibleForTesting;
 | 
					import com.google.common.annotations.VisibleForTesting;
 | 
				
			||||||
import java.util.Optional;
 | 
					import java.util.Optional;
 | 
				
			||||||
import java.util.OptionalInt;
 | 
					import java.util.OptionalInt;
 | 
				
			||||||
import javax.annotation.Nullable;
 | 
					import javax.annotation.Nullable;
 | 
				
			||||||
 | 
					import javax.validation.constraints.AssertTrue;
 | 
				
			||||||
import javax.validation.constraints.Size;
 | 
					import javax.validation.constraints.Size;
 | 
				
			||||||
import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessUtil;
 | 
					import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessUtil;
 | 
				
			||||||
import org.whispersystems.textsecuregcm.storage.Device.DeviceCapabilities;
 | 
					import org.whispersystems.textsecuregcm.storage.Device.DeviceCapabilities;
 | 
				
			||||||
| 
						 | 
					@ -127,4 +130,10 @@ public class AccountAttributes {
 | 
				
			||||||
  public void setPhoneNumberIdentityRegistrationId(final Integer phoneNumberIdentityRegistrationId) {
 | 
					  public void setPhoneNumberIdentityRegistrationId(final Integer phoneNumberIdentityRegistrationId) {
 | 
				
			||||||
    this.phoneNumberIdentityRegistrationId = phoneNumberIdentityRegistrationId;
 | 
					    this.phoneNumberIdentityRegistrationId = phoneNumberIdentityRegistrationId;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @AssertTrue
 | 
				
			||||||
 | 
					  public boolean isEachRegistrationIdValid() {
 | 
				
			||||||
 | 
					    return validRegistrationId(registrationId) &&
 | 
				
			||||||
 | 
					        (phoneNumberIdentityRegistrationId == null || validRegistrationId(phoneNumberIdentityRegistrationId));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,7 @@ import javax.validation.constraints.NotNull;
 | 
				
			||||||
import org.signal.libsignal.protocol.IdentityKey;
 | 
					import org.signal.libsignal.protocol.IdentityKey;
 | 
				
			||||||
import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
 | 
					import org.whispersystems.textsecuregcm.util.ByteArrayAdapter;
 | 
				
			||||||
import org.whispersystems.textsecuregcm.util.IdentityKeyAdapter;
 | 
					import org.whispersystems.textsecuregcm.util.IdentityKeyAdapter;
 | 
				
			||||||
 | 
					import org.whispersystems.textsecuregcm.util.RegistrationIdValidator;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public record ChangeNumberRequest(
 | 
					public record ChangeNumberRequest(
 | 
				
			||||||
    @Schema(description="""
 | 
					    @Schema(description="""
 | 
				
			||||||
| 
						 | 
					@ -78,4 +79,9 @@ public record ChangeNumberRequest(
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return spks.isEmpty() || PreKeySignatureValidator.validatePreKeySignatures(pniIdentityKey, spks);
 | 
					    return spks.isEmpty() || PreKeySignatureValidator.validatePreKeySignatures(pniIdentityKey, spks);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @AssertTrue
 | 
				
			||||||
 | 
					  public boolean isEachPniRegistrationIdValid() {
 | 
				
			||||||
 | 
					    return pniRegistrationIds == null || pniRegistrationIds.values().stream().allMatch(RegistrationIdValidator::validRegistrationId);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,6 +16,7 @@ public record LinkDeviceRequest(@Schema(requiredMode = Schema.RequiredMode.REQUI
 | 
				
			||||||
                                """)
 | 
					                                """)
 | 
				
			||||||
                                String verificationCode,
 | 
					                                String verificationCode,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                @Valid
 | 
				
			||||||
                                AccountAttributes accountAttributes,
 | 
					                                AccountAttributes accountAttributes,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                @NotNull
 | 
					                                @NotNull
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,7 +33,6 @@ import java.util.Map;
 | 
				
			||||||
import java.util.NoSuchElementException;
 | 
					import java.util.NoSuchElementException;
 | 
				
			||||||
import java.util.Objects;
 | 
					import java.util.Objects;
 | 
				
			||||||
import java.util.Optional;
 | 
					import java.util.Optional;
 | 
				
			||||||
import java.util.OptionalInt;
 | 
					 | 
				
			||||||
import java.util.Queue;
 | 
					import java.util.Queue;
 | 
				
			||||||
import java.util.UUID;
 | 
					import java.util.UUID;
 | 
				
			||||||
import java.util.concurrent.CompletableFuture;
 | 
					import java.util.concurrent.CompletableFuture;
 | 
				
			||||||
| 
						 | 
					@ -386,21 +385,6 @@ public class AccountsManager {
 | 
				
			||||||
    return updatedAccount.get();
 | 
					    return updatedAccount.get();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public static boolean validNewAccountAttributes(final AccountAttributes accountAttributes) {
 | 
					 | 
				
			||||||
    if (!validRegistrationId(accountAttributes.getRegistrationId())) {
 | 
					 | 
				
			||||||
      return false;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    final OptionalInt pniRegistrationId = accountAttributes.getPhoneNumberIdentityRegistrationId();
 | 
					 | 
				
			||||||
    if (pniRegistrationId.isPresent() && !validRegistrationId(pniRegistrationId.getAsInt())) {
 | 
					 | 
				
			||||||
      return false;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private static boolean validRegistrationId(int registrationId) {
 | 
					 | 
				
			||||||
    return registrationId > 0 && registrationId <= Device.MAX_REGISTRATION_ID;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public Account updatePniKeys(final Account account,
 | 
					  public Account updatePniKeys(final Account account,
 | 
				
			||||||
      final IdentityKey pniIdentityKey,
 | 
					      final IdentityKey pniIdentityKey,
 | 
				
			||||||
      final Map<Byte, ECSignedPreKey> pniSignedPreKeys,
 | 
					      final Map<Byte, ECSignedPreKey> pniSignedPreKeys,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -56,7 +56,6 @@ public class ChangeNumberManager {
 | 
				
			||||||
      throw new IllegalArgumentException("PNI identity key, signed pre-keys, device messages, and registration IDs must be all null or all non-null");
 | 
					      throw new IllegalArgumentException("PNI identity key, signed pre-keys, device messages, and registration IDs must be all null or all non-null");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (number.equals(account.getNumber())) {
 | 
					    if (number.equals(account.getNumber())) {
 | 
				
			||||||
      // The client has gotten confused/desynchronized with us about their own phone number, most likely due to losing
 | 
					      // The client has gotten confused/desynchronized with us about their own phone number, most likely due to losing
 | 
				
			||||||
      // our OK response to an immediately preceding change-number request, and are sending a change they don't realize
 | 
					      // our OK response to an immediately preceding change-number request, and are sending a change they don't realize
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,15 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2023 Signal Messenger, LLC
 | 
				
			||||||
 | 
					 * SPDX-License-Identifier: AGPL-3.0-only
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package org.whispersystems.textsecuregcm.util;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.whispersystems.textsecuregcm.storage.Device;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class RegistrationIdValidator {
 | 
				
			||||||
 | 
					  public static boolean validRegistrationId(int registrationId) {
 | 
				
			||||||
 | 
					    return registrationId > 0 && registrationId <= Device.MAX_REGISTRATION_ID;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -265,6 +265,36 @@ class AccountControllerV2Test {
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @ParameterizedTest
 | 
				
			||||||
 | 
					    @MethodSource
 | 
				
			||||||
 | 
					    void invalidRegistrationId(final Integer pniRegistrationId, final int expectedStatusCode) {
 | 
				
			||||||
 | 
					      when(registrationServiceClient.getSession(any(), any()))
 | 
				
			||||||
 | 
					          .thenReturn(CompletableFuture.completedFuture(
 | 
				
			||||||
 | 
					              Optional.of(new RegistrationServiceSession(new byte[16], NEW_NUMBER, true, null, null, null,
 | 
				
			||||||
 | 
					                  SESSION_EXPIRATION_SECONDS))));
 | 
				
			||||||
 | 
					      final ChangeNumberRequest changeNumberRequest = new ChangeNumberRequest(encodeSessionId("session"),
 | 
				
			||||||
 | 
					          null, NEW_NUMBER, "123", new IdentityKey(Curve.generateKeyPair().getPublicKey()),
 | 
				
			||||||
 | 
					          Collections.emptyList(), Collections.emptyMap(), null, Map.of((byte) 1, pniRegistrationId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      final Response response = resources.getJerseyTest()
 | 
				
			||||||
 | 
					          .target("/v2/accounts/number")
 | 
				
			||||||
 | 
					          .request()
 | 
				
			||||||
 | 
					          .header(HttpHeaders.AUTHORIZATION,
 | 
				
			||||||
 | 
					              AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
 | 
				
			||||||
 | 
					          .put(Entity.entity(changeNumberRequest, MediaType.APPLICATION_JSON_TYPE));
 | 
				
			||||||
 | 
					      assertEquals(expectedStatusCode, response.getStatus());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static Stream<Arguments> invalidRegistrationId() {
 | 
				
			||||||
 | 
					      return Stream.of(
 | 
				
			||||||
 | 
					          Arguments.of(0x3FFF, 200),
 | 
				
			||||||
 | 
					          Arguments.of(0, 422),
 | 
				
			||||||
 | 
					          Arguments.of(-1, 422),
 | 
				
			||||||
 | 
					          Arguments.of(0x3FFF + 1, 422),
 | 
				
			||||||
 | 
					          Arguments.of(Integer.MAX_VALUE, 422)
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Test
 | 
					    @Test
 | 
				
			||||||
    void rateLimitedNumber() throws Exception {
 | 
					    void rateLimitedNumber() throws Exception {
 | 
				
			||||||
      doThrow(new RateLimitExceededException(null, true))
 | 
					      doThrow(new RateLimitExceededException(null, true))
 | 
				
			||||||
| 
						 | 
					@ -398,13 +428,20 @@ class AccountControllerV2Test {
 | 
				
			||||||
     * Valid request JSON with the given Recovery Password
 | 
					     * Valid request JSON with the given Recovery Password
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private static String requestJsonRecoveryPassword(final byte[] recoveryPassword, final String newNumber) {
 | 
					    private static String requestJsonRecoveryPassword(final byte[] recoveryPassword, final String newNumber) {
 | 
				
			||||||
      return requestJson("", recoveryPassword, newNumber);
 | 
					      return requestJson("", recoveryPassword, newNumber, 123);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Valid request JSON with the given pniRegistrationId
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private static String requestJsonRegistrationIds(final Integer pniRegistrationId) {
 | 
				
			||||||
 | 
					      return requestJson("", new byte[0], "+18005551234", pniRegistrationId);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Valid request JSON with the give session ID and recovery password
 | 
					     * Valid request JSON with the give session ID and recovery password
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private static String requestJson(final String sessionId, final byte[] recoveryPassword, final String newNumber) {
 | 
					    private static String requestJson(final String sessionId, final byte[] recoveryPassword, final String newNumber, final Integer pniRegistrationId) {
 | 
				
			||||||
      return String.format("""
 | 
					      return String.format("""
 | 
				
			||||||
          {
 | 
					          {
 | 
				
			||||||
            "sessionId": "%s",
 | 
					            "sessionId": "%s",
 | 
				
			||||||
| 
						 | 
					@ -414,16 +451,16 @@ class AccountControllerV2Test {
 | 
				
			||||||
            "pniIdentityKey": "%s",
 | 
					            "pniIdentityKey": "%s",
 | 
				
			||||||
            "deviceMessages": [],
 | 
					            "deviceMessages": [],
 | 
				
			||||||
            "devicePniSignedPrekeys": {},
 | 
					            "devicePniSignedPrekeys": {},
 | 
				
			||||||
            "pniRegistrationIds": {}
 | 
					            "pniRegistrationIds": {"1": %d}
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          """, encodeSessionId(sessionId), encodeRecoveryPassword(recoveryPassword), newNumber, Base64.getEncoder().encodeToString(IDENTITY_KEY.serialize()));
 | 
					          """, encodeSessionId(sessionId), encodeRecoveryPassword(recoveryPassword), newNumber, Base64.getEncoder().encodeToString(IDENTITY_KEY.serialize()), pniRegistrationId);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Valid request JSON with the give session ID
 | 
					     * Valid request JSON with the give session ID
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private static String requestJson(final String sessionId, final String newNumber) {
 | 
					    private static String requestJson(final String sessionId, final String newNumber) {
 | 
				
			||||||
      return requestJson(sessionId, new byte[0], newNumber);
 | 
					      return requestJson(sessionId, new byte[0], newNumber, 123);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -576,6 +576,56 @@ class DeviceControllerTest {
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @ParameterizedTest
 | 
				
			||||||
 | 
					  @MethodSource
 | 
				
			||||||
 | 
					  void linkDeviceRegistrationId(final int registrationId, final int expectedStatusCode) {
 | 
				
			||||||
 | 
					    final Device existingDevice = mock(Device.class);
 | 
				
			||||||
 | 
					    when(existingDevice.getId()).thenReturn(Device.PRIMARY_ID);
 | 
				
			||||||
 | 
					    when(AuthHelper.VALID_ACCOUNT.getDevices()).thenReturn(List.of(existingDevice));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    VerificationCode deviceCode = resources.getJerseyTest()
 | 
				
			||||||
 | 
					        .target("/v1/devices/provisioning/code")
 | 
				
			||||||
 | 
					        .request()
 | 
				
			||||||
 | 
					        .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
 | 
				
			||||||
 | 
					        .get(VerificationCode.class);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final ECKeyPair aciIdentityKeyPair = Curve.generateKeyPair();
 | 
				
			||||||
 | 
					    final ECKeyPair pniIdentityKeyPair = Curve.generateKeyPair();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final ECSignedPreKey aciSignedPreKey = KeysHelper.signedECPreKey(1, aciIdentityKeyPair);
 | 
				
			||||||
 | 
					    final ECSignedPreKey pniSignedPreKey = KeysHelper.signedECPreKey(2, pniIdentityKeyPair);
 | 
				
			||||||
 | 
					    final KEMSignedPreKey aciPqLastResortPreKey = KeysHelper.signedKEMPreKey(3, aciIdentityKeyPair);
 | 
				
			||||||
 | 
					    final KEMSignedPreKey pniPqLastResortPreKey = KeysHelper.signedKEMPreKey(4, pniIdentityKeyPair);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    when(account.getIdentityKey(IdentityType.ACI)).thenReturn(new IdentityKey(aciIdentityKeyPair.getPublicKey()));
 | 
				
			||||||
 | 
					    when(account.getIdentityKey(IdentityType.PNI)).thenReturn(new IdentityKey(pniIdentityKeyPair.getPublicKey()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    when(keysManager.storeEcSignedPreKeys(any(), any())).thenReturn(CompletableFuture.completedFuture(null));
 | 
				
			||||||
 | 
					    when(keysManager.storePqLastResort(any(), any())).thenReturn(CompletableFuture.completedFuture(null));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final LinkDeviceRequest request = new LinkDeviceRequest(deviceCode.verificationCode(),
 | 
				
			||||||
 | 
					        new AccountAttributes(false, registrationId, null, null, true, null),
 | 
				
			||||||
 | 
					        new DeviceActivationRequest(aciSignedPreKey, pniSignedPreKey, aciPqLastResortPreKey, pniPqLastResortPreKey, Optional.of(new ApnRegistrationId("apn", null)), Optional.empty()));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try (final Response response = resources.getJerseyTest()
 | 
				
			||||||
 | 
					        .target("/v1/devices/link")
 | 
				
			||||||
 | 
					        .request()
 | 
				
			||||||
 | 
					        .header("Authorization", AuthHelper.getProvisioningAuthHeader(AuthHelper.VALID_NUMBER, "password1"))
 | 
				
			||||||
 | 
					        .put(Entity.entity(request, MediaType.APPLICATION_JSON_TYPE))) {
 | 
				
			||||||
 | 
					      assertEquals(expectedStatusCode, response.getStatus());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static Stream<Arguments> linkDeviceRegistrationId() {
 | 
				
			||||||
 | 
					    return Stream.of(
 | 
				
			||||||
 | 
					        Arguments.of(0x3FFF, 200),
 | 
				
			||||||
 | 
					        Arguments.of(0, 422),
 | 
				
			||||||
 | 
					        Arguments.of(-1, 422),
 | 
				
			||||||
 | 
					        Arguments.of(0x3FFF + 1, 422),
 | 
				
			||||||
 | 
					        Arguments.of(Integer.MAX_VALUE, 422)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static ECSignedPreKey ecSignedPreKeyWithBadSignature(final ECSignedPreKey signedPreKey) {
 | 
					  private static ECSignedPreKey ecSignedPreKeyWithBadSignature(final ECSignedPreKey signedPreKey) {
 | 
				
			||||||
    return new ECSignedPreKey(signedPreKey.keyId(),
 | 
					    return new ECSignedPreKey(signedPreKey.keyId(),
 | 
				
			||||||
        signedPreKey.publicKey(),
 | 
					        signedPreKey.publicKey(),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue