# Enhanced jailbreak-detection using app server_Cordova/Ionic

One of the main functions within AppSealing SDK is detecting the environment of the jailbroken device and forcibly closes the app. However, there is a possibility that these detection functions will be bypassed by more sophisticated attack methods. This is because, due to the characteristics of the iOS operating system, the code of the loaded dynamic library (dylib) is executed first when the app is launched. An attacker may distribute the code to patch a specific area of ​​the executable file in such a dynamic library.

If this code patch occurs before AppSealing's detection logic is executed, the code that terminates the app has been removed, so even if a jailbreak is detected, the app will remain running.

Of course, not everyone can perform this type of attack easily, but since this type of attack has been confirmed by a group of hackers with specialized hacking knowledge, AppSealing provides an additional jailbreak detection method to overcome such an attack situation.

Since the characteristic of this attack method is to change the code of the running app in advance, no matter how strong detection logic is added to the AppSealing library itself, the situation in which the code is patched by the dynamic library is unavoidable. Therefore, the newly provided jailbreak detection function does not detect in the app, but in a way that rejects all services and actions, such as log-in in or accepting API calls, in the case of a terminal suspected of being jailbroken in the server linked to the app.

The basic method is to obtain server credentials from the app through the AppSealing interface, add them to the existing authentication parameters, and send them to the server.

This method cannot be applied for a client-only app that does not work with the server.

The following sections describe, with example code, how to obtain and validate server credentials.

# iOS App Code

Additional process of your app needs verify the server credentials is to call a function in the AppSealing SDK to get the server credential string and send it to the server along with the existing authentication parameters.

Most apps that work with the server will go through a user authentication or login process, and in this process, the account information entered by the user will be transmitted to the server. You can add the server credential string to the parameters you send to the server.

The server credential string is obtained in the following way:

# Simple UI code into 'ViewController.swift' for Swift project

func userLogin( userID: String, password: String ) -> Bool
{
    let inst: AppSealingInterface = AppSealingInterface();
let appSealingCredential = String.init( cString: inst._GetEncryptedCredential() );
      // credential 값을 인증 정보와 함께 서버로 올린다
    let loginResult = processLogin( user: userID, pwd: password, credential: appSealingCredential );
    ...
    ...
}

# Simple UI code into 'ViewController.mm' for Objective-C project

- (BOOL)userLogin:(NSString*)userID withPassword:(NSString*)password
{
    char _appSealingCredential[290] = { 0, };   
    ObjC_GetEncryptedCredential( _appSealingCredential );
// credential 값을 인증 정보와 함께 서버로 올린다
    BOOL loginResult = processLogin( userID, password, _appSealingCredential );
    ...
    ...
}

If your server fails to validate credential, you should also force the login to fail and the app to not proceed further. However, since code such as checking the login result and closing the app is likely to be tampered by an attacker, the best practice is configuring your server to deny service or response for any requests from that client after the server fails credential validation.

This will be discussed again in the next section.

# Verification at app server

The credential data (hex string) returned from the interface call to the AppSealing module is only valid when the security logic inside AppSealing is normally performed and no dangerous situation is detected in the device.

If code patch attack is made through the dynamic library or the security logic is bypassed by other methods, valid credential data will not be generated, so the server should verify this value and blocks the attack situation of the device.

The app server must check whether the credential value sent by the client (app) is correct, and if it is not correct, it must deny authentication (login) and then deny any services (API call) requested by that client.

# [Preparation]

To verify credential data on the server, you need an AES Key and IV to decrypt the data sent from the client, and the original credential data to compare and verify.

All of these values can be acquired through the “Check Credential” button of the project in the ADC. Just copy the Hex string shown here and paste it into the example code and use it. First, connect to ADC as shown in the screen below and click the “Check Credential” button in the project box.

If you click the button, the following window is displayed, where you can check the Credential value and IV and key to be used for decryption. You can use the copy button to the left of the string to copy this value as it is, paste it into the server-side

If you click the button, the following window is displayed, where you can check the Credential value and IV and AES key to be used for decryption. You can use the copy button to the left of the string to copy this value as it is, paste it into the server-side verification code and use it.

# [In case your server code is using Node.js/Javascript]

Add following code to your existing code and use it for credential data validation. Authenticity can be determined by passing the credential value sent as a parameter in the login or authentication function of the existing server code to the verfityAppSealingCredential function provided in the code below. (The code below is also included in the SDK as a file named appsealing_credential.js).

** Note: In the code below, ORG_CREDENTIAL, AES_IV, and AES_KEY must be replaced with values obtained through the “Check Credential” function of the corresponding project in the ADC. If you just take the example code and use it intactly, credential value will not be verified properly.

var crypto = require('crypto');


function verifyAppSealingCredential( credential )
{
	// Need to Change : Get From ADC (via 'Check Credential') -----------------------------------------
	const ORG_CREDENTIAL = "572E0E1459453F2078D6576FF71ECD0DBCA0484430C7FA7FE45B788A37DE3A04204F5A55FEA83AC9AFBA2C688594F75A3828B23972DB34858EC4F6CC3202533E44121E5F2614B227E18B6419A83810F7511D5E51FCACD5175A1CC550F83CB874A7378ACDAFE78EB2E329CD5D3C384061C4669674F1EE6B1B59FB7D91835DB7EE";
	
	const AES_IV  = "055772B7434A4174749AFE09B1413472";
	const AES_KEY = "71CA94A64A4DEBF5566495AB03F6798F";
	//-------------------------------------------------------------------------------------------------


	// convert credential from hex string to byte array
	let decrypted_UTC = 0, decrpyted_buffer, aes_key2;


	// decrypt UTC
    try
	{
        const decipher = crypto.createDecipheriv( 'aes-128-ctr', Buffer.from( AES_KEY, 'hex' ), Buffer.from( AES_IV, 'hex' ));
        decrpyted_buffer = Buffer.concat( [decipher.update( credential.substr( 0, 32 ), 'hex' ), decipher.final()] );
		decrypted_UTC = decrpyted_buffer.slice( 0, 8 ).readUInt32LE();
    }
	catch( error )
	{
        throw error;
    }


	// verfity UTC with current time (+/-) 10sec
	const current_UTC = parseInt( Date.now() / 1000 );	// get current UTC in seconds
	if ( Math.abs( current_UTC - decrypted_UTC ) > 10 )
	{
		console.log( "Invalid UTC value has sent, deny login & all services for this client... " + Math.abs( current_UTC - decrypted_UTC ) );
		return false;
	}
	console.log( "** UTC verified : " + decrypted_UTC + " (current = " + current_UTC + ", diff = " + Math.abs( current_UTC - decrypted_UTC ) + ")" );


	// get AES KEY2
	aes_key2 = Buffer.concat( [new Uint8Array( decrpyted_buffer.slice( 0, 8 )), new Uint8Array( Buffer.from( ORG_CREDENTIAL.substring( 52, 52 + 16 ), 'hex' ))] );
	for( let i = 0; i < 16; i++ )
		aes_key2[i] ^= Buffer.from( AES_IV.substring( i * 2, i * 2 + 2 ), 'hex' ).readUInt8();


	// decrypt credential
	let decrypted_credential = [];
    try
	{
        const decipher = crypto.createDecipheriv( 'aes-128-ctr', aes_key2, Buffer.from( AES_IV, 'hex' ));
        decrypted_credential = Buffer.concat( [decipher.update( credential.substr( 32, 256 ), 'hex' ), decipher.final()] );
    }
	catch( error )
	{
        throw error;
    }


	// verfity credential with CREDENTIAL(ADC)
	// return if fail
	if ( ORG_CREDENTIAL.toLowerCase() != decrypted_credential.toString( 'hex' ).toLowerCase() )
	{
		console.log( "Invalid credential value has sent, deny login & all services for this client..." );
		return false;
	}


	console.log( "** Credential verified : PASS" );
	return true;
}

# [In case your server code is using Java]

Add following code to your existing code and use it for credential data validation. Authenticity can be determined by passing the credential value sent as a parameter in the login or authentication function of the existing server code to the verfityAppSealingCredential function provided in the code below. (The code below is also included in the SDK as a file named appsealing_credential.java).

** Note: In the code below, ORG_CREDENTIAL, AES_IV, and AES_KEY must be replaced with values obtained through the “Check Credential” function of the corresponding project in the ADC. If you just take the example code and use it intactly, credential value will not be verified properly.

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;


import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;


