Initial Checkin

This commit is contained in:
Matt Corallo 2014-01-09 21:48:05 -10:00
commit a25cf5f176
9 changed files with 286 additions and 0 deletions

8
background.js Normal file
View File

@ -0,0 +1,8 @@
function check_first_install() {
if (localStorage.getItem('first_install_ran'))
return;
localStorage.setItem('first_install_ran', 1);
chrome.tabs.create({url: "options.html"});
}
check_first_install();

115
helpers.js Normal file
View File

@ -0,0 +1,115 @@
/************************************************
*** Utilities to store data in local storage ***
************************************************/
var storage = {};
storage.putEncrypted = function(key, value) {
//TODO
localStorage.setItem("e" + key, value);
}
storage.getEncrypted = function(key, defaultValue) {
//TODO
var value = localStorage.getItem("e" + key);
if (value === null)
return defaultValue;
return value;
}
storage.putUnencrypted = function(key, value) {
localStorage.setItem("u" + key, value);
}
storage.getUnencrypted = function(key, defaultValue) {
var value = localStorage.getItem("u" + key);
if (value === null)
return defaultValue;
return value;
}
/************************************************
*** Utilities to communicate with the server ***
************************************************/
var URL_BASE = "http://textsecure-test.herokuapp.com";
var URL_CALLS = {};
URL_CALLS['devices'] = "/v1/devices";
URL_CALLS['keys'] = "/v1/keys";
/**
* REQUIRED PARAMS:
* call: URL_CALLS entry
* httpType: POST/GET/PUT/etc
* success_callback: function(response object) called on success
* error_callback: function(http status code = -1 or != 200) called on failure
* OPTIONAL PARAMS:
* urlParameters: crap appended to the url (probably including a leading /)
* user: user name to be sent in a basic auth header
* password: password to be sent in a basic auth header
* jsonData: JSON data sent in the request body
*/
function doAjax(param) {
if (param.urlParameters === undefined)
param.urlParameters = "";
$.ajax(URL_BASE + URL_CALLS[param.call] + param.urlParameters, {
type: param.httpType,
data: param.jsonData,
beforeSend: function(xhr) {
if (param.user !== undefined && param.password !== undefined)
xhr.setRequestHeader("Authorization", "Basic " + btoa(param.user + ":" + param.password));
},
success: function(response, textStatus, jqXHR) {
param.success_callback(response);
},
error: function(jqXHR, textStatus, errorThrown) {
var code = jqXHR.status;
if (code > 999 || code < 100)
code = -1;
param.error_callback(code);
},
cache: false
});
}
/*******************************************
*** Utilities to manage keys/randomness ***
*******************************************/
function getRandomBytes(size) {
//TODO: Better random (https://www.grc.com/r&d/js.htm?
try {
var array = new Uint8Array(size);
window.crypto.getRandomValues(array);
return array;
} catch (err) {
//TODO: ummm...wat?
}
}
function getNewPubKey(keyName) {
//TODO
var pubKey = "BRTJzsHPUWRRBxyo5MoaBRidMk2fwDlfqvU91b6pzbED";
var privKey = "";
storage.putEncrypted("pubKey" + keyName, pubKey);
storage.putEncrypted("privKey" + keyName, privKey);
return pubKey;
}
function getExistingPubKey(keyName) {
return storage.getEncrypted("pubKey" + keyName);
}
function generateKeys() {
var identityKey = getExistingPubKey("identityKey");
if (identityKey === undefined)
identityKey = getNewPubKey("identityKey");
var keyGroupId = storage.getEncrypted("lastKeyGroupId", -1) + 1;
storage.putEncrypted("lastKeyGroupId", keyGroupId);
var keys = {};
keys.keys = [];
for (var i = 0; i < 100; i++)
keys.keys[i] = {keyId: i, publicKey: getNewPubKey("key" + keyGroupId + i), identityKey: identityKey};
// 0xFFFFFF == 16777215
keys.lastResortKey = {keyId: 16777215, publicKey: getNewPubKey("lastResortKey" + keyGroupId), identityKey: identityKey};
return keys;
}

BIN
icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

6
jquery.js vendored Normal file

File diff suppressed because one or more lines are too long

23
manifest.json Normal file
View File

@ -0,0 +1,23 @@
{
"manifest_version": 2,
"name": "TextSecure",
"description": "Secure texting ",
"version": "0.0.1",
"offline_enabled": false,
"icons": { "128": "icon.png" },
"browser_action": {
"default_icon": {
"19": "icon.png"
},
"default_popup": "popup.html"
},
"background": {
"scripts": ["background.js"]
},
"options_page": "options.html"
}

19
options.css Normal file
View File

@ -0,0 +1,19 @@
* {
font-family: Ubuntu, Segoe, 'Lucidia Grande', sans-serif;
}
html {
margin: 12px 0 0 100px;
}
h1 {
font-size: 14pt;
font-weight: normal;
padding-bottom: 10px;
border-bottom: 1px solid #e0e0e0;
}
h2 {
font-size: 12pt;
font-weight: normal;
}

26
options.html Normal file
View File

@ -0,0 +1,26 @@
<html>
<head>
<title>TextSecure Options</title>
<link rel="stylesheet" href="options.css">
</head>
<body>
<h1>TextSecure</h1>
<div id="init-setup" style="display: none;">
<h2>Welcome to TextSecure. To get startet please get a 6-digit setup code from your phone and enter it below.</h2>
Phone number: <input type="text" title="Enter the phone number which you've registered TextSecure with." size="8" id="number" />
Code: <input type="text" pattern="[0-9]{3}-?[0-9]{3}" title="Enter the 6-didgit verificaion code displayed on your phone." size="8" id="code" /><br>
<button id="init-go" >Sync</button>
</div>
<div id="verify" style="display: none;">
<div id="verify1">Verifying number and setup code...<span id="verify1done"></span></div>
<div id="verify2">Generating keys...<span id="verify2done"></span></div>
<div id="verify3">Registering...<span id="verify3done"></span></div>
</div>
<div id="setup-complete" style="display: none;">
<h2>You are now registered on TextSecure with number <span id="complete-number"></span></h2>
</div>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="helpers.js"></script>
<script type="text/javascript" src="options.js"></script>
</body>
</html>

76
options.js Normal file
View File

@ -0,0 +1,76 @@
function codeMatches() {
var match = $('#code').val().match(/[0-9]{3}-?[0-9]{3}/g)
return match != null && match.length == 1 && match[0] == $('#code').val();
}
function numberMatches() {
return $('#number').value().replace(/\D/g, '').length == 10;
}
$('#code').on('change', function() {
if (!codeMatches())
$('#code').attr('style', 'background-color:#ff6666;');
else
$('#code').attr('style', '');
});
$('#number').on('change', function() {
if (!numberMatches())
$('#number').attr('style', 'background-color:#ff6666;');
else
$('#number').attr('style', '');
}
$('#init-go').click(function() {
if (codeMatches() && numberMatches()) {
var signaling_key = getRandomBytes(32 + 20);
var password = getRandomBytes(16);
var number = $('#number').value().replace(/\D/g, '');
$('#init-setup').hide();
$('#verify1done').html('');
$('#verify2done').html('');
$('#verify3done').html('');
$('#verify').show();
doAjax({call: 'devices', httpType: 'PUT', urlParameters: '/' + $('#code').val(), success_callback: function(response) {
$('#verify1done').html('done');
var keys = generateKeys();
$('#verify2done').html('done');
doAjax({call: 'keys', httpType: 'PUT', user: number + "." + response, password: password,
jsonData: keys, success_callback: function(response) {
$('#complete-number').html('');
$('#verify').hide();
$('#setup-complete').show();
}, error_callback: function(code) {
alert(code); //TODO
}
});
storage.putEncrypted('signaling_key', signaling_key);
storage.putEncrypted('login_password', password);
}, error_callback: function(code) {
var error;
switch(code) {
case 403:
error = "Invalid code, please try again.";
break;
case -1:
error = "Error connecting to server, please check your network connection.";
break;
default:
error = "Unknown error, please try again later.";
console.log("Got error code " + code);
}
alert(error); //TODO
}, user: number, password: password,
jsonData: {signalingKey: btoa(String.fromCharCode.apply(null, signaling_key)), supportsSms: false, fetchesMessages: true}
});
}
});
if (storage.getUnencrypted("number_id") === undefined) {
$('#init-setup').show();
} else {
$('#complete-number').html(storage.getUnencrypted("number_id"));
$('#setup-complete').show();
}

13
popup.html Normal file
View File

@ -0,0 +1,13 @@
<html>
<head>
<script type="text/javascript" src="jquery-2.0.3.min.js"></script>
</head>
<body>
<a onclick="$('#inbox').style('display: block;'); $('#send').style('display: none;')">Inbox</a>
<a onclick="$('#inbox').style('display: none;'); $('#send').style('display: block;')">Send</a>
<div id="inbox">
</div>
<div id="send" style="display:none;"></div>
</body>
</html>