Handle edge cases of Math.abs on integers.
This commit is contained in:
		
							parent
							
								
									2c2c497c12
								
							
						
					
					
						commit
						d138fa45df
					
				|  | @ -7,6 +7,7 @@ package org.whispersystems.textsecuregcm.auth; | ||||||
| import com.google.common.annotations.VisibleForTesting; | import com.google.common.annotations.VisibleForTesting; | ||||||
| import org.apache.commons.codec.binary.Hex; | import org.apache.commons.codec.binary.Hex; | ||||||
| import org.signal.libsignal.protocol.kdf.HKDF; | import org.signal.libsignal.protocol.kdf.HKDF; | ||||||
|  | import org.whispersystems.textsecuregcm.util.Util; | ||||||
| 
 | 
 | ||||||
| import java.nio.charset.StandardCharsets; | import java.nio.charset.StandardCharsets; | ||||||
| import java.security.MessageDigest; | import java.security.MessageDigest; | ||||||
|  | @ -32,13 +33,13 @@ public class AuthenticationCredentials { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public AuthenticationCredentials(String authenticationToken) { |   public AuthenticationCredentials(String authenticationToken) { | ||||||
|     this.salt                      = String.valueOf(Math.abs(new SecureRandom().nextInt())); |     this.salt = String.valueOf(Util.ensureNonNegativeInt(new SecureRandom().nextInt())); | ||||||
|     this.hashedAuthenticationToken = getV2HashedValue(salt, authenticationToken); |     this.hashedAuthenticationToken = getV2HashedValue(salt, authenticationToken); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @VisibleForTesting |   @VisibleForTesting | ||||||
|   public AuthenticationCredentials v1ForTesting(String authenticationToken) { |   public AuthenticationCredentials v1ForTesting(String authenticationToken) { | ||||||
|     String salt = String.valueOf(Math.abs(new SecureRandom().nextInt())); |     String salt = String.valueOf(Util.ensureNonNegativeInt(new SecureRandom().nextInt())); | ||||||
|     return new AuthenticationCredentials(getV1HashedValue(salt, authenticationToken), salt); |     return new AuthenticationCredentials(getV1HashedValue(salt, authenticationToken), salt); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -146,9 +146,18 @@ public class BaseAccountAuthenticator { | ||||||
| 
 | 
 | ||||||
|   @VisibleForTesting |   @VisibleForTesting | ||||||
|   public Account updateLastSeen(Account account, Device device) { |   public Account updateLastSeen(Account account, Device device) { | ||||||
|     final long lastSeenOffsetSeconds   = Math.abs(account.getUuid().getLeastSignificantBits()) % ChronoUnit.DAYS.getDuration().toSeconds(); |     // compute a non-negative integer between 0 and 86400. | ||||||
|  |     long n = Util.ensureNonNegativeLong(account.getUuid().getLeastSignificantBits()); | ||||||
|  |     final long lastSeenOffsetSeconds = n % ChronoUnit.DAYS.getDuration().toSeconds(); | ||||||
|  | 
 | ||||||
|  |     // produce a truncated timestamp which is either today at UTC midnight | ||||||
|  |     // or yesterday at UTC midnight, based on per-user randomized offset used. | ||||||
|     final long todayInMillisWithOffset = Util.todayInMillisGivenOffsetFromNow(clock, Duration.ofSeconds(lastSeenOffsetSeconds).negated()); |     final long todayInMillisWithOffset = Util.todayInMillisGivenOffsetFromNow(clock, Duration.ofSeconds(lastSeenOffsetSeconds).negated()); | ||||||
| 
 | 
 | ||||||
|  |     // only update the device's last seen time when it falls behind the truncated timestamp. | ||||||
|  |     // this ensure a few things: | ||||||
|  |     //   (1) each account will only update last-seen at most once per day | ||||||
|  |     //   (2) these updates will occur throughout the day rather than all occurring at UTC midnight. | ||||||
|     if (device.getLastSeen() < todayInMillisWithOffset) { |     if (device.getLastSeen() < todayInMillisWithOffset) { | ||||||
|       Metrics.summary(DAYS_SINCE_LAST_SEEN_DISTRIBUTION_NAME, IS_PRIMARY_DEVICE_TAG, String.valueOf(device.isMaster())) |       Metrics.summary(DAYS_SINCE_LAST_SEEN_DISTRIBUTION_NAME, IS_PRIMARY_DEVICE_TAG, String.valueOf(device.isMaster())) | ||||||
|           .record(Duration.ofMillis(todayInMillisWithOffset - device.getLastSeen()).toDays()); |           .record(Duration.ofMillis(todayInMillisWithOffset - device.getLastSeen()).toDays()); | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfigurati | ||||||
| import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicTurnConfiguration; | import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicTurnConfiguration; | ||||||
| import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; | import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager; | ||||||
| import org.whispersystems.textsecuregcm.util.Pair; | import org.whispersystems.textsecuregcm.util.Pair; | ||||||
|  | import org.whispersystems.textsecuregcm.util.Util; | ||||||
| import org.whispersystems.textsecuregcm.util.WeightedRandomSelect; | import org.whispersystems.textsecuregcm.util.WeightedRandomSelect; | ||||||
| 
 | 
 | ||||||
| import javax.crypto.Mac; | import javax.crypto.Mac; | ||||||
|  | @ -36,7 +37,7 @@ public class TurnTokenGenerator { | ||||||
|       List<String> urls         = urls(e164); |       List<String> urls         = urls(e164); | ||||||
|       Mac    mac                = Mac.getInstance("HmacSHA1"); |       Mac    mac                = Mac.getInstance("HmacSHA1"); | ||||||
|       long   validUntilSeconds  = (System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1)) / 1000; |       long   validUntilSeconds  = (System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1)) / 1000; | ||||||
|       long   user               = Math.abs(new SecureRandom().nextInt()); |       long   user               = Util.ensureNonNegativeInt(new SecureRandom().nextInt()); | ||||||
|       String userTime           = validUntilSeconds + ":"  + user; |       String userTime           = validUntilSeconds + ":"  + user; | ||||||
| 
 | 
 | ||||||
|       mac.init(new SecretKeySpec(key, "HmacSHA1")); |       mac.init(new SecretKeySpec(key, "HmacSHA1")); | ||||||
|  |  | ||||||
|  | @ -41,6 +41,7 @@ import org.whispersystems.textsecuregcm.entities.UserRemoteConfigList; | ||||||
| import org.whispersystems.textsecuregcm.storage.RemoteConfig; | import org.whispersystems.textsecuregcm.storage.RemoteConfig; | ||||||
| import org.whispersystems.textsecuregcm.storage.RemoteConfigsManager; | import org.whispersystems.textsecuregcm.storage.RemoteConfigsManager; | ||||||
| import org.whispersystems.textsecuregcm.util.Conversions; | import org.whispersystems.textsecuregcm.util.Conversions; | ||||||
|  | import org.whispersystems.textsecuregcm.util.Util; | ||||||
| 
 | 
 | ||||||
| @Path("/v1/config") | @Path("/v1/config") | ||||||
| public class RemoteConfigController { | public class RemoteConfigController { | ||||||
|  | @ -133,7 +134,7 @@ public class RemoteConfigController { | ||||||
|     digest.update(bb.array()); |     digest.update(bb.array()); | ||||||
| 
 | 
 | ||||||
|     byte[] hash   = digest.digest(hashKey); |     byte[] hash   = digest.digest(hashKey); | ||||||
|     int    bucket = (int)(Math.abs(Conversions.byteArrayToLong(hash)) % 100); |     int    bucket = (int)(Util.ensureNonNegativeLong(Conversions.byteArrayToLong(hash)) % 100); | ||||||
| 
 | 
 | ||||||
|     return bucket < configPercentage; |     return bucket < configPercentage; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -191,4 +191,29 @@ public class Util { | ||||||
|   public static Optional<String> findBestLocale(List<LanguageRange> priorityList, Collection<String> supportedLocales) { |   public static Optional<String> findBestLocale(List<LanguageRange> priorityList, Collection<String> supportedLocales) { | ||||||
|     return Optional.ofNullable(Locale.lookupTag(priorityList, supportedLocales)); |     return Optional.ofNullable(Locale.lookupTag(priorityList, supportedLocales)); | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Map ints to non-negative ints. | ||||||
|  |    * <br> | ||||||
|  |    * Unlike Math.abs this method handles Integer.MIN_VALUE correctly. | ||||||
|  |    * | ||||||
|  |    * @param n any int value | ||||||
|  |    * @return an int value guaranteed to be non-negative | ||||||
|  |    */ | ||||||
|  |   public static int ensureNonNegativeInt(int n) { | ||||||
|  |     return n == Integer.MIN_VALUE ? 0 : Math.abs(n); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   /** | ||||||
|  |    * Map longs to non-negative longs. | ||||||
|  |    * <br> | ||||||
|  |    * Unlike Math.abs this method handles Long.MIN_VALUE correctly. | ||||||
|  |    * | ||||||
|  |    * @param n any long value | ||||||
|  |    * @return a long value guaranteed to be non-negative | ||||||
|  |    */ | ||||||
|  |   public static long ensureNonNegativeLong(long n) { | ||||||
|  |     return n == Long.MIN_VALUE ? 0 : Math.abs(n); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 erik-signal
						erik-signal