2013-01-04 9 views
4

J'essaye d'utiliser le Stanford Javascript Crypto Library pour générer un jeton CMAC-AES pour une assertion OAuth 2.0, mais je suis loin d'être un expert en cryptographie. Quelqu'un peut-il donner un exemple en utilisant sjcl ou une bibliothèque js open-license? Je ne suis même pas sûr que ce soit possible en utilisant les fonctions existantes de sjcl.Comment générer du CMAC-AES en javascript

J'ai essayé d'utiliser un objet d'options comme j'ai vu dans this question, mais je ne comprends pas les modes ou d'autres options, et je n'ai trouvé aucune documentation à ce sujet. Je suppose que le sel et iv (pour être un MAC reproductible) devraient être statiques, mais je ne sais pas quelles valeurs ils devraient être.

// is there a way that works? 
var token = sjcl.encrypt(secret,assertion,{salt:foo,iv:bar}); 

Un peu d'histoire ... ceci est pour une ré-écriture d'applications mobiles natives (iOS et Android) à une seule application hybride (Cordova, HTML, CSS et JavaScript), et je voudrais garder comme beaucoup de code partagé en JavaScript que possible. Cela ne fonctionnera pas dans un environnement de navigateur "non sécurisé", mais plutôt dans les WebViews natifs. Si vous connaissez d'autres problèmes de sécurité, s'il vous plaît, j'aimerais les entendre. Sinon, je suppose que je vais devoir écrire un plugin pour passer le CMAC à partir d'implémentations natives.

Oublié de mentionner, j'utilise aussi Dojo et je suis au courant de dojox.encoding.crypto mais le problème est le même pour moi. Je ne sais pas si cela peut faire du CMAC ou combien il faudrait de modifications pour le faire fonctionner.

+0

SJCL utilise les modes (CCM) qui incluent la protection de l'intégrité, donc il y a peu de raisons pour eux d'inclure des algorithmes MAC. –

Répondre

4

J'ai passé trop de temps ce week-end à trouver cette solution, mais j'ai finalement réussi à la faire fonctionner. C'est incroyable combien d'endroits ça peut aller mal. Ceci est un module AMD dojo donc il peut facilement être chargé en utilisant un "require". L'utilisation est démontrée par les vecteurs de test qui suivent (ignorer les tests de sous-clé en utilisation normale). Une anomalie: assurez-vous que vous transmettez la clé et les messages sous forme de chaînes codées hexadécimales. Je prévois de le rendre un peu plus convivial et accepter les chaînes codées utf8String, mais avec sjcl.codec, il est assez facile de le faire seul.

J'apprécierais toute rétroaction, en particulier pour la fonction de remplissage et mon utilisation des méthodes bitArray.

