From 49bed1afa38a36948ebf063340ecf930d0507dcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mamy=20Andr=C3=A9-Ratsimbazafy?= Date: Wed, 11 Mar 2020 01:47:30 +0100 Subject: [PATCH 1/2] Move to openArray API (internal) for HKDF and HashToG2 --- blscurve/bls_signature_scheme.nim | 14 ++++---- blscurve/hash_to_curve.nim | 35 +++++++++----------- blscurve/hkdf.nim | 55 ++++++++++++++++--------------- 3 files changed, 53 insertions(+), 51 deletions(-) diff --git a/blscurve/bls_signature_scheme.nim b/blscurve/bls_signature_scheme.nim index 133b325..dd98306 100644 --- a/blscurve/bls_signature_scheme.nim +++ b/blscurve/bls_signature_scheme.nim @@ -513,11 +513,7 @@ func keyGen*(ikm: openarray[byte], publicKey: var PublicKey, secretKey: var Secr var prk: MDigest[sha256.bits] # 1. PRK = HKDF-Extract("BLS-SIG-KEYGEN-SALT-", IKM) - ctx.hkdfExtract( - prk, - cast[ptr byte](salt[0].unsafeAddr), salt.len.uint, - ikm[0].unsafeAddr, ikm.len.uint - ) + ctx.hkdfExtract(prk, salt, ikm) # curve order r = 52435875175126190479447740508185965837690552500527637822603658699938581184513 # const L = ceil((1.5 * ceil(log2(r))) / 8) = 48 @@ -526,17 +522,23 @@ func keyGen*(ikm: openarray[byte], publicKey: var PublicKey, secretKey: var Secr # 2. OKM = HKDF-Expand(PRK, "", L) const L = 48 var okm: array[L, byte] - ctx.hkdfExpand(prk, nil, 0, okm[0].addr, L) + ctx.hkdfExpand(prk, "", okm) # 3. x = OS2IP(OKM) mod r # 5. SK = x + debugecho "keyGen:" if not secretKey.intVal.fromBytes(okm): return false {.noSideEffect.}: + debugecho " CURVE_Order: ", CURVE_Order.toHex() BIG_384_mod(secretKey.intVal, CURVE_Order) + debugecho " seckey (mod): ", secretKey.toHex() + # 4. xP = x * P # 6. PK = point_to_pubkey(xP) publicKey = privToPub(secretKey) + debugecho " seckey (exit): ", secretKey.toHex() + return true diff --git a/blscurve/hash_to_curve.nim b/blscurve/hash_to_curve.nim index 44e09ee..38458a7 100644 --- a/blscurve/hash_to_curve.nim +++ b/blscurve/hash_to_curve.nim @@ -30,10 +30,9 @@ import func hashToBaseFP2[T]( ctx: var HMAC[T], - msg: ptr byte, msgLen: uint, + msg: ptr byte, msgLen: int, ctr: range[0'i8 .. 2'i8], - domainSepTag: ptr byte, - domainSepTagLen: uint + domainSepTag: string, ): FP2_BLS381 = ## Implementation of hash_to_base for the G2 curve of BLS12-381 ## https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-04#section-5.3 @@ -84,7 +83,10 @@ func hashToBaseFP2[T]( # with an extra null-byte beyond the declared length. assert not msg.isNil assert cast[ptr UncheckedArray[byte]](msg)[msgLen] == 0x00, "Expected message terminated by nul-byte but found " & $cast[ptr UncheckedArray[byte]](msg)[msgLen] & " (decimal value)" - hkdfExtract(ctx, mprime, domainSepTag, domainSepTagLen, msg, msgLen+1) + hkdfExtract( + ctx, mprime, domainSepTag, + toOpenArray(cast[ptr UncheckedArray[byte]](msg), 0, msgLen) # msgLen inclusive + ) info[0] = ord'H' info[1] = ord'2' @@ -96,7 +98,7 @@ func hashToBaseFP2[T]( ## for i in 1 .. m ## with m = 2 (extension degree of FP2) info[4] = byte(i) - hkdfExpand(ctx, mprime, info[0].addr, info.len.uint, t[0].addr, t.len.uint) + hkdfExpand(ctx, mprime, info, t) block: # HKDF output is greater than 384-bit (64 bytes = 512-bit) # and need to be stored and reduced in a DBIG @@ -404,13 +406,13 @@ func clearCofactor(P: var ECP2_BLS381) = P.affine() # Convert from Jacobian coordinates (x', y', z') to affine (x, y, 1); (x is not the curve parameter here) -func hashToG2(msg: ptr byte, msgLen: uint, domainSepTag: ptr byte, domainSepTagLen: uint): ECP2_BLS381 = +func hashToG2(msg: ptr byte, msgLen: int, domainSepTag: string): ECP2_BLS381 = ## Hash an arbitrary message to the G2 curve of BLS12-381 ## The message should have an extra null byte after its declared length var ctx: HMAC[sha256] - let u0 = hashToBaseFP2(ctx, msg, msgLen, ctr = 0, domainSepTag, domainSepTagLen) - let u1 = hashToBaseFP2(ctx, msg, msgLen, ctr = 1, domainSepTag, domainSepTagLen) + let u0 = hashToBaseFP2(ctx, msg, msgLen, ctr = 0, domainSepTag) + let u1 = hashToBaseFP2(ctx, msg, msgLen, ctr = 1, domainSepTag) result = mapToCurveG2(u0) let Q1 = mapToCurveG2(u1) @@ -427,31 +429,27 @@ func hashToG2*(msg: openarray[byte], domainSepTag: string): ECP2_BLS381 = msgWithNul[0 ..< msg.len] = msg msgWithNul[msg.len] = 0x00 - let - pmsg = cast[ptr byte](msgWithNul[0].unsafeAddr) - pdst = cast[ptr byte](domainSepTag[0].unsafeAddr) + let pmsg = cast[ptr byte](msgWithNul[0].unsafeAddr) - hashToG2(pmsg, msg.len.uint, pdst, domainSepTag.len.uint) + hashToG2(pmsg, msg.len, domainSepTag) func hashToG2*(message, domainSepTag: string): ECP2_BLS381 = ## Hash an arbitrary message to the G2 curve of BLS12-381 ## The message should have an extra null byte - let pdst = cast[ptr byte](domainSepTag[0].unsafeAddr) - if message.len == 0: # Special-casing empty strings (i.e. not constant-time) # is not an issue because empty strings are always vulnerable # to timing attacks. var empty = [byte 0x00] let pmsg = cast[ptr byte](empty[0].unsafeAddr) - result = hashToG2(pmsg, 0, pdst, domainSepTag.len.uint) + result = hashToG2(pmsg, 0, domainSepTag) else: # Strings are always nul-terminated in Nim so don't need # extra nul appending compared to openarray[byte] # to satisfy the spec requirements let pmsg = cast[ptr byte](message[0].unsafeAddr) - result = hashToG2(pmsg, message.len.uint, pdst, domainSepTag.len.uint) + result = hashToG2(pmsg, message.len, domainSepTag) # Unofficial test vectors for hashToG2 primitives # ---------------------------------------------------------------------- @@ -490,15 +488,14 @@ when isMainModule: let pmsg = if msg.len == 0: nil else: cast[ptr byte](msg[0].unsafeAddr) - let pdst = cast[ptr byte](dst[0].unsafeAddr) var ctx: HMAC[sha256] # Important: do we need to include the null byte at the end? let pointFP2 = hashToBaseFP2( ctx, - pmsg, msg.len.uint, + pmsg, msg.len, ctr, - pdst, dst.len.uint + dst ) doAssert fp2 == pointFP2 echo "Success hashToBaseFP2 ", astToStr(id) diff --git a/blscurve/hkdf.nim b/blscurve/hkdf.nim index cc29f39..f55ee4b 100644 --- a/blscurve/hkdf.nim +++ b/blscurve/hkdf.nim @@ -80,10 +80,10 @@ import nimcrypto/hmac -func hkdfExtract*[T](ctx: var HMAC[T], +func hkdfExtract*[T;S,I: char|byte](ctx: var HMAC[T], prk: var MDigest[T.bits], - salt: ptr byte, saltLen: uint, - ikm: ptr byte, ikmLen: uint + salt: openArray[S], + ikm: openArray[I] ) = ## "Extract" step of HKDF. ## Extract a fixed size pseudom-random key @@ -91,8 +91,8 @@ func hkdfExtract*[T](ctx: var HMAC[T], ## and a secret input keying material. ## ## Inputs: - ## - salt + saltLen: a buffer to an optional salt value (set to nil if unused) - ## - ikm + ikmLen: "input keying material", the secret value to hash. + ## - salt: a buffer to an optional salt value (set to nil if unused) + ## - ikm: "input keying material", the secret value to hash. ## ## Output: ## - prk: a pseudo random key of fixed size. The size is the same as the cryptographic hash chosen. @@ -100,16 +100,16 @@ func hkdfExtract*[T](ctx: var HMAC[T], ## Temporary: ## - ctx: a HMAC["cryptographic-hash"] context, for example HMAC[sha256]. mixin init, update, finish - ctx.init(salt, saltLen) - ctx.update(ikm, ikmLen) + ctx.init(salt) + ctx.update(ikm) discard ctx.finish(prk.data) # ctx.clear() - TODO: very expensive -func hkdfExpand*[T](ctx: var HMAC[T], +func hkdfExpand*[T;I: char|byte](ctx: var HMAC[T], prk: MDigest[T.bits], - info: ptr byte, infoLen: uint, - output: ptr byte, outLen: uint + info: openArray[I], + output: var openArray[byte] ) = ## "Expand" step of HKDF ## Expand a fixed size pseudo random-key @@ -117,12 +117,11 @@ func hkdfExpand*[T](ctx: var HMAC[T], ## ## Inputs: ## - prk: a pseudo random key (PRK) of fixed size. The size is the same as the cryptographic hash chosen. - ## - info + infolen: optional context and application specific information (set to nil if unused) - ## - outLen: The target length of the output + ## - info: optional context and application specific information (set to nil if unused) ## ## Output: ## - output: OKM (output keying material). The PRK is expanded to match - ## outLen, the result is tored in output. + ## the output length, the result is tored in output. ## ## Temporary: ## - ctx: a HMAC["cryptographic-hash"] context, for example HMAC[sha256]. @@ -131,23 +130,23 @@ func hkdfExpand*[T](ctx: var HMAC[T], const HashLen = T.bits div 8 static: doAssert T.bits >= 0 - # assert outLen <= 255*HashLen + # assert output.len <= 255*HashLen - let N = outLen div HashLen + let N = output.len div HashLen var t: MDigest[T.bits] let oArray = cast[ptr UncheckedArray[byte]](output) - for i in 0'u .. N: + for i in 0 .. N: ctx.init(prk.data) # T(0) = empty string if i != 0: ctx.update(t.data) - ctx.update(info, infoLen) + ctx.update(info) ctx.update([uint8(i+1)]) discard ctx.finish(t.data) let iStart = i * HashLen - let size = min(HashLen, int(outLen - iStart)) + let size = min(HashLen, output.len - iStart) copyMem(oArray[iStart].addr, t.data.addr, size) # ctx.clear() - TODO: very expensive @@ -178,15 +177,19 @@ when isMainModule: var ctx: HMAC[HashType] var prk: MDigest[HashType.bits] - let salt = if bsalt.len == 0: nil - else: bsalt[0].unsafeAddr - let ikm = if bikm.len == 0: nil - else: bikm[0].unsafeAddr - let info = if binfo.len == 0: nil - else: binfo[0].unsafeAddr + # let salt = if bsalt.len == 0: nil + # else: bsalt[0].unsafeAddr + # let ikm = if bikm.len == 0: nil + # else: bikm[0].unsafeAddr + # let info = if binfo.len == 0: nil + # else: binfo[0].unsafeAddr + let + salt = bsalt + ikm = bikm + info = binfo - hkdfExtract(ctx, prk, salt, bsalt.len.uint, ikm, bikm.len.uint) - hkdfExpand(ctx, prk, info, binfo.len.uint, output[0].addr, output.len.uint) + hkdfExtract(ctx, prk, salt, ikm) + hkdfExpand(ctx, prk, info, output) doAssert @(prk.data) == bprk, "\nComputed 0x" & toHex(prk.data) & "\nbut expected " & PRK & '\n' From 532f668fc08bff712ec1b093127c0ebdf8f00a49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mamy=20Andr=C3=A9-Ratsimbazafy?= Date: Wed, 11 Mar 2020 14:16:24 +0100 Subject: [PATCH 2/2] Fix #40 - Milagro can't parse integer with more than 381 used bits in a BIG_384 (but doesn't error) --- blscurve/bls_signature_scheme.nim | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/blscurve/bls_signature_scheme.nim b/blscurve/bls_signature_scheme.nim index dd98306..1efeea2 100644 --- a/blscurve/bls_signature_scheme.nim +++ b/blscurve/bls_signature_scheme.nim @@ -526,19 +526,14 @@ func keyGen*(ikm: openarray[byte], publicKey: var PublicKey, secretKey: var Secr # 3. x = OS2IP(OKM) mod r # 5. SK = x - debugecho "keyGen:" - if not secretKey.intVal.fromBytes(okm): + var dseckey: DBIG_384 + if not dseckey.fromBytes(okm): return false - {.noSideEffect.}: - debugecho " CURVE_Order: ", CURVE_Order.toHex() - BIG_384_mod(secretKey.intVal, CURVE_Order) - debugecho " seckey (mod): ", secretKey.toHex() + {.noSideEffect.}: + BIG_384_dmod(secretKey.intVal, dseckey, CURVE_Order) # 4. xP = x * P # 6. PK = point_to_pubkey(xP) publicKey = privToPub(secretKey) - - debugecho " seckey (exit): ", secretKey.toHex() - return true