Add hidden service v3 keygen and parser for base64 encoded secret keys

This commit is contained in:
Daniel Winzen
2018-11-25 14:36:28 +01:00
parent f0afbe14c9
commit 36fc7103cb
2 changed files with 145 additions and 83 deletions

View File

@ -1,4 +1,5 @@
<?php
require_once(__DIR__ . '/vendor/autoload.php');
const DBHOST='localhost'; // Database host
const DBUSER='hosting'; // Database user
const DBPASS='MY_PASSWORD'; // Database password
@ -87,18 +88,31 @@ server {
}
';
function get_onion($pkey){
function get_onion_v2($pkey) : string {
$keyData = openssl_pkey_get_details($pkey);
return base32_encode(hex2bin(substr(sha1(substr(base64_decode(substr($keyData['key'], 27, -26)), 22)), 0, 20)));
$pk = base64_decode(substr($keyData['key'], 27, -26));
$skipped_first_22 = substr($pk, 22);
$first_80_bits_of_sha1 = hex2bin(substr(sha1($skipped_first_22), 0, 20));
return base32_encode($first_80_bits_of_sha1);
}
function base32_encode($input) {
$map = array(
function get_onion_v3(string $sk) : string {
if(PHP_INT_SIZE === 4){
$pk = ParagonIE_Sodium_Core32_Ed25519::sk_to_pk($sk);
}else{
$pk = ParagonIE_Sodium_Core_Ed25519::sk_to_pk($sk);
}
$checksum = substr(hash('SHA3-256', '.onion checksum' . $pk . hex2bin('03'), true), 0, 2);
return base32_encode($pk . $checksum . hex2bin('03'));
}
function base32_encode(string $input) : string {
$map = [
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', // 7
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', // 15
'q', 'r', 's', 't', 'u', 'v', 'w', 'x', // 23
'y', 'z', '2', '3', '4', '5', '6', '7', // 31
);
];
if(empty($input)){
return '';
}
@ -300,3 +314,57 @@ HiddenServicePort 80 unix:/var/run/nginx/$socket
file_put_contents("/etc/tor/instances/$key/torrc", $torrc);
exec("service tor@$key reload");
}
function private_key_to_onion(string $priv_key) : array {
$ok = true;
$message = '';
$onion = '';
$priv_key = trim($priv_key);
if(($pkey = openssl_pkey_get_private($priv_key)) !== false){
$details=openssl_pkey_get_details($pkey);
if($details['bits'] !== 1024){
$message = 'Error: private key not of bitsize 1024.';
$ok = false;
}else{
$onion = get_onion_v2($pkey);
}
openssl_pkey_free($pkey);
return ['ok' => $ok, 'message' => $message, 'onion' => $onion];
} elseif(($priv = base64_decode($priv_key, true)) !== false){
if(strpos($priv, '== ed25519v1-secret: type0 ==' . hex2bin('000000')) !== 0 || strlen($priv) !== 96){
$message = 'Error: v3 secret key invalid.';
$ok = false;
} else {
$onion = get_onion_v3(substr($priv, 32));
}
return ['ok' => $ok, 'message' => $message, 'onion' => $onion];
}
$message = 'Error: private key invalid.';
$ok = false;
return ['ok' => $ok, 'message' => $message, 'onion' => $onion];
}
function generate_new_onion(int $version = 3) : array {
$priv_key = '';
$onion = '';
if($version === 2){
$pkey = openssl_pkey_new(['private_key_bits' => 1024, 'private_key_type' => OPENSSL_KEYTYPE_RSA]);
openssl_pkey_export($pkey, $priv_key);
$onion = get_onion_v2($pkey);
openssl_pkey_free($pkey);
} else {
$seed = random_bytes(32);
$sk = ed25519_seckey_expand($seed);
$priv_key = base64_encode('== ed25519v1-secret: type0 ==' . hex2bin('000000') . $sk);
$onion = get_onion_v3($sk);
}
return ['priv_key' => $priv_key, 'onion' => $onion];
}
function ed25519_seckey_expand(string $seed) : string {
$sk = hash('sha512', substr($seed, 0, 32), true);
$sk[0] = chr(ord($sk[0]) & 248);
$sk[31] = chr(ord($sk[31]) & 63);
$sk[31] = chr(ord($sk[31]) | 64);
return $sk;
}

View File

@ -54,33 +54,27 @@ if($_SERVER['REQUEST_METHOD']==='POST'){
}
}
if($ok){
$check=$db->prepare('SELECT null FROM onions WHERE onion=?;');
if(isset($_REQUEST['private_key']) && !empty(trim($_REQUEST['private_key']))){
$priv_key = trim($_REQUEST['private_key']);
if(($pkey=openssl_pkey_get_private($priv_key))!==false){
$details=openssl_pkey_get_details($pkey);
if($details['bits']!==1024){
echo '<p style="color:red;">Error: private key not of bitsize 1024.</p>';
$data = private_key_to_onion($priv_key);
$onion = $data['onion'];
if(!$data['ok']){
echo "<p style=\"color:red;\">$data[message]</p>";
$ok = false;
} else {
$onion=get_onion($pkey);
$check=$db->prepare('SELECT null FROM onions WHERE onion=?;');
$check->execute([$onion]);
if($check->fetch(PDO::FETCH_NUM)){
echo '<p style="color:red;">Error onion already exists.</p>';
$ok = false;
}
}
openssl_pkey_free($pkey);
}else{
echo '<p style="color:red;">Error: private key invalid.</p>';
$ok=false;
}
}else{
$check=$db->prepare('SELECT null FROM onions WHERE onion=?;');
do{
$pkey=openssl_pkey_new(['private_key_bits'=>1024, 'private_key_type'=>OPENSSL_KEYTYPE_RSA]);
openssl_pkey_export($pkey, $priv_key);
$onion=get_onion($pkey);
openssl_pkey_free($pkey);
$data = generate_new_onion(2);
$priv_key = $data['priv_key'];
$onion = $data['onion'];
$check->execute([$onion]);
}while($check->fetch(PDO::FETCH_NUM));
}