What's new
Xen Factory

Register today to become a member! Once signed in, you'll be able to start purchasing our products, ask questions, request support and suggest new ideas!

  • We are aware that a no permission error was shown when you tried to purchase in the last 2 days, this is now fixed.

Suggestion Improvements required: Licence key validation and key injection in digital goods downloads.

silas

New Member
Hi

Im very dissapointed with this addon i tought it will be very similar to downloads on other xf site.

See:
View attachment 5276

Their shop generates random serial/lic number. Thats perfect.


I checked rm marketplace db and i did not found any unique keys for purchases.
View attachment 5273

Unique lic key is important for future validation digital goods validation which can be super easy.

Here are my ideas...

To improve this addon three things are required:
1) generating of license key for digital goods
2) licence validate from client-side
3) proxy digital goods downloads to inject lic_key automatically


And this should be very easy.
Here are my ideas.

#1 -> generate uniq lic_key
uniq_lic.php
PHP:
<?php

function uniq_serial($data = null) {
    $data = $data ?? random_bytes(13);
    assert(strlen($data) == 13);

    $data[2] = chr(ord($data[2]) & 0x0f | 0x40);
    $data[6] = chr(ord($data[6]) & 0x3f | 0x80);

    return vsprintf('%s-%s-%s-%s-%s', str_split(bin2hex($data), 5));
}

$purchase_lic_key = uniq_serial();
// store here lic key to db $purchase_lic_key
echo 'Unique purchase serial: ' . strtoupper($purchase_lic_key);
// output:
// Unique purchase serial: B21C4-B09DF-A997A-CDD8F-62E63


#2 -> lic validation:
validate_lic_server.php
PHP:
<?php
// key validation, side: host (xf instance)
// eg. usage:
// validate_lic_server.php?lic=B21C4-B09DF-A997A-CDD8F-62E63


$validation_salt='xenforo'; //configured by user
$fake_salt='f4k3enc';       //configured by user


// break if no key to check
if(!isset($_GET['lic']) or empty($_GET['lic'])){
    exit();
}
$lic_key=$_GET['lic'];


function encrypt($data, $password){
    $iv = substr(sha1(mt_rand()), 0, 16);
    $password = sha1($password);

    $salt = sha1(mt_rand());
    $saltWithPassword = hash('sha256', $password.$salt);

    $encrypted = openssl_encrypt(
      "$data", 'aes-256-cbc', "$saltWithPassword", null, $iv
    );
    $msg_encrypted_bundle = "$iv:$salt:$encrypted";
    return $msg_encrypted_bundle;
}


// first read db and check
// if $lic_key not found:
//     exit();
// if found and expired:
//     // return fake response
//     $fake_lic=random_bytes(29);
//     echo encrypt($fake_lic, $fake_salt)
//     exit();
//if found and not expired:
echo encrypt($lic_key, $validation_salt);
exit();
// output is different each time for same lic_key, eg.:
//   8464e744b1b8b3ac:ef1fff98eeff36a6b407ede555d3eb3b96746dbe:+QcRcXX3eoyNxPENkyz/n1+/5ECRDm4VvIololjg7hQ=
//   106fe114e92e3e28:3ed9626f06cdb2892b10eb93134565802efd3ca4:qfUxlN6b4VLZh+fs88KWlIFY+S9Go+IuN7OV8OHmJfM=
//   7e638e19b5182dd5:dc96cc5b8fb17063c7201e7b3e930daf6a41dcec:cYVTq7JW21jzBQ0DuhR6kUOlGaNsNYWqAYBXK1rNODM=
//   ...
//

// go to client side now...
//
//   $auth_param=urlencode('106fe114e92e3e28:3ed9626f06cdb2892b10eb93134565802efd3ca4:qfUxlN6b4VLZh+fs88KWlIFY+S9Go+IuN7OV8OHmJfM=');
//   echo $auth_param;
//
// usage:
//   validate_lic_client.php?auth=106fe114e92e3e28%3A3ed9626f06cdb2892b10eb93134565802efd3ca4%3AqfUxlN6b4VLZh%2Bfs88KWlIFY%2BS9Go%2BIuN7OV8OHmJfM%3D


digital_goods side:
validate_lic_client.php
PHP:
<?php
// key validation, client side: digital goods anti-piracy snippet
// eg. usage:
// validate_lic_client.php?auth=106fe114e92e3e28:3ed9626f06cdb2892b10eb93134565802efd3ca4:qfUxlN6b4VLZh+fs88KWlIFY+S9Go+IuN7OV8OHmJfM=

$validation_salt='xenforo'; //configured by user


// break if no key to check
if(!isset($_GET['auth']) or empty($_GET['auth'])){
    exit();
}
$auth_key=$_GET['auth'];


function decrypt($msg_encrypted_bundle, $password){
    $password = sha1($password);

    $components = explode( ':', $msg_encrypted_bundle );
    $iv            = $components[0];
    $salt          = hash('sha256', $password.$components[1]);
    $encrypted_msg = $components[2];

    $decrypted_msg = openssl_decrypt(
      $encrypted_msg, 'aes-256-cbc', $salt, null, $iv
    );

    if ( $decrypted_msg === false )
    return false;
    return $decrypted_msg;
}


$d = decrypt($auth_key,$validation_salt);
echo $d;
// output:
// B21C4-B09DF-A997A-CDD8F-62E63


// eg. usage:
// validate_lic_client.php?lic=B21C4-B09DF-A997A-CDD8F-62E63&auth=106fe114e92e3e28:3ed9626f06cdb2892b10eb93134565802efd3ca4:qfUxlN6b4VLZh+fs88KWlIFY+S9Go+IuN7OV8OHmJfM=
echo '<br>';
if(!isset($_GET['lic']) or empty($_GET['lic'])){
    exit();
}
$lic_key=$_GET['lic'];

if($lic_key = $d){
    echo 'Decrypted auth and lic_key are same.';
} else {
    echo 'Auth_key does not match lic_key';
    // anti-piracy rules here...
}
// output:
// B21C4-B09DF-A997A-CDD8F-62E63
// Decrypted auth and lic_key are same.


#3 -> proxy download resource and inject lic_key
purchase_proxy.php
PHP:
<?php

$path_to_input_file="internal_data/attachment/resource_file.exe";
$FIND_CONTENT = "__TEMPLATE____AUTH_CONTENT_REPLACE____";
$REPLACE_CONTENT = "GENUINE->B21C4-B09DF-A997A-CDD8F-62E63";


$cmd_xxd=shell_exec('command -v xxd');
$cmd_sed=shell_exec('command -v sed');


header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($proxy_src_file) . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($proxy_src_file)); // content size must not change after injecting lic_key

// inject lic_key and output content on-the-fly
if (!empty($cmd_sed)){
  if (!empty($cmd_xxd)){
    echo shell_exec("xxd " . $path_to_input_file . " | sed \"0,/" . $FIND_CONTENT . "/s//" . $REPLACE_CONTENT . "/\" | xxd -r");
  } else {
    // when no xxd command found in shell, then fallback to sed command only, however this may cause binary files gets corrupted
    echo shell_exec("sed \"0,/" . $FIND_CONTENT . "/s//" . $REPLACE_CONTENT . "/\" " . $path_to_input_file);
  }
}
die();
// make sure to die

Hope this helps.

This could be massive addon for XF then.

Thank you
Best regards
 

Clement

Freaky Coder
Staff member
I understand but just want to ensure this is not taken from another add-on and thus would breach some copyrights by staying there.
 

silas

New Member
Ofcourse not.
The point is to improve addon. encrypt and decrypt part I think are very similar to the one you already use.

Here is an example of some linux app installer:
Bash:
#!/bin/sh
NAME=SampleAppInstaller

# This is sample bash automated software installer

# Helper functions
_err() {
    echo -e "$*"
    echo

    exit 1
}

available() {
    type -t "$1" >/dev/null && return 0
    return 1
}

need() {
    available "$1" && return 0

    _err needed command was not found: $1
    exit 1
}

# First extract real app runtime or real installer

need sed

TMP_DIR="tmp.${RANDOM}"

mkdir -p "${TMP_DIR}" || _err I/O or file rights error. Cannot continue.

need awk
need openssl

POS_FILE=$(awk '/^__SOFTWARE__/ { print NR + 1; exit 0; }' $0)
tail -n +${POS_FILE} $0 \
| openssl enc -base64 -d \
| cat > "${TMP_DIR}/installer"

