diff --git a/nimcrypto/bcmode.nim b/nimcrypto/bcmode.nim index 0cca060..da225bd 100644 --- a/nimcrypto/bcmode.nim +++ b/nimcrypto/bcmode.nim @@ -70,8 +70,12 @@ type y: array[16, byte] basectr: array[16, byte] buf: array[16, byte] + ectr: array[16, byte] aadlen: uint64 datalen: uint64 + ectrUsed: int + ghashPending: array[16, byte] + ghashUsed: int ## ECB (Electronic Code Book) Mode @@ -1083,6 +1087,37 @@ template sizeKey*[T](ctx: GCM[T]): int = mixin sizeKey sizeKey(ctx.cipher) +func refillEctr[T](ctx: var GCM[T]) {.inline.} = + ## Refresh keystream block and reset offset. + inc128(ctx.y) + ctx.cipher.encrypt(ctx.y, ctx.ectr) + ctx.ectrUsed = 0 + +func updateGhash[T](ctx: var GCM[T], data: openArray[byte]) {.inline.} = + ## Process data into GHASH, buffering partial blocks until full. + var offset = 0 + var length = len(data) + + if ctx.ghashUsed > 0 and length > 0: + let toCopy = min(16 - ctx.ghashUsed, length) + copyMem(addr ctx.ghashPending[ctx.ghashUsed], unsafeAddr data[offset], toCopy) + ctx.ghashUsed += toCopy + offset += toCopy + length -= toCopy + if ctx.ghashUsed == 16: + ghash(ctx.buf, ctx.h, ctx.ghashPending) + ctx.ghashUsed = 0 + + let alignedLen = (length shr 4) shl 4 + if alignedLen > 0: + ghash(ctx.buf, ctx.h, data.toOpenArray(offset, offset + alignedLen - 1)) + offset += alignedLen + length -= alignedLen + + if length > 0: + copyMem(addr ctx.ghashPending[0], unsafeAddr data[offset], length) + ctx.ghashUsed = length + func init*[T]( ctx: var GCM[T], key: openArray[byte], @@ -1121,6 +1156,7 @@ func init*[T]( ctx.datalen = 0 if len(aad) > 0: ghash(ctx.buf, ctx.h, aad) + ctx.refillEctr() func encrypt*[T]( ctx: var GCM[T], @@ -1133,19 +1169,20 @@ func encrypt*[T]( ## Note that length of ``input`` must be less or equal to length of ## ``output``. Length of ``input`` must not be zero. mixin encrypt - var ectr: array[16, byte] assert(len(input) <= len(output)) var length = len(input) var offset = 0 ctx.datalen += uint64(length) while length > 0: - let uselen = if length < 16: length else: 16 - inc128(ctx.y) - ctx.cipher.encrypt(ctx.y, ectr) + if ctx.ectrUsed == 16: + ctx.refillEctr() + let available = 16 - ctx.ectrUsed + let uselen = if length < available: length else: available for i in 0.. 0: - let uselen = if length < 16: length else: 16 - inc128(ctx.y) - ctx.cipher.encrypt(ctx.y, ectr) + if ctx.ectrUsed == 16: + ctx.refillEctr() + let available = 16 - ctx.ectrUsed + let uselen = if length < available: length else: available + ctx.updateGhash(input.toOpenArray(offset, offset + uselen - 1)) for i in 0.. 0: copyMem(tag, 0, ctx.basectr, 0, uselen) + if ctx.ghashUsed > 0: + zeroMem(addr ctx.ghashPending[ctx.ghashUsed], 16 - ctx.ghashUsed) + ghash(ctx.buf, ctx.h, ctx.ghashPending) + ctx.ghashUsed = 0 beStore64(workbuf, 0, ctx.aadlen shl 3) beStore64(workbuf, 8, ctx.datalen shl 3) ghash(ctx.buf, ctx.h, workbuf)