CBOR: Representación Binaria Concisa de Objetos Explicada
CBOR (Concise Binary Object Representation) es un formato de serialización de datos binarios estandarizado por la IETF como RFC 7049 (2013) y actualizado a RFC 8949 (2020). Diseñado explícitamente como un superconjunto binario de JSON, CBOR puede representar todos los tipos de datos JSON más datos binarios, arrays numéricos tipados y etiquetas semánticas — todo en una codificación binaria compacta que no requiere esquema. CBOR es la codificación requerida para CTAP2 (protocolo de autenticador FIDO2), WebAuthn, COSE (CBOR Object Signing and Encryption) y numerosos estándares IoT incluyendo CoAP (Constrained Application Protocol).
CBOR vs JSON vs MessagePack
| Aspecto | CBOR | JSON | MessagePack |
|---|---|---|---|
| Estándar | ✅ IETF RFC 8949 | ✅ ECMA-404/RFC 8259 | ❌ No IETF/ISO |
| Esquema requerido | ❌ No | ❌ No | ❌ No |
| Datos binarios | ✅ Byte string nativo | ❌ Solo Base64 | ✅ Tipo bin nativo |
| Números tipados | ✅ Half/float/double | ❌ Solo punto flotante | ✅ Tipos int/float |
| Etiquetas semánticas | ✅ Sí (tags 0-999) | ❌ No | ⚠️ Tipos ext |
| Longitud indefinida | ✅ Sí | ❌ No | ❌ No |
| Uso en protocolos IoT | ✅ CoAP, CTAP2 | ❌ Demasiado grande | ❌ No estandarizado |
| Tamaño vs JSON | ~50-70% | 100% | ~60-80% |
Codificación CBOR: Tipos Principales
Cada elemento de datos CBOR comienza con un byte inicial que codifica el tipo principal (3 bits) e información adicional (5 bits):
Byte inicial: MMMAAAAA
MMM = Tipo principal (0-7)
AAAAA = Información adicional (0-31)
Tipo Principal 0: Entero Sin Signo
Valor 0-23: Un byte (0x00-0x17)
Valor 24-255: Cabecera 1 byte (0x18) + 1 byte valor
Valor 256-65535: Cabecera 1 byte (0x19) + 2 bytes valor (big-endian)
Ejemplos:
0 → 0x00
23 → 0x17 (máximo un byte sin signo)
24 → 0x18 0x18
1000 → 0x19 0x03 0xE8
Tipo Principal 2: String de Bytes
h'0102030405' (5 bytes) → 0x45 0x01 0x02 0x03 0x04 0x05
Tipo Principal 6: Elemento Etiquetado
Las etiquetas añaden significado semántico al elemento de datos siguiente:
| Etiqueta | Semántica |
|---|---|
| 0 | String de fecha/hora (RFC 3339) |
| 1 | Fecha/hora basada en época (numérica) |
| 2/3 | Bignum positivo/negativo |
| 4 | Fracción decimal |
| 32 | URI |
| 37 | UUID |
| 55799 | CBOR autodescriptivo (número mágico) |
Tipo Principal 7: Flotantes y Valores Especiales
false → 0xF4
true → 0xF5
null → 0xF6
float16 → 0xF9 + 2 bytes
float32 → 0xFA + 4 bytes
float64 → 0xFB + 8 bytes
El float de media precisión (16 bits, float16) es especialmente útil para datos de sensores y inferencia ML — muchas mediciones IoT y activaciones de redes neuronales caben en float16 sin pérdida de precisión relevante.
Codificación de Longitud Indefinida
La codificación de longitud indefinida de CBOR permite la serialización en streaming cuando la longitud total no se conoce de antemano:
Array indefinido:
0x9F ← inicio array indefinido
0x01 ← elemento 1
0x02 ← elemento 2
0xFF ← break (fin del array)
Map indefinido:
0xBF ← inicio map indefinido
0x61 0x61 0x01 ← "a": 1
0x61 0x62 0x02 ← "b": 2
0xFF ← break
Esto es esencial para dispositivos con RAM limitada que no pueden almacenar toda la salida antes de enviarla.
Python: cbor2
import cbor2
import uuid
from datetime import datetime, timezone
# Codificación/decodificación básica (espeja la API de json.dumps/json.loads)
datos = {
"id": 42,
"nombre": "Alicia",
"puntuaciones": [99, 87, 94],
"activo": True,
"saldo": None,
}
codificado = cbor2.dumps(datos)
print(f"CBOR: {len(codificado)} bytes")
print(f"Hex: {codificado.hex()}")
decodificado = cbor2.loads(codificado)
assert decodificado["id"] == 42
# Tipos etiquetados
datos_sensor = {
"timestamp": datetime(2024, 3, 15, 10, 0, tzinfo=timezone.utc), # etiqueta 1
"device_id": uuid.uuid4(), # etiqueta 37
"lecturas": [23.5, 24.1, 22.8],
}
codificado = cbor2.dumps(datos_sensor)
# Datos binarios (sin overhead de base64)
encabezado_imagen = bytes([0x89, 0x50, 0x4E, 0x47]) # bytes mágicos PNG
msg = {"tipo": "imagen", "encabezado": encabezado_imagen, "ancho": 1920}
cbor_msg = cbor2.dumps(msg)
de_vuelta = cbor2.loads(cbor_msg)
assert de_vuelta["encabezado"] == encabezado_imagen # bytes, no base64
# I/O de archivo
with open('registro_sensor.cbor', 'wb') as f:
cbor2.dump(datos, f)
with open('registro_sensor.cbor', 'rb') as f:
recuperado = cbor2.load(f)
CBOR en Seguridad: COSE
CBOR Object Signing and Encryption (COSE, RFC 8152) usa CBOR como modelo de datos para todas las estructuras de seguridad — claves criptográficas, firmas digitales y estructuras de cifrado:
import cbor2
# Estructura COSE_Sign1 [cabecera_protegida, cabecera_no_protegida, payload, firma]
cabecera_protegida = cbor2.dumps({1: -7}) # alg: ES256
cabecera_no_protegida = {4: b'key-id-01'} # kid
payload = b'Hola, COSE!'
firma = b'\x00' * 64 # marcador de posición
cose_sign1 = cbor2.CBORTag(18, [
cabecera_protegida,
cabecera_no_protegida,
payload,
firma,
])
codificado = cbor2.dumps(cose_sign1)
CBOR en IoT: CoAP
El Constrained Application Protocol (CoAP) es HTTP para dispositivos restringidos (microcontroladores con < 100 KB de RAM). CoAP usa CBOR para sus payloads:
import cbor2
respuesta_sensor = cbor2.dumps({
"temp": cbor2.CBORTag(4, [-2, 2350]), # decimal 23.50°C
"hum": cbor2.CBORTag(4, [-1, 645]), # decimal 64.5%
"bat": 87, # 87%
"ts": 1710500000, # timestamp Unix
})
# 28 bytes vs ~90 bytes para JSON equivalente
print(f"Payload CoAP: {len(respuesta_sensor)} bytes")
Conclusión
La combinación de CBOR de estandarización IETF, sistema de tipos compatible con JSON, datos binarios nativos, etiquetas semánticas y codificación de longitud indefinida lo hace únicamente adecuado para aplicaciones de seguridad e IoT donde JSON es demasiado grande y MessagePack carece del respaldo de estándares formales. Si estás construyendo algo que toca FIDO2/WebAuthn, tokens de seguridad basados en COSE, sensores IoT CoAP, o cualquier protocolo que requiera una codificación binaria estandarizada por la IETF con rica extensibilidad, CBOR es el formato diseñado exactamente para ese propósito.
Conversiones relacionadas
Conversiones frecuentes del catálogo: