From 45f7b02aafb85e9bd9eba14b38d73538f8324231 Mon Sep 17 00:00:00 2001 From: 5HT Date: Thu, 3 Aug 2023 00:32:36 +0300 Subject: [PATCH] own ecdsa --- lib/ecdsa.ex | 2 +- lib/test/curve.ex | 18 ++++++++++++ lib/test/ecdsa.ex | 54 +++++++++++++++++++++++++++++++++++ lib/test/integer.ex | 63 +++++++++++++---------------------------- lib/test/knownCurves.ex | 53 ++++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 45 deletions(-) create mode 100644 lib/test/curve.ex create mode 100644 lib/test/ecdsa.ex create mode 100644 lib/test/knownCurves.ex diff --git a/lib/ecdsa.ex b/lib/ecdsa.ex index 3ee62d0..4947f5e 100644 --- a/lib/ecdsa.ex +++ b/lib/ecdsa.ex @@ -1,4 +1,4 @@ -defmodule CA.ECDSA do +defmodule CA.ECDSA.OTP do @moduledoc "CA/ECDSA ECC Signature." require CA diff --git a/lib/test/curve.ex b/lib/test/curve.ex new file mode 100644 index 0000000..9b70ed4 --- /dev/null +++ b/lib/test/curve.ex @@ -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 diff --git a/lib/test/ecdsa.ex b/lib/test/ecdsa.ex new file mode 100644 index 0000000..3d7ad68 --- /dev/null +++ b/lib/test/ecdsa.ex @@ -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 diff --git a/lib/test/integer.ex b/lib/test/integer.ex index 874491d..aad3f86 100644 --- a/lib/test/integer.ex +++ b/lib/test/integer.ex @@ -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 diff --git a/lib/test/knownCurves.ex b/lib/test/knownCurves.ex new file mode 100644 index 0000000..82e414b --- /dev/null +++ b/lib/test/knownCurves.ex @@ -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