chmod +x "${TMP_DIR}/installer"

# App extracted, now let's auto install licence, before run

echo \
"LK${NAME}
__TEMPLATE____AUTH_CONTENT_REPLACE____
__END
" > "${TMP_DIR}/licence"

# Let's run app installer
sh "${TMP_DIR}/installer"

echo Bye.

#     Included installer content injected using this method:

#     // cleanup current include
# sed -n '/^__SOFTWARE__/q;p' install_demo.sh > _install_tmp.sh

#     // inject real installer/app runtime
# echo "__SOFTWARE__" >> _install_tmp.sh
# cat installer | openssl enc -base64 >> _install_tmp.sh
# mv -f _install_tmp.sh install_demo.sh

exit 0
__SOFTWARE__
IyEvYmluL3NoCk5BTUU9U2FtcGxlQXBwSW5zdGFsbGVyCgojIEhlbHBlcjogR2V0
IHNjcmlwdCBsb2NhdGlvbgojICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMj
IyMjIyMjIyMjIyMKZnVuY3Rpb24gX2dldF9jd2QoKQp7CglpZiBbWyAkezA6MDox
fSAhPSAiLyIgXV07IHRoZW4KCQlDV0Q9IiRQV0QiLyQoZGlybmFtZSAiJDAiKQoJ
ZWxzZQoJCUNXRD0kKGRpcm5hbWUgIiQwIikKCWZpCgoJQ1dEPWBjZCAiJHtDV0R9
IiAyPi9kZXYvbnVsbCAmJiBwd2QgfHwgZWNobyAiJHtDV0R9ImAKCVJPT1Q9IiR7
Q1dEfSIKfQoKX2dldF9jd2QKY2QgIiRDV0QiIDI+L2Rldi9udWxsIDE+L2Rldi9u
dWxsCgpbIC1mICJsaWNlbmNlIiBdIHx8IGVjaG8gIkxpY2VuY2Uga2V5IG5vdCBm
b3VuZCwgaW5zdGFsbGluZyBpbiBhcyB0cmlhbC4iCgppZiBbIC1mICJsaWNlbmNl
IiBdOyB0aGVuCglMSUNfS0VZPWBjYXQgbGljZW5jZSB8IGdyZXAgLXYgLUUgIihf
X0VORHxMSyROQU1FfF9fX19BVVRIX0NPTlRFTlRfUkVQTEFDRV9fX18pImAKCVsg
LXogIiRMSUNfS0VZIiBdICYmIGVjaG8gIkxpY2VuY2Uga2V5IGRvZXMgbm90IGNv
bnRhaW4gbGljZW5jZSBmaWxlIgpmaQoKWyAtbiAiJExJQ19LRVkiIF0gJiYgZWNo
byAiTGljZW5jZSBrZXkgaXM6ICRMSUNfS0VZIgoKIyBtb3JlIGNoZWNrcwojIC4u
LgoKIyBQYXJlbnQgcHJvY2VzcyBoYXMgZGllZCBzbyB3ZSBhbHNvIGJldHRlciBk
aWUuCmV4aXQgMAo=

And after replacing licence key inside linux app installer:
Code:
sed "0,/__TEMPLATE____AUTH_CONTENT_REPLACE____/s//GENUINE->B21C4-B09DF-A997A-CDD8F-62E63/" install_demo.sh > digital_demo.sh

I can easily distributore it to the user with already built in licence.

Let's see if thats work:
Code:
./digital_demo.sh
Licence key is: GENUINE->B21C4-B09DF-A997A-CDD8F-62E63
Bye.

So I think this is must have!
 

Clement

Freaky Coder
Staff member
I don't intend to develop this ASAP as I just did a big release of this add-on and have a filled todo list, so no help required.
One thing I am not sure of implementation is the license key injection considering I won't be able to test this on the various server types and not all servers are running over linux.
 

silas

New Member
One thing I am not sure of implementation is the license key injection considering I won't be able to test this on the various server types and not all servers are running over linux.
I expected this and I can agree. But please consider ASAP random licence key and validation.
 

Clement

Freaky Coder
Staff member
This currently under development, at least for the key generation as a customer funded the development but required external license validation (i.e. key transmission by the app) but that part will be in a custom add-on (only the key generation will be in the main product).
 
Top