package peergos.shared.user.fs; 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.*; import peergos.shared.util.*; import java.io.*; import java.util.*; import java.util.concurrent.*; public class FileAccess implements Cborable { protected final SymmetricLink parent2meta; protected final byte[] properties; protected final FileRetriever retriever; protected final SymmetricLocationLink parentLink; public FileAccess(SymmetricLink parent2Meta, byte[] fileProperties, FileRetriever fileRetriever, SymmetricLocationLink parentLink) { this.parent2meta = parent2Meta; this.properties = fileProperties; this.retriever = fileRetriever; this.parentLink = parentLink; } @Override public CborObject toCbor() { return new CborObject.CborList( Arrays.asList( parent2meta.toCbor(), new CborObject.CborByteArray(properties), retriever == null ? new CborObject.CborNull() : retriever.toCbor(), parentLink == null ? new CborObject.CborNull() : parentLink.toCbor() )); } // 0=FILE, 1=DIR public byte getType() { return 0; } public boolean isDirectory() { return this.getType() == 1; } public SymmetricKey getParentKey(SymmetricKey parentKey) { return parentKey; } public SymmetricKey getMetaKey(SymmetricKey parentKey) { return parent2meta.target(parentKey); } public FileProperties getFileProperties(SymmetricKey parentKey) { try { byte[] nonce = Arrays.copyOfRange(properties, 0, TweetNaCl.SECRETBOX_NONCE_BYTES); byte[] cipher = Arrays.copyOfRange(properties, TweetNaCl.SECRETBOX_NONCE_BYTES, this.properties.length); return FileProperties.deserialize(getMetaKey(parentKey).decrypt(cipher, nonce)); } catch (IOException e) { throw new RuntimeException(e); } } public CompletableFuture<RetrievedFilePointer> getParent(SymmetricKey parentKey, NetworkAccess network) { if (this.parentLink == null) return CompletableFuture.completedFuture(null); return network.retrieveAllMetadata(Arrays.asList(parentLink), parentKey).thenApply(res -> { RetrievedFilePointer retrievedFilePointer = res.stream().findAny().get(); return retrievedFilePointer; }); } public FileRetriever retriever() { return retriever; } public CompletableFuture<Boolean> rename(FilePointer writableFilePointer, FileProperties newProps, NetworkAccess network) { if (!writableFilePointer.isWritable()) throw new IllegalStateException("Need a writable pointer!"); SymmetricKey metaKey = this.getMetaKey(writableFilePointer.baseKey); byte[] nonce = metaKey.createNonce(); FileAccess fa = new FileAccess(this.parent2meta, ArrayOps.concat(nonce, metaKey.encrypt(newProps.serialize(), nonce)), this.retriever, this.parentLink); return network.uploadChunk(fa, writableFilePointer.location, writableFilePointer.signer()); } public CompletableFuture<FileAccess> markDirty(FilePointer writableFilePointer, SymmetricKey newParentKey, NetworkAccess network) { // keep the same metakey, just marked as dirty SymmetricKey metaKey = this.getMetaKey(writableFilePointer.baseKey).makeDirty(); SymmetricLink newParentToMeta = SymmetricLink.fromPair(newParentKey, metaKey); SymmetricLocationLink newParentLink = SymmetricLocationLink.create(newParentKey, parentLink.target(writableFilePointer.baseKey), parentLink.targetLocation(writableFilePointer.baseKey)); FileAccess fa = new FileAccess(newParentToMeta, properties, this.retriever, newParentLink); return network.uploadChunk(fa, writableFilePointer.location, writableFilePointer.signer()) .thenApply(x -> fa); } public boolean isDirty(SymmetricKey baseKey) { return getMetaKey(baseKey).isDirty(); } public CompletableFuture<? extends FileAccess> copyTo(SymmetricKey baseKey, SymmetricKey newBaseKey, Location newParentLocation, SymmetricKey parentparentKey, SigningKeyPair entryWriterKey, byte[] newMapKey, NetworkAccess network) { if (!Arrays.equals(baseKey.serialize(), newBaseKey.serialize())) throw new IllegalStateException("FileAccess clone must have same base key as original!"); FileProperties props = getFileProperties(baseKey); FileAccess fa = FileAccess.create(newBaseKey, isDirectory() ? SymmetricKey.random() : getMetaKey(baseKey), props, this.retriever, newParentLocation, parentparentKey); return network.uploadChunk(fa, new Location(newParentLocation.owner, entryWriterKey.publicSigningKey, newMapKey), entryWriterKey) .thenApply(b -> fa); } public static FileAccess fromCbor(CborObject cbor) { if (! (cbor instanceof CborObject.CborList)) throw new IllegalStateException("Incorrect cbor for FileAccess: " + cbor); List<CborObject> value = ((CborObject.CborList) cbor).value; if (value.size() == 2) {// this is dir FileAccess fileBase = fromCbor(value.get(0)); return DirAccess.fromCbor(value.get(1), fileBase); } SymmetricLink parentToMeta = SymmetricLink.fromCbor(value.get(0)); byte[] properties = ((CborObject.CborByteArray)value.get(1)).value; FileRetriever retriever = value.get(2) instanceof CborObject.CborNull ? null : FileRetriever.fromCbor(value.get(2)); SymmetricLocationLink parentLink = value.get(3) instanceof CborObject.CborNull ? null : SymmetricLocationLink.fromCbor(value.get(3)); return new FileAccess(parentToMeta, properties, retriever, parentLink); } public static FileAccess create(SymmetricKey parentKey, SymmetricKey metaKey, FileProperties props, FileRetriever retriever, Location parentLocation, SymmetricKey parentparentKey) { byte[] nonce = metaKey.createNonce(); return new FileAccess(SymmetricLink.fromPair(parentKey, metaKey), ArrayOps.concat(nonce, metaKey.encrypt(props.serialize(), nonce)), retriever, SymmetricLocationLink.create(parentKey, parentparentKey, parentLocation)); } }