]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/LegacySenderKeyRecordStore.java
64928624ef969552e1f02c70eb990614e932b71d
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / storage / senderKeys / LegacySenderKeyRecordStore.java
1 package org.asamk.signal.manager.storage.senderKeys;
2
3 import org.asamk.signal.manager.api.Pair;
4 import org.asamk.signal.manager.helper.RecipientAddressResolver;
5 import org.asamk.signal.manager.storage.recipients.RecipientId;
6 import org.asamk.signal.manager.storage.recipients.RecipientResolver;
7 import org.signal.libsignal.protocol.InvalidMessageException;
8 import org.signal.libsignal.protocol.groups.state.SenderKeyRecord;
9 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory;
11
12 import java.io.File;
13 import java.io.FileInputStream;
14 import java.io.IOException;
15 import java.nio.file.Files;
16 import java.util.Arrays;
17 import java.util.List;
18 import java.util.Objects;
19 import java.util.UUID;
20 import java.util.regex.Matcher;
21 import java.util.regex.Pattern;
22
23 public class LegacySenderKeyRecordStore {
24
25 private final static Logger logger = LoggerFactory.getLogger(LegacySenderKeyRecordStore.class);
26
27 public static void migrate(
28 final File senderKeysPath,
29 final RecipientResolver resolver,
30 final RecipientAddressResolver addressResolver,
31 final SenderKeyStore senderKeyStore
32 ) {
33 final var files = senderKeysPath.listFiles();
34 if (files == null) {
35 return;
36 }
37
38 final var senderKeys = parseFileNames(files, resolver).stream().map(key -> {
39 final var record = loadSenderKeyLocked(key, senderKeysPath);
40 final var serviceId = addressResolver.resolveRecipientAddress(key.recipientId).serviceId();
41 if (record == null || serviceId.isEmpty()) {
42 return null;
43 }
44 return new Pair<>(new SenderKeyRecordStore.Key(serviceId.get().toString(),
45 key.deviceId,
46 key.distributionId), record);
47 }).filter(Objects::nonNull).toList();
48
49 senderKeyStore.addLegacySenderKeys(senderKeys);
50 deleteAllSenderKeys(senderKeysPath);
51 }
52
53 private static void deleteAllSenderKeys(File senderKeysPath) {
54 final var files = senderKeysPath.listFiles();
55 if (files == null) {
56 return;
57 }
58
59 for (var file : files) {
60 try {
61 Files.delete(file.toPath());
62 } catch (IOException e) {
63 logger.error("Failed to delete sender key file {}: {}", file, e.getMessage());
64 }
65 }
66 try {
67 Files.delete(senderKeysPath.toPath());
68 } catch (IOException e) {
69 logger.error("Failed to delete sender keys directory {}: {}", senderKeysPath, e.getMessage());
70 }
71 }
72
73 final static Pattern senderKeyFileNamePattern = Pattern.compile("(\\d+)_(\\d+)_([\\da-z\\-]+)");
74
75 private static List<Key> parseFileNames(final File[] files, final RecipientResolver resolver) {
76 return Arrays.stream(files)
77 .map(f -> senderKeyFileNamePattern.matcher(f.getName()))
78 .filter(Matcher::matches)
79 .map(matcher -> {
80 final var recipientId = resolver.resolveRecipient(Long.parseLong(matcher.group(1)));
81 if (recipientId == null) {
82 return null;
83 }
84 return new Key(recipientId, Integer.parseInt(matcher.group(2)), UUID.fromString(matcher.group(3)));
85 })
86 .filter(Objects::nonNull)
87 .toList();
88 }
89
90 private static File getSenderKeyFile(Key key, final File senderKeysPath) {
91 return new File(senderKeysPath,
92 key.recipientId().id() + "_" + key.deviceId() + "_" + key.distributionId().toString());
93 }
94
95 private static SenderKeyRecord loadSenderKeyLocked(final Key key, final File senderKeysPath) {
96 final var file = getSenderKeyFile(key, senderKeysPath);
97 if (!file.exists()) {
98 return null;
99 }
100 try (var inputStream = new FileInputStream(file)) {
101 return new SenderKeyRecord(inputStream.readAllBytes());
102 } catch (IOException | InvalidMessageException e) {
103 logger.warn("Failed to load sender key, resetting sender key: {}", e.getMessage());
104 return null;
105 }
106 }
107
108 record Key(RecipientId recipientId, int deviceId, UUID distributionId) {}
109 }