// in crypto/AesCmac.js 
define([ 
    "dojo/_base/declare" 
], function(declare) { 
return declare(null, { 

    /** 
    * This mostly follows the AES-128 CMAC algorithm found here. 
    * 
    * @see http://tools.ietf.org/html/rfc4493 
    * 
    * 
    * This module has a dependency on The Stanford Javascript Crypto Library. If using the 
    * minified build, be sure to add sjcl.mode.cbc object into the namespace since I guess it's 
    * too "dangerous" to include. 
    * 
    * @see http://crypto.stanford.edu/sjcl/ 
    * 
    * 
    * In JavaScript, all numbers are 64 bit floating point. The bitwise operators treat numbers 
    * as 32bit integers. But we're not guaranteed all 32 bits. ~0=-1 instead of 4,294,967,295. 
    * So for most of these bit operations we're using sjcl.bitArray to do the dirty work. 
    * 
    * @see http://www.hunlock.com/blogs/The_Complete_Javascript_Number_Reference 
    * 
    * 
    * The padding function is described here. 
    * @see http://www.cardwerk.com/smartcards/smartcard_standard_ISO7816-4_5_basic_organizations.aspx#chap5_6_3_1 
    */ 

    const_Bsize : 128, // in bits! not octets (16) 
    const_Zero : sjcl.codec.hex.toBits("0x00000000000000000000000000000000"), 
    const_Rb : sjcl.codec.hex.toBits("0x00000000000000000000000000000087"), 
    aesCipher : {}, 

    init : function(key) { 
     var keyBits = sjcl.codec.hex.toBits(key); 
     this.aesCipher = new sjcl.cipher.aes(keyBits); 
    }, 

    xor4Words : function(x, y) { 
     return [ 
       x[0]^y[0], x[1]^y[1], x[2]^y[2], x[3]^y[3] 
     ]; 
    }, 

    simpleShiftLeft : function(a, shift) { 
     return sjcl.bitArray.bitSlice(sjcl.bitArray.concat(a, [ 
      0 
     ]), shift, this.const_Bsize + shift); 
    }, 

    iso7816d4Padding : function(m) { 
     var bitLength = sjcl.bitArray.bitLength(m); 
     m = this.xor4Words(m, this.const_Zero); 
     var gap = this.const_Bsize - bitLength; 
     if (gap < 8) 
      return m; 
     var startWord = Math.floor(bitLength/32); 
     var startByte = Math.ceil((bitLength % 32)/8); // 0,1,2,3,4 
     if (startByte == 4) { 
      console.log("rolled over into next word"); 
      startWord++; 
      startByte = 0; 
      if (startWord == 4) { 
       // this should have been caught above on gap check 
       console.warn("this shouldn't ever happen"); 
       return m; 
      } 
     } 
     var last32 = m[startWord]; 
     // startByte: 0->2^31, 1->2^23, 2->2^15, 3->2^7 
     var bitmask = Math.pow(2, (4 - startByte) * 8 - 1) 
     last32 |= bitmask; 
     m[startWord] = last32; 
     return m; 
    }, 

    _encrypt : function(m) { 
     return sjcl.bitArray.clamp(sjcl.mode.cbc.encrypt(this.aesCipher, m, this.const_Zero), 
       this.const_Bsize); 
    }, 

    generateSubkeys : function() { 
     // Step 1 
     var L = this._encrypt(this.const_Zero); 

     // Step 2 
     var msbNeg = L[0] & 0x80000000; 
     var K1 = this.simpleShiftLeft(L, 1, 0); 
     if (msbNeg) { 
      K1 = this.xor4Words(K1, this.const_Rb); 
     } 

     // Step 3 
     msbNeg = K1[0] & 0x80000000; 
     var K2 = this.simpleShiftLeft(K1, 1, 0); 
     if (msbNeg) { 
      K2 = this.xor4Words(K2, this.const_Rb); 
     } 

     // Step 4 
     return { 
      "K1" : K1, 
      "K2" : K2 
     }; 
    }, 

    generateCmac : function(plainText) { 
     // Step 1 
     var subkeys = this.generateSubkeys(); 

     // Step 2 
     var M = sjcl.codec.hex.toBits(plainText); 
     var len = sjcl.bitArray.bitLength(M); // in bits! not octets 
     var n = Math.ceil(len/this.const_Bsize); 

     // Step 3 
     var lastBlockComplete; 
     if (n == 0) { 
      n = 1; 
      lastBlockComplete = false; 
     } else { 
      if (len % this.const_Bsize == 0) 
       lastBlockComplete = true; 
      else 
       lastBlockComplete = false; 
     } 

     // Step 4 
     var lastStart = (n - 1) * this.const_Bsize; 
     var M_last = sjcl.bitArray.bitSlice(M, lastStart); 
     if (lastBlockComplete) { 
      M_last = this.xor4Words(M_last, subkeys["K1"]); 
     } else { 
      M_last = this.iso7816d4Padding(M_last); 
      M_last = this.xor4Words(M_last, subkeys["K2"]); 
     } 

     // Step 5 
     var X = this.const_Zero; 
     var Y; 

     // Step 6 
     for (var i = 1; i <= n - 1; i++) { 
      var start = (i - 1) * this.const_Bsize; 
      var end = i * this.const_Bsize; 
      var M_i = sjcl.bitArray.bitSlice(M, start, end); 
      Y = this.xor4Words(X, M_i); 
      X = this._encrypt(Y); 
     } 
     Y = this.xor4Words(M_last, X); 
     // Step 7 
     return this._encrypt(Y); 
    } 
}); 
}); 

Voici les vecteurs de test

