Use constant-time comparison for keys and signatures. (#299)
This commit is contained in:
parent
f303954989
commit
7c1aac5dc1
|
@ -20,7 +20,7 @@ import bearssl
|
||||||
import nimcrypto/utils
|
import nimcrypto/utils
|
||||||
import minasn1
|
import minasn1
|
||||||
export minasn1.Asn1Error
|
export minasn1.Asn1Error
|
||||||
import stew/results
|
import stew/[results, ctops]
|
||||||
export results
|
export results
|
||||||
|
|
||||||
const
|
const
|
||||||
|
@ -540,8 +540,8 @@ proc `==`*(pubkey1, pubkey2: EcPublicKey): bool =
|
||||||
let op2 = pubkey2.getOffset()
|
let op2 = pubkey2.getOffset()
|
||||||
if op1 == -1 or op2 == -1:
|
if op1 == -1 or op2 == -1:
|
||||||
return false
|
return false
|
||||||
result = equalMem(unsafeAddr pubkey1.buffer[op1],
|
return CT.isEqual(pubkey1.buffer.toOpenArray(op1, pubkey1.key.qlen - 1),
|
||||||
unsafeAddr pubkey2.buffer[op2], pubkey1.key.qlen)
|
pubkey2.buffer.toOpenArray(op2, pubkey2.key.qlen - 1))
|
||||||
|
|
||||||
proc `==`*(seckey1, seckey2: EcPrivateKey): bool =
|
proc `==`*(seckey1, seckey2: EcPrivateKey): bool =
|
||||||
## Returns ``true`` if both keys ``seckey1`` and ``seckey2`` are equal.
|
## Returns ``true`` if both keys ``seckey1`` and ``seckey2`` are equal.
|
||||||
|
@ -560,19 +560,30 @@ proc `==`*(seckey1, seckey2: EcPrivateKey): bool =
|
||||||
let op2 = seckey2.getOffset()
|
let op2 = seckey2.getOffset()
|
||||||
if op1 == -1 or op2 == -1:
|
if op1 == -1 or op2 == -1:
|
||||||
return false
|
return false
|
||||||
result = equalMem(unsafeAddr seckey1.buffer[op1],
|
return CT.isEqual(seckey1.buffer.toOpenArray(op1, seckey1.key.xlen - 1),
|
||||||
unsafeAddr seckey2.buffer[op2], seckey1.key.xlen)
|
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.
|
## Return ``true`` if both signatures ``sig1`` and ``sig2`` are equal.
|
||||||
if isNil(sig1) and isNil(sig2):
|
if isNil(a) and isNil(b):
|
||||||
result = true
|
true
|
||||||
elif isNil(sig1) and (not isNil(sig2)):
|
elif isNil(a) and (not isNil(b)):
|
||||||
result = false
|
false
|
||||||
elif isNil(sig2) and (not isNil(sig1)):
|
elif isNil(b) and (not isNil(a)):
|
||||||
result = false
|
false
|
||||||
else:
|
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] =
|
proc init*(key: var EcPrivateKey, data: openarray[byte]): Result[void, Asn1Error] =
|
||||||
## Initialize EC `private key` or `signature` ``key`` from ASN.1 DER binary
|
## Initialize EC `private key` or `signature` ``key`` from ASN.1 DER binary
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
import constants, bearssl
|
import constants, bearssl
|
||||||
import nimcrypto/[hash, sha2, utils]
|
import nimcrypto/[hash, sha2, utils]
|
||||||
import stew/results
|
import stew/[results, ctops]
|
||||||
export results
|
export results
|
||||||
|
|
||||||
# This workaround needed because of some bugs in Nim Static[T].
|
# 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 =
|
proc `==`*(eda, edb: EdPrivateKey): bool =
|
||||||
## Compare ED25519 `private key` objects for equality.
|
## Compare ED25519 `private key` objects for equality.
|
||||||
result = (eda.data == edb.data)
|
result = CT.isEqual(eda.data, edb.data)
|
||||||
|
|
||||||
proc `==`*(eda, edb: EdPublicKey): bool =
|
proc `==`*(eda, edb: EdPublicKey): bool =
|
||||||
## Compare ED25519 `public key` objects for equality.
|
## Compare ED25519 `public key` objects for equality.
|
||||||
result = (eda.data == edb.data)
|
result = CT.isEqual(eda.data, edb.data)
|
||||||
|
|
||||||
proc `==`*(eda, edb: EdSignature): bool =
|
proc `==`*(eda, edb: EdSignature): bool =
|
||||||
## Compare ED25519 `signature` objects for equality.
|
## 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)
|
proc `$`*(key: EdPrivateKey): string = toHex(key.data)
|
||||||
## Return string representation of ED25519 `private key`.
|
## Return string representation of ED25519 `private key`.
|
||||||
|
|
|
@ -19,7 +19,7 @@ import nimcrypto/utils
|
||||||
import bearssl
|
import bearssl
|
||||||
import minasn1
|
import minasn1
|
||||||
export Asn1Error
|
export Asn1Error
|
||||||
import stew/results
|
import stew/[results, ctops]
|
||||||
export results
|
export results
|
||||||
|
|
||||||
const
|
const
|
||||||
|
@ -662,81 +662,78 @@ proc `$`*(sig: RsaSignature): string =
|
||||||
result.add(toHex(sig.buffer))
|
result.add(toHex(sig.buffer))
|
||||||
result.add(")")
|
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 =
|
proc `==`*(a, b: RsaPrivateKey): bool =
|
||||||
## Compare two RSA private keys for equality.
|
## Compare two RSA private keys for equality.
|
||||||
##
|
##
|
||||||
## Result is true if ``a`` and ``b`` are both ``nil`` or ``a`` and ``b`` are
|
## Result is true if ``a`` and ``b`` are both ``nil`` or ``a`` and ``b`` are
|
||||||
## equal by value.
|
## equal by value.
|
||||||
if isNil(a) and isNil(b):
|
if isNil(a) and isNil(b):
|
||||||
result = true
|
true
|
||||||
elif isNil(a) and (not isNil(b)):
|
elif isNil(a) and (not isNil(b)):
|
||||||
result = false
|
false
|
||||||
elif isNil(b) and (not isNil(a)):
|
elif isNil(b) and (not isNil(a)):
|
||||||
result = false
|
false
|
||||||
else:
|
else:
|
||||||
if a.seck.nBitlen == b.seck.nBitlen:
|
if a.seck.nBitlen == b.seck.nBitlen:
|
||||||
if cast[int](a.seck.nBitlen) > 0:
|
if cast[int](a.seck.nBitlen) > 0:
|
||||||
let r1 = cmp(getArray(a.buffer, a.seck.p, a.seck.plen),
|
let r1 = CT.isEqual(getArray(a.buffer, a.seck.p, a.seck.plen),
|
||||||
getArray(b.buffer, b.seck.p, b.seck.plen))
|
getArray(b.buffer, b.seck.p, b.seck.plen))
|
||||||
let r2 = cmp(getArray(a.buffer, a.seck.q, a.seck.qlen),
|
let r2 = CT.isEqual(getArray(a.buffer, a.seck.q, a.seck.qlen),
|
||||||
getArray(b.buffer, b.seck.q, b.seck.qlen))
|
getArray(b.buffer, b.seck.q, b.seck.qlen))
|
||||||
let r3 = cmp(getArray(a.buffer, a.seck.dp, a.seck.dplen),
|
let r3 = CT.isEqual(getArray(a.buffer, a.seck.dp, a.seck.dplen),
|
||||||
getArray(b.buffer, b.seck.dp, b.seck.dplen))
|
getArray(b.buffer, b.seck.dp, b.seck.dplen))
|
||||||
let r4 = cmp(getArray(a.buffer, a.seck.dq, a.seck.dqlen),
|
let r4 = CT.isEqual(getArray(a.buffer, a.seck.dq, a.seck.dqlen),
|
||||||
getArray(b.buffer, b.seck.dq, b.seck.dqlen))
|
getArray(b.buffer, b.seck.dq, b.seck.dqlen))
|
||||||
let r5 = cmp(getArray(a.buffer, a.seck.iq, a.seck.iqlen),
|
let r5 = CT.isEqual(getArray(a.buffer, a.seck.iq, a.seck.iqlen),
|
||||||
getArray(b.buffer, b.seck.iq, b.seck.iqlen))
|
getArray(b.buffer, b.seck.iq, b.seck.iqlen))
|
||||||
let r6 = cmp(getArray(a.buffer, a.pexp, a.pexplen),
|
let r6 = CT.isEqual(getArray(a.buffer, a.pexp, a.pexplen),
|
||||||
getArray(b.buffer, b.pexp, b.pexplen))
|
getArray(b.buffer, b.pexp, b.pexplen))
|
||||||
let r7 = cmp(getArray(a.buffer, a.pubk.n, a.pubk.nlen),
|
let r7 = CT.isEqual(getArray(a.buffer, a.pubk.n, a.pubk.nlen),
|
||||||
getArray(b.buffer, b.pubk.n, b.pubk.nlen))
|
getArray(b.buffer, b.pubk.n, b.pubk.nlen))
|
||||||
let r8 = cmp(getArray(a.buffer, a.pubk.e, a.pubk.elen),
|
let r8 = CT.isEqual(getArray(a.buffer, a.pubk.e, a.pubk.elen),
|
||||||
getArray(b.buffer, b.pubk.e, b.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
|
r1 and r2 and r3 and r4 and r5 and r6 and r7 and r8
|
||||||
else:
|
else:
|
||||||
result = true
|
true
|
||||||
|
else:
|
||||||
|
false
|
||||||
|
|
||||||
proc `==`*(a, b: RsaSignature): bool =
|
proc `==`*(a, b: RsaSignature): bool =
|
||||||
## Compare two RSA signatures for equality.
|
## Compare two RSA signatures for equality.
|
||||||
if isNil(a) and isNil(b):
|
if isNil(a) and isNil(b):
|
||||||
result = true
|
true
|
||||||
elif isNil(a) and (not isNil(b)):
|
elif isNil(a) and (not isNil(b)):
|
||||||
result = false
|
false
|
||||||
elif isNil(b) and (not isNil(a)):
|
elif isNil(b) and (not isNil(a)):
|
||||||
result = false
|
false
|
||||||
else:
|
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 =
|
proc `==`*(a, b: RsaPublicKey): bool =
|
||||||
## Compare two RSA public keys for equality.
|
## Compare two RSA public keys for equality.
|
||||||
if isNil(a) and isNil(b):
|
if isNil(a) and isNil(b):
|
||||||
result = true
|
true
|
||||||
elif isNil(a) and (not isNil(b)):
|
elif isNil(a) and (not isNil(b)):
|
||||||
result = false
|
false
|
||||||
elif isNil(b) and (not isNil(a)):
|
elif isNil(b) and (not isNil(a)):
|
||||||
result = false
|
false
|
||||||
else:
|
else:
|
||||||
let r1 = cmp(getArray(a.buffer, a.key.n, a.key.nlen),
|
let r1 = CT.isEqual(getArray(a.buffer, a.key.n, a.key.nlen),
|
||||||
getArray(b.buffer, b.key.n, b.key.nlen))
|
getArray(b.buffer, b.key.n, b.key.nlen))
|
||||||
let r2 = cmp(getArray(a.buffer, a.key.e, a.key.elen),
|
let r2 = CT.isEqual(getArray(a.buffer, a.key.e, a.key.elen),
|
||||||
getArray(b.buffer, b.key.e, b.key.elen))
|
getArray(b.buffer, b.key.e, b.key.elen))
|
||||||
result = r1 and r2
|
(r1 and r2)
|
||||||
|
|
||||||
proc sign*[T: byte|char](key: RsaPrivateKey,
|
proc sign*[T: byte|char](key: RsaPrivateKey,
|
||||||
message: openarray[T]): RsaResult[RsaSignature] {.gcsafe.} =
|
message: openarray[T]): RsaResult[RsaSignature] {.gcsafe.} =
|
||||||
|
|
Loading…
Reference in New Issue