Skip to content

Commit df8311e

Browse files
enhancement
Signed-off-by: Alexander-Ger-Reich <[email protected]>
1 parent 766e7b8 commit df8311e

File tree

5 files changed

+133
-2
lines changed

5 files changed

+133
-2
lines changed

library/src/main/java/com/owncloud/android/lib/common/OwnCloudClientManagerFactory.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public class OwnCloudClientManagerFactory {
1515
private static String sUserAgent = "Mozilla/5.0 (Android) Nextcloud-android";
1616
private static String proxyHost = "";
1717
private static int proxyPort = -1;
18+
private static boolean hash_check_enable = false;
1819

1920
public static OwnCloudClientManager getDefaultSingleton() {
2021
if (sDefaultSingleton == null) {
@@ -46,4 +47,12 @@ public static void setProxyPort(int port) {
4647
public static int getProxyPort() {
4748
return proxyPort;
4849
}
50+
51+
public static void setHASHcheck(boolean status) {
52+
hash_check_enable = status;
53+
}
54+
55+
public static boolean getHASHcheck() {
56+
return hash_check_enable;
57+
}
4958
}

library/src/main/java/com/owncloud/android/lib/resources/files/ChunkedFileUploadRemoteOperation.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@
3636

3737
import androidx.annotation.VisibleForTesting;
3838

39+
import java.io.FileInputStream;
40+
import java.security.DigestInputStream;
41+
import java.security.MessageDigest;
42+
import java.nio.ByteBuffer;
43+
import java.math.BigInteger;
3944

4045
public class ChunkedFileUploadRemoteOperation extends UploadFileRemoteOperation {
4146

@@ -216,6 +221,11 @@ protected RemoteOperationResult run(OwnCloudClient client) {
216221
moveMethod = new MoveMethod(originUri, destinationUri, true);
217222
moveMethod.addRequestHeader(OC_X_OC_MTIME_HEADER, String.valueOf(lastModificationTimestamp));
218223

224+
String Hash = FileUtils.getHASHfromFile(this, new File(localPath), "SHA-256");
225+
if(Hash != null){
226+
putMethod.addRequestHeader("X-Content-Hash", Hash);
227+
}
228+
219229
if (creationTimestamp != null && creationTimestamp > 0) {
220230
moveMethod.addRequestHeader(OC_X_OC_CTIME_HEADER, String.valueOf(creationTimestamp));
221231
}
@@ -291,6 +301,24 @@ private RemoteOperationResult uploadChunk(OwnCloudClient client, Chunk chunk) th
291301
putMethod.addRequestHeader(E2E_TOKEN, token);
292302
}
293303

304+
if (OwnCloudClientManagerFactory.getHASHcheck()) {
305+
try (RandomAccessFile hashRaf = new RandomAccessFile(file, "r")) {
306+
MessageDigest md = MessageDigest.getInstance("SHA-256");
307+
308+
FileChannel hashChannel = hashRaf.getChannel();
309+
ByteBuffer buf = ByteBuffer.allocate((int) chunk.getLength());
310+
hashChannel.position(chunk.getStart());
311+
hashChannel.read(buf);
312+
md.update(buf.array());
313+
314+
String chunkHash = String.format("%064x", new BigInteger(1, md.digest()));
315+
316+
putMethod.addRequestHeader("X-Content-Hash", chunkHash);
317+
} catch (Exception e) {
318+
Log_OC.w(TAG, "Could not compute chunk hash");
319+
}
320+
}
321+
294322
status = client.executeMethod(putMethod);
295323

296324
result = new RemoteOperationResult(isSuccess(status), putMethod);

library/src/main/java/com/owncloud/android/lib/resources/files/DownloadFileRemoteOperation.java

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package com.owncloud.android.lib.resources.files;
88

99
import com.owncloud.android.lib.common.OwnCloudClient;
10+
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
1011
import com.owncloud.android.lib.common.network.OnDatatransferProgressListener;
1112
import com.owncloud.android.lib.common.network.WebdavUtils;
1213
import com.owncloud.android.lib.common.operations.OperationCancelledException;
@@ -28,6 +29,15 @@
2829
import java.util.Set;
2930
import java.util.concurrent.atomic.AtomicBoolean;
3031

32+
import java.io.FileInputStream;
33+
import java.security.DigestInputStream;
34+
import java.security.MessageDigest;
35+
import java.math.BigInteger;
36+
import java.util.Locale;
37+
38+
import androidx.annotation.NonNull;
39+
40+
3141
/**
3242
* Remote operation performing the download of a remote file in the ownCloud server.
3343
*
@@ -135,8 +145,54 @@ private int downloadFile(OwnCloudClient client, File targetFile) throws IOExcept
135145
transferEncoding = "chunked".equals(transferEncodingHeader.getValue());
136146
}
137147

138-
if (transferred == totalToTransfer || transferEncoding) {
139-
savedFile = true;
148+
if (transferred == totalToTransfer || transferEncoding) {
149+
if (OwnCloudClientManagerFactory.getHASHcheck()){
150+
Header hashHeader = getMethod.getResponseHeader("X-Content-Hash");
151+
String expectedHash = hashHeader != null ? hashHeader.getValue() : null;
152+
if (expectedHash != null) {
153+
try {
154+
String[] entries = expectedHash.split(",");
155+
for (String entry : entries) {
156+
String[] parts = entry.split(";", 2);
157+
if (parts.length != 2) continue;
158+
String Algorithm = parts[0].trim().toLowerCase(Locale.ROOT);
159+
String hash = parts[1].trim();
160+
161+
String digestAlgorithm = null;
162+
163+
switch (Algorithm) {
164+
case "sha-256":
165+
digestAlgorithm = "SHA-256";
166+
break;
167+
default:
168+
// Skip unknown algorithm
169+
continue;
170+
}
171+
172+
String FileHash = FileUtils.getHASHfromFile(this, targetFile, digestAlgorithm);
173+
174+
if (!hash.equalsIgnoreCase(FileHash)) {
175+
// Hash is incorrect: delete file and abort
176+
Log_OC.w(TAG, "Hash mismatch: expected="+ hash +" actual="+ FileHash);
177+
status = 418;
178+
savedFile = false;
179+
}else{
180+
savedFile = true;
181+
break;
182+
}
183+
}
184+
} catch (Exception e) {
185+
Log_OC.w(TAG, "Could not compute chunk hash");
186+
status = 418;
187+
savedFile = false;
188+
}
189+
}else {
190+
savedFile = true;
191+
}
192+
}else {
193+
savedFile = true;
194+
}
195+
140196
Header modificationTime = getMethod.getResponseHeader("Last-Modified");
141197
if (modificationTime == null) {
142198
modificationTime = getMethod.getResponseHeader("last-modified");

library/src/main/java/com/owncloud/android/lib/resources/files/FileUtils.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,42 @@
77
package com.owncloud.android.lib.resources.files;
88

99
import java.io.File;
10+
import java.io.FileInputStream;
1011
import java.math.BigInteger;
12+
import java.security.DigestInputStream;
1113
import java.security.MessageDigest;
1214
import java.security.NoSuchAlgorithmException;
1315

1416
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
1517

18+
import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
19+
import com.owncloud.android.lib.common.utils.Log_OC;
20+
1621
public class FileUtils {
1722

1823
private static final String TAG = FileUtils.class.getSimpleName();
1924

2025
public static final String PATH_SEPARATOR = "/";
2126

27+
public static String getHASHfromFile(Object thi, File f, String digestAlgorithm) {
28+
if (OwnCloudClientManagerFactory.getHASHcheck()) {
29+
try {
30+
MessageDigest md = MessageDigest.getInstance(digestAlgorithm);
2231

32+
try (FileInputStream fis = new FileInputStream(f);
33+
DigestInputStream dis = new DigestInputStream(fis, md)) {
34+
byte[] buffer = new byte[8192];
35+
while (dis.read(buffer) != -1) {
36+
// digest is updated by reading
37+
}
38+
}
39+
return String.format("%064x", new BigInteger(1, md.digest()));
40+
} catch (Exception e) {
41+
Log_OC.w(thi, "Could not compute chunk hash");
42+
}
43+
}
44+
return null;
45+
}
2346
public static String getParentPath(String remotePath) {
2447
String parentPath = new File(remotePath).getParent();
2548
parentPath = parentPath.endsWith(PATH_SEPARATOR) ? parentPath : parentPath + PATH_SEPARATOR;

library/src/main/java/com/owncloud/android/lib/resources/files/UploadFileRemoteOperation.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@
3232
import java.util.Set;
3333
import java.util.concurrent.atomic.AtomicBoolean;
3434

35+
import java.io.FileInputStream;
36+
import java.security.DigestInputStream;
37+
import java.security.MessageDigest;
38+
import java.security.NoSuchAlgorithmException;
39+
import java.math.BigInteger;
40+
41+
42+
43+
3544
/**
3645
* Remote operation performing the upload of a remote file to the ownCloud server.
3746
*
@@ -211,6 +220,12 @@ protected RemoteOperationResult<String> uploadFile(OwnCloudClient client) throws
211220
}
212221

213222
putMethod.setRequestEntity(entity);
223+
224+
String Hash = FileUtils.getHASHfromFile(this, f, "SHA-256");
225+
if(Hash != null){
226+
putMethod.addRequestHeader("X-Content-Hash", Hash);
227+
}
228+
214229
status = client.executeMethod(putMethod);
215230

216231
result = new RemoteOperationResult<>(isSuccess(status), putMethod);

0 commit comments

Comments
 (0)