function testAesCmac() { 
/** 
* <pre> 
* Subkey Generation 
* K    2b7e1516 28aed2a6 abf71588 09cf4f3c 
* AES-128(key,0) 7df76b0c 1ab899b3 3e42f047 b91b546f 
* K1    fbeed618 35713366 7c85e08f 7236a8de 
* K2    f7ddac30 6ae266cc f90bc11e e46d513b 
* </pre> 
*/ 
var AesCmac = require("crypto/AesCmac"); 
var cmac = new AesCmac(); 
cmac.init("0x2b7e151628aed2a6abf7158809cf4f3c"); 

// Test AES-128 on zero initialization vector 
var t_0 = cmac._encrypt(cmac.const_Zero); 
var aes_0 = sjcl.codec.hex.toBits("0x7df76b0c1ab899b33e42f047b91b546f"); 
sjcl.bitArray.equal(t_0, aes_0) ? console.log("AES test passed!") : console 
     .error("AES test failed!"); 

// Test subkey equality 
var subkeys = cmac.generateSubkeys(); 
var K1 = sjcl.codec.hex.toBits("0xfbeed618357133667c85e08f7236a8de"); 
sjcl.bitArray.equal(subkeys["K1"], K1) ? console.log("K1 test passed!") : console 
     .error("K1 test failed!"); 
var K2 = sjcl.codec.hex.toBits("0xf7ddac306ae266ccf90bc11ee46d513b"); 
sjcl.bitArray.equal(subkeys["K2"], K2) ? console.log("K2 test passed!") : console 
     .error("K2 test failed!"); 

/** 
* <pre> 
* Example 1: len = 0 
* M    &lt;empty string&gt; 
* AES-CMAC  bb1d6929 e9593728 7fa37d12 9b756746 
* </pre> 
*/ 
var m1 = ""; 
var cmac1 = cmac.generateCmac(m1); 
var ex1 = sjcl.codec.hex.toBits("0xbb1d6929e95937287fa37d129b756746") 
sjcl.bitArray.equal(ex1, cmac1) ? console.log("cmac1 test passed!") : console 
     .error("cmac1 test failed!"); 
if (sjcl.codec.hex.fromBits(cmac1) !== "bb1d6929e95937287fa37d129b756746") 
    console.error(sjcl.codec.hex.fromBits(cmac1) + " !== bb1d6929e95937287fa37d129b756746"); 

/** 
* <pre> 
* Example 2: len = 16 
* M    6bc1bee2 2e409f96 e93d7e11 7393172a 
* AES-CMAC  070a16b4 6b4d4144 f79bdd9d d04a287c 
* </pre> 
*/ 
var m2 = "0x6bc1bee22e409f96e93d7e117393172a"; 
var cmac2 = cmac.generateCmac(m2); 
var ex2 = sjcl.codec.hex.toBits("0x070a16b46b4d4144f79bdd9dd04a287c") 
sjcl.bitArray.equal(ex2, cmac2) ? console.log("cmac2 test passed!") : console 
     .error("cmac2 test failed!"); 

/** 
* <pre> 
* Example 3: len = 40 
* M    6bc1bee2 2e409f96 e93d7e11 7393172a 
*    ae2d8a57 1e03ac9c 9eb76fac 45af8e51 
*    30c81c46 a35ce411 
* AES-CMAC  dfa66747 de9ae630 30ca3261 1497c827 
* </pre> 
*/ 
var m3 = "0x6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411"; 
var cmac3 = cmac.generateCmac(m3); 
var ex3 = sjcl.codec.hex.toBits("0xdfa66747de9ae63030ca32611497c827") 
sjcl.bitArray.equal(ex3, cmac3) ? console.log("cmac3 test passed!") : console 
     .error("cmac3 test failed!"); 

/** 
* <pre> 
* Example 4: len = 64 
* M    6bc1bee2 2e409f96 e93d7e11 7393172a 
*    ae2d8a57 1e03ac9c 9eb76fac 45af8e51 
*    30c81c46 a35ce411 e5fbc119 1a0a52ef 
*    f69f2445 df4f9b17 ad2b417b e66c3710 
* AES-CMAC  51f0bebf 7e3b9d92 fc497417 79363cfe 
* </pre> 
*/ 
var m4 = "0x6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51" 
     + "30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710"; 
var cmac4 = cmac.generateCmac(m4); 
var ex4 = sjcl.codec.hex.toBits("0x51f0bebf7e3b9d92fc49741779363cfe") 
sjcl.bitArray.equal(ex4, cmac4) ? console.log("cmac4 test passed!") : console 
     .error("cmac4 test failed!"); 
} 
+0

Merci de nous avoir signalé que, LilPickle, vous pouvez accepter votre réponse après un certain temps. Vous savez, je vais vous accorder quelques points pour cela. –

3

Vous pourriez jeter un coup d'œil et l'implémenter vous-même. L'implémentation de CMAC devrait être faisable si vous avez un chiffrement de bloc de primitive AES qui fonctionne. La partie la plus difficile est probablement de s'assurer que vous utilisez des octets réels au lieu de tout autre type qui est dans JavaScript.

AES CMAC est approuvé par NIST afin que vous puissiez tester par rapport à test factors.

+0

jsCryptolib est la première bibliothèque que j'ai examinée lors de ma première recherche sur Google. Elle indique que le CMAC est pris en charge mais je n'ai pas pu déterminer son utilisation ni trouver de référence à CMAC dans la source. J'ai recherché tout le téléchargement et cela m'a laissé perplexe. Pourriez-vous s'il vous plaît fournir un exemple? Malheureusement, je doute de ma capacité à l'implémenter moi-même. Merci pour l'aide. – LilPickle3000

+0

Impossible de le trouver non plus. Quel tas de merde, désolé je l'ai recommandé. –

+0

Merci pour l'assistance @owlstead. Certaines de vos autres réponses autour de SO ont également aidé. – LilPickle3000

Questions connexes