]> nmode's Git Repositories - signal-cli/blobdiff - src/main/java/org/asamk/signal/manager/storage/SignalAccount.java
Store storage key from keys sync message
[signal-cli] / src / main / java / org / asamk / signal / manager / storage / SignalAccount.java
index 6d5925739c41aabf2f13e283380ec1af08f45af6..c1aaa788a2f7466ebbdce001e9266fb8eb0be83d 100644 (file)
@@ -41,7 +41,7 @@ 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;
+import org.whispersystems.signalservice.api.storage.StorageKey;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -53,13 +53,14 @@ import java.nio.channels.Channels;
 import java.nio.channels.ClosedChannelException;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileLock;
+import java.util.Base64;
 import java.util.Collection;
 import java.util.UUID;
 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;
@@ -71,6 +72,7 @@ public class SignalAccount implements Closeable {
     private String password;
     private String registrationLockPin;
     private MasterKey pinMasterKey;
+    private StorageKey storageKey;
     private String signalingKey;
     private ProfileKey profileKey;
     private int preKeyIdOffset;
@@ -91,7 +93,7 @@ public class SignalAccount implements Closeable {
         this.fileChannel = fileChannel;
         this.lock = lock;
         jsonProcessor.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE); // disable autodetect
-        jsonProcessor.enable(SerializationFeature.INDENT_OUTPUT); // for pretty print, you can disable it.
+        jsonProcessor.enable(SerializationFeature.INDENT_OUTPUT); // for pretty print
         jsonProcessor.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
         jsonProcessor.disable(JsonParser.Feature.AUTO_CLOSE_SOURCE);
         jsonProcessor.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET);
@@ -138,6 +140,8 @@ public class SignalAccount implements Closeable {
 
         account.registered = false;
 
+        account.migrateLegacyConfigs();
+
         return account;
     }
 
@@ -179,6 +183,8 @@ public class SignalAccount implements Closeable {
         account.registered = true;
         account.isMultiDevice = true;
 
+        account.migrateLegacyConfigs();
+
         return account;
     }
 
@@ -196,8 +202,8 @@ public class SignalAccount implements Closeable {
             }
             final ProfileKey profileKey;
             try {
-                profileKey = new ProfileKey(Base64.decode(profileKeyString));
-            } catch (InvalidInputException | IOException e) {
+                profileKey = new ProfileKey(Base64.getDecoder().decode(profileKeyString));
+            } catch (InvalidInputException ignored) {
                 continue;
             }
             contact.profileKey = null;
@@ -260,7 +266,11 @@ public class SignalAccount implements Closeable {
         JsonNode pinMasterKeyNode = rootNode.get("pinMasterKey");
         pinMasterKey = pinMasterKeyNode == null || pinMasterKeyNode.isNull()
                 ? null
-                : new MasterKey(Base64.decode(pinMasterKeyNode.asText()));
+                : new MasterKey(Base64.getDecoder().decode(pinMasterKeyNode.asText()));
+        JsonNode storageKeyNode = rootNode.get("storageKey");
+        storageKey = storageKeyNode == null || storageKeyNode.isNull()
+                ? null
+                : new StorageKey(Base64.getDecoder().decode(storageKeyNode.asText()));
         if (rootNode.has("signalingKey")) {
             signalingKey = Utils.getNotNullNode(rootNode, "signalingKey").asText();
         }
@@ -276,7 +286,8 @@ public class SignalAccount implements Closeable {
         }
         if (rootNode.has("profileKey")) {
             try {
-                profileKey = new ProfileKey(Base64.decode(Utils.getNotNullNode(rootNode, "profileKey").asText()));
+                profileKey = new ProfileKey(Base64.getDecoder()
+                        .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",
@@ -354,7 +365,7 @@ public class SignalAccount implements Closeable {
         messageCache = new MessageCache(getMessageCachePath(dataPath, username));
 
         JsonNode threadStoreNode = rootNode.get("threadStore");
-        if (threadStoreNode != null) {
+        if (threadStoreNode != null && !threadStoreNode.isNull()) {
             LegacyJsonThreadStore threadStore = jsonProcessor.convertValue(threadStoreNode,
                     LegacyJsonThreadStore.class);
             // Migrate thread info to group and contact store
@@ -391,11 +402,14 @@ public class SignalAccount implements Closeable {
                 .put("isMultiDevice", isMultiDevice)
                 .put("password", password)
                 .put("registrationLockPin", registrationLockPin)
-                .put("pinMasterKey", pinMasterKey == null ? null : Base64.encodeBytes(pinMasterKey.serialize()))
+                .put("pinMasterKey",
+                        pinMasterKey == null ? null : Base64.getEncoder().encodeToString(pinMasterKey.serialize()))
+                .put("storageKey",
+                        storageKey == null ? null : Base64.getEncoder().encodeToString(storageKey.serialize()))
                 .put("signalingKey", signalingKey)
                 .put("preKeyIdOffset", preKeyIdOffset)
                 .put("nextSignedPreKeyId", nextSignedPreKeyId)
-                .put("profileKey", Base64.encodeBytes(profileKey.serialize()))
+                .put("profileKey", Base64.getEncoder().encodeToString(profileKey.serialize()))
                 .put("registered", registered)
                 .putPOJO("axolotlStore", signalProtocolStore)
                 .putPOJO("groupStore", groupStore)
@@ -499,6 +513,10 @@ public class SignalAccount implements Closeable {
         this.deviceId = deviceId;
     }
 
+    public boolean isMasterDevice() {
+        return deviceId == SignalServiceAddress.DEFAULT_DEVICE_ID;
+    }
+
     public String getPassword() {
         return password;
     }
@@ -523,6 +541,17 @@ public class SignalAccount implements Closeable {
         this.pinMasterKey = pinMasterKey;
     }
 
+    public StorageKey getStorageKey() {
+        if (pinMasterKey != null) {
+            return pinMasterKey.deriveStorageServiceKey();
+        }
+        return storageKey;
+    }
+
+    public void setStorageKey(final StorageKey storageKey) {
+        this.storageKey = storageKey;
+    }
+
     public String getSignalingKey() {
         return signalingKey;
     }
@@ -579,7 +608,9 @@ public class SignalAccount implements Closeable {
 
     @Override
     public void close() throws IOException {
-        save();
+        if (fileChannel.isOpen()) {
+            save();
+        }
         synchronized (fileChannel) {
             try {
                 lock.close();