RosettaCodeData/Task/Elliptic-Curve-Digital-Signature-Algorithm/Raku/elliptic-curve-digital-signature-algorithm.raku
2023-09-01 09:35:06 -07:00

101 lines
3 KiB
Raku
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

module EC {
use FiniteField;
our class Point {
has ($.x, $.y);
submethod TWEAK { fail unless $!y**2 == $!x**3 + $*a*$!x + $*b }
multi method gist(::?CLASS:U:) { "Point at Horizon" }
multi method new($x, $y) { samewith :$x, :$y }
}
multi infix:<==>(Point:D $A, Point:D $B) is export { $A.x == $B.x and $A.y == $B.y }
multi prefix:<->(Point $P) returns Point is export { Point.new: $P.x, -$P.y }
multi infix:<+>(Point $A, Point:U) is export { $A }
multi infix:<+>(Point:U, Point $B) is export { $B }
multi infix:<+>(Point:D $A, Point:D $B) returns Point is export {
my ;
if $A.x == $B.x and $A.y == -$B.y { return Point }
elsif $A == $B {
return Point if $A.y == 0;
= (3*$A.x² + $*a) / (2*$A.y);
}
else { = ($A.y - $B.y) / ($A.x - $B.x); }
given **2 - $A.x - $B.x {
return Point.new: $_, *($A.x - $_) - $A.y;
}
}
multi infix:<*>(0, Point ) is export { Point }
multi infix:<*>(1, Point $p) is export { $p }
multi infix:<*>(2, Point:D $p) is export { $p + $p }
multi infix:<*>(Int $n, Point $p) is export { 2*(($n div 2)*$p) + ($n mod 2)*$p }
}
import EC;
module ECDSA {
use Digest::SHA256::Native;
our class Signature {
has UInt ($.c, $.d);
multi method new(Str $message, UInt :$private-key) {
my $z = :16(sha256-hex $message) % $*n;
loop (my $k = my $s = my $r = 0; $r == 0; ) {
loop ( $r = $s = 0 ; $r == 0 ; ) {
$r = (( $k = (1..^$*n).roll ) * $*G).x % $*n;
}
{
use FiniteField; my $*modulus = $*n;
$s = ($z + $r*$private-key) / $k;
}
}
samewith c => $r, d => $s;
}
multi method verify(Str $message, EC::Point :$public-key) {
my $z = :16(sha256-hex $message) % $*n;
my ($u1, $u2);
{
use FiniteField;
my $*modulus = $*n;
my $w = 1/$!d;
($u1, $u2) = $z*$w, $!c*$w;
}
my $p = ($u1 * $*G) + ($u2 * $public-key);
die unless ($p.x mod $*n) == ($!c mod $*n);
}
}
}
my ($*a, $*b) = 355, 671;
my $*modulus = my $*p = 1073741789;
my $*G = EC::Point.new: 13693, 10088;
my $*n = 1073807281;
die "G is not of order n" if $*n*$*G;
my $private-key = ^$*n .pick;
my $public-key = $private-key*$*G;
my $message = "Show me the monKey";
my $signature = ECDSA::Signature.new: $message, :$private-key;
printf "The curve E is : 𝑦² = 𝑥³ + %s 𝑥 + %s (mod %s)\n", $*a, $*b, $*p;
printf "with generator G at : (%s, %s)\n", $*G.x, $*G.y;
printf "G's order is : %d\n", $*n;
printf "The private key is : %d\n", $private-key;
printf "The public key is at : (%s, %s)\n", $public-key.x, $public-key.y;
printf "The message is : %s\n", $message;
printf "The signature is : (%s, %s)\n", $signature.c, $signature.d;
{
use Test;
lives-ok {
$signature.verify: $message, :$public-key;
}, "good signature for <$message>";
my $altered = $message.subst(/monKey/, "money");
dies-ok {
$signature.verify: $altered, :$public-key
}, "wrong signature for <$altered>";
}