Skip to content

Important Notice:

This is a Working Draft of the DATEX Specification. This is document is work in progress and may change at any time. It is not intended to be used for production purposes.

11 DATEX Block Handling

typescript
abstract function dxbOut (dxb: Protocol.DXB)
typescript
function extractReceivers(buffer: Uint8[], i: Uint8):

	flags <- buffer[i++]
	hasPointerId: boolean <- flags & 0b00000001
	hasEndpoints: boolean <- flags & 0b00000010
	endpointsHaveKeys: boolean <- flags & 0b00000100

	receivers <- Map<DATEX.Endpoint, Uint8[]>

	if hasPointerId:
		(pointerId,i) <- extractSlice(buffer, i, 26)

	if hasEndpoints:
		(count, i) <- extractUint16(buffer, i)
		for c in 0..count:
			(receiver, i) <- extractEndpoint(buffer,i )
			if endpointsHaveKeys:
				(encryptedKey, i) <- extractSlice(buffer, i, 512)
				receivers.set(receiver, encryptedKey)
			else 
				receivers.set(receiver, null)

	return (receivers, pointerId, i)
typescript
function parseEndpoint(data: Uint8[]):
	i <- 0
	return DATEX.Endpoint {
		type: data[i++],
		id: getSlice(buffer, i, 18),
		instance: getUint16(buffer, i+18)
	}
typescript
function extractEndpoint(buffer: Uint8[], i: Uint8):
	(slice, i) <- getSlice(buffer, i, 21)
	return (parseEndpoint(slice), i)
typescript
function extractDXBRoutingHeaderData(dxb: Protocol.DXB)
	i <- 0;
	if not (dxb[i++] = 0x01 and dxb[i++] = 0x64):
		return

	version <- dxb[i++]
	ttl <- dxb[i++]
	routingHeaderFlags <- dxb[i++]
	isLargeSize: boolean <- routingHeaderFlags & 0b00001000
	hasUnencryptedSignature: boolean <- routingHeaderFlags & 0b00000001
	hasEncryptedSignature: boolean <- routingHeaderFlags & 0b00000100

	blockSize: Uint31
	if isLargeSize:
		(blockSize, i) <- extractUint16(dxb, i)
	else:
		blockSize <- dxb[i++]
	
	i <- i + 8

	senderType <- dxb[i]

	if senderType != 255:
		(sender, i) <- extractEndpoint(dxb, i)
	else:
		i <- i + 1

	(receivers, pointerId, i) <- extractReceivers(dxb, i)

	if hasEncryptedSignature:
		i <- i + 192
	else if hasUnencryptedSignature:
		i <- i + x // TODO

	(blockHeaderFlagsAndCreationTimestamp, i) <- extractUint64(dxb, i)
	creationTimestampMs: Uint64 <- blockHeaderFlagsAndCreationTimestamp & 0x7ffffffffff

	blockHeaderFlags <- blockHeaderFlagsAndCreationTimestamp >> 43
	(expirationOffset, i) <- extractUint32(dxb, i)

	creationTimestamp <- DATEX.Time.fromMilliseconds (creationTimestampMs)

	hasExpirationOffset <- blockHeaderFlags & 0b000000010000000000000

	size <- i - 8
	if hasExpirationOffset:
		expirationTimestamp <- DATEX.Time.fromMilliseconds (creationTimestampMs + expirationOffset*1000)
		size <- size - 4
	
	return Protocol.DXBRoutingHeaderData {
		size,
		version,
		ttl,
		flags: routingHeaderFlags,

		sender,
		receivers,
		pointerId,

		creationTimestamp,
		expirationTimestamp,

		blockSize
	}
typescript
function decryptSignature(signature: Uint8[], sender: DATEX.Endpoint, global: Runtime.Global):
	// TODO
typescript
function parseUnresolvedDXBSubBlock(
	dxb: Protocol.DXB,
	routingHeaderData: Protocol.DXBRoutingHeaderData,
	global: Runtime.Global
):
	i <- routingHeaderData.size

	routingHeaderFlags <- routingHeaderData.flags

	hasUnencryptedSignature <- routingHeaderFlags & 0b00000001
	hasEncryption <- routingHeaderFlags & 0b00000010
	hasEncryptedSignature <- routingHeaderFlags & 0b00000100

	if hasEncryptedSignature:
		(encryptedSignature, i) <- getSlice(dxb, i, X)
		unencryptedSignature = decryptSignature(
			encryptedSignature,
			routingHeaderData.sender,
			global
		)
	else if hasUnencryptedSignature:
		(unencryptedSignature, i) <- getSlice(dxb, i, 192)

	data <- dxb[i..len(dxb)] // optional signed part starting with block header, flags, onbehalfof and body

	(flags, i) <- extractUint64(dxb, i) >> 43

	blockType <- flags & 0b111100000000000000000
	allowExecute <- flags & 0b000010000000000000000
	endOfBlock <- flags & 0b000001000000000000000
	endOfScope <- flags & 0b000000100000000000000

	hasExpirationOffset <- flags & 0b000000010000000000000
	hasRepresentedBy <- flags & 0b000000001000000000000
	isCompressed <- flags & 0b000000000100000000000
	isSignatureInLastSubblock <- flags & 0b000000000010000000000
	
	if hasExpirationOffset:
		i <- i + 4
	
	if hasRepresentedBy:
		(representedBy, i) <- extractEndpoint(dxb, i)
	
	if isEncrypted:
		(iv, i) <- extractSlice(buffer, i, 16)

	return Runtime.DXBUnresolvedSubBlock {
		headerData: Runtime.DXBBlockHeaderData {
			routingData: routingHeaderData,
			isSigned,
			isEncrypted,
			isSignatureInLastSubblock,

			scopeId,
			blockIndex,
			blockSubIndex,

			blockType,
			allowExecute,
			endOfBlock,
			endOfScope,
			representedBy,
		},
		iv,
		signature,
		data
	}
