/** * From the Lucky13 paper: An individual record R (viewed as a byte sequence of length at least * zero) is processed as follows. The sender maintains an 8-byte sequence number SQN which is * incremented for each record sent, and forms a 5-byte field HDR consisting of a 1-byte type * field, a 2-byte version field, and a 2-byte length field. It then calculates a MAC over the * bytes SQN || HDR || R. * * @param protocolVersion * @param contentType * @param data * @return */ public byte[] calculateMac( ProtocolVersion protocolVersion, ProtocolMessageType contentType, byte[] data) { byte[] SQN = ArrayConverter.longToUint64Bytes(sequenceNumber); byte[] HDR = ArrayConverter.concatenate( contentType.getArrayValue(), protocolVersion.getValue(), ArrayConverter.intToBytes(data.length, 2)); writeMac.update(SQN); writeMac.update(HDR); writeMac.update(data); LOGGER.debug( "The MAC was caluculated over the following data: {}", ArrayConverter.bytesToHexString(ArrayConverter.concatenate(SQN, HDR, data))); byte[] result = writeMac.doFinal(); LOGGER.debug("MAC result: {}", ArrayConverter.bytesToHexString(result)); // we increment sequence number for the sent records sequenceNumber++; return result; }
public byte[] calculateDtlsMac( ProtocolVersion protocolVersion, ProtocolMessageType contentType, byte[] data, long dtlsSequenceNumber, int epochNumber) { byte[] SQN = ArrayConverter.concatenate( ArrayConverter.intToBytes(epochNumber, 2), ArrayConverter.longToUint48Bytes(dtlsSequenceNumber)); byte[] HDR = ArrayConverter.concatenate( contentType.getArrayValue(), protocolVersion.getValue(), ArrayConverter.intToBytes(data.length, 2)); writeMac.update(SQN); writeMac.update(HDR); writeMac.update(data); if (LOGGER.isDebugEnabled()) { LOGGER.debug( "The MAC will be caluculated over the following data: {}", ArrayConverter.bytesToHexString( ArrayConverter.concatenate( ArrayConverter.intToBytes(epochNumber, 2), ArrayConverter.longToUint48Bytes(sequenceNumber), HDR, data))); } byte[] result = writeMac.doFinal(); if (LOGGER.isDebugEnabled()) { LOGGER.debug("MAC result: {}", ArrayConverter.bytesToHexString(result)); } return result; }
public TlsRecordBlockCipher(TlsContext tlsContext) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException { this.tlsContext = tlsContext; ProtocolVersion protocolVersion = tlsContext.getProtocolVersion(); CipherSuite cipherSuite = tlsContext.getSelectedCipherSuite(); if (protocolVersion == ProtocolVersion.TLS11 || protocolVersion == ProtocolVersion.TLS12 || protocolVersion == ProtocolVersion.DTLS10 || protocolVersion == ProtocolVersion.DTLS12) { useExplicitIv = true; } bulkCipherAlg = BulkCipherAlgorithm.getBulkCipherAlgorithm(cipherSuite); CipherAlgorithm cipherAlg = AlgorithmResolver.getCipher(cipherSuite); int keySize = cipherAlg.getKeySize(); encryptCipher = Cipher.getInstance(cipherAlg.getJavaName()); decryptCipher = Cipher.getInstance(cipherAlg.getJavaName()); MacAlgorithm macAlg = AlgorithmResolver.getMacAlgorithm(cipherSuite); readMac = Mac.getInstance(macAlg.getJavaName()); writeMac = Mac.getInstance(macAlg.getJavaName()); int secretSetSize = (2 * keySize) + readMac.getMacLength() + writeMac.getMacLength(); if (!useExplicitIv) { secretSetSize += encryptCipher.getBlockSize() + decryptCipher.getBlockSize(); } byte[] masterSecret = tlsContext.getMasterSecret(); byte[] seed = tlsContext.getServerClientRandom(); PRFAlgorithm prfAlgorithm = AlgorithmResolver.getPRFAlgorithm( tlsContext.getProtocolVersion(), tlsContext.getSelectedCipherSuite()); byte[] keyBlock = PseudoRandomFunction.compute( prfAlgorithm, masterSecret, PseudoRandomFunction.KEY_EXPANSION_LABEL, seed, secretSetSize); LOGGER.debug("A new key block was generated: {}", ArrayConverter.bytesToHexString(keyBlock)); int offset = 0; byte[] clientMacWriteSecret = Arrays.copyOfRange(keyBlock, offset, offset + readMac.getMacLength()); offset += readMac.getMacLength(); LOGGER.debug( "Client MAC write Secret: {}", ArrayConverter.bytesToHexString(clientMacWriteSecret)); byte[] serverMacWriteSecret = Arrays.copyOfRange(keyBlock, offset, offset + writeMac.getMacLength()); offset += writeMac.getMacLength(); LOGGER.debug( "Server MAC write Secret: {}", ArrayConverter.bytesToHexString(serverMacWriteSecret)); clientWriteKey = Arrays.copyOfRange(keyBlock, offset, offset + keySize); offset += keySize; LOGGER.debug("Client write key: {}", ArrayConverter.bytesToHexString(clientWriteKey)); serverWriteKey = Arrays.copyOfRange(keyBlock, offset, offset + keySize); offset += keySize; LOGGER.debug("Server write key: {}", ArrayConverter.bytesToHexString(serverWriteKey)); byte[] clientWriteIv, serverWriteIv; if (useExplicitIv) { clientWriteIv = new byte[encryptCipher.getBlockSize()]; RandomHelper.getRandom().nextBytes(clientWriteIv); serverWriteIv = new byte[decryptCipher.getBlockSize()]; RandomHelper.getRandom().nextBytes(serverWriteIv); } else { clientWriteIv = Arrays.copyOfRange(keyBlock, offset, offset + encryptCipher.getBlockSize()); offset += encryptCipher.getBlockSize(); LOGGER.debug("Client write IV: {}", ArrayConverter.bytesToHexString(clientWriteIv)); serverWriteIv = Arrays.copyOfRange(keyBlock, offset, offset + decryptCipher.getBlockSize()); offset += decryptCipher.getBlockSize(); LOGGER.debug("Server write IV: {}", ArrayConverter.bytesToHexString(serverWriteIv)); } if (tlsContext.getMyConnectionEnd() == ConnectionEnd.CLIENT) { encryptIv = new IvParameterSpec(clientWriteIv); decryptIv = new IvParameterSpec(serverWriteIv); encryptKey = new SecretKeySpec(clientWriteKey, bulkCipherAlg.getJavaName()); decryptKey = new SecretKeySpec(serverWriteKey, bulkCipherAlg.getJavaName()); encryptCipher.init(Cipher.ENCRYPT_MODE, encryptKey, encryptIv); decryptCipher.init(Cipher.DECRYPT_MODE, decryptKey, decryptIv); readMac.init(new SecretKeySpec(serverMacWriteSecret, macAlg.getJavaName())); writeMac.init(new SecretKeySpec(clientMacWriteSecret, macAlg.getJavaName())); } else { decryptIv = new IvParameterSpec(clientWriteIv); encryptIv = new IvParameterSpec(serverWriteIv); // todo check if this correct??? encryptCipher.init( Cipher.ENCRYPT_MODE, new SecretKeySpec(serverWriteKey, bulkCipherAlg.getJavaName()), encryptIv); decryptCipher.init( Cipher.DECRYPT_MODE, new SecretKeySpec(clientWriteKey, bulkCipherAlg.getJavaName()), decryptIv); readMac.init(new SecretKeySpec(clientMacWriteSecret, macAlg.getJavaName())); writeMac.init(new SecretKeySpec(serverMacWriteSecret, macAlg.getJavaName())); } if (offset != keyBlock.length) { throw new CryptoException("Offset exceeded the generated key block length"); } // mac has to be put into one or more blocks, depending on the MAC/block // length // additionally, there is a need for one explicit IV block minimalEncryptedRecordLength = ((readMac.getMacLength() / decryptCipher.getBlockSize()) + 2) * decryptCipher.getBlockSize(); }