public class AppSealingCredential
{
    private static boolean verifyAppSealingCredential( final String credential )
    {
        // Need to Change : Get From ADC (via 'Check Credential') -----------------------------------------
        final String ORG_CREDENTIAL = "572E0E1459453F2078D6576FF71ECD0DBCA0484430C7FA7FE45B788A37DE3A04204F5A55FEA83AC9AFBA2C688594F75A3828B23972DB34858EC4F6CC3202533E44121E5F2614B227E18B6419A83810F7511D5E51FCACD5175A1CC550F83CB874A7378ACDAFE78EB2E329CD5D3C384061C4669674F1EE6B1B59FB7D91835DB7EE";


        final String AES_IV  = "055772B7434A4174749AFE09B1413472";
        final String AES_KEY = "71CA94A64A4DEBF5566495AB03F6798F";
        //-------------------------------------------------------------------------------------------------


        long decrypted_UTC = 0;
        byte[] decryptedUTC = new byte[8];


        // decrypt UTC
        try
        {
            SecretKeySpec key = new SecretKeySpec( DatatypeConverter.parseHexBinary( AES_KEY ), "AES" );
            IvParameterSpec ivSpec = new IvParameterSpec( DatatypeConverter.parseHexBinary( AES_IV ));


            Cipher cipher = Cipher.getInstance( "AES/CTR/NoPadding" );
            cipher.init( Cipher.DECRYPT_MODE, key, ivSpec );


            byte[] encryptedUTC = DatatypeConverter.parseHexBinary( credential.substring( 0, 32 ));
            System.arraycopy( cipher.doFinal( encryptedUTC ), 0, decryptedUTC, 0, 8 );
            System.out.println( DatatypeConverter.printHexBinary( decryptedUTC ));




            decrypted_UTC = ByteBuffer.wrap( decryptedUTC ).order( ByteOrder.LITTLE_ENDIAN ).getInt() & 0xFFFFFFFFFL;
        }
        catch( Exception e )
        {
            System.out.println( "[Error] " + e.getLocalizedMessage() );
        }


        // verfity UTC with current time (+/-) 10sec
        long current_UTC = System.currentTimeMillis() / 1000;	// get current UTC in seconds


        if ( Math.abs( current_UTC - decrypted_UTC ) > 10 )
        {
            System.out.println( "Invalid UTC value has sent, deny login & all services for this client..." );
            return false;
        }
        System.out.println( "** UTC verified : " + decrypted_UTC + " (current = " + current_UTC + ", diff = " + Math.abs( current_UTC - decrypted_UTC ) + ")" );


        // get AES KEY2
        byte[] aes_key2 = Arrays.copyOf( decryptedUTC, 16 );
        byte[] xor = DatatypeConverter.parseHexBinary( ORG_CREDENTIAL.substring( 52, 52 + 16 ));
        System.arraycopy( xor, 0, aes_key2, decryptedUTC.length, xor.length );


        for( int i = 0; i < 16; i++ )
            aes_key2[i] ^= ( byte )DatatypeConverter.parseHexBinary( AES_IV.substring( i * 2, i * 2 + 2 ))[0];
        System.out.println( DatatypeConverter.printHexBinary( aes_key2 ));


        // decrypt credential
        byte[] decrypted_credential = null;
        try
        {
            SecretKeySpec key = new SecretKeySpec( aes_key2, "AES" );
            IvParameterSpec ivSpec = new IvParameterSpec( DatatypeConverter.parseHexBinary( AES_IV ));


            Cipher cipher = Cipher.getInstance( "AES/CTR/NoPadding" );
            cipher.init( Cipher.DECRYPT_MODE, key, ivSpec );


            System.out.println( "########### " + credential.substring( 32, 32 + 256 ) );
            byte[] encrypted_credential = DatatypeConverter.parseHexBinary( credential.substring( 32, 32 + 256 ));
            decrypted_credential = cipher.doFinal( encrypted_credential );
        }
        catch( Exception e )
        {
            System.out.println( "[Error] " + e.getLocalizedMessage() );
        }


        // verfity credential with CREDENTIAL(ADC)
        // return if fail
        if ( !ORG_CREDENTIAL.equalsIgnoreCase( DatatypeConverter.printHexBinary( decrypted_credential )))
        {
            System.out.println( "Invalid credential value has sent, deny login & all services for this client..." );
            return false;
        }


        System.out.println( "** Credential verified : PASS" );
        return true;
    }
}

# [In case your server code is using http://ASP.NET (opens new window) / C#]

Add following code to your existing code and use it for credential data validation. Authenticity can be determined by passing the credential value sent as a parameter in the login or authentication function of the existing server code to the verfityAppSealingCredential function provided in the code below. (The code below is also included in the SDK as a file named appsealing_credential.cs, because C# does not support AES CTR mode, you must include the AesCtrTransform function for CTR processing as shown in the code below.)

** Note: In the code below, ORG_CREDENTIAL, AES_IV, and AES_KEY must be replaced with values obtained through the “Check Credential” function of the corresponding project in the ADC. If you just take the example code and use it intactly, credential value will not be verified properly.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;


namespace AppSealingSDK
{	
    class AppSealingCredential
    {
        public static void AesCtrTransform( byte[] key, byte[] salt, Stream inputStream, Stream outputStream )
        {
            SymmetricAlgorithm aes = new AesManaged { Mode = CipherMode.ECB, Padding = PaddingMode.None };


            int blockSize = aes.BlockSize / 8;
            if ( salt.Length != blockSize )
                throw new ArgumentException( "Salt size must be same as block size " + $"(actual: {salt.Length}, expected: {blockSize})" );


            byte[] counter = ( byte[] )salt.Clone();


            Queue<byte> xorMask = new Queue<byte>();
            var zeroIv = new byte[blockSize];
            ICryptoTransform counterEncryptor = aes.CreateEncryptor( key, zeroIv );


            int b;
            while(( b = inputStream.ReadByte()) != -1 )
            {
                if ( xorMask.Count == 0 )
                {
                    var counterModeBlock = new byte[blockSize];
                    counterEncryptor.TransformBlock( counter, 0, counter.Length, counterModeBlock, 0 );


                    for( var i2 = counter.Length - 1; i2 >= 0; i2-- )
                    {
                        if ( ++counter[i2] != 0 )
                            break;
                    }
                    foreach( var b2 in counterModeBlock )
                        xorMask.Enqueue( b2 );
                }
                var mask = xorMask.Dequeue();
                outputStream.WriteByte(( byte )((( byte )b ) ^ mask ));
            }
        }
        public static byte[] StringToByteArray( String hex )
        {
            return Enumerable.Range( 0, hex.Length / 2 ).Select( x => Convert.ToByte( hex.Substring( x * 2, 2 ), 16 )).ToArray();
        }
        public static bool VerifyAppSealingCredential( String credential )
        {
            // Need to Change : Get From ADC (via 'Check Credential') -----------------------------------------
            const String ORG_CREDENTIAL = "572E0E1459453F2078D6576FF71ECD0DBCA0484430C7FA7FE45B788A37DE3A04204F5A55FEA83AC9AFBA2C688594F75A3828B23972DB34858EC4F6CC3202533E44121E5F2614B227E18B6419A83810F7511D5E51FCACD5175A1CC550F83CB874A7378ACDAFE78EB2E329CD5D3C384061C4669674F1EE6B1B59FB7D91835DB7EE";


            const String AES_IV  = "055772B7434A4174749AFE09B1413472";
            const String AES_KEY = "71CA94A64A4DEBF5566495AB03F6798F";
            //-------------------------------------------------------------------------------------------------


            long decrypted_UTC = 0;
            byte[] decrypted_buffer = new byte[8];


            // decrypt UTC
            try
            {
                byte[] encrypted_UTC = StringToByteArray( credential.Substring( 0, 32 ));
                byte[] result = new byte[16];
                AesCtrTransform( StringToByteArray( AES_KEY ), StringToByteArray( AES_IV ), new MemoryStream( encrypted_UTC ), new MemoryStream( result ));
                Array.Copy( result, 0, decrypted_buffer, 0, 8 );
                decrypted_UTC = BitConverter.ToInt64( decrypted_buffer, 0 );
                System.Console.WriteLine( "** UTC = " + decrypted_UTC );
            }
            catch(Exception e )
            {
                System.Console.WriteLine( "[Error] " + e.Message );
            }


            // verfity UTC with current time (+/-) 10sec
            long current_UTC = DateTimeOffset.Now.ToUnixTimeMilliseconds() / 1000;	// get current UTC in seconds
            if ( Math.Abs( current_UTC - decrypted_UTC ) > 10 )
            {
                System.Console.WriteLine( "Invalid UTC value has sent, deny login & all services for this client..." );
                return false;
            }
            System.Console.WriteLine( "** UTC verified : " + decrypted_UTC + " (current = " + current_UTC + ", diff = " + Math.Abs(current_UTC - decrypted_UTC ) + ")" );


            // get AES KEY2
            byte[] aes_key2 = new byte[16];
            Array.Copy( decrypted_buffer, 0, aes_key2, 0, 8 );
            byte[] xor = StringToByteArray( ORG_CREDENTIAL.Substring( 52, 16 ));
            Array.Copy( xor, 0, aes_key2, decrypted_buffer.Length, xor.Length );


            for(int i = 0; i< 16; i++ )
                aes_key2[i] ^= ( byte )StringToByteArray( AES_IV.Substring( i* 2, 2 ))[0];


            // decrypt credential
            byte[] decrypted_credential = null;
            try
            {
                byte[] encrypted_credential = StringToByteArray( credential.Substring( 32, 256 ));
                decrypted_credential = new byte[encrypted_credential.Length];
                AesCtrTransform( aes_key2, StringToByteArray( AES_IV ), new MemoryStream( encrypted_credential ), new MemoryStream( decrypted_credential ));
            }
            catch(Exception e )
            {
                System.Console.WriteLine( "[Error] " + e.Message );
            }


            // verfity credential with CREDENTIAL(ADC)
            // return if fail
            if ( !ORG_CREDENTIAL.Equals( BitConverter.ToString( decrypted_credential ).Replace( "-", "" ), StringComparison.InvariantCultureIgnoreCase ))
            {
                System.Console.WriteLine( "Invalid credential value has sent, deny login & all services for this client..." );
                return false;
            }


            System.Console.WriteLine( "** Credential verified : PASS" );
            return true;
        }
}

# [In case your server code is using python]

Add following code to your existing code and use it for credential data validation. Authenticity can be determined by passing the credential value sent as a parameter in the login or authentication function of the existing server code to the verfityAppSealingCredential function provided in the code below. (The code below is also included in the SDK as a file named appsealing_credential.py)

** Note: In the code below, ORG_CREDENTIAL, AES_IV, and AES_KEY must be replaced with values obtained through the “Check Credential” function of the corresponding project in the ADC. If you just take the example code and use it intactly, credential value will not be verified properly.

from Crypto.Cipher import AES
from Crypto.Util import Counter
import time


#=============================================================
def verifyAppSealingCredential( credential ):


    # Need to Change : Get From ADC (via 'Check Credential') ---------------------------------------------
    ORG_CREDENTIAL = "572E0E1459453F2078D6576FF71ECD0DBCA0484430C7FA7FE45B788A37DE3A04204F5A55FEA83AC9AFBA2C688594F75A3828B23972DB34858EC4F6CC3202533E44121E5F2614B227E18B6419A83810F7511D5E51FCACD5175A1CC550F83CB874A7378ACDAFE78EB2E329CD5D3C384061C4669674F1EE6B1B59FB7D91835DB7EE"
    AES_IV  = "055772B7434A4174749AFE09B1413472"
    AES_KEY = "71CA94A64A4DEBF5566495AB03F6798F"
    #-----------------------------------------------------------------------------------------------------


	# decrypt UTC
    try:
        counter = Counter.new( 128, initial_value = int.from_bytes( bytes.fromhex( AES_IV ), "big" ))
        cipher = AES.new( bytes.fromhex( AES_KEY ), AES.MODE_CTR, counter = counter )
        descrypted_buffer = cipher.decrypt( bytes.fromhex( credential[:32] ))


        decrypted_UTC = int.from_bytes( descrypted_buffer[:8], "little" )
    except Exception as e:
        print( '[Error] ', e )
        return 0;


    # verfity UTC with current time (+/-) 10sec
    current_UTC = round( time.time() * 1000 );	# get current UTC in seconds


    if abs( current_UTC - decrypted_UTC ) > 10:
        print( "Invalid UTC value has sent, deny login & all services for this client..." )
        #return 0


    print( "** UTC verified : ", decrypted_UTC, " (current = ", current_UTC, ", diff = ", abs( current_UTC - decrypted_UTC ), ")" )


    # get AES KEY2
    aes_key2 = bytearray( 16 )
    aes_key2[0:8] = descrypted_buffer[0:8]
    aes_key2[8:8] = bytes.fromhex( ORG_CREDENTIAL[52:68] )
    print( aes_key2[:16].hex() )


    for i in range( 0, 16 ):
        aes_key2[i] ^= bytes.fromhex( AES_IV[i * 2:i * 2 + 2] )[0]
    print( aes_key2[:16].hex() )


    # decrypt credential
    try:
        counter2 = Counter.new( 128, initial_value = int.from_bytes( bytes.fromhex( AES_IV ), "big" ))
        cipher2 = AES.new( aes_key2[:16], AES.MODE_CTR, counter = counter2 )
        print( credential[32:288] )
        decrypted_credential = cipher2.decrypt( bytes.fromhex( credential[32:288] ))
    except Exception as e:
        print( '[Error] ', e )
        return 0;


    # verfity credential with CREDENTIAL(ADC)
    # return if fail
    if ORG_CREDENTIAL.casefold() != decrypted_credential.hex().casefold():
        print( "Invalid credential value has sent, deny login & all services for this client..." )
        return 0


    print( "** Credential verified : PASS" )
    return 1

# [In case your server code is using ruby script]

Add following code to your existing code and use it for credential data validation. Authenticity can be determined by passing the credential value sent as a parameter in the login or authentication function of the existing server code to the verfityAppSealingCredential function provided in the code below. (The code below is also included in the SDK as a file named appsealing_credential.rb)

** Note: In the code below, ORG_CREDENTIAL, AES_IV, and AES_KEY must be replaced with values obtained through the “Check Credential” function of the corresponding project in the ADC. If you just take the example code and use it intactly, credential value will not be verified properly.

require 'securerandom'
require 'net/https'
require 'json'


# Need to Change : Get From ADC (via 'Check Credential') ---------------------------------------------
ORG_CREDENTIAL = "572E0E1459453F2078D6576FF71ECD0DBCA0484430C7FA7FE45B788A37DE3A04204F5A55FEA83AC9AFBA2C688594F75A3828B23972DB34858EC4F6CC3202533E44121E5F2614B227E18B6419A83810F7511D5E51FCACD5175A1CC550F83CB874A7378ACDAFE78EB2E329CD5D3C384061C4669674F1EE6B1B59FB7D91835DB7EE"
AES_IV  = "055772B7434A4174749AFE09B1413472"
AES_KEY = "71CA94A64A4DEBF5566495AB03F6798F"
#-----------------------------------------------------------------------------------------------------


def verifyAppSealingCredential( credential )


    # decrypt UTC
    begin
        aes = OpenSSL::Cipher::AES.new( "128-CTR" )
        aes.decrypt
        aes.key = [AES_KEY].pack( 'H*' )
        aes.iv = [AES_IV].pack( 'H*' )
        decrypted_buffer = aes.update( [credential[0..31]].pack( 'H*' )) + aes.final


        decrypted_UTC = decrypted_buffer.slice( 0, 8 ).unpack( 'V' ).first
    rescue => e
        puts '[Error] ' + e.to_s
        return 0
	end


    # verfity UTC with current time (+/-) 10sec
    current_UTC = Time.now.strftime( '%s%L' ).to_i;	# get current UTC in seconds


    if ( current_UTC - decrypted_UTC ).abs > 10
        puts "Invalid UTC value has sent, deny login & all services for this client..."
        return 0
    end


    puts "** UTC verified : " + decrypted_UTC.to_s + " (current = " + current_UTC.to_s + ", diff = " + ( current_UTC - decrypted_UTC ).abs.to_s + ")"


    # get AES KEY2
    aes_key2 = decrypted_buffer.bytes.slice( 0, 8 ) + [ORG_CREDENTIAL[52..67]].pack( 'H*' ).bytes
    for i in 0..15 do
        aes_key2[i] ^= [AES_IV[i * 2..i * 2 + 1]].pack( 'H*' ).bytes[0]
    end


    # decrypt credential
    begin
        aes = OpenSSL::Cipher::AES.new( "128-CTR" )
        aes.decrypt
        aes.key = aes_key2.pack( 'C*' )
        aes.iv = [AES_IV].pack( 'H*' )
        decrypted_credential = aes.update( [credential[32..287]].pack( 'H*' )) + aes.final
    rescue => e
        puts '[Error] ' + e.to_s
        return 0
	end


    # verfity credential with CREDENTIAL(ADC)
    # return if fail
    if ORG_CREDENTIAL.casecmp( decrypted_credential.unpack( 'H*' ).first ) != 0
        print( "Invalid credential value has sent, deny login & all services for this client..." )
        return 0
    end


    print( "** Credential verified : PASS" )
    return 1


end

# [In case your server code is using C++]

Add following code to your existing code and use it for credential data validation. Authenticity can be determined by passing the credential value sent as a parameter in the login or authentication function of the existing server code to the verfityAppSealingCredential function provided in the code below. (The code below is also included in the SDK as a file named appsealing_credential.cpp with aes.hpp/aes.cpp)

** Note: In the code below, ORG_CREDENTIAL, AES_IV, and AES_KEY must be replaced with values obtained through the “Check Credential” function of the corresponding project in the ADC. If you just take the example code and use it intactly, credential value will not be verified properly.

#include <iostream>
#include <vector>
#include <time.h>
#include "aes.cpp"


typedef std::vector<unsigned char> bytes;
bytes HexToBytes( const std::string& hex )
{
    std::vector<unsigned char> bytes;
    for( unsigned int i = 0; i < hex.length(); i += 2 )
    {
        std::string byteString = hex.substr( i, 2 );
        unsigned char byte = ( unsigned char )strtol( byteString.c_str(), NULL, 16 );
        bytes.push_back( byte );
    }
    return bytes;
}


static bool verifyAppSealingCredential( const char* credential )
{
    // Need to Change : Get From ADC (via 'Check Credential') ---------------------------------------------
    const char* ORG_CREDENTIAL = "572E0E1459453F2078D6576FF71ECD0DBCA0484430C7FA7FE45B788A37DE3A04204F5A55FEA83AC9AFBA2C688594F75A3828B23972DB34858EC4F6CC3202533E44121E5F2614B227E18B6419A83810F7511D5E51FCACD5175A1CC550F83CB874A7378ACDAFE78EB2E329CD5D3C384061C4669674F1EE6B1B59FB7D91835DB7EE";
    const char* AES_IV  = "055772B7434A4174749AFE09B1413472";
    const char* AES_KEY = "71CA94A64A4DEBF5566495AB03F6798F";
    //-----------------------------------------------------------------------------------------------------


    unsigned char decrypted_buffer[16], decrypted_credential[128];


    // decrypt UTC
    struct AES_ctx ctx;
    try
    {
        memcpy( decrypted_buffer, HexToBytes( std::string( credential ).substr( 0, 32 )).data(), 16 );
        
        AES_init_ctx_iv( &ctx, HexToBytes( AES_KEY ).data(), HexToBytes( AES_IV ).data() );
        AES_CTR_xcrypt_buffer( &ctx, decrypted_buffer, 16 );


        long decrypted_UTC = *(( long* )decrypted_buffer );


        std::cout << "** UTC = " << decrypted_UTC << "\n";


        // verfity UTC with current time (+/-) 10sec
        time_t current_UTC = time( 0 );
        current_UTC = 1664439768;//REMOVE
        if ( abs( current_UTC - decrypted_UTC ) > 10 )
        {
            std::cout << "Invalid UTC value has sent, deny login & all services for this client...\n";
            return false;
        }
        std::cout << "** UTC verified : " << decrypted_UTC << " (current = " << current_UTC << ", diff = " << abs(current_UTC - decrypted_UTC ) << ")\n";


        // get AES KEY2
        unsigned char aes_key2[16];
        memcpy( aes_key2, decrypted_buffer, 8 );
        bytes XOR = HexToBytes( std::string( ORG_CREDENTIAL ).substr( 52, 16 ));
        memcpy( aes_key2 + 8, XOR.data(), XOR.size() );


        for( int i = 0; i < 16; i++ )
            aes_key2[i] ^= HexToBytes( std::string( AES_IV ).substr( i * 2, 2 )).data()[0];


        // decrypt credential
        memcpy( decrypted_credential, HexToBytes( std::string( credential ).substr( 32, 256 )).data(), 128 );


        AES_init_ctx_iv( &ctx, aes_key2, HexToBytes( AES_IV ).data() );
        AES_CTR_xcrypt_buffer( &ctx, decrypted_credential, 128 );
    }
    catch( const std::out_of_range& e )
    {
        std::cout << "pos exceeds string size\n";
    }


    // verfity credential with CREDENTIAL(ADC)
    // return if fail
    if ( memcmp( HexToBytes( ORG_CREDENTIAL ).data(), decrypted_credential, 128 ) != 0 )
    {
        std::cout << "Invalid credential value has sent, deny login & all services for this client...";
        return false;
    }
    std::cout << "** Credential verified : PASS";
}
Last Updated: 11/19/2024, 6:55:37 AM