From 530b0fe326d8a9623e27e081b51512cdf1d5b5d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=20Luka=20=C5=A0ijanec?= Date: Tue, 20 Jun 2023 01:51:29 +0200 Subject: =?UTF-8?q?=C5=BE=20initial=20commit?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 + .gitmodules | 3 + "prog/\305\276/QR-Code-generator" | 1 + "prog/\305\276/app.html" | 473 ++++++++++++++++++++++++++++++++++++++ "prog/\305\276/composer.json" | 13 ++ "prog/\305\276/gen.html" | 60 +++++ "prog/\305\276/index.php" | 311 +++++++++++++++++++++++++ "prog/\305\276/package.json" | 5 + "prog/\305\276/test.php" | 72 ++++++ 9 files changed, 943 insertions(+) create mode 100644 .gitmodules create mode 160000 "prog/\305\276/QR-Code-generator" create mode 100644 "prog/\305\276/app.html" create mode 100644 "prog/\305\276/composer.json" create mode 100644 "prog/\305\276/gen.html" create mode 100644 "prog/\305\276/index.php" create mode 100644 "prog/\305\276/package.json" create mode 100755 "prog/\305\276/test.php" diff --git a/.gitignore b/.gitignore index 88d1567..23baf12 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,8 @@ core *.out .gdb_history __pycache__/ +db +composer.lock +package-lock.json +vendor/ +node_modules/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..edd56a2 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "prog/ž/QR-Code-generator"] + path = prog/ž/QR-Code-generator + url = https://github.com/nayuki/QR-Code-generator diff --git "a/prog/\305\276/QR-Code-generator" "b/prog/\305\276/QR-Code-generator" new file mode 160000 index 0000000..22fac31 --- /dev/null +++ "b/prog/\305\276/QR-Code-generator" @@ -0,0 +1 @@ +Subproject commit 22fac31bdf81da68730c177c0e931c93234d2a30 diff --git "a/prog/\305\276/app.html" "b/prog/\305\276/app.html" new file mode 100644 index 0000000..f5601e1 --- /dev/null +++ "b/prog/\305\276/app.html" @@ -0,0 +1,473 @@ + + + + + +
+
+ +
+
+
+ + +
+ + +gen.html +
my pubkey: +
+ +
+ +
+ + + diff --git "a/prog/\305\276/composer.json" "b/prog/\305\276/composer.json" new file mode 100644 index 0000000..63b5b5e --- /dev/null +++ "b/prog/\305\276/composer.json" @@ -0,0 +1,13 @@ +{ + "name": "sijanec/zcaron", + "type": "project", + "require": { + "mdanter/ecc": "^1.0" + }, + "authors": [ + { + "name": "Anton Luka Šijanec", + "email": "anton@sijanec.eu" + } + ] +} diff --git "a/prog/\305\276/gen.html" "b/prog/\305\276/gen.html" new file mode 100644 index 0000000..47fe749 --- /dev/null +++ "b/prog/\305\276/gen.html" @@ -0,0 +1,60 @@ + +

private

+ +
+

public

+ +
+ + diff --git "a/prog/\305\276/index.php" "b/prog/\305\276/index.php" new file mode 100644 index 0000000..5b46ce1 --- /dev/null +++ "b/prog/\305\276/index.php" @@ -0,0 +1,311 @@ +curve384(); +$generator = EccFactory::getNistCurves()->generator384(); +$useDerandomizedSignatures = true; +$algorithm = 'sha384'; +$math = new GmpMath(); +function sec1parse ($in) { + switch ($in[0]) { + case "\x02": + $isOdd = false; + break; + case "\x03": + $isOdd = true; + break; + default: + return null; + } + global $math; + global $curve; + $x = $math->stringToInt(substr($in, 1, 48)); + $y = $curve->recoverYfromX($isOdd, $x); + global $adapter; + global $generator; + return new PublicKey($adapter, $generator, new Point($adapter, $curve, $x, $y)); +} +class Transaction { + public $sender; + public $recipient; + public $amount; + public $comment; + public $nonce; + public $r; + public $s; + public function parse ($in) { + $this->sender = substr($in, 0, 49); + $this->recipient = substr($in, 49, 49); + $amount = substr($in, 49*2, 4); + $this->amount = unpack("N", $amount)[1]; + $this->comment = substr($in, 49*2+4, 256); + $this->nonce = substr($in, 49*2+4+256, 32); + $this->r = substr($in, 49*2+4+256+32, 48); + $this->s = substr($in, 49*2+4+256+32+48, 48); + } + public function serialize ($without_signature = false) { + return str_pad($this->sender, 49, "\0") . str_pad($this->recipient, 49, "\0") . pack("N", $this->amount) . str_pad($this->comment, 256, "\0") . str_pad($this->nonce, 32, "\0") . ($without_signature ? "" : (str_pad($this->r, 48, "\0") . str_pad($this->s, 48, "\0"))); + } + public function verify () { + global $adapter; + global $generator; + global $algorithm; + global $math; + $signer = new Signer($adapter); + $publickey = sec1parse($this->sender); + $hasher = new SignHasher($algorithm, $adapter); + $hash = $hasher->makeHash($this->serialize(true), $generator); + return $signer->verify($publickey, new \Mdanter\Ecc\Crypto\Signature\Signature($math->stringToInt($this->r), $math->stringToInt($this->s)), $hash); + } + public function hash () { + return hash("sha256", $this->serialize(), true); + } +} +function tx_from_row($row) { + $tx = new Transaction(); + $tx->sender = $row["sender"]; + $tx->recipient = $row["recipient"]; + $tx->amount = $row["amount"]; + $tx->comment = $row["comment"]; + $tx->nonce = $row["nonce"]; + $tx->r = $row["r"]; + $tx->s = $row["s"]; + return $tx; +} +function last_tx ($db) { + foreach ($db->query("select * from transactions order by id desc limit 1") as $row); + if ($row) + return tx_from_row($row); + return; +} +if (!empty($_REQUEST["src"])) { + header("Content-Type: text/plain"); + die(file_get_contents($_SERVER["SCRIPT_FILENAME"])); +} +if ($_SERVER["REQUEST_METHOD"] == "OPTIONS") { + http_response_code(204); + header("Access-Control-Allow-Origin: *"); + header("Access-Control-Allow-Methods: *"); + header("Access-Control-Allow-Headers: *"); + header("Access-Control-Max-Age: 86400"); + die(); +} +define("TEXT", "text/plain"); +function response ($code, $body="", $type="application/octet-stream") { + http_response_code($code); + header("Content-Type: " . $type); + header("Access-Control-Allow-Origin: *"); + header("Access-Control-Allow-Methods: *"); + header("Access-Control-Allow-Headers: *"); + header("Access-Control-Max-Age: 86400"); + echo $body; +} +if (($ret = @file_get_contents("error_status.txt")) !== false) { + response(500, $ret, TEXT); + die(); +} +function computers_post_handler ($in, $db, $forcepost=false) { + $numcomp = sizeof($db->query("select url from computers")); + if (strlen($in) % 256) { + return [413, "content length should've been divisible by 256", TEXT]; + } + $in = str_split($in, 256); + $stmt = $db->prepare("insert or ignore into computers (url) values (:url)"); + foreach ($in as $url) { + $stmt->bindParam(":url", $url, PDO::PARAM_LOB); + $stmt->execute(); + } + $stmt = null; + $computers = []; + foreach ($db->query("select url from computers") as $url) + $computers[] = $url; + if ($numcomp != sizeof($computers) || $forcepost) { + foreach ($computers as $url) // this would be better with curl parallel/multi + file_get_contents(explode("\0", $url)[0] . "computers", false, stream_context_create(["http" => ["method" => "POST", "content" => implode("", $computers), "timeout" => 1]])); + return [201]; + } else { + return [202]; + } +} +function transactions_post_handler ($in, $db) { + $tx = new Transaction(); + $txlen = strlen($tx->serialize()); + if (strlen($in) % $txlen) { + return [469, "body length should've been divisible by $txlen", TEXT]; + } + $in = str_split($in, $txlen); + foreach ($in as $txstr) { + $tx->parse($txstr); + if (!$tx->verify()) + continue; + $stmt = $db->prepare("select * from transactions where hash=:hash"); + $txhash = $tx->hash(); + $stmt->bindParam(":hash", $txhash, PDO::PARAM_LOB); + $stmt->execute(); + if ($stmt->rowCount()) + continue; + $stmt = null; + $stmt = $db->prepare("insert or ignore into transactions (sender, recipient, amount, comment, nonce, r, s, hash) values (:sender, :recipient, :amount, :comment, :nonce, :r, :s, :hash)"); + $stmt->bindParam(":sender", $tx->sender, PDO::PARAM_LOB); + $stmt->bindParam(":recipient", $tx->recipient, PDO::PARAM_LOB); + $stmt->bindParam(":amount", $tx->amount, PDO::PARAM_LOB); + $stmt->bindParam(":comment", $tx->comment, PDO::PARAM_LOB); + $stmt->bindParam(":nonce", $tx->nonce, PDO::PARAM_LOB); + $stmt->bindParam(":r", $tx->r, PDO::PARAM_LOB); + $stmt->bindParam(":s", $tx->s, PDO::PARAM_LOB); + $stmt->bindParam(":hash", $txhash, PDO::PARAM_LOB); + $stmt->execute(); + $stmt = null; + $computers = []; + foreach ($db->query("select url from computers") as $url) + $computers[] = $url; + foreach ($computers as $url) + file_get_contents(explode("\0", $url)[0] . "transaction", false, stream_context_create(["http" => ["method" => "POST", "content" => $in, "timeout" => 1]])); + } + return [200]; +} +function transactions_get_handler ($db, $after) { + $response = ""; + $ret = $db->query("select * from transactions order by id"); + $hash = null; + $stmt = $db->prepare("select * from transactions where hash=:hash"); + $stmt->bindParam(":hash", $after, PDO::PARAM_LOB); + $stmt->execute(); + if ($stmt->fetch()) + $hash = $after; + $stmt = null; + foreach ($ret as $row) + if ($hash) { + if ($hash == tx_from_row($row)->hash()) + $hash = null; + } else + $response .= tx_from_row($row)->serialize(); + if ($response == "") + return [204]; + return [200, $response]; +} +function sync_checkpoint_computer ($db, $url) { + $stmt = $db->prepare("select last_hash from computers where url=:url"); + $stmt->bindParam(":url", $url, PDO::PARAM_LOB); + $stmt->execute(); + return $stmt->fetchColumn(0); +} +# create table computers (url TEXT NOT NULL UNIQUE CHECK(length(url) == 256), last_hash TEXT NOT NULL UNIQUE CHECK(length(last_hash) == 32), date default CURRENT_TIMESTAMP); +# create table transactions (id integer primary key autoincrement, sender TEXT NOT NULL CHECK(length(sender) == 49), recipient TEXT NOT NULL CHECK(length(recipient) == 49), amount INTEGER NOT NULL CHECK(amount >= 0), comment TEXT NOT NULL CHECK(length(comment) == 256), nonce TEXT NOT NULL CHECK(length(nonce) == 32), r TEXT NOT NULL CHECK(length(r) == 48), s TEXT NOT NULL CHECK(length(s) == 48), hash TEXT NOT NULL UNIQUE CHECK(length(hash) == 32), date default CURRENT_TIMESTAMP); +$db = new PDO("sqlite:db", null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]); +if (!$db) + response(503, "db: " . $e->getMessage(), TEXT); +switch ($_REQUEST["e"] . "-" . $_SERVER["REQUEST_METHOD"]) { + case "sec1decompress-GET": + $x = $math->intToString(sec1parse(hex2bin($_REQUEST["s"]))->getPoint()->getX()); + $y = $math->intToString(sec1parse(hex2bin($_REQUEST["s"]))->getPoint()->getY()); + response(200, "\x04$x$y"); + break; + case "sec1decompress-POST": + $in = file_get_contents("php://input"); + global $math; + $x = $math->intToString(sec1parse($in)->getPoint()->getX()); + $y = $math->intToString(sec1parse($in)->getPoint()->getY()); + response(200, "\x04$x$y"); + break; + case "push-POST": + $in = file_get_contents("php://input"); + for ($i = 0; $i < 60; $i++) { + $resp = transactions_get_handler($db, $in); + if ($resp[0] == 200) { + response(...$resp); + break; + } + usleep(250000); + } + if ($resp[0] != 200) + response(204); + break; + case "jutro-GET": + $computers = []; + foreach ($db->query("select url from computers") as $url) + $computers[] = $url; + $send = ""; + foreach ($computers as $url) { + $recvd = file_get_contents(explode("\0", $url)[0] . "computers"); + if (strlen($recvd) % 256) { + error_log("server $url returned non mod256 computers get response length", 3, "log"); + continue; + } + $send .= $recvd; + } + computers_post_handler($send, $db, true); + $computers = []; + foreach ($db->query("select url from computers") as $url) + $computers[] = $url; + foreach ($computers as $url) { + $transactions = file_get_contents(explode("\0", $url[0])[0] . "transactions", false, stream_context_create(["http" => ["header" => "After: " . bin2hex(sync_checkpoint_computer($db, $url)) . "\r\n", "timeout" => 1]])); + $tx = new Transaction(); + if (strlen($transactions) % strlen($tx->serialize())) { + error_log("server $url returned not correct mod for transactions response length", 3, "log"); + continue; + } + foreach (str_split($transactions, strlen($tx->serialize())) as $transaction) { + $tx->parse($transaction); + $txhash = $tx->hash; + $stmt = $db->prepare("update computers set last_hash=:last_hash where url=:url"); + $stmt->bindParam(":last_hash", $txhash, PDO::PARAM_LOB); + $stmt->bindParam(":url", $url, PDO::PARAM_LOB); + transactions_post_handler($transaction); + } + } + break; + case "computers-GET": + $ret = $db->query("select url from computers"); + response(200); + foreach ($ret as $row) + echo $row[0]; + break; + case "computers-POST": + $in = file_get_contents("php://input"); + response(...computers_post_handler($in, $db)); + break; + case "transactions-POST": + $in = file_get_contents("php://input"); + response(...transactions_post_handler($in, $db)); + break; + case "transactions-GET": + response(...transactions_get_handler($db, hex2bin($_SERVER["HTTP_AFTER"]))); + break; + case "state-GET": + $ret = $db->query("select * from transactions order by id"); + $out = ""; + $balances = []; + foreach ($ret as $row) { + $tx = tx_from_row($row); + if (!$tx->verify()) { + $message = "transaction with internal id {$row["id"]} has an invalid signature."; + file_put_contents("error_status.txt", $message); + response(500, $message); + break 2; + } + @$balances[$tx->sender] -= $tx->amount; + @$balances[$tx->recipient] += $tx->amount; + } + response(200); + foreach ($balances as $key => $value) // do not trust balances provided by this API, since they + $packed = pack("q", $value); // are cast to machine dependent int by php + if (pack("Q", 123) === pack("P", 123)) // machine is little endian + $packed = strrev($packed); + echo $key . $packed; + break; + default: + response(400, "unknown endpoint or method not allowed", TEXT); + break; +} diff --git "a/prog/\305\276/package.json" "b/prog/\305\276/package.json" new file mode 100644 index 0000000..8cfcfb2 --- /dev/null +++ "b/prog/\305\276/package.json" @@ -0,0 +1,5 @@ +{ + "dependencies": { + "html5-qrcode": "^2.3.8" + } +} diff --git "a/prog/\305\276/test.php" "b/prog/\305\276/test.php" new file mode 100755 index 0000000..dc3ab50 --- /dev/null +++ "b/prog/\305\276/test.php" @@ -0,0 +1,72 @@ +#!/usr/bin/php +generator384(); +$useDerandomizedSignatures = true; +$algorithm = 'sha384'; +$derSerializer = new DerPrivateKeySerializer($adapter); + +## generate der key +$private = $generator->createPrivateKey(); +echo "privkey: " . $private->getSecret() . PHP_EOL; +$der = $derSerializer->serialize($private); +$math = new GmpMath(); +// echo bin2hex($math->intToString($private->getSecret())) . PHP_EOL; +// echo bin2hex($der) . PHP_EOL; + +## You'll be restoring from a key, as opposed to generating one. +$key = $derSerializer->parse($der); + +$document = 'I am writing today...'; + +$hasher = new SignHasher($algorithm, $adapter); +$hash = $hasher->makeHash($document, $generator); + +echo "message: $document" . PHP_EOL; +echo "hash: $hash" . PHP_EOL; + +# Derandomized signatures are not necessary, but is avoids +# the risk of a low entropy RNG, causing accidental reuse +# of a k value for a different message, which leaks the +# private key. +if ($useDerandomizedSignatures) { + $random = \Mdanter\Ecc\Random\RandomGeneratorFactory::getHmacRandomGenerator($key, $hash, $algorithm); +} else { + $random = \Mdanter\Ecc\Random\RandomGeneratorFactory::getRandomGenerator(); +} +$randomK = $random->generate($generator->getOrder()); + +$signer = new Signer($adapter); +$signature = $signer->sign($key, $hash, $randomK); + +# $serializer = new DerSignatureSerializer(); +# $serializedSig = $serializer->serialize($signature); +# echo base64_encode($serializedSig) . PHP_EOL; + +echo "signature: r=" . $signature->getR() . " s=" . $signature->getS() . PHP_EOL; + +$pubkey = $key->getPublicKey(); +$x = $pubkey->getPoint()->getX(); +$y = $pubkey->getPoint()->getY(); + +echo "public key: x=" . $x . " y=" . $y . PHP_EOL; + +$publickey = new PublicKey($adapter, $generator, new Point($adapter, EccFactory::getNistCurves()->curve384(), $x, $y)); + +echo "signature check " . ($signer->verify($publickey, $signature, $hash) ? "passed" : "failed") . PHP_EOL; -- cgit v1.2.3