Use constant-time comparison for keys and signatures. (#299)

This commit is contained in:
Eugene Kabanov 2020-08-08 09:53:33 +03:00 committed by GitHub
parent f303954989
commit 7c1aac5dc1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 75 additions and 67 deletions

View File

@ -20,7 +20,7 @@ import bearssl
import nimcrypto/utils
import minasn1
export minasn1.Asn1Error
import stew/results
import stew/[results, ctops]
export results
const
@ -540,8 +540,8 @@ proc `==`*(pubkey1, pubkey2: EcPublicKey): bool =
let op2 = pubkey2.getOffset()
if op1 == -1 or op2 == -1:
return false
result = equalMem(unsafeAddr pubkey1.buffer[op1],
unsafeAddr pubkey2.buffer[op2], pubkey1.key.qlen)
return CT.isEqual(pubkey1.buffer.toOpenArray(op1, pubkey1.key.qlen - 1),
pubkey2.buffer.toOpenArray(op2, pubkey2.key.qlen - 1))
proc `==`*(seckey1, seckey2: EcPrivateKey): bool =
## Returns ``true`` if both keys ``seckey1`` and ``seckey2`` are equal.
@ -560,19 +560,30 @@ proc `==`*(seckey1, seckey2: EcPrivateKey): bool =
let op2 = seckey2.getOffset()
if op1 == -1 or op2 == -1:
return false
result = equalMem(unsafeAddr seckey1.buffer[op1],
unsafeAddr seckey2.buffer[op2], seckey1.key.xlen)
return CT.isEqual(seckey1.buffer.toOpenArray(op1, seckey1.key.xlen - 1),
seckey2.buffer.toOpenArray(op2, seckey2.key.xlen - 1))
proc `==`*(sig1, sig2: EcSignature): bool =
proc `==`*(a, b: EcSignature): bool =
## Return ``true`` if both signatures ``sig1`` and ``sig2`` are equal.
if isNil(sig1) and isNil(sig2):
result = true
elif isNil(sig1) and (not isNil(sig2)):
result = false
elif isNil(sig2) and (not isNil(sig1)):
result = false
if isNil(a) and isNil(b):
true
elif isNil(a) and (not isNil(b)):
false
elif isNil(b) and (not isNil(a)):
false
else:
result = (sig1.buffer == sig2.buffer)
# We need to cover all the cases because Signature initialization procedure
# do not perform any checks.
if len(a.buffer) == 0 and len(b.buffer) == 0:
true
elif len(a.buffer) == 0 and len(b.buffer) != 0:
false
elif len(b.buffer) == 0 and len(a.buffer) != 0:
false
elif len(a.buffer) != len(b.buffer):
false
else:
CT.isEqual(a.buffer, b.buffer)
proc init*(key: var EcPrivateKey, data: openarray[byte]): Result[void, Asn1Error] =
## Initialize EC `private key` or `signature` ``key`` from ASN.1 DER binary

View File

@ -15,7 +15,7 @@
import constants, bearssl
import nimcrypto/[hash, sha2, utils]
import stew/results
import stew/[results, ctops]
export results
# This workaround needed because of some bugs in Nim Static[T].
@ -1725,15 +1725,15 @@ proc getBytes*(sig: EdSignature): seq[byte] = @(sig.data)
proc `==`*(eda, edb: EdPrivateKey): bool =
## Compare ED25519 `private key` objects for equality.
result = (eda.data == edb.data)
result = CT.isEqual(eda.data, edb.data)
proc `==`*(eda, edb: EdPublicKey): bool =
## Compare ED25519 `public key` objects for equality.
result = (eda.data == edb.data)
result = CT.isEqual(eda.data, edb.data)
proc `==`*(eda, edb: EdSignature): bool =
## Compare ED25519 `signature` objects for equality.
result = (eda.data == edb.data)
result = CT.isEqual(eda.data, edb.data)
proc `$`*(key: EdPrivateKey): string = toHex(key.data)
## Return string representation of ED25519 `private key`.

View File

@ -19,7 +19,7 @@ import nimcrypto/utils
import bearssl
import minasn1
export Asn1Error
import stew/results
import stew/[results, ctops]
export results
const
@ -662,81 +662,78 @@ proc `$`*(sig: RsaSignature): string =
result.add(toHex(sig.buffer))
result.add(")")
proc cmp(a: openarray[byte], b: openarray[byte]): bool =
let alen = len(a)
let blen = len(b)
if alen == blen:
if alen == 0:
true
else:
var n = alen
var res = 0
while n > 0:
dec(n)
res = res or int(a[n] xor b[n])
(res == 0)
else:
false
proc `==`*(a, b: RsaPrivateKey): bool =
## Compare two RSA private keys for equality.
##
## Result is true if ``a`` and ``b`` are both ``nil`` or ``a`` and ``b`` are
## equal by value.
if isNil(a) and isNil(b):
result = true
true
elif isNil(a) and (not isNil(b)):
result = false
false
elif isNil(b) and (not isNil(a)):
result = false
false
else:
if a.seck.nBitlen == b.seck.nBitlen:
if cast[int](a.seck.nBitlen) > 0:
let r1 = cmp(getArray(a.buffer, a.seck.p, a.seck.plen),
getArray(b.buffer, b.seck.p, b.seck.plen))
let r2 = cmp(getArray(a.buffer, a.seck.q, a.seck.qlen),
getArray(b.buffer, b.seck.q, b.seck.qlen))
let r3 = cmp(getArray(a.buffer, a.seck.dp, a.seck.dplen),
getArray(b.buffer, b.seck.dp, b.seck.dplen))
let r4 = cmp(getArray(a.buffer, a.seck.dq, a.seck.dqlen),
getArray(b.buffer, b.seck.dq, b.seck.dqlen))
let r5 = cmp(getArray(a.buffer, a.seck.iq, a.seck.iqlen),
getArray(b.buffer, b.seck.iq, b.seck.iqlen))
let r6 = cmp(getArray(a.buffer, a.pexp, a.pexplen),
getArray(b.buffer, b.pexp, b.pexplen))
let r7 = cmp(getArray(a.buffer, a.pubk.n, a.pubk.nlen),
getArray(b.buffer, b.pubk.n, b.pubk.nlen))
let r8 = cmp(getArray(a.buffer, a.pubk.e, a.pubk.elen),
getArray(b.buffer, b.pubk.e, b.pubk.elen))
result = r1 and r2 and r3 and r4 and r5 and r6 and r7 and r8
let r1 = CT.isEqual(getArray(a.buffer, a.seck.p, a.seck.plen),
getArray(b.buffer, b.seck.p, b.seck.plen))
let r2 = CT.isEqual(getArray(a.buffer, a.seck.q, a.seck.qlen),
getArray(b.buffer, b.seck.q, b.seck.qlen))
let r3 = CT.isEqual(getArray(a.buffer, a.seck.dp, a.seck.dplen),
getArray(b.buffer, b.seck.dp, b.seck.dplen))
let r4 = CT.isEqual(getArray(a.buffer, a.seck.dq, a.seck.dqlen),
getArray(b.buffer, b.seck.dq, b.seck.dqlen))
let r5 = CT.isEqual(getArray(a.buffer, a.seck.iq, a.seck.iqlen),
getArray(b.buffer, b.seck.iq, b.seck.iqlen))
let r6 = CT.isEqual(getArray(a.buffer, a.pexp, a.pexplen),
getArray(b.buffer, b.pexp, b.pexplen))
let r7 = CT.isEqual(getArray(a.buffer, a.pubk.n, a.pubk.nlen),
getArray(b.buffer, b.pubk.n, b.pubk.nlen))
let r8 = CT.isEqual(getArray(a.buffer, a.pubk.e, a.pubk.elen),
getArray(b.buffer, b.pubk.e, b.pubk.elen))
r1 and r2 and r3 and r4 and r5 and r6 and r7 and r8
else:
result = true
true
else:
false
proc `==`*(a, b: RsaSignature): bool =
## Compare two RSA signatures for equality.
if isNil(a) and isNil(b):
result = true
true
elif isNil(a) and (not isNil(b)):
result = false
false
elif isNil(b) and (not isNil(a)):
result = false
false
else:
result = (a.buffer == b.buffer)
# We need to cover all the cases because Signature initialization procedure
# do not perform any checks.
if len(a.buffer) == 0 and len(b.buffer) == 0:
true
elif len(a.buffer) == 0 and len(b.buffer) != 0:
false
elif len(b.buffer) == 0 and len(a.buffer) != 0:
false
elif len(a.buffer) != len(b.buffer):
false
else:
CT.isEqual(a.buffer, b.buffer)
proc `==`*(a, b: RsaPublicKey): bool =
## Compare two RSA public keys for equality.
if isNil(a) and isNil(b):
result = true
true
elif isNil(a) and (not isNil(b)):
result = false
false
elif isNil(b) and (not isNil(a)):
result = false
false
else:
let r1 = cmp(getArray(a.buffer, a.key.n, a.key.nlen),
getArray(b.buffer, b.key.n, b.key.nlen))
let r2 = cmp(getArray(a.buffer, a.key.e, a.key.elen),
getArray(b.buffer, b.key.e, b.key.elen))
result = r1 and r2
let r1 = CT.isEqual(getArray(a.buffer, a.key.n, a.key.nlen),
getArray(b.buffer, b.key.n, b.key.nlen))
let r2 = CT.isEqual(getArray(a.buffer, a.key.e, a.key.elen),
getArray(b.buffer, b.key.e, b.key.elen))
(r1 and r2)
proc sign*[T: byte|char](key: RsaPrivateKey,
message: openarray[T]): RsaResult[RsaSignature] {.gcsafe.} =