Key Wrap and Unwrap from RFC 3394
This commit is contained in:
parent
26c6ab9b97
commit
2f69d22130
|
@ -32,6 +32,7 @@
|
||||||
"test": "grunt test"
|
"test": "grunt test"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"aes": "^0.1.0",
|
||||||
"asmcrypto-lite": "^1.0.0",
|
"asmcrypto-lite": "^1.0.0",
|
||||||
"babel-core": "^6.26.0",
|
"babel-core": "^6.26.0",
|
||||||
"babel-preset-es2015": "^6.3.13",
|
"babel-preset-es2015": "^6.3.13",
|
||||||
|
|
|
@ -14,6 +14,7 @@ import signature from './signature';
|
||||||
import random from './random';
|
import random from './random';
|
||||||
import pkcs1 from './pkcs1';
|
import pkcs1 from './pkcs1';
|
||||||
import crypto from './crypto.js';
|
import crypto from './crypto.js';
|
||||||
|
import rfc3394 from './rfc3394.js';
|
||||||
|
|
||||||
const mod = {
|
const mod = {
|
||||||
/** @see module:crypto/cipher */
|
/** @see module:crypto/cipher */
|
||||||
|
@ -32,6 +33,8 @@ const mod = {
|
||||||
random: random,
|
random: random,
|
||||||
/** @see module:crypto/pkcs1 */
|
/** @see module:crypto/pkcs1 */
|
||||||
pkcs1: pkcs1,
|
pkcs1: pkcs1,
|
||||||
|
/** @see module:crypto/rfc3394 */
|
||||||
|
rfc3394: rfc3394,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (var i in crypto) {
|
for (var i in crypto) {
|
||||||
|
|
142
src/crypto/rfc3394.js
Normal file
142
src/crypto/rfc3394.js
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
// OpenPGP.js - An OpenPGP implementation in javascript
|
||||||
|
// Copyright (C) 2015-2016 Decentral
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Lesser General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 3.0 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
// Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
// Implementation of RFC 3394 Key Wrap & Key Unwrap funcions
|
||||||
|
|
||||||
|
import AES from 'aes';
|
||||||
|
|
||||||
|
function createArrayBuffer(data) {
|
||||||
|
if (typeof data === "string") {
|
||||||
|
var length = data.length;
|
||||||
|
var buffer = new ArrayBuffer(length);
|
||||||
|
var view = new Uint8Array(buffer);
|
||||||
|
for (var j = 0; j < length; ++j) {
|
||||||
|
view[j] = data.charCodeAt(j);
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
return new Uint8Array(data).buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function unpack(data) {
|
||||||
|
var length = data.length;
|
||||||
|
var buffer = createArrayBuffer(data);
|
||||||
|
var view = new DataView(buffer);
|
||||||
|
var arr = new Uint32Array(length/4);
|
||||||
|
for (var i=0; i<length/4; ++i) {
|
||||||
|
arr[i] = view.getUint32(4*i);
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pack() {
|
||||||
|
var length = 0;
|
||||||
|
for (var k=0; k<arguments.length; ++k) {
|
||||||
|
length += 4*arguments[k].length;
|
||||||
|
}
|
||||||
|
var buffer = new ArrayBuffer(length);
|
||||||
|
var view = new DataView(buffer);
|
||||||
|
var offset = 0;
|
||||||
|
for (var i=0; i<arguments.length; ++i) {
|
||||||
|
for (var j=0; j<arguments[i].length; ++j) {
|
||||||
|
view.setUint32(offset+4*j, arguments[i][j]);
|
||||||
|
}
|
||||||
|
offset += 4*arguments[i].length;
|
||||||
|
}
|
||||||
|
return new Uint8Array(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createCipher(key) {
|
||||||
|
var length = key.length;
|
||||||
|
var buffer = createArrayBuffer(key);
|
||||||
|
var view = new DataView(buffer);
|
||||||
|
key = new Array(length/4);
|
||||||
|
for (var i=0; i<length/4; ++i) {
|
||||||
|
key[i] = view.getUint32(4*i);
|
||||||
|
}
|
||||||
|
return new AES(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
function wrap(key, data) {
|
||||||
|
var IV = new Uint32Array([0xA6A6A6A6, 0xA6A6A6A6]);
|
||||||
|
var aes = createCipher(key);
|
||||||
|
var P = unpack(data);
|
||||||
|
var A = IV;
|
||||||
|
var R = P;
|
||||||
|
var n = P.length/2;
|
||||||
|
var t = new Uint32Array([0, 0]);
|
||||||
|
var B = new Uint32Array(4);
|
||||||
|
for (var j = 0; j <= 5; ++j) {
|
||||||
|
for (var i = 0; i < n; ++i) {
|
||||||
|
t[1] = n * j + (1 + i);
|
||||||
|
// B = A
|
||||||
|
B[0] = A[0];
|
||||||
|
B[1] = A[1];
|
||||||
|
// B = A || R[i]
|
||||||
|
B[2] = R[2*i];
|
||||||
|
B[3] = R[2*i+1];
|
||||||
|
// B = AES(K, B)
|
||||||
|
B = aes.encrypt(B);
|
||||||
|
// A = MSB(64, B) ^ t
|
||||||
|
A = B.subarray(0, 2);
|
||||||
|
A[0] = A[0] ^ t[0];
|
||||||
|
A[1] = A[1] ^ t[1];
|
||||||
|
// R[i] = LSB(64, B)
|
||||||
|
R[2*i] = B[2];
|
||||||
|
R[2*i+1] = B[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pack(A, R);
|
||||||
|
}
|
||||||
|
|
||||||
|
function unwrap(key, data) {
|
||||||
|
var IV = new Uint32Array([0xA6A6A6A6, 0xA6A6A6A6]);
|
||||||
|
var aes = createCipher(key);
|
||||||
|
var C = unpack(data);
|
||||||
|
var A = C.subarray(0, 2);
|
||||||
|
var R = C.subarray(2);
|
||||||
|
var n = C.length/2-1;
|
||||||
|
var t = new Uint32Array([0, 0]);
|
||||||
|
var B = new Uint32Array(4);
|
||||||
|
for (var j = 5; j >= 0; --j) {
|
||||||
|
for (var i = n - 1; i >= 0; --i) {
|
||||||
|
t[1] = n * j + (i + 1);
|
||||||
|
// B = A ^ t
|
||||||
|
B[0] = A[0] ^ t[0];
|
||||||
|
B[1] = A[1] ^ t[1];
|
||||||
|
// B = (A ^ t) || R[i]
|
||||||
|
B[2] = R[2*i];
|
||||||
|
B[3] = R[2*i+1];
|
||||||
|
// B = AES-1(B)
|
||||||
|
B = aes.decrypt(B);
|
||||||
|
// A = MSB(64, B)
|
||||||
|
A = B.subarray(0, 2);
|
||||||
|
// R[i] = LSB(64, B)
|
||||||
|
R[2*i] = B[2];
|
||||||
|
R[2*i+1] = B[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (A[0] === IV[0] && A[1] === IV[1]) {
|
||||||
|
return pack(R);
|
||||||
|
}
|
||||||
|
throw new Error("Key Data Integrity failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
wrap: wrap,
|
||||||
|
unwrap: unwrap
|
||||||
|
};
|
|
@ -3,4 +3,5 @@ describe('Crypto', function () {
|
||||||
require('./hash');
|
require('./hash');
|
||||||
require('./random.js');
|
require('./random.js');
|
||||||
require('./crypto.js');
|
require('./crypto.js');
|
||||||
|
require('./rfc3394.js');
|
||||||
});
|
});
|
||||||
|
|
59
test/crypto/rfc3394.js
Normal file
59
test/crypto/rfc3394.js
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp');
|
||||||
|
|
||||||
|
var expect = require('chai').expect;
|
||||||
|
|
||||||
|
describe('AES Key Wrap and Unwrap', function () {
|
||||||
|
var test_vectors = [
|
||||||
|
[
|
||||||
|
"128 bits of Key Data with a 128-bit KEK",
|
||||||
|
"000102030405060708090A0B0C0D0E0F",
|
||||||
|
"00112233445566778899AABBCCDDEEFF",
|
||||||
|
"1FA68B0A8112B447 AEF34BD8FB5A7B82 9D3E862371D2CFE5"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"128 bits of Key Data with a 192-bit KEK",
|
||||||
|
"000102030405060708090A0B0C0D0E0F1011121314151617",
|
||||||
|
"00112233445566778899AABBCCDDEEFF",
|
||||||
|
"96778B25AE6CA435 F92B5B97C050AED2 468AB8A17AD84E5D"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"128 bits of Key Data with a 256-bit KEK",
|
||||||
|
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||||
|
"00112233445566778899AABBCCDDEEFF",
|
||||||
|
"64E8C3F9CE0F5BA2 63E9777905818A2A 93C8191E7D6E8AE7"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"192 bits of Key Data with a 192-bit KEK",
|
||||||
|
"000102030405060708090A0B0C0D0E0F1011121314151617",
|
||||||
|
"00112233445566778899AABBCCDDEEFF0001020304050607",
|
||||||
|
"031D33264E15D332 68F24EC260743EDC E1C6C7DDEE725A93 6BA814915C6762D2"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"192 bits of Key Data with a 256-bit KEK",
|
||||||
|
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||||
|
"00112233445566778899AABBCCDDEEFF0001020304050607",
|
||||||
|
"A8F9BC1612C68B3F F6E6F4FBE30E71E4 769C8B80A32CB895 8CD5D17D6B254DA1"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"256 bits of Key Data with a 256-bit KEK",
|
||||||
|
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F",
|
||||||
|
"00112233445566778899AABBCCDDEEFF000102030405060708090A0B0C0D0E0F",
|
||||||
|
"28C9F404C4B810F4 CBCCB35CFB87F826 3F5786E2D80ED326 CBC7F0E71A99F43B FB988B9B7A02DD21"
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
test_vectors.forEach(function(test) {
|
||||||
|
it(test[0], function(done) {
|
||||||
|
var kek = openpgp.util.hex2bin(test[1]);
|
||||||
|
var input = test[2].replace(/\s/g, "");
|
||||||
|
var input_bin = openpgp.util.hex2bin(input);
|
||||||
|
var output = test[3].replace(/\s/g, "");
|
||||||
|
var output_bin = openpgp.util.hex2bin(output);
|
||||||
|
expect(openpgp.util.hexidump(openpgp.crypto.rfc3394.wrap(kek, input_bin)).toUpperCase()).to.equal(output);
|
||||||
|
expect(openpgp.util.hexidump(openpgp.crypto.rfc3394.unwrap(kek, output_bin)).toUpperCase()).to.equal(input);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user