Categories
Explainer Technical

ECDSA signatures with node.js and Swift

In the process of de-risking some business model questions for Digamo, I went looking for an open source project that would allow us to generate and verify license keys. We’re not sure what our business model will be there, but understanding the technical costs of licensing is important to making decisions.

While this is a problem that has been solved, I didn’t find anything especially modern, well-maintained, or documented—and nothing in Swift or Node, which I’m using for everything right now.

Some research did suggest that asymmetric cryptography was the way to go, using a private key to sign a registration string, and a public key distributed with the app to verify the signature. This was also the approach used in the older projects that tried to solve this problem for Mac apps.

Still, I found precious little documentation or tutorials to walk me through the implementation. All the signing examples took place within the same context, rather than between client and server. Here’s an approach that appears to be working, using Node and a Swift Mac app.

Try the demo

Generate a signed message file here: https://eccsign-server.glitch.me

Download, build and run the macOS verification app here: https://github.com/daniloc/eccsignaturetest

Once the app is running, you can double-click the signed message files from the Glitch app to automatically load them. File free to tamper with the content of the plaintext field by opening a file in your favorite text editor, then try to verify again. Tampering should cause the verification step to fail.

The public and private keys are included for your inspection and experimentation, as a “known-good” configuration. In your own projects you should use care to ensure your private key is not distributed.

Generate encryption keys

Per the instructions from IBM’s BlueECC project, here’s how you get started:

On macOS you can install OpenSSL using brew:

brew install openssl

Once you have installed OpenSSL, create your private key:

openssl ecparam -name prime256v1 -genkey -noout -out ec256priv.pem

Using the private key, create your public key:

openssl ec -in ec256priv.pem -pubout -out ec256pub.pem

It seems you want to use the prime256v1 curve—other algorithms have some cross-platform issues.

This will leave you with a private and public key. The private key goes on your server to generate signatures. The public key can be distributed with your app to verify those signatures.

Signing with Node.js

The npm module EC-Key will make it easy to load your key:

let ECKey = require("ec-key");

let pem = fs.readFileSync("./privatekey.pem");
let eccPrivateKey = new ECKey(pem, "pem")

Here my implementation gets a little clunky—there may be better ways to do this, but at least it seems pretty flexible. Create a JavaScript object with whatever keys and content you want:

var objectToSign = {}
objectToSign.message = message
let date = new Date().toJSON()
objectToSign.date = date

Convert that into a JSON string:

let outputString = JSON.stringify(objectToSign)

Then, create a signature from that string:

let signature = eccPrivateKey.createSign("SHA256").update(outputString).sign("base64")

And pack the plaintext string and signature into a second object:

let outputObject = {}
outputObject.plaintext = outputString
outputObject.signatureBase64 = signature

You can then convert the output to JSON and have the user download the file.

See the whole thing in action with this Glitch project.

Verification in Swift

Add the BlueECC package to your project. Install manually, or use the Swift Package Manager. In Xcode, choose File > Swift Packages > Add Package Dependency…

Search for “CryptorECC,” and pick “BlueECC.”

Add your public key file to your project, and import CryptorECC
to the file where you’re working. Then you can grab the public key like this:

let filepath = Bundle.main.path(forResource: "ec256pub", ofType: "pem")!

let keyString = try? String(contentsOfFile: filepath)

let publicKey = try ECPublicKey(key: keyString)

With the public key loaded from your bundle into memory, you can now verify a signature with it:

let signature = try ECSignature(asn1: Data(base64Encoded: signatureBase64)!)

(signatureBase64 is the output from createSign() above)

let verified = signature.verify(plaintext: plaintext, using: publicKey)

The constant verified will tell you if the plaintext and signature match.

Here’s a Mac app you can build and run to see this in action.

Feedback

Is this a good approach? See anywhere it could work better? Drop me a line: danilo@futurefluent.com.

Categories
Explainer Technical

How to Ship a Software Project

I’ve spent the last 14 years building software, off and on. Over that period I’ve been on the hook for dozens of ship dates. Some I missed. Many I hit.

Through all those adventures, I’ve settled on a basic process I think anyone can use to get from deep-in-the-weeds to a triumphant ship. None of this is especially innovative. Plenty of people likely ship using some variation of this exact procedure. But I wanted to write it up in case it was useful.

1. Pick a ship date—but don’t get attached to it

Start by choosing a date on which it would be nice to have shipped your project. Any criteria is valid here. Business needs, deadlines imposed by programs to which you want to apply, even press strategy.

But don’t get too attached to this ship date. You have no idea if you can make it. At this point it’s an experimental constraint, which will guide the rest of your planning process.

2. Determine the minimally viable feature set your project needs for a successful ship

What does the audience of your project need to be better off than if you’d never shipped the project at all? What is the simplest feature set you can deliver to make an impact?

by Henrik Crisp

