From f9631e1a4a7d0e9480484a32a420b93196ffceb9 Mon Sep 17 00:00:00 2001 From: Zlatin Balevsky Date: Wed, 18 Jul 2018 19:18:22 +0100 Subject: [PATCH] hashing of files --- .../groovy/com/muwire/core/FileHasher.groovy | 45 +++++++++++++ .../com/muwire/core/FileHasherTest.groovy | 66 ++++++++++++++++++- 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/core/src/main/groovy/com/muwire/core/FileHasher.groovy b/core/src/main/groovy/com/muwire/core/FileHasher.groovy index cd952cd3..dd4b07f3 100644 --- a/core/src/main/groovy/com/muwire/core/FileHasher.groovy +++ b/core/src/main/groovy/com/muwire/core/FileHasher.groovy @@ -1,5 +1,11 @@ package com.muwire.core +import java.nio.MappedByteBuffer +import java.nio.channels.FileChannel +import java.nio.channels.FileChannel.MapMode +import java.security.MessageDigest +import java.security.NoSuchAlgorithmException + class FileHasher { /** max size of shared file is 128 GB */ @@ -21,4 +27,43 @@ class FileHasher { throw new IllegalArgumentException("File too large $size") } + + final MessageDigest digest + + FileHasher() { + try { + digest = MessageDigest.getInstance("SHA-256") + } catch (NoSuchAlgorithmException impossible) { + digest = null + System.exit(1) + } + } + + InfoHash hashFile(File file) { + final long length = file.length() + final int size = 0x1 << getPieceSize(length) + int numPieces = (int) (length / size) + if (numPieces * size < length) + numPieces++ + + def output = new ByteArrayOutputStream() + RandomAccessFile raf = new RandomAccessFile(file, "r") + try { + MappedByteBuffer buf + for (int i = 0; i < numPieces - 1; i++) { + buf = raf.getChannel().map(MapMode.READ_ONLY, size * i, size) + digest.update buf + output.write(digest.digest(), 0, 32) + } + def lastPieceLength = length - (numPieces - 1) * size + buf = raf.getChannel().map(MapMode.READ_ONLY, length - lastPieceLength, lastPieceLength) + digest.update buf + output.write(digest.digest(), 0, 32) + } finally { + raf.close() + } + + byte [] hashList = output.toByteArray() + InfoHash.fromHashList(hashList) + } } diff --git a/core/src/test/groovy/com/muwire/core/FileHasherTest.groovy b/core/src/test/groovy/com/muwire/core/FileHasherTest.groovy index 3c3512b9..4a0e5dce 100644 --- a/core/src/test/groovy/com/muwire/core/FileHasherTest.groovy +++ b/core/src/test/groovy/com/muwire/core/FileHasherTest.groovy @@ -2,10 +2,26 @@ package com.muwire.core import static org.junit.jupiter.api.Assertions.assertEquals +import org.junit.After +import org.junit.Before import org.junit.Test class FileHasherTest extends GroovyTestCase { + def hasher = new FileHasher() + File tmp + + @Before + void setUp() { + tmp = File.createTempFile("testFile", "test") + tmp.deleteOnExit() + } + + @After + void tearDown() { + tmp?.delete() + } + @Test void testPieceSize() { assert 18 == FileHasher.getPieceSize(1000000) @@ -14,6 +30,54 @@ class FileHasherTest extends GroovyTestCase { shouldFail IllegalArgumentException, { FileHasher.getPieceSize(Long.MAX_VALUE) } - + } + + @Test + void testHash1Byte() { + def fos = new FileOutputStream(tmp) + fos.write(0) + fos.close() + def ih = hasher.hashFile(tmp) + assert ih.getHashList().length == 32 + } + + @Test + void testHash1PieceExact() { + def fos = new FileOutputStream(tmp) + byte [] b = new byte[ 0x1 << 18] + fos.write b + fos.close() + def ih = hasher.hashFile tmp + assert ih.getHashList().length == 32 + } + + @Test + void testHash1Piece1Byte() { + def fos = new FileOutputStream(tmp) + byte [] b = new byte[ (0x1 << 18) + 1] + fos.write b + fos.close() + def ih = hasher.hashFile tmp + assert ih.getHashList().length == 64 + } + + @Test + void testHash2Pieces() { + def fos = new FileOutputStream(tmp) + byte [] b = new byte[ (0x1 << 19)] + fos.write b + fos.close() + def ih = hasher.hashFile tmp + assert ih.getHashList().length == 64 + } + + @Test + void testHash2Pieces2Bytes() { + def fos = new FileOutputStream(tmp) + byte [] b = new byte[ (0x1 << 19) + 2] + fos.write b + fos.close() + def ih = hasher.hashFile tmp + assert ih.getHashList().length == 32 * 3 } }