A few months ago I decided to finally start using a password manager. Partly out of wanting to make life more convenient and partly because of a particularly long chain of mass password leaks that had happened that month.

So I set out to look for good password manager. I wasn’t thrilled with all of the options out there. The open-source options out there tend to have poor cross-platform support, often requiring unofficial clients for browser, and non-linux support. And the two commercial offerings I was familiar with, don’t offer lifetime licences and instead ask for a monthly subscription rate. Blech.

So I decided to write my own.

If any security minded folks are reading this and shuddering at the prospect, never fear! While this wound up being an interesting project, I ultimately ended up using a commercial password manager.

Portability was an explicit goal of this enterprise, and I enjoy writing Scala, so that’s the platform I decided upon.

This post won’t talk much about security. I used the standard, built-in password based encryption that ships with the JDK. Rather, this will be about some new (at least to me) Scala-isms.

1. Writing a custom DSL to reduce JavaFX boilerplate

Before this, the only UI programming I had done is a JVM language was toy programs in Swing for school. JavaFX, as best as I can tell, is an evolution of Swing with better APIs. But even with it’s better APIs, it still retains the big awkwardness of the Swing APIs.

You can find yourself writng code that looks like:

Text t = new Text();
t.setText("hello")
t.setFontSize(14);

So the first thing I did was write some implicit classes to at least reduce the amount of typing to:

text("hello") withFontSize (14)

The accompanying library code looks something like this:


// in FxUtils.scala
trait Fontable[T] {
  def withFontSize(i: Double): T
}

implicit class FontableText(txt: Text) extends Fontable[Text] {
  def withFontSize(i: Double): Text = {
    txt.setFont(Font.font(i))
      txt
  }
}

def text(label: String): Text = {
  new Text(label)
}

You can attach event handlers in the same way. So this fairly verbose code in Java 8:

Button p = new Button();
p.setText("click me")
p.addEventHandler((e) -> /* do something */);

Became:

button("click on") onClick( () => /* do something */ )

But the most bang-for-my buck extension was with layout code.

Laying out a Grid in code, using the Java API looks something like this:

Grid g = new Grid();
g.add(0, 0, topLeftComponent);
g.add(0, 1, bottomLeftComponent);
g.add(1, 0, topRightComponent);
g.add(1, 1, bottomRightComponent);

And with my DSL it became:

layoutRows(
  Seq(topLeftComponent, topRightComponent),
  Seq(bottomLeftComponent, bottomRightComponent),
)

I found that it became easier to manipulate the UI in code when I was describing the UI in this more declarative manner. Since everything was an expression, and each view was subsequently one large nested expression, the compiler would prevent me from creating a widget and forgetting to hook it into anything. There was also less opportunity for sloppy copy-pasting that would make me attach an event handler to the wrong widget.

2. Rolling a custom monad for domain specific logic

As I made progress, I found that I was writing lots of repetitive code to deal with the same few error conditions.

At first I tried using the Scala built-ins that are touted as replacements for Java-style exceptions. Optionals threw away too much information, and I found that I wound up writing lots of code and rolling lots of bespoke error types to get Either to work – I needed the left-hand side to be a rich datatype to describe the error.

This led me to write my own Monadic container for data. Each UI activity invariably wound up touching a password-encrypted “vault” file, so I wound up calling this type Secure[T] – I’m not crazy about the name though.

Secure[T] is capable of describing several error states — a corrupted vault-file was found, some IO failed, or password verification failed.

Let’s take a simple example. Say I modelled my data like this:

type VaultContents = Set[VaultEntry]

case class VaultEntry(label: String, username: string, password: string)

I can write functions that are meant to be called from a view with the following signatures:

/**
 * finds an entry, if it exists, in the vault file
 */
def findEntry(vault: VaultContents, label: String): Optional[VaultEntry]

/**
 * Finds any entries where passwords are used twice.
 */
def findDuplicatePasswords(vault: VaultContents): Set[VaultEntry]

/**
 * overwrites or appends a new entry to the contents of the vault
 */
def addEntry(vault: VaultContents, entry: VaultEntry): VaultContents

I also need functions to read and write the vault to disk:

/**
 * Reads the contents of `location` and decrypts using the master password.
 */
def readContents(masterPassword: String ,location: File): Secure[VaultContents]

/**
 * Writes the vault to a given location, encrypting with master password
 */
def writeContents(
  masterPassword: String, 
  location: File,
  contents: VaultContents): Secure[Unit]

At every callsite, we need to handle the possibilies I listed above – bad password, corrupted file, etc.

In Java land, we would use checked exceptions. In Scala, we can call those functions in a for-comprehension to make sure those errors get propagated. For example, here’s a function that reads the vault file, finds the password for a given labl, and copies it to the users clipboard.

def copyPassword(label: String, masterPassword: String) = {
  val result : Secure[Optional[String]] = for {
    contents <-   readContents(masterPassword, vaultLocation) // returns Secure[VaultContents]
    entry = findEntry(contents)
  } yield entry.map(_.password)

  result match {
    case Ok(Some(entry)) =>
      copyEntryToClipboard(entry)
    case Ok(None) =>
      ui.errorMessage("password does not exist!")
    case BadPassword =>
      ui.errorMessage("bad vault password")
    case CorruptedFile(error) =>
      ui.errorMessage("vault file was corrupted: " + e)
    }
}

This wound up being a pretty nice way of passing around error states. This let me write lots of code paths that did some password-verified vault operation low in the call stack, and plenty of application logic further up the stack. The application logic was largely written to be unaware of Secure[T], and for comprehensions handled threading any possible failures all the way up my controllers, where I was calling these functions.