├── .formatter.exs ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── config └── config.exs ├── lib ├── ecc.ex └── ecc │ └── crypto.ex ├── mix.exs └── test ├── ec_private_key.pem ├── ec_public_key.pem ├── ecc_lib_test.exs ├── ecc_server_test.exs └── test_helper.exs /.formatter.exs: -------------------------------------------------------------------------------- 1 | [ 2 | inputs: ["*.{ex,exs}", "{config,lib,test}/**/*.{ex,exs}"] 3 | ] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /_build 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: elixir 3 | elixir: 4 | - 1.7 5 | - 1.8 6 | - 1.9 7 | otp_release: 8 | - 22.0 9 | - 22.1.7 10 | install: 11 | - mix local.hex --force 12 | - yes y | mix deps.get 13 | script: yes y | mix test --no-start 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.1.3 2 | 3 | Add possibility to register ECC by an atom for GenServer 4 | 5 | ## 0.1.2 6 | 7 | Add support for use of ECC by a supervisor 8 | 9 | ## 0.1.1 10 | 11 | Also support Elixir v0.14 12 | 13 | ## 0.1.0 14 | 15 | Initial Release 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Marius Melzer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Elliptic Curve Cryptography (ECC) for Elixir [![[travis]](https://travis-ci.org/farao/elixir-ecc.png)](https://travis-ci.org/farao/elixir-ecc) 2 | ===================== 3 | 4 | An elixir library for elliptic curve cryptography (MIT License). You can use it to sign messages and to verify signatures with a public key. 5 | 6 | ### Generate public key pair 7 | 8 | Use an existing elliptic curve public key pair or generate one using openssl (adapt the curve name according to your needs): 9 | 10 | ``` 11 | openssl ecparam -out ec_private_key.pem -name secp521r1 -genkey 12 | openssl ec -in ec_private_key.pem -pubout -out ec_public_key.pem 13 | ``` 14 | ### Install 15 | 16 | Simply add ```{:ecc, "~>0.1.0"}``` to the dependencies in your projects ```mix.exs``` file and run ```mix deps.get ecc``` 17 | 18 | ### Use as GenServer-Module 19 | 20 | ECC is a GenServer-Module. You can start a new process passing in both the private and the public key combined in one (still pem-style) string: 21 | 22 | ```elixir 23 | pem_public = File.read!("ec_public_key.pem") 24 | pem_private = File.read!("ec_private_key.pem") 25 | pem = Enum.join([pem_public, pem_private]) 26 | 27 | {:ok, _} = ECC.start_link(pem, :ecc) 28 | {:ok, signature} = GenServer.call(:ecc, {:sign, "Hello", :sha512}) 29 | 30 | public_key = GenServer.call(:ecc, :get_public_key) 31 | {:ok, result} = GenServer.call(:ecc, {:verify_signature, "Hello", signature, public_key, :sha512}) 32 | IO.puts("Hello == Hello? #{result}") # true 33 | 34 | {:ok, result} = GenServer.call(:ecc, {:verify_signature, "World", signature, public_key, :sha512}) 35 | IO.puts("Hello == World? #{result}") # false 36 | ``` 37 | 38 | ### Use as a library 39 | 40 | You can also use ECC.Crypto as a library. The pem-string passed to ECC.Crypto.parse_public_key/1 needs to additionally include an EC PARAMETERS section. In the example, we therefore join both pems to one string: 41 | 42 | ```elixir 43 | pem_public = File.read!("ec_public_key.pem") 44 | pem_private = File.read!("ec_private_key.pem") 45 | pem = Enum.join([pem_public, pem_private]) 46 | 47 | {:ok, public_key} = ECC.Crypto.parse_public_key(pem) 48 | {:ok, private_key} = ECC.Crypto.parse_private_key(pem) 49 | 50 | {:ok, signature} = ECC.Crypto.sign("Hello", :sha512, private_key) 51 | {:ok, result} = ECC.Crypto.verify_signature("Hello", signature, :sha512, public_key) 52 | IO.puts("Hello == Hello? #{result}") # true 53 | 54 | {:ok, result} = ECC.Crypto.verify_signature("World", signature, :sha512, public_key) 55 | IO.puts("Hello == World? #{result}") # false 56 | ``` 57 | -------------------------------------------------------------------------------- /config/config.exs: -------------------------------------------------------------------------------- 1 | # This file is responsible for configuring your application 2 | # and its dependencies with the aid of the Mix.Config module. 3 | use Mix.Config 4 | 5 | # This configuration is loaded before any dependency and is restricted 6 | # to this project. If another project depends on this project, this 7 | # file won't be loaded nor affect the parent project. For this reason, 8 | # if you want to provide default values for your application for third- 9 | # party users, it should be done in your mix.exs file. 10 | 11 | # Sample configuration: 12 | # 13 | # config :logger, 14 | # level: :info, 15 | # format: "$time $metadata[$level] $message\n" 16 | 17 | # It is also possible to import configuration files, relative to this 18 | # directory. For example, you can emulate configuration per environment 19 | # by uncommenting the line below and defining dev.exs, test.exs and such. 20 | # Configuration from the imported file will override the ones defined 21 | # here (which is why it is important to import them last). 22 | # 23 | # import_config "#{Mix.env}.exs" 24 | -------------------------------------------------------------------------------- /lib/ecc.ex: -------------------------------------------------------------------------------- 1 | defmodule ECC do 2 | use GenServer 3 | 4 | def start(pem, register_name \\ nil) do 5 | if register_name do 6 | GenServer.start(__MODULE__, pem, name: register_name) 7 | else 8 | GenServer.start(__MODULE__, pem) 9 | end 10 | end 11 | 12 | def start_link(pem, register_name \\ nil) do 13 | if register_name do 14 | GenServer.start_link(__MODULE__, pem, name: register_name) 15 | else 16 | GenServer.start_link(__MODULE__, pem) 17 | end 18 | end 19 | 20 | def init(pem) do 21 | with {:ok, public_key} = ECC.Crypto.parse_public_key(pem), 22 | {:ok, private_key} = ECC.Crypto.parse_private_key(pem), 23 | do: {:ok, %{public: public_key, private: private_key}} 24 | end 25 | 26 | def handle_call(:get_public_key, _from, keys) do 27 | {:reply, keys.public, keys} 28 | end 29 | 30 | def handle_call({:sign, msg, hash_type}, _from, keys) do 31 | {:reply, ECC.Crypto.sign(msg, hash_type, keys.private), keys} 32 | end 33 | 34 | def handle_call({:verify_signature, msg, signature, public_key, hash_type}, _from, keys) do 35 | result = ECC.Crypto.verify_signature(msg, signature, hash_type, public_key) 36 | {:reply, result, keys} 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/ecc/crypto.ex: -------------------------------------------------------------------------------- 1 | defmodule ECC.Crypto do 2 | def parse_public_key(pem) do 3 | try do 4 | pem_keys = :public_key.pem_decode(pem) 5 | 6 | ec_params = 7 | find_entry(pem_keys, :EcpkParameters) 8 | |> :public_key.pem_entry_decode() 9 | 10 | {:SubjectPublicKeyInfo, pem_public, _} = find_entry(pem_keys, :SubjectPublicKeyInfo) 11 | 12 | {:SubjectPublicKeyInfo, _, ec_point} = 13 | :public_key.der_decode(:SubjectPublicKeyInfo, pem_public) 14 | 15 | public_key = {{:ECPoint, ec_point}, ec_params} 16 | {:ok, public_key} 17 | rescue 18 | e -> {:error, "Could not find or parse public key: #{e}"} 19 | end 20 | end 21 | 22 | def parse_private_key(pem) do 23 | try do 24 | private_key = 25 | pem 26 | |> :public_key.pem_decode() 27 | |> find_entry(:ECPrivateKey) 28 | |> :public_key.pem_entry_decode() 29 | 30 | {:ok, private_key} 31 | rescue 32 | e -> {:error, "Could not find or parse private key: #{e}"} 33 | end 34 | end 35 | 36 | def sign(msg, hash_type, private_key) do 37 | try do 38 | {:ok, :public_key.sign(msg, hash_type, private_key)} 39 | rescue 40 | e -> {:error, "Could not sign message: #{e}"} 41 | end 42 | end 43 | 44 | def verify_signature(msg, signature, hash_type, public_key) do 45 | try do 46 | {:ok, :public_key.verify(msg, hash_type, signature, public_key)} 47 | rescue 48 | e -> {:error, "Could not verify signature: #{e}"} 49 | end 50 | end 51 | 52 | defp find_entry(list, key) do 53 | Enum.find(list, &(elem(&1, 0) == key)) 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /mix.exs: -------------------------------------------------------------------------------- 1 | defmodule ElixirEllipticCurve.Mixfile do 2 | use Mix.Project 3 | 4 | def project do 5 | [ 6 | app: :ecc, 7 | version: "0.1.3", 8 | elixir: ">=1.6.0", 9 | description: description(), 10 | package: package(), 11 | deps: deps() 12 | ] 13 | end 14 | 15 | def application do 16 | [extra_applications: [:public_key]] 17 | end 18 | 19 | defp deps do 20 | [] 21 | end 22 | 23 | def description do 24 | """ 25 | An elixir module for elliptic curve cryptography. 26 | It can be used either as a library or as a GenServer-Task 27 | for signing messages and verifying signatures with a public key. 28 | """ 29 | end 30 | 31 | defp package do 32 | [ 33 | files: ["lib", "mix.exs", "README*", "LICENSE*", "ec_*_key.pem", "test"], 34 | contributors: ["Marius Melzer"], 35 | licenses: ["MIT"], 36 | links: %{"GitHub" => "https://github.com/farao/elixir-ecc"} 37 | ] 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /test/ec_private_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PARAMETERS----- 2 | BgUrgQQAIw== 3 | -----END EC PARAMETERS----- 4 | -----BEGIN EC PRIVATE KEY----- 5 | MIHbAgEBBEFNBBzkBJbAOJry2ve0y83LzTTZEhMx0W5J9jOQuYUm+NWEZOmAgLZz 6 | B+oQSluXtwZA1+uGtArvlO+yZD5vz1t4XqAHBgUrgQQAI6GBiQOBhgAEAQi24Wtt 7 | eEfmdMJHO/7n6F9jiMiVqbMdMbS8cZ7Q0nrrCoIzPVFS6ZqdvZgZ6/2zbjXwHiiu 8 | nQe91az9f9bUW7eqAQurOSDCva1ngCnXA6MpKVqWNZ3xlSp9nxI1v2Jb1Q1ajuh8 9 | a+v6jT6sG2hihxMgZ8YMJKh5fSR6Ed2TbNwonoI2 10 | -----END EC PRIVATE KEY----- 11 | -------------------------------------------------------------------------------- /test/ec_public_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBCLbha214R+Z0wkc7/ufoX2OIyJWp 3 | sx0xtLxxntDSeusKgjM9UVLpmp29mBnr/bNuNfAeKK6dB73VrP1/1tRbt6oBC6s5 4 | IMK9rWeAKdcDoykpWpY1nfGVKn2fEjW/YlvVDVqO6Hxr6/qNPqwbaGKHEyBnxgwk 5 | qHl9JHoR3ZNs3CiegjY= 6 | -----END PUBLIC KEY----- 7 | -------------------------------------------------------------------------------- /test/ecc_lib_test.exs: -------------------------------------------------------------------------------- 1 | defmodule ECC.LibTest do 2 | use ExUnit.Case 3 | 4 | test "signing/checking signatures" do 5 | pem_public = File.read!("test/ec_public_key.pem") 6 | pem_private = File.read!("test/ec_private_key.pem") 7 | pem = Enum.join([pem_public, pem_private]) 8 | 9 | {:ok, public_key} = ECC.Crypto.parse_public_key(pem) 10 | {:ok, private_key} = ECC.Crypto.parse_private_key(pem) 11 | 12 | {:ok, signature} = ECC.Crypto.sign("Hello", :sha512, private_key) 13 | assert {:ok, true} == ECC.Crypto.verify_signature("Hello", signature, :sha512, public_key) 14 | assert {:ok, false} == ECC.Crypto.verify_signature("World", signature, :sha512, public_key) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /test/ecc_server_test.exs: -------------------------------------------------------------------------------- 1 | defmodule ECC.ServerTest do 2 | use ExUnit.Case 3 | 4 | test "signing/checking signatures" do 5 | pem_public = File.read!("test/ec_public_key.pem") 6 | pem_private = File.read!("test/ec_private_key.pem") 7 | pem = Enum.join([pem_public, pem_private]) 8 | 9 | {:ok, _} = ECC.start_link(pem, :ecc) 10 | {:ok, signature} = GenServer.call(:ecc, {:sign, "Hello", :sha512}) 11 | 12 | public_key = GenServer.call(:ecc, :get_public_key) 13 | 14 | {:ok, result} = 15 | GenServer.call(:ecc, {:verify_signature, "Hello", signature, public_key, :sha512}) 16 | 17 | assert result 18 | 19 | {:ok, result} = 20 | GenServer.call(:ecc, {:verify_signature, "World", signature, public_key, :sha512}) 21 | 22 | assert not result 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /test/test_helper.exs: -------------------------------------------------------------------------------- 1 | ExUnit.start() 2 | --------------------------------------------------------------------------------