Bumping manifests a=b2g-bump
[gecko.git] / toolkit / identity / jwcrypto.jsm
blob4bcba730ff722f34db01c79a7c16ed100ef990c7
1 /* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
2 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 "use strict";
10 const Cu = Components.utils;
11 const Ci = Components.interfaces;
12 const Cc = Components.classes;
13 const Cr = Components.results;
15 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
16 Cu.import("resource://gre/modules/Services.jsm");
17 Cu.import("resource://gre/modules/identity/LogUtils.jsm");
19 XPCOMUtils.defineLazyServiceGetter(this,
20                                    "IdentityCryptoService",
21                                    "@mozilla.org/identity/crypto-service;1",
22                                    "nsIIdentityCryptoService");
24 this.EXPORTED_SYMBOLS = ["jwcrypto"];
26 const ALGORITHMS = { RS256: "RS256", DS160: "DS160" };
27 const DURATION_MS = 1000 * 60 * 2; // 2 minutes default assertion lifetime
29 function log(...aMessageArgs) {
30   Logger.log.apply(Logger, ["jwcrypto"].concat(aMessageArgs));
33 function generateKeyPair(aAlgorithmName, aCallback) {
34   log("Generate key pair; alg =", aAlgorithmName);
36   IdentityCryptoService.generateKeyPair(aAlgorithmName, function(rv, aKeyPair) {
37     if (!Components.isSuccessCode(rv)) {
38       return aCallback("key generation failed");
39     }
41     var publicKey;
43     switch (aKeyPair.keyType) {
44      case ALGORITHMS.RS256:
45       publicKey = {
46         algorithm: "RS",
47         exponent:  aKeyPair.hexRSAPublicKeyExponent,
48         modulus:   aKeyPair.hexRSAPublicKeyModulus
49       };
50       break;
52      case ALGORITHMS.DS160:
53       publicKey = {
54         algorithm: "DS",
55         y: aKeyPair.hexDSAPublicValue,
56         p: aKeyPair.hexDSAPrime,
57         q: aKeyPair.hexDSASubPrime,
58         g: aKeyPair.hexDSAGenerator
59       };
60       break;
62     default:
63       return aCallback("unknown key type");
64     }
66     let keyWrapper = {
67       serializedPublicKey: JSON.stringify(publicKey),
68       _kp: aKeyPair
69     };
71     return aCallback(null, keyWrapper);
72   });
75 function sign(aPayload, aKeypair, aCallback) {
76   aKeypair._kp.sign(aPayload, function(rv, signature) {
77     if (!Components.isSuccessCode(rv)) {
78       log("ERROR: signer.sign failed");
79       return aCallback("Sign failed");
80     }
81     log("signer.sign: success");
82     return aCallback(null, signature);
83   });
86 function jwcryptoClass()
90 jwcryptoClass.prototype = {
91   /*
92    * Determine the expiration of the assertion.  Returns expiry date
93    * in milliseconds as integer.
94    *
95    * @param localtimeOffsetMsec (optional)
96    *        The number of milliseconds that must be added to the local clock
97    *        for it to agree with the server.  For example, if the local clock
98    *        if two minutes fast, localtimeOffsetMsec would be -120000
99    *
100    * @param now (options)
101    *        Current date in milliseconds.  Useful for mocking clock
102    *        skew in testing.
103    */
104   getExpiration: function(duration=DURATION_MS, localtimeOffsetMsec=0, now=Date.now()) {
105     return now + localtimeOffsetMsec + duration;
106   },
108   isCertValid: function(aCert, aCallback) {
109     // XXX check expiration, bug 769850
110     aCallback(true);
111   },
113   generateKeyPair: function(aAlgorithmName, aCallback) {
114     log("generating");
115     generateKeyPair(aAlgorithmName, aCallback);
116   },
118   /*
119    * Generate an assertion and return it through the provided callback.
120    *
121    * @param aCert
122    *        Identity certificate
123    *
124    * @param aKeyPair
125    *        KeyPair object
126    *
127    * @param aAudience
128    *        Audience of the assertion
129    *
130    * @param aOptions (optional)
131    *        Can include:
132    *        {
133    *          localtimeOffsetMsec: <clock offset in milliseconds>,
134    *          now: <current date in milliseconds>
135    *          duration: <validity duration for this assertion in milliseconds>
136    *        }
137    *
138    *        localtimeOffsetMsec is the number of milliseconds that need to be
139    *        added to the local clock time to make it concur with the server.
140    *        For example, if the local clock is two minutes fast, the offset in
141    *        milliseconds would be -120000.
142    *
143    * @param aCallback
144    *        Function to invoke with resulting assertion.  Assertion
145    *        will be string or null on failure.
146    */
147   generateAssertion: function(aCert, aKeyPair, aAudience, aOptions, aCallback) {
148     if (typeof aOptions == "function") {
149       aCallback = aOptions;
150       aOptions = { };
151     }
153     // for now, we hack the algorithm name
154     // XXX bug 769851
155     var header = {"alg": "DS128"};
156     var headerBytes = IdentityCryptoService.base64UrlEncode(
157                           JSON.stringify(header));
159     var payload = {
160       exp: this.getExpiration(
161                aOptions.duration, aOptions.localtimeOffsetMsec, aOptions.now),
162       aud: aAudience
163     };
164     var payloadBytes = IdentityCryptoService.base64UrlEncode(
165                           JSON.stringify(payload));
167     log("payload bytes", payload, payloadBytes);
168     sign(headerBytes + "." + payloadBytes, aKeyPair, function(err, signature) {
169       if (err)
170         return aCallback(err);
172       var signedAssertion = headerBytes + "." + payloadBytes + "." + signature;
173       return aCallback(null, aCert + "~" + signedAssertion);
174     });
175   }
179 this.jwcrypto = new jwcryptoClass();
180 this.jwcrypto.ALGORITHMS = ALGORITHMS;