This is an Elixir 1.9+ translation of Stark Bank`s ecdsa-python. It is compatible with OpenSSL and uses elegant math such as Jacobian Coordinates to speed up the ECDSA on pure Elixir.
To install Stark Bank`s ECDSA-Elixir, add starkbank_ecdsa
to your list of dependencies in mix.exs
:
def deps do
[
{:starkbank_ecdsa, "~> 1.1.0"}
]
end
We currently support secp256k1
and prime256v1
, but it's super easy to add more curves to the project. Just add them on lib/curve/knownCurves.ex
We ran a test on a MAC Pro i7 2017. The libraries were run 100 times and the averages displayed bellow were obtained:
Library | sign | verify |
---|---|---|
crypto | 1.0ms | 2.0ms |
starkbank_ecdsa | 1.9ms | 3.8ms |
How to sign a json message for Stark Bank:
# Generate privateKey from PEM string
privateKey =
EllipticCurve.PrivateKey.fromPem!("""
-----BEGIN EC PARAMETERS-----
BgUrgQQACg==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIODvZuS34wFbt0X53+P5EnSj6tMjfVK01dD1dgDH02RzoAcGBSuBBAAK
oUQDQgAE/nvHu/SQQaos9TUljQsUuKI15Zr5SabPrbwtbfT/408rkVVzq8vAisbB
RmpeRREXj5aog/Mq8RrdYy75W9q/Ig==
-----END EC PRIVATE KEY-----
""")
# Create message from json (using external Jason package: https://hexdocs.pm/jason/Jason.html)
message =
Jason.encode!(%{
transfers: [
%{
amount: 100_000_000,
taxId: "594.739.480-42",
name: "Daenerys Targaryen Stormborn",
bankCode: "341",
branchCode: "2201",
accountNumber: "76543-8",
tags: ["daenerys", "targaryen", "transfer-1-external-id"]
}
]
})
signature = EllipticCurve.Ecdsa.sign(message, privateKey)
# Generate Signature in base64. This result can be sent to Stark Bank in the request header as the Digital-Signature parameter.
signature
|> EllipticCurve.Signature.toBase64()
|> IO.puts()
# To double check if the message matches the signature, do this:
publicKey = privateKey |> EllipticCurve.PrivateKey.getPublicKey()
Ecdsa.verify?(message, signature, publicKey) |> IO.puts()
Simple use:
# Generate new Keys
privateKey = EllipticCurve.PrivateKey.generate()
publicKey = EllipticCurve.PrivateKey.getPublicKey(privateKey)
message = "My test message"
# Generate Signature
signature = EllipticCurve.Ecdsa.sign(message, privateKey)
# To verify if the signature is valid
EllipticCurve.Ecdsa.verify?(message, signature, publicKey) |> IO.puts()
This library is compatible with OpenSSL, so you can use it to generate keys:
openssl ecparam -name secp256k1 -genkey -out privateKey.pem
openssl ec -in privateKey.pem -pubout -out publicKey.pem
Create a message.txt file and sign it:
openssl dgst -sha256 -sign privateKey.pem -out signatureDer.txt message.txt
To verify, do this:
publicKeyPem = File.read!("publicKey.pem")
signatureDer = File.read!("signatureDer.txt")
message = File.read!("message.txt")
publicKey = EllipticCurve.PublicKey.fromPem!(publicKeyPem)
signature = EllipticCurve.Signature.fromDer!(signatureDer)
EllipticCurve.Ecdsa.verify?(message, signature, publicKey) |> IO.puts()
You can also verify it on terminal:
openssl dgst -sha256 -verify publicKey.pem -signature signatureDer.txt message.txt
NOTE: If you want to create a Digital Signature to use with Stark Bank, you need to convert the binary signature to base64.
openssl base64 -in signatureDer.txt -out signatureBase64.txt
You can do the same with this library:
signatureDer = File.read!("signatureDer.txt")
signature = EllipticCurve.Signature.fromDer!(signatureDer)
EllipticCurve.Signature.toBase64(signature) |> IO.puts()
mix test