Fixed multihash implementation.

This commit is contained in:
cheatfate 2018-12-04 23:11:13 +02:00
parent 09facf55e4
commit 2e1b5b0fbf
1 changed files with 101 additions and 54 deletions

View File

@ -10,10 +10,10 @@
## This module implements MultiHash. ## This module implements MultiHash.
## Supported hashes are: ## Supported hashes are:
## 1. IDENTITY ## 1. IDENTITY
## 2. SHA2256/SHA512 ## 2. SHA2-256/SHA2-512
## 3. DBL-SHA2-256 ## 3. DBL-SHA2-256
## 4. SHA3/KECCAK ## 4. SHA3/KECCAK
## 5. SHAKE128/SHAKE256 ## 5. SHAKE-128/SHAKE-256
## 6. BLAKE2s/BLAKE2s ## 6. BLAKE2s/BLAKE2s
## ##
## Hashes which are not yet supported ## Hashes which are not yet supported
@ -28,7 +28,8 @@ const
MaxHashSize* = 128 MaxHashSize* = 128
type type
MHashCoderProc* = proc(data: openarray[byte], output: var openarray[byte]) MHashCoderProc* = proc(data: openarray[byte],
output: var openarray[byte]) {.nimcall, gcsafe.}
MHash* = object MHash* = object
name*: string name*: string
code*: int code*: int
@ -43,7 +44,7 @@ type
MultiHashError* = object of Exception MultiHashError* = object of Exception
proc identityHash(data: openarray[byte], output: var openarray[byte]) = proc identhash(data: openarray[byte], output: var openarray[byte]) =
if len(output) > 0: if len(output) > 0:
var length = if len(data) > len(output): len(output) var length = if len(data) > len(output): len(output)
else: len(data) else: len(data)
@ -160,11 +161,11 @@ proc shake_256hash(data: openarray[byte], output: var openarray[byte]) =
sctx.clear() sctx.clear()
const const
HashesList = [ HashesList = [
MHash(name: "identity", code: 0x00, size: 0, coder: identityHash), MHash(name: "identity", code: 0x00, size: 0, coder: identhash),
MHash(name: "dbl-sha2-256", code: 0x56, size: sha256.sizeDigest, MHash(name: "dbl-sha2-256", code: 0x56, size: sha256.sizeDigest,
coder: dblsha2_256hash), coder: dblsha2_256hash
),
MHash(name: "sha2-256", code: 0x12, size: sha256.sizeDigest, MHash(name: "sha2-256", code: 0x12, size: sha256.sizeDigest,
coder: sha2_256hash coder: sha2_256hash
), ),
@ -292,7 +293,7 @@ const
MHash(name: "blake2s-232", code: 0xB25D, size: 29, coder: blake2Shash), MHash(name: "blake2s-232", code: 0xB25D, size: 29, coder: blake2Shash),
MHash(name: "blake2s-240", code: 0xB25E, size: 30, coder: blake2Shash), MHash(name: "blake2s-240", code: 0xB25E, size: 30, coder: blake2Shash),
MHash(name: "blake2s-248", code: 0xB25F, size: 31, coder: blake2Shash), MHash(name: "blake2s-248", code: 0xB25F, size: 31, coder: blake2Shash),
MHash(name: "blake2s-256", code: 0xB260, size: 32, coder: blake2Shash), MHash(name: "blake2s-256", code: 0xB260, size: 32, coder: blake2Shash)
] ]
proc initMultiHashNameTable(): Table[string, MHash] {.compileTime.} = proc initMultiHashNameTable(): Table[string, MHash] {.compileTime.} =
@ -313,7 +314,7 @@ proc multihashName*(code: int): string =
## Returns MultiHash digest name from its code. ## Returns MultiHash digest name from its code.
let hash = CodeHashes.getOrDefault(code) let hash = CodeHashes.getOrDefault(code)
if isNil(hash.coder): if isNil(hash.coder):
raise newException(MultihashError, "Hash not found") raise newException(MultiHashError, "Hash not supported")
else: else:
result = hash.name result = hash.name
@ -321,7 +322,7 @@ proc multihashCode*(name: string): int =
## Returns MultiHash digest code from its name. ## Returns MultiHash digest code from its name.
let hash = NameHashes.getOrDefault(name) let hash = NameHashes.getOrDefault(name)
if isNil(hash.coder): if isNil(hash.coder):
raise newException(MultihashError, "Hash not found") raise newException(MultiHashError, "Hash not supported")
else: else:
result = hash.code result = hash.code
@ -332,12 +333,12 @@ proc digestImplWithHash(hash: MHash, data: openarray[byte]): MultiHash =
result.data.writeVarint(uint(hash.code)) result.data.writeVarint(uint(hash.code))
if hash.size == 0: if hash.size == 0:
result.data.writeVarint(uint(len(data))) result.data.writeVarint(uint(len(data)))
result.dpos = len(result.data) result.dpos = len(result.data.buffer)
result.data.writeArray(data) result.data.writeArray(data)
result.size = len(data) result.size = len(data)
else: else:
result.data.writeVarint(uint(hash.size)) result.data.writeVarint(uint(hash.size))
result.dpos = len(result.data) result.dpos = len(result.data.buffer)
hash.coder(data, buffer.toOpenArray(0, hash.size - 1)) hash.coder(data, buffer.toOpenArray(0, hash.size - 1))
result.data.writeArray(buffer.toOpenArray(0, hash.size - 1)) result.data.writeArray(buffer.toOpenArray(0, hash.size - 1))
result.size = hash.size result.size = hash.size
@ -348,7 +349,7 @@ proc digestImplWithoutHash(hash: MHash, data: openarray[byte]): MultiHash =
result.size = len(data) result.size = len(data)
result.data.writeVarint(uint(hash.code)) result.data.writeVarint(uint(hash.code))
result.data.writeVarint(uint(len(data))) result.data.writeVarint(uint(len(data)))
result.dpos = len(result.data) result.dpos = len(result.data.buffer)
result.data.writeArray(data) result.data.writeArray(data)
proc digest*(mhtype: typedesc[MultiHash], hashname: string, proc digest*(mhtype: typedesc[MultiHash], hashname: string,
@ -376,6 +377,8 @@ proc init*[T](mhtype: typedesc[MultiHash], hashname: string,
let hash = NameHashes.getOrDefault(hashname) let hash = NameHashes.getOrDefault(hashname)
if isNil(hash.coder): if isNil(hash.coder):
raise newException(MultihashError, "Hash not supported") raise newException(MultihashError, "Hash not supported")
if hash.size != len(mdigest.data):
raise newException(MultiHashError, "Incorrect MDigest[T] size")
result = digestImplWithoutHash(hash, mdigest.data) result = digestImplWithoutHash(hash, mdigest.data)
proc init*[T](mhtype: typedesc[MultiHash], hashcode: int, proc init*[T](mhtype: typedesc[MultiHash], hashcode: int,
@ -385,68 +388,117 @@ proc init*[T](mhtype: typedesc[MultiHash], hashcode: int,
let hash = CodeHashes.getOrDefault(hashcode) let hash = CodeHashes.getOrDefault(hashcode)
if isNil(hash.coder): if isNil(hash.coder):
raise newException(MultihashError, "Hash not supported") raise newException(MultihashError, "Hash not supported")
if (hash.size != 0) and (hash.size != len(mdigest.data)):
raise newException(MultiHashError, "Incorrect MDigest[T] size")
result = digestImplWithoutHash(hash, mdigest.data) result = digestImplWithoutHash(hash, mdigest.data)
proc init*[T](mhtype: typedesc[MultiHash], hashname: string, proc init*(mhtype: typedesc[MultiHash], hashname: string,
bdigest: openarray[byte]): MultiHash {.inline.} = bdigest: openarray[byte]): MultiHash {.inline.} =
## Create MultiHash from array of bytes ``bdigest`` and hash algorithm code ## Create MultiHash from array of bytes ``bdigest`` and hash algorithm code
## ``hashcode``. ## ``hashcode``.
let hash = NameHashes.getOrDefault(hashname) let hash = NameHashes.getOrDefault(hashname)
if isNil(hash.coder): if isNil(hash.coder):
raise newException(MultihashError, "Hash not supported") raise newException(MultihashError, "Hash not supported")
if (hash.size != 0) and (hash.size != len(bdigest)):
raise newException(MultiHashError, "Incorrect bdigest size")
result = digestImplWithoutHash(hash, bdigest) result = digestImplWithoutHash(hash, bdigest)
proc init*[T](mhtype: typedesc[MultiHash], hashcode: int, proc init*(mhtype: typedesc[MultiHash], hashcode: int,
bdigest: openarray[byte]): MultiHash {.inline.} = bdigest: openarray[byte]): MultiHash {.inline.} =
## Create MultiHash from array of bytes ``bdigest`` and hash algorithm code ## Create MultiHash from array of bytes ``bdigest`` and hash algorithm code
## ``hashcode``. ## ``hashcode``.
let hash = CodeHashes.getOrDefault(hashcode) let hash = CodeHashes.getOrDefault(hashcode)
if isNil(hash.coder): if isNil(hash.coder):
raise newException(MultihashError, "Hash not supported") raise newException(MultihashError, "Hash not supported")
if (hash.size != 0) and (hash.size != len(bdigest)):
raise newException(MultiHashError, "Incorrect bdigest size")
result = digestImplWithoutHash(hash, bdigest) result = digestImplWithoutHash(hash, bdigest)
proc decode*(mhtype: typedesc[MultiHash], data: openarray[byte],
proc fromBytes*(mhtype: typedesc[MultiHash], data: openarray[byte]): MultiHash = mhash: var MultiHash): int =
discard ## Decode MultiHash value from array of bytes ``data``.
##
proc shcopy*(m1: var MultiHash, m2: MultiHash) = ## On success decoded MultiHash will be stored into ``mhash`` and number of
shallowCopy(m1.data.buffer, m2.data.buffer) ## bytes consumed will be returned.
m1.data.offset = m2.data.offset ##
m1.data.length = m2.data.length ## On error ``-1`` will be returned.
m1.code = m2.code var code, size: uint64
m1.size = m2.size var res, dpos: int
m1.dpos = m2.dpos if len(data) < 2:
return -1
proc validate*(mh: MultiHash): bool = var vb = initVBuffer(data)
## Returns ``true`` if MultiHash ``mh`` is valid. if vb.isEmpty():
var code: uint64 return -1
var size: uint64 res = vb.readVarint(code)
var res: int
var dpos: int
var vb: MultiHash
shcopy(vb, mh)
if vb.data.isEmpty():
return false
res = vb.data.readVarint(code)
if res == -1: if res == -1:
return false return -1
dpos = res dpos += res
res = vb.data.readVarint(size) res = vb.readVarint(size)
if res == -1: if res == -1:
return false return -1
dpos += res dpos += res
if size > 0x7FFF_FFFF'u64: if size > 0x7FFF_FFFF'u64:
return false return -1
let hash = CodeHashes.getOrDefault(int(code)) let hash = CodeHashes.getOrDefault(int(code))
if isNil(hash.coder): if isNil(hash.coder):
return false return -1
if (hash.size != 0) and (hash.size != int(size)): if (hash.size != 0) and (hash.size != int(size)):
return -1
if not vb.isEnough(int(size)):
return -1
mhash = MultiHash.init(int(code),
vb.buffer.toOpenArray(vb.offset,
vb.offset + int(size) - 1))
result = vb.offset + int(size)
proc init*(mhtype: typedesc[MultiHash],
data: openarray[byte]): MultiHash {.inline.} =
## Create MultiHash from bytes array ``data``.
if MultiHash.decode(data, result) == -1:
raise newException(MultihashError, "Incorrect MultiHash binary format")
proc init*(mhtype: typedesc[MultiHash], data: string): MultiHash {.inline.} =
## Create MultiHash from hexadecimal string representation ``data``.
if MultiHash.decode(fromHex(data), result) == -1:
raise newException(MultihashError, "Incorrect MultiHash binary format")
proc cmp(a: openarray[byte], b: openarray[byte]): bool {.inline.} =
if len(a) != len(b):
return false return false
if not vb.data.isEnough(int(size)): var n = len(a)
var res, diff: int
while n > 0:
dec(n)
diff = int(a[n]) - int(b[n])
res = (res and -not(diff)) or diff
result = (res == 0)
proc `==`*[T](mh: MultiHash, mdigest: MDigest[T]): bool =
## Compares MultiHash with nimcrypto's MDigest[T], returns ``true`` if
## hashes are equal, ``false`` otherwise.
if mh.dpos == 0:
return false return false
if (dpos + int(size)) != len(vb.data): if len(mdigest.data) != mh.size:
return false return false
result = true result = cmp(mh.data.buffer.toOpenArray(mh.dpos, mh.dpos + mh.size - 1),
mdigest.data.toOpenArray(0, len(mdigest.data) - 1))
proc `==`*[T](mdigest: MDigest[T], mh: MultiHash): bool {.inline.} =
## Compares MultiHash with nimcrypto's MDigest[T], returns ``true`` if
## hashes are equal, ``false`` otherwise.
result = `==`(mh, mdigest)
proc `==`*(a, b: MultiHash): bool =
## Compares MultiHashes ``a`` and ``b``, returns ``true`` if
## hashes are equal, ``false`` otherwise.
if a.dpos == 0 and b.dpos == 0:
return true
if a.code != b.code:
return false
if a.size != b.size:
return false
result = cmp(a.data.buffer.toOpenArray(a.dpos, a.dpos + a.size - 1),
b.data.buffer.toOpenArray(b.dpos, b.dpos + b.size - 1))
proc hex*(value: MultiHash): string = proc hex*(value: MultiHash): string =
## Return hexadecimal string representation of MultiHash ``value``. ## Return hexadecimal string representation of MultiHash ``value``.
@ -461,8 +513,3 @@ proc `$`*(value: MultiHash): string =
let digest = toHex(value.data.buffer.toOpenArray(value.dpos, let digest = toHex(value.data.buffer.toOpenArray(value.dpos,
value.dpos + value.size - 1)) value.dpos + value.size - 1))
result = multihashName(value.code) & "/" & digest result = multihashName(value.code) & "/" & digest
when isMainModule:
var msg = "Hello World!"
echo MultiHash.digest("sha2-256", cast[seq[byte]](msg))