Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions hub/src/main/java/cloud/katta/crypto/uvf/UvfJWKCallback.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright (c) 2025 shift7 GmbH. All rights reserved.
*/

package cloud.katta.crypto.uvf;

import ch.cyberduck.core.Host;
import ch.cyberduck.core.LoginOptions;
import ch.cyberduck.core.exception.LoginCanceledException;
import ch.cyberduck.core.vault.JWKCallback;
import ch.cyberduck.core.vault.JWKCredentials;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.nimbusds.jose.jwk.JWK;

public class UvfJWKCallback implements JWKCallback {

private final JWK key;

public UvfJWKCallback(final JWK key) throws JsonProcessingException {
this.key = key;
}

@Override
public void close(final String input) {
//
}

@Override
public JWKCredentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) throws LoginCanceledException {
return new JWKCredentials(key);
}
}
12 changes: 8 additions & 4 deletions hub/src/main/java/cloud/katta/crypto/uvf/UvfMetadataPayload.java
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ public byte[] computeRootDirUvf() throws JsonProcessingException {
}

public static final class UniversalVaultFormatJWKS {
private final OctetSequenceKey memberKey;
private final ECKey recoveryKeyJWK;
private final P384KeyPair recoveryKey;

Expand All @@ -147,8 +148,6 @@ public OctetSequenceKey memberKey() {
return memberKey;
}

private final OctetSequenceKey memberKey;

private UniversalVaultFormatJWKS() throws JOSEException {
memberKey = new OctetSequenceKeyGenerator(256)
.keyID("org.cryptomator.hub.memberkey")
Expand All @@ -173,8 +172,13 @@ public JWKSet toJWKSet() {
return new JWKSet(Arrays.asList(memberKey, recoveryKeyJWK));
}

public String serializePublicRecoverykey() {
return new JWKSet(recoveryKeyJWK).toPublicJWKSet().toString();
/**
* Retrieve Recovery Key as JSON Web Key
*
* @return The JSON object string representation. Only public keys will be included
*/
public String serializePublicRecoveryKey() {
return new JWKSet(recoveryKeyJWK).toString();
}

public UvfAccessTokenPayload toAccessToken() {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright (c) 2025 shift7 GmbH. All rights reserved.
*/

package cloud.katta.crypto.uvf;

import ch.cyberduck.core.Host;
import ch.cyberduck.core.HostUrlProvider;
import ch.cyberduck.core.cryptomator.impl.uvf.VaultMetadataUVFProvider;
import ch.cyberduck.core.vault.VaultMetadataProvider;

import java.nio.charset.StandardCharsets;
import java.util.UUID;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.nimbusds.jose.JOSEException;

public class VaultIdMetadataUVFProvider implements VaultMetadataUVFProvider {

private final UUID vaultId;
private final UvfMetadataPayload.UniversalVaultFormatJWKS jwks;
private final byte[] vaultMetadata;
private final byte[] rootDirectoryMetadata;
private final String hashedRootDirId;

public VaultIdMetadataUVFProvider(final Host storage, final UUID vaultId,
final UvfMetadataPayload.UniversalVaultFormatJWKS jwks, final UvfMetadataPayload vaultMetadata) throws JOSEException, JsonProcessingException {
this(vaultId, jwks, vaultMetadata.encrypt(
String.format("%s/api", new HostUrlProvider(false, true).get(storage)),
vaultId,
jwks.toJWKSet()
).getBytes(StandardCharsets.US_ASCII),
vaultMetadata.computeRootDirUvf(),
vaultMetadata.computeRootDirIdHash()
);
}

public VaultIdMetadataUVFProvider(final UUID vaultId, final UvfMetadataPayload.UniversalVaultFormatJWKS jwks, final byte[] vaultMetadata, final byte[] rootDirectoryMetadata, final String hashedRootDirId) {
this.vaultId = vaultId;
this.jwks = jwks;
this.vaultMetadata = vaultMetadata;
this.rootDirectoryMetadata = rootDirectoryMetadata;
this.hashedRootDirId = hashedRootDirId;
}

public static VaultIdMetadataUVFProvider cast(VaultMetadataProvider provider) {
if(provider instanceof VaultIdMetadataUVFProvider) {
return (VaultIdMetadataUVFProvider) provider;
}
else {
throw new IllegalArgumentException("Unsupported metadata type " + provider.getClass());
}
}

public UUID getVaultId() {
return vaultId;
}

public UvfMetadataPayload.UniversalVaultFormatJWKS getJwks() {
return jwks;
}

@Override
public byte[] getMetadata() {
return vaultMetadata;
}

@Override
public byte[] getRootDirectoryMetadata() {
return rootDirectoryMetadata;
}

@Override
public String getDirPath() {
return hashedRootDirId;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public UvfMetadataPayload toUvfMetadataPayload(final Path bucket) {
.provider(this.getProfile())
.defaultPath(bucket.getAbsolute())
.region(this.getRegion())
.nickname(null != bucket.attributes().getDisplayname() ? bucket.attributes().getDisplayname() : "Vault"))
.nickname(bucket.attributes().getDisplayname()))
.withAutomaticAccessGrant(new VaultMetadataJWEAutomaticAccessGrantDto()
.enabled(true)
.maxWotDepth(null));
Expand Down
Loading