she_encrypt
Version:
Cryptographic function for ciphering SHE commands args (M1-M3/M4-M5)
496 lines (454 loc) • 16.7 kB
JavaScript
/**
* SHE_encrypt.js
*
* This file provides a class calculating SHE command args for provisionning.
* SHE commands args registers are computed as illustrating here under:
*
* Keys:
* ====
* Knew is the key to be provisionned (a.k.a. K_mac)
* Kauth is the auth key used for key provisionning (a.k.a. K_master)
* KEY_UPDATE_ENC_C & KEY_UPDATE_MAC_C are ctes (see spec)
*
* - K1 = AesMP( KAuth | KEY_UPDATE_ENC_C)
* - K2 = AesMP( KAuth | KEY_UPDATE_MAC_C)
* - K3 = AesMP( Knew | KEY_UPDATE_ENC_C)
* - K4 = AesMP( Knew | KEY_UPDATE_MAC_C)
*
* Args registers:
* ==============
* UID is the unique ECU id (120bits)
* SheID is the slot number in which to persist key in ECU
* AuthID is the slot number for the Auth key in the ECU to use for provisionning
* CID is counter value (incr by one at each key persisted)
* FID is protection flags built as:
* WRITE_PROT_BIT|BOOT_PROT_BIT|DEBUG_PROT_BIT|KEY_USAGE|WILDCARD|CMAC_USAGE
* W B D K W CM
*
* W: if this flag is set “1”, key slot can’t be overwritten.
* B: if this flag is set to “1”, key can’t be used if the Secured
* boot process has not finished or failed.
* D: If this flag is to “1”, key can’t be used if the debugger is
* attached on device.
* K: Tells if a key can be used for cipher/decipher or MAC. Flag
* set means to be used for MAC generation/verification/
* W: UID can be replaced with Wild card value (zeros) when this
* flag is not set.
* CM: This flag is only checked if K is set otherwise it shall be “0”,
* If the flag is set the key can only be used for MAC verification
* otherwise used for Generation.
*
* +------------+---+---+---+---+---+---+---+
* | FID| W | B | D | K | W |Cma| C |
* |SHE k | | | | | |Usa| |
* +------------+---+---+---+---+---+---+---+
* |SECRET K | | x*| x*| | | | |
* +------------+---+---+---+---+---+---+---+
* |MASTER K | x | x | x | | x | | x |
* +------------+---+---+---+---+---+---+---+
* |BOOT MAC K | x | | x | | x | | x |
* +------------+---+---+---+---+---+---+---+
* |BOOT MAC | x | | x | | x | | x |
* +------------+---+---+---+---+---+---+---+
* |K {n} | x | x | x | x | x | x | x |
* +------------+---+---+---+---+---+---+---+
* |SECRET K | | | | | | | |
* +------------+---+---+---+---+---+---+---+
* |RAM K{page} | | | | | | | |
* +------------+---+---+---+---+---+---+---+
* | * use flag for master key |
* +----------------------------------------+
*
*
* - M1 = UID|SheID|AuthID
* - M2 = Aes_[CBC,K1,IV=0](CID^’┤|FID^’|“0...0"_{95}|KID’)
* - M3 = AesCMAC[K2](M1|M2)
* - M4 = M1|M4*
* - M4* = Aes_[ECB,K3](CID|"80...0"_{100})
* - M5 = AesCMAC[K4](M4)
*
* See "Secure Hardware Extesion Functional Specification v1.1"
* and Vector's "Technical Reference SHE Key Update Protocol"
*/
// Use an IIFE for namespace isolation
((root) => {
"use script";
/*jslint indent: 2, bitwise: false, nomen: false, plusplus: false, white: false, regexp: false */
/*global document, window, escape, unescape, module, require, Uint32Array */
const printf = require('printf');
const aesjs = require('aes-js');
const aesCmac = require('node-aes-cmac').aesCmac;
const MP = require('miyaguchipreneel');
/**
* SHE_encrypt
*
* constructor
*
*/
function SHE_encrypt(is_k_mac, k_master, key, cid, channel)
{
/* Handle optional arg */
if (channel === undefined)
channel = 1;
/* Ctes defs */
const KeyUpdateEncCte = Buffer.from("010153484500800000000000000000b0", 'hex');
const KeyUpdateMacCte = Buffer.from("010253484500800000000000000000b0", 'hex');
const bufferIV = Buffer.from("00000000000000000000000000000000", 'hex');
const M1_K_MASTER = Buffer.from("00000000000000000000000000000011", 'hex');
const M1_K_MAC_ch1 = Buffer.from("00000000000000000000000000000041", 'hex');
const M1_K_MAC_ch2 = Buffer.from("00000000000000000000000000000051", 'hex');
const M1_K_MAC_ch3 = Buffer.from("00000000000000000000000000000061", 'hex');
const M1_K_MAC_ch4 = Buffer.from("00000000000000000000000000000071", 'hex');
const M1_K_MAC_ch5 = Buffer.from("00000000000000000000000000000081", 'hex');
const CIDpadding = "8000000000000000000000000";
const M2_K_MASTER = "0000000000000000000000000";
const M2_K_MAC = "1000000000000000000000000";
const KEY_CONF_CTE = "4436F90004D9991EE22563CC967D2345";
/* Instanciate MiyaguchiPreneel compression function */
const mp = new MP();
var Kauth = k_master;
var Knew = key;
// Init internals
this.mp = mp;
this.KeyUpdateEncCte = KeyUpdateEncCte;
this.KeyUpdateMacCte = KeyUpdateMacCte;
this.bufferIV = bufferIV;
this.CIDpadding = CIDpadding;
this.M1_K_MASTER = M1_K_MASTER;
this.M1_K_MAC = M1_K_MAC_ch1;
this.M1_K_MAC_ch1 = M1_K_MAC_ch1;
this.M1_K_MAC_ch2 = M1_K_MAC_ch2;
this.M1_K_MAC_ch3 = M1_K_MAC_ch3;
this.M1_K_MAC_ch4 = M1_K_MAC_ch4;
this.M1_K_MAC_ch5 = M1_K_MAC_ch5;
this.M2_K_MASTER = M2_K_MASTER;
this.M2_K_MAC = M2_K_MAC;
this.channel = channel;
this.Kauth = Kauth;
this.Knew = Knew;
// Init prototype internals
SHE_encrypt.prototype.mp = mp;
SHE_encrypt.prototype.KeyUpdateEncCte = KeyUpdateEncCte;
SHE_encrypt.prototype.KeyUpdateMacCte = KeyUpdateMacCte;
SHE_encrypt.prototype.bufferIV = bufferIV;
SHE_encrypt.prototype.CIDpadding = CIDpadding;
SHE_encrypt.prototype.M1_K_MASTER = M1_K_MASTER;
SHE_encrypt.prototype.M1_K_MAC = M1_K_MAC_ch1;
SHE_encrypt.prototype.M1_K_MAC_ch1 = M1_K_MAC_ch1;
SHE_encrypt.prototype.M1_K_MAC_ch2 = M1_K_MAC_ch2;
SHE_encrypt.prototype.M1_K_MAC_ch3 = M1_K_MAC_ch3;
SHE_encrypt.prototype.M1_K_MAC_ch4 = M1_K_MAC_ch4;
SHE_encrypt.prototype.M1_K_MAC_ch5 = M1_K_MAC_ch5;
SHE_encrypt.prototype.M2_K_MASTER = M2_K_MASTER;
SHE_encrypt.prototype.M2_K_MAC = M2_K_MAC;
SHE_encrypt.prototype.channel = channel;
SHE_encrypt.prototype.Kauth = this.Kauth;
SHE_encrypt.prototype.Knew = this.Knew;
/*
* dk = KDF_ENC(k)
*
* Key Derivation Function used in the SHE protocol specification
* with the Enc cte from spec.
*/
SHE_encrypt.prototype.KDF_ENC = (k) =>
{
var key =
Buffer.concat(
[
(k instanceof Buffer ?
Buffer.from(k) :
Buffer.from(k, 'hex')
),
SHE_encrypt.prototype.KeyUpdateEncCte
]
);
return(
SHE_encrypt.prototype.mp.comp(
SHE_encrypt.prototype.bufferIV,
key
)
);
}
/*
* dk = KDF_MAC(k)
*
* Key Derivation Function used in the SHE protocol specification
* with the Mac cte from spec.
*/
SHE_encrypt.prototype.KDF_MAC = (k) =>
{
var key =
Buffer.concat(
[
(k instanceof Buffer ?
Buffer.from(k) :
Buffer.from(k, 'hex')
),
SHE_encrypt.prototype.KeyUpdateMacCte
]
);
return(
SHE_encrypt.prototype.mp.comp(
SHE_encrypt.prototype.bufferIV,
key
)
);
}
/*
* build_M2
*
* Build the M2 arg for the SHE trusted execution environment
*/
SHE_encrypt.prototype.build_M2 = (cid, key, is_k_mac) =>
{
var m2 = printf(
"%07x%s%s",
cid,
(is_k_mac ?
SHE_encrypt.prototype.M2_K_MAC:
SHE_encrypt.prototype.M2_K_MASTER),
key.toString('hex')
);
if (m2.length != 64)
throw "SHE_encrypt: Error: M2 length must be 32 bytes (hexa 64 characters)";
return(Buffer.from(m2, 'hex'));
}
/*
* encrypt_M2
*
* Cipher the M2 arg for the SHE trusted execution environment
*/
SHE_encrypt.prototype.encrypt_M2 = (msg, key) =>
{
var k1 = SHE_encrypt.prototype.KDF_ENC(key);
var aesCbc =
new aesjs.ModeOfOperation.cbc(
aesjs.utils.hex.toBytes(key.toString('hex')),
aesjs.utils.hex.toBytes(SHE_encrypt.prototype.bufferIV.toString('hex'))
);
var m2str = aesCbc.encrypt(
aesjs.utils.hex.toBytes(msg.toString('hex'))
);
var m2 = Buffer.from(m2str);
return(m2);
}
/*
* build_M3
*
* Build the M3 arg for the SHE trusted execution environment
*/
SHE_encrypt.prototype.build_M3 = (M1, M2, key) =>
{
console.info("M1 = %s", M1.toString('hex'));
console.info("M2 = %s", M2.toString('hex'));
console.info("key = %s", key.toString('hex'));
return(
aesCmac(
key,
Buffer.concat([M1, M2]),
{
returnAsBuffer: true
}
).toString('hex')
);
}
/*
* build_M4
*
* Build the M4 arg for the SHE trusted execution environment
*/
SHE_encrypt.prototype.build_M4 = (is_k_mac, cid, key, channel) =>
{
if (channel === undefined)
channel = 1;
var aesEcb =
new aesjs.ModeOfOperation.ecb(
aesjs.utils.hex.toBytes(SHE_encrypt.prototype.KDF_ENC(key).toString('hex'))
);
var m4str = aesEcb.encrypt(
aesjs.utils.hex.toBytes(printf("%07x%s", cid, SHE_encrypt.prototype.CIDpadding.toString('hex')))
);
var m1_k_mac = "";
if (is_k_mac)
switch (channel)
{
case 1:
m1_k_mac = SHE_encrypt.prototype.M1_K_MAC_ch1.toString('hex');
break;
case 2:
m1_k_mac = SHE_encrypt.prototype.M1_K_MAC_ch2.toString('hex');
break;
case 3:
m1_k_mac = SHE_encrypt.prototype.M1_K_MAC_ch3.toString('hex');
break;
case 4:
m1_k_mac = SHE_encrypt.prototype.M1_K_MAC_ch4.toString('hex');
break;
case 5:
m1_k_mac = SHE_encrypt.prototype.M1_K_MAC_ch5.toString('hex');
break;
default:
break;
}
var m1_k = is_k_mac ? m1_k_mac : SHE_encrypt.prototype.M1_K_MASTER.toString('hex');
return(
Buffer.from(
printf(
"%s%s",
m1_k,
Buffer.from(m4str).toString('hex')
),
'hex')
);
}
/*
* build_M5
*
* Build the M5 arg for the SHE trusted execution environment
*/
SHE_encrypt.prototype.build_M5 = (M4, key) =>
{
return(
aesCmac(
Buffer.from(SHE_encrypt.prototype.KDF_MAC(key).toString('hex'), 'hex'),
Buffer.from(M4, 'hex'),
{
returnAsBuffer: true
}
).toString('hex')
);
}
/*
* generate_SHE
*
* Generate the SHE trusted execution environment arguments for key
* provisionning.
* Command args M1 to M5 are generated
* M1, M2 and M3 are key provisionning args
* M4 and M5 are authentication args
*/
SHE_encrypt.prototype.generate_SHE = (is_k_mac, k_master, key, cid, channel) =>
{
var m1, m2, m3;
try {
if (is_k_mac)
{
switch (channel)
{
case 1:
m1 = SHE_encrypt.prototype.M1;
break;
case 2:
m1 = SHE_encrypt.prototype.M1;
break;
case 3:
m1 = SHE_encrypt.prototype.M1;
break;
case 4:
m1 = SHE_encrypt.prototype.M1;
break;
case 5:
m1 = SHE_encrypt.prototype.M1;
break;
}
}
else
m1 = SHE_encrypt.prototype.M1_K_MASTER;
m2 = SHE_encrypt.prototype.build_M2(cid, key, i_k_mac);
m2 = SHE_encrypt.prototype.encrypt_M2(m2, k_master);
m3 = SHE_encrypt.prototype.build_M3(m1, m2, k_master);
}
catch (err)
{
throw "k_master key not found !";
}
return(m1 + m2 + m3);
}
/*
* keyConfirmation
*
* Compute Key Confirmation as specified in RG MAC Spec reqs: MAC_PROV_01x
*/
SHE_encrypt.prototype.keyConfirmation = (k_mac) =>
{
return(
aesCmac(
(k_mac instanceof Buffer ?
k_mac :
Buffer.from(k_mac, 'hex')
),
Buffer.from(KEY_CONF_CTE, 'hex'),
{
returnAsBuffer: true
}
).toString('hex')
);
}
/*
* Init prototype/instance methods
*/
this.KDF_MAC = SHE_encrypt.prototype.KDF_MAC;
this.KDF_ENC = SHE_encrypt.prototype.KDF_ENC;
this.encrypt_M2 = SHE_encrypt.prototype.encrypt_M2;
this.build_M4 = SHE_encrypt.prototype.build_M4;
this.build_M3 = SHE_encrypt.prototype.build_M3;
this.build_M2 = SHE_encrypt.prototype.build_M2;
this.K1 = SHE_encrypt.prototype.K1 = this.KDF_ENC(this.Kauth);
this.K2 = SHE_encrypt.prototype.K2 = this.KDF_MAC(this.Kauth);
this.K3 = SHE_encrypt.prototype.K3 = this.KDF_ENC(this.Knew);
this.K4 = SHE_encrypt.prototype.K4 = this.KDF_MAC(this.Knew);
console.info("K1 = %s", this.K1.toString('hex'));
console.info("K2 = %s", this.K2.toString('hex'));
console.info("K3 = %s", this.K3.toString('hex'));
console.info("K4 = %s", this.K4.toString('hex'));
/*
* Initialize commands args M1 to M5
*/
switch (channel)
{
case 1:
this.M1 = SHE_encrypt.prototype.M1 = (is_k_mac ? this.M1_K_MAC_ch1 : this.M1_K_MASTER);
break;
case 2:
this.M1 = SHE_encrypt.prototype.M1 = (is_k_mac ? this.M1_K_MAC_ch2 : this.M1_K_MASTER);
break;
case 3:
this.M1 = SHE_encrypt.prototype.M1 = (is_k_mac ? this.M1_K_MAC_ch3 : this.M1_K_MASTER);
break;
case 4:
this.M1 = SHE_encrypt.prototype.M1 = (is_k_mac ? this.M1_K_MAC_ch4 : this.M1_K_MASTER);
break;
case 5:
this.M1 = SHE_encrypt.prototype.M1 = (is_k_mac ? this.M1_K_MAC_ch5 : this.M1_K_MASTER);
break;
}
this.M2 = SHE_encrypt.prototype.M2 = this.encrypt_M2(this.build_M2(cid, this.Knew, is_k_mac), this.K1);
this.M3 = SHE_encrypt.prototype.M3 = this.build_M3(this.M1, this.M2, this.K2);
this.M4 = SHE_encrypt.prototype.M4 = this.build_M4(is_k_mac, cid, this.Knew, channel);
this.M5 = SHE_encrypt.prototype.M5 = this.build_M5(this.M4, this.Knew);
return(this);
}
// NodeJS
if (typeof exports !== 'undefined')
{
exports.SHE_encrypt = SHE_encrypt;
exports.KDF_MAC = SHE_encrypt.prototype.KDF_MAC;
exports.KDF_ENC = SHE_encrypt.prototype.KDF_ENC;
exports.encrypt_M2 = SHE_encrypt.prototype.encrypt_M2;
exports.keyConfirmation = SHE_encrypt.prototype.keyConfirmation;
module.exports = SHE_encrypt;
}
// RequireJS/AMD
// http://www.requirejs.org/docs/api.html
// https://github.com/amdjs/amdjs-api/wiki/AMD
else if (typeof(define) === 'function' && define.amd)
{
define([], function() { return SHE_encrypt; });
}
// Web Browsers
else
{
root.SHE_encrypt = SHE_encrypt;
}
})(this);