Use constant-time hex encoding/decoding procedures explicitly. (#305)

* Use constant-time hex encoding/decoding procedures explicitly.

* Add comments.
This commit is contained in:
Eugene Kabanov 2020-08-11 17:48:21 +03:00 committed by GitHub
parent 2325692f55
commit d47b2d805f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 96 additions and 94 deletions

View File

@ -70,13 +70,15 @@ when supported(PKScheme.Secp256k1):
import ecnist, bearssl
import ../protobuf/minprotobuf, ../vbuffer, ../multihash, ../multicodec
import nimcrypto/[rijndael, twofish, sha2, hash, hmac, utils]
import nimcrypto/[rijndael, twofish, sha2, hash, hmac]
# We use `ncrutils` for constant-time hexadecimal encoding/decoding procedures.
import nimcrypto/utils as ncrutils
import ../utility
import stew/results
export results
# This is workaround for Nim's `import` bug
export rijndael, twofish, sha2, hash, hmac, utils
export rijndael, twofish, sha2, hash, hmac, ncrutils
from strutils import split
@ -514,20 +516,14 @@ proc init*[T: PrivateKey|PublicKey](key: var T, data: string): bool =
## hexadecimal string representation.
##
## Returns ``true`` on success.
try:
key.init(utils.fromHex(data))
except ValueError:
false
key.init(ncrutils.fromHex(data))
proc init*(sig: var Signature, data: string): bool =
## Initialize signature ``sig`` from serialized hexadecimal string
## representation.
##
## Returns ``true`` on success.
try:
sig.init(utils.fromHex(data))
except ValueError:
false
sig.init(ncrutils.fromHex(data))
proc init*(t: typedesc[PrivateKey],
data: openarray[byte]): CryptoResult[PrivateKey] =
@ -559,10 +555,7 @@ proc init*(t: typedesc[Signature],
proc init*(t: typedesc[PrivateKey], data: string): CryptoResult[PrivateKey] =
## Create new private key from libp2p's protobuf serialized hexadecimal string
## form.
try:
t.init(utils.fromHex(data))
except ValueError:
err(KeyError)
t.init(ncrutils.fromHex(data))
when supported(PKScheme.RSA):
proc init*(t: typedesc[PrivateKey], key: rsa.RsaPrivateKey): PrivateKey =
@ -591,17 +584,11 @@ when supported(PKScheme.ECDSA):
proc init*(t: typedesc[PublicKey], data: string): CryptoResult[PublicKey] =
## Create new public key from libp2p's protobuf serialized hexadecimal string
## form.
try:
t.init(utils.fromHex(data))
except ValueError:
err(KeyError)
t.init(ncrutils.fromHex(data))
proc init*(t: typedesc[Signature], data: string): CryptoResult[Signature] =
## Create new signature from serialized hexadecimal string form.
try:
t.init(utils.fromHex(data))
except ValueError:
err(SigError)
t.init(ncrutils.fromHex(data))
proc `==`*(key1, key2: PublicKey): bool {.inline.} =
## Return ``true`` if two public keys ``key1`` and ``key2`` of the same
@ -709,7 +696,7 @@ func shortLog*(key: PrivateKey|PublicKey): string =
proc `$`*(sig: Signature): string =
## Get string representation of signature ``sig``.
result = toHex(sig.data)
result = ncrutils.toHex(sig.data)
proc sign*(key: PrivateKey,
data: openarray[byte]): CryptoResult[Signature] {.gcsafe.} =

View File

@ -17,7 +17,8 @@
{.push raises: [Defect].}
import bearssl
import nimcrypto/utils
# We use `ncrutils` for constant-time hexadecimal encoding/decoding procedures.
import nimcrypto/utils as ncrutils
import minasn1
export minasn1.Asn1Error
import stew/[results, ctops]
@ -289,7 +290,7 @@ proc `$`*(seckey: EcPrivateKey): string =
result = "Corrupted key"
else:
let e = offset + cast[int](seckey.key.xlen) - 1
result = toHex(seckey.buffer.toOpenArray(offset, e))
result = ncrutils.toHex(seckey.buffer.toOpenArray(offset, e))
proc `$`*(pubkey: EcPublicKey): string =
## Return string representation of EC public key.
@ -305,14 +306,14 @@ proc `$`*(pubkey: EcPublicKey): string =
result = "Corrupted key"
else:
let e = offset + cast[int](pubkey.key.qlen) - 1
result = toHex(pubkey.buffer.toOpenArray(offset, e))
result = ncrutils.toHex(pubkey.buffer.toOpenArray(offset, e))
proc `$`*(sig: EcSignature): string =
## Return hexadecimal string representation of EC signature.
if isNil(sig) or len(sig.buffer) == 0:
result = "Empty or uninitialized ECNIST signature"
else:
result = toHex(sig.buffer)
result = ncrutils.toHex(sig.buffer)
proc toRawBytes*(seckey: EcPrivateKey, data: var openarray[byte]): EcResult[int] =
## Serialize EC private key ``seckey`` to raw binary form and store it
@ -708,14 +709,16 @@ proc init*(sig: var EcSignature, data: openarray[byte]): Result[void, Asn1Error]
else:
err(Asn1Error.Incorrect)
proc init*[T: EcPKI](sospk: var T, data: string): Result[void, Asn1Error] {.inline.} =
proc init*[T: EcPKI](sospk: var T,
data: string): Result[void, Asn1Error] {.inline.} =
## Initialize EC `private key`, `public key` or `signature` ``sospk`` from
## ASN.1 DER hexadecimal string representation ``data``.
##
## Procedure returns ``Asn1Status``.
sospk.init(fromHex(data))
sospk.init(ncrutils.fromHex(data))
proc init*(t: typedesc[EcPrivateKey], data: openarray[byte]): EcResult[EcPrivateKey] =
proc init*(t: typedesc[EcPrivateKey],
data: openarray[byte]): EcResult[EcPrivateKey] =
## Initialize EC private key from ASN.1 DER binary representation ``data`` and
## return constructed object.
var key: EcPrivateKey
@ -725,7 +728,8 @@ proc init*(t: typedesc[EcPrivateKey], data: openarray[byte]): EcResult[EcPrivate
else:
ok(key)
proc init*(t: typedesc[EcPublicKey], data: openarray[byte]): EcResult[EcPublicKey] =
proc init*(t: typedesc[EcPublicKey],
data: openarray[byte]): EcResult[EcPublicKey] =
## Initialize EC public key from ASN.1 DER binary representation ``data`` and
## return constructed object.
var key: EcPublicKey
@ -735,7 +739,8 @@ proc init*(t: typedesc[EcPublicKey], data: openarray[byte]): EcResult[EcPublicKe
else:
ok(key)
proc init*(t: typedesc[EcSignature], data: openarray[byte]): EcResult[EcSignature] =
proc init*(t: typedesc[EcSignature],
data: openarray[byte]): EcResult[EcSignature] =
## Initialize EC signature from raw binary representation ``data`` and
## return constructed object.
var sig: EcSignature
@ -748,10 +753,7 @@ proc init*(t: typedesc[EcSignature], data: openarray[byte]): EcResult[EcSignatur
proc init*[T: EcPKI](t: typedesc[T], data: string): EcResult[T] =
## Initialize EC `private key`, `public key` or `signature` from hexadecimal
## string representation ``data`` and return constructed object.
try:
t.init(fromHex(data))
except ValueError:
err(EcKeyIncorrectError)
t.init(ncrutils.fromHex(data))
proc initRaw*(key: var EcPrivateKey, data: openarray[byte]): bool =
## Initialize EC `private key` or `scalar` ``key`` from raw binary
@ -833,9 +835,10 @@ proc initRaw*[T: EcPKI](sospk: var T, data: string): bool {.inline.} =
## raw hexadecimal string representation ``data``.
##
## Procedure returns ``true`` on success, ``false`` otherwise.
result = sospk.initRaw(fromHex(data))
result = sospk.initRaw(ncrutils.fromHex(data))
proc initRaw*(t: typedesc[EcPrivateKey], data: openarray[byte]): EcResult[EcPrivateKey] =
proc initRaw*(t: typedesc[EcPrivateKey],
data: openarray[byte]): EcResult[EcPrivateKey] =
## Initialize EC private key from raw binary representation ``data`` and
## return constructed object.
var res: EcPrivateKey
@ -844,7 +847,8 @@ proc initRaw*(t: typedesc[EcPrivateKey], data: openarray[byte]): EcResult[EcPriv
else:
ok(res)
proc initRaw*(t: typedesc[EcPublicKey], data: openarray[byte]): EcResult[EcPublicKey] =
proc initRaw*(t: typedesc[EcPublicKey],
data: openarray[byte]): EcResult[EcPublicKey] =
## Initialize EC public key from raw binary representation ``data`` and
## return constructed object.
var res: EcPublicKey
@ -853,7 +857,8 @@ proc initRaw*(t: typedesc[EcPublicKey], data: openarray[byte]): EcResult[EcPubli
else:
ok(res)
proc initRaw*(t: typedesc[EcSignature], data: openarray[byte]): EcResult[EcSignature] =
proc initRaw*(t: typedesc[EcSignature],
data: openarray[byte]): EcResult[EcSignature] =
## Initialize EC signature from raw binary representation ``data`` and
## return constructed object.
var res: EcSignature
@ -865,7 +870,7 @@ proc initRaw*(t: typedesc[EcSignature], data: openarray[byte]): EcResult[EcSigna
proc initRaw*[T: EcPKI](t: typedesc[T], data: string): T {.inline.} =
## Initialize EC `private key`, `public key` or `signature` from raw
## hexadecimal string representation ``data`` and return constructed object.
result = t.initRaw(fromHex(data))
result = t.initRaw(ncrutils.fromHex(data))
proc scalarMul*(pub: EcPublicKey, sec: EcPrivateKey): EcPublicKey =
## Return scalar multiplication of ``pub`` and ``sec``.
@ -926,7 +931,7 @@ proc getSecret*(pubkey: EcPublicKey, seckey: EcPrivateKey): seq[byte] =
copyMem(addr result[0], addr data[0], res)
proc sign*[T: byte|char](seckey: EcPrivateKey,
message: openarray[T]): EcResult[EcSignature] {.gcsafe.} =
message: openarray[T]): EcResult[EcSignature] {.gcsafe.} =
## Get ECDSA signature of data ``message`` using private key ``seckey``.
if isNil(seckey):
return err(EcKeyIncorrectError)

View File

@ -14,7 +14,9 @@
{.push raises: Defect.}
import constants, bearssl
import nimcrypto/[hash, sha2, utils]
import nimcrypto/[hash, sha2]
# We use `ncrutils` for constant-time hexadecimal encoding/decoding procedures.
import nimcrypto/utils as ncrutils
import stew/[results, ctops]
export results
@ -1735,14 +1737,17 @@ proc `==`*(eda, edb: EdSignature): bool =
## Compare ED25519 `signature` objects for equality.
result = CT.isEqual(eda.data, edb.data)
proc `$`*(key: EdPrivateKey): string = toHex(key.data)
proc `$`*(key: EdPrivateKey): string =
## Return string representation of ED25519 `private key`.
ncrutils.toHex(key.data)
proc `$`*(key: EdPublicKey): string = toHex(key.data)
proc `$`*(key: EdPublicKey): string =
## Return string representation of ED25519 `private key`.
ncrutils.toHex(key.data)
proc `$`*(sig: EdSignature): string = toHex(sig.data)
proc `$`*(sig: EdSignature): string =
## Return string representation of ED25519 `signature`.
ncrutils.toHex(sig.data)
proc init*(key: var EdPrivateKey, data: openarray[byte]): bool =
## Initialize ED25519 `private key` ``key`` from raw binary
@ -1779,32 +1784,24 @@ proc init*(key: var EdPrivateKey, data: string): bool =
## representation ``data``.
##
## Procedure returns ``true`` on success.
try:
init(key, fromHex(data))
except ValueError:
false
init(key, ncrutils.fromHex(data))
proc init*(key: var EdPublicKey, data: string): bool =
## Initialize ED25519 `public key` ``key`` from hexadecimal string
## representation ``data``.
##
## Procedure returns ``true`` on success.
try:
init(key, fromHex(data))
except ValueError:
false
init(key, ncrutils.fromHex(data))
proc init*(sig: var EdSignature, data: string): bool =
## Initialize ED25519 `signature` ``sig`` from hexadecimal string
## representation ``data``.
##
## Procedure returns ``true`` on success.
try:
init(sig, fromHex(data))
except ValueError:
false
init(sig, ncrutils.fromHex(data))
proc init*(t: typedesc[EdPrivateKey], data: openarray[byte]): Result[EdPrivateKey, EdError] =
proc init*(t: typedesc[EdPrivateKey],
data: openarray[byte]): Result[EdPrivateKey, EdError] =
## Initialize ED25519 `private key` from raw binary representation ``data``
## and return constructed object.
var res: t
@ -1813,7 +1810,8 @@ proc init*(t: typedesc[EdPrivateKey], data: openarray[byte]): Result[EdPrivateKe
else:
ok(res)
proc init*(t: typedesc[EdPublicKey], data: openarray[byte]): Result[EdPublicKey, EdError] =
proc init*(t: typedesc[EdPublicKey],
data: openarray[byte]): Result[EdPublicKey, EdError] =
## Initialize ED25519 `public key` from raw binary representation ``data``
## and return constructed object.
var res: t
@ -1822,7 +1820,8 @@ proc init*(t: typedesc[EdPublicKey], data: openarray[byte]): Result[EdPublicKey,
else:
ok(res)
proc init*(t: typedesc[EdSignature], data: openarray[byte]): Result[EdSignature, EdError] =
proc init*(t: typedesc[EdSignature],
data: openarray[byte]): Result[EdSignature, EdError] =
## Initialize ED25519 `signature` from raw binary representation ``data``
## and return constructed object.
var res: t
@ -1831,7 +1830,8 @@ proc init*(t: typedesc[EdSignature], data: openarray[byte]): Result[EdSignature,
else:
ok(res)
proc init*(t: typedesc[EdPrivateKey], data: string): Result[EdPrivateKey, EdError] =
proc init*(t: typedesc[EdPrivateKey],
data: string): Result[EdPrivateKey, EdError] =
## Initialize ED25519 `private key` from hexadecimal string representation
## ``data`` and return constructed object.
var res: t
@ -1840,7 +1840,8 @@ proc init*(t: typedesc[EdPrivateKey], data: string): Result[EdPrivateKey, EdErro
else:
ok(res)
proc init*(t: typedesc[EdPublicKey], data: string): Result[EdPublicKey, EdError] =
proc init*(t: typedesc[EdPublicKey],
data: string): Result[EdPublicKey, EdError] =
## Initialize ED25519 `public key` from hexadecimal string representation
## ``data`` and return constructed object.
var res: t
@ -1849,7 +1850,8 @@ proc init*(t: typedesc[EdPublicKey], data: string): Result[EdPublicKey, EdError]
else:
ok(res)
proc init*(t: typedesc[EdSignature], data: string): Result[EdSignature, EdError] =
proc init*(t: typedesc[EdSignature],
data: string): Result[EdSignature, EdError] =
## Initialize ED25519 `signature` from hexadecimal string representation
## ``data`` and return constructed object.
var res: t

View File

@ -13,7 +13,8 @@
import stew/[endians2, results]
export results
import nimcrypto/utils
# We use `ncrutils` for constant-time hexadecimal encoding/decoding procedures.
import nimcrypto/utils as ncrutils
type
Asn1Error* {.pure.} = enum
@ -593,7 +594,7 @@ proc init*(t: typedesc[Asn1Buffer], data: openarray[byte]): Asn1Buffer =
proc init*(t: typedesc[Asn1Buffer], data: string): Asn1Buffer =
## Initialize ``Asn1Buffer`` from hexadecimal string ``data``.
result.buffer = fromHex(data)
result.buffer = ncrutils.fromHex(data)
proc init*(t: typedesc[Asn1Buffer]): Asn1Buffer =
## Initialize empty ``Asn1Buffer``.
@ -612,7 +613,7 @@ proc init*(t: typedesc[Asn1Composite], idx: int): Asn1Composite =
proc `$`*(buffer: Asn1Buffer): string =
## Return string representation of ``buffer``.
result = toHex(buffer.toOpenArray())
result = ncrutils.toHex(buffer.toOpenArray())
proc `$`*(field: Asn1Field): string =
## Return string representation of ``field``.
@ -621,7 +622,7 @@ proc `$`*(field: Asn1Field): string =
result.add("]")
if field.kind == Asn1Tag.NoSupport:
result.add(" ")
result.add(toHex(field.toOpenArray()))
result.add(ncrutils.toHex(field.toOpenArray()))
elif field.kind == Asn1Tag.Boolean:
result.add(" ")
result.add($field.vbool)
@ -630,24 +631,24 @@ proc `$`*(field: Asn1Field): string =
if field.length <= 8:
result.add($field.vint)
else:
result.add(toHex(field.toOpenArray()))
result.add(ncrutils.toHex(field.toOpenArray()))
elif field.kind == Asn1Tag.BitString:
result.add(" ")
result.add("(")
result.add($field.ubits)
result.add(" bits) ")
result.add(toHex(field.toOpenArray()))
result.add(ncrutils.toHex(field.toOpenArray()))
elif field.kind == Asn1Tag.OctetString:
result.add(" ")
result.add(toHex(field.toOpenArray()))
result.add(ncrutils.toHex(field.toOpenArray()))
elif field.kind == Asn1Tag.Null:
result.add(" NULL")
elif field.kind == Asn1Tag.Oid:
result.add(" ")
result.add(toHex(field.toOpenArray()))
result.add(ncrutils.toHex(field.toOpenArray()))
elif field.kind == Asn1Tag.Sequence:
result.add(" ")
result.add(toHex(field.toOpenArray()))
result.add(ncrutils.toHex(field.toOpenArray()))
proc write*[T: Asn1Buffer|Asn1Composite](abc: var T, tag: Asn1Tag) =
## Write empty value to buffer or composite with ``tag``.

View File

@ -14,13 +14,13 @@
## Copyright(C) 2018 Thomas Pornin <pornin@bolet.org>.
{.push raises: Defect.}
import nimcrypto/utils
import bearssl
import minasn1
export Asn1Error
import stew/[results, ctops]
export results
# We use `ncrutils` for constant-time hexadecimal encoding/decoding procedures.
import nimcrypto/utils as ncrutils
export Asn1Error, results
const
DefaultPublicExponent* = 65537'u32
@ -574,14 +574,16 @@ proc init*(sig: var RsaSignature, data: openarray[byte]): Result[void, Asn1Error
else:
err(Asn1Error.Incorrect)
proc init*[T: RsaPKI](sospk: var T, data: string): Result[void, Asn1Error] {.inline.} =
proc init*[T: RsaPKI](sospk: var T,
data: string): Result[void, Asn1Error] {.inline.} =
## Initialize EC `private key`, `public key` or `scalar` ``sospk`` from
## hexadecimal string representation ``data``.
##
## Procedure returns ``Result[void, Asn1Status]``.
sospk.init(fromHex(data))
sospk.init(ncrutils.fromHex(data))
proc init*(t: typedesc[RsaPrivateKey], data: openarray[byte]): RsaResult[RsaPrivateKey] =
proc init*(t: typedesc[RsaPrivateKey],
data: openarray[byte]): RsaResult[RsaPrivateKey] =
## Initialize RSA private key from ASN.1 DER binary representation ``data``
## and return constructed object.
var res: RsaPrivateKey
@ -590,7 +592,8 @@ proc init*(t: typedesc[RsaPrivateKey], data: openarray[byte]): RsaResult[RsaPriv
else:
ok(res)
proc init*(t: typedesc[RsaPublicKey], data: openarray[byte]): RsaResult[RsaPublicKey] =
proc init*(t: typedesc[RsaPublicKey],
data: openarray[byte]): RsaResult[RsaPublicKey] =
## Initialize RSA public key from ASN.1 DER binary representation ``data``
## and return constructed object.
var res: RsaPublicKey
@ -599,7 +602,8 @@ proc init*(t: typedesc[RsaPublicKey], data: openarray[byte]): RsaResult[RsaPubli
else:
ok(res)
proc init*(t: typedesc[RsaSignature], data: openarray[byte]): RsaResult[RsaSignature] =
proc init*(t: typedesc[RsaSignature],
data: openarray[byte]): RsaResult[RsaSignature] =
## Initialize RSA signature from raw binary representation ``data`` and
## return constructed object.
var res: RsaSignature
@ -611,7 +615,7 @@ proc init*(t: typedesc[RsaSignature], data: openarray[byte]): RsaResult[RsaSigna
proc init*[T: RsaPKI](t: typedesc[T], data: string): T {.inline.} =
## Initialize RSA `private key`, `public key` or `signature` from hexadecimal
## string representation ``data`` and return constructed object.
result = t.init(fromHex(data))
result = t.init(ncrutils.fromHex(data))
proc `$`*(key: RsaPrivateKey): string =
## Return string representation of RSA private key.
@ -622,21 +626,24 @@ proc `$`*(key: RsaPrivateKey): string =
result.add($key.seck.nBitlen)
result.add(" bits)\n")
result.add("p = ")
result.add(toHex(getArray(key.buffer, key.seck.p, key.seck.plen)))
result.add(ncrutils.toHex(getArray(key.buffer, key.seck.p, key.seck.plen)))
result.add("\nq = ")
result.add(toHex(getArray(key.buffer, key.seck.q, key.seck.qlen)))
result.add(ncrutils.toHex(getArray(key.buffer, key.seck.q, key.seck.qlen)))
result.add("\ndp = ")
result.add(toHex(getArray(key.buffer, key.seck.dp, key.seck.dplen)))
result.add(ncrutils.toHex(getArray(key.buffer, key.seck.dp,
key.seck.dplen)))
result.add("\ndq = ")
result.add(toHex(getArray(key.buffer, key.seck.dq, key.seck.dqlen)))
result.add(ncrutils.toHex(getArray(key.buffer, key.seck.dq,
key.seck.dqlen)))
result.add("\niq = ")
result.add(toHex(getArray(key.buffer, key.seck.iq, key.seck.iqlen)))
result.add(ncrutils.toHex(getArray(key.buffer, key.seck.iq,
key.seck.iqlen)))
result.add("\npre = ")
result.add(toHex(getArray(key.buffer, key.pexp, key.pexplen)))
result.add(ncrutils.toHex(getArray(key.buffer, key.pexp, key.pexplen)))
result.add("\nm = ")
result.add(toHex(getArray(key.buffer, key.pubk.n, key.pubk.nlen)))
result.add(ncrutils.toHex(getArray(key.buffer, key.pubk.n, key.pubk.nlen)))
result.add("\npue = ")
result.add(toHex(getArray(key.buffer, key.pubk.e, key.pubk.elen)))
result.add(ncrutils.toHex(getArray(key.buffer, key.pubk.e, key.pubk.elen)))
result.add("\n")
proc `$`*(key: RsaPublicKey): string =
@ -648,9 +655,9 @@ proc `$`*(key: RsaPublicKey): string =
result = "RSA key ("
result.add($nbitlen)
result.add(" bits)\nn = ")
result.add(toHex(getArray(key.buffer, key.key.n, key.key.nlen)))
result.add(ncrutils.toHex(getArray(key.buffer, key.key.n, key.key.nlen)))
result.add("\ne = ")
result.add(toHex(getArray(key.buffer, key.key.e, key.key.elen)))
result.add(ncrutils.toHex(getArray(key.buffer, key.key.e, key.key.elen)))
result.add("\n")
proc `$`*(sig: RsaSignature): string =
@ -659,7 +666,7 @@ proc `$`*(sig: RsaSignature): string =
result = "Empty or uninitialized RSA signature"
else:
result = "RSA signature ("
result.add(toHex(sig.buffer))
result.add(ncrutils.toHex(sig.buffer))
result.add(")")
proc `==`*(a, b: RsaPrivateKey): bool =