Skip to content
This repository has been archived by the owner on Oct 28, 2021. It is now read-only.

Commit

Permalink
Handling passphrase
Browse files Browse the repository at this point in the history
  • Loading branch information
Rémy HUBSCHER committed Mar 14, 2017
1 parent 2ed617a commit fe8dcff
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 35 deletions.
73 changes: 60 additions & 13 deletions Main.elm
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type alias Flags =
{ lockAfterSeconds : Maybe Int
, fxaToken : Maybe String
, contentWasSyncedRemotely : Maybe String
, passphrase : Maybe String
}


Expand Down Expand Up @@ -65,7 +66,7 @@ type alias Model =
, lockAfterSeconds : Maybe Int
, contentWasSynced : Bool
, fxaToken : Maybe String
, passphrase : String
, passphrase : Maybe String
, content : String
, loadedContent :
-- may be desynchronized with "content", only used to redraw the
Expand Down Expand Up @@ -98,12 +99,20 @@ init flags =
else
False

lock =
case flags.passphrase of
Nothing ->
True

Just _ ->
False

model =
{ lock = True
{ lock = lock
, lockAfterSeconds = flags.lockAfterSeconds
, fxaToken = flags.fxaToken
, contentWasSynced = contentWasSynced
, passphrase = ""
, passphrase = flags.passphrase
, content = ""
, loadedContent = ""
, modified = False
Expand All @@ -114,7 +123,7 @@ init flags =
, gearMenuOpen = False
}
in
lockOnStartup model flags.lockAfterSeconds
lockOnStartup (Debug.log "Init" model) flags.lockAfterSeconds



Expand Down Expand Up @@ -151,19 +160,51 @@ lockOnStartup model lockAfterSeconds =
model ! [ getData {}, retrieveData model.fxaToken ]


encryptIfPassphrase : Maybe String -> String -> Cmd Msg
encryptIfPassphrase passphrase content =
case passphrase of
Nothing ->
Cmd.none

Just passphrase ->
encryptData { content = content, passphrase = passphrase }


decryptIfPassphrase : Maybe String -> Maybe String -> Cmd Msg
decryptIfPassphrase passphrase content =
case passphrase of
Nothing ->
Cmd.none

Just passphrase ->
decryptData { content = content, passphrase = passphrase }


update : Msg -> Model -> ( Model, Cmd Msg )
update message model =
case message of
NewPassphrase passphrase ->
{ model | passphrase = passphrase } ! []
{ model
| passphrase =
if passphrase == "" then
Nothing
else
Just passphrase
}
! []

GetData ->
model ! [ getData {}, retrieveData model.fxaToken ]
{ model | error = "" } ! [ savePassphrase model.passphrase, getData {}, retrieveData model.fxaToken ]

DataRetrieved list ->
case list of
[ "pad", data ] ->
model ! [ decryptData (Debug.log "data retrieved" { content = Just data, passphrase = model.passphrase }) ]
case model.passphrase of
Nothing ->
{ model | error = "No passphrase to decrypt content." } ! []

Just passphrase ->
model ! [ decryptIfPassphrase model.passphrase (Just data) ]

[ key, value ] ->
Debug.crash ("Unsupported newData key: " ++ key)
Expand Down Expand Up @@ -213,10 +254,10 @@ update message model =
]

NewError error ->
{ model | lock = True, content = "", passphrase = "", error = "Wrong passphrase" } ! []
{ model | lock = True, content = "", passphrase = Nothing, error = "Wrong passphrase" } ! []

Lock ->
{ model | lock = True, gearMenuOpen = False, content = "", passphrase = "", error = "" } ! [ encryptData { content = model.content, passphrase = model.passphrase } ]
{ model | lock = True, gearMenuOpen = False, content = "", passphrase = Nothing } ! [ dropPassphrase {}, encryptIfPassphrase model.passphrase model.content ]

DataSaved key ->
case key of
Expand All @@ -234,7 +275,7 @@ update message model =

TimeOut debounceCount ->
if debounceCount == model.debounceCount then
model ! [ encryptData { content = model.content, passphrase = model.passphrase } ]
model ! [ encryptIfPassphrase model.passphrase model.content ]
else
model ! []

Expand Down Expand Up @@ -284,10 +325,10 @@ update message model =
_ =
Debug.log "kinto result" result
in
model ! [ saveData { key = "contentWasSynced", content = Encode.bool True } ]
model ! [ saveData { key = "contentWasSynced", content = Encode.string "true" } ]

DataRetrievedFromKinto (Ok data) ->
model ! [ decryptData (Debug.log "data retrieved" { content = Just data, passphrase = model.passphrase }) ]
model ! [ decryptIfPassphrase model.passphrase (Just data) ]