You’re reading this because, presumably, you would like to ship. So it’s essential you understand that you can’t immediately ship everything your mind imagines. You can’t ship perfection. Perfection and shipping hate one another, so you have to pick one. I suggest picking ship, but that’s up to you.

Shipping requires hard choices. The more things you try to bundle into a shipping project, the more risk you incur. The risk multiplies invisibly, as it’s impossible to know ahead of time about complex interactions that produce bugs, reduce performance, or otherwise introduce surprises.

The fewer things you try to ship at once, the less risk you incur. So when you make the list of things you want to see in your first ship, do everything you can to keep it as short as possible.

3. List all of the dependencies that must be satisfied for your minimally viable feature set

What needs to exist for these features to work? How much already exists? Get your mind around all the dependent tasks, from things that need to be built, to things that must be bought, to things that must be designed, in order for these features to be ready for public consumption. With luck, you’ve already got some of these dependencies handled.

Can you license or purchase things instead of building them yourself? Can you leverage open source software rather than rolling your own solutions? Think through how many off-the-shelf opportunities exist to satisfy your dependencies. But also remember there’s no free lunch: integrating outside resources can still be costly.

4. Estimate how much time it will take to satisfy your dependencies—then multiply

Software development time estimates are comically error-prone. But if you price the estimation risk into your estimate, you can earn yourself some breathing room. There will be bugs that keep coming back. There will be surprises as you integrate third-party tools and content. There will be tasks that seemed easy at the outset, but whose implementation reveals a raft of additional complexity.

That’s life in the software business.

As you create an inventory of your dependencies and the time you estimate they’ll need to cook, multiply by 2.5.

Maybe that seems excessive. But it’s a concession to the fact that our frail meat brains are bad at storing and modeling the total complexity of a software system. When you made your initial guess, you were quite possibly ignoring more than half the picture. Price that limited perspective into your final guess.

Or don’t. It’s your ship. You can use whatever multiple you feel comfortable with here. Long experience tells me that for my estimates, though, I usually want 2.5x.

Don’t forget time for testing your project ahead of ship. I can’t tell you how long this will take, since different environments have different constraints. If you’re shipping native code for approval in the iOS App Store, for example, you’ll want to do much more thorough testing than if you’re shipping a web app you can update at-will.

You’ll also want to budget time to make fixes based on what you learn in the QA process.

5. Add up your time estimates and compare the timeline to your ship date

If you’re very lucky, your time estimates and your ship date mesh perfectly.

But you’re probably not lucky. You’re working with software, and software delights in thwarting our optimism. What if your requirements push far past the ship date?

You’re going to be tempted to manipulate the estimates. Don’t do this. There’s a reason we estimate in pieces and add things up, instead of working backwards from the ship date.

In this process, here are all the things we’ve considered in order from most real to least real:

  1. The dependencies for our desired features
  2. The minimally viable feature set
  3. The time estimates
  4. The ship date

The ship date is the least real thing we’re dealing with! Don’t let it drive.

That said, if you absolutely must treat the ship date as a hard constraint, you have options. None of them include goosing the estimates.

6. Make hard choices: trim scope or move the ship date

I know. You want a pony.

🗓💰

But now you know what the pony costs.

You’re going to ship the pony 18 months later than you would have liked. Are you going to have enough money to survive for that extra 18 months? Will you give up an important competitive advantage by taking that extra 18 months? Are you OK waiting 18 extra months before getting feedback from customers about whether you’re even on the right track?

Maybe you’re fine moving the ship date. Sometimes that’s the right call.

But often, this is the point in the exercise where you take out your knife and start trimming scope. Maybe you’re not going to ship a pony. Maybe it’s time to ship a bicycle instead. With the knowledge of the time and financial cost of your minimally viable feature set, maybe you can find inspiration to pare down even more.

Trim scope until your estimates for the remaining work—which you have not goosed—give you a few days of margin under your ship date.

Now you can get to work, having a clear idea of what’s worth prioritizing and what can wait. You can repeat this process whenever you feel your project getting off track or losing steam. It helps me to track my requirements and dependencies on something tactile, like notecards or sticky notes, but you can document the process you’ve gone through using whatever systems make sense for you and your team.

But, Danilo, this isn’t the agile/scrum/[cargo cult methodology] way!

Whatever, man. I’m sure it isn’t. This is the way I know to get a project out the door on a date agreed upon by all parties, with scope documented for all parties, with trade-offs understood by all parties.

Within that, I’ve seen it work alongside tools like sprints, planning meetings, stand-ups, and retrospectives. I really like doing all of those. If you have existing systems you like to use to communicate and collaborate, you can keep them.

But you need a way to understand the relationship between costs, scope and outcomes, and to communicate those constraints across all functions. This is the basic approach I’ve long used for that. I hope it’s helpful in your adventures.


Further reading:

1.0 by Rands: “Shipping a 1.0 product isn’t going to kill you, but it will try.”