Giter Site home page Giter Site logo

blueecc's Introduction

Kitura

APIDoc Build Status - Master macOS Linux Apache 2 Slack Status

BlueECC

A cross platform Swift implementation of Elliptic Curve Digital Signature Algorithm (ECDSA) and Elliptic Curve Integrated Encryption Scheme (ECIES). This allows you to sign, verify, encrypt and decrypt using elliptic curve keys.

Swift version

The latest version of BlueECC requires Swift 5.2 or later. You can download this version of the Swift binaries by following this link. Compatibility with other Swift versions is not guaranteed.

Usage

Add dependencies

Add the BlueECC package to the dependencies within your application’s Package.swift file. Substitute "x.x.x" with the latest BlueECC release.

.package(url: "https://github.com/Kitura/BlueECC.git", from: "x.x.x")

Add CryptorECC to your target's dependencies:

.target(name: "example", dependencies: ["CryptorECC"]),

Import package

import CryptorECC

Getting Started

Elliptic curve private key

you can generate an ECPrivate key using BlueECC.

let p256PrivateKey = try ECPrivateKey.make(for: .prime256v1)

You can then view the key in it's PEM format as follows:

let privateKeyPEM = p256PrivateKey.pemString

The following curves are supported:

  • prime256v1
  • secp384r1
  • secp521r1

Alternatively, you may generate private key using a third party provider:

let privateKey =
"""
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQglf7ztYnsaHX2yiHJ
meHFl5dg05y4a/hD7wwuB7hSRpmhRANCAASKRzmboLbG0NZ54B5PXxYSU7fvO8U7
PyniQCWG+Agc3bdcgKU0RKApWYuBJKrZqyqLB2tTlgdtwcWSB0AEzVI8
-----END PRIVATE KEY-----
"""

The following commands generate private keys for the three supported curves as .pem files:

// p-256
$ openssl ecparam -name prime256v1 -genkey -noout -out key.pem
// p-384
$ openssl ecparam -name secp384r1 -genkey -noout -out key.pem
// p-521
$ openssl ecparam -name secp521r1 -genkey -noout -out key.pem

These keys will be formatted as follows:

let privateKey =
"""
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIJX+87WJ7Gh19sohyZnhxZeXYNOcuGv4Q+8MLge4UkaZoAoGCCqGSM49
AwEHoUQDQgAEikc5m6C2xtDWeeAeT18WElO37zvFOz8p4kAlhvgIHN23XIClNESg
KVmLgSSq2asqiwdrU5YHbcHFkgdABM1SPA==
-----END EC PRIVATE KEY-----
"""

The key string can then be used to initialize an ECPrivateKey instance:

let eccPrivateKey = try ECPrivateKey(key: privateKey)

Elliptic curve public key

You can use OpenSSL to generate an elliptic curve public key .pem file from any of the above elliptic curve private key files:

$ openssl ec -in key.pem -pubout -out public.pem

This will produce a public key formatted as follows:

let publicKey =
"""
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEikc5m6C2xtDWeeAeT18WElO37zvF
Oz8p4kAlhvgIHN23XIClNESgKVmLgSSq2asqiwdrU5YHbcHFkgdABM1SPA==
-----END PUBLIC KEY-----
"""

These keys can then be used to initialize an ECPrivateKey instance:

let eccPublicKey = try ECPublicKey(key: publicKey)

Alternatively, you can extract the public key from your ECPrivateKey:

let eccPublicKey = try eccPrivateKey.extractPublicKey()
print(eccPublicKey.pemString)

Signing String or Data

BlueECC extends String and Data so you can call sign directly on your plaintext using an EC private key. This creates an ECSignature containing the r and s signature values:

let message = "hello world"
let signature = try message.sign(with: eccPrivateKey)

Verifying the signature

Use the public key to verify the signature for the plaintext:

let verified = signature.verify(plaintext: message, using: eccPublicKey)
if verified {
    print("Signature is valid for provided plaintext")
}

Encrypting String or Data

Use the public key to encrypt your plaintext String or Data to encrypted Data or an encrypted Base64Encoded String:

let encryptedData = try "Hello World".encrypt(with: eccPublicKey)
print(encryptedData.base64EncodedString())

Decrypting to plaintext

Use the private key to decrypt the encrypted Data or Base64Encoded String to plaintext Data or UTF8 String:

let decryptedData = try encryptedData.decrypt(with: eccPrivateKey)
print(String(data: decryptedData, encoding: .utf8))

