4

Stanford Javascript Crypto Libraryを使用してOAuth2.0アサーションのCMAC-AESトークンを生成しようとしていますが、暗号化の専門家にはほど遠いです。誰かがsjclまたはそこにあるオープンライセンスのjsライブラリを使用した例を挙げられますか?sjclの既存の関数を使用できるかどうかさえわかりません。

この質問で見たようなオプションオブジェクトを使用してみましたが、モードやその他のオプションがわかりません。また、これに関するドキュメントも見つかりませんでした。ソルトとiv(再現可能なMACになるため)は静的である必要があると思いますが、それらがどのような値であるかはわかりません。

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

少し背景...これは、ネイティブモバイルアプリ(iOSおよびandroid)を単一のハイブリッドアプリ(Cordova、HTML、CSS、およびJavaScript)に書き直すためのものであり、JavaScriptでできるだけ多くのコードを共有したいと思います。 。これは「安全でない」ブラウザ環境ではなく、ネイティブWebViewで実行されます。他のセキュリティ問題をご存知の場合は、ぜひお聞かせください。

それ以外の場合は、ネイティブ実装からCMACを渡すためのプラグインを作成する必要があると思います。

言及するのを忘れて、私もDojoを使用していて、dojox.encoding.cryptoを知っていますが、問題は私にとっても同じです。CMACを実行できるかどうか、またはそれを機能させるためにどの程度の変更が必要かはわかりません。

4

2 に答える 2

5

今週末、このソリューションを考え出すのに時間がかかりすぎましたが、ようやく機能するようになりました。それがうまくいかない場所がいくつあるかは驚くべきことです。これはdojoAMDモジュールであるため、「require」を使用して簡単にロードできます。使用法は、次のテストベクトルによって示されます(通常の使用法ではサブキーテストを無視してください)。1つの癖:キーとメッセージを16進エンコードされた文字列として渡していることを確認してください。私はそれを少し親しみやすくし、utf8Stringでエンコードされた文字列も受け入れる予定ですが、sjcl.codecを使用すると、自分でそれを行うのはかなり簡単です。

特にパディング関数と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);
    }
});
});

これがテストベクトルです

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!");
}
于 2013-01-07T22:21:14.743 に答える
3

あなたはそれを見て、それを自分で実装することができます。AESプリミティブブロック暗号化が機能している場合は、CMACの実装が実行可能である必要があります。最も難しい部分は、JavaScript内にある他のタイプではなく、実際のバイトを使用することを確認することです。

AES CMACはNIST承認済みであるため、テスト要素に対してテストできます。

于 2013-01-04T02:43:14.117 に答える