package peergos.shared.user; import jsinterop.annotations.*; import peergos.shared.*; import peergos.shared.cbor.*; import peergos.shared.crypto.*; import peergos.shared.crypto.asymmetric.*; import peergos.shared.crypto.symmetric.*; import peergos.shared.user.fs.*; import peergos.shared.util.*; import java.io.*; import java.util.*; import java.util.concurrent.*; import java.util.stream.*; @JsType public class EntryPoint implements Cborable{ public final FilePointer pointer; public final String owner; public final Set<String> readers, writers; public EntryPoint(FilePointer pointer, String owner, Set<String> readers, Set<String> writers) { this.pointer = pointer; this.owner = owner; this.readers = readers; this.writers = writers; } public byte[] serializeAndEncrypt(BoxingKeyPair user, PublicBoxingKey target) throws IOException { return target.encryptMessageFor(this.serialize(), user.secretBoxingKey); } public byte[] serializeAndSymmetricallyEncrypt(SymmetricKey key) { byte[] nonce = key.createNonce(); return ArrayOps.concat(nonce, key.encrypt(serialize(), nonce)); } /** * * @param path The path of the file this entry point corresponds to * @param network * @return */ public CompletableFuture<Boolean> isValid(String path, NetworkAccess network) { String[] parts = path.split("/"); String claimedOwner = parts[1]; // check claimed owner actually owns the signing key PublicSigningKey entryWriter = pointer.getLocation().writer; return network.coreNode.getPublicKey(claimedOwner).thenCompose(ownerKey -> { if (! ownerKey.isPresent()) throw new IllegalStateException("No owner key present for user " + claimedOwner); return UserContext.getWriterData(network, ownerKey.get()).thenApply(wd -> { // TODO do this recursively to handle arbitrary trees of key ownership return wd.props.ownedKeys.contains(entryWriter); }); }); } @Override public CborObject toCbor() { return new CborObject.CborList(Arrays.asList( pointer.toCbor(), new CborObject.CborString(owner), new CborObject.CborList(readers.stream().sorted().map(CborObject.CborString::new).collect(Collectors.toList())), new CborObject.CborList(writers.stream().sorted().map(CborObject.CborString::new).collect(Collectors.toList())) )); } static EntryPoint fromCbor(CborObject cbor) { if (! (cbor instanceof CborObject.CborList)) throw new IllegalStateException("Incorrect cbor type for EntryPoint: " + cbor); List<CborObject> value = ((CborObject.CborList) cbor).value; FilePointer pointer = FilePointer.fromCbor(value.get(0)); String owner = ((CborObject.CborString) value.get(1)).value; Set<String> readers = ((CborObject.CborList) value.get(2)).value .stream() .map(c -> ((CborObject.CborString) c).value) .collect(Collectors.toSet()); Set<String> writers = ((CborObject.CborList) value.get(3)).value .stream() .map(c -> ((CborObject.CborString) c).value) .collect(Collectors.toSet()); return new EntryPoint(pointer, owner, readers, writers); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; EntryPoint that = (EntryPoint) o; if (pointer != null ? !pointer.equals(that.pointer) : that.pointer != null) return false; if (owner != null ? !owner.equals(that.owner) : that.owner != null) return false; if (readers != null ? !readers.equals(that.readers) : that.readers != null) return false; return writers != null ? writers.equals(that.writers) : that.writers == null; } @Override public int hashCode() { int result = pointer != null ? pointer.hashCode() : 0; result = 31 * result + (owner != null ? owner.hashCode() : 0); result = 31 * result + (readers != null ? readers.hashCode() : 0); result = 31 * result + (writers != null ? writers.hashCode() : 0); return result; } static EntryPoint symmetricallyDecryptAndDeserialize(byte[] input, SymmetricKey key) throws IOException { byte[] nonce = Arrays.copyOfRange(input, 0, 24); byte[] raw = key.decrypt(Arrays.copyOfRange(input, 24, input.length), nonce); return fromCbor(CborObject.fromByteArray(raw)); } }