Skip to content

Commit

Permalink
own ecdsa
Browse files Browse the repository at this point in the history
  • Loading branch information
5HT committed Aug 2, 2023
1 parent f43ffcc commit 45f7b02
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 45 deletions.
2 changes: 1 addition & 1 deletion lib/ecdsa.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule CA.ECDSA do
defmodule CA.ECDSA.OTP do

@moduledoc "CA/ECDSA ECC Signature."
require CA
Expand Down
18 changes: 18 additions & 0 deletions lib/test/curve.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
defmodule CA.Curve do
@moduledoc false
require CA.Integer
require CA.Point
defstruct [:A, :B, :P, :N, :G, :name, :oid]
def contains?(curve, p) do
cond do
p.x < 0 || p.x > curve."P" - 1 -> false
p.y < 0 || p.y > curve."P" - 1 -> false
CA.Integer.ipow(p.y, 2) - (CA.Integer.ipow(p.x, 3) + curve."A" * p.x + curve."B")
|> CA.Integer.modulo(curve."P") != 0 -> false
true -> true
end
end
def getLength(curve) do
div(1 + String.length(Integer.to_string(curve."N", 16)), 2)
end
end
54 changes: 54 additions & 0 deletions lib/test/ecdsa.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
defmodule CA.ECDSA do
require CA.Point
require CA.Integer
require CA.Jacobian
require CA.ECDSA.OTP

def numberFromString(data) do
Base.encode16(data)
|> Integer.parse(16)
|> (fn {parsedInt, ""} -> parsedInt end).()
end

def sign(message, privateKey, options \\ []) do
%{hashfunc: hashfunc} = Enum.into(options, %{hashfunc: :sha256})
number = :crypto.hash(hashfunc, message) |> numberFromString()
curve = CA.KnownCurves.secp256k1()
randNum = CA.Integer.between(1, curve."N" - 1)
r = CA.Jacobian.multiply(curve."G", randNum, curve."N", curve."A", curve."P").x
|> CA.Integer.modulo(curve."N")
s = ((number + r * privateKey) * CA.Jacobian.inv(randNum, curve."N"))
|> CA.Integer.modulo(curve."N")
{r, s}
end

def private(bin), do: :erlang.element(2,X509.PrivateKey.from_pem(bin))
def public(bin), do: #:erlang.element(1,:erlang.element(2,
:public_key.pem_entry_decode(hd(:public_key.pem_decode(bin)))

def verify(file, signature, pub) do
{:ok, msg} = :file.read_file file
{:ok, pem} = :file.read_file pub
verify(msg, CA.ECDSA.OTP.signature(signature), public(pem), [])
end

def verify(message, {r,s}, publicKey, options) do
%{hashfunc: hashfunc} = Enum.into(options, %{hashfunc: :sha256})
number = :crypto.hash(hashfunc, message) |> numberFromString()
curve = CA.KnownCurves.secp256k1()
inv = CA.Jacobian.inv(s, curve."N")
v = CA.Jacobian.add(
CA.Jacobian.multiply(curve."G", CA.Integer.modulo(number * inv, curve."N"),
curve."N", curve."A", curve."P"),
CA.Jacobian.multiply(publicKey, CA.Integer.modulo(r * inv, curve."N"),
curve."N", curve."A", curve."P" ), curve."A", curve."P")
cond do
r < 1 || r >= curve."N" -> false
s < 1 || s >= curve."N" -> false
CA.Point.isAtInfinity?(v) -> false
CA.Integer.modulo(v.x, curve."N") != r -> false
true -> true
end
end

end
63 changes: 19 additions & 44 deletions lib/test/integer.ex
Original file line number Diff line number Diff line change
@@ -1,61 +1,36 @@
defmodule CA.Integer do
@moduledoc false

import Bitwise

def modulo(x, n) do
rem(x, n)
|> correctNegativeModulo(n)
end

def modulo(x, n), do: rem(x, n) |> correctNegativeModulo(n)
def correctNegativeModulo(r, n) when r < 0, do: r + n
def correctNegativeModulo(r, _n), do: r

def ipow(base, p, acc \\ 1)
def ipow(base, p, acc) when p > 0, do: ipow(base, p - 1, base * acc)
def ipow(_base, _p, acc), do: acc

def between(minimum, maximum) when minimum < maximum do
range = maximum - minimum + 1
{bytesNeeded, mask} = calculateParameters(range)
randomNumber = :crypto.strong_rand_bytes(bytesNeeded)
|> :binary.bin_to_list()
|> bytesToNumber &&& mask

if randomNumber < range do
minimum + randomNumber
else
between(minimum, maximum)
end
range = maximum - minimum + 1
{bytesNeeded, mask} = calculateParameters(range)
randomNumber = :crypto.strong_rand_bytes(bytesNeeded)
|> :binary.bin_to_list()
|> bytesToNumber &&& mask

if randomNumber < range do
minimum + randomNumber
else
between(minimum, maximum)
end
end

def bytesToNumber(randomBytes, randomNumber \\ 0, i \\ 0)
def bytesToNumber([randomByte | otherRandomBytes], randomNumber, i), do:
bytesToNumber(otherRandomBytes, randomNumber ||| randomByte <<< (8 * i), i + 1)
def bytesToNumber([], randomNumber, _i), do: randomNumber

def bytesToNumber([randomByte | otherRandomBytes], randomNumber, i) do
bytesToNumber(
otherRandomBytes,
randomNumber ||| randomByte <<< (8 * i),
i + 1
)
end
def calculateParameters(range), do: calculateParameters(range, 1, 0)
def calculateParameters(range, mask, bitsNeeded) when range > 0, do:
calculateParameters(range >>> 1, mask <<< 1 ||| 1, bitsNeeded + 1)
def calculateParameters(_range, mask, bitsNeeded), do: {div(bitsNeeded, 8) + 1, mask}

def bytesToNumber([], randomNumber, _i) do
randomNumber
end

def calculateParameters(range) do
calculateParameters(range, 1, 0)
end

def calculateParameters(range, mask, bitsNeeded) when range > 0 do
calculateParameters(
range >>> 1,
mask <<< 1 ||| 1,
bitsNeeded + 1
)
end

def calculateParameters(_range, mask, bitsNeeded) do
{div(bitsNeeded, 8) + 1, mask}
end
end
53 changes: 53 additions & 0 deletions lib/test/knownCurves.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
defmodule CA.KnownCurves do
require CA.Curve
require CA.Point

@secp256k1Oid [1, 3, 132, 0, 10]
@secp256k1name :secp256k1
@prime256v1 [1, 2, 840, 10045, 3, 1, 7]
@prime256v1name :prime256v1

def getCurveByOid(oid) do
case oid do
@secp256k1Oid -> secp256k1()
@prime256v1 -> prime256v1()
end
end

def getCurveByName(name) do
case name do
@secp256k1name -> secp256k1()
@prime256v1name -> prime256v1()
end
end

def secp256k1() do
%CA.Curve{
name: @secp256k1name,
A: 0x0000000000000000000000000000000000000000000000000000000000000000,
B: 0x0000000000000000000000000000000000000000000000000000000000000007,
P: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F,
N: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,
G: %CA.Point{
x: 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
y: 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8
},
oid: @secp256k1Oid
}
end

def prime256v1() do
%CA.Curve{
name: @prime256v1name,
A: 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC,
B: 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B,
P: 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF,
N: 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551,
G: %CA.Point{
x: 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296,
y: 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5
},
oid: @prime256v1
}
end
end

0 comments on commit 45f7b02

Please sign in to comment.