Skip to content

Commit

Permalink
Implemented TOTP (#12)
Browse files Browse the repository at this point in the history
* totp

* Got totp working
  • Loading branch information
ilyakooo0 authored Aug 26, 2022
1 parent 5342153 commit 4f8e878
Show file tree
Hide file tree
Showing 12 changed files with 237 additions and 18 deletions.
9 changes: 8 additions & 1 deletion backend/BW.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { CipherCreateRequest } from "../../deps/bw/libs/common/src/models/reques
import { Cipher } from "../../deps/bw/libs/common/src/models/domain/cipher";
import { CipherData } from "../../deps/bw/libs/common/src/models/data/cipherData";
import { PasswordGenerationService } from "../../deps/bw/libs/common/src/services/passwordGeneration.service";
import { TotpService } from "../../deps/bw/libs/shared/dist/src/services/totp.service"

function sanitize(obj) {
return JSON.parse(JSON.stringify(obj));
Expand Down Expand Up @@ -84,7 +85,8 @@ export function getServices() {
},
crypto: bg.cryptoService,
cryptoFunctions: bg.cryptoFunctionService,
passwordGeneration: bg.passwordGenerationService
passwordGeneration: bg.passwordGenerationService,
totpService: bg.totpService,
}
}

Expand Down Expand Up @@ -175,6 +177,11 @@ class MainBackground {
this.policyService,
this.stateService
);
this.totpService = new TotpService(
this.cryptoFunctionService,
this.logService,
this.stateService
);
}

async bootstrap() {
Expand Down
6 changes: 6 additions & 0 deletions backend/BW.purs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ import Effect (Effect)
import Effect.Unsafe (unsafePerformEffect)
import Untagged.Union (type (|+|))

type TotpService
= { getCode :: String -> Promise String
, getTimeInterval :: String -> Int
}

type CryptoService
= { makeKey :: Fn4 Password String KDF Int (Promise SymmetricCryptoKey)
, hashPassword :: Fn3 Password SymmetricCryptoKey (JNullable HashPurpose) (Promise StringHash)
Expand Down Expand Up @@ -85,6 +90,7 @@ type Services
, getApi :: Urls -> JNullable IdentityTokenResponse -> Promise ApiService
, cryptoFunctions :: CryptoFunctions
, passwordGeneration :: PasswordGeneration
, totpService :: TotpService
}

foreign import getServices :: Effect Services
5 changes: 4 additions & 1 deletion backend/BW/Logic.purs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ encodeCipher (Bridge.FullCipher { name, cipher, id, favorite, reprompt }) = do
)
(unwrap login.uris)
username <- encryptNullable login.username
totp <- encryptNullable login.totp
pure
x
{ login =
Expand All @@ -264,7 +265,7 @@ encodeCipher (Bridge.FullCipher { name, cipher, id, favorite, reprompt }) = do
, uris: JOpt $ opt uris
, username
, passwordRevisionDate: jnull
, totp: jnull
, totp: totp
, autofillOnPageLoad: JOpt undefined
}
, type = cipherTypeLogin
Expand Down Expand Up @@ -326,12 +327,14 @@ decodeCipher cipher = do
Just login -> do
username <- decryptNullable login.username
password <- decryptNullable login.password
totp <- decryptNullable login.totp
uris <- fromJOpt [] <$> ((traverse >>> traverse) (_.uri >>> decrypt) login.uris)
pure $ Bridge.LoginCipher
$ Bridge.Cipher_LoginCipher
{ username
, password
, uris: wrap uris
, totp
}
n
| cipherTypeCard == n -> do
Expand Down
31 changes: 31 additions & 0 deletions backend/Bridge.purs
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,18 @@ derive instance genericCipher_LoginCipher_password_Maybe :: Generic Cipher_Login
derive instance eqCipher_LoginCipher_password_Maybe :: Eq Cipher_LoginCipher_password_Maybe
derive instance ordCipher_LoginCipher_password_Maybe :: Ord Cipher_LoginCipher_password_Maybe

newtype Cipher_LoginCipher_totp_Maybe =
Cipher_LoginCipher_totp_Maybe (Maybe String)

derive instance newtypeCipher_LoginCipher_totp_Maybe :: Newtype Cipher_LoginCipher_totp_Maybe _
instance encodeJsonCipher_LoginCipher_totp_Maybe :: EncodeJson Cipher_LoginCipher_totp_Maybe where
encodeJson = genericEncodeAeson Argonaut.defaultOptions
instance decodeJsonCipher_LoginCipher_totp_Maybe :: DecodeJson Cipher_LoginCipher_totp_Maybe where
decodeJson = genericDecodeAeson Argonaut.defaultOptions
derive instance genericCipher_LoginCipher_totp_Maybe :: Generic Cipher_LoginCipher_totp_Maybe _
derive instance eqCipher_LoginCipher_totp_Maybe :: Eq Cipher_LoginCipher_totp_Maybe
derive instance ordCipher_LoginCipher_totp_Maybe :: Ord Cipher_LoginCipher_totp_Maybe

newtype Cipher_LoginCipher_uris_List =
Cipher_LoginCipher_uris_List (Array String)

Expand Down Expand Up @@ -389,6 +401,7 @@ derive instance ordCipher_LoginCipher_username_Maybe :: Ord Cipher_LoginCipher_u
newtype Cipher_LoginCipher =
Cipher_LoginCipher {
password :: Cipher_LoginCipher_password_Maybe
, totp :: Cipher_LoginCipher_totp_Maybe
, uris :: Cipher_LoginCipher_uris_List
, username :: Cipher_LoginCipher_username_Maybe
}
Expand Down Expand Up @@ -458,6 +471,7 @@ data Cmd =
| NeedsReset
| Open String
| RequestCipher String
| RequestTotp String
| SendMasterPassword String
| UpdateCipher FullCipher

Expand Down Expand Up @@ -559,6 +573,22 @@ derive instance genericSub_NeedsMasterPassword :: Generic Sub_NeedsMasterPasswor
derive instance eqSub_NeedsMasterPassword :: Eq Sub_NeedsMasterPassword
derive instance ordSub_NeedsMasterPassword :: Ord Sub_NeedsMasterPassword

newtype Sub_Totp =
Sub_Totp {
code :: String
, interval :: Int
, source :: String
}

derive instance newtypeSub_Totp :: Newtype Sub_Totp _
instance encodeJsonSub_Totp :: EncodeJson Sub_Totp where
encodeJson = genericEncodeAeson Argonaut.defaultOptions
instance decodeJsonSub_Totp :: DecodeJson Sub_Totp where
decodeJson = genericDecodeAeson Argonaut.defaultOptions
derive instance genericSub_Totp :: Generic Sub_Totp _
derive instance eqSub_Totp :: Eq Sub_Totp
derive instance ordSub_Totp :: Ord Sub_Totp

data Sub =
CaptchaDone
| CipherChanged FullCipher
Expand All @@ -573,6 +603,7 @@ data Sub =
| NeedsMasterPassword Sub_NeedsMasterPassword
| RecieveEmail String
| Reset
| Totp Sub_Totp
| WrongPassword

instance encodeJsonSub :: EncodeJson Sub where
Expand Down
13 changes: 12 additions & 1 deletion backend/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -206,13 +206,24 @@ main = do
send $ Bridge.CipherChanged newCipher
performSync
pure unit
Bridge.DeleteCipher c@(Bridge.FullCipher { id, name }) ->
Bridge.DeleteCipher c@(Bridge.FullCipher { id }) ->
runWithDecryptionKey do
api <- getAuthedApi
liftPromise $ api.deleteCipher id
send $ Bridge.CipherDeleted c
performSync
pure unit
Bridge.RequestTotp totp ->
runWithDecryptionKey do
{ totpService } <- askAt (Proxy :: _ "services")
code <- liftPromise $ totpService.getCode totp
let
interval = totpService.getTimeInterval totp
send $ Bridge.Totp
$ Bridge.Sub_Totp
{ interval, code, source: totp
}
pure unit

processCipher ::
forall r.
Expand Down
7 changes: 7 additions & 0 deletions bridge.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ Sub:
- CipherChanged: FullCipher
- GeneratedPassword: String
- CipherDeleted: FullCipher
- Totp:
source: String
code: String
interval: Int

Cmd:
- Init
Expand All @@ -38,6 +42,7 @@ Cmd:
- CreateCipher: FullCipher
- GeneratePassword: PasswordGeneratorConfig
- DeleteCipher: FullCipher
- RequestTotp: String

FullCipher:
reprompt: Int
Expand All @@ -53,6 +58,8 @@ Cipher:
Maybe: String
password:
Maybe: String
totp:
Maybe: String
- NoteCipher: String
- CardCipher:
cardholderName:
Expand Down
45 changes: 44 additions & 1 deletion frontend/Bridge.elm
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,17 @@ jsonEncCipher_LoginCipher_password_Maybe val = (maybeEncode (Json.Encode.string



type alias Cipher_LoginCipher_totp_Maybe = (Maybe String)

jsonDecCipher_LoginCipher_totp_Maybe : Json.Decode.Decoder ( Cipher_LoginCipher_totp_Maybe )
jsonDecCipher_LoginCipher_totp_Maybe =
Json.Decode.maybe (Json.Decode.string)

jsonEncCipher_LoginCipher_totp_Maybe : Cipher_LoginCipher_totp_Maybe -> Value
jsonEncCipher_LoginCipher_totp_Maybe val = (maybeEncode (Json.Encode.string)) val



type alias Cipher_LoginCipher_uris_List = (List String)

jsonDecCipher_LoginCipher_uris_List : Json.Decode.Decoder ( Cipher_LoginCipher_uris_List )
Expand All @@ -407,21 +418,24 @@ jsonEncCipher_LoginCipher_username_Maybe val = (maybeEncode (Json.Encode.string

type alias Cipher_LoginCipher =
{ password: Cipher_LoginCipher_password_Maybe
, totp: Cipher_LoginCipher_totp_Maybe
, uris: Cipher_LoginCipher_uris_List
, username: Cipher_LoginCipher_username_Maybe
}

jsonDecCipher_LoginCipher : Json.Decode.Decoder ( Cipher_LoginCipher )
jsonDecCipher_LoginCipher =
Json.Decode.succeed (\ppassword puris pusername -> {password = ppassword, uris = puris, username = pusername})
Json.Decode.succeed (\ppassword ptotp puris pusername -> {password = ppassword, totp = ptotp, uris = puris, username = pusername})
|> required "password" (jsonDecCipher_LoginCipher_password_Maybe)
|> required "totp" (jsonDecCipher_LoginCipher_totp_Maybe)
|> required "uris" (jsonDecCipher_LoginCipher_uris_List)
|> required "username" (jsonDecCipher_LoginCipher_username_Maybe)

jsonEncCipher_LoginCipher : Cipher_LoginCipher -> Value
jsonEncCipher_LoginCipher val =
Json.Encode.object
[ ("password", jsonEncCipher_LoginCipher_password_Maybe val.password)
, ("totp", jsonEncCipher_LoginCipher_totp_Maybe val.totp)
, ("uris", jsonEncCipher_LoginCipher_uris_List val.uris)
, ("username", jsonEncCipher_LoginCipher_username_Maybe val.username)
]
Expand Down Expand Up @@ -512,6 +526,7 @@ type Cmd =
| NeedsReset
| Open String
| RequestCipher String
| RequestTotp String
| SendMasterPassword String
| UpdateCipher FullCipher

Expand All @@ -529,6 +544,7 @@ jsonDecCmd =
, ("NeedsReset", Json.Decode.lazy (\_ -> Json.Decode.succeed NeedsReset))
, ("Open", Json.Decode.lazy (\_ -> Json.Decode.map Open (Json.Decode.string)))
, ("RequestCipher", Json.Decode.lazy (\_ -> Json.Decode.map RequestCipher (Json.Decode.string)))
, ("RequestTotp", Json.Decode.lazy (\_ -> Json.Decode.map RequestTotp (Json.Decode.string)))
, ("SendMasterPassword", Json.Decode.lazy (\_ -> Json.Decode.map SendMasterPassword (Json.Decode.string)))
, ("UpdateCipher", Json.Decode.lazy (\_ -> Json.Decode.map UpdateCipher (jsonDecFullCipher)))
]
Expand All @@ -549,6 +565,7 @@ jsonEncCmd val =
NeedsReset -> ("NeedsReset", encodeValue (Json.Encode.list identity []))
Open v1 -> ("Open", encodeValue (Json.Encode.string v1))
RequestCipher v1 -> ("RequestCipher", encodeValue (Json.Encode.string v1))
RequestTotp v1 -> ("RequestTotp", encodeValue (Json.Encode.string v1))
SendMasterPassword v1 -> ("SendMasterPassword", encodeValue (Json.Encode.string v1))
UpdateCipher v1 -> ("UpdateCipher", encodeValue (jsonEncFullCipher v1))
in encodeSumTaggedObject "tag" "contents" keyval val
Expand Down Expand Up @@ -700,6 +717,29 @@ jsonEncSub_NeedsMasterPassword val =



type alias Sub_Totp =
{ code: String
, interval: Int
, source: String
}

jsonDecSub_Totp : Json.Decode.Decoder ( Sub_Totp )
jsonDecSub_Totp =
Json.Decode.succeed (\pcode pinterval psource -> {code = pcode, interval = pinterval, source = psource})
|> required "code" (Json.Decode.string)
|> required "interval" (Json.Decode.int)
|> required "source" (Json.Decode.string)

jsonEncSub_Totp : Sub_Totp -> Value
jsonEncSub_Totp val =
Json.Encode.object
[ ("code", Json.Encode.string val.code)
, ("interval", Json.Encode.int val.interval)
, ("source", Json.Encode.string val.source)
]



type Sub =
CaptchaDone
| CipherChanged FullCipher
Expand All @@ -714,6 +754,7 @@ type Sub =
| NeedsMasterPassword Sub_NeedsMasterPassword
| RecieveEmail String
| Reset
| Totp Sub_Totp
| WrongPassword

jsonDecSub : Json.Decode.Decoder ( Sub )
Expand All @@ -732,6 +773,7 @@ jsonDecSub =
, ("NeedsMasterPassword", Json.Decode.lazy (\_ -> Json.Decode.map NeedsMasterPassword (jsonDecSub_NeedsMasterPassword)))
, ("RecieveEmail", Json.Decode.lazy (\_ -> Json.Decode.map RecieveEmail (Json.Decode.string)))
, ("Reset", Json.Decode.lazy (\_ -> Json.Decode.succeed Reset))
, ("Totp", Json.Decode.lazy (\_ -> Json.Decode.map Totp (jsonDecSub_Totp)))
, ("WrongPassword", Json.Decode.lazy (\_ -> Json.Decode.succeed WrongPassword))
]
jsonDecObjectSetSub = Set.fromList []
Expand All @@ -753,6 +795,7 @@ jsonEncSub val =
NeedsMasterPassword v1 -> ("NeedsMasterPassword", encodeValue (jsonEncSub_NeedsMasterPassword v1))
RecieveEmail v1 -> ("RecieveEmail", encodeValue (Json.Encode.string v1))
Reset -> ("Reset", encodeValue (Json.Encode.list identity []))
Totp v1 -> ("Totp", encodeValue (jsonEncSub_Totp v1))
WrongPassword -> ("WrongPassword", encodeValue (Json.Encode.list identity []))
in encodeSumTaggedObject "tag" "contents" keyval val

1 change: 1 addition & 0 deletions frontend/GlobalEvents.elm
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ import Bridge
type Event
= UpdateCipher Bridge.FullCipher
| GeneratedPassword String
| DecodedTotp Bridge.Sub_Totp
3 changes: 2 additions & 1 deletion frontend/Logic/Cipher.elm
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,10 @@ normalizeIdentityCipher { address1, address2, address3, city, company, country,


normalizeLoginCipher : Bridge.Cipher_LoginCipher -> Bridge.Cipher_LoginCipher
normalizeLoginCipher { password, uris, username } =
normalizeLoginCipher { password, totp, uris, username } =
{ password = password
, uris = uris |> List.map String.trim |> List.filter String.isEmpty
, totp = normalizeStringMaybe totp
, username = username
}

Expand Down
Loading

0 comments on commit 4f8e878

Please sign in to comment.