Encryption interoperability

Cross platform encryption and decryption is currently only supported with prime256v1 curves. The secp384r1 and secp521r1 curves do not support Linux encryption with Apple platform decryption and vice versa.

If you would like to interoperate with this repo, The following describes the encryption process:

  • Generate an ephemeral EC key pair
  • Use ECDH of your EC pair to generate a symmetric key
  • Use SHA256 ANSI x9.63 Key Derivation Function with the ephemeral public key to generate a 32 byte key
  • Use the first 16 bytes as an AES-GCM key
  • Use the second 16 bytes as the initialization vector (IV)
  • Use aes_128_gcm to encrypt the plaintext and generate a 16 byte GCM tag
  • Send the ephemeral public key, encrypted data and GCM tag

This is equivalent to: kSecKeyAlgorithmECIESEncryptionStandardVariableIVX963SHA256AESGCM when using apple security.

API Documentation

For more information visit our API reference.

Community

We love to talk server-side Swift, and Kitura. Join our Slack to meet the team!

License

This library is licensed under Apache 2.0. Full license text is available in LICENSE.

blueecc's People

Contributors

andrew-lees11 avatar dannys42 avatar djones6 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

blueecc's Issues

Use SecKeyCreateRandomKey

Similar to Kitura/BlueRSA#77

Need to stop using SecKeyGeneratePair, and switch to SecKeyCreateRandomKey.

I'll work on getting a PR put together, but, similar to BlueRSA, would like an updated release to go to Cocoapods, so it can be consumed that way.

-Thanks!

Add support for generating keys within the repo

Currently we require users to use OpenSSL command line to generate EC keys.
It would be nice if they could use this repo to create the key pairs without any further installations.
This would also allow you dynamically generate key pairs so you could create a short lived keys.

secp521r1 signatures based on r|s are sometimes invalid

We saw some issues when using Swift-JWT with the ES512 signer, which uses the BlueECC crypto underneath. The issue was, that the generated signature is sometimes invalid, producing r and s values with 65 Bytes instead of 66 Bytes in ~0,1% of the cases.

By adding the following test, you see that the verification fails sometimes:

func test_SignatureGeneration() throws {
    let secp521r1Key = try ECPrivateKey.make(for: .secp521r1)
    let secp521r1PubKey = try secp521r1Key.extractPublicKey()
    do {
        for i in 1 ..< 10000 {
            let signature521 = try "Hello world".sign(with: secp521r1Key)
            XCTAssertEqual(signature521.r.count, 66) // those two assertions
            XCTAssertEqual(signature521.s.count, 66) // will sometimes fail
            let copiedSignature = try ECSignature(r: signature521.r, s: signature521.s)
            let verified521 = copiedSignature.verify(plaintext: "Hello world", using: secp521r1PubKey)
            XCTAssertTrue(verified521)
            print("\(i) done")
        }
    } catch {
        return XCTFail("test_SignatureGeneration failed: \(error)")
    }
}

During debugging, I found that in the asn1ToRSSig method, the returned trimmed r and s values sometimes have 65 Bytes instead of 66. However, I was unsuccessful just padding them with null bytes.

The error must be somewhere in the transformation from the original asn1 to the rs format, because when using asn1 in the test above directly:

let copiedSignature = try ECSignature(asn1: signature521.asn1)

the error does not occur.

Note: I was unable to reproduce this on a different curve (e.g. es384). I guess this is due to the nature of the 521 bit curve.

Add patten matching for error.

We use the same pattern as Swift-JWT for errors. This pr adds pattern matching for the errors so they are easier to handle and it would be nice to implement them in this repo as well.

Carthage

Could you please provide support of Carthage dependency manager?

Swift-JWT fails to compile

When using Colab

%install '.package(url: "https://github.com/IBM-Swift/Swift-JWT.git", from: "3.0.0")' SwiftJWT

It appears to be using Swift version 5.3-dev rather than 5.2. I know 5.3 is being announced this week.

Details below. This was working late 2019.

[4/24] Merging module Logging
SIL verification failed: return with stack allocs that haven't been deallocated: state.Stack.empty()
Verifying instruction:
%18 = struct $EllipticCurve (%0 : $EllipticCurve.InternalRepresentation, %1 : $Optional, %2 : $Int32, %6 : $@callee_guaranteed (Optional<UnsafePointer>, Int, Optional<UnsafeMutablePointer>) -> Optional<UnsafeMutablePointer>, %17 : $Int, %3 : $Int) // user: %19
-> return %18 : $EllipticCurve // id: %19
In function:
// EllipticCurve.init(internalRepresentation:signingAlgorithm:nativeCurve:keySize:)
sil private [ossa] @$s10CryptorECC13EllipticCurveV22internalRepresentation16signingAlgorithm06nativeD07keySizeA2C08InternalF033_9ED7A968DCE1F3D03FCEF1D6E4BAB780LLO_s13OpaquePointerVSgs5Int32VSitcAILlfC : $@convention(method) (EllipticCurve.InternalRepresentation, Optional, Int32, Int, @thin EllipticCurve.Type) -> @owned EllipticCurve {
// %0 // user: %18
// %1 // user: %18
// %2 // user: %18
// %3 // user: %18
bb0(%0 : $EllipticCurve.InternalRepresentation, %1 : $Optional, %2 : $Int32, %3 : $Int, %4 : $@thin EllipticCurve.Type):
// function_ref @nonobjc SHA256(:::)
%5 = function_ref @$sSo6SHA256ySpys5UInt8VGSgSPyACGSg_SiAEtFTO : $@convention(thin) (Optional<UnsafePointer>, Int, Optional<UnsafeMutablePointer>) -> Optional<UnsafeMutablePointer> // user: %6
%6 = thin_to_thick_function %5 : $@convention(thin) (Optional<UnsafePointer>, Int, Optional<UnsafeMutablePointer>) -> Optional<UnsafeMutablePointer> to $@callee_guaranteed (Optional<UnsafePointer>, Int, Optional<UnsafeMutablePointer>) -> Optional<UnsafeMutablePointer> // user: %18
%7 = alloc_stack $Int // users: %17, %15
%8 = metatype $@thin Int.Type
%9 = metatype $@thick Int.Type // user: %15
// function_ref SHA256_DIGEST_LENGTH.getter
%10 = function_ref @$sSC20SHA256_DIGEST_LENGTHs5Int32Vvg : $@convention(thin) () -> Int32 // user: %11
%11 = apply %10() : $@convention(thin) () -> Int32 // user: %13
%12 = alloc_stack $Int32 // users: %16, %15, %13
store %11 to [trivial] %12 : $*Int32 // id: %13
// function_ref SignedInteger<>.init(
:)
%14 = function_ref @$sSZss17FixedWidthIntegerRzrlEyxqd__cSzRd__lufC : $@convention(method) <τ_0_0 where τ_0_0 : FixedWidthInteger, τ_0_0 : SignedInteger><τ_1_0 where τ_1_0 : BinaryInteger> (@in τ_1_0, @thick τ_0_0.Type) -> @out τ_0_0 // user: %15
%15 = apply %14<Int, Int32>(%7, %12, %9) : $@convention(method) <τ_0_0 where τ_0_0 : FixedWidthInteger, τ_0_0 : SignedInteger><τ_1_0 where τ_1_0 : BinaryInteger> (@in τ_1_0, @thick τ_0_0.Type) -> @out τ_0_0
dealloc_stack %12 : $*Int32 // id: %16
%17 = load [trivial] %7 : $*Int // user: %18
%18 = struct $EllipticCurve (%0 : $EllipticCurve.InternalRepresentation, %1 : $Optional, %2 : $Int32, %6 : $@callee_guaranteed (Optional<UnsafePointer>, Int, Optional<UnsafeMutablePointer>) -> Optional<UnsafeMutablePointer>, %17 : $Int, %3 : $Int) // user: %19
return %18 : $EllipticCurve // id: %19
} // end sil function '$s10CryptorECC13EllipticCurveV22internalRepresentation16signingAlgorithm06nativeD07keySizeA2C08InternalF033_9ED7A968DCE1F3D03FCEF1D6E4BAB780LLO_s13OpaquePointerVSgs5Int32VSitcAILlfC'

