Elliptic Curve Digital Signature Algorithm: Difference between revisions
Content added Content deleted
(julia example) |
|||
Line 918: | Line 918: | ||
Signature verified: true |
Signature verified: true |
||
</pre> |
</pre> |
||
=={{header|Julia}}== |
|||
<lang julia>module ToyECDSA |
|||
using SHA |
|||
import Base.in, Base.==, Base.+, Base.* |
|||
export ECDSA_Key, ECDSA_Public_Key, genkey, ECDSA_sign, isverifiedECDSA |
|||
# T will be BigInt in most applications |
|||
mutable struct CurveFP{T} |
|||
p::T |
|||
a::T |
|||
b::T |
|||
CurveFP(p, a::T, b::T) where T <: Number = new{T}(p, a, b) |
|||
end |
|||
Nullable = Union{Number, Nothing} |
|||
mutable struct PointEC{T} |
|||
curve::CurveFP{T} |
|||
x::T |
|||
y::T |
|||
order::Nullable |
|||
function PointEC(curve, x::T, y::T, order=nothing) where T <: Number |
|||
@assert((x, y) in curve) |
|||
new{T}(curve, x, y, order) |
|||
end |
|||
end |
|||
struct PointINF end |
|||
const INFINITY = PointINF() |
|||
function ==(point_a::PointEC, point_b::PointEC) |
|||
if point_a.curve == point_b.curve && point_a.x == point_b.x && point_a.y == point_b.y |
|||
return true |
|||
end |
|||
return false |
|||
end |
|||
function ==(curve_a::CurveFP, curve_b::CurveFP) |
|||
if curve_a.a == curve_b.a && curve_a.b == curve_b.b && curve_a.p == curve_b.p |
|||
return true |
|||
end |
|||
return false |
|||
end |
|||
+(point_a::PointINF, point_b::PointINF) = point_b |
|||
+(point_a::PointINF, point_b::PointEC) = point_b |
|||
+(point_a::PointEC, point_b::PointINF) = point_a |
|||
function +(point_a::PointEC, point_b::PointEC) |
|||
@assert(point_a.curve == point_b.curve) |
|||
if point_a.x == point_b.x |
|||
if (point_a.y + point_b.y) % point_a.curve.p == 0 |
|||
return INFINITY |
|||
else |
|||
return double(point_a) |
|||
end |
|||
end |
|||
p = point_a.curve.p |
|||
λ = (point_a.y - point_b.y) * inverse_mod(point_a.x - point_b.x, p) |
|||
xr = mod(λ * λ - point_a.x - point_b.x, p) |
|||
yr = mod(λ * (point_a.x - xr) - point_a.y, p) |
|||
return PointEC(point_a.curve, xr, yr, point_a.order) |
|||
end |
|||
function inverse_mod(a::Number, m::Number) |
|||
if a < 0 || m <= a |
|||
a = mod1(a, m) |
|||
end |
|||
c, d, r, s = a, m, 0, 1 |
|||
while c != 0 |
|||
q = d ÷ c |
|||
t = d - q * c |
|||
d = c |
|||
c = t |
|||
t = r - q * s |
|||
r = s |
|||
s = t |
|||
end |
|||
return d > 0 ? r : r + m |
|||
end |
|||
function leftmost_bit(x::Number) |
|||
@assert(x > 0) |
|||
result = BigInt(1) |
|||
while result <= x |
|||
result = 2*result |
|||
end |
|||
return div(result, 2) |
|||
end |
|||
*(point_a::PointINF, int_b::Number) = INFINITY |
|||
*(int_b::Number, point_a::PointINF) = INFINITY |
|||
*(int_b::Number, point_a::PointEC) = point_a * int_b |
|||
function *(point_a::PointEC, int_b::Number) |
|||
if point_a.order != nothing |
|||
int_b %= point_a.order |
|||
end |
|||
if int_b == 0 |
|||
return INFINITY |
|||
end |
|||
int_3b = 3 * int_b |
|||
negative_a = PointEC(point_a.curve, point_a.x, -point_a.y, point_a.order) |
|||
i = BigInt(leftmost_bit(int_3b) ÷ 2) |
|||
result = point_a |
|||
while i > 1 |
|||
result = double(result) |
|||
if (int_3b & i) != 0 && (int_b & i) == 0 |
|||
result = result + point_a |
|||
end |
|||
if ( int_3b & i ) == 0 && ( int_b & i ) != 0 |
|||
result = result + negative_a |
|||
end |
|||
i ÷= 2 |
|||
end |
|||
return result |
|||
end |
|||
in(z::Tuple, curve::CurveFP) = (z[2]^2 - (z[1]^3 + curve.a*z[1] + curve.b)) % curve.p == 0 |
|||
in(x::Number,y::Number, curve::CurveFP) = (y^2 -(x^3 + curve.a*x + curve.b)) % curve.p == 0 |
|||
in(p::PointEC, curve::CurveFP) = (p.y^2 - (p.x^3 + curve.a * p.x + curve.b)) % curve.p == 0 |
|||
in(point::PointINF, curve::CurveFP) = true |
|||
double(point_a::PointINF) = INFINITY |
|||
function double(point_a::PointEC) |
|||
p = point_a.curve.p |
|||
a = point_a.curve.a |
|||
l = mod((3 * point_a.x^2 + a) * inverse_mod(2 * point_a.y, p), p) |
|||
x3 = mod(l^2 - 2 * point_a.x, p) |
|||
y3 = mod(l * (point_a.x - x3) - point_a.y, p) |
|||
return PointEC(point_a.curve, x3, y3) |
|||
end |
|||
const secp256k1 = ( # use the Bitcoin ECDSA curve |
|||
p = big"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", |
|||
a = big"0x0", |
|||
b = big"0x7", |
|||
r = big"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", |
|||
Gx = big"0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", |
|||
Gy = big"0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", |
|||
) |
|||
@assert((secp256k1.Gy^2 - secp256k1.Gx^3 - 7) % secp256k1.p == 0) |
|||
function generatemultiplier(stdcurve) |
|||
return foldl((x, y) -> 16 * BigInt(x) + y, rand(0:16, ndigits(stdcurve.r - 1, base=16))) |
|||
end |
|||
struct ECDSA_Key |
|||
E::CurveFP |
|||
secret::BigInt |
|||
G::PointEC |
|||
r::BigInt |
|||
W::PointEC |
|||
end |
|||
struct ECDSA_Public_Key |
|||
E::CurveFP |
|||
G::PointEC |
|||
r::BigInt |
|||
W::PointEC |
|||
end |
|||
function genkey(curve=secp256k1) # default: use Bitcoin standard EC curve secp256k1 |
|||
E = CurveFP(curve.p, curve.a, curve.b) |
|||
s = generatemultiplier(curve) |
|||
G = PointEC(E, curve.Gx, curve.Gy, curve.r) |
|||
W = s * G |
|||
return ECDSA_Key(E, s, G, curve.r, W) |
|||
end |
|||
aspublickey(k::ECDSA_Key) = ECDSA_Public_Key(k.E, k.G, k.r, k.W) |
|||
privatekey(k::ECDSA_Key) = k.secret |
|||
function ECDSA_sign(m::String, key::ECDSA_Key, digestfunction=sha256) |
|||
r, f = key.r, digestfunction(codeunits(m)) # f = H(m) |
|||
# order of curve points length must be >= sha digest length (in bytes) |
|||
@assert(ndigits(r, base=16) >= length(f)) |
|||
c, d, bindigest = BigInt(0), BigInt(0), foldl((x, y) -> 16 * BigInt(x) + y, f) |
|||
while c == 0 || d == 0 |
|||
u = generatemultiplier(secp256k1) |
|||
V = u * key.G |
|||
c = mod(V.x, r) |
|||
d = mod((inverse_mod(u, r) * (bindigest + key.secret * c)), r) |
|||
end |
|||
return aspublickey(key), c, d |
|||
end |
|||
function isverifiedECDSA(m::String, publickey, c, d, digestfunction=sha256) |
|||
if 1 <= c < publickey.r && 1 <= d < publickey.r |
|||
r, f = publickey.r, digestfunction(codeunits(m)) |
|||
h, bindigest = inverse_mod(d, r), foldl((x, y) -> 16 * BigInt(x) + y, f) |
|||
h1, h2 = mod(bindigest * h, r), mod(c * h, r) |
|||
verifierpoint = h1 * publickey.G + h2 * publickey.W |
|||
return mod(verifierpoint.x, r) == c |
|||
end |
|||
return false |
|||
end |
|||
end # module |
|||
using .ToyECDSA |
|||
const key = genkey() |
|||
const msg = "Bill says this is an elliptic curve digital signature algorithm." |
|||
const altered = "Bill says this isn't an elliptic curve digital signature algorithm." |
|||
publickey, c, d = ECDSA_sign(msg, key) |
|||
println("ECDSA of message <$msg> verified: ", isverifiedECDSA(msg, publickey, c, d)) |
|||
println("ECDSA of message <$altered> verified: ", isverifiedECDSA(altered, publickey, c, d)) |
|||
</lang>{{out}} |
|||
<pre> |
|||
ECDSA of message <Bill says this is an elliptic curve digital signature algorithm.> verified: true |
|||
ECDSA of message <Bill says this isn't an elliptic curve digital signature algorithm.> verified: false |
|||
</pre> |
|||
=={{header|Phix}}== |
=={{header|Phix}}== |