package peergos.shared.corenode;
import peergos.shared.*;
import peergos.shared.cbor.*;
import peergos.shared.crypto.asymmetric.*;
import peergos.shared.crypto.random.*;
import peergos.shared.user.*;
import peergos.shared.user.fs.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
public class TofuCoreNode implements CoreNode {
public static final String KEY_STORE_NAME = ".keystore";
private final CoreNode source;
private final TofuKeyStore tofu;
private UserContext context;
public TofuCoreNode(CoreNode source, TofuKeyStore tofu) {
this.source = source;
this.tofu = tofu;
}
public void setContext(UserContext context) {
this.context = context;
}
private static String getStorePath(String username) {
return "/" + username + "/" + KEY_STORE_NAME;
}
public static CompletableFuture<TofuKeyStore> load(String username, TrieNode root, NetworkAccess network, SafeRandom random) {
if (username == null)
return CompletableFuture.completedFuture(new TofuKeyStore());
return root.getByPath(getStorePath(username), network).thenCompose(fileOpt -> {
if (! fileOpt.isPresent())
return CompletableFuture.completedFuture(new TofuKeyStore());
return fileOpt.get().getInputStream(network, random, x -> {}).thenCompose(reader -> {
byte[] storeData = new byte[(int) fileOpt.get().getSize()];
return reader.readIntoArray(storeData, 0, storeData.length)
.thenApply(x -> TofuKeyStore.fromCbor(CborObject.fromByteArray(storeData)));
});
});
}
private CompletableFuture<Boolean> commit() {
return context.getUserRoot()
.thenCompose(home -> {
byte[] data = tofu.serialize();
AsyncReader.ArrayBacked dataReader = new AsyncReader.ArrayBacked(data);
return home.uploadFile(KEY_STORE_NAME, dataReader, true, (long) data.length,
context.network, context.crypto.random, x-> {}, context.fragmenter());
});
}
public CompletableFuture<Boolean> updateUser(String username) {
return source.getChain(username).thenCompose(chain -> {
tofu.updateChain(username, chain);
return commit();
});
}
@Override
public CompletableFuture<Optional<PublicSigningKey>> getPublicKey(String username) {
Optional<PublicSigningKey> local = tofu.getPublicKey(username);
if (local.isPresent())
return CompletableFuture.completedFuture(local);
return source.getChain(username)
.thenCompose(chain -> {
tofu.updateChain(username, chain);
return commit().thenApply(x -> tofu.getPublicKey(username));
});
}
@Override
public CompletableFuture<String> getUsername(PublicSigningKey key) {
Optional<String> local = tofu.getUsername(key);
if (local.isPresent())
return CompletableFuture.completedFuture(local.get());
return source.getUsername(key)
.thenCompose(username -> {
return source.getChain(username).thenCompose(chain -> {
tofu.updateChain(username, chain);
return commit().thenApply(x -> username);
});
});
}
@Override
public CompletableFuture<List<UserPublicKeyLink>> getChain(String username) {
List<UserPublicKeyLink> localChain = tofu.getChain(username);
if (! localChain.isEmpty())
return CompletableFuture.completedFuture(localChain);
return source.getChain(username)
.thenCompose(chain -> {
tofu.updateChain(username, chain);
return commit().thenApply(x -> tofu.getChain(username));
});
}
@Override
public CompletableFuture<Boolean> updateChain(String username, List<UserPublicKeyLink> chain) {
tofu.updateChain(username, chain);
return commit().thenCompose(x -> source.updateChain(username, chain));
}
@Override
public CompletableFuture<List<String>> getUsernames(String prefix) {
return source.getUsernames(prefix);
}
@Override
public CompletableFuture<Boolean> followRequest(PublicSigningKey target, byte[] encryptedPermission) {
return source.followRequest(target, encryptedPermission);
}
@Override
public CompletableFuture<byte[]> getFollowRequests(PublicSigningKey owner) {
return source.getFollowRequests(owner);
}
@Override
public CompletableFuture<Boolean> removeFollowRequest(PublicSigningKey owner, byte[] data) {
return source.removeFollowRequest(owner, data);
}
@Override
public void close() throws IOException {}
}