UNPKG

she_encrypt

Version:

Cryptographic function for ciphering SHE commands args (M1-M3/M4-M5)

496 lines (454 loc) 16.7 kB
#!/usr/bin/env node /** * 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);