Last active
July 4, 2022 18:40
-
-
Save overheadhunter/93a7da2ae51fa41cef3205223089e45e to your computer and use it in GitHub Desktop.
TypeScript Key Derivation Functions ANSI-X9.63 and NIST SP 800-56A Rev. 2 ConcatKDF
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* KDF as defined in <a href="https://doi.org/10.6028/NIST.SP.800-56Ar2">NIST SP 800-56A Rev. 2 Section 5.8.1</a> using SHA-256 | |
* | |
* @param z A shared secret | |
* @param keyDataLen Desired key length (in bytes) | |
* @param otherInfo Concatenated form of AlgorithmID || PartyUInfo || PartyVInfo {|| SuppPubInfo }{|| SuppPrivInfo } | |
* @returns key data | |
*/ | |
public static async concatKDF(z: Uint8Array, keyDataLen: number, otherInfo: Uint8Array): Promise<Uint8Array> { | |
const hashLen = 32; // output length of SHA-256 | |
const reps = Math.ceil(keyDataLen / hashLen); | |
if (reps >= 0xFFFFFFFF) { | |
throw new Error('unsupported keyDataLen'); | |
} | |
if (4 + z.byteLength + otherInfo.byteLength > 0xFFFFFFFF) { | |
// technically max hash length for sha256 is 2^64-1 bits, but JS doesn't allow (safe) 64 bit numbers | |
// it is safe to restrict this kdf to smaller input lengths | |
throw new Error('unsupported input length'); | |
} | |
const key = new Uint8Array(reps * hashLen); | |
const tmp = new ArrayBuffer(4 + z.byteLength + otherInfo.byteLength); | |
for (let i = 0; i < reps; i++) { | |
new DataView(tmp, 0, 4).setUint32(0, i + 1, false); | |
new Uint8Array(tmp).set(z, 4); | |
new Uint8Array(tmp).set(otherInfo, 4 + z.byteLength); | |
const digest = await crypto.subtle.digest('SHA-256', tmp); | |
key.set(new Uint8Array(digest), i * hashLen); | |
} | |
return key.slice(0, keyDataLen); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Performs <a href="https://www.secg.org/sec1-v2.pdf">ANSI-X9.63-KDF</a> with SHA-256 | |
* @param sharedSecret A shared secret | |
* @param sharedInfo Additional authenticated data | |
* @param keyDataLen Desired key length (in bytes) | |
* @return key data | |
*/ | |
public static async x963KDF(sharedSecret: Uint8Array, sharedInfo: Uint8Array, keyDataLen: number): Promise<Uint8Array> { | |
const hashLen = 32; | |
const n = Math.ceil(keyDataLen / hashLen); | |
const buffer = new Uint8Array(n * hashLen); | |
const tmp = new ArrayBuffer(sharedSecret.byteLength + 4 + sharedInfo.byteLength); | |
for (let i = 0; i < n; i++) { | |
new Uint8Array(tmp).set(sharedSecret, 0); | |
new DataView(tmp, sharedSecret.byteLength, 4).setUint32(0, i + 1, false); | |
new Uint8Array(tmp).set(sharedInfo, sharedSecret.byteLength + 4); | |
const digest = await crypto.subtle.digest('SHA-256', tmp); | |
buffer.set(new Uint8Array(digest), i * hashLen); | |
} | |
return buffer.slice(0, keyDataLen); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment