# node:crypto is underused

In&nbsp;this post, I'll show you\* the basics of `node:crypto`, the builtin
Node.js cryptography module. I find it pretty cool, but not well known.

<aside>* and the forgetful future me.</aside>

I'm not saying everybody needs to be a security expert. I'm def not one! But I
feel that many engineers I know are needlessly scared of cryptography. With just
a tiny sprinkle of it, we can do amazing things: like keeping our users' data
private with encryption or signing stuff we give out to the public to guarantee
it hasn't been tampered with. It's been a while since I've written a
tutorial-style blog post, so here&nbsp;it goes.

<aside>

`node:` URL scheme was added in Node v12.20.0. The `crypto` package on NPM is
deprecated, so you won't ever import a malicious package here. Regardless, I'll
refer to it with a prefix to discern from capitalized `Crypto` Web API.

</aside>

export const codesandbox =
  "https://codesandbox.io/p/sandbox/frosty-bassi-lpry6c?file=%2Findex.mts%3A17%2C5&embed=1";

[codesandbox]:
  https://codesandbox.io/p/sandbox/frosty-bassi-lpry6c?file=%2Findex.mts%3A17%2C5&embed=1

<Callout>
  You can find all code snippets from this on{" "}
  <Link href={codesandbox}>CodeSandbox</Link>.
</Callout>

First things first, we need to generate a key pair.

<div>

```tsx twoslash include generating-keys
import { promisify } from "node:util";
import { generateKeyPair } from "node:crypto";

const { privateKey, publicKey } = await promisify(generateKeyPair)("rsa", {
  modulusLength: 2048,
});
```

```tsx twoslash
// @include: generating-keys
```

</div>

Easy, right? We've got ourselves an RSA public and private key.

[Rivest–Shamir–Adleman](<https://en.wikipedia.org/wiki/RSA_(cryptosystem)>)
public-key cryptosystem was discovered in the late 70s, just like hip-hop. You
need to now pause reading this, play the
["Rapper's Delight"](https://www.youtube.com/watch?v=mcCK99wHrk0) by The
Sugarhill Gang, while reading about RSA Security's
[relationship with NSA](https://en.wikipedia.org/wiki/RSA_Security#Relationship_with_NSA).

Now back to JavaScript.

Let's write and encode a short message, so we have something to work with.

```tsx twoslash include original-string
const originalString = "my important message";
const originalUint8Array = new Uint8Array(
  originalString.split("").map((ch) => ch.charCodeAt(0)),
);
```

```tsx twoslash
// @include: original-string
```

Now anybody can encrypt it with our public key, but only we can decrypt it.

```tsx twoslash
// @include: generating-keys
// @include: original-string
// ---cut---
import { publicEncrypt, privateDecrypt } from "node:crypto";

const encryptedBuffer = publicEncrypt(publicKey, originalUint8Array);
const decryptedBuffer = privateDecrypt(privateKey, encryptedBuffer);
const decryptedString = decryptedBuffer.toString();
```

The functions for signing and verifying have a bit of a peculiar API, where we
can pass `null` or `undefined` as the first argument if we already have our key
in a `KeyObject` instead of a raw string. I'm pretty used to
`JSON.stringify(object, null, 2)`, so I think I actually prefer it over
repeating the algorithm name.

```tsx twoslash
// @include: generating-keys
// @include: original-string
// ---cut---
import { sign, verify } from "node:crypto";

const signature = sign(null, originalUint8Array, privateKey);
const verified = verify(null, originalUint8Array, publicKey, signature);
```

If you're just interested in signing, you should probably use
[Ed25519](https://en.wikipedia.org/wiki/EdDSA#Ed25519). It's faster than RSA,
twisted Edwards curves sound like a band name, and GitHub [recommends
it][github-docs] for your SSH keys.

<aside>
  Britney Spears' ["I Wanna Go"](https://www.youtube.com/watch?v=T-sxSd1uwoU)
  topped the charts when Ed25519 was published. A lot of things went downhill
  since the 70s.
</aside>

```tsx twoslash
import { promisify } from "node:util";
import { generateKeyPair } from "node:crypto";
// ---cut---
const signingKeys = await promisify(generateKeyPair)("ed25519");
```

[github-docs]:
  https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent

Both RSA and Ed25519 can be used to sign and verify messages, but Ed25519 is
just a digital signature scheme, so you can't use it for encryption.

If you change `rsa` to `ed25519` in the [sandbox][codesandbox], one of the tests
will fail with a friendly message:

```_ class=code-error-message
Error: error:03000096:digital envelope routines::operation not supported for
this keytype
```

A bit annoying that it doesn't tell us about it on the type level, but without
redesigning the API with types in mind, the type error would also be a bit
nasty.

I remember thinking "Why shouldn't I just use RSA for everything and limit the
number of keys flying around?" sometime in the past. Apart from the performance,
and some possible security reasons, there's
[apparently](https://en.wikipedia.org/wiki/Digital_signature#Using_separate_key_pairs_for_signing_and_encryption)
[some](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf)
[legislation](https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=uriserv:OJ.L_.2014.257.01.0073.01.ENG)
stating that _digital signatures_ are legally binding. I probably wouldn't
interpret hashing a message and then raising the hash to the power of a number
that's kept in secret as _a signature_, but I am not a lawyer, and I am
unfortunately doomed to focus on the implementation.

import { CodesandboxIframe } from "../src/own/CodesandboxIframe";

<CodesandboxIframe client:only="solid" src={codesandbox} />

**Further Reading**

- [Introduction to Cryptography and RSA (Spring 2011, MIT OCW)](https://ocw.mit.edu/courses/6-045j-automata-computability-and-complexity-spring-2011/a58157daa3e96833038e8169b8978393_MIT6_045JS11_rsa.pdf)
- [Chosen-ciphertext attack](https://en.wikipedia.org/wiki/Chosen-ciphertext_attack)
- [Web Cryptography API Spec](https://w3c.github.io/webcrypto/#abstract)
- [How the NSA (may have) put a backdoor in RSA’s cryptography](https://blog.cloudflare.com/how-the-nsa-may-have-put-a-backdoor-in-rsas-cryptography-a-technical-primer/)
