Skip to content

Commit

Permalink
feat(KDFs): add HKDF algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
aykxt committed Mar 25, 2021
1 parent f91253c commit d6260ca
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 17 deletions.
59 changes: 44 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,65 @@
# Crypto
# 🔐 Crypto

![ci](https://github.com/aykxt/crypto/workflows/ci/badge.svg)

A collection of useful crypto algorithms written in Typescript.
A collection of useful cryptographic algorithms written in Typescript.

---

> ⚠ This project is still in an early stage of development. Expect **breaking
> changes**.
---

## Supported algorithms

### Block ciphers
### [Block ciphers]

- [AES] (Rijndael)
- [Blowfish]
- ECB, CBC, CFB, OFB and CTR [block modes]

### [Message Authentication Code] algorithms (MACs)

- [HMAC]

### [Key Derivation Functions] (KDFs)

- AES (Advanced Encryption Standard)
- Blowfish
- ECB, CBC, CFB, OFB and CTR block modes
- [HKDF]

## Examples

#### AES-128-CBC

```ts
import { Aes } from "https://deno.land/x/crypto/aes.ts";
import { Ecb } from "https://deno.land/x/crypto/block-modes.ts";
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
import { Cbc, Padding } from "https://deno.land/x/crypto/block-modes.ts";

//deno-fmt-ignore
const key = new Uint8Array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]);
const te = new TextEncoder();

const cipher = new Ecb(Aes, key);
const key = te.encode("SuperDuperSecret");
const data = te.encode("DataToBeEncrypted");
const iv = new Uint8Array(16);

//deno-fmt-ignore
const data = new Uint8Array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]);
// Ciphers have an internal state, you should therefore create
// separate ciphers for encryption and decryption
const cipher = new Cbc(Aes, key, iv, Padding.PKCS7);
const decipher = new Cbc(Aes, key, iv, Padding.PKCS7);

const encrypted = cipher.encrypt(data);

assertEquals(cipher.decrypt(encrypted), data);
const decrypted = decipher.decrypt(encrypted);
```

### Disclaimer

This repository has not yet received any formal cryptographic and security
reviews. **USE AT YOUR OWN RISK**

[Block ciphers]: https://en.wikipedia.org/wiki/Block_cipher
[block modes]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
[AES]: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
[Blowfish]: https://en.wikipedia.org/wiki/Blowfish_(cipher)
[Message Authentication Code]: https://en.wikipedia.org/wiki/Message_authentication_code
[HMAC]: https://en.wikipedia.org/wiki/HMAC
[Key Derivation Functions]: https://en.wikipedia.org/wiki/Key_derivation_function
[HKDF]: https://en.wikipedia.org/wiki/HKDF
5 changes: 4 additions & 1 deletion dev_deps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@ export {
assertThrows,
} from "https://deno.land/std@0.91.0/testing/asserts.ts";
export { parse as parseArgs } from "https://deno.land/std@0.91.0/flags/mod.ts";
export { encodeToString as encodeToHex } from "https://deno.land/x/std@0.91.0/encoding/hex.ts";
export {
decodeString as decodeHex,
encodeToString as encodeToHex,
} from "https://deno.land/x/std@0.91.0/encoding/hex.ts";
2 changes: 2 additions & 0 deletions hkdf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { hkdf } from "./src/hkdf/mod.ts";
export type { SupportedAlgorithm } from "./src/hkdf/mod.ts";
37 changes: 37 additions & 0 deletions src/hkdf/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { hmac, outputSizes } from "../hmac/mod.ts";
import type { SupportedAlgorithm } from "../hmac/mod.ts";

export type { SupportedAlgorithm };

/**
* RFC 5869 HMAC-based Key Derivation Function (HKDF)
*/
export function hkdf(
hash: SupportedAlgorithm,
length: number,
ikm: Uint8Array,
salt?: Uint8Array,
info?: Uint8Array,
) {
const hashLen = outputSizes[hash];

if (!salt) salt = new Uint8Array(hashLen);
if (!info) info = new Uint8Array();

const prk = hmac(hash, salt, ikm);

let t = new Uint8Array();
const nb = Math.ceil(length / hashLen);
const okm = new Uint8Array(nb * hashLen);

for (let i = 0; i < nb; i++) {
const concat = new Uint8Array(t.length + info.length + 1);
concat.set(t);
concat.set(info, t.length);
concat.set([i + 1], t.length + info.length);
t = hmac(hash, prk, concat);
okm.set(t, hashLen * i);
}

return okm.slice(0, length);
}
26 changes: 25 additions & 1 deletion src/hmac/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createHash, SupportedAlgorithm } from "../../deps.ts";

export type { SupportedAlgorithm } from "../../deps.ts";

const blockSizes: Record<SupportedAlgorithm, number> = {
export const blockSizes: Record<SupportedAlgorithm, number> = {
"sha3-512": 72,
"sha3-384": 48,
"sha3-256": 136,
Expand All @@ -23,6 +23,30 @@ const blockSizes: Record<SupportedAlgorithm, number> = {
keccak224: 144,
};

export const outputSizes: Record<SupportedAlgorithm, number> = {
"sha3-512": 64,
"sha3-384": 48,
"sha3-256": 32,
"sha3-224": 28,
sha512: 64,
sha384: 48,
sha256: 32,
sha224: 28,
sha1: 20,
md5: 16,
md4: 16,
md2: 16,
ripemd320: 40,
ripemd160: 20,
keccak512: 64,
keccak384: 48,
keccak256: 32,
keccak224: 28,
};

/**
* RFC 2104 Keyed Hash Message Authentication Code (HMAC)
*/
export function hmac(
hash: SupportedAlgorithm,
key: Uint8Array,
Expand Down
18 changes: 18 additions & 0 deletions tests/hkdf.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { hkdf } from "../hkdf.ts";
import { assertEquals, decodeHex, encodeToHex } from "../dev_deps.ts";

Deno.test("HKDF-SHA256", () => {
// https://tools.ietf.org/html/rfc5869#appendix-A.1

const ikm = decodeHex("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b");
const salt = decodeHex("000102030405060708090a0b0c");
const info = decodeHex("f0f1f2f3f4f5f6f7f8f9");
const len = 42;

const okm = hkdf("sha256", len, ikm, salt, info);

const expected =
"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865";

assertEquals(encodeToHex(okm), expected);
});

0 comments on commit d6260ca

Please sign in to comment.