如何在javascript中生成CMAC-AES [英] How can I generate CMAC-AES in javascript

查看:219
本文介绍了如何在javascript中生成CMAC-AES的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用斯坦福Javascript加密库生成一个CMAC-AES令牌,用于OAuth 2.0断言,但我远不是一个密码学专家。有人可以给一个例子使用sjcl或任何开放许可证的js库吗?我不确定可能使用sjcl的现有函数。

I am attempting to use the Stanford Javascript Crypto Library to generate an CMAC-AES token for an OAuth 2.0 assertion, but I'm far from being an expert with cryptography. Can someone give an example using sjcl or any open-license js library out there? I'm not even sure it's possible using sjcl's existing functions.

我尝试使用一个选项对象,如我在这个问题,但我不明白模式或其他选项,我找不到任何文档。我认为盐和iv(一个可复制的MAC)必须是静态的,但我不知道它们应该是什么值。

I tried using an options object like I saw in this question, but I don't understand the modes or other options, and I couldn't find any documentation on this. I figure the salt and iv (to be a reproduceable MAC) would have to be static, but I don't know what values they should be.

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

一点背景...这是为了重写本机移动应用一个单一的混合应用程序(Cordova,HTML,CSS和JavaScript),我想保持尽可能多的代码在JavaScript中共享尽可能。这不会在不安全的浏览器环境中运行,而是本地WebViews。如果你知道其他安全问题,我想听听他们。

A little background... this is for a rewrite of native mobile apps (iOS and android) to a single hybrid app (Cordova, HTML, CSS, and JavaScript), and I would like to keep as much code shared in JavaScript as possible. This won't be running in an "insecure" browser environment, but rather the native WebViews. If you know of other security issues, please I'd like to hear them.

否则我想我必须编写一个插件从本机实现传递CMAC 。

Otherwise I guess I'll have to write a plugin to pass the CMAC from native implementations.

忘了提及,我也使用Dojo,我知道dojox.encoding.crypto,但问题是一样的。

Forgot to mention, I'm also using Dojo and I'm aware of dojox.encoding.crypto but the problem is the same for me. Not sure if it can do CMAC or how much modification it would take to make it work.

推荐答案

这个周末花了太多时间提出这个解决方案,但我终于得到它的工作。它是惊人的有多少地方它可以出错。这是一个dojo AMD模块,所以它可以很容易地使用需要加载。用法由后面的测试向量演示(忽略正常使用中的子项测试)。一个怪癖:确保你传递的键和消息作为十六进制编码字符串。我计划让它有点友好,并接受utf8String编码的字符串,但与sjcl.codec它是很容易做到自己。

Spent too much time this weekend coming up with this solution, but I finally got it working. It's amazing how many places it can go wrong. This is a dojo AMD module so it can easily be loaded using a "require". Usage is demonstrated by the test vectors that follow (ignore the subkey tests in normal usage). One quirk: make sure you are passing the key and messages as hex encoded strings. I plan to make it a little friendlier and accept utf8String encoded strings as well, but with sjcl.codec it is fairly easy to do that on your own.

欣赏任何反馈,特别是对于填充函数和我对bitArray方法的使用。

I'd appreciate any feedback, especially for the padding function and my usage of the bitArray methods.

// 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!");
}

这篇关于如何在javascript中生成CMAC-AES的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