diff --git a/src/main.js b/src/main.js
--- a/src/main.js
+++ b/src/main.js
@@ -1,13 +1,15 @@
 import * as util from "./util.js";
 import {blake2s} from "./blake.js";
+import {pbkdf2} from "./pbkdf2.js";
 import {Chacha20,encrypt,decrypt} from "./chacha.js";
 
-export default {util,blake2s,Chacha20};
+export default {util,blake2s,pbkdf2,Chacha20,encrypt,decrypt};
 
 // export for tests running on Node
 if(typeof module!=='undefined'&&module.hasOwnProperty('exports')){
 	module.exports.util=util;
 	module.exports.blake2s=blake2s;
+	module.exports.pbkdf2=pbkdf2;
 	module.exports.Chacha20=Chacha20;
 	module.exports.encrypt=encrypt;
 	module.exports.decrypt=decrypt;
diff --git a/src/pbkdf2.js b/src/pbkdf2.js
new file mode 100644
--- /dev/null
+++ b/src/pbkdf2.js
@@ -0,0 +1,27 @@
+import {int322bytesBE} from "./util.js";
+import {blake2s} from "./blake.js";
+
+const HASH_LENGTH=32;
+
+export function pbkdf2(password,salt,iterationCount,outLength){
+	// max BLAKE2 key length is 32B
+	if(password.length>32){password=blake2s(password);}
+	
+	let blockCount=Math.ceil(outLength/HASH_LENGTH);
+	let result=[];
+	
+	for(let i=0;i<blockCount;i++){
+		let block=_computeBlock(password,salt,iterationCount,i+1);
+		result=result.concat(block);
+	}
+	return result.slice(0,outLength);
+}
+
+function _computeBlock(password,salt,iterationCount,blockIndex){
+	let u=blake2s(salt.concat(int322bytesBE(blockIndex)),HASH_LENGTH,password);
+	for(let i=1;i<iterationCount;i++){
+		let ui=blake2s(u,HASH_LENGTH,password);
+		u=u.map((b,j)=>b^ui[j]);
+	}
+	return u;
+}
diff --git a/src/util.js b/src/util.js
--- a/src/util.js
+++ b/src/util.js
@@ -16,6 +16,9 @@ export function bytes2int32s(arr){
 	return res;
 }
 
+/**
+ * Converts a 32 bit integer into 4 bytes in little endian order.
+ */
 export function int322bytes(x){
 	let res=[];
 	for(let i=0;i<4;i++){
@@ -25,6 +28,15 @@ export function int322bytes(x){
 	return res;
 }
 
+/**
+ * Converts a 32 bit integer into 4 bytes in big endian order.
+ */
+export function int322bytesBE(x){
+	let res=int322bytes(x);
+	res.reverse();
+	return res;
+}
+
 export function int32s2bytes(arr){
 	return arr.map(int322bytes).reduce((acc,bytes)=>acc.concat(bytes));
 }