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

jws.decode support for encoding option #86

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
4 changes: 4 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ node_js:
- 5
- 6
- 7
- 8
- 9
- 10
- 11
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var VerifyStream = require('./lib/verify-stream');
var ALGORITHMS = [
'HS256', 'HS384', 'HS512',
'RS256', 'RS384', 'RS512',
'PS256', 'PS384', 'PS512',
'ES256', 'ES384', 'ES512'
];

Expand Down
15 changes: 11 additions & 4 deletions lib/sign-stream.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
/*global module*/
var base64url = require('base64url');
var DataStream = require('./data-stream');
var jwa = require('jwa');
var Stream = require('stream');
var toString = require('./tostring');
var toBuffer = require('./to-buffer');
var util = require('util');

function base64url(buf) {
return buf
.toString('base64')
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
}

function jwsSecuredInput(header, payload, encoding) {
encoding = encoding || 'utf8';
var encodedHeader = base64url(toString(header), 'binary');
var encodedPayload = base64url(toString(payload), encoding);
var encodedHeader = base64url(toBuffer(header));
var encodedPayload = base64url(toBuffer(payload, encoding));
return util.format('%s.%s', encodedHeader, encodedPayload);
}

Expand Down
19 changes: 19 additions & 0 deletions lib/to-buffer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
'use strict';

var Buffer = require('safe-buffer').Buffer;

module.exports = function toBuffer(val, encoding) {
if (Buffer.isBuffer(val)) {
return val;
}
if (typeof val === 'string') {
return Buffer.from(val, encoding || 'utf8');
}
if (typeof val === 'number') {
// This won't work for very large or very small numbers, but is consistent
// with previous behaviour at least
val = val.toString();
return Buffer.from(val, 'utf8');
}
return Buffer.from(JSON.stringify(val), 'utf8');
};
8 changes: 4 additions & 4 deletions lib/verify-stream.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*global module*/
var base64url = require('base64url');
var Buffer = require('safe-buffer').Buffer;
var DataStream = require('./data-stream');
var jwa = require('jwa');
var Stream = require('stream');
Expand All @@ -20,7 +20,7 @@ function safeJsonParse(thing) {

function headerFromJWS(jwsSig) {
var encodedHeader = jwsSig.split('.', 1)[0];
return safeJsonParse(base64url.decode(encodedHeader, 'binary'));
return safeJsonParse(Buffer.from(encodedHeader, 'base64').toString('binary'));
}

function securedInputFromJWS(jwsSig) {
Expand All @@ -34,7 +34,7 @@ function signatureFromJWS(jwsSig) {
function payloadFromJWS(jwsSig, encoding) {
encoding = encoding || 'utf8';
var payload = jwsSig.split('.')[1];
return base64url.decode(payload, encoding);
return Buffer.from(payload, 'base64').toString(encoding);
}

function isValidJws(string) {
Expand Down Expand Up @@ -66,7 +66,7 @@ function jwsDecode(jwsSig, opts) {
if (!header)
return null;

var payload = payloadFromJWS(jwsSig);
var payload = payloadFromJWS(jwsSig, opts.encoding);
if (header.typ === 'JWT' || opts.json)
payload = JSON.parse(payload, opts.encoding);
Copy link

@loucadufault loucadufault Jan 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can remove opts.encoding as a second argument to JSON.parse, as this is an argument for the reviver function, see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse:

Non-callable values are ignored.


Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@
"readmeFilename": "readme.md",
"gitHead": "c0f6b27bcea5a2ad2e304d91c2e842e4076a6b03",
"dependencies": {
"base64url": "^2.0.0",
"jwa": "^1.1.5",
"jwa": "^1.2.0",
"safe-buffer": "^5.0.1"
},
"devDependencies": {
Expand Down
33 changes: 20 additions & 13 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This was developed against `draft-ietf-jose-json-web-signature-08` and
implements the entire spec **except** X.509 Certificate Chain
signing/verifying (patches welcome).

There are both syncronous (`jws.sign`, `jws.verify`) and streaming
There are both synchronous (`jws.sign`, `jws.verify`) and streaming
(`jws.createSign`, `jws.createVerify`) APIs.

# Install
Expand All @@ -18,6 +18,7 @@ $ npm install jws
# Usage

## jws.ALGORITHMS

Array of supported algorithms. The following algorithms are currently supported.

alg Parameter Value | Digital Signature or MAC Algorithm
Expand All @@ -28,12 +29,14 @@ HS512 | HMAC using SHA-512 hash algorithm
RS256 | RSASSA using SHA-256 hash algorithm
RS384 | RSASSA using SHA-384 hash algorithm
RS512 | RSASSA using SHA-512 hash algorithm
PS256 | RSASSA-PSS using SHA-256 hash algorithm
PS384 | RSASSA-PSS using SHA-384 hash algorithm
PS512 | RSASSA-PSS using SHA-512 hash algorithm
ES256 | ECDSA using P-256 curve and SHA-256 hash algorithm
ES384 | ECDSA using P-384 curve and SHA-384 hash algorithm
ES512 | ECDSA using P-521 curve and SHA-512 hash algorithm
none | No digital signature or MAC value included


## jws.sign(options)

(Synchronous) Return a JSON Web Signature for a header and a payload.
Expand Down Expand Up @@ -64,7 +67,7 @@ const signature = jws.sign({

## jws.verify(signature, algorithm, secretOrKey)

(Synchronous) Returns`true` or `false` for whether a signature matches a
(Synchronous) Returns `true` or `false` for whether a signature matches a
secret or key.

`signature` is a JWS Signature. `header.alg` must be a value found in `jws.ALGORITHMS`.
Expand All @@ -89,6 +92,7 @@ Returns an object with three properties, e.g.
```

## jws.createSign(options)

Returns a new SignStream object.

Options:
Expand All @@ -103,7 +107,8 @@ value is known ahead of time, or a stream for convenience.
`key`/`privateKey`/`secret` may also be an object when using an encrypted
private key, see the [crypto documentation][encrypted-key-docs].

Example
Example:

```js

// This...
Expand All @@ -115,10 +120,10 @@ jws.createSign({
// ...
});

// is equivilant to this:
const signer = jws.createSign(
// is equivalent to this:
const signer = jws.createSign({
header: { alg: 'RS256' },
);
});
privateKeyStream.pipe(signer.privateKey);
payloadStream.pipe(signer.payload);
signer.on('done', function(signature) {
Expand All @@ -127,6 +132,7 @@ signer.on('done', function(signature) {
```

## jws.createVerify(options)

Returns a new VerifyStream object.

Options:
Expand All @@ -139,7 +145,8 @@ Options:
All options expect a string or a buffer when the value is known ahead of
time, or a stream for convenience.

Example
Example:

```js

// This...
Expand All @@ -160,8 +167,9 @@ verifier.on('done', function(verified, obj) {
```

## Class: SignStream
A `Readable Stream` that emits a single data event, the calculated
signature, when done.

A `Readable Stream` that emits a single data event (the calculated
signature) when done.

### Event: 'done'
`function (signature) { }`
Expand All @@ -171,7 +179,7 @@ signature, when done.
A `Writable Stream` that expects the JWS payload. Do *not* use if you
passed a `payload` option to the constructor.

Example
Example:

```js
payloadStream.pipe(signer.payload);
Expand Down Expand Up @@ -200,6 +208,7 @@ of whether or not that signature was valid.
`valid` is a boolean for whether or not the signature is valid.

### verifier.signature

A `Writable Stream` that expects a JWS Signature. Do *not* use if you
passed a `signature` option to the constructor.

Expand All @@ -208,7 +217,6 @@ passed a `signature` option to the constructor.
A `Writable Stream` that expects a public key or secret. Do *not* use if you
passed a `key` or `secret` option to the constructor.


# TODO

* It feels like there should be some convenience options/APIs for
Expand All @@ -217,7 +225,6 @@ passed a `key` or `secret` option to the constructor.

* X.509 support, ugh


# License

MIT
Expand Down
21 changes: 21 additions & 0 deletions test/jws.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,3 +330,24 @@ test('jws.isValid', function (t) {
t.same(jws.isValid(valid), true);
t.end();
});

test('#50 mangled binary payload', function(t) {
const sig = jws.sign({
header: {
alg: 'HS256'
},
payload: new Buffer('TkJyotZe8NFpgdfnmgINqg==', 'base64'),
secret: new Buffer('8NRxgIkVxP8LyyXSL4b1dg==', 'base64')
});

t.same(sig, 'eyJhbGciOiJIUzI1NiJ9.TkJyotZe8NFpgdfnmgINqg.9XilaLN_sXqWFtlUCdAlGI85PCEbJZSIQpakyAle-vo');
t.end();
});

test('jws.decode supports encoding option', function (t) {
const payloadString = 'åäöí';
const jwsObj = jws.sign({ header: { alg: 'hs256' }, payload: payloadString, secret: 'shhh', encoding: "latin1" });
const parts = jws.decode(jwsObj, {encoding: "latin1"});
t.same(parts.payload, payloadString, 'should match payload');
t.end();
});