Skip to content

Instantly share code, notes, and snippets.

@bellbind
Created May 15, 2014 07:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bellbind/87c0caf6c48cfd26ddf6 to your computer and use it in GitHub Desktop.
Save bellbind/87c0caf6c48cfd26ddf6 to your computer and use it in GitHub Desktop.
[nodejs]Understanding S/Key style One-time password system
// S/Key style One-time Password system
var crypto = require("crypto");
// [functions for OTP system]
// generate server info of one time password
var initAccount = function (passphrase) {
var alg = "sha256";
var enc = "hex";
var times = 100;
var seed = crypto.randomBytes(10).toString(enc);
var otp = seed + passphrase;
for (var i = 0; i < times; i++) {
var hash = crypto.createHash(alg);
otp = hash.update(otp).digest(enc);
}
return {
seed: seed,
times: times,
otp: otp,
alg: alg,
enc: enc,
};
};
// create message to client
var otpMessage = function (account) {
if (account.times <= 1) return null; // require to re-initialize seed
return {
seed: account.seed,
times: account.times - 1,
alg: account.alg,
enc: account.enc,
};
};
// generate OTP at clients
var generateOtp = function (message, passphrase) {
var otp = message.seed + passphrase;
for (var i = 0; i < message.times; i++) {
var hash = crypto.createHash(message.alg);
otp = hash.update(otp).digest(message.enc);
}
return otp;
};
// check OTP is valid at server
var checkOtp = function (account, otp) {
var hash = crypto.createHash(account.alg);
var valid = hash.update(otp).digest(account.enc) === account.otp;
if (valid) {
// drop previous valid otp
account.otp = otp;
account.times -= 1;
return true;
}
return false;
};
//////////////////////////////////////////////////////////////////////
// [case of otp flow]
var main = function () {
// [start]
var client = {pass: "Hello"};
var server = initAccount(client.pass);
// [first session]
// client requests info for server
var msg = otpMessage(server);
// client gerenates otp
var otp = generateOtp(msg, client.pass);
// server checks otp
var result = checkOtp(server, otp); // => true
console.log("generated otp success: " + result);
// server checks stolen otp
var result = checkOtp(server, otp); // => false
console.log("reused otp fail: " + result);
// [next session]
// client requests info for server
var msg = otpMessage(server);
// client gerenates otp
var otp = generateOtp(msg, client.pass);
// server checks otp
var result = checkOtp(server, otp); // => true
console.log("generated otp success: " + result);
};
main();
@bellbind
Copy link
Author

As a OTP System,

  • The server does not record client pass-phrase, and drop one validated OTP
  • The pass phrase itself is not passed via network

but

  • Server modifies the info of last otp and times at each time succeeded
  • Server required to forcely reinitialize to generate new seed when reached times is 1

@bellbind
Copy link
Author

[Key Concept of S/Key style OTP system]

At first, The server holds multiply(n times) applied hash function to the client shared key. e.g.

  • On Server: [n, H(H(...H(Key)...))]

When The client request for auth, the server respond times as "n-1".

  • (from Server) Auth Request: [n-1]

The client calculate and respond n-1 times applied the hash function to the shared key.

  • (from Client) Auth Response: [H(...(H(Key)...)]

The server apply the hash function to the auth response and compare stored value

  • H(Auth Response) == H(H.(...H(Key)...))

If comparation success,
then update the server stored value to the Auth Response as the n-1 hashed key
(== the old password dropped forever as ONE TIME),
and the server perform authentication succeeded.

  • On Server: [n-1, H(...H(Key)...)]

If comparation failed, then the server just perform authentication failed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment