mirror of https://github.com/vacp2p/nim-libp2p.git
Refactor minasn1 and fix security issues. (#323)
* Refactor minasn1 and fix security issues. * Fix for RSA test vectors.
This commit is contained in:
parent
d47b2d805f
commit
59b290fcc7
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
{.push raises: [Defect].}
|
{.push raises: [Defect].}
|
||||||
|
|
||||||
import stew/[endians2, results]
|
import stew/[endians2, results, ctops]
|
||||||
export results
|
export results
|
||||||
# We use `ncrutils` for constant-time hexadecimal encoding/decoding procedures.
|
# We use `ncrutils` for constant-time hexadecimal encoding/decoding procedures.
|
||||||
import nimcrypto/utils as ncrutils
|
import nimcrypto/utils as ncrutils
|
||||||
|
@ -123,7 +123,7 @@ proc len*[T: Asn1Buffer|Asn1Composite](abc: T): int {.inline.} =
|
||||||
len(abc.buffer) - abc.offset
|
len(abc.buffer) - abc.offset
|
||||||
|
|
||||||
proc len*(field: Asn1Field): int {.inline.} =
|
proc len*(field: Asn1Field): int {.inline.} =
|
||||||
result = field.length
|
field.length
|
||||||
|
|
||||||
template getPtr*(field: untyped): pointer =
|
template getPtr*(field: untyped): pointer =
|
||||||
cast[pointer](unsafeAddr field.buffer[field.offset])
|
cast[pointer](unsafeAddr field.buffer[field.offset])
|
||||||
|
@ -154,30 +154,32 @@ proc code*(tag: Asn1Tag): byte {.inline.} =
|
||||||
of Asn1Tag.Context:
|
of Asn1Tag.Context:
|
||||||
0xA0'u8
|
0xA0'u8
|
||||||
|
|
||||||
proc asn1EncodeLength*(dest: var openarray[byte], length: int64): int =
|
proc asn1EncodeLength*(dest: var openarray[byte], length: uint64): int =
|
||||||
## Encode ASN.1 DER length part of TLV triple and return number of bytes
|
## Encode ASN.1 DER length part of TLV triple and return number of bytes
|
||||||
## (octets) used.
|
## (octets) used.
|
||||||
##
|
##
|
||||||
## If length of ``dest`` is less then number of required bytes to encode
|
## If length of ``dest`` is less then number of required bytes to encode
|
||||||
## ``length`` value, then result of encoding will not be stored in ``dest``
|
## ``length`` value, then result of encoding WILL NOT BE stored in ``dest``
|
||||||
## but number of bytes (octets) required will be returned.
|
## but number of bytes (octets) required will be returned.
|
||||||
if length < 0x80:
|
if length < 0x80'u64:
|
||||||
if len(dest) >= 1:
|
if len(dest) >= 1:
|
||||||
dest[0] = cast[byte](length)
|
dest[0] = byte(length and 0x7F'u64)
|
||||||
result = 1
|
1
|
||||||
else:
|
else:
|
||||||
result = 0
|
var res = 1'u64
|
||||||
var z = length
|
var z = length
|
||||||
while z != 0:
|
while z != 0:
|
||||||
inc(result)
|
inc(res)
|
||||||
z = z shr 8
|
z = z shr 8
|
||||||
if len(dest) >= result + 1:
|
if uint64(len(dest)) >= res:
|
||||||
dest[0] = cast[byte](0x80 + result)
|
dest[0] = byte((0x80'u64 + (res - 1'u64)) and 0xFF)
|
||||||
var o = 1
|
var o = 1
|
||||||
for j in countdown(result - 1, 0):
|
for j in countdown(res - 2, 0):
|
||||||
dest[o] = cast[byte](length shr (j shl 3))
|
dest[o] = byte((length shr (j shl 3)) and 0xFF'u64)
|
||||||
inc(o)
|
inc(o)
|
||||||
inc(result)
|
# Because our `length` argument is `uint64`, `res` could not be bigger
|
||||||
|
# then 9, so it is safe to convert it to `int`.
|
||||||
|
int(res)
|
||||||
|
|
||||||
proc asn1EncodeInteger*(dest: var openarray[byte],
|
proc asn1EncodeInteger*(dest: var openarray[byte],
|
||||||
value: openarray[byte]): int =
|
value: openarray[byte]): int =
|
||||||
|
@ -185,35 +187,46 @@ proc asn1EncodeInteger*(dest: var openarray[byte],
|
||||||
## and return number of bytes (octets) used.
|
## and return number of bytes (octets) used.
|
||||||
##
|
##
|
||||||
## If length of ``dest`` is less then number of required bytes to encode
|
## If length of ``dest`` is less then number of required bytes to encode
|
||||||
## ``value``, then result of encoding will not be stored in ``dest``
|
## ``value``, then result of encoding WILL NOT BE stored in ``dest``
|
||||||
## but number of bytes (octets) required will be returned.
|
## but number of bytes (octets) required will be returned.
|
||||||
var buffer: array[16, byte]
|
var buffer: array[16, byte]
|
||||||
var o = 0
|
|
||||||
var lenlen = 0
|
var lenlen = 0
|
||||||
for i in 0..<len(value):
|
|
||||||
if value[o] != 0x00:
|
let offset =
|
||||||
break
|
block:
|
||||||
inc(o)
|
var o = 0
|
||||||
if len(value) > 0:
|
for i in 0 ..< len(value):
|
||||||
if o == len(value):
|
if value[o] != 0x00:
|
||||||
dec(o)
|
break
|
||||||
if value[o] >= 0x80'u8:
|
inc(o)
|
||||||
lenlen = asn1EncodeLength(buffer, len(value) - o + 1)
|
if o < len(value):
|
||||||
result = 1 + lenlen + 1 + (len(value) - o)
|
o
|
||||||
|
else:
|
||||||
|
o - 1
|
||||||
|
|
||||||
|
let destlen =
|
||||||
|
if len(value) > 0:
|
||||||
|
if value[offset] >= 0x80'u8:
|
||||||
|
lenlen = asn1EncodeLength(buffer, uint64(len(value) - offset + 1))
|
||||||
|
1 + lenlen + 1 + (len(value) - offset)
|
||||||
|
else:
|
||||||
|
lenlen = asn1EncodeLength(buffer, uint64(len(value) - offset))
|
||||||
|
1 + lenlen + (len(value) - offset)
|
||||||
else:
|
else:
|
||||||
lenlen = asn1EncodeLength(buffer, len(value) - o)
|
2
|
||||||
result = 1 + lenlen + (len(value) - o)
|
|
||||||
else:
|
if len(dest) >= destlen:
|
||||||
result = 2
|
var shift = 1
|
||||||
if len(dest) >= result:
|
|
||||||
var s = 1
|
|
||||||
dest[0] = Asn1Tag.Integer.code()
|
dest[0] = Asn1Tag.Integer.code()
|
||||||
copyMem(addr dest[1], addr buffer[0], lenlen)
|
copyMem(addr dest[1], addr buffer[0], lenlen)
|
||||||
if value[o] >= 0x80'u8:
|
# If ``destlen > 2`` it means that ``len(value) > 0`` too.
|
||||||
dest[1 + lenlen] = 0x00'u8
|
if destlen > 2:
|
||||||
s = 2
|
if value[offset] >= 0x80'u8:
|
||||||
if len(value) > 0:
|
dest[1 + lenlen] = 0x00'u8
|
||||||
copyMem(addr dest[s + lenlen], unsafeAddr value[o], len(value) - o)
|
shift = 2
|
||||||
|
copyMem(addr dest[shift + lenlen], unsafeAddr value[offset],
|
||||||
|
len(value) - offset)
|
||||||
|
destlen
|
||||||
|
|
||||||
proc asn1EncodeInteger*[T: SomeUnsignedInt](dest: var openarray[byte],
|
proc asn1EncodeInteger*[T: SomeUnsignedInt](dest: var openarray[byte],
|
||||||
value: T): int =
|
value: T): int =
|
||||||
|
@ -232,11 +245,12 @@ proc asn1EncodeBoolean*(dest: var openarray[byte], value: bool): int =
|
||||||
## If length of ``dest`` is less then number of required bytes to encode
|
## If length of ``dest`` is less then number of required bytes to encode
|
||||||
## ``value``, then result of encoding will not be stored in ``dest``
|
## ``value``, then result of encoding will not be stored in ``dest``
|
||||||
## but number of bytes (octets) required will be returned.
|
## but number of bytes (octets) required will be returned.
|
||||||
result = 3
|
let res = 3
|
||||||
if len(dest) >= result:
|
if len(dest) >= res:
|
||||||
dest[0] = Asn1Tag.Boolean.code()
|
dest[0] = Asn1Tag.Boolean.code()
|
||||||
dest[1] = 0x01'u8
|
dest[1] = 0x01'u8
|
||||||
dest[2] = if value: 0xFF'u8 else: 0x00'u8
|
dest[2] = if value: 0xFF'u8 else: 0x00'u8
|
||||||
|
res
|
||||||
|
|
||||||
proc asn1EncodeNull*(dest: var openarray[byte]): int =
|
proc asn1EncodeNull*(dest: var openarray[byte]): int =
|
||||||
## Encode ASN.1 DER `NULL` and return number of bytes (octets) used.
|
## Encode ASN.1 DER `NULL` and return number of bytes (octets) used.
|
||||||
|
@ -244,13 +258,14 @@ proc asn1EncodeNull*(dest: var openarray[byte]): int =
|
||||||
## If length of ``dest`` is less then number of required bytes to encode
|
## If length of ``dest`` is less then number of required bytes to encode
|
||||||
## ``value``, then result of encoding will not be stored in ``dest``
|
## ``value``, then result of encoding will not be stored in ``dest``
|
||||||
## but number of bytes (octets) required will be returned.
|
## but number of bytes (octets) required will be returned.
|
||||||
result = 2
|
let res = 2
|
||||||
if len(dest) >= result:
|
if len(dest) >= res:
|
||||||
dest[0] = Asn1Tag.Null.code()
|
dest[0] = Asn1Tag.Null.code()
|
||||||
dest[1] = 0x00'u8
|
dest[1] = 0x00'u8
|
||||||
|
res
|
||||||
|
|
||||||
proc asn1EncodeOctetString*(dest: var openarray[byte],
|
proc asn1EncodeOctetString*(dest: var openarray[byte],
|
||||||
value: openarray[byte]): int =
|
value: openarray[byte]): int =
|
||||||
## Encode array of bytes as ASN.1 DER `OCTET STRING` and return number of
|
## Encode array of bytes as ASN.1 DER `OCTET STRING` and return number of
|
||||||
## bytes (octets) used.
|
## bytes (octets) used.
|
||||||
##
|
##
|
||||||
|
@ -258,38 +273,50 @@ proc asn1EncodeOctetString*(dest: var openarray[byte],
|
||||||
## ``value``, then result of encoding will not be stored in ``dest``
|
## ``value``, then result of encoding will not be stored in ``dest``
|
||||||
## but number of bytes (octets) required will be returned.
|
## but number of bytes (octets) required will be returned.
|
||||||
var buffer: array[16, byte]
|
var buffer: array[16, byte]
|
||||||
var lenlen = asn1EncodeLength(buffer, len(value))
|
let lenlen = asn1EncodeLength(buffer, uint64(len(value)))
|
||||||
result = 1 + lenlen + len(value)
|
let res = 1 + lenlen + len(value)
|
||||||
if len(dest) >= result:
|
if len(dest) >= res:
|
||||||
dest[0] = Asn1Tag.OctetString.code()
|
dest[0] = Asn1Tag.OctetString.code()
|
||||||
copyMem(addr dest[1], addr buffer[0], lenlen)
|
copyMem(addr dest[1], addr buffer[0], lenlen)
|
||||||
if len(value) > 0:
|
if len(value) > 0:
|
||||||
copyMem(addr dest[1 + lenlen], unsafeAddr value[0], len(value))
|
copyMem(addr dest[1 + lenlen], unsafeAddr value[0], len(value))
|
||||||
|
res
|
||||||
|
|
||||||
proc asn1EncodeBitString*(dest: var openarray[byte],
|
proc asn1EncodeBitString*(dest: var openarray[byte],
|
||||||
value: openarray[byte], bits = 0): int =
|
value: openarray[byte], bits = 0): int =
|
||||||
## Encode array of bytes as ASN.1 DER `BIT STRING` and return number of bytes
|
## Encode array of bytes as ASN.1 DER `BIT STRING` and return number of bytes
|
||||||
## (octets) used.
|
## (octets) used.
|
||||||
##
|
##
|
||||||
## ``bits`` number of used bits in ``value``. If ``bits == 0``, all the bits
|
## ``bits`` number of unused bits in ``value``. If ``bits == 0``, all the bits
|
||||||
## from ``value`` are used, if ``bits != 0`` only number of ``bits`` will be
|
## from ``value`` will be used.
|
||||||
## used.
|
|
||||||
##
|
##
|
||||||
## If length of ``dest`` is less then number of required bytes to encode
|
## If length of ``dest`` is less then number of required bytes to encode
|
||||||
## ``value``, then result of encoding will not be stored in ``dest``
|
## ``value``, then result of encoding will not be stored in ``dest``
|
||||||
## but number of bytes (octets) required will be returned.
|
## but number of bytes (octets) required will be returned.
|
||||||
var buffer: array[16, byte]
|
var buffer: array[16, byte]
|
||||||
var lenlen = asn1EncodeLength(buffer, len(value) + 1)
|
let bitlen =
|
||||||
var lbits = 0
|
if bits != 0:
|
||||||
if bits != 0:
|
(len(value) shl 3) - bits
|
||||||
lbits = len(value) shl 3 - bits
|
else:
|
||||||
result = 1 + lenlen + 1 + len(value)
|
(len(value) shl 3)
|
||||||
if len(dest) >= result:
|
|
||||||
|
# Number of bytes used
|
||||||
|
let bytelen = (bitlen + 7) shr 3
|
||||||
|
# Number of unused bits
|
||||||
|
let unused = (8 - (bitlen and 7)) and 7
|
||||||
|
let mask = not((1'u8 shl unused) - 1'u8)
|
||||||
|
var lenlen = asn1EncodeLength(buffer, uint64(bytelen + 1))
|
||||||
|
let res = 1 + lenlen + 1 + len(value)
|
||||||
|
if len(dest) >= res:
|
||||||
dest[0] = Asn1Tag.BitString.code()
|
dest[0] = Asn1Tag.BitString.code()
|
||||||
copyMem(addr dest[1], addr buffer[0], lenlen)
|
copyMem(addr dest[1], addr buffer[0], lenlen)
|
||||||
dest[1 + lenlen] = cast[byte](lbits)
|
dest[1 + lenlen] = byte(unused)
|
||||||
if len(value) > 0:
|
if bytelen > 0:
|
||||||
copyMem(addr dest[2 + lenlen], unsafeAddr value[0], len(value))
|
let lastbyte = value[bytelen - 1]
|
||||||
|
copyMem(addr dest[2 + lenlen], unsafeAddr value[0], bytelen)
|
||||||
|
# Set unused bits to zero
|
||||||
|
dest[2 + lenlen + bytelen - 1] = lastbyte and mask
|
||||||
|
res
|
||||||
|
|
||||||
proc asn1EncodeTag[T: SomeUnsignedInt](dest: var openarray[byte],
|
proc asn1EncodeTag[T: SomeUnsignedInt](dest: var openarray[byte],
|
||||||
value: T): int =
|
value: T): int =
|
||||||
|
@ -297,53 +324,48 @@ proc asn1EncodeTag[T: SomeUnsignedInt](dest: var openarray[byte],
|
||||||
if value <= cast[T](0x7F):
|
if value <= cast[T](0x7F):
|
||||||
if len(dest) >= 1:
|
if len(dest) >= 1:
|
||||||
dest[0] = cast[byte](value)
|
dest[0] = cast[byte](value)
|
||||||
result = 1
|
1
|
||||||
else:
|
else:
|
||||||
var s = 0
|
var s = 0
|
||||||
|
var res = 0
|
||||||
while v != 0:
|
while v != 0:
|
||||||
v = v shr 7
|
v = v shr 7
|
||||||
s += 7
|
s += 7
|
||||||
inc(result)
|
inc(res)
|
||||||
if len(dest) >= result:
|
if len(dest) >= res:
|
||||||
var k = 0
|
var k = 0
|
||||||
while s != 0:
|
while s != 0:
|
||||||
s -= 7
|
s -= 7
|
||||||
dest[k] = cast[byte](((value shr s) and cast[T](0x7F)) or cast[T](0x80))
|
dest[k] = cast[byte](((value shr s) and cast[T](0x7F)) or cast[T](0x80))
|
||||||
inc(k)
|
inc(k)
|
||||||
dest[k - 1] = dest[k - 1] and 0x7F'u8
|
dest[k - 1] = dest[k - 1] and 0x7F'u8
|
||||||
|
res
|
||||||
|
|
||||||
proc asn1EncodeOid*(dest: var openarray[byte], value: openarray[int]): int =
|
proc asn1EncodeOid*(dest: var openarray[byte], value: openarray[int]): int =
|
||||||
## Encode array of integers ``value`` as ASN.1 DER `OBJECT IDENTIFIER` and
|
## Encode array of integers ``value`` as ASN.1 DER `OBJECT IDENTIFIER` and
|
||||||
## return number of bytes (octets) used.
|
## return number of bytes (octets) used.
|
||||||
##
|
##
|
||||||
## OBJECT IDENTIFIER requirements for ``value`` elements:
|
|
||||||
## * len(value) >= 2
|
|
||||||
## * value[0] >= 1 and value[0] < 2
|
|
||||||
## * value[1] >= 1 and value[1] < 39
|
|
||||||
##
|
|
||||||
## If length of ``dest`` is less then number of required bytes to encode
|
## If length of ``dest`` is less then number of required bytes to encode
|
||||||
## ``value``, then result of encoding will not be stored in ``dest``
|
## ``value``, then result of encoding will not be stored in ``dest``
|
||||||
## but number of bytes (octets) required will be returned.
|
## but number of bytes (octets) required will be returned.
|
||||||
var buffer: array[16, byte]
|
var buffer: array[16, byte]
|
||||||
result = 1
|
var res = 1
|
||||||
doAssert(len(value) >= 2)
|
|
||||||
doAssert(value[0] >= 1 and value[0] < 2)
|
|
||||||
doAssert(value[1] >= 1 and value[1] <= 39)
|
|
||||||
var oidlen = 1
|
var oidlen = 1
|
||||||
for i in 2..<len(value):
|
for i in 2..<len(value):
|
||||||
oidlen += asn1EncodeTag(buffer, cast[uint64](value[i]))
|
oidlen += asn1EncodeTag(buffer, cast[uint64](value[i]))
|
||||||
result += asn1EncodeLength(buffer, oidlen)
|
res += asn1EncodeLength(buffer, uint64(oidlen))
|
||||||
result += oidlen
|
res += oidlen
|
||||||
if len(dest) >= result:
|
if len(dest) >= res:
|
||||||
let last = dest.high
|
let last = dest.high
|
||||||
var offset = 1
|
var offset = 1
|
||||||
dest[0] = Asn1Tag.Oid.code()
|
dest[0] = Asn1Tag.Oid.code()
|
||||||
offset += asn1EncodeLength(dest.toOpenArray(offset, last), oidlen)
|
offset += asn1EncodeLength(dest.toOpenArray(offset, last), uint64(oidlen))
|
||||||
dest[offset] = cast[byte](value[0] * 40 + value[1])
|
dest[offset] = cast[byte](value[0] * 40 + value[1])
|
||||||
offset += 1
|
offset += 1
|
||||||
for i in 2..<len(value):
|
for i in 2..<len(value):
|
||||||
offset += asn1EncodeTag(dest.toOpenArray(offset, last),
|
offset += asn1EncodeTag(dest.toOpenArray(offset, last),
|
||||||
cast[uint64](value[i]))
|
cast[uint64](value[i]))
|
||||||
|
res
|
||||||
|
|
||||||
proc asn1EncodeOid*(dest: var openarray[byte], value: openarray[byte]): int =
|
proc asn1EncodeOid*(dest: var openarray[byte], value: openarray[byte]): int =
|
||||||
## Encode array of bytes ``value`` as ASN.1 DER `OBJECT IDENTIFIER` and return
|
## Encode array of bytes ``value`` as ASN.1 DER `OBJECT IDENTIFIER` and return
|
||||||
|
@ -356,12 +378,13 @@ proc asn1EncodeOid*(dest: var openarray[byte], value: openarray[byte]): int =
|
||||||
## ``value``, then result of encoding will not be stored in ``dest``
|
## ``value``, then result of encoding will not be stored in ``dest``
|
||||||
## but number of bytes (octets) required will be returned.
|
## but number of bytes (octets) required will be returned.
|
||||||
var buffer: array[16, byte]
|
var buffer: array[16, byte]
|
||||||
var lenlen = asn1EncodeLength(buffer, len(value))
|
let lenlen = asn1EncodeLength(buffer, uint64(len(value)))
|
||||||
result = 1 + lenlen + len(value)
|
let res = 1 + lenlen + len(value)
|
||||||
if len(dest) >= result:
|
if len(dest) >= res:
|
||||||
dest[0] = Asn1Tag.Oid.code()
|
dest[0] = Asn1Tag.Oid.code()
|
||||||
copyMem(addr dest[1], addr buffer[0], lenlen)
|
copyMem(addr dest[1], addr buffer[0], lenlen)
|
||||||
copyMem(addr dest[1 + lenlen], unsafeAddr value[0], len(value))
|
copyMem(addr dest[1 + lenlen], unsafeAddr value[0], len(value))
|
||||||
|
res
|
||||||
|
|
||||||
proc asn1EncodeSequence*(dest: var openarray[byte],
|
proc asn1EncodeSequence*(dest: var openarray[byte],
|
||||||
value: openarray[byte]): int =
|
value: openarray[byte]): int =
|
||||||
|
@ -372,12 +395,13 @@ proc asn1EncodeSequence*(dest: var openarray[byte],
|
||||||
## ``value``, then result of encoding will not be stored in ``dest``
|
## ``value``, then result of encoding will not be stored in ``dest``
|
||||||
## but number of bytes (octets) required will be returned.
|
## but number of bytes (octets) required will be returned.
|
||||||
var buffer: array[16, byte]
|
var buffer: array[16, byte]
|
||||||
var lenlen = asn1EncodeLength(buffer, len(value))
|
let lenlen = asn1EncodeLength(buffer, uint64(len(value)))
|
||||||
result = 1 + lenlen + len(value)
|
let res = 1 + lenlen + len(value)
|
||||||
if len(dest) >= result:
|
if len(dest) >= res:
|
||||||
dest[0] = Asn1Tag.Sequence.code()
|
dest[0] = Asn1Tag.Sequence.code()
|
||||||
copyMem(addr dest[1], addr buffer[0], lenlen)
|
copyMem(addr dest[1], addr buffer[0], lenlen)
|
||||||
copyMem(addr dest[1 + lenlen], unsafeAddr value[0], len(value))
|
copyMem(addr dest[1 + lenlen], unsafeAddr value[0], len(value))
|
||||||
|
res
|
||||||
|
|
||||||
proc asn1EncodeComposite*(dest: var openarray[byte],
|
proc asn1EncodeComposite*(dest: var openarray[byte],
|
||||||
value: Asn1Composite): int =
|
value: Asn1Composite): int =
|
||||||
|
@ -387,29 +411,34 @@ proc asn1EncodeComposite*(dest: var openarray[byte],
|
||||||
## ``value``, then result of encoding will not be stored in ``dest``
|
## ``value``, then result of encoding will not be stored in ``dest``
|
||||||
## but number of bytes (octets) required will be returned.
|
## but number of bytes (octets) required will be returned.
|
||||||
var buffer: array[16, byte]
|
var buffer: array[16, byte]
|
||||||
var lenlen = asn1EncodeLength(buffer, len(value.buffer))
|
let lenlen = asn1EncodeLength(buffer, uint64(len(value.buffer)))
|
||||||
result = 1 + lenlen + len(value.buffer)
|
let res = 1 + lenlen + len(value.buffer)
|
||||||
if len(dest) >= result:
|
if len(dest) >= res:
|
||||||
dest[0] = value.tag.code()
|
dest[0] = value.tag.code()
|
||||||
copyMem(addr dest[1], addr buffer[0], lenlen)
|
copyMem(addr dest[1], addr buffer[0], lenlen)
|
||||||
copyMem(addr dest[1 + lenlen], unsafeAddr value.buffer[0],
|
copyMem(addr dest[1 + lenlen], unsafeAddr value.buffer[0],
|
||||||
len(value.buffer))
|
len(value.buffer))
|
||||||
|
res
|
||||||
|
|
||||||
proc asn1EncodeContextTag*(dest: var openarray[byte], value: openarray[byte],
|
proc asn1EncodeContextTag*(dest: var openarray[byte], value: openarray[byte],
|
||||||
tag: int): int =
|
tag: int): int =
|
||||||
## Encode ASN.1 DER `CONTEXT SPECIFIC TAG` ``tag`` for value ``value`` and
|
## Encode ASN.1 DER `CONTEXT SPECIFIC TAG` ``tag`` for value ``value`` and
|
||||||
## return number of bytes (octets) used.
|
## return number of bytes (octets) used.
|
||||||
##
|
##
|
||||||
|
## Note: Only values in [0, 15] range can be used as context tag ``tag``
|
||||||
|
## values.
|
||||||
|
##
|
||||||
## If length of ``dest`` is less then number of required bytes to encode
|
## If length of ``dest`` is less then number of required bytes to encode
|
||||||
## ``value``, then result of encoding will not be stored in ``dest``
|
## ``value``, then result of encoding will not be stored in ``dest``
|
||||||
## but number of bytes (octets) required will be returned.
|
## but number of bytes (octets) required will be returned.
|
||||||
var buffer: array[16, byte]
|
var buffer: array[16, byte]
|
||||||
var lenlen = asn1EncodeLength(buffer, len(value))
|
let lenlen = asn1EncodeLength(buffer, uint64(len(value)))
|
||||||
result = 1 + lenlen + len(value)
|
let res = 1 + lenlen + len(value)
|
||||||
if len(dest) >= result:
|
if len(dest) >= res:
|
||||||
dest[0] = 0xA0'u8 or (cast[byte](tag) and 0x0F)
|
dest[0] = 0xA0'u8 or (byte(tag and 0xFF) and 0x0F'u8)
|
||||||
copyMem(addr dest[1], addr buffer[0], lenlen)
|
copyMem(addr dest[1], addr buffer[0], lenlen)
|
||||||
copyMem(addr dest[1 + lenlen], unsafeAddr value[0], len(value))
|
copyMem(addr dest[1 + lenlen], unsafeAddr value[0], len(value))
|
||||||
|
res
|
||||||
|
|
||||||
proc getLength(ab: var Asn1Buffer): Asn1Result[uint64] =
|
proc getLength(ab: var Asn1Buffer): Asn1Result[uint64] =
|
||||||
## Decode length part of ASN.1 TLV triplet.
|
## Decode length part of ASN.1 TLV triplet.
|
||||||
|
@ -458,197 +487,300 @@ proc read*(ab: var Asn1Buffer): Asn1Result[Asn1Field] =
|
||||||
field: Asn1Field
|
field: Asn1Field
|
||||||
tag, ttag, offset: int
|
tag, ttag, offset: int
|
||||||
length, tlength: uint64
|
length, tlength: uint64
|
||||||
klass: Asn1Class
|
aclass: Asn1Class
|
||||||
inclass: bool
|
inclass: bool
|
||||||
|
|
||||||
inclass = false
|
inclass = false
|
||||||
while true:
|
while true:
|
||||||
offset = ab.offset
|
offset = ab.offset
|
||||||
klass = ? ab.getTag(tag)
|
aclass = ? ab.getTag(tag)
|
||||||
|
|
||||||
if klass == Asn1Class.ContextSpecific:
|
case aclass
|
||||||
|
of Asn1Class.ContextSpecific:
|
||||||
if inclass:
|
if inclass:
|
||||||
return err(Asn1Error.Incorrect)
|
return err(Asn1Error.Incorrect)
|
||||||
|
else:
|
||||||
inclass = true
|
inclass = true
|
||||||
ttag = tag
|
ttag = tag
|
||||||
tlength = ? ab.getLength()
|
tlength = ? ab.getLength()
|
||||||
|
of Asn1Class.Universal:
|
||||||
elif klass == Asn1Class.Universal:
|
|
||||||
length = ? ab.getLength()
|
length = ? ab.getLength()
|
||||||
|
|
||||||
if inclass:
|
if inclass:
|
||||||
if length >= tlength:
|
if length >= tlength:
|
||||||
return err(Asn1Error.Incorrect)
|
return err(Asn1Error.Incorrect)
|
||||||
|
|
||||||
if cast[byte](tag) == Asn1Tag.Boolean.code():
|
case byte(tag)
|
||||||
|
of Asn1Tag.Boolean.code():
|
||||||
# BOOLEAN
|
# BOOLEAN
|
||||||
if length != 1:
|
if length != 1:
|
||||||
return err(Asn1Error.Incorrect)
|
return err(Asn1Error.Incorrect)
|
||||||
if not ab.isEnough(cast[int](length)):
|
|
||||||
|
if not ab.isEnough(int(length)):
|
||||||
return err(Asn1Error.Incomplete)
|
return err(Asn1Error.Incomplete)
|
||||||
|
|
||||||
let b = ab.buffer[ab.offset]
|
let b = ab.buffer[ab.offset]
|
||||||
if b != 0xFF'u8 and b != 0x00'u8:
|
if b != 0xFF'u8 and b != 0x00'u8:
|
||||||
return err(Asn1Error.Incorrect)
|
return err(Asn1Error.Incorrect)
|
||||||
|
|
||||||
field = Asn1Field(kind: Asn1Tag.Boolean, klass: klass,
|
field = Asn1Field(kind: Asn1Tag.Boolean, klass: aclass,
|
||||||
index: ttag, offset: cast[int](ab.offset),
|
index: ttag, offset: int(ab.offset),
|
||||||
length: 1)
|
length: 1)
|
||||||
shallowCopy(field.buffer, ab.buffer)
|
shallowCopy(field.buffer, ab.buffer)
|
||||||
field.vbool = (b == 0xFF'u8)
|
field.vbool = (b == 0xFF'u8)
|
||||||
ab.offset += 1
|
ab.offset += 1
|
||||||
return ok(field)
|
return ok(field)
|
||||||
elif cast[byte](tag) == Asn1Tag.Integer.code():
|
|
||||||
|
of Asn1Tag.Integer.code():
|
||||||
# INTEGER
|
# INTEGER
|
||||||
if not ab.isEnough(cast[int](length)):
|
if length == 0:
|
||||||
return err(Asn1Error.Incomplete)
|
return err(Asn1Error.Incorrect)
|
||||||
if ab.buffer[ab.offset] == 0x00'u8:
|
|
||||||
length -= 1
|
if not ab.isEnough(int(length)):
|
||||||
ab.offset += 1
|
return err(Asn1Error.Incomplete)
|
||||||
field = Asn1Field(kind: Asn1Tag.Integer, klass: klass,
|
|
||||||
index: ttag, offset: cast[int](ab.offset),
|
# Count number of leading zeroes
|
||||||
length: cast[int](length))
|
var zc = 0
|
||||||
shallowCopy(field.buffer, ab.buffer)
|
while (zc < int(length)) and (ab.buffer[ab.offset + zc] == 0x00'u8):
|
||||||
if length <= 8:
|
inc(zc)
|
||||||
for i in 0..<int(length):
|
|
||||||
field.vint = (field.vint shl 8) or
|
if zc > 1:
|
||||||
cast[uint64](ab.buffer[ab.offset + i])
|
return err(Asn1Error.Incorrect)
|
||||||
ab.offset += cast[int](length)
|
|
||||||
return ok(field)
|
if zc == 0:
|
||||||
elif cast[byte](tag) == Asn1Tag.BitString.code():
|
# Negative or Positive integer
|
||||||
|
field = Asn1Field(kind: Asn1Tag.Integer, klass: aclass,
|
||||||
|
index: ttag, offset: int(ab.offset),
|
||||||
|
length: int(length))
|
||||||
|
shallowCopy(field.buffer, ab.buffer)
|
||||||
|
if (ab.buffer[ab.offset] and 0x80'u8) == 0x80'u8:
|
||||||
|
# Negative integer
|
||||||
|
if length <= 8:
|
||||||
|
# We need this transformation because our field.vint is uint64.
|
||||||
|
for i in 0 ..< 8:
|
||||||
|
if i < 8 - int(length):
|
||||||
|
field.vint = (field.vint shl 8) or 0xFF'u64
|
||||||
|
else:
|
||||||
|
let offset = ab.offset + i - (8 - int(length))
|
||||||
|
field.vint = (field.vint shl 8) or uint64(ab.buffer[offset])
|
||||||
|
else:
|
||||||
|
# Positive integer
|
||||||
|
if length <= 8:
|
||||||
|
for i in 0 ..< int(length):
|
||||||
|
field.vint = (field.vint shl 8) or
|
||||||
|
uint64(ab.buffer[ab.offset + i])
|
||||||
|
ab.offset += int(length)
|
||||||
|
return ok(field)
|
||||||
|
else:
|
||||||
|
if length == 1:
|
||||||
|
# Zero value integer
|
||||||
|
field = Asn1Field(kind: Asn1Tag.Integer, klass: aclass,
|
||||||
|
index: ttag, offset: int(ab.offset),
|
||||||
|
length: int(length), vint: 0'u64)
|
||||||
|
shallowCopy(field.buffer, ab.buffer)
|
||||||
|
ab.offset += int(length)
|
||||||
|
return ok(field)
|
||||||
|
else:
|
||||||
|
# Positive integer with leading zero
|
||||||
|
field = Asn1Field(kind: Asn1Tag.Integer, klass: aclass,
|
||||||
|
index: ttag, offset: int(ab.offset) + 1,
|
||||||
|
length: int(length) - 1)
|
||||||
|
shallowCopy(field.buffer, ab.buffer)
|
||||||
|
if length <= 9:
|
||||||
|
for i in 1 ..< int(length):
|
||||||
|
field.vint = (field.vint shl 8) or
|
||||||
|
uint64(ab.buffer[ab.offset + i])
|
||||||
|
ab.offset += int(length)
|
||||||
|
return ok(field)
|
||||||
|
|
||||||
|
of Asn1Tag.BitString.code():
|
||||||
# BIT STRING
|
# BIT STRING
|
||||||
if not ab.isEnough(cast[int](length)):
|
if length == 0:
|
||||||
|
# BIT STRING should include `unused` bits field, so length should be
|
||||||
|
# bigger then 1.
|
||||||
|
return err(Asn1Error.Incorrect)
|
||||||
|
|
||||||
|
elif length == 1:
|
||||||
|
if ab.buffer[ab.offset] != 0x00'u8:
|
||||||
|
return err(Asn1Error.Incorrect)
|
||||||
|
else:
|
||||||
|
# Zero-length BIT STRING.
|
||||||
|
field = Asn1Field(kind: Asn1Tag.BitString, klass: aclass,
|
||||||
|
index: ttag, offset: int(ab.offset + 1),
|
||||||
|
length: 0, ubits: 0)
|
||||||
|
shallowCopy(field.buffer, ab.buffer)
|
||||||
|
ab.offset += int(length)
|
||||||
|
return ok(field)
|
||||||
|
|
||||||
|
else:
|
||||||
|
if not ab.isEnough(int(length)):
|
||||||
|
return err(Asn1Error.Incomplete)
|
||||||
|
|
||||||
|
let unused = ab.buffer[ab.offset]
|
||||||
|
if unused > 0x07'u8:
|
||||||
|
# Number of unused bits should not be bigger then `7`.
|
||||||
|
return err(Asn1Error.Incorrect)
|
||||||
|
|
||||||
|
let mask = (1'u8 shl int(unused)) - 1'u8
|
||||||
|
if (ab.buffer[ab.offset + int(length) - 1] and mask) != 0x00'u8:
|
||||||
|
## All unused bits should be set to `0`.
|
||||||
|
return err(Asn1Error.Incorrect)
|
||||||
|
|
||||||
|
field = Asn1Field(kind: Asn1Tag.BitString, klass: aclass,
|
||||||
|
index: ttag, offset: int(ab.offset + 1),
|
||||||
|
length: int(length - 1), ubits: int(unused))
|
||||||
|
shallowCopy(field.buffer, ab.buffer)
|
||||||
|
ab.offset += int(length)
|
||||||
|
return ok(field)
|
||||||
|
|
||||||
|
of Asn1Tag.OctetString.code():
|
||||||
|
# OCTET STRING
|
||||||
|
if not ab.isEnough(int(length)):
|
||||||
return err(Asn1Error.Incomplete)
|
return err(Asn1Error.Incomplete)
|
||||||
field = Asn1Field(kind: Asn1Tag.BitString, klass: klass,
|
|
||||||
index: ttag, offset: cast[int](ab.offset + 1),
|
field = Asn1Field(kind: Asn1Tag.OctetString, klass: aclass,
|
||||||
length: cast[int](length - 1))
|
index: ttag, offset: int(ab.offset),
|
||||||
|
length: int(length))
|
||||||
shallowCopy(field.buffer, ab.buffer)
|
shallowCopy(field.buffer, ab.buffer)
|
||||||
field.ubits = cast[int](((length - 1) shl 3) - ab.buffer[ab.offset])
|
ab.offset += int(length)
|
||||||
ab.offset += cast[int](length)
|
|
||||||
return ok(field)
|
return ok(field)
|
||||||
elif cast[byte](tag) == Asn1Tag.OctetString.code():
|
|
||||||
# OCT STRING
|
of Asn1Tag.Null.code():
|
||||||
if not ab.isEnough(cast[int](length)):
|
|
||||||
return err(Asn1Error.Incomplete)
|
|
||||||
field = Asn1Field(kind: Asn1Tag.OctetString, klass: klass,
|
|
||||||
index: ttag, offset: cast[int](ab.offset),
|
|
||||||
length: cast[int](length))
|
|
||||||
shallowCopy(field.buffer, ab.buffer)
|
|
||||||
ab.offset += cast[int](length)
|
|
||||||
return ok(field)
|
|
||||||
elif cast[byte](tag) == Asn1Tag.Null.code():
|
|
||||||
# NULL
|
# NULL
|
||||||
if length != 0:
|
if length != 0:
|
||||||
return err(Asn1Error.Incorrect)
|
return err(Asn1Error.Incorrect)
|
||||||
field = Asn1Field(kind: Asn1Tag.Null, klass: klass,
|
|
||||||
index: ttag, offset: cast[int](ab.offset),
|
field = Asn1Field(kind: Asn1Tag.Null, klass: aclass, index: ttag,
|
||||||
length: 0)
|
offset: int(ab.offset), length: 0)
|
||||||
shallowCopy(field.buffer, ab.buffer)
|
shallowCopy(field.buffer, ab.buffer)
|
||||||
ab.offset += cast[int](length)
|
ab.offset += int(length)
|
||||||
return ok(field)
|
return ok(field)
|
||||||
elif cast[byte](tag) == Asn1Tag.Oid.code():
|
|
||||||
|
of Asn1Tag.Oid.code():
|
||||||
# OID
|
# OID
|
||||||
if not ab.isEnough(cast[int](length)):
|
if not ab.isEnough(int(length)):
|
||||||
return err(Asn1Error.Incomplete)
|
return err(Asn1Error.Incomplete)
|
||||||
field = Asn1Field(kind: Asn1Tag.Oid, klass: klass,
|
|
||||||
index: ttag, offset: cast[int](ab.offset),
|
field = Asn1Field(kind: Asn1Tag.Oid, klass: aclass,
|
||||||
length: cast[int](length))
|
index: ttag, offset: int(ab.offset),
|
||||||
|
length: int(length))
|
||||||
shallowCopy(field.buffer, ab.buffer)
|
shallowCopy(field.buffer, ab.buffer)
|
||||||
ab.offset += cast[int](length)
|
ab.offset += int(length)
|
||||||
return ok(field)
|
return ok(field)
|
||||||
elif cast[byte](tag) == Asn1Tag.Sequence.code():
|
|
||||||
|
of Asn1Tag.Sequence.code():
|
||||||
# SEQUENCE
|
# SEQUENCE
|
||||||
if not ab.isEnough(cast[int](length)):
|
if not ab.isEnough(int(length)):
|
||||||
return err(Asn1Error.Incomplete)
|
return err(Asn1Error.Incomplete)
|
||||||
field = Asn1Field(kind: Asn1Tag.Sequence, klass: klass,
|
|
||||||
index: ttag, offset: cast[int](ab.offset),
|
field = Asn1Field(kind: Asn1Tag.Sequence, klass: aclass,
|
||||||
length: cast[int](length))
|
index: ttag, offset: int(ab.offset),
|
||||||
|
length: int(length))
|
||||||
shallowCopy(field.buffer, ab.buffer)
|
shallowCopy(field.buffer, ab.buffer)
|
||||||
ab.offset += cast[int](length)
|
ab.offset += int(length)
|
||||||
return ok(field)
|
return ok(field)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return err(Asn1Error.NoSupport)
|
return err(Asn1Error.NoSupport)
|
||||||
|
|
||||||
inclass = false
|
inclass = false
|
||||||
ttag = 0
|
ttag = 0
|
||||||
else:
|
else:
|
||||||
return err(Asn1Error.NoSupport)
|
return err(Asn1Error.NoSupport)
|
||||||
|
|
||||||
proc getBuffer*(field: Asn1Field): Asn1Buffer =
|
proc getBuffer*(field: Asn1Field): Asn1Buffer {.inline.} =
|
||||||
## Return ``field`` as Asn1Buffer to enter composite types.
|
## Return ``field`` as Asn1Buffer to enter composite types.
|
||||||
shallowCopy(result.buffer, field.buffer)
|
Asn1Buffer(buffer: field.buffer, offset: field.offset, length: field.length)
|
||||||
result.offset = field.offset
|
|
||||||
result.length = field.length
|
|
||||||
|
|
||||||
proc `==`*(field: Asn1Field, data: openarray[byte]): bool =
|
proc `==`*(field: Asn1Field, data: openarray[byte]): bool =
|
||||||
## Compares field ``field`` data with ``data`` and returns ``true`` if both
|
## Compares field ``field`` data with ``data`` and returns ``true`` if both
|
||||||
## buffers are equal.
|
## buffers are equal.
|
||||||
let length = len(field.buffer)
|
let length = len(field.buffer)
|
||||||
if length > 0:
|
if length == 0 and len(data) == 0:
|
||||||
if field.length == len(data):
|
true
|
||||||
result = equalMem(unsafeAddr field.buffer[field.offset],
|
else:
|
||||||
unsafeAddr data[0], field.length)
|
if length > 0:
|
||||||
|
if field.length == len(data):
|
||||||
|
CT.isEqual(
|
||||||
|
field.buffer.toOpenArray(field.offset,
|
||||||
|
field.offset + field.length - 1),
|
||||||
|
data.toOpenArray(0, field.length - 1))
|
||||||
|
else:
|
||||||
|
false
|
||||||
|
else:
|
||||||
|
false
|
||||||
|
|
||||||
proc init*(t: typedesc[Asn1Buffer], data: openarray[byte]): Asn1Buffer =
|
proc init*(t: typedesc[Asn1Buffer], data: openarray[byte]): Asn1Buffer =
|
||||||
## Initialize ``Asn1Buffer`` from array of bytes ``data``.
|
## Initialize ``Asn1Buffer`` from array of bytes ``data``.
|
||||||
result.buffer = @data
|
Asn1Buffer(buffer: @data)
|
||||||
|
|
||||||
proc init*(t: typedesc[Asn1Buffer], data: string): Asn1Buffer =
|
proc init*(t: typedesc[Asn1Buffer], data: string): Asn1Buffer =
|
||||||
## Initialize ``Asn1Buffer`` from hexadecimal string ``data``.
|
## Initialize ``Asn1Buffer`` from hexadecimal string ``data``.
|
||||||
result.buffer = ncrutils.fromHex(data)
|
Asn1Buffer(buffer: ncrutils.fromHex(data))
|
||||||
|
|
||||||
proc init*(t: typedesc[Asn1Buffer]): Asn1Buffer =
|
proc init*(t: typedesc[Asn1Buffer]): Asn1Buffer =
|
||||||
## Initialize empty ``Asn1Buffer``.
|
## Initialize empty ``Asn1Buffer``.
|
||||||
result.buffer = newSeq[byte]()
|
Asn1Buffer(buffer: newSeq[byte]())
|
||||||
|
|
||||||
proc init*(t: typedesc[Asn1Composite], tag: Asn1Tag): Asn1Composite =
|
proc init*(t: typedesc[Asn1Composite], tag: Asn1Tag): Asn1Composite =
|
||||||
## Initialize ``Asn1Composite`` with tag ``tag``.
|
## Initialize ``Asn1Composite`` with tag ``tag``.
|
||||||
result.tag = tag
|
Asn1Composite(tag: tag, buffer: newSeq[byte]())
|
||||||
result.buffer = newSeq[byte]()
|
|
||||||
|
|
||||||
proc init*(t: typedesc[Asn1Composite], idx: int): Asn1Composite =
|
proc init*(t: typedesc[Asn1Composite], idx: int): Asn1Composite =
|
||||||
## Initialize ``Asn1Composite`` with tag context-specific id ``id``.
|
## Initialize ``Asn1Composite`` with tag context-specific id ``id``.
|
||||||
result.tag = Asn1Tag.Context
|
Asn1Composite(tag: Asn1Tag.Context, idx: idx, buffer: newSeq[byte]())
|
||||||
result.idx = idx
|
|
||||||
result.buffer = newSeq[byte]()
|
|
||||||
|
|
||||||
proc `$`*(buffer: Asn1Buffer): string =
|
proc `$`*(buffer: Asn1Buffer): string =
|
||||||
## Return string representation of ``buffer``.
|
## Return string representation of ``buffer``.
|
||||||
result = ncrutils.toHex(buffer.toOpenArray())
|
ncrutils.toHex(buffer.toOpenArray())
|
||||||
|
|
||||||
proc `$`*(field: Asn1Field): string =
|
proc `$`*(field: Asn1Field): string =
|
||||||
## Return string representation of ``field``.
|
## Return string representation of ``field``.
|
||||||
result = "["
|
var res = "["
|
||||||
result.add($field.kind)
|
res.add($field.kind)
|
||||||
result.add("]")
|
res.add("]")
|
||||||
if field.kind == Asn1Tag.NoSupport:
|
case field.kind
|
||||||
result.add(" ")
|
of Asn1Tag.Boolean:
|
||||||
result.add(ncrutils.toHex(field.toOpenArray()))
|
res.add(" ")
|
||||||
elif field.kind == Asn1Tag.Boolean:
|
res.add($field.vbool)
|
||||||
result.add(" ")
|
res
|
||||||
result.add($field.vbool)
|
of Asn1Tag.Integer:
|
||||||
elif field.kind == Asn1Tag.Integer:
|
res.add(" ")
|
||||||
result.add(" ")
|
|
||||||
if field.length <= 8:
|
if field.length <= 8:
|
||||||
result.add($field.vint)
|
res.add($field.vint)
|
||||||
else:
|
else:
|
||||||
result.add(ncrutils.toHex(field.toOpenArray()))
|
res.add(ncrutils.toHex(field.toOpenArray()))
|
||||||
elif field.kind == Asn1Tag.BitString:
|
res
|
||||||
result.add(" ")
|
of Asn1Tag.BitString:
|
||||||
result.add("(")
|
res.add(" ")
|
||||||
result.add($field.ubits)
|
res.add("(")
|
||||||
result.add(" bits) ")
|
res.add($field.ubits)
|
||||||
result.add(ncrutils.toHex(field.toOpenArray()))
|
res.add(" bits) ")
|
||||||
elif field.kind == Asn1Tag.OctetString:
|
res.add(ncrutils.toHex(field.toOpenArray()))
|
||||||
result.add(" ")
|
res
|
||||||
result.add(ncrutils.toHex(field.toOpenArray()))
|
of Asn1Tag.OctetString:
|
||||||
elif field.kind == Asn1Tag.Null:
|
res.add(" ")
|
||||||
result.add(" NULL")
|
res.add(ncrutils.toHex(field.toOpenArray()))
|
||||||
elif field.kind == Asn1Tag.Oid:
|
res
|
||||||
result.add(" ")
|
of Asn1Tag.Null:
|
||||||
result.add(ncrutils.toHex(field.toOpenArray()))
|
res.add(" NULL")
|
||||||
elif field.kind == Asn1Tag.Sequence:
|
res
|
||||||
result.add(" ")
|
of Asn1Tag.Oid:
|
||||||
result.add(ncrutils.toHex(field.toOpenArray()))
|
res.add(" ")
|
||||||
|
res.add(ncrutils.toHex(field.toOpenArray()))
|
||||||
|
res
|
||||||
|
of Asn1Tag.Sequence:
|
||||||
|
res.add(" ")
|
||||||
|
res.add(ncrutils.toHex(field.toOpenArray()))
|
||||||
|
res
|
||||||
|
of Asn1Tag.Context:
|
||||||
|
res.add(" ")
|
||||||
|
res.add(ncrutils.toHex(field.toOpenArray()))
|
||||||
|
res
|
||||||
|
else:
|
||||||
|
res.add(" ")
|
||||||
|
res.add(ncrutils.toHex(field.toOpenArray()))
|
||||||
|
res
|
||||||
|
|
||||||
proc write*[T: Asn1Buffer|Asn1Composite](abc: var T, tag: Asn1Tag) =
|
proc write*[T: Asn1Buffer|Asn1Composite](abc: var T, tag: Asn1Tag) =
|
||||||
## Write empty value to buffer or composite with ``tag``.
|
## Write empty value to buffer or composite with ``tag``.
|
||||||
|
@ -656,7 +788,7 @@ proc write*[T: Asn1Buffer|Asn1Composite](abc: var T, tag: Asn1Tag) =
|
||||||
## This procedure must be used to write `NULL`, `0` or empty `BIT STRING`,
|
## This procedure must be used to write `NULL`, `0` or empty `BIT STRING`,
|
||||||
## `OCTET STRING` types.
|
## `OCTET STRING` types.
|
||||||
doAssert(tag in {Asn1Tag.Null, Asn1Tag.Integer, Asn1Tag.BitString,
|
doAssert(tag in {Asn1Tag.Null, Asn1Tag.Integer, Asn1Tag.BitString,
|
||||||
Asn1Tag.OctetString})
|
Asn1Tag.OctetString})
|
||||||
var length: int
|
var length: int
|
||||||
if tag == Asn1Tag.Null:
|
if tag == Asn1Tag.Null:
|
||||||
length = asn1EncodeNull(abc.toOpenArray())
|
length = asn1EncodeNull(abc.toOpenArray())
|
||||||
|
|
|
@ -0,0 +1,214 @@
|
||||||
|
## Nim-Libp2p
|
||||||
|
## Copyright (c) 2018 Status Research & Development GmbH
|
||||||
|
## Licensed under either of
|
||||||
|
## * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
||||||
|
## * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
||||||
|
## at your option.
|
||||||
|
## This file may not be copied, modified, or distributed except according to
|
||||||
|
## those terms.
|
||||||
|
import unittest
|
||||||
|
import ../libp2p/crypto/minasn1
|
||||||
|
import nimcrypto/utils as ncrutils
|
||||||
|
|
||||||
|
when defined(nimHasUsed): {.used.}
|
||||||
|
|
||||||
|
const Asn1EdgeValues = [
|
||||||
|
0'u64, (1'u64 shl 7) - 1'u64,
|
||||||
|
(1'u64 shl 7), (1'u64 shl 8) - 1'u64,
|
||||||
|
(1'u64 shl 8), (1'u64 shl 16) - 1'u64,
|
||||||
|
(1'u64 shl 16), (1'u64 shl 24) - 1'u64,
|
||||||
|
(1'u64 shl 24), (1'u64 shl 32) - 1'u64,
|
||||||
|
(1'u64 shl 32), (1'u64 shl 40) - 1'u64,
|
||||||
|
(1'u64 shl 40), (1'u64 shl 48) - 1'u64,
|
||||||
|
(1'u64 shl 48), (1'u64 shl 56) - 1'u64,
|
||||||
|
(1'u64 shl 56), 0xFFFF_FFFF_FFFF_FFFF'u64
|
||||||
|
]
|
||||||
|
|
||||||
|
const Asn1EdgeExpects = [
|
||||||
|
"00", "7F",
|
||||||
|
"8180", "81FF",
|
||||||
|
"820100", "82FFFF",
|
||||||
|
"83010000", "83FFFFFF",
|
||||||
|
"8401000000", "84FFFFFFFF",
|
||||||
|
"850100000000", "85FFFFFFFFFF",
|
||||||
|
"86010000000000", "86FFFFFFFFFFFF",
|
||||||
|
"8701000000000000", "87FFFFFFFFFFFFFF",
|
||||||
|
"880100000000000000", "88FFFFFFFFFFFFFFFF",
|
||||||
|
]
|
||||||
|
|
||||||
|
const Asn1UIntegerValues8 = [
|
||||||
|
0x00'u8, 0x7F'u8, 0x80'u8, 0xFF'u8,
|
||||||
|
]
|
||||||
|
|
||||||
|
const Asn1UIntegerExpects8 = [
|
||||||
|
"020100", "02017F", "02020080", "020200FF"
|
||||||
|
]
|
||||||
|
|
||||||
|
const Asn1UIntegerValues16 = [
|
||||||
|
0x00'u16, 0x7F'u16, 0x80'u16, 0xFF'u16,
|
||||||
|
0x7FFF'u16, 0x8000'u16, 0xFFFF'u16
|
||||||
|
]
|
||||||
|
|
||||||
|
const Asn1UIntegerExpects16 = [
|
||||||
|
"020100", "02017F", "02020080", "020200FF", "02027FFF",
|
||||||
|
"0203008000", "020300FFFF"
|
||||||
|
]
|
||||||
|
|
||||||
|
const Asn1UIntegerValues32 = [
|
||||||
|
0x00'u32, 0x7F'u32, 0x80'u32, 0xFF'u32,
|
||||||
|
0x7FFF'u32, 0x8000'u32, 0xFFFF'u32,
|
||||||
|
0x7FFF_FFFF'u32, 0x8000_0000'u32, 0xFFFF_FFFF'u32
|
||||||
|
]
|
||||||
|
|
||||||
|
const Asn1UIntegerExpects32 = [
|
||||||
|
"020100", "02017F", "02020080", "020200FF", "02027FFF",
|
||||||
|
"0203008000", "020300FFFF", "02047FFFFFFF", "02050080000000",
|
||||||
|
"020500FFFFFFFF"
|
||||||
|
]
|
||||||
|
|
||||||
|
const Asn1UIntegerValues64 = [
|
||||||
|
0x00'u64, 0x7F'u64, 0x80'u64, 0xFF'u64,
|
||||||
|
0x7FFF'u64, 0x8000'u64, 0xFFFF'u64,
|
||||||
|
0x7FFF_FFFF'u64, 0x8000_0000'u64, 0xFFFF_FFFF'u64,
|
||||||
|
0x7FFF_FFFF_FFFF_FFFF'u64, 0x8000_0000_0000_0000'u64,
|
||||||
|
0xFFFF_FFFF_FFFF_FFFF'u64
|
||||||
|
]
|
||||||
|
|
||||||
|
const Asn1UIntegerExpects64 = [
|
||||||
|
"020100", "02017F", "02020080", "020200FF", "02027FFF",
|
||||||
|
"0203008000", "020300FFFF", "02047FFFFFFF", "02050080000000",
|
||||||
|
"020500FFFFFFFF", "02087FFFFFFFFFFFFFFF", "0209008000000000000000",
|
||||||
|
"020900FFFFFFFFFFFFFFFF"
|
||||||
|
]
|
||||||
|
|
||||||
|
suite "Minimal ASN.1 encode/decode suite":
|
||||||
|
test "Length encoding edge values":
|
||||||
|
var empty = newSeq[byte](0)
|
||||||
|
for i in 0 ..< len(Asn1EdgeValues):
|
||||||
|
var value = newSeq[byte](9)
|
||||||
|
let r1 = asn1EncodeLength(empty, Asn1EdgeValues[i])
|
||||||
|
let r2 = asn1EncodeLength(value, Asn1EdgeValues[i])
|
||||||
|
value.setLen(r2)
|
||||||
|
check:
|
||||||
|
r1 == (len(Asn1EdgeExpects[i]) shr 1)
|
||||||
|
r2 == (len(Asn1EdgeExpects[i]) shr 1)
|
||||||
|
check:
|
||||||
|
ncrutils.fromHex(Asn1EdgeExpects[i]) == value
|
||||||
|
|
||||||
|
test "ASN.1 DER INTEGER encoding/decoding of native unsigned values test":
|
||||||
|
proc decodeBuffer(data: openarray[byte]): uint64 =
|
||||||
|
var ab = Asn1Buffer.init(data)
|
||||||
|
let fres = ab.read()
|
||||||
|
doAssert(fres.isOk() and fres.get().kind == Asn1Tag.Integer)
|
||||||
|
fres.get().vint
|
||||||
|
|
||||||
|
proc encodeInteger[T](value: T): seq[byte] =
|
||||||
|
var buffer = newSeq[byte](16)
|
||||||
|
let res = asn1EncodeInteger(buffer, value)
|
||||||
|
buffer.setLen(res)
|
||||||
|
buffer
|
||||||
|
|
||||||
|
for i in 0 ..< len(Asn1UIntegerValues8):
|
||||||
|
let buffer = encodeInteger(Asn1UIntegerValues8[i])
|
||||||
|
check:
|
||||||
|
toHex(buffer) == Asn1UIntegerExpects8[i]
|
||||||
|
decodeBuffer(buffer) == uint64(Asn1UIntegerValues8[i])
|
||||||
|
|
||||||
|
for i in 0 ..< len(Asn1UIntegerValues16):
|
||||||
|
let buffer = encodeInteger(Asn1UIntegerValues16[i])
|
||||||
|
check:
|
||||||
|
toHex(buffer) == Asn1UIntegerExpects16[i]
|
||||||
|
decodeBuffer(buffer) == uint64(Asn1UIntegerValues16[i])
|
||||||
|
|
||||||
|
for i in 0 ..< len(Asn1UIntegerValues32):
|
||||||
|
let buffer = encodeInteger(Asn1UIntegerValues32[i])
|
||||||
|
check:
|
||||||
|
toHex(buffer) == Asn1UIntegerExpects32[i]
|
||||||
|
decodeBuffer(buffer) == uint64(Asn1UIntegerValues32[i])
|
||||||
|
|
||||||
|
for i in 0 ..< len(Asn1UIntegerValues64):
|
||||||
|
let buffer = encodeInteger(Asn1UIntegerValues64[i])
|
||||||
|
check:
|
||||||
|
toHex(buffer) == Asn1UIntegerExpects64[i]
|
||||||
|
decodeBuffer(buffer) == uint64(Asn1UIntegerValues64[i])
|
||||||
|
|
||||||
|
test "ASN.1 DER INTEGER incorrect values decoding test":
|
||||||
|
proc decodeBuffer(data: string): Asn1Result[Asn1Field] =
|
||||||
|
var ab = Asn1Buffer.init(fromHex(data))
|
||||||
|
ab.read()
|
||||||
|
|
||||||
|
check:
|
||||||
|
decodeBuffer("0200").error == Asn1Error.Incorrect
|
||||||
|
decodeBuffer("0201").error == Asn1Error.Incomplete
|
||||||
|
decodeBuffer("02020000").error == Asn1Error.Incorrect
|
||||||
|
decodeBuffer("0203000001").error == Asn1Error.Incorrect
|
||||||
|
|
||||||
|
test "ASN.1 DER BITSTRING encoding/decoding with unused bits test":
|
||||||
|
proc encodeBits(value: string, bitsUsed: int): seq[byte] =
|
||||||
|
var buffer = newSeq[byte](16)
|
||||||
|
let res = asn1EncodeBitString(buffer, fromHex(value), bitsUsed)
|
||||||
|
buffer.setLen(res)
|
||||||
|
buffer
|
||||||
|
|
||||||
|
proc decodeBuffer(data: string): Asn1Field =
|
||||||
|
var ab = Asn1Buffer.init(fromHex(data))
|
||||||
|
let fres = ab.read()
|
||||||
|
doAssert(fres.isOk() and fres.get().kind == Asn1Tag.BitString)
|
||||||
|
fres.get()
|
||||||
|
|
||||||
|
check:
|
||||||
|
toHex(encodeBits("FF", 7)) == "03020780"
|
||||||
|
toHex(encodeBits("FF", 6)) == "030206C0"
|
||||||
|
toHex(encodeBits("FF", 5)) == "030205E0"
|
||||||
|
toHex(encodeBits("FF", 4)) == "030204F0"
|
||||||
|
toHex(encodeBits("FF", 3)) == "030203F8"
|
||||||
|
toHex(encodeBits("FF", 2)) == "030202FC"
|
||||||
|
toHex(encodeBits("FF", 1)) == "030201FE"
|
||||||
|
toHex(encodeBits("FF", 0)) == "030200FF"
|
||||||
|
|
||||||
|
let f0 = decodeBuffer("030200FF")
|
||||||
|
let f0b = @(f0.buffer.toOpenArray(f0.offset, f0.offset + f0.length - 1))
|
||||||
|
let f1 = decodeBuffer("030201FE")
|
||||||
|
let f1b = @(f1.buffer.toOpenArray(f1.offset, f1.offset + f1.length - 1))
|
||||||
|
let f2 = decodeBuffer("030202FC")
|
||||||
|
let f2b = @(f2.buffer.toOpenArray(f2.offset, f2.offset + f2.length - 1))
|
||||||
|
let f3 = decodeBuffer("030203F8")
|
||||||
|
let f3b = @(f3.buffer.toOpenArray(f3.offset, f3.offset + f3.length - 1))
|
||||||
|
let f4 = decodeBuffer("030204F0")
|
||||||
|
let f4b = @(f4.buffer.toOpenArray(f4.offset, f4.offset + f4.length - 1))
|
||||||
|
let f5 = decodeBuffer("030205E0")
|
||||||
|
let f5b = @(f5.buffer.toOpenArray(f5.offset, f5.offset + f5.length - 1))
|
||||||
|
let f6 = decodeBuffer("030206C0")
|
||||||
|
let f6b = @(f6.buffer.toOpenArray(f6.offset, f6.offset + f6.length - 1))
|
||||||
|
let f7 = decodeBuffer("03020780")
|
||||||
|
let f7b = @(f7.buffer.toOpenArray(f7.offset, f7.offset + f7.length - 1))
|
||||||
|
|
||||||
|
check:
|
||||||
|
f0.ubits == 0
|
||||||
|
toHex(f0b) == "FF"
|
||||||
|
f1.ubits == 1
|
||||||
|
toHex(f1b) == "FE"
|
||||||
|
f2.ubits == 2
|
||||||
|
toHex(f2b) == "FC"
|
||||||
|
f3.ubits == 3
|
||||||
|
toHex(f3b) == "F8"
|
||||||
|
f4.ubits == 4
|
||||||
|
toHex(f4b) == "F0"
|
||||||
|
f5.ubits == 5
|
||||||
|
toHex(f5b) == "E0"
|
||||||
|
f6.ubits == 6
|
||||||
|
toHex(f6b) == "C0"
|
||||||
|
f7.ubits == 7
|
||||||
|
toHex(f7b) == "80"
|
||||||
|
|
||||||
|
test "ASN.1 DER BITSTRING incorrect values decoding test":
|
||||||
|
proc decodeBuffer(data: string): Asn1Result[Asn1Field] =
|
||||||
|
var ab = Asn1Buffer.init(fromHex(data))
|
||||||
|
ab.read()
|
||||||
|
|
||||||
|
check:
|
||||||
|
decodeBuffer("0300").error == Asn1Error.Incorrect
|
||||||
|
decodeBuffer("030180").error == Asn1Error.Incorrect
|
||||||
|
decodeBuffer("030107").error == Asn1Error.Incorrect
|
||||||
|
decodeBuffer("030200").error == Asn1Error.Incomplete
|
||||||
|
decodeBuffer("030208FF").error == Asn1Error.Incorrect
|
|
@ -2,7 +2,8 @@ import testvarint,
|
||||||
testminprotobuf,
|
testminprotobuf,
|
||||||
teststreamseq
|
teststreamseq
|
||||||
|
|
||||||
import testrsa,
|
import testminasn1,
|
||||||
|
testrsa,
|
||||||
testecnist,
|
testecnist,
|
||||||
tested25519,
|
tested25519,
|
||||||
testsecp256k1,
|
testsecp256k1,
|
||||||
|
|
Loading…
Reference in New Issue