Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TLS 1.2 implementation #534

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 48 additions & 1 deletion lib/aesCipherSuites.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,43 @@ var tls = module.exports = forge.tls;
/**
* Supported cipher suites.
*/
// TLS 1.2
tls.CipherSuites['TLS_RSA_WITH_AES_128_CBC_SHA256'] = {
id: [0x00,0x3c],
name: 'TLS_RSA_WITH_AES_128_CBC_SHA256',
initSecurityParameters: function(sp) {
console.log('----- AES128 initSecurityParameters()')
sp.bulk_cipher_algorithm = tls.BulkCipherAlgorithm.aes;
sp.cipher_type = tls.CipherType.block;
sp.enc_key_length = 16;
sp.block_length = 16;
sp.fixed_iv_length = 16;
sp.record_iv_length = 16;
sp.mac_algorithm = tls.MACAlgorithm.hmac_sha256;
sp.mac_length = 32;
sp.mac_key_length = 32;
},
initConnectionState: initConnectionState
};
// TLS 1.2
tls.CipherSuites['TLS_RSA_WITH_AES_256_CBC_SHA256'] = {
id: [0x00,0x3d],
name: 'TLS_RSA_WITH_AES_256_CBC_SHA256',
initSecurityParameters: function(sp) {
console.log('----- AES256 initSecurityParameters()')
sp.bulk_cipher_algorithm = tls.BulkCipherAlgorithm.aes;
sp.cipher_type = tls.CipherType.block;
sp.enc_key_length = 32;
sp.block_length = 16;
sp.fixed_iv_length = 16;
sp.record_iv_length = 16;
sp.mac_algorithm = tls.MACAlgorithm.hmac_sha256;
sp.mac_length = 32;
sp.mac_key_length = 32;
},
initConnectionState: initConnectionState
};

