Scrypto is an open source cryptographic toolkit designed to make it easier and safer for developers to use cryptography in their applications.
It was extracted from Scorex, open-source modular blockchain & cryptocurrency framework.
Public Domain.
If you want to check benchmarks for authenticated AVL+ trees, please visit dedicated repository. Use the repository as code examples for the trees also, though one code example is provided in "Authenticated Data Structures" section below.
Scrypto is available on Sonatype for Scala 2.12!
resolvers += "Sonatype Releases" at "https://oss.sonatype.org/content/repositories/releases/"
You can use Scrypto in your sbt project by simply adding the following dependency to your build file:
// For Scala 2.12
libraryDependencies += "org.consensusresearch" %% "scrypto" % "1.2.0"
// For Scala 2.11
libraryDependencies += "org.consensusresearch" %% "scrypto" % "1.2.0-RC3"
Supported hash algorithms are:
- Blake
- Blake2b
- BMW
- CubeHash
- Echo
- Fugue
- Groestl
- Hamsi
- JH
- Keccak
- Luffa
- Sha
- SHAvite
- SIMD
- Skein
- Whirlpool
Take a look at CryptographicHash interface and use supported hash algorithms like
Keccak512("some string or bytes")
All provided hash functions are secure, and their implementations are thread safe.
It's possible to apply hash functions sequentially to create more secure hash function. The most well-known X11 hash chain is available from this library.
You can easily create your own hash chain function:
import scorex.crypto.applyHashes
object MyCustomHash extends CryptographicHash {
override val DigestSize: Int = 64
override def hash(input: Message): Digest = applyHashes(input, Blake512, Sha512, Groestl512, Skein512)
}
or just
val myHashChain = hashChain(Blake512, BMW512, Groestl512, Skein512, JH512, Keccak512, Luffa512, Wirlpool)
Note, that hash chain will be as good as the strongest of the algorithms included in the chain.
You can create commutative hash from any hash function with CommutativeHash
case class like CommutativeHash(Sha256)
.
A hash function h is commutative if h(x,y)==h(y,x) , for all x and y.
Scrypto has implementations of few binary-to-text encoding schemes:
- Base16
- Base58
- Base64
Example:
val encoded = Base64.encode(data)
val restored = Base64.decode(encoded)
restored shouldBe data
Scrypto supports following elliptic curves:
- Curve25519(& Ed25519)
Example:
val curveImpl = new Curve25519
val keyPair = curveImpl.createKeyPair()
val sig = curveImpl.sign(keyPair._1, message)
assert(curveImpl.verify(sig, message, keyPair._2))
Note on security: Scrypto provides Scala wrapper for Curve25519-Java by Whisper Systems, so has the same security properties. JDK's SecureRandom used to obtain seed bytes.
Scrypto supports authenticated AVL+ trees with the batching compression support and guaranteed verifier efficiency, see http://eprint.iacr.org/2016/994 for details.
The implementation can be found in the scorex.crypto.authds.avltree.batch
package. Here are code snippets on how to generate
proofs and check them. In this example we demonstrate two batches of two modifications per batch, starting with the empty tree: in the first batch, a prover inserts two values
into the tree; in the second batch, the prover updates the first value and deletes the second one.
- First, we create a prover and get an initial root hash from it (in a real application, this value is a public constant because anyone, including verifiers, can compute it by using the same two lines of code)
val prover = new BatchAVLProver(keyLength = 1, valueLength = 8)
val initRoot = prover.rootHash
- Second, we create the first batch of tree modifications, inserting keys 1 and 2 with values 0 for each
val m1 = Insert(Array(1:Byte), Array.fill(8)(0:Byte))
val m2 = Insert(Array(2:Byte), Array.fill(8)(0:Byte))
- Prover applies the two modifications to an empty tree, obtains the first batch proof, and announces the next root hash
root1
prover.performOneModification(m1)
prover.performOneModification(m2)
val proof1 = prover.generateProof
val root1 = prover.rootHash
- A proof is just an array of bytes, so you can immediately send it over a
wire or save it to a disk. Next, the prover performs two more modifications, obtains a second proof, and announces the second
root hash
root2
val m3 = Update(Array(1:Byte), Array.fill(8)(1:Byte))
val m4 = Remove(Array(2:Byte))
prover.performOneModification(m3)
prover.performOneModification(m4)
val proof2 = prover.generateProof
val root2 = prover.rootHash
- We now verify the proofs. For each batch, we construct a verifier using the digest that preceded the batch and the proof of the batch, and apply modifications. If verification fails, the verifier digest will equal None. Else, the verifier's new digest is the correct one for the tree as modified by the verifier. Furthermore, if the verifier performed the same modifications as the prover, then the verifier and prover digests will match.
val verifier1 = new BatchAVLVerifier(initRoot, proof1, keyLength = 1, valueLength = 8)
verifier1.performOneModification(m1)
verifier1.performOneModification(m2)
verifier1.digest match {
case Some(rt1) if rt1.sameElements(root1) =>
//If announced root1 is already trusted, then verification of the second batch can simply start here
val verifier2 = new BatchAVLVerifier(rt1, proof2, keyLength = 1, valueLength = 8)
verifier2.performOneModification(m3)
verifier2.performOneModification(m4)
verifier2.digest match {
case Some(rt2) if rt2.sameElements(root2) => println("declared root2 value and proofs are valid")
case _ => println("second proof or announced root value NOT valid")
}
case _ =>
println("first proof or announced root1 NOT valid")
}
Note that the verifier has additional optional parameters that can be used to limit its maximum running time, to prevent a malicious prover from forcing the verifier to waste cycles.
Run sbt test
from a folder contains the framework to launch tests.
The code is under Public Domain CC0 license means you can do anything with it. Full license text is in COPYING file
Your contributions are always welcome! Please submit a pull request or create an issue to add a new cryptographic primitives or better implementations.