Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #18 from searler/checksum
generalize SignatureCodec to support checksums and Digests
- Loading branch information
Showing
5 changed files
with
224 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
package scodec | ||
package codecs | ||
|
||
import java.security.MessageDigest | ||
import java.util.Arrays | ||
import java.util.zip.{ CRC32, Adler32, Checksum } | ||
import scodec.bits.ByteVector | ||
|
||
/** | ||
* Create "checksum" implementations for [[SignerFactory]] | ||
* @group checksum | ||
*/ | ||
object ChecksumFactory { | ||
/** | ||
* Creates a `java.security.Digest` factory for the specified algorithm | ||
* | ||
*/ | ||
def digest(algorithm: String): SignerFactory = | ||
new DigestFactory(algorithm) | ||
|
||
/** | ||
* Fletcher-16 checksum | ||
*/ | ||
val fletcher16: SignerFactory = new ChecksumFactory { | ||
def newSigner = new Fletcher16Checksum | ||
} | ||
|
||
/** | ||
* CRC-32 checksum | ||
*/ | ||
val crc32: SignerFactory = new ChecksumFactory { | ||
def newSigner = new ZipChecksumSigner(new CRC32()) | ||
} | ||
|
||
/** | ||
* Adler-32 checksum | ||
*/ | ||
val adler32: SignerFactory = new ChecksumFactory { | ||
def newSigner = new ZipChecksumSigner(new Adler32()) | ||
} | ||
|
||
/** | ||
* `java.security.Digest` implementation of Signer | ||
*/ | ||
private class DigestSigner(impl: MessageDigest) extends Signer { | ||
def update(data: Array[Byte]): Unit = impl.update(data) | ||
def sign: Array[Byte] = impl.digest | ||
def verify(signature: Array[Byte]): Boolean = MessageDigest.isEqual(impl.digest(), signature) | ||
} | ||
|
||
/** | ||
* A checksum does not have a distinct verify implementation | ||
*/ | ||
private trait ChecksumFactory extends SignerFactory { | ||
def newVerifier: Signer = newSigner | ||
} | ||
|
||
private class DigestFactory(val algorithm: String) extends ChecksumFactory { | ||
def newSigner: Signer = new DigestSigner(MessageDigest.getInstance(algorithm)) | ||
} | ||
|
||
/** | ||
* http://en.wikipedia.org/wiki/Fletcher's_checksum | ||
*/ | ||
private class Fletcher16Checksum extends Signer { | ||
var checksum = (0, 0) | ||
def update(data: Array[Byte]): Unit = { | ||
checksum = data.foldLeft(checksum) { (p, b) => | ||
val lsb = (p._2 + (0xff & b)) % 255 | ||
((p._1 + lsb) % 255, lsb) | ||
} | ||
} | ||
def sign: Array[Byte] = Array(checksum._1.asInstanceOf[Byte], checksum._2.asInstanceOf[Byte]) | ||
def verify(signature: Array[Byte]): Boolean = Arrays.equals(sign, signature) | ||
} | ||
|
||
/** | ||
* `java.util.zip.Checksum` implementation of Signer | ||
*/ | ||
private class ZipChecksumSigner(impl: Checksum) extends Signer { | ||
def update(data: Array[Byte]): Unit = impl.update(data, 0, data.length) | ||
def sign: Array[Byte] = ByteVector.fromLong(impl.getValue()).drop(4).toArray | ||
def verify(signature: Array[Byte]): Boolean = MessageDigest.isEqual(sign, signature) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package scodec.codecs | ||
|
||
import org.scalacheck._ | ||
import org.scalatest.WordSpec | ||
import org.scalatest.prop.GeneratorDrivenPropertyChecks | ||
import scodec.bits.ByteVector | ||
|
||
/** | ||
* Test the Fletcher checksum functionality | ||
* | ||
*/ | ||
class FletcherChecksumTest extends WordSpec with GeneratorDrivenPropertyChecks { | ||
|
||
"fletcher16 checksum" should { | ||
/** | ||
* http://en.wikipedia.org/wiki/Fletcher's_checksum | ||
* | ||
* "Example calculation of the Fletcher-16 checksum" | ||
* | ||
*/ | ||
"wikipedia" in { | ||
val signer = ChecksumFactory.fletcher16.newSigner | ||
signer.update(Array(0x01, 0x02)) | ||
assert(signer.verify(Array(0x04, 0x03))) | ||
} | ||
|
||
"0xAA * (3N+2) => Array(0x0,0x55)" in { | ||
forAll(Gen.posNum[Int]) { (n: Int) => | ||
pattern(n, 2, | ||
Array(0x0, 0x55)) | ||
} | ||
} | ||
|
||
"0xAA * (3N + 1) => Array(0xAA, 0xAA)" in { | ||
forAll(Gen.posNum[Int]) { (n: Int) => | ||
pattern(n, 1, | ||
Array(0xAA.asInstanceOf[Byte], 0xAA.asInstanceOf[Byte])) | ||
} | ||
} | ||
|
||
"0xAA * (3N) => Array(0x0, 0x0)" in { | ||
forAll(Gen.posNum[Int]) { (n: Int) => | ||
pattern(n, 0, | ||
Array(0x0, 0x0)) | ||
} | ||
} | ||
} | ||
|
||
private def pattern(n: Int, delta: Int, expected: Array[Byte]): Unit = { | ||
val signer = ChecksumFactory.fletcher16.newSigner | ||
signer.update(ByteVector.fill(3 * n + delta)(0xAA).toArray) | ||
assert(signer.verify(expected)) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters