leif-ibsen / swiftecc Goto Github PK
View Code? Open in Web Editor NEWSwift Elliptic Curve Cryptography (ECIES, ECDSA and ECDH)
License: MIT License
Swift Elliptic Curve Cryptography (ECIES, ECDSA and ECDH)
License: MIT License
Hi,When I use bouncycastle to decrypt on the java side, iOS and java cannot decrypt each other.I want to know whether the ECIES standard in SwiftECC is ANSI X9.63 or IEEE 1363a?
When using modular arithmetic, the application of a reduction function is necessary after each arithmetic operation, which ensures that the result is shifted into the expected value range.
There are many different methods for this task. The library under investigation uses the Barret reduction [1]. A clear presentation of the implemented method can be found in chapter 3.2 "General Barret Reduction" of [2].
The concrete implementation looks as follows [3]:
func reduceModP(_ x: BInt) -> BInt {
let x1 = x.isNegative ? -x : x
var t = x1 - ((x1 * self.u) >> self.shifts) * self.p
if t >= self.p {
t -= self.p
}
return t.isZero ? BInt.ZERO : (x.isNegative ? self.p - t : t)
}
From [3] also emerge the conditions that must be met for the algorithm to complete successfully. These checks are not fully implemented a priori.
For example, the procedure only works if
but this is not checked. Additionally, in the above code line 3, a modulo operation provided in the actual algorithm is not performed.
In summary, the implemented procedure is equivalent to the one described in [2] if and only if
applies.
A check of the allowed range of values for the input parameter x must be performed at the beginning of the function and an exception must be thrown in case of an error.
[1] P.Barret. Implementing the Rivest Shamir and Adleman Public Key Encryption Algorithm on a Standard Digital Signal Processor: https://link.springer.com/con- tent/pdf/10.1007%2F3-540-47721-7_24.pdf.
[2] C.Roma. Hardware Implementation of Barrett Reduction Exploiting Constant Multiplica- tion: https://uwspace.uwaterloo.ca/bitstream/handle/10012/15191/Roma_Crystal.pdf
[3] Source code of the reduceModP function: https://github.com/leif-ibsen/Swif- tECC/blob/621c70126966e5c289c4dc0ff801c6282b6baa05/Sources/SwiftECC/DomainP/Do mainP.swift#L218
I'm using the next code:
func test() {
let domain = Domain.instance(curve: .EC256k1)
let (pubKey, privKey) = domain.makeKeyPair()
let pubPEM = pubKey.pem
let privPEM = privKey.pem
print(pubPEM)
print(privPEM)
let object = MyObject()
let jsonEncoder = JSONEncoder()
let jsonData = try! jsonEncoder.encode(object)
let json = String(data: jsonData, encoding: String.Encoding.utf8)
let message = json!.data(using: .utf8)!
let sig = privKey.sign(msg: message)
let ok = pubKey.verify(signature: sig, msg: message)
print(sig)
let encrypted = pubKey.encrypt(msg: message, cipher: .AES256)
print(encrypted)
let decrypted = try! privKey.decrypt(msg: encrypted, cipher: .AES256)
print(String(data: decrypted, encoding: .utf8))
}
How can I get iv here? It's required by the server
A relevant function in the arithmetic of a group spanned by an elliptic curve is the multiplication of a scalar by a curve point.
A traditional and very performant method for this is the Double-And-Add algorithm. However, this has the problem that the execution speed depends on the number and position of the 1-bits in the binary representation of the scalar value. If there is the possibility of a timing side channel, it may be possible to determine the value of the scalar. This is particularly critical, for example, in the generation of an ECDSA signature, where a secret scalar (the private key) is multiplied by a known point (the curve generator).
The library under study relies on an addition-subtraction ladder for this operation, which is described as Algorithm 7.2.4 in [1]. Also for this method the execution time depends on the binary representation of the scalar.
To prevent the timing attacks described above, scalar multiplication should be performed in the form of a constant-time algorithm [2], where the execution time is independent of the concrete values of the input parameters.
The so-called Montgomery Ladder [3] has proven to be a very efficient and simple method for this purpose.
⚠ The low rating of this vulnerability is based on the fact that only the verification of signatures is relevant in the context of this security review. For applications in which signatures are created or key pairs are generated, this is a more severe vulnerability.
[1] R.Crandall & C.Pomer. Prime Numbers. A computational perspective:
https://www.springer.com/gp/book/9780387252827
[2] D. Bernstein & T.Lange. SafeCurves: Ladders: https://safecurves.cr.yp.to/ladder.html
[3] D. Bernstein & T.Lange. Montgomery curves and the Montgomery ladder:
https://eprint.iacr.org/2017/293.pdf
The validation of an ECDSA signature is implemented in the PublicKey.swift file in the class ECPublicKey [1]:
The ECDSA procedure is well explained, for example, in Section 4.4.1 "ECDSA" of [2]. The algorithm for verifying such a signature is described in Algorithm 4.30 "ECDSA Signature Verification".
In this implementation, the following validation steps are not performed:
This may lead to an incorrect result in the signature check [3].
It is essential to complete the specified check steps in the specified function.
[1] Source code of the verify function: https://github.com/leif-ibsen/SwiftECC/blob/621c70126966e5c289c4dc0ff801c6282b6baa05/Sources/SwiftECC/PublicKey.sw
ift#L112
[2] D. Hankerson, A. Menezes, S. Vanstone. Guide to Elliptic Curve Cryptography:
https://www.springer.com/gp/book/9780387952734
[3] ECDSA Signature Verfication Checks: https://crypto.stackexchange.com/a/80252
Hey @leif-ibsen
I guess there is no documentation in README for encryption/decryption with SymmetricKey.
I am struggling with encryption/decryption with use of SymmetricKey.
Could you please update the README ?
Also Could you please add example?
Thank you
Can this library be used to calculate shared secret the same way as in Node or Java / Kotlin?
Here is the code that generates shared secret in Kotlin:
private fun computeSharedSecret(otherPublicKey: PublicKey, myPrivateKey: PrivateKey): ByteArray {
val keyAgreement = KeyAgreement.getInstance("ECDH")
keyAgreement.init(myPrivateKey)
keyAgreement.doPhase(otherPublicKey, true)
return keyAgreement.generateSecret()
}
Here is the code in NodeJS
const myPrivateKey = Buffer.from("iUfaS6XxDCOS65sGqeunCQJR4045pTA3H4cCcYqfLpg", 'base64')
const otherSidePublicKey = Buffer.from("BJPPEL/HhVR4Yv8qyKT/1A8rcRhmP8aXBKCikXeShNhjWWWKjDuKt9zco7Flt9l14uJW1lt2kCIjb8e64wDW5Sg=", 'base64')
const ecdh = crypto.createECDH('secp256k1')
ecdh.setPrivateKey(myPrivateKey)
const sharedSecret = ecdh.computeSecret(otherSidePublicKey)
None of these example above use messageDigest or data parameter to calculate shared secret. Is it possible to calculate shared secret this way using SwiftECC?
Hi,
Why would you hide a very handy constructor for ECPublicKey: ef281d1#diff-1a8c1117bd15ccee147f31e050227cdb59bc4320b33a94eee8f995c669a4aeacL29 ?
Thanks,
p
In the decode function of the Base64 class, x is declared outside the for loop, but is not needed across loop calls.
public static func decode(_ input: String) throws -> Bytes {
var bytes: Bytes = []
var eq = 0
var i = 0
var x = 0
var bbbb = Bytes(repeating: 0, count: 4) for s in input {
x = Int(s.unicodeScalars.first!.value)
[...]
}
if i > 0 {
throw ECException.base64
}
return bytes
}
We recommend declaring the variable x inside the for loop.
[1] Source code of the decode function in the Base64 class: https://github.com/leif-ibsen/SwiftECC/blob/621c70126966e5c289c4dc0ff801c6282b6baa05/Sources/SwiftECC/Base64.swift #L104
Nice lib. Could you also implement decompression of EC key? Meaning computation of Y coordinate if there is given only X coordinate with prefix (= compressed key).
Any implementation of elliptic curves is based on the arithmetic operations, which are defined on the associated modular group. In particular, these include:
Unfortunately, the examined library makes some mistakes in this implementation, which can subsequently lead to erroneous results in signature verification.
After the addition or subtraction of two scalars, the result must be reduced modularly to fall back into the expected value range. This is done by the library in a very simple way [1]:
func addModP(_ x: BInt, _ y: BInt) -> BInt {
let z = x + y
return z >= self.p ? z - self.p : z
}
func subModP(_ x: BInt, _ y: BInt) -> BInt {
let z = x - y
return z.isNegative ? z + self.p : z
}
However, this reduction is only correct if both input parameters x and y were already in the expected range of values, i.e. 0 ≤ 𝑥 < 𝑝 as well as 0 ≤ 𝑦 < 𝑝 holds. However, this fact is not verified in the code.
In the methods the following methods of the Domain
or DomainP
classes, no point verification is performed at the beginning:
This means that points can be passed as input parameters that are not on the curve at all. This can, especially in the context of the Elliptic Curve Diffie-Hellman (ECDHI) method, lead to a so-called Invalid Curve Attack [2].
The faulty methods look like the following [3]:
public func double(_ p: Point) -> Point {
return self.characteristic2 ? self.domain2!.double(Point2.fromPoint(domain2!.rp, p)).toPoint() : self.domainP!.double(p)
}
public func add(_ p1: Point, _ p2: Point) -> Point {
return self.characteristic2 ? self.domain2!.add(Point2.fromPoint(domain2!.rp, p1), Point2.fromPoint(domain2!.rp, p2)).toPoint() : self.domainP!.add(p1, p2)
}
public func subtract(_ p1: Point, _ p2: Point) -> Point {
return self.characteristic2 ? self.domain2!.subtract(Point2.fromPoint(domain2!.rp, p1), Point2.fromPoint(domain2!.rp, p2)).toPoint() : self.domainP!.subtract(p1, p2)
}
public func negate(_ p: Point) -> Point {
return self.characteristic2 ? self.domain2!.negate(Point2.fromPoint(domain2!.rp, p)).toPoint() : self.domainP!.negate(p)
}
public func multiply(_ p: Point, _ n: BInt) -> Point {
return self.characteristic2 ? self.domain2!.multiply(Point2.fromPoint(domain2!.rp, p), n).toPoint() : self.domainP!.multiply(p, n)
}
Currently, such a check of the points (using the contains() method of the Domain class is only performed when decoding ASN.1 encoded points, but not when using manually instantiated points.
As a minimal solution, it should be checked at the beginning whether the input parameters fall within the expected range of values. It would be better to rely on the reduceModP reduction function already available in the same class here instead of a manual implementation, but this may be associated with speed losses.
Before any arithmetic operation it must be ensured that the points passed as input parameter actually lie on the underlying elliptic curve.
Support linux
How to reproduce:
let domain256k1 = Domain.instance(curve: .EC256r1)
let k1 = domain256k1.makeKeyPair()
let k2 = domain256k1.makeKeyPair()
let from = DispatchTime.now().uptimeNanoseconds
try k1.1.keyAgreement(pubKey: k2.0, length: 32, md: .SHA1, sharedInfo: []) // 👈 here
let now = DispatchTime.now().uptimeNanoseconds
let fromNow = String(Double(now - from) / 1_000_000_000)
print("took: " + fromNow + " sec") // Running this on my M1 Max MacBook Pro prints `took: 0.534008709 sec`
The current implementation exhibits slower performance even compared to Javascript implementations such as crypto-browserify or the node:crypto package, which finish the task within a few milliseconds. Even Java implementation with BouncyCastle is faster
Upon exploring the code, it appears that this line. could be contributing to the slower performance.
We generate a keypair
let (asymPublicKey, _) = Domain.instance(curve: .BP256r1).makeKeyPair()
Get x and y components from the public key
asymPublicKey.w.x.asMagnitudeBytes()
asymPublicKey.w.y.asMagnitudeBytes()
Then we use this information to send a rather complicated backend request and receive the following error
could not create public key from x,y components - elliptic curve point is not on curve
We only get this error about once per 30 requests.
For example, with the following keypair the error is reproducible:
PublicKey:
-----BEGIN PUBLIC KEY-----
MFowFAYHKoZIzj0CAQYJKyQDAwIIAQEHA0IABIUCEqHiyt6ENtryBdxEk9Wrwy/W
XXSv4Lefh2nBR7XWAPZVcbKUAhr2s0oRS+ceE6tEl+HM157wr8tguXQ4Xbo=
-----END PUBLIC KEY-----
PrivateKey:
-----BEGIN EC PRIVATE KEY-----
MHgCAQEEIHctkCXo/LMh1qxqt7uL/wjTKU/RBpGEMngiCsvlmOevoAsGCSskAwMC
CAEBB6FEA0IABIUCEqHiyt6ENtryBdxEk9Wrwy/WXXSv4Lefh2nBR7XWAPZVcbKU
Ahr2s0oRS+ceE6tEl+HM157wr8tguXQ4Xbo=
-----END EC PRIVATE KEY-----
This error is related to V4 and V5.
I'm currently having a issue of loading my existing eth keys into ECPrivateKey, but i'm not able to come up with the pem or der format it needs, so i am wondering if there is a way to get pem/der format or just having a another alternative way to load my existing keys?
Hi,The ECPublicKey generated by the private key, how to print the hex string of the public key?
when archiving the project, the error "Integer literal '4294967295' overflows when stored into 'Int'" keep showing
xcode 13.3 on big sur
is there any way to solve this?
thank you!
Greetings,
Steps to reproduce
Error
Arithmetic operation '-1060120681 * 31' (on type 'Int') results in an overflow.
// Small prime product
static let SPP = BInt(3 * 5 * 7 * 11 * 13 * 17 * 19 * 23 * 29 * 31 * 37 * 41)
Possible solution
Replace multiplication with initialization from String:
static let SPP = BInt("152125131763605")!
Among other things, the constructor of the ECSignature
class can be passed an object of type Domain
, which is subsequently used for padding the two signature values [1]:
public convenience init(asn1: ASN1, domain: Domain) throws {
guard let seq = asn1 as? ASN1Sequence else {
throw ECException.asn1Structure
}
if seq.getValue().count < 2 {
throw ECException.asn1Structure
}
guard let r0 = seq.get(0) as? ASN1Integer else {
throw ECException.asn1Structure
}
guard let s1 = seq.get(1) as? ASN1Integer else {
throw ECException.asn1Structure
}
self.init(r: domain.align(r0.value.asMagnitudeBytes()), s:domain.align(s1.value.asMagnitudeBytes()))
}
However, the curve type obtained here is not stored in the class, but discarded after the constructor terminates. This leads to the fact that the signature can be checked unnoticed with an ECPublicKey object, which was created for another curve.
As long as the signature values in the constructor are not changed in content, this should not affect the result of the verification, but it leads to less efficient error detection.
If the curve specified in the constructor is stored within the class, then this value can be matched against the curve used for the key right at the start of the ECPublikKey.verify() method, and immediately terminated with a matching error if necessary.
References
[1] Source code of the constructor of the ECSignature class: https://github.com/leif-ibsen/SwiftECC/blob/621c70126966e5c289c4dc0ff801c6282b6baa05/Sources/SwiftECC/Signature.sw ift#L34
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.
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).
[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
Currently when you want to upgrade from Xcode 12.* to Xcode 13.0 the SwiftECC library is not going to build due to the following message:
SwiftECC/Sources/SwiftECC/PrivateKey.swift:180:41: integer literal '4294967295' overflows when stored into 'Int'
if length >= mda.digestLength * 0xffffffff || length < 0 {
Hi Leif,
We are trying to use SwiftECC in the same project as Shield.
Both have a sub-dependency to a package called BigInt
.
Unfortunately, Swift Package Manager does not provide any ways to resolve this kind of ambiguity. See here fore a similar issue.
Therefore, I wanted to ask if there is an option for you to refactor your BigInt
dependency to a different name.
All the best,
Lorenz
Hi,
Currently this project doesn't have a License. This makes it impossible to use in another app legally.
Could we add an License?
Proposal
I think the MIT License would be good for this project, as it allows a bigger community to use this library.
We'd just have to add a LICENSE document in the root with the MIT License.
I saw several implementations on different Domain files, but I have no clue what is the difference between them.
func ecdsaSignature(_ params: String) {
guard let privateKey = try? ECPrivateKey(pem: pkey) else {
return
}
let message = params.data(using: .utf8)!
let sig = privateKey.sign(msg: message)
print(sig.description)
let publicKey = ECPublicKey(privateKey: privateKey)
let ok = publicKey.verify(signature: sig, msg: message)
print(ok)
}
can this library be used in an objc application?
There are currently no or only incomplete unit tests for the following classes, which means that a faulty implementation would remain undetected currently or in the future.
The library uses its own implementation of the hash procedure SHA-2[1]. This is used, among other things, in the creation and validation of ECDSA signatures.
For ECDSA signatures using Brainpool curves, there are no known-answer tests that can ensure the correctness of the procedure compared to other implementations. There is only an attempt to re-verify data signed by the library itself. Unfortunately, a verification of an externally created signature or a validation of the arithmetic does not take place.
All variants of the SHA-2 family should be subjected to rigorous unit testing within the library.
Both for the arithmetic of the curves (addition, scalar multiplication, ...) and for ECDSA signatures, unit tests should be created with externally validated test data.
In all cases, standardized test vectors should be used whenever possible [2] [3]. If no such vectors are available, test data should be created using established software from another vendor (e.g. OpenSSL).
[1] https://github.com/leif-ibsen/SwiftECC/blob/1.0.2/Sources/SwiftECC/Cipher/SHA2.swift
[2]NIST. The Secure Hash AlgorithmValidation System (SHAVS): https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Pro-gram/documents/shs/SHAVS.pdf
[3]L.Bruckert et.al. RFC 8734: ECC Brainpool Curves Test vectors: https://www.rfc-edi-tor.org/rfc/rfc8734#name-test-vectors
Greetings,
In some cases, the signature BitWidth could be different from the Public Key Bitwitdh.
Proposal
Add an optional parameter to the verify
-function to specify the Bidwith as an Integer.
I would like to use this library through Cocoapods, why don't you want to add support for this? Cocoapods is the most popular dependency manager
Hi,
is it possible to add curve25519 domain for creating the EC keys?
I'm relatively new but this library has helped me so far. But our requirement is to implement Curve25519 encryption.. so please let me know how I can create a curve25519 domain public key.
Hi Leif,
Thank you for your great work on this library.
We are considering using it in one of our applications. As security is extremely relevant for our purpose, we would like to have this library reviewed by a company specialised in the field.
If anything comes up during the review, we would like to create pull requests so that the findings flow back into the library.
Are you open for this kind of cooperation? Please contact me via [email protected] if you want to discuss this issue directly.
Best,
Lorenz
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.