Skip to content

Commit

Permalink
Implement crypto.pbkdf2
Browse files Browse the repository at this point in the history
  • Loading branch information
fhanau committed May 8, 2023
1 parent f8f14f3 commit 120be04
Show file tree
Hide file tree
Showing 7 changed files with 478 additions and 2 deletions.
16 changes: 14 additions & 2 deletions src/node/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ import {
checkPrimeSync,
} from 'node-internal:crypto_random';

import {
pbkdf2,
pbkdf2Sync,
ArrayLike,
} from 'node-internal:crypto_pbkdf2';

export {
randomBytes,
randomFillSync,
Expand All @@ -47,6 +53,9 @@ export {
generatePrimeSync,
checkPrime,
checkPrimeSync,
pbkdf2,
pbkdf2Sync,
ArrayLike as arrayLike,
}

// We do not implement the openssl secure heap.
Expand Down Expand Up @@ -85,6 +94,9 @@ export default {
generatePrimeSync,
checkPrime,
checkPrimeSync,
// Pbkdf2
pbkdf2,
pbkdf2Sync,
// Misc
secureHeapUsed,
setEngine,
Expand Down Expand Up @@ -173,8 +185,8 @@ export default {
// * Key Derivation
// * [ ] crypto.hkdf(digest, ikm, salt, info, keylen, callback)
// * [ ] crypto.hkdfSync(digest, ikm, salt, info, keylen)
// * [ ] crypto.pbkdf2(password, salt, iterations, keylen, digest, callback)
// * [ ] crypto.pbkdf2Sync(password, salt, iterations, keylen, digest)
// * [x] crypto.pbkdf2(password, salt, iterations, keylen, digest, callback)
// * [x] crypto.pbkdf2Sync(password, salt, iterations, keylen, digest)
// * [ ] crypto.scrypt(password, salt, keylen[, options], callback)
// * [ ] crypto.scryptSync(password, salt, keylen[, options])
// * WebCrypto
Expand Down
8 changes: 8 additions & 0 deletions src/node/internal/crypto.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
// Copyright (c) 2017-2022 Cloudflare, Inc.
// Licensed under the Apache 2.0 license found in the LICENSE file or at:
// https://opensource.org/licenses/Apache-2.0
import {
Buffer,
} from 'node-internal:internal_buffer';

// random
export function getRandomInt(min: number, max: number): number;
export function checkPrimeSync(candidate: ArrayBufferView, num_checks: number): boolean;
export function randomPrime(size: number, safe: boolean, add?: ArrayBufferView|undefined, rem?: ArrayBufferView|undefined): ArrayBuffer;

// pbkdf2
export type ArrayLike = ArrayBuffer|SharedArrayBuffer|string|Buffer|DataView;
export function getPbkdf(password: ArrayLike, salt: ArrayLike, iterations: number, keylen: number, digest: string): ArrayBuffer;
93 changes: 93 additions & 0 deletions src/node/internal/crypto_pbkdf2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright (c) 2017-2022 Cloudflare, Inc.
// Licensed under the Apache 2.0 license found in the LICENSE file or at:
// https://opensource.org/licenses/Apache-2.0
//
// Adapted from Deno and Node.js:
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
//
// Adapted from Node.js. Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

/* todo: the following is adopted code, enabling linting one day */
/* eslint-disable */

'use strict';

import { default as cryptoImpl } from 'node-internal:crypto';
type ArrayLike = cryptoImpl.ArrayLike;
export {ArrayLike};

import {
Buffer,
} from 'node-internal:internal_buffer';

import {
validateInt32,
validateFunction,
validateString,
} from 'node-internal:validators';

import {
getArrayBufferOrView,
} from 'node-internal:crypto_util';

export function pbkdf2Sync(password: ArrayLike, salt: ArrayLike, iterations: number,
keylen: number, digest: string): Buffer {
({ password, salt, iterations, keylen, digest } =
check(password, salt, iterations, keylen, digest));

const result = cryptoImpl.getPbkdf(password, salt, iterations, keylen, digest);
return Buffer.from(result);
}

export type Pbkdf2Callback = (err?: Error|null, result?: Buffer) => void;
export function pbkdf2(password: ArrayLike, salt: ArrayLike, iterations: number, keylen: number,
digest: string, callback: Pbkdf2Callback): void {
if (typeof digest === 'function') {
// Appease node test cases
validateString(undefined, 'digest');
}
validateFunction(callback, 'callback');
({ password, salt, iterations, keylen, digest } =
check(password, salt, iterations, keylen, digest));

new Promise<ArrayBuffer>((res, rej) => {
try {
res(cryptoImpl.getPbkdf(password, salt, iterations, keylen, digest));
} catch(err) {
rej(err);
}
}).then((val) => callback(null, Buffer.from(val)), (err) => callback(err));
}

function check(password: ArrayLike|ArrayBufferView, salt: ArrayLike|ArrayBufferView, iterations: number, keylen: number,
digest: string): any {
validateString(digest, 'digest');

password = getArrayBufferOrView(password, 'password');
salt = getArrayBufferOrView(salt, 'salt');
// OpenSSL uses a signed int to represent these values, so we are restricted
// to the 31-bit range here (which is plenty).
validateInt32(iterations, 'iterations', 1);
validateInt32(keylen, 'keylen', 0);

return { password, salt, iterations, keylen, digest };
}
4 changes: 4 additions & 0 deletions src/workerd/api/node/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ class CryptoImpl final: public jsg::Object {
jsg::Optional<kj::Array<kj::byte>> add, jsg::Optional<kj::Array<kj::byte>> rem);

bool checkPrimeSync(kj::Array<kj::byte> bufferView, uint32_t num_checks);
// Pbkdf2
kj::Array<kj::byte> getPbkdf(kj::Array<kj::byte> password, kj::Array<kj::byte> salt,
uint32_t num_iterations, uint32_t keylen, kj::String name);
JSG_RESOURCE_TYPE(CryptoImpl) {
JSG_METHOD(randomPrime);
JSG_METHOD(checkPrimeSync);
JSG_METHOD(getPbkdf);
}
};

Expand Down
Loading

0 comments on commit 120be04

Please sign in to comment.