Giter VIP home page Giter VIP logo

browserid-crypto's Introduction

JavaScript implementation of JSON Web Signatures and JSON Web Tokens as needed by BrowserID.

Build Status

  • libs contains third-party libraries that need to be included. See libs/dependencies.txt and libs/package.txt

  • This is written as CommonJS modules for node and such. Browserify is used to bundle it all up.

NOTE: this is written as future documentation of v0.2 APIs, which will not be backwards compatible with v0.1.

Overview

JSON Web Tokens (JWTs) look like:

eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9
.
eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt
cGxlLmNvbS9pc19yb290Ijp0cnVlfQ
.
dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

(line breaks are for readability)

JWTs are made up of three components, each base64url-encoded, joined by a period character. A JWT can be either a JWS (JSON Web Signature) or a JWE (JSON Web Encryption). In this library, we only consider JWS. Because JWT is effectively the abstract superclass of both JWS and JWE, we don't expose JWT APIs directly (as of v0.2.0). We simply expose a JWS API.

We use JWK (JSON Web Keys) to specify keys: http://tools.ietf.org/html/draft-ietf-jose-json-web-key-00

We use JWA (JSON Web Algorithms) to specify algorithms: http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-00 (we add algorithm "DSA" to indicate DSA, with DS160 the standard DSA 1024/160.)

Usage

  1. for Node 4+ ensure that you are using g++ 4.8 (use CXX=g++-4.8 to force that version)
  2. npm install browserid-crypto
  3. in javascript: require('browserid-crypto')

Basic API

var jwcrypto = require("browserid-crypto");
require("browserid-crypto/lib/algs/ds");

// random number generation is taken care of automatically
// with auto-seeding that is optimized for server or browser
// setup

// more entropy can be added as follows
// this can be useful to incorporate server-provided entropy
// on clients that don't have any good entropy of their own
// entropy should be either a 32 bit int, an array of ints, or a string
jwcrypto.addEntropy('entropy');

// generate a key
// we use DSA, which is "DS" in JSON Web Algorithm parlance
// we use keysize 160, which has a specific interpretation based
// on the algorithm, in this case DSA 1024/160, standard DSA.
jwcrypto.generateKeypair({
    algorithm: 'DSA',
    keysize: 160
}, function(err, keypair) {
    // error in err?

    // serialize the public key
    console.log(keypair.publicKey.serialize());

    // just the JSON object to embed in another structure
    console.log(JSON.stringify({stuff: keypair.publicKey.toSimpleObject()}));

    // replace this with the key to sign
    var publicKeyToCertify = keypair.publicKey.serialize();

    // create and sign a JWS
    var payload = {principal: {email: '[email protected]'},
                    pubkey: jwcrypto.loadPublicKey(publicKeyToCertify)};

    jwcrypto.sign(payload, keypair.secretKey, function(err, jws) {
        // error in err?

        // serialize it
        console.log(jws.toString());

        // replace with things to verify
    var signedObject = jws;
    var publicKey = keypair.publicKey;

        // verify it
        jwcrypto.verify(signedObject, publicKey, function(err, payload) {
            // if verification fails, then err tells you why
            // if verification succeeds, err is null, and payload is
            // the signed JS object.
        });
    });

    // replace this with the key to load
    var storedSecretKey = keypair.secretKey.serialize();

    // also, if loading a secret key from somewhere
    var otherSecretKey = jwcrypto.loadSecretKey(storedSecretKey);
});

Assertions

Sometimes the JSON object to sign should be a standard assertion with pre-defined fields.

var assertion = require("browserid-crypto").assertion;

// payload of the assertion
var payload = {principal: {email: '[email protected]'}};

// add special fields which will be encoded properly
// payload cannot contain reserved fields
assertion.sign(payload, {issuer: "foo.com", expiresAt: new Date(new Date().valueOf() + 5000),
                            issuedAt: new Date().valueOf(), audience: "https://example.com"},
                    keypair.secretKey,
                    function(err, signedAssertion) {
    // a normal signedObject, much like above
    // can be verified with jwcrypto.verify

    // or verified specifically for jwt, with expiration verification
    var now = new Date();
    assertion.verify(signedObject, keypair.publicKey, now, function(err, payload, assertionParams) {
        // payload is the original payload
        // assertionParams contains issuedAt, expiresAt as dates
        // and issuer and audience as strings.
    });
});

Note that timestamps (for issuedAt and expiresAt) are integers containing the standard JS milliseconds-since-epoch, or objects with methods named .valueOf() which will return such an integer. The assertion format currently serializes these integers verbatim; a future version may serialize them as seconds (instead of milliseconds) to conform with the JWT specifications.

Certs

Sometimes the JSON objects to sign are certificates

var cert = require("browserid-crypto").cert;

var keyToCertify = keypairToCertify.publicKey;
var principal = {email: "[email protected]"};

var assertionParams = {issuer: "foo.com", issuedAt: new Date(),
                        expiresAt: new Date()};

// cert params, kid is optional, others are required
var certParams = {kid: "key-2012-08-11",
                    publicKey: keyToCertify,
                    principal: principal};

var additionalPayload = {};

// payload cannot contain reserved fields
cert.sign(certParams,
            assertionParams, additionalPayload,
            keypair.secretKey,
            function(err, signedObject) {
    // normal signedObject
    // can be verified with jwcrypto.verify

    // or verified specifically for certification
    // include a date that is considered the "now"
    cert.verify(signedObject, keypair.publicKey, now, function(err, payload, assertionParams, certParams) {
        // the extra payload
        // the assertionParams specifics
        // the certParams include publicKey being certified, and principal bound to it.
    });
});

// bundle a cert chain and an assertion
var bundle = cert.bundle([certs], assertion);

function getPK(issuer, next) {
    // function to get a public key for an issuer
}

var now = new Date();

// verify just the chain of certs
cert.verifyChain([certs], now, getPK, function(err, certParamsArray) {
    // err is an error or null
    // if no error:
    // certParamsArray is the array of individual cert params from each verification
    // including specifically the publicKey and principal parameters
});

// verify a chain of certs and assertion
cert.verifyBundle(bundle, now, getPK, function(err, certParamsArray, payload, assertionParams) {
    // err is an error or null
    // if no error:
    // certParamsArray is the array of individual cert params from each verification
    // payload is the assertion payload, and assertionParams is the assertion params.
});

browserid-crypto's People

Contributors

benadida avatar callahad avatar fmarier avatar jaredhirsch avatar jedp avatar jmandel avatar jrgm avatar lloyd avatar malept avatar mozilla-github-standards avatar ozten avatar rfk avatar seanmonstar avatar thomasburguiere avatar valeriangalliat avatar vladikoff avatar warner avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

browserid-crypto's Issues

supply rng object to generate calls directly?

right now, randomness source is assumed inside the JWK generation and signature calls. Seems like we might want to make this modular enough to supply at generation/signing time.

Is "bigint" a required dependency?

The readme implies it is required, but its listed in package.json as an optional dependency and all the tests pass without it installed.

Cannot call method 'powm' of undefined

My (temporary) browserid primary at underpin.no is unable to get the assertion verified. I located the problem in jwcrypto by running browserid locally. Stacktrace:

stack=[TypeError: Cannot call method 'powm' of undefined,
     at Object.modPow (/home/runar/dev/browserid/node_modules/jwcrypto/libs/all.js:2786:51),
     at Object.modPowInt (/home/runar/dev/browserid/node_modules/jwcrypto/libs/all.js:2789:19),
     at RSAKey.RSADoPublic [as doPublic] (/home/runar/dev/browserid/node_modules/jwcrypto/libs/all.js:1842:12),
     at RSAKey._rsasign_verifyString [as verifyString]
(/home/runar/dev/browserid/node_modules/jwcrypto/libs/all.js:2492:29),
     at Object.verify (/home/runar/dev/browserid/node_modules/jwcrypto/algs/rs.js:105:19),
     at Object._verify [as verify] (/home/runar/dev/browserid/node_modules/jwcrypto/jws.js:161:32),
     at /home/runar/dev/browserid/node_modules/jwcrypto/jwcert.js:157:19,     at Array.forEach (native),
     at Function.<anonymous> (/home/runar/dev/browserid/node_modules/jwcrypto/underscore.js:8:487),
     at /home/runar/dev/browserid/node_modules/jwcrypto/jwcert.js:151:9]

The problem occuers after the following browserid exchange when logging in to http://dev.myfavoritebeer.org :

HAR (HTML Archive) viewer

The offending error message is show in the response to "POST auth_with_assertion"

It might be my certificate that's missing something, but I fail to see what it would be.

My SSH key was generated in ruby like this:

OpenSSL::PKey::RSA.new(2048)

My /.well-known/browserid document says:

{"public-key":{"algorithm":"RS","n":"22305005103303022568370286567808371931269331533495050709598982090300943109050219570457337433786107501910861702107754392618386823097475133679871998535401672214352909439119431016278585751720546080745962527155941845123435307146504100671425705615816458901761385129890198545644807932563229289250550059603068122250487488773311011323485649216467344192222506551772255829840449796412462436604843860723045382060493908771076842589855303482302089446765968512116642766421092898646646757646147139304860621316271699416577639029809549120073101590746269802801920586379944018551836314125731533296535841682591569080448014344696202532139","e":"65537"},"authentication":"/sign_in","provisioning":"/provision"}

I return it with the following code:

    {
      "public-key" => { "algorithm"=> "RS", "n" => @pubkey.n.to_s, "e" => @pubkey.e.to_s },
      "authentication" => "/sign_in",
      "provisioning" => "/provision"
    }.to_json

The link to HAR Viewer above shows the HTML GET /provision returns. Then it shows underpin.no recognize me as the user "r" when we GET /whoami.

assertion.js uses 'const' which fails in IE

When bidbundle is generated and created, it uses "const" on line 5:

const jwcrypto = require("./jwcrypto"),

This causes IE of all variants to throw an exception when loading the crypto file.

test.html does not work with browserify 1.13.5

The browserified jwcrypto does not seem to work with the test.html page: it fails with "require is not defined". The issue is linked to the bump to version 1.13.5 of browserify. Downgrading to 1.8.1 fixes the issue.

bin/check-assertion looks for 'public-key', will fail on newer cert format

While reviewing PR#45, I noticed a couple of other places in which bin/check-assertion is using public-key instead of publicKey. These places are extracting the data from the raw unJSONed cert/assertion string, bypassing the new versioned-deserialization code. So they'll break against new-format certs.

The better approach would probably be do call cert.extractCertParamsFrom() on the payload, and then use the .publicKey property of the object that comes back. This .publicKey is a stable API name, as opposed to the version-dependent name inside the serialized object.

new-format keys use "+" and "/" (non-URL base64).. is that right?

Most of browserid uses "base64url" encoding, the Base64 encoding that uses "" and "-" instead of "+" and "/". As an experiment, I ran jwcrypto's bin/generate-keypair with lib/version.js modified to use DEFAULT_DATA_FORMAT_VERSION = '2012.08.15', and the key it generated is using the "+/" alphabet instead of "-":

{"algorithm":"DS","version":"2012.08.15","y":"HPh1+Oq/FL2ZZYV2ppm6NchlG4gFhJxeMnT/+ZCHj2zj/8kTapXQIKRLPcG8XCjchWNRFw/LSq2N0623NznCdeQUkETGeS3FvznEnO25flMt+jOwlznR/zBt7QzgumLRKkaG/FiyyCB7OgtnBk1xUvlRbs2CShG7HGUVKteUCSvv9bUSQ2a1NmrSMNwVuKoVYRImjTtolPQIXhmyxiO1QWU8FSnoN9qv84g0i0BXxFd0y5XVLicGhNd69OhTv2RJHyi5pZzYWQd38rhhgrLugUafSbfAH+2OX/jntctSxjtxH8g7KHWGgjE/hh1LwwgHUC8gXjgLXeNHam/V3D/bng==","p":"1sTlBFaXdWx6MS0CwiicJdQPmVQmH3tYdiFLbfEJxzi3Yiaxmbt+M/j8esHcwxbh58eJc5Ub/G/y4AzJh812/PsLjACWsLRg//rJYMpBNsKPS/tYDeR89+eTTDmF47PZQ7d/Bu8q86w0lPw8b8SYEKY4U4YqArscgkoBt/xojkAoUnpYrVjJ1RKSJmDbXVBbwmOvKTvJO81tiFoVdXnX9SlSI23Z0GpPw7wiR9IfGnD1hI6wF2UTU3yYP1o2c38B+CtEVG6Ofw+rxFfj3h2cXbqWllsQoqBYCwrQ+IF54QBmEH+3QxSgfmdFhjvHl7cALr7AsACpjraXQUcJrBe0AQ==","q":"seNw9kcsh1TM116ZZm7I7x/XSLdIu7wIUD2CzoBVqzs=","g":"moJpqy47czpSQhedj43bF/+TKX2eqwA3bbIRoisZyFTfqAFm3yEyy8UfsiSwkEq7Itose3hQ94ISTLV1sRb0HqfE/HWx13UlIEzXwjoVmZAEwjzetyNZ7nTohqHd54Va4F/oR0R9CmgFkALDgZp13H3Lsw4576w24H4sQEt8qYsmOyX6MUupPAYlcYvUic6m0EuksLfxVu60xWxEtQ5Ptbzp164NVbN5Il/rAhSgS+1y8z4GZNKQ58hA3z4qu15IGJ+k6QZG8YZ9sonGVgR2eZ976EIKbcAdB43kN/KA//LX3fEkjVbhpUuTOkFinWwlKYPFh5UQWALTDXvNgZz27w=="}

Is this intentional? Do you want to change this to use the "-" alphabet before switching to the new format? (if so, you should probably do it soon, since 0.4.0 won't actually be able to accept version=2012.08.15 keys if the spec is changed to make those keys use the "-" alphabet)

verifyBundle returns crappy error messages

If an assertion is a malformed string, .verifyBundle() just says "no certificates provided". That is what the verifier returns to clients. This does not do RPs implementing persona support any favors. Can we do better with only a couple lines of code and make integration easier? Yes we can!

CODE_OF_CONDUCT.md file missing

As of January 1 2019, Mozilla requires that all GitHub projects include this CODE_OF_CONDUCT.md file in the project root. The file has two parts:

  1. Required Text - All text under the headings Community Participation Guidelines and How to Report, are required, and should not be altered.
  2. Optional Text - The Project Specific Etiquette heading provides a space to speak more specifically about ways people can work effectively and inclusively together. Some examples of those can be found on the Firefox Debugger project, and Common Voice. (The optional part is commented out in the raw template file, and will not be visible until you modify and uncomment that part.)

If you have any questions about this file, or Code of Conduct policies and procedures, please reach out to [email protected].

(Message COC001)

Missing PKCS#1 signature padding

Hi guys,

I'm the maintainer of the Nimbus JOSE+JWT Java library. A developer reported that RSA signatures generated by jwcrypto occasionally miss a padding character, which causes initial signature check to fail. The initial check ensures the signature length in bytes matches the key modulus length.

Here is the original issue: https://bitbucket.org/nimbusds/nimbus-jose-jwt/issue/72/signature-verification-crashing-with

It looks like the jwcrypto signing function doesn't apply the required padding once the signature is computed:

https://github.com/mozilla/jwcrypto/blob/master/libs/external/rsa-sign.js#L65

I found a padding function available in the jwcrypto code that perhaps could solve this:

https://github.com/mozilla/jwcrypto/blob/master/libs/external/rsa.js#L28

Cheers,

Vladimir

Rename to browserid-crypto

While this library was originally meant to be a general-purpose jwt/jws library, it has only ever been used by its authors as the crypto library behind browserid. As such, we should make it clear that we don't recommend using it for other (unsupported) use cases.

I'd like to propose renaming this repo (and the npm module) to browserid-crypto.

  • rename repo in github
  • update package.json
  • update README (make sure the code examples still work)
  • publish on npm (and fix maintainers)
  • file bug on Persona, auth-server, and oauth-server to update their dependency
  • submit PR to browserid-local-verify to switch to browserid-crypto
  • add deprecation note to jwcrypto on npm (may require another upload)

ECC support

This library is listed as not supporting ES256. If this is correct, then tag this as a feature request/enhancement.

improve check-assertion

check assertion is not ergonomic. I say "hello", and then it vomits on me:

$ bin/check-assertion 

undefined:1

^
SyntaxError: Unexpected token u
    at Object.parse (native)
    at Function.PublicKey.deserialize (/home/lth/dev/jwcrypto/lib/algs/index.js:72:18)
    at Object.exports.loadPublicKey (/home/lth/dev/jwcrypto/lib/jwcrypto.js:70:25)
    at Object.<anonymous> (/home/lth/dev/jwcrypto/bin/check-assertion:13:25)
    at Module._compile (module.js:449:26)
    at Object.Module._extensions..js (module.js:467:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.runMain (module.js:487:10)
    at process.startup.processNextTick.process._tickCallback (node.js:244:9)

In addition to basic usage output, here are some other things it could do better:

  1. assertions can be stored in files, they can be provided on the command line, they can be actual encoded assertions, or objects extracted from protocol messages. How hard would it be to handle whatever the user throws at you?
  2. public keys are worse than assertions. they are available in .well-known encoded as json. but they can also be encoded as base64, And really, why does the user even need to provide a public key? the protocol specifies how to get a public key given a domain (issuer)
  3. if the tool finds a problem, it should make it clear that it thinks its a problem. maybe prepend FATAL to the output?

add throttling to avoid long-running script errors for large keys/weak browsers

jwcrypto doesn't currently ever yield to the UI thread via setTimeout(), which means large keys (or crappy browsers like IE8 with not-necessarily-large keys) will lock up and/or throw the "long-running script" error. See callahad/mockmyid#4 for an example of this happening in real life.

The basic formula is simple, but making it work is tough: replace for loop with function call, increment a counter on each turn, yield to UI thread every so often.

Games are CPU-intensive but yield kindly to the UI thread, no reason we can't do the same here--especially if we want this library to be widely usable.

Ref on long-running scripts (5 million turns in IE) http://www.nczonline.net/blog/2009/01/05/what-determines-that-a-script-is-long-running/

Errors lack stack traces

Every error I've seen while working with jwcrypto lacks a stacktrace. I usually add new Error('foo') to debug these cases.

Please make functions like NoSuchAlgorithmException use Error.

mozilla/persona#2154

Not implemented: no such algorithm: DS

$ cat package.json
{
  "dependencies": {
    "jwcrypto": "0.5.0"
  }
}

Steps to reproduce

const jwcrypto = require('jwcrypto');

require("jwcrypto/lib/algs/rs");
require("jwcrypto/lib/algs/ds");

jwcrypto.generateKeypair({
    algorithm: "DS",
    keysize: 256
  }, function(err, keypair) {
    if (err) {
      return console.error(err);
    }
    console.log(Object.keys(keypair));
});

Actual results

$ node index

/Users/pdehaan/dev/tmp/jwcrypt/node_modules/jwcrypto/lib/algs/index.js:62
    throw new NotImplementedException("no such algorithm: " + alg);
          ^
Not implemented: no such algorithm: DS

Expected results

No error.

Workaround

Use "DSA" instead of "DS":

jwcrypto.generateKeypair({
    algorithm: "DSA", // <---------- this guy right here
    keysize: 256
  }, function(err, keypair) {
    if (err) {
      return console.error(err);
    }
    console.log(Object.keys(keypair));
});

Output:

$ node index
[ 'publicKey', 'secretKey', 'algorithm', 'keysize' ]

Windows 7 install issues

Reported by @jsmolina via email:

I am almost finishing my drupal module for browserid-IDP.

I was trying to launch the certifier on a Windows 7 machine, but:
"jwcrypto": "0.2.2",

This module has a dependency with bigint, which fails to be installed on windows because node-waf is missing:

node-waf configure build

"node-waf" no se reconoce como un comando interno o externo,
programa o archivo por lotes ejecutable.
npm http GET https://registry.npmjs.org/wordwrap
npm ERR! error rolling back Error: ENOTEMPTY, rmdir 'c:\browserid\mozilla-browse
rid-certifier-0e8c58b\node_modules\jwcrypto\node_modules\browserify\example\simp
le-build'

@lloyd @seanmonstar - Do you know if jwcrypto has been installed on Windows based machines? (I think you tested iconv recently, and this is another browserid dependency)

./bundle.sh generates bidbundle.js which doesn't export anything?

Right now, if you run ./bundle.sh, the resulting bidbundle.js file wraps everything except OVERRIDE in a closure, and doesn't export anything from the closure.

The resulting file doesn't seem to actually work?

If you remove the (function() {})() wrapper from the first and last lines, you get access to useful stuff, and the file behaves as expected.

I'm guessing browserify has some kind of option that could solve this, exporting the require object, and maybe some other objects, too?

RS and DS inclusion in node.js package

Neither RSA nor DSA are included in the default node.js package. Is this by design? Am I missing a proper way to use jwcrypto, that is not var jwcrypto = require('jwcrypto')?

This is fixed by adding two lines to index.js:

require("./lib/algs/ds");
require("./lib/algs/rs");

Reduce dependencies for production deployment

Assuming bundling is a developer build step that gets checked into git...

There are a bunch of dependencies that aren't strictly needed for a production runtime deployment.

"dependencies": {
    "browserify": "1.13.5",
    "vows": "0.5.13",
    "optimist": "0.2.6",
    "uglify-js": "1.0.6",
    "bigint": "https://github.com/benadida/node-bigint/tarball/2ac68"
},

I think this should be:

"dependencies": {
    "optimist": "0.2.6",
}

Note: bigint dependency is in both dependency and optionalDependencies... so assuming optionalDependencies is good enough.

Loading from objects on 0.5.x

I'm currently trying to wrap my head around jwcrypto and json[steak] or whatever the cool kids call it there days so you can assume that I'm missing some major points here.

Scenario

I'm trying to generate a DSA keypair, store it and at some point retrieve it and get back the secret and public keys. The serialization and deserialization methods seemed like a nice place to start so here goes.

Any tips on what I'm missing or doing wrong? Thanks :)

Issues

  1. When generating the keypair the algorithm required for the function to work is "DSA" but when using the loadSomethingFromObject(obj) the algorithm needs to be "DS". Other than the fact that my OCD is kicking in, it just seems that I'm doing something wrong here.

  2. The keysizes are null after loading the keys and the p & y bit sizes are not the same before and after the serialization/deserialization of the keys.
    ps. As you can imagine they keys don't seem to be the same either! :P Just saying.

Sample

var fs = require("fs");
var jwcrypto = require("jwcrypto");
var algs = require("jwcrypto/lib/algs");

require("jwcrypto/lib/algs/ds");

var keyPairObj = {
  algorithm: "DSA",
  keysize: 128
};

console.info("# Generating a new keypair.");

jwcrypto.generateKeypair(keyPairObj, function(err, keyPair) {
  if (err) { return console.error(err); }

  keyPair.secretKey.serializeToObject(keyPairObj);
  keyPair.publicKey.serializeToObject(keyPairObj);

  console.info("generated keyPair.secretKey.keysize is", keyPair.secretKey.keysize);
  console.info("generated keyPair.secretKey.p.bitLength() is", keyPair.secretKey.p.bitLength());
  console.info("generated keyPair.publicKey.keysize is", keyPair.publicKey.keysize);
  console.info("generated keyPair.publicKey.y.bitLength() is", keyPair.publicKey.y.bitLength());
  console.info("# Now using the serialized object.", JSON.stringify(keyPairObj));

  // Comment this to see the first issue.
  keyPairObj.algorithm = "DS";

  keyPair = new algs.KeyPair();
  keyPair.secretKey = jwcrypto.loadSecretKeyFromObject(keyPairObj);
  keyPair.publicKey = jwcrypto.loadPublicKeyFromObject(keyPairObj);

  console.info("loaded keyPair.secretKey.keysize is", keyPair.secretKey.keysize);
  console.info("loaded keyPair.secretKey.p.bitLength() is", keyPair.secretKey.p.bitLength());
  console.info("loaded keyPair.publicKey.keysize is", keyPair.publicKey.keysize);
  console.info("loaded keyPair.publicKey.y.bitLength() is", keyPair.publicKey.y.bitLength());
});

Output

# Generating a new keypair.
generated keyPair.secretKey.keysize is 128
generated keyPair.secretKey.p.bitLength() is 1024
generated keyPair.publicKey.keysize is 128
generated keyPair.publicKey.y.bitLength() is 1024
# Now using the serialized object. {"algorithm":"DSA","keysize":128,"x":"zZxiaf238dMD6LEgHlKDFpiySkY","p":"_2AEg9tqv8W0Xqt4WUs1M9VQ2fG_Kpkqeo2qbcNPgEWtTm4MQp0zTu6q79fiPUgQvgDkzBSSy6MluoH_LVpbMFqNF-s79KBqNJ05LgDTKXRKUXk4A0ToKhjEeTNDj4keIq7vgS1pyPdeMmy3DqAAw_d239vWBGOMLvcX_CbQLhc","q":"4h4E-RHR7XmRAI7Kqzv3dZhDCcM","g":"xSpKD_O35h_fGGfOhBODaaYVT0r6kpZuPIJ-Jc-mz1CLkOXeQZ4TN-B6Lp4qPNXepwTRdfjr9q85fWnhELlq-xfHoDJZMp5IKbDQO7x4lrFbSt5T4TCFjMNNliaaqJBB9AkTbHJCo4iVydW8ytTzia8dekvROYvQct_6iWIzOXo","y":"7veH-9ta3-bmxGrlhbiKKPYHu4pDy5O4Hff3iwNC-umWVvbIcxT7Df4wExaqUrrxnkoBDWoQNHWyLkHNZsyahLkAKDkghIa3_mgVaN-foIQdkEKcQDSechHCbhS-DsBwHiyyPEREZDhHlFOagBu5lUBel0MiKdgfkrsIwPHYaRE"}
loaded keyPair.secretKey.keysize is null
loaded keyPair.secretKey.p.bitLength() is 658
loaded keyPair.publicKey.keysize is null
loaded keyPair.publicKey.y.bitLength() is 659

Example / link to verifying signatures in the browser?

Sorry if the answer to this is obvious, but how does one verify the signatures that are generated by jwcrypto in the browser?

E.g. could someone give (or write up in the README or other documentation), and example of signing an arbitrary piece of data with jwcrypto, sending it to a client (browser), and having the browser verify the signature with its copy of the server's public key?

update readme

from the current readme:

NOTE: this is written as future documentation of v0.2 APIs, which will not be backwards compatible with v0.1.

I'm pretty sure that comment is misleading...but I'm also not sure how to update the doc so that it is truthful

Support DS160 alg in header

Currently, jwcrypto implements DS160 but calls it DS128 for backwards compatibility.

It should call it DS160 as well so we can verify assertions that proclaim DS160 as their algorithm.

See Bug 769851

Publish v0.5.1 to npm

It looks like we have a v0.5.1 tag in git, but the package is not on npm. Anything we need to take care of before publishing it?

plan for JOSE and new data formats

@callahad and I walked through this today. Our thoughts are:

  • as far as we know, nobody is using the "new-format" _20120815_serializeToObject() methods (by modifying version.DEFAULT_DATA_FORMAT_VERSION), so we will rip it out
  • we haven't deployed any new-format BrowserID code yet, so the only format that currently matters is the one defined in https://github.com/mozilla/id-specs/blob/prod/browserid/index.md , which uses jwcrypto's _LEGACY_serializeToObject() methods
  • we want existing code (namely browserid) that uses jwcrypto to continue getting the same formats as before
  • his plan (http://mozilla.github.io/id-specs/docs/formats/keys/) is to guide BrowserID towards using key and cert formats that are simple extensions of the upstream JOSE specs (http://tools.ietf.org/wg/jose/), which (as of today) appear to be getting close to finalized

So the first step is to extend jwcrypto to be capable of (but not defaulting to) serializing keys and certs in JOSE-format, and be able to parse both legacy (current BrowserID) and JOSE-format keys/certs (and provide a method to reveal which format it got). The API should let callers use a method name or argument to control which serialization format is used, rather than having them set a module-wide variable.

Then BrowserID can learn to use JOSE-format keys/certs, with whatever compatibility cleverness is necessary to accommodate various parts of the Persona ecosystem using old keys/certs (e.g. the verifier should handle either, but the keysigner should only emit JOSE-format certs if it is given a JOSE-format pubkey).

JOSE-format pubkeys ("JWK") have a mandatory kty (Key Type) property, while old-format BrowserID keys never do, which provides a distinguisher during deserialization. kty="RSA" is the most likely value. BrowserID keys always contain an alg property with values like "RS256" or "DS160" (where DSA keys are a non-JWA extension). For JOSE-format keys, alg is OPTIONAL.

We need to find a clean way to distinguish JOSE-format JWTs from old-format BrowserID certs. BrowserID currently uses a header with a single alg property. The JWS draft says that typ is OPTIONAL in headers, which is unfortunate for us, and alg is a MUST. We will probably have to distinguish formats based upon a property of the signed body.

jwcrypto should have side-effect free modules

In the lib/algs/ directory are node modules which must be required for their side effects.

Example:
https://github.com/mozilla/jwcrypto/blob/master/bin/certify#L12

Actual:

https://github.com/mozilla/jwcrypto/blob/master/lib/algs/ds.js#L298

Expected:

  1. jwcrypto should use it's register pattern from index.js, instead of each algorithm registering itself.

  2. or a high level module should be required that does this wiring

Otherwise all scripts have to do

require("../lib/algs/ds");
require("../lib/algs/rs");

If we add a new algorithm, then we have to touch N scripts.

Verification error

This is going to be fun.

This was generated by FxOS and sent to the Loop server. It seems to fail here, and I've logged the v and r values below.

@rfk, have we seen any "invalid signature" errors in Sync?

๐Ÿ” :~/git/jwcrypto/bin (master) $ ./check-assertion eyJhbGciOiJSUzI1NiJ9.eyJmeGEtZ2VuZXJhdGlvbiI6MTQxMTYzNTExOTM2OSwiZnhhLWxhc3RBdXRoQXQiOjE0MTIyNjE2MzQsImZ4YS12ZXJpZmllZEVtYWlsIjoibG9vcGZpcmVlQGdtYWlsLmNvbSIsInB1YmxpYy1rZXkiOnsiYWxnb3JpdGhtIjoiRFMiLCJ5IjoiYzg3MDBlMWY4ZDdjMDJlMTg2ZTEzMzRjYzBmZDRhY2Q4MGUxODIwYzZjMjQzMmY3MTIwMzY3YWU1NzAxZjU5ZmJkNjNkOWFjNzY5YWNjZGRmOGM3MjRlYzMzYTlmOTQ1OTlhZjY2MWI3YmM4ZDY3MDZhMGQ3YjQ1NDA5YzVjZmJmZjY4NDI2OTVlMzY0YjYxMjc2ZWI0ZDU3MDA2NWFkODY0NjdiMzAzMjQ1ZjYyMzQ4OWY0ZDU3MjgzNWIwYjM4MmRhYmNhZjUxNDY4NzY0ZTI2YjE2OWE1NDRkMTZkZmZlZTgwZTI5ZDc5MWM0MzVmNjA0MzM0NjU1OWFhZjMyZiIsInAiOiJmZjYwMDQ4M2RiNmFiZmM1YjQ1ZWFiNzg1OTRiMzUzM2Q1NTBkOWYxYmYyYTk5MmE3YThkYWE2ZGMzNGY4MDQ1YWQ0ZTZlMGM0MjlkMzM0ZWVlYWFlZmQ3ZTIzZDQ4MTBiZTAwZTRjYzE0OTJjYmEzMjViYTgxZmYyZDVhNWIzMDVhOGQxN2ViM2JmNGEwNmEzNDlkMzkyZTAwZDMyOTc0NGE1MTc5MzgwMzQ0ZTgyYTE4YzQ3OTMzNDM4Zjg5MWUyMmFlZWY4MTJkNjljOGY3NWUzMjZjYjcwZWEwMDBjM2Y3NzZkZmRiZDYwNDYzOGMyZWY3MTdmYzI2ZDAyZTE3IiwicSI6ImUyMWUwNGY5MTFkMWVkNzk5MTAwOGVjYWFiM2JmNzc1OTg0MzA5YzMiLCJnIjoiYzUyYTRhMGZmM2I3ZTYxZmRmMTg2N2NlODQxMzgzNjlhNjE1NGY0YWZhOTI5NjZlM2M4MjdlMjVjZmE2Y2Y1MDhiOTBlNWRlNDE5ZTEzMzdlMDdhMmU5ZTJhM2NkNWRlYTcwNGQxNzVmOGViZjZhZjM5N2Q2OWUxMTBiOTZhZmIxN2M3YTAzMjU5MzI5ZTQ4MjliMGQwM2JiYzc4OTZiMTViNGFkZTUzZTEzMDg1OGNjMzRkOTYyNjlhYTg5MDQxZjQwOTEzNmM3MjQyYTM4ODk1YzlkNWJjY2FkNGYzODlhZjFkN2E0YmQxMzk4YmQwNzJkZmZhODk2MjMzMzk3YSJ9LCJwcmluY2lwYWwiOnsiZW1haWwiOiI5MGY1ZmYxZmZmZTA0NTg5YTkwMWJkMTAyMWNmN2Q2Y0BhcGkuYWNjb3VudHMuZmlyZWZveC5jb20ifSwiaWF0IjoxNDEyMjYxNjI1NzM3LCJleHAiOjE0MTIyODMyMzU3MzcsImlzcyI6ImFwaS5hY2NvdW50cy5maXJlZm94LmNvbSJ9.SXucxBdwtx84O_ZRYU3DojUdqcDalb7TYvhMsoR-VglYe9wXyz-xYj7UVJGetJ1Iv_9Z6kVDFUC-cPAnFu_3mAaRlikF6xoFNHPTHmxWcOyYmhUKOAEsEoplkWxi4-KZZ2VToPM1Pi0Bhb0fMzvDQA27DpuEGjP2VMsctRoUi6W3qyADnubwW74expR2egVOmLnWqB8wrHfYXO1eAbj9Dki3sFIH4pIbAxIzelWc_zcnD_bxoMOmBveVZklj6cL887KLhHCpkQimQ_oboXmyti6XX-bGdVm1R-g5FbkgPChCABXadfYUYNGzw3PYMwdLD7n5FpMteAAUqBUt3elBbA~eyJhbGciOiJEUzEyOCJ9.eyJleHAiOjIyMDA2NjgyOTg5NjUsImF1ZCI6ImFwcDovL2xvb3Auc2VydmljZXMubW96aWxsYS5jb20ifQ==.GZ2DWiXahXBrItFTlKQaJ2eDpYGa9F-JGJYvSQX2qYIopquzkZanQg==
==== cert ====
issuer: api.accounts.firefox.com
principal: {"email":"[email protected]"}
iat: Thu Oct 02 2014 07:53:45 GMT-0700 (PDT) (1412261625737)
exp: Thu Oct 02 2014 13:53:55 GMT-0700 (PDT) (1412283235737)
(seeking public key! trying to fetch .well-known from api.accounts.firefox.com)
(using a median timestamp to ensure no timestamp failure)
cert is properly signed

==== assertion ====
{ header: { alg: 'DS128' },
  payload: { exp: 2200668298965, aud: 'app://loop.services.mozilla.com' },
  signature: '199d835a25da85706b22d15394a41a276783a5819af45f8918962f4905f6a98228a6abb39196a742',
  headerSegment: 'eyJhbGciOiJEUzEyOCJ9',
  payloadSegment: 'eyJleHAiOjIyMDA2NjgyOTg5NjUsImF1ZCI6ImFwcDovL2xvb3Auc2VydmljZXMubW96aWxsYS5jb20ifQ==',
  cryptoSegment: 'GZ2DWiXahXBrItFTlKQaJ2eDpYGa9F-JGJYvSQX2qYIopquzkZanQg==' }
audience: app://loop.services.mozilla.com
expires: Mon Sep 26 2039 09:44:58 GMT-0700 (PDT)
FATAL: assertion was likely issued after cert expired
(verifying with an expiration date that should be valid for this assertion.)
v= { _bigint: <BigInt 848809248273192254414407524785984902693848122582> } r= { _bigint: <BigInt 146237428651951442442213079183673724113648854401> }
FATAL: assertion is NOT properly signed: VerificationError: invalid signature

==== bundle ====
assuming public key is the key for api.accounts.firefox.com
FATAL: assertion is invalid: { [VerificationError: expired] message: 'expired' }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.