]> nmode's Git Repositories - signal-cli/blobdiff - src/main/java/org/asamk/signal/manager/storage/SignalAccount.java
Make loggers private
[signal-cli] / src / main / java / org / asamk / signal / manager / storage / SignalAccount.java
index c3573209b2523f472bcb6777891273f53145c1b2..87b1efd4db5c954d6fbb6ad8d63aee72c3ac234e 100644 (file)
@@ -16,6 +16,7 @@ import org.asamk.signal.manager.storage.contacts.JsonContactsStore;
 import org.asamk.signal.manager.storage.groups.GroupInfo;
 import org.asamk.signal.manager.storage.groups.GroupInfoV1;
 import org.asamk.signal.manager.storage.groups.JsonGroupStore;
+import org.asamk.signal.manager.storage.messageCache.MessageCache;
 import org.asamk.signal.manager.storage.profiles.ProfileStore;
 import org.asamk.signal.manager.storage.protocol.IdentityInfo;
 import org.asamk.signal.manager.storage.protocol.JsonSignalProtocolStore;
@@ -25,8 +26,9 @@ import org.asamk.signal.manager.storage.protocol.SignalServiceAddressResolver;
 import org.asamk.signal.manager.storage.stickers.StickerStore;
 import org.asamk.signal.manager.storage.threads.LegacyJsonThreadStore;
 import org.asamk.signal.manager.storage.threads.ThreadInfo;
-import org.asamk.signal.util.IOUtils;
-import org.asamk.signal.util.Util;
+import org.asamk.signal.manager.util.IOUtils;
+import org.asamk.signal.manager.util.KeyUtils;
+import org.asamk.signal.manager.util.Utils;
 import org.signal.zkgroup.InvalidInputException;
 import org.signal.zkgroup.profiles.ProfileKey;
 import org.slf4j.Logger;
@@ -36,6 +38,8 @@ import org.whispersystems.libsignal.state.PreKeyRecord;
 import org.whispersystems.libsignal.state.SignedPreKeyRecord;
 import org.whispersystems.libsignal.util.Medium;
 import org.whispersystems.libsignal.util.Pair;
+import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
+import org.whispersystems.signalservice.api.kbs.MasterKey;
 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
 import org.whispersystems.util.Base64;
 
@@ -55,7 +59,7 @@ import java.util.stream.Collectors;
 
 public class SignalAccount implements Closeable {
 
-    final static Logger logger = LoggerFactory.getLogger(SignalAccount.class);
+    private final static Logger logger = LoggerFactory.getLogger(SignalAccount.class);
 
     private final ObjectMapper jsonProcessor = new ObjectMapper();
     private final FileChannel fileChannel;
@@ -66,6 +70,7 @@ public class SignalAccount implements Closeable {
     private boolean isMultiDevice = false;
     private String password;
     private String registrationLockPin;
+    private MasterKey pinMasterKey;
     private String signalingKey;
     private ProfileKey profileKey;
     private int preKeyIdOffset;
@@ -80,6 +85,8 @@ public class SignalAccount implements Closeable {
     private ProfileStore profileStore;
     private StickerStore stickerStore;
 
+    private MessageCache messageCache;
+
     private SignalAccount(final FileChannel fileChannel, final FileLock lock) {
         this.fileChannel = fileChannel;
         this.lock = lock;
@@ -96,6 +103,8 @@ public class SignalAccount implements Closeable {
         try {
             SignalAccount account = new SignalAccount(pair.first(), pair.second());
             account.load(dataPath);
+            account.migrateLegacyConfigs();
+
             return account;
         } catch (Throwable e) {
             pair.second().close();
@@ -124,6 +133,9 @@ public class SignalAccount implements Closeable {
         account.recipientStore = new RecipientStore();
         account.profileStore = new ProfileStore();
         account.stickerStore = new StickerStore();
+
+        account.messageCache = new MessageCache(getMessageCachePath(dataPath, username));
+
         account.registered = false;
 
         return account;
@@ -161,12 +173,40 @@ public class SignalAccount implements Closeable {
         account.recipientStore = new RecipientStore();
         account.profileStore = new ProfileStore();
         account.stickerStore = new StickerStore();
+
+        account.messageCache = new MessageCache(getMessageCachePath(dataPath, username));
+
         account.registered = true;
         account.isMultiDevice = true;
 
         return account;
     }
 
+    public void migrateLegacyConfigs() {
+        if (getProfileKey() == null && isRegistered()) {
+            // Old config file, creating new profile key
+            setProfileKey(KeyUtils.createProfileKey());
+            save();
+        }
+        // Store profile keys only in profile store
+        for (ContactInfo contact : getContactStore().getContacts()) {
+            String profileKeyString = contact.profileKey;
+            if (profileKeyString == null) {
+                continue;
+            }
+            final ProfileKey profileKey;
+            try {
+                profileKey = new ProfileKey(Base64.decode(profileKeyString));
+            } catch (InvalidInputException | IOException e) {
+                continue;
+            }
+            contact.profileKey = null;
+            getProfileStore().storeProfileKey(contact.getAddress(), profileKey);
+        }
+        // Ensure our profile key is stored in profile store
+        getProfileStore().storeProfileKey(getSelfAddress(), getProfileKey());
+    }
+
     public static File getFileName(File dataPath, String username) {
         return new File(dataPath, username);
     }
@@ -211,28 +251,32 @@ public class SignalAccount implements Closeable {
             deviceId = node.asInt();
         }
         if (rootNode.has("isMultiDevice")) {
-            isMultiDevice = Util.getNotNullNode(rootNode, "isMultiDevice").asBoolean();
+            isMultiDevice = Utils.getNotNullNode(rootNode, "isMultiDevice").asBoolean();
         }
-        username = Util.getNotNullNode(rootNode, "username").asText();
-        password = Util.getNotNullNode(rootNode, "password").asText();
+        username = Utils.getNotNullNode(rootNode, "username").asText();
+        password = Utils.getNotNullNode(rootNode, "password").asText();
         JsonNode pinNode = rootNode.get("registrationLockPin");
         registrationLockPin = pinNode == null || pinNode.isNull() ? null : pinNode.asText();
+        JsonNode pinMasterKeyNode = rootNode.get("pinMasterKey");
+        pinMasterKey = pinMasterKeyNode == null || pinMasterKeyNode.isNull()
+                ? null
+                : new MasterKey(Base64.decode(pinMasterKeyNode.asText()));
         if (rootNode.has("signalingKey")) {
-            signalingKey = Util.getNotNullNode(rootNode, "signalingKey").asText();
+            signalingKey = Utils.getNotNullNode(rootNode, "signalingKey").asText();
         }
         if (rootNode.has("preKeyIdOffset")) {
-            preKeyIdOffset = Util.getNotNullNode(rootNode, "preKeyIdOffset").asInt(0);
+            preKeyIdOffset = Utils.getNotNullNode(rootNode, "preKeyIdOffset").asInt(0);
         } else {
             preKeyIdOffset = 0;
         }
         if (rootNode.has("nextSignedPreKeyId")) {
-            nextSignedPreKeyId = Util.getNotNullNode(rootNode, "nextSignedPreKeyId").asInt();
+            nextSignedPreKeyId = Utils.getNotNullNode(rootNode, "nextSignedPreKeyId").asInt();
         } else {
             nextSignedPreKeyId = 0;
         }
         if (rootNode.has("profileKey")) {
             try {
-                profileKey = new ProfileKey(Base64.decode(Util.getNotNullNode(rootNode, "profileKey").asText()));
+                profileKey = new ProfileKey(Base64.decode(Utils.getNotNullNode(rootNode, "profileKey").asText()));
             } catch (InvalidInputException e) {
                 throw new IOException(
                         "Config file contains an invalid profileKey, needs to be base64 encoded array of 32 bytes",
@@ -240,9 +284,9 @@ public class SignalAccount implements Closeable {
             }
         }
 
-        signalProtocolStore = jsonProcessor.convertValue(Util.getNotNullNode(rootNode, "axolotlStore"),
+        signalProtocolStore = jsonProcessor.convertValue(Utils.getNotNullNode(rootNode, "axolotlStore"),
                 JsonSignalProtocolStore.class);
-        registered = Util.getNotNullNode(rootNode, "registered").asBoolean();
+        registered = Utils.getNotNullNode(rootNode, "registered").asBoolean();
         JsonNode groupStoreNode = rootNode.get("groupStore");
         if (groupStoreNode != null) {
             groupStore = jsonProcessor.convertValue(groupStoreNode, JsonGroupStore.class);
@@ -307,6 +351,8 @@ public class SignalAccount implements Closeable {
             stickerStore = new StickerStore();
         }
 
+        messageCache = new MessageCache(getMessageCachePath(dataPath, username));
+
         JsonNode threadStoreNode = rootNode.get("threadStore");
         if (threadStoreNode != null) {
             LegacyJsonThreadStore threadStore = jsonProcessor.convertValue(threadStoreNode,
@@ -345,6 +391,7 @@ public class SignalAccount implements Closeable {
                 .put("isMultiDevice", isMultiDevice)
                 .put("password", password)
                 .put("registrationLockPin", registrationLockPin)
+                .put("pinMasterKey", pinMasterKey == null ? null : Base64.encodeBytes(pinMasterKey.serialize()))
                 .put("signalingKey", signalingKey)
                 .put("preKeyIdOffset", preKeyIdOffset)
                 .put("nextSignedPreKeyId", nextSignedPreKeyId)
@@ -424,6 +471,10 @@ public class SignalAccount implements Closeable {
         return stickerStore;
     }
 
+    public MessageCache getMessageCache() {
+        return messageCache;
+    }
+
     public String getUsername() {
         return username;
     }
@@ -444,6 +495,10 @@ public class SignalAccount implements Closeable {
         return deviceId;
     }
 
+    public void setDeviceId(final int deviceId) {
+        this.deviceId = deviceId;
+    }
+
     public String getPassword() {
         return password;
     }
@@ -456,14 +511,18 @@ public class SignalAccount implements Closeable {
         return registrationLockPin;
     }
 
-    public String getRegistrationLock() {
-        return null; // TODO implement KBS
-    }
-
     public void setRegistrationLockPin(final String registrationLockPin) {
         this.registrationLockPin = registrationLockPin;
     }
 
+    public MasterKey getPinMasterKey() {
+        return pinMasterKey;
+    }
+
+    public void setPinMasterKey(final MasterKey pinMasterKey) {
+        this.pinMasterKey = pinMasterKey;
+    }
+
     public String getSignalingKey() {
         return signalingKey;
     }
@@ -480,6 +539,10 @@ public class SignalAccount implements Closeable {
         this.profileKey = profileKey;
     }
 
+    public byte[] getSelfUnidentifiedAccessKey() {
+        return UnidentifiedAccess.deriveAccessKeyFrom(getProfileKey());
+    }
+
     public int getPreKeyIdOffset() {
         return preKeyIdOffset;
     }
@@ -504,8 +567,19 @@ public class SignalAccount implements Closeable {
         isMultiDevice = multiDevice;
     }
 
+    public boolean isUnrestrictedUnidentifiedAccess() {
+        // TODO make configurable
+        return false;
+    }
+
+    public boolean isDiscoverableByPhoneNumber() {
+        // TODO make configurable
+        return true;
+    }
+
     @Override
     public void close() throws IOException {
+        save();
         synchronized (fileChannel) {
             try {
                 lock.close();