Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 54 additions & 12 deletions nimcrypto/bcmode.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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],
Expand Down Expand Up @@ -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],
Expand All @@ -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..<uselen:
output[offset + i] = ectr[i] xor input[offset + i]
ghash(ctx.buf, ctx.h, output.toOpenArray(offset, offset + uselen - 1))
output[offset + i] = ctx.ectr[ctx.ectrUsed + i] xor input[offset + i]
ctx.updateGhash(output.toOpenArray(offset, offset + uselen - 1))
ctx.ectrUsed += uselen
length -= uselen
offset += uselen

Expand All @@ -1160,19 +1197,20 @@ func decrypt*[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
ctx.updateGhash(input.toOpenArray(offset, offset + uselen - 1))
for i in 0..<uselen:
output[offset + i] = ectr[i] xor input[offset + i]
ghash(ctx.buf, ctx.h, input.toOpenArray(offset, offset + uselen - 1))
output[offset + i] = ctx.ectr[ctx.ectrUsed + i] xor input[offset + i]
ctx.ectrUsed += uselen
length -= uselen
offset += uselen

Expand All @@ -1189,6 +1227,10 @@ func getTag*[T](
var workbuf: array[16, byte]
if taglen > 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)
Expand Down