tls.CipherSuites['TLS_RSA_WITH_AES_128_CBC_SHA'] = {
id: [0x00,0x2f],
name: 'TLS_RSA_WITH_AES_128_CBC_SHA',
Expand Down Expand Up @@ -48,6 +85,7 @@ tls.CipherSuites['TLS_RSA_WITH_AES_256_CBC_SHA'] = {
initConnectionState: initConnectionState
};


function initConnectionState(state, c, sp) {
var client = (c.entity === forge.tls.ConnectionEnd.client);

Expand All @@ -69,7 +107,16 @@ function initConnectionState(state, c, sp) {

// MAC setup
state.read.macLength = state.write.macLength = sp.mac_length;
state.read.macFunction = state.write.macFunction = tls.hmac_sha1;
switch(sp.mac_algorithm) {
case tls.MACAlgorithm.hmac_sha256:
state.read.macFunction = state.write.macFunction = tls.hmac_sha256;
break;
case tls.MACAlgorithm.hmac_sha1:
state.read.macFunction = state.write.macFunction = tls.hmac_sha1;
break;
default:
throw new Exception('Unknown HMAC function');
}
}

/**
Expand Down
140 changes: 111 additions & 29 deletions lib/tls.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ require('./pem');
require('./pki');
require('./random');
require('./sha1');
require('./sha256');
require('./util');

/**
Expand Down Expand Up @@ -357,8 +358,37 @@ var prf_TLS1 = function(secret, label, seed, length) {
*
* @return the pseudo random bytes in a byte buffer.
*/
var prf_sha256 = function(secret, label, seed, length) {
// FIXME: implement me for TLS 1.2
var prf_TLS12 = function(secret, label, seed, length, hashType) {
var rval = forge.util.createBuffer();

var ai = forge.util.createBuffer();
var shaBytes = forge.util.createBuffer();
var hmac = forge.hmac.create();
seed = label + seed;

var shaItr = Math.ceil(length / 32);

hmac.start(hashType, secret);
ai.clear();
ai.putBytes(seed);
for(var i = 0; i < shaItr; ++i) {
// HMAC_hash(secret, A(i-1))
hmac.start(null, null);
hmac.update(ai.getBytes());
ai.putBuffer(hmac.digest());

// HMAC_hash(secret, A(i) + seed)
hmac.start(null, null);
hmac.update(ai.bytes() + seed);
shaBytes.putBuffer(hmac.digest());
}
rval.putBytes(shaBytes.getBytes(length));

return rval;
};

var prf_TLS12_sha256 = function(secret, label, seed, length) {
return prf_TLS12(secret, label, seed, length, "SHA256");
};

/**
Expand Down Expand Up @@ -393,6 +423,21 @@ var hmac_sha1 = function(key, seqNum, record) {
return hmac.digest().getBytes();
};

var hmac_sha256 = function(key, seqNum, record) {
var hmac = forge.hmac.create();
hmac.start('SHA256', key);
var b = forge.util.createBuffer();
b.putInt32(seqNum[0]);
b.putInt32(seqNum[1]);
b.putByte(record.type);
b.putByte(record.version.major);
b.putByte(record.version.minor);
b.putInt16(record.length);
b.putBytes(record.fragment.bytes());
hmac.update(b.getBytes());
return hmac.digest().getBytes();
};

/**
* Compresses the TLSPlaintext record into a TLSCompressed record using the
* deflate algorithm.
Expand Down Expand Up @@ -511,6 +556,7 @@ tls.Versions = {
TLS_1_2: {major: 3, minor: 3}
};
tls.SupportedVersions = [
tls.Versions.TLS_1_2,
tls.Versions.TLS_1_1,
tls.Versions.TLS_1_0
];
Expand Down Expand Up @@ -708,6 +754,10 @@ tls.HeartbeatMessageType = {
*/
tls.CipherSuites = {};

tls.usingTLS12 = function (c) {
return (c.session.version.major === tls.Versions.TLS_1_2.major &&
c.session.version.minor === tls.Versions.TLS_1_2.minor);
};
/**
* Gets a supported cipher suite from its 2 byte ID.
*
Expand Down Expand Up @@ -926,6 +976,7 @@ tls.createSecurityParameters = function(c, msg) {
/* Note: security params are from TLS 1.2, some values like prf_algorithm
are ignored for TLS 1.0/1.1 and the builtin as specified in the spec is
used. */
var tls12 = tls.usingTLS12(c);

// TODO: handle other options from server when more supported

Expand Down Expand Up @@ -1005,7 +1056,6 @@ tls.handleServerHello = function(c, record, length) {

// indicate session version has been set
c.session.version = c.version;

// get the session ID from the message
var sessionId = msg.session_id.bytes();

Expand Down Expand Up @@ -1477,6 +1527,8 @@ tls.handleClientKeyExchange = function(c, record, length) {
* @param length the length of the handshake message.
*/
tls.handleCertificateRequest = function(c, record, length) {
var tls12 = tls.usingTLS12(c);

// minimum of 3 bytes in message
if(length < 3) {
return c.error(c, {
Expand Down Expand Up @@ -1800,6 +1852,7 @@ tls.handleChangeCipherSpec = function(c, record) {
* @param length the length of the handshake message.
*/
tls.handleFinished = function(c, record, length) {
var tls12 = tls.usingTLS12(c);
// rewind to get full bytes for message so it can be manually
// digested below (special case for Finished messages because they
// must be digested *after* handling as opposed to all others)
Expand All @@ -1810,20 +1863,32 @@ tls.handleFinished = function(c, record, length) {

// message contains only verify_data
var vd = record.fragment.getBytes();
var prf = prf_TLS1;

// ensure verify data is correct
b = forge.util.createBuffer();
b.putBuffer(c.session.md5.digest());
b.putBuffer(c.session.sha1.digest());
if(tls12) {
// TODO: Add support for SHA224 and SHA512.
prf = prf_TLS12_sha256;
switch(c.session.sp.mac_algorithm) {
case tls.MACAlgorithm.hmac_sha256:
case tls.MACAlgorithm.hmac_sha1:
b.putBuffer(c.session.sha256.digest());
break;
default:
throw new Exception('Bad MACAlgorithm');
}
} else {
b.putBuffer(c.session.md5.digest());
b.putBuffer(c.session.sha1.digest());
}

// set label based on entity type
var client = (c.entity === tls.ConnectionEnd.client);
var label = client ? 'server finished' : 'client finished';

// TODO: determine prf function and verify length for TLS 1.2
var sp = c.session.sp;
var vdl = 12;
var prf = prf_TLS1;
b = prf(sp.master_secret, label, b.getBytes(), vdl);
if(b.getBytes() !== vd) {
return c.error(c, {
Expand All @@ -1839,6 +1904,7 @@ tls.handleFinished = function(c, record, length) {
// digest finished message now that it has been handled
c.session.md5.update(msgBytes);
c.session.sha1.update(msgBytes);
c.session.sha256.update(msgBytes);

// resuming session as client or NOT resuming session as server
if((c.session.resuming && client) || (!c.session.resuming && !client)) {
Expand Down Expand Up @@ -2044,7 +2110,8 @@ tls.handleHandshake = function(c, record) {
serverCertificate: null,
clientCertificate: null,
md5: forge.md.md5.create(),
sha1: forge.md.sha1.create()
sha1: forge.md.sha1.create(),
sha256: forge.md.sha1.create()
};
}

Expand All @@ -2058,6 +2125,7 @@ tls.handleHandshake = function(c, record) {
type !== tls.HandshakeType.finished) {
c.session.md5.update(bytes);
c.session.sha1.update(bytes);
c.session.sha256.update(bytes);
}

// handle specific handshake type record
Expand Down Expand Up @@ -2358,22 +2426,20 @@ tls.generateKeys = function(c, sp) {
// TLS 1.0 but we don't care right now because AES is better and we have
// an implementation for it

// TODO: TLS 1.2 implementation
/*
// determine the PRF
var prf;
switch(sp.prf_algorithm) {
case tls.PRFAlgorithm.tls_prf_sha256:
prf = prf_sha256;
break;
default:
// should never happen
throw new Error('Invalid PRF');
var tls12 = tls.usingTLS12(c);
var prf = prf_TLS1; // TLS 1.0/1.1 implementation

if(tls12) { // TLS 1.2
switch(sp.prf_algorithm) {
case tls.PRFAlgorithm.tls_prf_sha256:
prf = prf_TLS12_sha256;
break;
default:
// should never happen
throw new Error('Invalid PRF');
}
}
*/

// TLS 1.0/1.1 implementation
var prf = prf_TLS1;

// concatenate server and client random
var random = sp.client_random + sp.server_random;
Expand Down Expand Up @@ -3294,14 +3360,26 @@ tls.createChangeCipherSpec = function() {
tls.createFinished = function(c) {
// generate verify_data
var b = forge.util.createBuffer();
b.putBuffer(c.session.md5.digest());
b.putBuffer(c.session.sha1.digest());

// TODO: determine prf function and verify length for TLS 1.2
var client = (c.entity === tls.ConnectionEnd.client);
var sp = c.session.sp;
var vdl = 12;
var prf = prf_TLS1;
var vdl = 12; // Same as in TLS1.1 -- see https://tools.ietf.org/html/rfc5246#page-5
var tls12 = tls.usingTLS12(c);
var prf = null;

if(tls12) {
// Defined PRFs for 1.2 use a SHA256 digest -- see https://tools.ietf.org/html/rfc5246#page-5
switch(sp.mac_algorithm) {
case tls.MACAlgorithm.hmac_sha256:
case tls.MACAlgorithm.hmac_sha1:
default:
b.putBuffer(c.session.sha256.digest());
}
prf = prf_TLS12_sha256;
} else {
b.putBuffer(c.session.md5.digest());
b.putBuffer(c.session.sha1.digest());
prf = prf_TLS1;
}
var label = client ? 'client finished' : 'server finished';
b = prf(sp.master_secret, label, b.getBytes(), vdl);

Expand Down Expand Up @@ -3398,6 +3476,7 @@ tls.queue = function(c, record) {
var bytes = record.fragment.bytes();
c.session.md5.update(bytes);
c.session.sha1.update(bytes);
c.session.sha256.update(bytes);
bytes = null;
}

Expand Down Expand Up @@ -3984,7 +4063,8 @@ tls.createConnection = function(options) {
clientCertificate: null,
sp: {},
md5: forge.md.md5.create(),
sha1: forge.md.sha1.create()
sha1: forge.md.sha1.create(),
sha256: forge.md.sha256.create()
};

// use existing session information
Expand Down Expand Up @@ -4162,9 +4242,11 @@ for(var key in tls) {

// expose prf_tls1 for testing
forge.tls.prf_tls1 = prf_TLS1;
forge.tls.prf_tls12 = prf_TLS12;

// expose sha1 hmac method
forge.tls.hmac_sha1 = hmac_sha1;
forge.tls.hmac_sha256 = hmac_sha256;

// expose session cache creation
forge.tls.createSessionCache = tls.createSessionCache;
Expand Down
Loading