Stack dump:
0. Program arguments: /swift/toolchain/usr/bin/swift -frontend -c /content/swift-install/package/.build/checkouts/BlueECC/Sources/CryptorECC/ASN1.swift /content/swift-install/package/.build/checkouts/BlueECC/Sources/CryptorECC/Data+Extensions.swift /content/swift-install/package/.build/checkouts/BlueECC/Sources/CryptorECC/ECDecryptable.swift /content/swift-install/package/.build/checkouts/BlueECC/Sources/CryptorECC/ECEncryptable.swift /content/swift-install/package/.build/checkouts/BlueECC/Sources/CryptorECC/ECError.swift /content/swift-install/package/.build/checkouts/BlueECC/Sources/CryptorECC/ECPrivateKey.swift -primary-file /content/swift-install/package/.build/checkouts/BlueECC/Sources/CryptorECC/ECPublicKey.swift -primary-file /content/swift-install/package/.build/checkouts/BlueECC/Sources/CryptorECC/ECSignable.swift -primary-file /content/swift-install/package/.build/checkouts/BlueECC/Sources/CryptorECC/ECSignature.swift -primary-file /content/swift-install/package/.build/checkouts/BlueECC/Sources/CryptorECC/EllipticCurve.swift -primary-file /content/swift-install/package/.build/checkouts/BlueECC/Sources/CryptorECC/SSLPointerTricks.swift -supplementary-output-file-map /tmp/supplementaryOutputs-0d11d3 -target x86_64-unknown-linux-gnu -disable-objc-interop -I /content/swift-install/package/.build/x86_64-unknown-linux-gnu/debug -enable-testing -g -module-cache-path /content/swift-install/package/.build/x86_64-unknown-linux-gnu/debug/ModuleCache -swift-version 5 -Onone -D SWIFT_PACKAGE -D DEBUG -enable-anonymous-context-mangled-names -Xcc -fmodule-map-file=/content/swift-install/package/.build/checkouts/OpenSSL/Sources/OpenSSL/module.modulemap -parse-as-library -module-name CryptorECC -o /content/swift-install/package/.build/x86_64-unknown-linux-gnu/debug/CryptorECC.build/ECPublicKey.swift.o -o /content/swift-install/package/.build/x86_64-unknown-linux-gnu/debug/CryptorECC.build/ECSignable.swift.o -o /content/swift-install/package/.build/x86_64-unknown-linux-gnu/debug/CryptorECC.build/ECSignature.swift.o -o /content/swift-install/package/.build/x86_64-unknown-linux-gnu/debug/CryptorECC.build/EllipticCurve.swift.o -o /content/swift-install/package/.build/x86_64-unknown-linux-gnu/debug/CryptorECC.build/SSLPointerTricks.swift.o -index-store-path /content/swift-install/package/.build/x86_64-unknown-linux-gnu/debug/index/store -index-system-modules

  1. Swift version 5.3-dev (LLVM 5a342fdfac, Swift ec4315266b)
  2. While evaluating request SILGenSourceFileRequest(SIL Generation for file "/content/swift-install/package/.build/checkouts/BlueECC/Sources/CryptorECC/EllipticCurve.swift")
  3. While silgen emitConstructor SIL function "@$s10CryptorECC13EllipticCurveV22internalRepresentation16signingAlgorithm06nativeD07keySizeA2C08InternalF033_9ED7A968DCE1F3D03FCEF1D6E4BAB780LLO_s13OpaquePointerVSgs5Int32VSitcAILlfC".

xCode 11.4 / Swift 5.2 crashes on parsing PublicKey in simulators and <= iOS 12

The following code crashes on Simulators and my iOS12.4 iPhone 6when built with xCode 11.4, but works ok on xCode 11.3.

Does not seem to crash on iOS 13.4.

let p256PrivateKey = try! ECPrivateKey.make(for: .prime256v1) print(p256PrivateKey.pemString) publicKey = try! p256PrivateKey.extractPublicKey() print(publicKey.pemString)

Error is happening at this line:
guard let secKey = SecKeyCreateWithData(keyData as CFData, [kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom, kSecAttrKeyClass: kSecAttrKeyClassPublic] as CFDictionary, &error)

Error:
objc[1049]: Class _TtGCs18_DictionaryStorageaSo11CFStringRefS0__$ is implemented in both /usr/lib/swift/libswiftCore.dylib (0x1c1d492d8) and /usr/lib/swift/libswiftCore.dylib (0x1c1d495e8). One of the two will be used. Which one is undefined. Fatal error: invalid unsafeDowncast

Conversion from Data to ASN1 crashes

Description:
When initializing ECSignature with wrong data, the conversion in ASN1.toASN1Element crashes for at

default: // octet string
    let (length, lengthOfLength) = readLength(data: data.advanced(by: 1))

Reproduce:

let signed = try ECSignature(asn1: Data(base64Encoded: "abcd")!)

Expected:
ECSignature throws an error.

Actual:
Program crashes with the following message:
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

Cocoapods

Any support for cocoapods dependency manager?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.