typescript
function getMissingSubBlockIds(
	block: Runtime.DXBBlock,
	global: Runtime.Global
):

	missingSubBlockIds <- []
	subBlocks <- block.subBlocks
	indicies <- getRingBufferIndexRange(block.activeBoundA, block.activeBoundB)

	for i in indicies:
		if not (i in block.subBlocks)
			missingSubBlockIds += i
	return missingSubBlockIds
typescript
function decryptRSA(encryptedData: Uint8[], global: Runtime.Global);
privateDecryptionKey < -global.endpoint.encryptionPrivateKey;
return RSADecrypt(encryptedData, privateDecryptionKey);
typescript
function decryptBlockData(encryptedData: Uint8[], scope: Runtime.Scope, block: Runtime.DXBBlock, subBlock: Runtime.DXBUnresolvedSubBlock, global: Runtime.Global):
	iv <- subBlock.iv

	encryptedKey <- 
		subBlock.headerData.routingData.receivers[global.endpoint] or
		block.headerData.routingData.receivers[global.endpoint] or
		scope.headerData.routingData.receivers[global.endpoint]

	decryptedKey <- decryptRSA(encryptedKey, global)
	return AESDecrypt(encryptedData, decryptedKey)
typescript
function collectSubBlocks(block: Runtime.DXBBlock, scope: Runtime.Scope, global: Runtime.Global):

	indicies <- getRingBufferIndexRange(block.activeBoundA, block.activeBoundB)

	lastAvailableIndex <- 0
	// loop over all available subblocks, validate signature (combined subBlock or single subBlock)
	for i in indicies:
		subBlock <- block.subBlocks[i]
		if not subBlock:
			if block.headerData.isSignatureInLastSubblock:
				return
			else:
				break
		lastAvailableIndex <- i;

		if	block.headerData.isSignatureInLastSubblock and
			subBlock.headerData.endOfBlock:
			signature <- subBlock.signature
			signedData <- Uint8[]()

			for j in indicies:
				signedData += block.subBlocks[j].data

			// validate signature, subblock(s) must be signed
			isValid <- validateSignature(signedData, signature)
			if not isValid:
				// invalid data, gargabe collection ...
				return
			
		else if subBlock.headerData.isSigned:
			signedData <- subBlock.data
			isValid <- validateSignature(signedData, signature)
			if not isValid:
				return
		else:
			// if one subblock is not signed whole block is marked as non-signed
			block.headerData.isSigned = false

	executable <- Uint8[]()
	availableIndicies <- getRingBufferIndexRange(indicies[0],lastAvailableIndex)

	// optional decryption
	for i in availableIndicies:
		subBlock <- block.subBlocks[i]
		if subBlock.headerData.isEncrypted
			encryptedData = subBlock.data
			// keys can be defined in block, subBlock or scope
			// IV is located in each subblock
			decryptedData <- decryptBlockData(encryptedData, scope, block, subBlock, global)
			executable <- executable + decryptedData
		else:
			executable <- executable + subBlock.data

	block.activeBoundA <- lastAvailableIndex
	scope.executable <- scope.executable + executable
typescript
function executeDXB(
	dxb: Protocol.DXB,
	routingHeaderData: Protocol.DXBRoutingHeaderData,
	global: Runtime.Global
):

	unresolvedSubBlock <- parseUnresolvedDXBSubBlock(dxb, routingHeaderData, global)

	endpoint <- global.endpoint
	if not (endpoint in global.scopes):
		global.scopes[endpoint] <- Map<Uint32, Map<Uint32, Runtime.DXBBlock>>()

	headerData <- unresolvedSubBlock.headerData

	scopeId <- headerData.scopeId
	if not (scopeId in global.scopes[endpoint]):
		global.scopes[endpoint][scopeId] <- Runtime.Scope{ headerData }

	blockId <- headerData.blockId
	if not (blockId in global.scopes[endpoint][scopeId].blocks):
		global.scopes[endpoint][scopeId].blocks[blockId] <- Runtime.DXBBlock{ headerData }
	
	block <- global.scopes[endpoint][scopeId].blocks[blockId]
	blockSubIndex <- headerData.blockSubIndex

	if (blockSubIndex > block.activeBoundB or blockSubIndex < block.activeBoundA) and block.lastSubBlockReceived:
		return

	block.subBlocks[blockSubIndex] <- unresolvedSubBlock

	if blockSubIndex > block.activeBoundB
		block.activeBoundB <- blockSubIndex 
	else if blockSubIndex < block.activeBoundA
		block.activeBoundB <- blockSubIndex 

	missingSubBlockIds <- getMissingSubBlockIds(block, global)

	if len(missingSubBlockIds) > 0:
		triggerResend(missingBlockIds, block, global) // TODO

	collectSubBlocks(
		block,
		global
	)

	runtimeExecution(scope, global)
typescript
function redirectDXB (
	dxb: Protocol.DXB,
	routingHeaderData: Protocol.DXBRoutingHeaderData,
	global: Runtime.Global
):
typescript
function dxbIn (
	dxb: Protocol.DXB,
	global: Runtime.Global
):
	routingHeaderData <- extractDXBRoutingHeaderData(dxb)
	receivers <- routingHeaderData.receivers

	if global.endpoint in receivers:
		executeDXB(dxb, routingHeaderData, global)

	if not (len(receivers) = 1 and receivers[0] = global.endpoint):
		redirectDXB(dxb, routingHeaderData, global)