Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

nacl.setReturnType and generic byte array acceptance #79

Closed
wants to merge 9 commits into from
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,10 @@ or [download source code](https://github.com/dchest/tweetnacl-js/releases).
Usage
------

All API functions accept and return bytes as `Uint8Array`s. If you need to
encode or decode strings, use functions from `nacl.util` namespace.
All API functions accept `Uint8Array`s, Node/Browserify Buffers, or any
api-compatible byte array objects. Return type defaults to Uint8Array, but can
be changed to node.js or browserify Buffer using util.setReturnType. If you
need to encode or decode strings, use functions from `nacl.util` namespace.

### Public-key authenticated encryption (box)

Expand Down Expand Up @@ -326,6 +328,26 @@ Returns `false` if either of the arguments has zero length, or arguments have
different lengths, or their contents differ.


### Return Types

#### nacl.setReturnType(constructor)

Sets the type of byte array used to return data from high level functions. The
constructor must accept an integer length or another valid byte array whose data
is copied or referenced in. It must have a valid length property, and implement
subarray, slice, or both methods as in Uint8Array. When an element is set to a
value outside of 0-255 inclusive, the object must truncate it in the same way
Uint8Array and Node.js Buffers do: `(new constructor([257]))[0] == 1`

Default is Uint8Array.

```
nacl.hash(new Buffer("nyan")) //-> returns Uint8Array
nacl.setReturnType(Buffer)
nacl.hash(new Buffer("nyan")) //-> returns Buffer
```


### Utilities

Encoding/decoding functions are provided for convenience. They are correct,
Expand Down
81 changes: 50 additions & 31 deletions nacl-fast.js
Original file line number Diff line number Diff line change
Expand Up @@ -1983,7 +1983,7 @@ function crypto_sign(sm, m, n, sk) {
for (i = 0; i < n; i++) sm[64 + i] = m[i];
for (i = 0; i < 32; i++) sm[32 + i] = d[32 + i];

crypto_hash(r, sm.subarray(32), n+32);
crypto_hash(r, subarray(sm, 32), n+32);
reduce(r);
scalarbase(p, r);
pack(sm, p);
Expand All @@ -2000,7 +2000,7 @@ function crypto_sign(sm, m, n, sk) {
}
}

modL(sm.subarray(32), x);
modL(subarray(sm, 32), x);
return smlen;
}

