mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-26 07:05:49 +00:00
352 lines
8.7 KiB
JavaScript
352 lines
8.7 KiB
JavaScript
var async = require('async');
|
|
var blobClient = require('./blob');
|
|
var AuthInfo = require('./authinfo').AuthInfo;
|
|
var crypt = require('./crypt').Crypt;
|
|
|
|
function VaultClient(opts) {
|
|
if (!opts) {
|
|
opts = {};
|
|
}
|
|
|
|
if (typeof opts === 'string') {
|
|
opts = { domain: opts };
|
|
}
|
|
|
|
this.domain = opts.domain || 'ripple.com';
|
|
this.authInfo = new AuthInfo();
|
|
this.infos = { };
|
|
};
|
|
|
|
/**
|
|
* Get a ripple name from a given account address, if it has one
|
|
* @param {string} address - Account address to query
|
|
* @param {string} url - Url of blob vault
|
|
*/
|
|
|
|
VaultClient.prototype.getRippleName = function(address, url, callback) {
|
|
//use the url from previously retrieved authInfo, if necessary
|
|
if (!url) {
|
|
callback(new Error('Blob vault URL is required'));
|
|
} else {
|
|
blobClient.getRippleName(url, address, callback);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Authenticate and retrieve a decrypted blob using a ripple name and password
|
|
*
|
|
* @param {string} username
|
|
* @param {string} password
|
|
* @param {function} fn - Callback function
|
|
*/
|
|
|
|
VaultClient.prototype.login = function(username, password, callback) {
|
|
var self = this;
|
|
|
|
function getAuthInfo(callback) {
|
|
self.authInfo.get(self.domain, username, function(err, authInfo) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
|
|
if (authInfo.version !== 3) {
|
|
return callback(new Error('This wallet is incompatible with this version of the vault-client.'));
|
|
}
|
|
|
|
if (!authInfo.pakdf) {
|
|
return callback(new Error('No settings for PAKDF in auth packet.'));
|
|
}
|
|
|
|
if (!authInfo.exists) {
|
|
return callback(new Error('User does not exist.'));
|
|
}
|
|
|
|
if (typeof authInfo.blobvault !== 'string') {
|
|
return callback(new Error('No blobvault specified in the authinfo.'));
|
|
}
|
|
|
|
callback(null, authInfo);
|
|
});
|
|
};
|
|
|
|
function deriveLoginKeys(authInfo, callback) {
|
|
//derive login keys
|
|
crypt.derive(authInfo.pakdf, 'login', username.toLowerCase(), password, function(err, keys) {
|
|
if (err) {
|
|
callback(err);
|
|
} else {
|
|
callback(null, authInfo, keys);
|
|
}
|
|
});
|
|
};
|
|
|
|
function getBlob(authInfo, keys, callback) {
|
|
blobClient.get(authInfo.blobvault, keys.id, keys.crypt, function(err, blob) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
|
|
//save for relogin
|
|
self.infos[keys.id] = authInfo;
|
|
|
|
callback(null, {
|
|
blob: blob,
|
|
username: authInfo.username,
|
|
verified: authInfo.emailVerified
|
|
});
|
|
});
|
|
};
|
|
|
|
var steps = [
|
|
getAuthInfo,
|
|
deriveLoginKeys,
|
|
getBlob
|
|
];
|
|
|
|
async.waterfall(steps, callback);
|
|
};
|
|
|
|
/**
|
|
* Retreive and decrypt blob using a blob url, id and crypt derived previously.
|
|
*
|
|
* @param {string} url - Blob vault url
|
|
* @param {string} id - Blob id from previously retreived blob
|
|
* @param {string} key - Blob decryption key
|
|
* @param {function} fn - Callback function
|
|
*/
|
|
|
|
VaultClient.prototype.relogin = function(url, id, key, callback) {
|
|
//use the url from previously retrieved authInfo, if necessary
|
|
if (!url && this.infos[id]) {
|
|
url = this.infos[id].blobvault;
|
|
}
|
|
|
|
if (!url) {
|
|
return callback(new Error('Blob vault URL is required'));
|
|
}
|
|
|
|
blobClient.get(url, id, key, function(err, blob) {
|
|
if (err) {
|
|
callback(err);
|
|
} else {
|
|
callback (null, { blob: blob });
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Decrypt the secret key using a username and password
|
|
*
|
|
* @param {string} username
|
|
* @param {string} password
|
|
* @param {string} encryptSecret
|
|
* @param {function} fn - Callback function
|
|
*/
|
|
|
|
VaultClient.prototype.unlock = function(username, password, encryptSecret, callback) {
|
|
var self = this;
|
|
|
|
function deriveUnlockKey(authInfo, callback) {
|
|
//derive unlock key
|
|
crypt.derive(authInfo.pakdf, 'unlock', username.toLowerCase(), password, function(err, keys) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
|
|
callback(null, {
|
|
keys: keys,
|
|
secret: crypt.decrypt(keys.unlock, encryptSecret)
|
|
});
|
|
});
|
|
};
|
|
|
|
self.authInfo.get(self.domain, username, function(err, authInfo) {
|
|
if (err) {
|
|
callback(err);
|
|
} else {
|
|
deriveUnlockKey(authInfo, callback);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Retrieve the decrypted blob and secret key in one step using
|
|
* the username and password
|
|
*
|
|
* @param {string} username
|
|
* @param {string} password
|
|
* @param {function} fn - Callback function
|
|
*/
|
|
|
|
VaultClient.prototype.loginAndUnlock = function(username, password, callback) {
|
|
var self = this;
|
|
|
|
function deriveUnlockKey(authInfo, blob, callback) {
|
|
//derive unlock key
|
|
crypt.derive(authInfo.pakdf, 'unlock', username.toLowerCase(), password, function(err, keys) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
|
|
callback(null, {
|
|
blob: blob,
|
|
unlock: keys.unlock,
|
|
secret: crypt.decrypt(keys.unlock, blob.encrypted_secret),
|
|
username: authInfo.username,
|
|
verified: authInfo.emailVerified
|
|
});
|
|
});
|
|
};
|
|
|
|
this.login(username, password, function(err, resp) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
|
|
if (!resp.blob || !resp.blob.encrypted_secret) {
|
|
return callback(new Error('Unable to retrieve blob and secret.'));
|
|
}
|
|
|
|
if (!resp.blob.id || !resp.blob.key) {
|
|
return callback(new Error('Unable to retrieve keys.'));
|
|
}
|
|
|
|
//get authInfo via id - would have been saved from login
|
|
var authInfo = self.infos[resp.blob.id];
|
|
|
|
if (!authInfo) {
|
|
return callback(new Error('Unable to find authInfo'));
|
|
}
|
|
|
|
deriveUnlockKey(authInfo, resp.blob, callback);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Check blobvault for existance of username
|
|
*
|
|
* @param {string} username
|
|
* @param {function} fn - Callback function
|
|
*/
|
|
|
|
VaultClient.prototype.exists = function(username, callback) {
|
|
this.authInfo.get(this.domain, username.toLowerCase(), function(err, authInfo) {
|
|
if (err) {
|
|
callback(err);
|
|
} else {
|
|
callback(null, !!authInfo.exists);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Verify an email address for an existing user
|
|
*
|
|
* @param {string} username
|
|
* @param {string} token - Verification token
|
|
* @param {function} fn - Callback function
|
|
*/
|
|
|
|
VaultClient.prototype.verify = function(username, token, callback) {
|
|
var self = this;
|
|
|
|
this.authInfo.get(this.domain, username.toLowerCase(), function(err, authInfo) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
|
|
if (typeof authInfo.blobvault !== 'string') {
|
|
return callback(new Error('No blobvault specified in the authinfo.'));
|
|
}
|
|
|
|
blobClient.verify(authInfo.blobvault, username.toLowerCase(), token, callback);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Register a new user and save to the blob vault
|
|
*
|
|
* @param {object} options
|
|
* @param {string} options.username
|
|
* @param {string} options.password
|
|
* @param {string} options.masterkey //optional, will create if absent
|
|
* @param {string} options.email
|
|
* @param {string} options.activateLink
|
|
* @param {object} options.oldUserBlob //optional
|
|
* @param {function} fn
|
|
*/
|
|
|
|
VaultClient.prototype.register = function(options, fn) {
|
|
var self = this;
|
|
var username = String(options.username).trim();
|
|
var password = String(options.password).trim();
|
|
|
|
function getAuthInfo(callback) {
|
|
self.authInfo.get(self.domain, username, function(err, authInfo) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
|
|
if (typeof authInfo.blobvault !== 'string') {
|
|
return callback(new Error('No blobvault specified in the authinfo.'));
|
|
}
|
|
|
|
if (!authInfo.pakdf) {
|
|
return callback(new Error('No settings for PAKDF in auth packet.'));
|
|
}
|
|
|
|
callback(null, authInfo);
|
|
});
|
|
};
|
|
|
|
function deriveKeys(authInfo, callback) {
|
|
// derive unlock and login keys
|
|
var keys = { };
|
|
|
|
function deriveKey(keyType, callback) {
|
|
crypt.derive(authInfo.pakdf, keyType, username.toLowerCase(), password, function(err, key) {
|
|
if (err) {
|
|
callback(err);
|
|
} else {
|
|
keys[keyType] = key;
|
|
callback();
|
|
}
|
|
});
|
|
};
|
|
|
|
async.eachSeries([ 'login', 'unlock' ], deriveKey, function(err) {
|
|
if (err) {
|
|
callback(err);
|
|
} else {
|
|
callback(null, authInfo, keys);
|
|
}
|
|
});
|
|
};
|
|
|
|
function create(authInfo, keys) {
|
|
var params = {
|
|
url: authInfo.blobvault,
|
|
id: keys.loginKeys.id,
|
|
crypt: keys.loginKeys.crypt,
|
|
unlock: keys.unlockKeys.unlock,
|
|
username: username,
|
|
email: options.email,
|
|
masterkey: options.masterkey || crypt.createMaster(),
|
|
activateLink: options.activateLink,
|
|
oldUserBlob: options.oldUserBlob
|
|
};
|
|
|
|
blobClient.create(params, function(err, blob) {
|
|
if (err) {
|
|
callback(err);
|
|
} else {
|
|
callback(null, blob, loginKeys, authInfo.username);
|
|
}
|
|
});
|
|
};
|
|
|
|
async.waterfall([ getAuthInfo, deriveKeys ], create);
|
|
};
|
|
|
|
exports.VaultClient = VaultClient;
|