DataRetrievedFromKinto (Err (Kinto.ServerError 404 _ error)) ->
update (UpdateContent model.loadedContent) model
Expand Down Expand Up @@ -376,7 +417,7 @@ formView model =
[ Html.Attributes.id "password"
, Html.Attributes.type_ "password"
, Html.Attributes.placeholder "Passphrase"
, Html.Attributes.value model.passphrase
, Html.Attributes.value (Maybe.withDefault "" model.passphrase)
, Html.Events.onInput NewPassphrase
]
[]
Expand Down Expand Up @@ -654,6 +695,12 @@ port saveData : { key : String, content : Encode.Value } -> Cmd msg
port dataSaved : (String -> msg) -> Sub msg


port savePassphrase : Maybe String -> Cmd msg


port dropPassphrase : {} -> Cmd msg



-- Decrypt data ports

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"publish-to-gh-pages": "npm run build && gh-pages --dist ./www",
"cordova-emulate-android": "npm run build && cordova emulate android",
"cordova-android": "npm run build && cordova run android",
"test": "npm run lint"
"test": "npm run lint",
"web-ext-sources": "rm -fr Hoverpad-Sources.zip && zip -r Hoverpad-Sources.zip Main.elm background.js elm-package.json encryption.js index.html Makefile manifest.json package.json ports.js README.md style.css icons/"
},
"repository": {
"type": "git",
Expand Down
80 changes: 59 additions & 21 deletions ports.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,33 @@ const CONTENT_KEY = "pad";

const IS_WEB_EXTENSION = (typeof chrome === "object" && typeof chrome.storage === "object");

function storePassphrase(passphrase) {
// Insecurely storing the passphrase.
sessionStorage.setItem("temporaryPassphrase", btoa(passphrase));
return Promise.resolve(null);
}

function dropPassphrase() {
sessionStorage.removeItem("temporaryPassphrase");
return Promise.resolve(null);
}

function getPassphrase() {
// Insecurely retrieving the passphrase.
const passphrase = sessionStorage.getItem("temporaryPassphrase");
if (passphrase === null) {
return null;
}
return Promise.resolve(atob(passphrase));
}

function getItem(key) {
if (!IS_WEB_EXTENSION) {
return Promise.resolve(localStorage.getItem(key));
} else {
return new Promise(function(resolve, reject) {
chrome.storage.local.get(key, function(data) {
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
} else {
resolve(data[key] || null);
}
resolve(data[key] || null);
});
});
}
Expand All @@ -34,11 +49,7 @@ function setItem(key, value) {
let payload = {};
payload[key] = value;
chrome.storage.local.set(payload, () => {
if (chrome.runtime.lastError) {
reject(chrome.runtime.lastError);
} else {
resolve(null);
}
resolve(null);
});
});
}
Expand Down Expand Up @@ -76,6 +87,28 @@ function createElmApp(flags) {
});
});

app.ports.savePassphrase.subscribe(function(passphrase) {
storePassphrase(passphrase)
.then(function() {
console.log("Passphrase saved");
})
.catch(function(err) {
console.error(err);
app.ports.newError.send('Could not save passphrase: ' + err.message);
});
});

app.ports.dropPassphrase.subscribe(function() {
dropPassphrase()
.then(function() {
console.log("Passphrase dropped");
})
.catch(function(err) {
console.error(err);
app.ports.newError.send('Could not drop passphrase: ' + err.message);
});
});

app.ports.decryptData.subscribe(function(data) {
if (!data.content) {
app.ports.dataDecrypted.send(null);
Expand Down Expand Up @@ -177,14 +210,19 @@ function handleMaybeInt(maybeString) {
return maybeInt;
}

Promise.all([getItem('lockAfterSeconds'), getItem('bearer'), getItem('contentWasSynced')])
.then(function(results) {
console.log("Flags", results);
const flags = {lockAfterSeconds: handleMaybeInt(results[0]),
fxaToken: results[1],
contentWasSyncedRemotely: results[2]};
createElmApp(flags);
})
.catch(function(err) {
console.error(err);
});
Promise.all([
getItem('lockAfterSeconds'),
getItem('bearer'),
getItem('contentWasSynced'),
getPassphrase()
]).then(function(results) {
const flags = {
lockAfterSeconds: handleMaybeInt(results[0]),
fxaToken: results[1],
contentWasSyncedRemotely: results[2],
passphrase: results[3]
};
createElmApp(flags);
}).catch(function(err) {
console.error(err);
});

0 comments on commit fe8dcff

Please sign in to comment.