From 3146cff50093824d528d82fc04a37c096d480791 Mon Sep 17 00:00:00 2001 From: Charanpreet Date: Fri, 2 Feb 2024 17:05:51 +0530 Subject: [PATCH] feat: added 1:1 utility for signing and verification in PHP --- .../signing_and_verification/php/README.md | 84 ++++++ .../php/composer.json | 14 - .../signing_and_verification/php/src/.env | 4 - .../php/src/index.php | 250 ++++++++++-------- 4 files changed, 229 insertions(+), 123 deletions(-) create mode 100644 utilities/signing_and_verification/php/README.md delete mode 100644 utilities/signing_and_verification/php/composer.json delete mode 100644 utilities/signing_and_verification/php/src/.env diff --git a/utilities/signing_and_verification/php/README.md b/utilities/signing_and_verification/php/README.md new file mode 100644 index 0000000..38fb8f9 --- /dev/null +++ b/utilities/signing_and_verification/php/README.md @@ -0,0 +1,84 @@ +# Signing and Verification +Steps for signing and verification + +1. Install php and dependencies + +```sh +# linux setup +# download php +sudo apt install php php-curl +# Download Composer +curl -sS https://getcomposer.org/installer -o composer-setup.php +# install composer +sudo php composer-setup.php --install-dir=/usr/local/bin --filename=composer +# change to cloned directory +cd /reference-implementations/utilities/signing_and_verification/php +composer install + +# brew setup +# install php +brew install php # latest version is 8.0.0, this will work for versions >= 7.4 +# install composer +brew install composer +# change to cloned directory +cd /reference-implementations/utilities/signing_and_verification/php +composer install +``` +2. ENV variables +```sh +SIGNING_PRIV_KEY="your signing private key" +SIGNING_PUB_KEY="your signing public key" +COUNTERPARTY_SIGNING_PUB_KEY="the other party's signing public key" +ENC_PUB_KEY="your encryption/crypto public key" +ENC_PRIV_KEY="your encryption/crypto private key" +COUNTERPARTY_PUB_KEY="the other party's encryption/crypto public key" +SUBSCRIBER_ID="your subscriber id" +UNIQUE_KEY_ID="your ukid" +AUTH_HEADER="the auth header that is to be verified" +REQUEST_BODY="json stringified payload" +``` +3. Generate keys +```sh +composer run start -- -g + +# OUTPUT +Signing priv key: VfwASYHVjMAC63LClJKVTvjHcvuUZ4oQKhXmpY6+pwsu7b5xNQzhD/drIPer5m3kasjjicaj/+lblZsNnlQMCw== +Signing pub key: Lu2+cTUM4Q/3ayD3q+Zt5GrI44nGo//pW5WbDZ5UDAs= +Encryption priv key: MC4CAQEwBQYDK2VuBCIEINaTmxwcMRLGuxX1lrwo0Lxd2FHqn84YqQoDzVQXe46+ +Encryption pub key: MCowBQYDK2VuAyEAFT6F4dxn1waTvLUbY5tdKh/IezuOp+tlHkAwQw82qXU= +``` +4. Create authorisation header +```sh +# REQUEST_BODY, SIGNING_PRIV_KEY, SUBSCRIBER_ID, UNIQUE_KEY_ID should be set +composer run start -- -s + +# OUTPUT +Signature keyId="buyer-app.ondc.org|207|ed25519",algorithm="ed25519",created="1641287875",expires="1641291475",headers="(created) (expires) digest",signature="fKQWvXhln4UdyZdL87ViXQObdBme0dHnsclD2LvvnHoNxIgcvAwUZOmwAnH5QKi9Upg5tRaxpoGhCFGHD+d+Bw==" +``` + +5. Verify authorisation header +```sh +# AUTH_HEADER, REQUEST_BODY, COUNTERPARTY_SIGNING_PUB_KEY should be set +composer run start -- -v + +#OUTPUT +0 | 1 # depending upon truth value (true = 1) +``` + +6. Encrypt Payload +```sh +# ENC_PRIV_KEY, COUNTERPARTY_PUB_KEY should be set +composer run start -- -e 'message to encrypt' + +#OUTPUT +dq6j2KZp61G6PMM9IhHW2fbOnquy7gkwJN/tVXkKAI4= +``` + +8. Decrypt Payload +```sh +# ENC_PRIV_KEY, ENC_PUB_KEY should be set +composer run start -d 'dq6j2KZp61G6PMM9IhHW2fbOnquy7gkwJN/tVXkKAI4=' + +#OUTPUT +message to encrypt +``` diff --git a/utilities/signing_and_verification/php/composer.json b/utilities/signing_and_verification/php/composer.json deleted file mode 100644 index 3e6e13a..0000000 --- a/utilities/signing_and_verification/php/composer.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "charnpreet/signver", - "type": "project", - "autoload": { - "psr-4": { - "Charnpreet\\Signver\\": "src/" - } - }, - "require": { - "vlucas/phpdotenv": "^5.6", - "sop/crypto-types": "dev-master", - "phpseclib/phpseclib": "^3.0" - } -} diff --git a/utilities/signing_and_verification/php/src/.env b/utilities/signing_and_verification/php/src/.env deleted file mode 100644 index 0bb1ff8..0000000 --- a/utilities/signing_and_verification/php/src/.env +++ /dev/null @@ -1,4 +0,0 @@ -PRIVATE_KEY="3LFlZAAuAMyppOq35pGz1b6ujlX4PAcEuGmMe6ONGuqyx2kXoC1/oh/VzDUzVSMdCqx2yZ3wtqTuoAvN58U5xg==" -PUBLIC_KEY="ssdpF6Atf6If1cw1M1UjHQqsdsmd8Lak7qALzefFOcY=" -SUBSCRIBER_ID="subscriber_id" -UNIQUE_KEY_ID="ukid" \ No newline at end of file diff --git a/utilities/signing_and_verification/php/src/index.php b/utilities/signing_and_verification/php/src/index.php index fe84563..b1b2936 100644 --- a/utilities/signing_and_verification/php/src/index.php +++ b/utilities/signing_and_verification/php/src/index.php @@ -1,135 +1,175 @@ load(); +$dotenv = Dotenv\Dotenv::createImmutable(dirname(__DIR__, 1)); +$dotenv->safeload(); -function hash_msg(string $msg): string{ - return base64_encode(sodium_crypto_generichash($msg,"",64)); +function hash_msg(string $msg): string +{ + return base64_encode(sodium_crypto_generichash($msg, "", 64)); } -function create_signing_string(string $digest_base64, string $created=null, string $expires=null): string { - $now = new DateTime(); - $one_hour = new DateInterval("PT1H"); - +function create_signing_string(string $digest_base64, string $created = null, string $expires = null): string +{ + $now = new DateTime(); + $one_hour = new DateInterval("PT1H"); - if ($created == null) { - $created = $now->getTimestamp(); - } + if ($created == null) { + $created = $now->getTimestamp(); + } - if ($expires == null) { - $expires = $now->add($one_hour)->getTimestamp(); - } + if ($expires == null) { + $expires = $now->add($one_hour)->getTimestamp(); + } - $signing_string = "(created): $created\n(expires): $expires\ndigest: BLAKE-512=$digest_base64"; - return $signing_string; + $signing_string = "(created): $created\n(expires): $expires\ndigest: BLAKE-512=$digest_base64"; + return $signing_string; } -function sign_response(string $signing_key, string $private_key): string { - return base64_encode(sodium_crypto_sign_detached($signing_key, base64_decode($private_key))); +function sign_response(string $signing_key, string $private_key): string +{ + return base64_encode(sodium_crypto_sign_detached($signing_key, base64_decode($private_key))); } -function verify_response(string $signature, string $signing_key, string $public_key): bool { - return sodium_crypto_sign_verify_detached(base64_decode($signature), $signing_key, base64_decode($public_key)); +function verify_response(string $signature, string $signing_key, string $public_key): bool +{ + return sodium_crypto_sign_verify_detached(base64_decode($signature), $signing_key, base64_decode($public_key)); } -function create_authorisation_header(string $request_body, string $created=null, string $expires=null) { - $now = new DateTime(); - $one_hour = new DateInterval("PT1H"); - - if($created == null) { - $created = $now->getTimestamp(); - } - - if($expires == null) { - $expires = $now->add($one_hour)->getTimeStamp(); - } - - $signing_key = create_signing_string(hash_msg($request_body), $created, $expires); - $signature = sign_response($signing_key, $_ENV['PRIVATE_KEY']); - - $subscriber_id = $_ENV['SUBSCRIBER_ID']; - $unique_key_id = $_ENV['UNIQUE_KEY_ID']; - - $header = "Signature keyId=\"$subscriber_id|$unique_key_id|ed25519\",algorithm=\"ed25519\",created=\"$created\",expires=\"$expires\",headers=\"($created) ($expires) digest\",signature=\"$signature\""; - return $header; +function create_authorisation_header(string $request_body, string $created = null, string $expires = null) +{ + $now = new DateTime(); + $one_hour = new DateInterval("PT1H"); + + if ($created == null) { + $created = $now->getTimestamp(); + } + + if ($expires == null) { + $expires = $now->add($one_hour)->getTimeStamp(); + } + + $signing_key = create_signing_string(hash_msg($request_body), $created, $expires); + $signature = sign_response($signing_key, $_ENV['SIGNING_PRIV_KEY']); + + $subscriber_id = $_ENV['SUBSCRIBER_ID']; + $unique_key_id = $_ENV['UNIQUE_KEY_ID']; + + $header = "Signature keyId=\"$subscriber_id|$unique_key_id|ed25519\",algorithm=\"ed25519\",created=\"$created\",expires=\"$expires\",headers=\"($created) ($expires) digest\",signature=\"$signature\""; + return $header; } // util -function get_filter_dict(string $filter_string) { - $filter_string_list = preg_split("/,/", $filter_string); - $filter_string_list = array_map(function($item) { return trim($item, " \n\r\t\v\0\""); }, $filter_string_list); - $filter_string_dict = []; - foreach($filter_string_list as $item) { - $split_item = preg_split("/=/", $item, 2); - $filter_string_dict[$split_item[0]] = trim($split_item[1], " \n\r\t\v\0\""); - } - return $filter_string_dict; +function get_filter_dict(string $filter_string) +{ + $filter_string_list = preg_split("/,/", $filter_string); + $filter_string_list = array_map(function ($item) {return trim($item, " \n\r\t\v\0\"");}, $filter_string_list); + $filter_string_dict = []; + foreach ($filter_string_list as $item) { + $split_item = preg_split("/=/", $item, 2); + $filter_string_dict[$split_item[0]] = trim($split_item[1], " \n\r\t\v\0\""); + } + return $filter_string_dict; +} + +function verify_authorisation_header(string $auth_header, string $request_body_str, string $public_key): bool +{ + $auth_header = str_replace("Signature ", "", $auth_header); + $header_parts = get_filter_dict($auth_header); + $created = (int) $header_parts["created"]; + $expires = (int) $header_parts["expires"]; + + $now = new DateTime(); + if ($created <= $now->getTimestamp() && $expires >= $now->getTimestamp()) { + $signing_key = create_signing_string(hash_msg($request_body_str), $created, $expires); + return verify_response($header_parts['signature'], $signing_key, $public_key); + } + return false; +} + +function gen_keys() +{ + $signkeypair = sodium_crypto_sign_keypair(); + $signprivkey = sodium_crypto_sign_secretkey($signkeypair); + $signpubkey = sodium_crypto_sign_publickey($signkeypair); + + // using libsodium + $enckeypair = sodium_crypto_box_keypair(); + $encprivkey = sodium_crypto_box_secretkey($enckeypair); + $encpubkey = sodium_crypto_box_publickey($enckeypair); + + // $openssl = openssl_pkey_get_private($encprivkey); + + // echo "Encryption priv key: ", base64_encode($encprivkey), "\n"; + $encprivkey = new OneAsymmetricKey(new X25519AlgorithmIdentifier(), "\x04\x20" . $encprivkey); + $encpubkey = new PublicKeyInfo(new X25519AlgorithmIdentifier(), new BitString($encpubkey)); + + echo "Signing priv key: ", base64_encode($signprivkey), "\n"; + echo "Signing pub key: ", base64_encode($signpubkey), "\n"; + echo "Encryption priv key: ", base64_encode($encprivkey->toDER()), "\n"; + echo "Encryption pub key: ", base64_encode($encpubkey->toDER()), "\n"; } -function verify_authorisation_header(string $auth_header, string $request_body_str, string $public_key): bool { - $auth_header = str_replace("Signature ", "", $auth_header); - $header_parts = get_filter_dict($auth_header); - $created = (int) $header_parts["created"]; - $expires = (int) $header_parts["expires"]; - - $now = new DateTime(); - if ($created <= $now->getTimestamp() && $expires >= $now->getTimestamp()) { - $signing_key = create_signing_string(hash_msg($request_body_str), $created, $expires); - return verify_response($header_parts['signature'], $signing_key, $public_key); - } - return false; +function encrypt(string $crypto_private_key, string $crypto_public_key, ?string $message = null): string +{ + $pkey = OneAsymmetricKey::fromDER(base64_decode($crypto_private_key)); + $pubkey = PublicKeyInfo::fromDER(base64_decode($crypto_public_key)); + $pkey = hex2bin(str_replace("0420", "", bin2hex($pkey->privateKeyData()))); + $shkey = sodium_crypto_box_keypair_from_secretkey_and_publickey($pkey, $pubkey->publicKeyData()); + $shpkey = sodium_crypto_box_secretkey($shkey); + + $cipher = new AES('ecb'); + $cipher->setKey($shpkey); + return base64_encode($cipher->encrypt($message == null ? 'ONDC is a great initiative!' : $message)); } -function gen_keys() { - $signkeypair = sodium_crypto_sign_keypair(); - $signprivkey = sodium_crypto_sign_secretkey($signkeypair); - $signpubkey = sodium_crypto_sign_publickey($signkeypair); - - // using libsodium - $enckeypair = sodium_crypto_box_keypair(); - $encprivkey = sodium_crypto_box_secretkey($enckeypair); - $encpubkey = sodium_crypto_box_publickey($enckeypair); - - // $openssl = openssl_pkey_get_private($encprivkey); - - echo "Encryption priv key: ", base64_encode($encprivkey), "\n"; - $encprivkey = new OneAsymmetricKey(new X25519AlgorithmIdentifier(), "\x04\x20".$encprivkey); - $encpubkey = new PublicKeyInfo(new X25519AlgorithmIdentifier(), new BitString($encpubkey)); - - echo "Signing priv key: ", base64_encode($signprivkey), "\n"; - echo "Signing pub key: ", base64_encode($signpubkey), "\n"; - echo "Encryption priv key: ", base64_encode($encprivkey->toDER()), "\n"; - echo "Encryption pub key: ", base64_encode($encpubkey->toDER()), "\n"; +function decrypt(string $crypto_private_key, string $crypto_public_key, string $cipher_text): string +{ + $pkey = OneAsymmetricKey::fromDER(base64_decode($crypto_private_key)); + $pubkey = PublicKeyInfo::fromDER(base64_decode($crypto_public_key)); + $pkey = hex2bin(str_replace("0420", "", bin2hex($pkey->privateKeyData()))); + $shkey = sodium_crypto_box_keypair_from_secretkey_and_publickey($pkey, $pubkey->publicKeyData()); + $shpkey = sodium_crypto_box_secretkey($shkey); + + $cipher = new AES('ecb'); + $cipher->setKey($shpkey); + return ($cipher->decrypt(base64_decode($cipher_text))); } -function encrypt(string $crypto_private_key, string $crypto_public_key, ?string $message=null): string { - $pkey = OneAsymmetricKey::fromDER(base64_decode($crypto_private_key)); - $pubkey = PublicKeyInfo::fromDER(base64_decode($crypto_public_key)); - $pkey = hex2bin(str_replace("0420","", bin2hex($pkey->privateKeyData()))); - $shkey = sodium_crypto_box_keypair_from_secretkey_and_publickey($pkey, $pubkey->publicKeyData()); - $shpkey = sodium_crypto_box_secretkey($shkey); - - $cipher = new AES('ecb'); - $cipher->setKey($shpkey); - return base64_encode($cipher->encrypt($message == null ? 'ONDC is a great initiative!':$message)); +function main(array $args) +{ + switch ($args[1]) { + case "-g": + gen_keys(); + break; + case "-e": + echo encrypt($_ENV['ENC_PRIV_KEY'], $_ENV['COUNTERPARTY_PUB_KEY'], $args[2]); + break; + case "-d": + echo decrypt($_ENV['ENC_PRIV_KEY'], $_ENV['ENC_PUB_KEY'], $args[2]); + break; + case "-s": + echo create_authorisation_header($_ENV['REQUEST_BODY']); + break; + case "-v": + echo verify_authorisation_header($_ENV['AUTH_HEADER'] || '', $_ENV['REQUEST_BODY'] || '', $_ENV["COUNTERPARTY_SIGNING_PUB_KEY"]); + break; + default: + echo "$args[1] is not a valid argument: please check" . "\n"; + echo "signing and verification helper: " . "\n"; + echo "-g: generate signing and enc key pairs" . "\n"; + echo "-e: encrypt with enc keys" . "\n"; + echo "-d: decrypt with enc keys" . "\n"; + echo "-s: create signed header" . "\n"; + echo "-v: verify signed header" . "\n"; + } } -function decrypt(string $crypto_private_key, string $crypto_public_key, string $cipher_text): string { - $pkey = OneAsymmetricKey::fromDER(base64_decode($crypto_private_key)); - $pubkey = PublicKeyInfo::fromDER(base64_decode($crypto_public_key)); - $pkey = hex2bin(str_replace("0420","", bin2hex($pkey->privateKeyData()))); - $shkey = sodium_crypto_box_keypair_from_secretkey_and_publickey($pkey, $pubkey->publicKeyData()); - $shpkey = sodium_crypto_box_secretkey($shkey); - - $cipher = new AES('ecb'); - $cipher->setKey($shpkey); - return ($cipher->decrypt(base64_decode($cipher_text))); -} \ No newline at end of file +main($argv);