1246 lines
46 KiB
Nim
1246 lines
46 KiB
Nim
#
|
|
#
|
|
# NimCrypto
|
|
# (c) Copyright 2016 Eugene Kabanov
|
|
#
|
|
# See the file "LICENSE", included in this
|
|
# distribution, for details about the copyright.
|
|
#
|
|
|
|
## This module implements various Block Cipher Modes.
|
|
##
|
|
## The five modes currently supported:
|
|
## * ECB (Electronic Code Book)
|
|
## * CBC (Cipher Block Chaining)
|
|
## * CFB (Cipher FeedBack)
|
|
## * OFB (Output FeedBack)
|
|
## * CTR (Counter)
|
|
## * GCM (Galois/Counter Mode)
|
|
##
|
|
## You can use any of this modes with all the block ciphers of nimcrypto library
|
|
##
|
|
## GHASH implementation is Nim version of `ghash_ctmul64.c` which is part
|
|
## of decent BearSSL project <https://bearssl.org>.
|
|
## Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
|
|
##
|
|
## Tests made according to official test vectors (Appendix F)
|
|
## http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf
|
|
## GCM tests made according official test vectors (Appendix B)
|
|
## https://pdfs.semanticscholar.org/114a/4222c53f1a6879f1a77f1bae2fc0f8f55348.pdf
|
|
## and OpenSSL vectors
|
|
## https://github.com/majek/openssl/blob/master/crypto/evp/evptests.txt
|
|
import utils
|
|
|
|
{.deadCodeElim:on.}
|
|
|
|
const
|
|
MaxBlockSize = 256
|
|
MaxBlockBytesSize = MaxBlockSize shr 3
|
|
|
|
type
|
|
ECB*[T] = object
|
|
## ECB (Electronic Code Book) context object
|
|
cipher: T
|
|
tmp: array[MaxBlockBytesSize, byte]
|
|
|
|
CBC*[T] = object
|
|
## CBC (Cipher Block Chaining) context object
|
|
cipher: T
|
|
iv: array[MaxBlockBytesSize, byte]
|
|
tmp: array[MaxBlockBytesSize, byte]
|
|
|
|
OFB*[T] = object
|
|
## OFB (Output FeedBack) context object
|
|
cipher: T
|
|
iv: array[MaxBlockBytesSize, byte]
|
|
|
|
CFB*[T] = object
|
|
## CFB (Cipher FeedBack) context object
|
|
cipher: T
|
|
iv: array[MaxBlockBytesSize, byte]
|
|
|
|
CTR*[T] = object
|
|
## CTR (Counter) context object
|
|
cipher: T
|
|
iv: array[MaxBlockBytesSize, byte]
|
|
ecount: array[MaxBlockBytesSize, byte]
|
|
num: uint
|
|
|
|
GCM*[T] = object
|
|
## GCM (Galois/Counter Mode) context object
|
|
cipher: T
|
|
h: array[16, byte]
|
|
y: array[16, byte]
|
|
basectr: array[16, byte]
|
|
buf: array[16, byte]
|
|
aadlen: uint64
|
|
datalen: uint64
|
|
|
|
## ECB (Electronic Code Book) Mode
|
|
|
|
template sizeBlock*[T](ctx: ECB[T]): int =
|
|
## Size of ``ECB[T]`` block in octets (bytes). This value is equal
|
|
## to cipher ``T`` block size.
|
|
mixin sizeBlock
|
|
sizeBlock(ctx.cipher)
|
|
|
|
template sizeKey*[T](ctx: ECB[T]): int =
|
|
## Size of ``ECB[T]`` key in octets (bytes). This value is equal
|
|
## to cipher ``T`` key size.
|
|
mixin sizeKey
|
|
sizeKey(ctx.cipher)
|
|
|
|
proc init*[T](ctx: var ECB[T], key: ptr byte) =
|
|
## Initialize ``ECB[T]`` with encryption key ``key``.
|
|
##
|
|
## Note! Size of data pointed by ``key`` must be at least ``ctx.sizeKey``
|
|
## octets (bytes).
|
|
mixin init
|
|
assert(not isNil(key))
|
|
assert(ctx.sizeBlock <= MaxBlockSize)
|
|
init(ctx.cipher, key)
|
|
|
|
proc init*[T](ctx: var ECB[T], key: openarray[byte]) {.inline.} =
|
|
## Initialize ``ECB[T]`` with encryption key ``key``.
|
|
##
|
|
## This procedure will not perform any additional padding for encryption
|
|
## key ``key``.
|
|
##
|
|
## Length of ``key`` must be at least ``ECB[T].sizeKey()`` octets (bytes).
|
|
##
|
|
## You can see examples of usage ECB mode here ``examples/ecb.nim``.
|
|
assert(len(key) >= ctx.sizeKey())
|
|
init(ctx, unsafeAddr key[0])
|
|
|
|
proc init*[T](ctx: var ECB[T], key: openarray[char]) {.inline.} =
|
|
## Initialize ``ECB[T]`` with encryption key ``key``.
|
|
##
|
|
## This procedure will not perform any additional padding for encryption
|
|
## key ``key``.
|
|
##
|
|
## Length of ``key`` must be at least ``ECB[T].sizeKey()`` octets (bytes).
|
|
##
|
|
## You can see examples of usage ECB mode here ``examples/ecb.nim``.
|
|
assert(len(key) >= ctx.sizeKey())
|
|
init(ctx, cast[ptr byte](unsafeAddr key[0]))
|
|
|
|
proc clear*[T](ctx: var ECB[T]) {.inline.} =
|
|
## Clear ``ECB[T]`` context ``ctx``.
|
|
burnMem(ctx)
|
|
|
|
proc encrypt*[T](ctx: var ECB[T], inp: ptr byte, oup: ptr byte,
|
|
length: uint): uint {.discardable.} =
|
|
## Perform ``ECB[T]`` encryption of plain data pointed by ``inp`` of length
|
|
## ``length`` and store encrypted data to ``oup``. ``oup`` must be able to
|
|
## hold at least ``length`` octets (bytes) of data.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. ``length`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``length mod ctx.sizeBlock == 0``.
|
|
##
|
|
## Procedure returns number of processed octets (bytes).
|
|
mixin encrypt
|
|
assert(not isNil(inp) and not isNil(oup))
|
|
assert(length mod uint(ctx.sizeBlock) == 0)
|
|
|
|
var blen = uint(ctx.sizeBlock)
|
|
var ip = cast[ptr UncheckedArray[byte]](inp)
|
|
var op = cast[ptr UncheckedArray[byte]](oup)
|
|
var tp = cast[ptr UncheckedArray[byte]](addr ctx.tmp[0])
|
|
|
|
var i = length
|
|
while i != 0:
|
|
if i < blen:
|
|
copyMem(tp, ip, i)
|
|
ctx.cipher.encrypt(cast[ptr byte](tp), cast[ptr byte](op))
|
|
break
|
|
ctx.cipher.encrypt(cast[ptr byte](ip), cast[ptr byte](op))
|
|
i = i - blen
|
|
ip = cast[ptr UncheckedArray[byte]](cast[uint](ip) + blen)
|
|
op = cast[ptr UncheckedArray[byte]](cast[uint](op) + blen)
|
|
result = length
|
|
|
|
proc decrypt*[T](ctx: var ECB[T], inp: ptr byte, oup: ptr byte,
|
|
length: uint): uint {.discardable.} =
|
|
## Perform ``ECB[T]`` decryption of encrypted data pointed by ``inp`` of
|
|
## length ``length`` and store plain data to ``oup``. ``oup`` must be able to
|
|
## hold at least ``length`` octets (bytes) of data.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. ``length`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``length mod ctx.sizeBlock == 0``.
|
|
##
|
|
## Procedure returns number of processed octets (bytes).
|
|
mixin decrypt
|
|
assert(not isNil(inp) and not isNil(oup))
|
|
assert(length mod uint(ctx.sizeBlock) == 0)
|
|
|
|
var blen = uint(ctx.sizeBlock)
|
|
var ip = cast[ptr UncheckedArray[byte]](inp)
|
|
var op = cast[ptr UncheckedArray[byte]](oup)
|
|
var i = length
|
|
while i != 0:
|
|
ctx.cipher.decrypt(cast[ptr byte](ip), cast[ptr byte](op))
|
|
i = i - blen
|
|
ip = cast[ptr UncheckedArray[byte]](cast[uint](ip) + blen)
|
|
op = cast[ptr UncheckedArray[byte]](cast[uint](op) + blen)
|
|
result = length
|
|
|
|
proc encrypt*[T](ctx: var ECB[T], input: openarray[byte],
|
|
output: var openarray[byte]) {.inline.} =
|
|
## Encrypt array of data ``input`` and store encrypted data to array
|
|
## ``output`` using ``ECB[T]`` context ``ctx``.
|
|
##
|
|
## Note that length of ``input`` array must be less or equal to length of
|
|
## ``output`` array. Length of ``input`` array must not be zero.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. Length of ``input`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``len(input) mod ctx.sizeBlock == 0``.
|
|
assert(len(input) <= len(output))
|
|
assert(len(input) > 0)
|
|
encrypt(ctx, unsafeAddr input[0], addr output[0], uint(len(input)))
|
|
|
|
proc encrypt*[T](ctx: var ECB[T], input: openarray[char],
|
|
output: var openarray[char]) {.inline.} =
|
|
## Encrypt array of data ``input`` and store encrypted data to array
|
|
## ``output`` using ``ECB[T]`` context ``ctx``.
|
|
##
|
|
## Note that length of ``input`` array must be less or equal to length of
|
|
## ``output`` array. Length of ``input`` array must not be zero.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. Length of ``input`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``len(input) mod ctx.sizeBlock == 0``.
|
|
assert(len(input) <= len(output))
|
|
assert(len(input) > 0)
|
|
encrypt(ctx, cast[ptr byte](unsafeAddr input[0]),
|
|
cast[ptr byte](addr output[0]), uint(len(input)))
|
|
|
|
proc decrypt*[T](ctx: var ECB[T], input: openarray[byte],
|
|
output: var openarray[byte]) {.inline.} =
|
|
## Decrypt array of data ``input`` and store decrypted data to array
|
|
## ``output`` using ``ECB[T]`` context ``ctx``.
|
|
##
|
|
## Note that length of ``input`` array must be less or equal to length of
|
|
## ``output`` array. Length of ``input`` array must not be zero.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. Length of ``input`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``len(input) mod ctx.sizeBlock == 0``.
|
|
assert(len(input) <= len(output))
|
|
assert(len(input) > 0)
|
|
decrypt(ctx, unsafeAddr input[0], addr output[0], uint(len(input)))
|
|
|
|
proc decrypt*[T](ctx: var ECB[T], input: openarray[char],
|
|
output: var openarray[char]) {.inline.} =
|
|
## Decrypt array of data ``input`` and store decrypted data to array
|
|
## ``output`` using ``ECB[T]`` context ``ctx``.
|
|
##
|
|
## Note that length of ``input`` array must be less or equal to length of
|
|
## ``output`` array. Length of ``input`` array must not be zero.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. Length of ``input`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``len(input) mod ctx.sizeBlock == 0``.
|
|
assert(len(input) <= len(output))
|
|
assert(len(input) > 0)
|
|
decrypt(ctx, cast[ptr byte](unsafeAddr input[0]),
|
|
cast[ptr byte](addr output[0]), uint(len(input)))
|
|
|
|
## CBC (Cipher Block Chaining) Mode
|
|
|
|
template sizeBlock*[T](ctx: CBC[T]): int =
|
|
## Size of ``CBC[T]`` block in octets (bytes). This value is equal
|
|
## to cipher ``T`` block size.
|
|
mixin sizeBlock
|
|
sizeBlock(ctx.cipher)
|
|
|
|
template sizeKey*[T](ctx: CBC[T]): int =
|
|
## Size of ``CBC[T]`` key in octets (bytes). This value is equal
|
|
## to cipher ``T`` key size.
|
|
mixin sizeKey
|
|
sizeKey(ctx.cipher)
|
|
|
|
proc init*[T](ctx: var CBC[T], key: ptr byte, iv: ptr byte) =
|
|
## Initialize ``CBC[T]`` with encryption key ``key`` and initial vector (IV)
|
|
## ``iv``.
|
|
##
|
|
## Note! Size of encryption key pointed by ``key`` must be at least
|
|
## ``ctx.sizeKey`` octets (bytes) and size of initial vector ``iv`` must be at
|
|
## least ``ctx.sizeBlock`` octets (bytes).
|
|
##
|
|
## You can see examples of usage CBC mode here ``examples/cbc.nim``.
|
|
mixin init
|
|
assert(not isNil(key) and not isNil(iv))
|
|
init(ctx.cipher, key)
|
|
assert(ctx.sizeBlock <= MaxBlockSize)
|
|
copyMem(addr ctx.iv[0], iv, ctx.sizeBlock)
|
|
|
|
proc init*[T](ctx: var CBC[T], key: openarray[byte], iv: openarray[byte]) =
|
|
## Initialize ``CBC[T]`` with encryption key ``key`` and initial vector (IV)
|
|
## ``iv``.
|
|
##
|
|
## This procedure will not perform any additional padding for encryption
|
|
## key ``key`` and initial vector ``iv``.
|
|
##
|
|
## Length of ``key`` must be at least ``ctx.sizeKey()`` octets (bytes).
|
|
## Length of ``iv`` must be at least ``ctx.sizeBlock()`` octets (bytes)
|
|
##
|
|
## You can see examples of usage CBC mode here ``examples/cbc.nim``.
|
|
mixin init
|
|
assert(len(iv) >= ctx.sizeBlock)
|
|
assert(len(key) >= ctx.sizeKey)
|
|
assert(ctx.sizeBlock <= MaxBlockSize)
|
|
init(ctx.cipher, unsafeAddr key[0])
|
|
copyMem(addr ctx.iv[0], unsafeAddr iv[0], ctx.sizeBlock)
|
|
|
|
proc init*[T](ctx: var CBC[T], key: openarray[char], iv: openarray[char]) =
|
|
## Initialize ``CBC[T]`` with encryption key ``key`` and initial vector (IV)
|
|
## ``iv``.
|
|
##
|
|
## This procedure will not perform any additional padding for encryption
|
|
## key ``key`` and initial vector ``iv``.
|
|
##
|
|
## Length of ``key`` must be at least ``ctx.sizeKey()`` octets (bytes).
|
|
## Length of ``iv`` must be at least ``ctx.sizeBlock()`` octets (bytes)
|
|
##
|
|
## You can see examples of usage CBC mode here ``examples/cbc.nim``.
|
|
mixin init
|
|
assert(len(iv) >= ctx.sizeBlock)
|
|
assert(len(key) >= ctx.sizeKey)
|
|
assert(ctx.sizeBlock <= MaxBlockSize)
|
|
init(ctx.cipher, cast[ptr byte](unsafeAddr key[0]))
|
|
copyMem(addr ctx.iv[0], unsafeAddr iv[0], ctx.sizeBlock)
|
|
|
|
proc clear*[T](ctx: var CBC[T]) {.inline.} =
|
|
## Clear ``CBC[T]`` context ``ctx``.
|
|
burnMem(ctx)
|
|
|
|
proc encrypt*[T](ctx: var CBC[T], inp: ptr byte, oup: ptr byte,
|
|
length: uint): uint {.discardable.} =
|
|
## Perform ``CBC[T]`` encryption of plain data pointed by ``inp`` of length
|
|
## ``length`` and store encrypted data to ``oup``. ``oup`` must be able to
|
|
## hold at least ``length`` octets (bytes) of data.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. ``length`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``length mod ctx.sizeBlock == 0``.
|
|
##
|
|
## Procedure returns number of processed octets (bytes).
|
|
mixin encrypt
|
|
assert(not isNil(inp) and not isNil(oup))
|
|
assert(length != 0)
|
|
|
|
var blen = uint(ctx.sizeBlock)
|
|
var ip = cast[ptr UncheckedArray[byte]](inp)
|
|
var op = cast[ptr UncheckedArray[byte]](oup)
|
|
var cp = cast[ptr UncheckedArray[byte]](addr ctx.iv[0])
|
|
|
|
var i = length
|
|
while i != 0:
|
|
var n = 0'u
|
|
while (n < blen) and (n < length):
|
|
op[n] = ip[n] xor cp[n]
|
|
inc(n)
|
|
while n < blen:
|
|
op[n] = cp[n]
|
|
inc(n)
|
|
ctx.cipher.encrypt(cast[ptr byte](op), cast[ptr byte](op))
|
|
cp = op
|
|
if i < blen:
|
|
break
|
|
i = i - blen
|
|
ip = cast[ptr UncheckedArray[byte]](cast[uint](ip) + blen)
|
|
op = cast[ptr UncheckedArray[byte]](cast[uint](op) + blen)
|
|
copyMem(addr ctx.iv[0], cp, blen)
|
|
result = length
|
|
|
|
proc decrypt*[T](ctx: var CBC[T], inp: ptr byte, oup: ptr byte,
|
|
length: uint): uint {.discardable.} =
|
|
## Perform ``CBC[T]`` decryption of encrypted data pointed by ``inp`` of
|
|
## length ``length`` and store plain data to ``oup``. ``oup`` must be able to
|
|
## hold at least ``length`` octets (bytes) of data.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. ``length`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``length mod ctx.sizeBlock == 0``.
|
|
##
|
|
## Procedure returns number of processed octets (bytes).
|
|
mixin decrypt
|
|
assert(not isNil(inp) and not isNil(oup))
|
|
assert(length != 0)
|
|
|
|
let blen = uint(ctx.sizeBlock)
|
|
var ip = cast[ptr UncheckedArray[byte]](inp)
|
|
var op = cast[ptr UncheckedArray[byte]](oup)
|
|
var tp = cast[ptr UncheckedArray[byte]](addr ctx.tmp[0])
|
|
var cp = cast[ptr UncheckedArray[byte]](addr ctx.iv[0])
|
|
|
|
var i = length
|
|
while i != 0:
|
|
var n = 0'u
|
|
ctx.cipher.decrypt(cast[ptr byte](ip), cast[ptr byte](tp))
|
|
while (n < blen) and (n < length):
|
|
var c = ip[n]
|
|
op[n] = tp[n] xor cp[n]
|
|
cp[n] = c
|
|
inc(n)
|
|
if i < blen:
|
|
while n < blen:
|
|
cp[n] = ip[n]
|
|
break
|
|
i = i - blen
|
|
ip = cast[ptr UncheckedArray[byte]](cast[uint](ip) + blen)
|
|
op = cast[ptr UncheckedArray[byte]](cast[uint](op) + blen)
|
|
result = length
|
|
|
|
proc encrypt*[T](ctx: var CBC[T], input: openarray[byte],
|
|
output: var openarray[byte]) {.inline.} =
|
|
## Encrypt array of data ``input`` and store encrypted data to array
|
|
## ``output`` using ``CBC[T]`` context ``ctx``.
|
|
##
|
|
## Note that length of ``input`` array must be less or equal to length of
|
|
## ``output`` array. Length of ``input`` array must not be zero.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. Length of ``input`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``len(input) mod ctx.sizeBlock == 0``.
|
|
assert(len(input) <= len(output))
|
|
assert(len(input) > 0)
|
|
encrypt(ctx, unsafeAddr input[0], addr output[0], uint(len(input)))
|
|
|
|
proc encrypt*[T](ctx: var CBC[T], input: openarray[char],
|
|
output: var openarray[char]) {.inline.} =
|
|
## Encrypt array of data ``input`` and store encrypted data to array
|
|
## ``output`` using ``CBC[T]`` context ``ctx``.
|
|
##
|
|
## Note that length of ``input`` array must be less or equal to length of
|
|
## ``output`` array. Length of ``input`` array must not be zero.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. Length of ``input`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``len(input) mod ctx.sizeBlock == 0``.
|
|
assert(len(input) <= len(output))
|
|
assert(len(input) > 0)
|
|
encrypt(ctx, cast[ptr byte](unsafeAddr input[0]),
|
|
cast[ptr byte](addr output[0]), uint(len(input)))
|
|
|
|
proc decrypt*[T](ctx: var CBC[T], input: openarray[byte],
|
|
output: var openarray[byte]) {.inline.} =
|
|
## Decrypt array of data ``input`` and store decrypted data to array
|
|
## ``output`` using ``CBC[T]`` context ``ctx``.
|
|
##
|
|
## Note that length of ``input`` array must be less or equal to length of
|
|
## ``output`` array. Length of ``input`` array must not be zero.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. Length of ``input`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``len(input) mod ctx.sizeBlock == 0``.
|
|
assert(len(input) <= len(output))
|
|
assert(len(input) > 0)
|
|
decrypt(ctx, unsafeAddr input[0], addr output[0], uint(len(input)))
|
|
|
|
proc decrypt*[T](ctx: var CBC[T], input: openarray[char],
|
|
output: var openarray[char]) {.inline.} =
|
|
## Decrypt array of data ``input`` and store decrypted data to array
|
|
## ``output`` using ``CBC[T]`` context ``ctx``.
|
|
##
|
|
## Note that length of ``input`` array must be less or equal to length of
|
|
## ``output`` array. Length of ``input`` array must not be zero.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. Length of ``input`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``len(input) mod ctx.sizeBlock == 0``.
|
|
assert(len(input) <= len(output))
|
|
assert(len(input) > 0)
|
|
decrypt(ctx, cast[ptr byte](unsafeAddr input[0]),
|
|
cast[ptr byte](addr output[0]), uint(len(input)))
|
|
|
|
## CTR (Counter) Mode
|
|
|
|
template sizeBlock*[T](ctx: CTR[T]): int =
|
|
## Size of ``CTR[T]`` block in octets (bytes). This value is equal
|
|
## to cipher ``T`` block size.
|
|
mixin sizeBlock
|
|
sizeBlock(ctx.cipher)
|
|
|
|
template sizeKey*[T](ctx: CTR[T]): int =
|
|
## Size of ``CTR[T]`` key in octets (bytes). This value is equal
|
|
## to cipher ``T`` key size.
|
|
mixin sizeKey
|
|
sizeKey(ctx.cipher)
|
|
|
|
proc inc128(counter: ptr UncheckedArray[byte]) =
|
|
var n = 16'u32
|
|
var c = 1'u32
|
|
while true:
|
|
dec(n)
|
|
c = c + counter[n]
|
|
counter[n] = cast[byte](c)
|
|
c = c shr 8
|
|
if n == 0:
|
|
break
|
|
|
|
proc inc128(counter: var array[16, byte]) =
|
|
var n = 16'u32
|
|
var c = 1'u32
|
|
while true:
|
|
dec(n)
|
|
c = c + counter[n]
|
|
counter[n] = cast[byte](c)
|
|
c = c shr 8
|
|
if n == 0:
|
|
break
|
|
|
|
proc inc256(counter: ptr UncheckedArray[byte]) =
|
|
var n = 32'u32
|
|
var c = 1'u32
|
|
while true:
|
|
dec(n)
|
|
c = c + counter[n]
|
|
counter[n] = cast[byte](c)
|
|
c = c shr 8
|
|
if n == 0:
|
|
break
|
|
|
|
proc init*[T](ctx: var CTR[T], key: ptr byte, iv: ptr byte) =
|
|
## Initialize ``CTR[T]`` with encryption key ``key`` and initial vector (IV)
|
|
## ``iv``.
|
|
##
|
|
## Note! Size of encryption key pointed by ``key`` must be at least
|
|
## ``ctx.sizeKey`` octets (bytes) and size of initial vector ``iv`` must be at
|
|
## least ``ctx.sizeBlock`` octets (bytes).
|
|
##
|
|
## You can see examples of usage CTR mode here ``examples/ctr.nim``.
|
|
mixin init
|
|
assert(not isNil(key) and not isNil(iv))
|
|
assert(ctx.sizeBlock <= MaxBlockSize)
|
|
init(ctx.cipher, key)
|
|
copyMem(addr ctx.iv[0], iv, ctx.sizeBlock)
|
|
|
|
proc init*[T](ctx: var CTR[T], key: openarray[byte], iv: openarray[byte]) =
|
|
## Initialize ``CTR[T]`` with encryption key ``key`` and initial vector (IV)
|
|
## ``iv``.
|
|
##
|
|
## This procedure will not perform any additional padding for encryption
|
|
## key ``key`` and initial vector ``iv``.
|
|
##
|
|
## Length of ``key`` array must be at least ``ctx.sizeKey()`` octets (bytes).
|
|
## Length of ``iv`` array must be at least ``ctx.sizeBlock()`` octets (bytes).
|
|
##
|
|
## You can see examples of usage CTR mode here ``examples/ctr.nim``.
|
|
mixin init
|
|
assert(len(iv) >= ctx.sizeBlock)
|
|
assert(len(key) >= ctx.sizeKey)
|
|
assert(ctx.sizeBlock <= MaxBlockSize)
|
|
init(ctx.cipher, unsafeAddr key[0])
|
|
copyMem(addr ctx.iv[0], unsafeAddr iv[0], ctx.sizeBlock)
|
|
|
|
proc init*[T](ctx: var CTR[T], key: openarray[char],
|
|
iv: openarray[char]) {.inline.} =
|
|
## Initialize ``CTR[T]`` with encryption key ``key`` and initial vector (IV)
|
|
## ``iv``.
|
|
##
|
|
## This procedure will not perform any additional padding for encryption
|
|
## key ``key`` and initial vector ``iv``.
|
|
##
|
|
## Length of ``key`` array must be at least ``ctx.sizeKey()`` octets (bytes).
|
|
## Length of ``iv`` array must be at least ``ctx.sizeBlock()`` octets (bytes).
|
|
##
|
|
## You can see examples of usage CTR mode here ``examples/ctr.nim``.
|
|
mixin init
|
|
assert(len(iv) >= ctx.sizeBlock)
|
|
assert(len(key) >= ctx.sizeKey)
|
|
assert(ctx.sizeBlock <= MaxBlockSize)
|
|
init(ctx.cipher, cast[ptr byte](unsafeAddr key[0]))
|
|
copyMem(addr ctx.iv[0], unsafeAddr iv[0], ctx.sizeBlock)
|
|
|
|
proc clear*[T](ctx: var CTR[T]) {.inline.} =
|
|
## Clear ``CTR[T]`` context ``ctx``.
|
|
burnMem(ctx)
|
|
|
|
proc encrypt*[T](ctx: var CTR[T], inp: ptr byte, oup: ptr byte,
|
|
length: uint): uint {.discardable.} =
|
|
## Perform ``CTR[T]`` encryption of plain data pointed by ``inp`` of length
|
|
## ``length`` and store encrypted data to ``oup``. ``oup`` must be able to
|
|
## hold at least ``length`` octets (bytes) of data.
|
|
##
|
|
## Procedure returns number of processed octets (bytes).
|
|
mixin encrypt
|
|
assert(not isNil(inp) and not isNil(oup))
|
|
assert(length != 0)
|
|
assert(ctx.sizeBlock == (128 div 8) or ctx.sizeBlock == (256 div 8))
|
|
var n = ctx.num
|
|
var i = 0'u
|
|
var ip = cast[ptr UncheckedArray[byte]](inp)
|
|
var op = cast[ptr UncheckedArray[byte]](oup)
|
|
var cp = cast[ptr UncheckedArray[byte]](addr ctx.iv[0])
|
|
let mask = uint(ctx.sizeBlock)
|
|
|
|
while i < length:
|
|
if n == 0:
|
|
ctx.cipher.encrypt(addr ctx.iv[0], addr ctx.ecount[0])
|
|
if ctx.sizeBlock == (128 div 8):
|
|
inc128(cp)
|
|
elif ctx.sizeBlock == (256 div 8):
|
|
inc256(cp)
|
|
op[i] = cast[byte](ip[i] xor ctx.ecount[n])
|
|
inc(i)
|
|
n = (n + 1) mod mask
|
|
|
|
ctx.num = uint(n)
|
|
result = ctx.num
|
|
|
|
proc decrypt*[T](ctx: var CTR[T], inp: ptr byte, oup: ptr byte,
|
|
length: uint): uint {.discardable, inline.} =
|
|
## Perform ``CTR[T]`` decryption of encrypted data pointed by ``inp`` of
|
|
## length ``length`` and store decrypted data to ``oup``. ``oup`` must be able
|
|
## to hold at least ``length`` octets (bytes) of data.
|
|
##
|
|
## Procedures returns number of processed octets (bytes).
|
|
mixin encrypt
|
|
result = encrypt(ctx, inp, oup, length)
|
|
|
|
proc encrypt*[T](ctx: var CTR[T], input: openarray[byte],
|
|
output: var openarray[byte]) {.inline.} =
|
|
## Encrypt array of data ``input`` and store encrypted data to array
|
|
## ``output`` using ``CTR[T]`` context ``ctx``.
|
|
##
|
|
## Note that length of ``input`` array must be less or equal to length of
|
|
## ``output`` array. Length of ``input`` array must not be zero.
|
|
assert(len(input) <= len(output))
|
|
assert(len(input) > 0)
|
|
encrypt(ctx, unsafeAddr input[0], addr output[0], uint(len(input)))
|
|
|
|
proc encrypt*[T](ctx: var CTR[T], input: openarray[char],
|
|
output: var openarray[char]) {.inline.} =
|
|
## Encrypt array of data ``input`` and store encrypted data to array
|
|
## ``output`` using ``CTR[T]`` context ``ctx``.
|
|
##
|
|
## Note that length of ``input`` array must be less or equal to length of
|
|
## ``output`` array. Length of ``input`` array must not be zero.
|
|
assert(len(input) <= len(output))
|
|
assert(len(input) > 0)
|
|
encrypt(ctx, cast[ptr byte](unsafeAddr input[0]),
|
|
cast[ptr byte](addr output[0]), uint(len(input)))
|
|
|
|
proc decrypt*[T](ctx: var CTR[T], input: openarray[byte],
|
|
output: var openarray[byte]) {.inline.} =
|
|
## Decrypt array of data ``input`` and store decrypted data to array
|
|
## ``output`` using ``CTR[T]`` context ``ctx``.
|
|
##
|
|
## Note that length of ``input`` array must be less or equal to length of
|
|
## ``output`` array. Length of ``input`` array must not be zero.
|
|
assert(len(input) <= len(output))
|
|
assert(len(input) > 0)
|
|
decrypt(ctx, unsafeAddr input[0], addr output[0], uint(len(input)))
|
|
|
|
proc decrypt*[T](ctx: var CTR[T], input: openarray[char],
|
|
output: var openarray[char]) {.inline.} =
|
|
## Decrypt array of data ``input`` and store decrypted data to array
|
|
## ``output`` using ``CTR[T]`` context ``ctx``.
|
|
##
|
|
## Note that length of ``input`` array must be less or equal to length of
|
|
## ``output`` array. Length of ``input`` array must not be zero.
|
|
assert(len(input) <= len(output))
|
|
assert(len(input) > 0)
|
|
decrypt(ctx, cast[ptr byte](unsafeAddr input[0]),
|
|
cast[ptr byte](addr output[0]), uint(len(input)))
|
|
|
|
## OFB (Output Feedback) Mode
|
|
|
|
template sizeBlock*[T](ctx: OFB[T]): int =
|
|
## Size of ``OFB[T]`` block in octets (bytes). This value is equal
|
|
## to cipher ``T`` block size.
|
|
mixin sizeBlock
|
|
sizeBlock(ctx.cipher)
|
|
|
|
template sizeKey*[T](ctx: OFB[T]): int =
|
|
## Size of ``OFB[T]`` key in octets (bytes). This value is equal
|
|
## to cipher ``T`` key size.
|
|
mixin sizeKey
|
|
sizeKey(ctx.cipher)
|
|
|
|
proc init*[T](ctx: var OFB[T], key: ptr byte, iv: ptr byte) =
|
|
## Initialize ``OFB[T]`` with encryption key ``key`` and initial vector (IV)
|
|
## ``iv``.
|
|
##
|
|
## Note! Size of encryption key pointed by ``key`` must be at least
|
|
## ``ctx.sizeKey`` octets (bytes) and size of initial vector ``iv`` must be at
|
|
## least ``ctx.sizeBlock`` octets (bytes).
|
|
##
|
|
## You can see examples of usage OFB mode here ``examples/ofb.nim``.
|
|
mixin init
|
|
assert(not isNil(key) and not isNil(iv))
|
|
assert(ctx.sizeBlock <= MaxBlockSize)
|
|
init(ctx.cipher, key)
|
|
copyMem(addr ctx.iv[0], iv, ctx.sizeBlock)
|
|
|
|
proc init*[T](ctx: var OFB[T], key: openarray[byte], iv: openarray[byte]) =
|
|
## Initialize ``OFB[T]`` with encryption key ``key`` and initial vector (IV)
|
|
## ``iv``.
|
|
##
|
|
## This procedure will not perform any additional padding for encryption
|
|
## key ``key`` and initial vector ``iv``.
|
|
##
|
|
## Length of ``key`` array must be at least ``ctx.sizeKey()`` octets (bytes).
|
|
## Length of ``iv`` array must be at least ``ctx.sizeBlock()`` octets (bytes).
|
|
##
|
|
## You can see examples of usage OFB mode here ``examples/ofb.nim``.
|
|
mixin init
|
|
assert(len(iv) >= ctx.sizeBlock)
|
|
assert(len(key) >= ctx.sizeKey)
|
|
assert(ctx.sizeBlock <= MaxBlockSize)
|
|
init(ctx.cipher, unsafeAddr key[0])
|
|
copyMem(addr ctx.iv[0], unsafeAddr iv[0], ctx.sizeBlock)
|
|
|
|
proc init*[T](ctx: var OFB[T], key: openarray[char], iv: openarray[char]) =
|
|
## Initialize ``OFB[T]`` with encryption key ``key`` and initial vector (IV)
|
|
## ``iv``.
|
|
##
|
|
## This procedure will not perform any additional padding for encryption
|
|
## key ``key`` and initial vector ``iv``.
|
|
##
|
|
## Length of ``key`` array must be at least ``ctx.sizeKey()`` octets (bytes).
|
|
## Length of ``iv`` array must be at least ``ctx.sizeBlock()`` octets (bytes).
|
|
##
|
|
## You can see examples of usage OFB mode here ``examples/ofb.nim``.
|
|
mixin init
|
|
assert(len(iv) >= ctx.sizeBlock)
|
|
assert(len(key) >= ctx.sizeKey)
|
|
assert(ctx.sizeBlock <= MaxBlockSize)
|
|
init(ctx.cipher, cast[ptr byte](unsafeAddr key[0]))
|
|
copyMem(addr ctx.iv[0], unsafeAddr iv[0], ctx.sizeBlock)
|
|
|
|
proc clear*[T](ctx: var OFB[T]) {.inline.} =
|
|
## Clear ``OFB[T]`` context ``ctx``.
|
|
burnMem(ctx)
|
|
|
|
proc encrypt*[T](ctx: var OFB[T], inp: ptr byte, oup: ptr byte,
|
|
length: uint): uint {.discardable.} =
|
|
## Perform ``OFB[T]`` encryption of plain data pointed by ``inp`` of length
|
|
## ``length`` and store encrypted data to ``oup``. ``oup`` must be able to
|
|
## hold at least ``length`` octets (bytes) of data.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. ``length`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``length mod ctx.sizeBlock == 0``.
|
|
##
|
|
## Procedure returns number of processed octets (bytes).
|
|
mixin encrypt
|
|
assert(not isNil(inp) and not isNil(oup))
|
|
assert(length != 0)
|
|
assert(ctx.sizeBlock == (128 div 8) or ctx.sizeBlock == (256 div 8))
|
|
var n = 0
|
|
var i = 0'u
|
|
var ip = cast[ptr UncheckedArray[byte]](inp)
|
|
var op = cast[ptr UncheckedArray[byte]](oup)
|
|
var cp = cast[ptr UncheckedArray[byte]](addr ctx.iv[0])
|
|
let mask = ctx.sizeBlock
|
|
|
|
while i < length:
|
|
if n == 0:
|
|
ctx.cipher.encrypt(cast[ptr byte](cp), cast[ptr byte](cp))
|
|
op[i] = ip[i] xor cp[n]
|
|
inc(i)
|
|
n = (n + 1) mod mask
|
|
result = uint(n)
|
|
|
|
proc decrypt*[T](ctx: var OFB[T], inp: ptr byte, oup: ptr byte,
|
|
length: uint): uint {.discardable, inline.} =
|
|
## Perform ``OFB[T]`` decryption of encrypted data pointed by ``inp`` of
|
|
## length ``length`` and store plain data to ``oup``. ``oup`` must be able to
|
|
## hold at least ``length`` octets (bytes) of data.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. ``length`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``length mod ctx.sizeBlock == 0``.
|
|
##
|
|
## Procedure returns number of processed octets (bytes).
|
|
mixin encrypt
|
|
result = encrypt(ctx, inp, oup, length)
|
|
|
|
proc encrypt*[T](ctx: var OFB[T], input: openarray[byte],
|
|
output: var openarray[byte]) {.inline.} =
|
|
## Encrypt array of data ``input`` and store encrypted data to array
|
|
## ``output`` using ``OFB[T]`` context ``ctx``.
|
|
##
|
|
## Note that length of ``input`` array must be less or equal to length of
|
|
## ``output`` array. Length of ``input`` array must not be zero.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. Length of ``input`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``len(input) mod ctx.sizeBlock == 0``.
|
|
assert(len(input) <= len(output))
|
|
assert(len(input) > 0)
|
|
encrypt(ctx, unsafeAddr input[0], addr output[0], uint(len(input)))
|
|
|
|
proc encrypt*[T](ctx: var OFB[T], input: openarray[char],
|
|
output: var openarray[char]) {.inline.} =
|
|
## Encrypt array of data ``input`` and store encrypted data to array
|
|
## ``output`` using ``OFB[T]`` context ``ctx``.
|
|
##
|
|
## Note that length of ``input`` array must be less or equal to length of
|
|
## ``output`` array. Length of ``input`` array must not be zero.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. Length of ``input`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``len(input) mod ctx.sizeBlock == 0``.
|
|
assert(len(input) <= len(output))
|
|
assert(len(input) > 0)
|
|
encrypt(ctx, cast[ptr byte](unsafeAddr input[0]),
|
|
cast[ptr byte](addr output[0]), uint(len(input)))
|
|
|
|
proc decrypt*[T](ctx: var OFB[T], input: openarray[byte],
|
|
output: var openarray[byte]) {.inline.} =
|
|
## Decrypt array of data ``input`` and store decrypted data to array
|
|
## ``output`` using ``OFB[T]`` context ``ctx``.
|
|
##
|
|
## Note that length of ``input`` array must be less or equal to length of
|
|
## ``output`` array. Length of ``input`` array must not be zero.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. Length of ``input`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``len(input) mod ctx.sizeBlock == 0``.
|
|
assert(len(input) <= len(output))
|
|
assert(len(input) > 0)
|
|
decrypt(ctx, unsafeAddr input[0], addr output[0], uint(len(input)))
|
|
|
|
proc decrypt*[T](ctx: var OFB[T], input: openarray[char],
|
|
output: var openarray[char]) {.inline.} =
|
|
## Decrypt array of data ``input`` and store decrypted data to array
|
|
## ``output`` using ``OFB[T]`` context ``ctx``.
|
|
##
|
|
## Note that length of ``input`` array must be less or equal to length of
|
|
## ``output`` array. Length of ``input`` array must not be zero.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. Length of ``input`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``len(input) mod ctx.sizeBlock == 0``.
|
|
assert(len(input) <= len(output))
|
|
assert(len(input) > 0)
|
|
decrypt(ctx, cast[ptr byte](unsafeAddr input[0]),
|
|
cast[ptr byte](addr output[0]), uint(len(input)))
|
|
|
|
## CFB (Cipher Feedback) Mode
|
|
|
|
template sizeBlock*[T](ctx: CFB[T]): int =
|
|
## Size of ``CFB[T]`` block in octets (bytes). This value is equal
|
|
## to cipher ``T`` block size.
|
|
mixin sizeBlock
|
|
sizeBlock(ctx.cipher)
|
|
|
|
template sizeKey*[T](ctx: CFB[T]): int =
|
|
## Size of ``CFB[T]`` key in octets (bytes). This value is equal
|
|
## to cipher ``T`` key size.
|
|
mixin sizeKey
|
|
sizeKey(ctx.cipher)
|
|
|
|
proc init*[T](ctx: var CFB[T], key: ptr byte, iv: ptr byte) =
|
|
## Initialize ``CFB[T]`` with encryption key ``key`` and initial vector (IV)
|
|
## ``iv``.
|
|
##
|
|
## Note! Size of encryption key pointed by ``key`` must be at least
|
|
## ``ctx.sizeKey`` octets (bytes) and size of initial vector ``iv`` must be at
|
|
## least ``ctx.sizeBlock`` octets (bytes).
|
|
##
|
|
## You can see examples of usage CFB mode here ``examples/cfb.nim``.
|
|
mixin init
|
|
assert(not isNil(key) and not isNil(iv))
|
|
assert(ctx.sizeBlock <= MaxBlockSize)
|
|
init(ctx.cipher, key)
|
|
copyMem(addr ctx.iv[0], iv, ctx.sizeBlock)
|
|
|
|
proc init*[T](ctx: var CFB[T], key: openarray[byte], iv: openarray[byte]) =
|
|
## Initialize ``CFB[T]`` with encryption key ``key`` and initial vector (IV)
|
|
## ``iv``.
|
|
##
|
|
## This procedure will not perform any additional padding for encryption
|
|
## key ``key`` and initial vector ``iv``.
|
|
##
|
|
## Length of ``key`` array must be at least ``ctx.sizeKey()`` octets (bytes).
|
|
## Length of ``iv`` array must be at least ``ctx.sizeBlock()`` octets (bytes).
|
|
##
|
|
## You can see examples of usage CFB mode here ``examples/cfb.nim``.
|
|
mixin init
|
|
assert(len(iv) >= ctx.sizeBlock)
|
|
assert(len(key) >= ctx.sizeKey)
|
|
assert(ctx.sizeBlock <= MaxBlockSize)
|
|
init(ctx.cipher, unsafeAddr key[0])
|
|
copyMem(addr ctx.iv[0], unsafeAddr iv[0], ctx.sizeBlock)
|
|
|
|
proc init*[T](ctx: var CFB[T], key: openarray[char], iv: openarray[char]) =
|
|
## Initialize ``CFB[T]`` with encryption key ``key`` and initial vector (IV)
|
|
## ``iv``.
|
|
##
|
|
## This procedure will not perform any additional padding for encryption
|
|
## key ``key`` and initial vector ``iv``.
|
|
##
|
|
## Length of ``key`` array must be at least ``ctx.sizeKey()`` octets (bytes).
|
|
## Length of ``iv`` array must be at least ``ctx.sizeBlock()`` octets (bytes).
|
|
##
|
|
## You can see examples of usage CFB mode here ``examples/cfb.nim``.
|
|
mixin init
|
|
assert(len(iv) >= ctx.sizeBlock)
|
|
assert(len(key) >= ctx.sizeKey)
|
|
assert(ctx.sizeBlock <= MaxBlockSize)
|
|
init(ctx.cipher, cast[ptr byte](unsafeAddr key[0]))
|
|
copyMem(addr ctx.iv[0], unsafeAddr iv[0], ctx.sizeBlock)
|
|
|
|
proc clear*[T](ctx: var CFB[T]) {.inline.} =
|
|
## Clear ``CFB[T]`` context ``ctx``.
|
|
burnMem(ctx)
|
|
|
|
proc encrypt*[T](ctx: var CFB[T], inp: ptr byte, oup: ptr byte,
|
|
length: uint): uint {.discardable.} =
|
|
## Perform ``CFB[T]`` encryption of plain data pointed by ``inp`` of length
|
|
## ``length`` and store encrypted data to ``oup``. ``oup`` must be able to
|
|
## hold at least ``length`` octets (bytes) of data.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. ``length`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``length mod ctx.sizeBlock == 0``.
|
|
##
|
|
## Procedure returns number of processed octets (bytes).
|
|
mixin encrypt
|
|
assert(not isNil(inp) and not isNil(oup))
|
|
assert(length != 0)
|
|
var n = 0
|
|
var i = 0'u
|
|
var ip = cast[ptr UncheckedArray[byte]](inp)
|
|
var op = cast[ptr UncheckedArray[byte]](oup)
|
|
var cp = cast[ptr UncheckedArray[byte]](addr ctx.iv[0])
|
|
let mask = ctx.sizeBlock
|
|
|
|
while i < length:
|
|
if n == 0:
|
|
ctx.cipher.encrypt(cast[ptr byte](cp), cast[ptr byte](cp))
|
|
cp[n] = cp[n] xor ip[i]
|
|
op[i] = cp[n]
|
|
inc(i)
|
|
n = (n + 1) mod mask
|
|
result = uint(n)
|
|
|
|
proc decrypt*[T](ctx: var CFB[T], inp: ptr byte, oup: ptr byte,
|
|
length: uint): uint {.discardable.} =
|
|
## Perform ``CFB[T]`` decryption of encrypted data pointed by ``inp`` of
|
|
## length ``length`` and store plain data to ``oup``. ``oup`` must be able to
|
|
## hold at least ``length`` octets (bytes) of data.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. ``length`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``length mod ctx.sizeBlock == 0``.
|
|
##
|
|
## Procedure returns number of processed octets (bytes).
|
|
mixin encrypt
|
|
assert(not isNil(inp) and not isNil(oup))
|
|
assert(length != 0)
|
|
var n = 0
|
|
var i = 0'u
|
|
var ip = cast[ptr UncheckedArray[byte]](inp)
|
|
var op = cast[ptr UncheckedArray[byte]](oup)
|
|
var cp = cast[ptr UncheckedArray[byte]](addr ctx.iv[0])
|
|
let mask = ctx.sizeBlock
|
|
|
|
while i < length:
|
|
if n == 0:
|
|
ctx.cipher.encrypt(cast[ptr byte](cp), cast[ptr byte](cp))
|
|
let c = ip[i]
|
|
op[i] = cp[n] xor c
|
|
cp[n] = c
|
|
inc(i)
|
|
n = (n + 1) mod mask
|
|
result = uint(n)
|
|
|
|
proc encrypt*[T](ctx: var CFB[T], input: openarray[byte],
|
|
output: var openarray[byte]) {.inline.} =
|
|
## Encrypt array of data ``input`` and store encrypted data to array
|
|
## ``output`` using ``CFB[T]`` context ``ctx``.
|
|
##
|
|
## Note that length of ``input`` array must be less or equal to length of
|
|
## ``output`` array. Length of ``input`` array must not be zero.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. Length of ``input`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``len(input) mod ctx.sizeBlock == 0``.
|
|
assert(len(input) <= len(output))
|
|
assert(len(input) > 0)
|
|
encrypt(ctx, unsafeAddr input[0], addr output[0], uint(len(input)))
|
|
|
|
proc encrypt*[T](ctx: var CFB[T], input: openarray[char],
|
|
output: var openarray[char]) {.inline.} =
|
|
## Encrypt array of data ``input`` and store encrypted data to array
|
|
## ``output`` using ``CFB[T]`` context ``ctx``.
|
|
##
|
|
## Note that length of ``input`` array must be less or equal to length of
|
|
## ``output`` array. Length of ``input`` array must not be zero.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. Length of ``input`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``len(input) mod ctx.sizeBlock == 0``.
|
|
assert(len(input) <= len(output))
|
|
assert(len(input) > 0)
|
|
encrypt(ctx, cast[ptr byte](unsafeAddr input[0]),
|
|
cast[ptr byte](addr output[0]), uint(len(input)))
|
|
|
|
proc decrypt*[T](ctx: var CFB[T], input: openarray[byte],
|
|
output: var openarray[byte]) =
|
|
## Decrypt array of data ``input`` and store decrypted data to array
|
|
## ``output`` using ``CFB[T]`` context ``ctx``.
|
|
##
|
|
## Note that length of ``input`` array must be less or equal to length of
|
|
## ``output`` array. Length of ``input`` array must not be zero.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. Length of ``input`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``len(input) mod ctx.sizeBlock == 0``.
|
|
assert(len(input) <= len(output))
|
|
assert(len(input) > 0)
|
|
decrypt(ctx, unsafeAddr input[0], addr output[0], uint(len(input)))
|
|
|
|
proc decrypt*[T](ctx: var CFB[T], input: openarray[char],
|
|
output: var openarray[char]) =
|
|
## Decrypt array of data ``input`` and store decrypted data to array
|
|
## ``output`` using ``CFB[T]`` context ``ctx``.
|
|
##
|
|
## Note that length of ``input`` array must be less or equal to length of
|
|
## ``output`` array. Length of ``input`` array must not be zero.
|
|
##
|
|
## Note, that this procedure do not perform any additional padding, so you
|
|
## need to do it on your own. Length of ``input`` must be aligned to the
|
|
## ``ctx.sizeBlock`` value, e.g. ``len(input) mod ctx.sizeBlock == 0``.
|
|
assert(len(input) <= len(output))
|
|
assert(len(input) > 0)
|
|
decrypt(ctx, cast[ptr byte](unsafeAddr input[0]),
|
|
cast[ptr byte](addr output[0]), uint(len(input)))
|
|
|
|
## GCM (Galois Counter Mode)
|
|
|
|
# GHASH implementation is Nim version of `ghash_ctmul64.c` which is part
|
|
# of decent BearSSL project <https://bearssl.org>.
|
|
# Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
|
|
|
|
proc bmul64(x, y: uint64): uint64 =
|
|
var x0, x1, x2, x3, y0, y1, y2, y3, z0, z1, z2, z3: uint64
|
|
x0 = x and 0x1111111111111111'u64
|
|
x1 = x and 0x2222222222222222'u64
|
|
x2 = x and 0x4444444444444444'u64
|
|
x3 = x and 0x8888888888888888'u64
|
|
y0 = y and 0x1111111111111111'u64
|
|
y1 = y and 0x2222222222222222'u64
|
|
y2 = y and 0x4444444444444444'u64
|
|
y3 = y and 0x8888888888888888'u64
|
|
z0 = (x0 * y0) xor (x1 * y3) xor (x2 * y2) xor (x3 * y1)
|
|
z1 = (x0 * y1) xor (x1 * y0) xor (x2 * y3) xor (x3 * y2)
|
|
z2 = (x0 * y2) xor (x1 * y1) xor (x2 * y0) xor (x3 * y3)
|
|
z3 = (x0 * y3) xor (x1 * y2) xor (x2 * y1) xor (x3 * y0)
|
|
z0 = z0 and 0x1111111111111111'u64
|
|
z1 = z1 and 0x2222222222222222'u64
|
|
z2 = z2 and 0x4444444444444444'u64
|
|
z3 = z3 and 0x8888888888888888'u64
|
|
result = z0 or z1 or z2 or z3
|
|
|
|
template RMS(x, m, s) =
|
|
x = ((x and uint64(m)) shl (s)) or ((x shr (s)) and uint64(m))
|
|
|
|
proc rev64(x: uint64): uint64 =
|
|
var xx = x
|
|
RMS(xx, 0x5555555555555555'u64, 1)
|
|
RMS(xx, 0x3333333333333333'u64, 2)
|
|
RMS(xx, 0x0F0F0F0F0F0F0F0F'u64, 4)
|
|
RMS(xx, 0x00FF00FF00FF00FF'u64, 8)
|
|
RMS(xx, 0x0000FFFF0000FFFF'u64, 16)
|
|
result = (xx shl 32) or (xx shr 32)
|
|
|
|
proc ghash(y: var openarray[byte], h: openarray[byte],
|
|
data: ptr byte, size: int) =
|
|
var
|
|
y0, y1, h0, h1, h2, h0r, h1r, h2r: uint64
|
|
buf: ptr byte
|
|
|
|
y1 = EGETU64(addr y[0], 0)
|
|
y0 = EGETU64(addr y[0], 8)
|
|
h1 = EGETU64(unsafeAddr h[0], 0)
|
|
h0 = EGETU64(unsafeAddr h[0], 8)
|
|
h0r = rev64(h0)
|
|
h1r = rev64(h1)
|
|
h2 = h0 xor h1
|
|
h2r = h0r xor h1r
|
|
|
|
var length = size
|
|
buf = data
|
|
while length > 0:
|
|
var tmp: array[16, byte]
|
|
var src: ptr byte
|
|
var y0r, y1r, y2, y2r: uint64
|
|
var z0, z1, z2, z0h, z1h, z2h, v0, v1, v2, v3: uint64
|
|
|
|
if length >= 16:
|
|
src = buf
|
|
buf = cast[ptr byte](cast[uint](buf) + 16)
|
|
length -= 16
|
|
else:
|
|
zeroMem(addr tmp[0], 16)
|
|
copyMem(addr tmp[0], buf, length)
|
|
src = addr tmp[0]
|
|
length = 0
|
|
|
|
y1 = y1 xor GETU64(src, 0)
|
|
y0 = y0 xor GETU64(src, 8)
|
|
|
|
y0r = rev64(y0)
|
|
y1r = rev64(y1)
|
|
y2 = y0 xor y1;
|
|
y2r = y0r xor y1r;
|
|
|
|
z0 = bmul64(y0, h0)
|
|
z1 = bmul64(y1, h1)
|
|
z2 = bmul64(y2, h2)
|
|
z0h = bmul64(y0r, h0r)
|
|
z1h = bmul64(y1r, h1r)
|
|
z2h = bmul64(y2r, h2r)
|
|
z2 = z2 xor (z0 xor z1)
|
|
z2h = z2h xor (z0h xor z1h)
|
|
z0h = rev64(z0h) shr 1
|
|
z1h = rev64(z1h) shr 1
|
|
z2h = rev64(z2h) shr 1
|
|
|
|
v0 = z0
|
|
v1 = z0h xor z2
|
|
v2 = z1 xor z2h
|
|
v3 = z1h
|
|
|
|
v3 = (v3 shl 1) or (v2 shr 63)
|
|
v2 = (v2 shl 1) or (v1 shr 63)
|
|
v1 = (v1 shl 1) or (v0 shr 63)
|
|
v0 = (v0 shl 1)
|
|
|
|
v2 = v2 xor (v0 xor (v0 shr 1) xor (v0 shr 2) xor (v0 shr 7))
|
|
v1 = v1 xor ((v0 shl 63) xor (v0 shl 62) xor (v0 shl 57))
|
|
v3 = v3 xor (v1 xor (v1 shr 1) xor (v1 shr 2) xor (v1 shr 7))
|
|
v2 = v2 xor ((v1 shl 63) xor (v1 shl 62) xor (v1 shl 57))
|
|
|
|
y0 = v2
|
|
y1 = v3
|
|
|
|
EPUTU64(addr y, 0, y1)
|
|
EPUTU64(addr y, 8, y0)
|
|
|
|
template sizeBlock*[T](ctx: GCM[T]): int =
|
|
## Size of ``GCM[T]`` block in octets (bytes). This value is equal
|
|
## to cipher ``T`` block size.
|
|
mixin sizeBlock
|
|
sizeBlock(ctx.cipher)
|
|
|
|
template sizeKey*[T](ctx: GCM[T]): int =
|
|
## Size of ``GCM[T]`` key in octets (bytes). This value is equal
|
|
## to cipher ``T`` key size.
|
|
mixin sizeKey
|
|
sizeKey(ctx.cipher)
|
|
|
|
proc init*[T](ctx: var GCM[T], key: openarray[byte], iv: openarray[byte],
|
|
aad: openarray[byte]) =
|
|
## Initialize ``GCM[T]`` with encryption key ``key``, initial vector (IV)
|
|
## ``iv`` and additional authentication data (AAD) ``aad``.
|
|
##
|
|
## Size of ``key`` must be at least ``ctx.sizeKey()`` octets (bytes).
|
|
## Size of cipher ``T`` block must be 128 bits (16 bytes).
|
|
##
|
|
## You can see examples of usage GCM mode here ``examples/gcm.nim``.
|
|
mixin init
|
|
# GCM supports only 128bit block ciphers
|
|
assert(ctx.sizeBlock == 16)
|
|
assert(len(key) == ctx.sizeKey)
|
|
burnMem(ctx)
|
|
ctx.cipher.init(key)
|
|
ctx.cipher.encrypt(ctx.h, ctx.h)
|
|
if len(iv) == 12:
|
|
copyMem(addr ctx.y[0], unsafeAddr iv[0], 12)
|
|
inc128(ctx.y)
|
|
else:
|
|
var tmp: array[16, byte]
|
|
ghash(ctx.y, ctx.h, unsafeAddr iv[0], len(iv))
|
|
EPUTU32(addr tmp[0], 12, len(iv) shl 3)
|
|
ghash(ctx.y, ctx.h, addr tmp[0], 16)
|
|
ctx.cipher.encrypt(ctx.y, ctx.basectr)
|
|
let slen = len(aad)
|
|
ctx.aadlen = uint64(slen)
|
|
ctx.datalen = 0
|
|
if len(aad) > 0:
|
|
ghash(ctx.buf, ctx.h, unsafeAddr aad[0], slen)
|
|
|
|
proc encrypt*[T](ctx: var GCM[T], input: openarray[byte],
|
|
output: var openarray[byte]) =
|
|
## Encrypt array of data ``input`` and store encrypted data to array
|
|
## ``output`` using ``GCM[T]`` context ``ctx``.
|
|
##
|
|
## 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))
|
|
assert(len(input) > 0)
|
|
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)
|
|
for i in 0..<uselen:
|
|
output[offset + i] = ectr[i] xor input[offset + i]
|
|
ghash(ctx.buf, ctx.h, addr output[offset], uselen)
|
|
length -= uselen
|
|
offset += uselen
|
|
|
|
proc decrypt*[T](ctx: var GCM[T], input: openarray[byte],
|
|
output: var openarray[byte]) =
|
|
## Decrypt array of data ``input`` and store decrypted data to array
|
|
## ``output`` using ``GCM[T]`` context ``ctx``.
|
|
##
|
|
## 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))
|
|
assert(len(input) > 0)
|
|
|
|
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)
|
|
for i in 0..<uselen:
|
|
output[offset + i] = ectr[i] xor input[offset + i]
|
|
ghash(ctx.buf, ctx.h, unsafeAddr input[offset], uselen)
|
|
length -= uselen
|
|
offset += uselen
|
|
|
|
proc getTag*[T](ctx: var GCM[T], tag: var openarray[byte]) =
|
|
## Obtain authentication tag from ``GCM[T]`` context ``ctx`` and store it to
|
|
## ``tag``.
|
|
##
|
|
## Note that maximum size of ``tag`` is 128 bits (16 bytes).
|
|
let taglen = len(tag)
|
|
let uselen = if taglen < 16: taglen else: 16
|
|
var workbuf: array[16, byte]
|
|
if taglen > 0:
|
|
copyMem(addr tag[0], addr ctx.basectr[0], uselen)
|
|
EPUTU64(addr workbuf[0], 0, ctx.aadlen shl 3)
|
|
EPUTU64(addr workbuf[0], 8, ctx.datalen shl 3)
|
|
ghash(ctx.buf, ctx.h, addr workbuf[0], 16)
|
|
for i in 0..<uselen:
|
|
tag[i] = tag[i] xor ctx.buf[i]
|
|
|
|
proc getTag*[T](ctx: var GCM[T]): array[16, byte] {.noinit.} =
|
|
## Obtain authentication tag from ``GCM[T]`` context ``ctx`` and return it as
|
|
## result array.
|
|
getTag(ctx, result)
|
|
|
|
proc clear*[T](ctx: var GCM[T]) {.inline.} =
|
|
## Clear ``GCM[T]`` context ``ctx``.
|
|
burnMem(ctx)
|