Expand Down Expand Up @@ -2059,7 +2059,7 @@ function crypto_sign_open(m, sm, n, pk) {
reduce(h);
scalarmult(p, q, h);

scalarbase(q, sm.subarray(32));
scalarbase(q, subarray(sm, 32));
add(p, q);
pack(t, p);

Expand Down Expand Up @@ -2147,22 +2147,41 @@ function checkBoxLengths(pk, sk) {
if (sk.length !== crypto_box_SECRETKEYBYTES) throw new Error('bad secret key size');
}

var validatedBuffers = {};
function checkArrayTypes() {
var t, i;
var i, c, x;
for (i = 0; i < arguments.length; i++) {
if ((t = Object.prototype.toString.call(arguments[i])) !== '[object Uint8Array]')
throw new TypeError('unexpected type ' + t + ', use Uint8Array');
c = arguments[i].constructor;
if (validatedBuffers[c.name] === true) continue;
var x = new c(1);
x[0] = 257;
if (x[0] !== 1 || x.length !== 1 || typeof (x.subarray || x.slice) !== 'function')
throw new TypeError('unexpected type ' + c.name + ' use Uint8Array, Buffer, or equivalent')
validatedBuffers[c.name] = true;
}
}

function cleanup(arr) {
for (var i = 0; i < arr.length; i++) arr[i] = 0;
}

var returnType = Uint8Array;
nacl.setReturnType = function(c) {
checkArrayTypes(new c(1))
if ((new c(new Uint8Array([1,2])))[1] !== 2)
throw new TypeError('constructor must accept Uint8Array data as argument');
returnType = c;
}

// slice by reference a buffer
function subarray(buffer) {
return (buffer.subarray || buffer.slice).apply(buffer, Array.prototype.slice.call(arguments, 1));
}

nacl.util = {};

nacl.util.decodeUTF8 = function(s) {
var i, d = unescape(encodeURIComponent(s)), b = new Uint8Array(d.length);
var i, d = unescape(encodeURIComponent(s)), b = new returnType(d.length);
for (i = 0; i < d.length; i++) b[i] = d.charCodeAt(i);
return b;
};
Expand All @@ -2185,16 +2204,16 @@ nacl.util.encodeBase64 = function(arr) {

nacl.util.decodeBase64 = function(s) {
if (typeof atob === 'undefined') {
return new Uint8Array(Array.prototype.slice.call(new Buffer(s, 'base64'), 0));
return new returnType(Array.prototype.slice.call(new Buffer(s, 'base64'), 0));
} else {
var i, d = atob(s), b = new Uint8Array(d.length);
var i, d = atob(s), b = new returnType(d.length);
for (i = 0; i < d.length; i++) b[i] = d.charCodeAt(i);
return b;
}
};

nacl.randomBytes = function(n) {
var b = new Uint8Array(n);
var b = new returnType(n);
randombytes(b, n);
return b;
};
Expand All @@ -2203,21 +2222,21 @@ nacl.secretbox = function(msg, nonce, key) {
checkArrayTypes(msg, nonce, key);
checkLengths(key, nonce);
var m = new Uint8Array(crypto_secretbox_ZEROBYTES + msg.length);
var c = new Uint8Array(m.length);
var c = new returnType(m.length);
for (var i = 0; i < msg.length; i++) m[i+crypto_secretbox_ZEROBYTES] = msg[i];
crypto_secretbox(c, m, m.length, nonce, key);
return c.subarray(crypto_secretbox_BOXZEROBYTES);
return subarray(c, crypto_secretbox_BOXZEROBYTES);
};

nacl.secretbox.open = function(box, nonce, key) {
checkArrayTypes(box, nonce, key);
checkLengths(key, nonce);
var c = new Uint8Array(crypto_secretbox_BOXZEROBYTES + box.length);
var m = new Uint8Array(c.length);
var m = new returnType(c.length);
for (var i = 0; i < box.length; i++) c[i+crypto_secretbox_BOXZEROBYTES] = box[i];
if (c.length < 32) return false;
if (crypto_secretbox_open(m, c, c.length, nonce, key) !== 0) return false;
return m.subarray(crypto_secretbox_ZEROBYTES);
return subarray(m, crypto_secretbox_ZEROBYTES);
};

nacl.secretbox.keyLength = crypto_secretbox_KEYBYTES;
Expand All @@ -2228,15 +2247,15 @@ nacl.scalarMult = function(n, p) {
checkArrayTypes(n, p);
if (n.length !== crypto_scalarmult_SCALARBYTES) throw new Error('bad n size');
if (p.length !== crypto_scalarmult_BYTES) throw new Error('bad p size');
var q = new Uint8Array(crypto_scalarmult_BYTES);
var q = new returnType(crypto_scalarmult_BYTES);
crypto_scalarmult(q, n, p);
return q;
};

nacl.scalarMult.base = function(n) {
checkArrayTypes(n);
if (n.length !== crypto_scalarmult_SCALARBYTES) throw new Error('bad n size');
var q = new Uint8Array(crypto_scalarmult_BYTES);
var q = new returnType(crypto_scalarmult_BYTES);
crypto_scalarmult_base(q, n);
return q;
};
Expand All @@ -2252,7 +2271,7 @@ nacl.box = function(msg, nonce, publicKey, secretKey) {
nacl.box.before = function(publicKey, secretKey) {
checkArrayTypes(publicKey, secretKey);
checkBoxLengths(publicKey, secretKey);
var k = new Uint8Array(crypto_box_BEFORENMBYTES);
var k = new returnType(crypto_box_BEFORENMBYTES);
crypto_box_beforenm(k, publicKey, secretKey);
return k;
};
Expand All @@ -2267,8 +2286,8 @@ nacl.box.open = function(msg, nonce, publicKey, secretKey) {
nacl.box.open.after = nacl.secretbox.open;

nacl.box.keyPair = function() {
var pk = new Uint8Array(crypto_box_PUBLICKEYBYTES);
var sk = new Uint8Array(crypto_box_SECRETKEYBYTES);
var pk = new returnType(crypto_box_PUBLICKEYBYTES);
var sk = new returnType(crypto_box_SECRETKEYBYTES);
crypto_box_keypair(pk, sk);
return {publicKey: pk, secretKey: sk};
};
Expand All @@ -2277,9 +2296,9 @@ nacl.box.keyPair.fromSecretKey = function(secretKey) {
checkArrayTypes(secretKey);
if (secretKey.length !== crypto_box_SECRETKEYBYTES)
throw new Error('bad secret key size');
var pk = new Uint8Array(crypto_box_PUBLICKEYBYTES);
var pk = new returnType(crypto_box_PUBLICKEYBYTES);
crypto_scalarmult_base(pk, secretKey);
return {publicKey: pk, secretKey: new Uint8Array(secretKey)};
return {publicKey: pk, secretKey: new returnType(secretKey)};
};

nacl.box.publicKeyLength = crypto_box_PUBLICKEYBYTES;
Expand All @@ -2292,7 +2311,7 @@ nacl.sign = function(msg, secretKey) {
checkArrayTypes(msg, secretKey);
if (secretKey.length !== crypto_sign_SECRETKEYBYTES)
throw new Error('bad secret key size');
var signedMsg = new Uint8Array(crypto_sign_BYTES+msg.length);
var signedMsg = new returnType(crypto_sign_BYTES+msg.length);
crypto_sign(signedMsg, msg, msg.length, secretKey);
return signedMsg;
};
Expand All @@ -2306,14 +2325,14 @@ nacl.sign.open = function(signedMsg, publicKey) {
var tmp = new Uint8Array(signedMsg.length);
var mlen = crypto_sign_open(tmp, signedMsg, signedMsg.length, publicKey);
if (mlen < 0) return null;
var m = new Uint8Array(mlen);
var m = new returnType(mlen);
for (var i = 0; i < m.length; i++) m[i] = tmp[i];
return m;
};

nacl.sign.detached = function(msg, secretKey) {
var signedMsg = nacl.sign(msg, secretKey);
var sig = new Uint8Array(crypto_sign_BYTES);
var sig = new returnType(crypto_sign_BYTES);
for (var i = 0; i < sig.length; i++) sig[i] = signedMsg[i];
return sig;
};
Expand All @@ -2333,8 +2352,8 @@ nacl.sign.detached.verify = function(msg, sig, publicKey) {
};

nacl.sign.keyPair = function() {
var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES);
var sk = new Uint8Array(crypto_sign_SECRETKEYBYTES);
var pk = new returnType(crypto_sign_PUBLICKEYBYTES);
var sk = new returnType(crypto_sign_SECRETKEYBYTES);
crypto_sign_keypair(pk, sk);
return {publicKey: pk, secretKey: sk};
};
Expand All @@ -2343,17 +2362,17 @@ nacl.sign.keyPair.fromSecretKey = function(secretKey) {
checkArrayTypes(secretKey);
if (secretKey.length !== crypto_sign_SECRETKEYBYTES)
throw new Error('bad secret key size');
var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES);
var pk = new returnType(crypto_sign_PUBLICKEYBYTES);
for (var i = 0; i < pk.length; i++) pk[i] = secretKey[32+i];
return {publicKey: pk, secretKey: new Uint8Array(secretKey)};
return {publicKey: pk, secretKey: new returnType(secretKey)};
};

nacl.sign.keyPair.fromSeed = function(seed) {
checkArrayTypes(seed);
if (seed.length !== crypto_sign_SEEDBYTES)
throw new Error('bad seed size');
var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES);
var sk = new Uint8Array(crypto_sign_SECRETKEYBYTES);
var pk = new returnType(crypto_sign_PUBLICKEYBYTES);
var sk = new returnType(crypto_sign_SECRETKEYBYTES);
for (var i = 0; i < 32; i++) sk[i] = seed[i];
crypto_sign_keypair(pk, sk, true);
return {publicKey: pk, secretKey: sk};
Expand All @@ -2366,7 +2385,7 @@ nacl.sign.signatureLength = crypto_sign_BYTES;

nacl.hash = function(msg) {
checkArrayTypes(msg);
var h = new Uint8Array(crypto_hash_BYTES);
var h = new returnType(crypto_hash_BYTES);
crypto_hash(h, msg, msg.length);
return h;
};
Expand Down
5 changes: 3 additions & 2 deletions nacl-fast.min.js

Large diffs are not rendered by default.

Loading