Crypto Review Findings #11 - Use of non-standard curves is possible
lorenz-loesch-ynx opened this issue · comments
Vulnerability details
Theoretically, it is possible to define an infinite number of different elliptic curves. However, it has been found that only a small number of these curves are actually usable for cryptographically secure deployment. The definition of the individual curve parameters is an extremely difficult matter, and even small errors can substantially impair the security or speed of the procedures based on them. [1]
It is therefore strongly recommended to use only standardized curves published by well-known organizations such as the IETF, NIST, or BSI for the cryptographic use of elliptic curves.
Furthermore, in recent years it has been recognized that a free definability of parameters in a cryptographic protocol, a cryptographic agility or cipher agility can severely limit security [2].
The studied library provides the possibility to use arbitrary elliptic curves. The is evident in the domainFromASN1 method of the Domain class, which is called when parsing a public key, among other things [3]:
static func domainFromASN1(_ asn1: ASN1) throws -> Domain {
if let oid = asn1 as? ASN1ObjectIdentifier {
return try Domain.instance(oid: oid)
}
[...]
guard let aa = seq2.get(0) as? ASN1OctetString else {
throw ECException.asn1Structure
}
guard let bb = seq2.get(1) as? ASN1OctetString else {
throw ECException.asn1Structure
}
let a = BInt(magnitude: aa.value)
let b = BInt(magnitude: bb.value)
guard let g = seq.get(3) as? ASN1OctetString else {
throw ECException.asn1Structure
}
if g.value.count & 0x1 == 0 || g.value[0] != 4 {
throw ECException.asn1Structure
}
let l = (g.value.count - 1) / 2
let gx = BInt(magnitude: Bytes(g.value[1 ..< l + 1]))
let gy = BInt(magnitude: Bytes(g.value[l + 1 ..< g.value.count]))
guard let ord = seq.get(4) as? ASN1Integer else {
throw ECException.asn1Structure
}
let order = ord.value
guard let co = seq.get(5) as? ASN1Integer else {
throw ECException.asn1Structure
}
if !co.value.isPositive {
throw ECException.asn1Structure
}
let cofactor = co.value.asInt()! 34 [...]
}
In this function, not only is the specification of a standard curve by OID (lines 2-4) allowed, but also the direct specification of freely chosen curve parameters.
Since this information often comes from untrusted sources (e.g., if the public key is included in a signature), this can be used for an attack where the calculation is forced on a deliberately insecure curve.
Countermeasures
The use of non-standard curves is generally to be classified as insecure. We therefore recommend to generally remove their usability in the library, or at least only allow them if a special unsafe mode has been manually activated (e.g. via compiler flag).
References
[1] D. Bernstein & T.Lange. SafeCurves: choosing safe curves for elliptic-curve cryptography:
https://safecurves.cr.yp.to/index.html
[2] Scott Arciszewski. Against Cipher Agility in Cryptography Protocols: https://paragonie.com/blog/2019/10/against-cipher-agility-in-cryptography-protocols
[3] Source code of the domainFromASN1 function: https://github.com/leif-ibsen/SwiftECC/blob/621c70126966e5c289c4dc0ff801c6282b6baa05/Sources/SwiftECC/Domain.swift #L517
The intention with being able to create new domains is that users can play with,
and experiment with their own domains.
It is not necessarily the case that they should create production quality domains,
although they can try to do that, if they so wish.
In release 2.1.0 it is possible to compare domains for equality, and if an application only wishes to use a certain set of domains, it is easy to check if a given domain is in that set and reject it if it is not.