Protocolo de federación

De Wiki Diaspora*
Saltar a: navegación, buscar

El propósito de este documento es describir cómo funciona la comunicación entre servidores de Diaspora. Sin embargo, quienes implementen este protocolo deberán saber que Diaspora aún está en fase alpha, y como tal, este documento no es autoritario, y puede estar un poco atrasado en cuanto a la implementación de referencia.

Compartir asimétrico

Algo que es importante notar es la noción de Diaspora sobre el «compartir» asimétrico.

En una relación de «amistad» simétrica, ninguno de los involucrados ve las publicaciones del otro hasta que uno realice una petición de amistad, y el otro confirme.

En un modelo «asimétrico», si empiezas a compartir con alguien, habrás decidido enviarle publicaciones. La otra persona podría o no elegir compartir contigo también. Si decide no hacerlo, tú verás únicamente las publicaciones que hayan marcado explícitamente como «públicas».

Los mensajes públicos son enviados con el protocolo Salmon, como todas las publicaciones.

Protocolos centrales de Diaspora

Los servidores de Diaspora se comunican uno con otro en distintas situaciones:

  • Cuando descubren información sobre usuarios en otro servidor.
  • Cuando envían información sobre la gente con quien empiezas a compartir. Este información incluye:
    • Notificaciones al comenzar a compartir con ellos.
    • Publicaciones que realices.
    • Comentarios que se hagan (por ti o por otros) en una de tus publicaciones.
    • Los «me gusta» que se haga (por ti o por otros) en una de tus publicaciones.
    • Conversaciones (cada hilo en la bandeja de entrada tiene un objeto que lo representa).
    • Mensajes (cada mensaje individual en una conversación).
    • Información de perfil.
    • Retracciones en las publicaciones.
    • Retracciones de comentarios/me gusta.

Este documente no cubre la semántica de cada mensaje listado anteriormente. Para obtener más información sobre este tema, lee sobre la semántica en los mensajes de Diaspora.

Descubrimiento

Los pods de Diaspora deben tener la capacidad de descubrir a usuarios en otros pods, dada la dirección webfinger del usuario. Por conveniencia, la interfaz de usuario de Diaspora pueden permitir a los usuarios buscar a otros por nombre, buscando en la lista de nombres conocidos para el pod (tal como usuarios locales). No obstante, la interfaz de usuario de un pod no pueden permitir a un usuario encontrar a una persona por el nombre, si dicha persona no ha marcado su perfil como «disponible para búsqueda», en su hcard (ver abajo).

Si [email protected] quiere descubrir a [email protected], entonces el pod de Alice debe realizar una búsqueda Webfinger, con la dirección de Bob. Webfinger es un protocolo abierto.[1] Cabe notar que el perfil webfinger de Bob no necesita estar alojado en el pod de Bob. Cualquier servidor webfinger bastará, siempre que el perfil de Bob contenga los elementos necesarios para Diaspora. Sin embargo, los pods de Diaspora deberían alojar perfiles webfinger de sus usuarios.

El pod de Alice primero obtendrá el archivo host-meta a partir de la dirección webfinger de Bob. El archivo host-meta está ubicado en https://bob.diaspora.ejemplo.com/.well-known/host-meta. En este archivo, el pod de Alice encontrará un enlace con el elemento rel="lrdd", tal como:

<Link rel="lrdd" template="https://bob.diaspora.ejemplo.com/?q={uri}"  type="application/xrd+xml" />

El pod de Alice transformará entonces la dirección webfinger de Bob, y reemplazará {uri} con esto. Primero, la dirección webfinger de Bob es codificada.Alice hará una petición GET a: https://bob.diaspora.ejemplo.com/?q=bob%40bob.diaspora.ejemplo.com.

El servidor webfinger de Bob (que podría ser el pod mismo) responderá con el perfil webfinger de Bob. El protocolo webfinger podría verse de esta manera:

<?xml version="1.0" encoding="UTF-8"?>
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
  <Subject>acct:[email protected]</Subject>
  <Alias>"http://bob.diaspora.ejemplo.com/"</Alias>
  <Link rel="http://microformats.org/profile/hcard" type="text/html" href="http://bob.diaspora.ejemplo.com/hcard/users/((guid))"/>
  <Link rel="http://joindiaspora.com/seed_location" type="text/html" href="http://bob.diaspora.ejemplo.com/"/>
  <Link rel="http://joindiaspora.com/guid" type="text/html" href="((guid))"/>
  <Link rel="http://schemas.google.com/g/2010#updates-from" type="application/atom+xml" href="http://bob.diaspora.ejemplo.com/public/bob.atom"/>
  <Link rel="diaspora-public-key" type="RSA" href="((base64-encoded representation of the rsa public key))"/>
</XRD>

Veamos los elementos más de cerca.

Subject

<Subject>acct:[email protected]</Subject>

Este elemento debe contener la dirección webfinger que Alice solicitó. De lo contrario, este perfil webfinger debe ser ignorado por el pod de Alice.

Alias

<Alias>"http://bob.diaspora.ejemplo.com/"</Alias>

hcard

<Link rel="http://microformats.org/profile/hcard" type="text/html" href="http://bob.diaspora.ejemplo.com/hcard/users/((guid))"/>

El perfil webfinger de Bob debe contener un enlace a una hcard. La hcarad contiene información personal, tal como el nombre completo de Bob, un enlace a la foto de Bob, etcétera.[2]

Al igual que el perfil webfinger, la hcard no necesita estar alojad en un pod de Diaspora. De hecho, puede estar en una locación distinta tanto al pod de Diaspora, como al host webfinger. No obstante, si la hcard está alojada en el pod de Diaspora, entonces la URL debería ser:

http://bob.diaspora.ejemplo.com/hcard/users/((bobs-guid))

Cuando un usuario crea una cuenta en un pod, el pod debe asignarle un GUID: una línea de caracteres aleatorios, con al menos 8 dígitos hexadecimales. El pod de Diaspora debe utilizar el GUID para crear una URL en la hcard, y debe alojar la información de los usuarios a manera de hcard en esta locación.

Diaspora añada un campo, entity_searchable, que contiene un elemento con la clase «searchable», que puede ser «false» o «true». Si el valor es «false», los pods no podrán a los usuarios realizar una búsqueda de Bob por nombre, o por ningún medio, salvo la dirección webfinger de Bob.

Diaspora también incluye un campo URL con el ID de «pod_location», que enlaza al pod de Diaspora de Bob. Aquí se muestra un ejemplo de una hcard:

<div id="content">
<h1>Bob Exampleman</h1>
<div id="content_inner">
<div class="entity_profile vcard author" id="i">
<h2>User profile</h2>
<dl class="entity_nickname">
<dt>Nickname</dt>
<dd>
<a class="nickname url uid" href="http://bob.diaspora.ejemplo.com/" rel="me">Bob Exampleman</a>
</dd>
</dl>
<dl class="entity_given_name">
<dt>First name</dt>
<dd>
<span class="given_name">Bob</span>
</dd>
</dl>
<dl class="entity_family_name">
<dt>Family name</dt>
<dd>
<span class="family_name">Exampleman</span>
</dd>
</dl>
<dl class="entity_fn">
<dt>Full name</dt>
<dd>
<span class="fn">Bob Exampleman</span>
</dd>
</dl>
<dl class="entity_url">
<dt>URL</dt>
<dd>
<a class="url" href="http://bob.diaspora.ejemplo.com/" id="pod_location" rel="me">http://bob.diaspora.ejemplo.com/</a>
</dd>
</dl>
<dl class="entity_photo">
<dt>Photo</dt>
<dd>
<img class="photo avatar" height="300px" src="http://bob.diaspora.ejemplo.com/uploads/images/thumb_large_sTBJOBJScE.jpg" width="300px">
</dd>
</dl>
<dl class="entity_photo_medium">
<dt>Photo</dt>
<dd>
<img class="photo avatar" height="100px" src="http://bob.diaspora.ejemplo.com/uploads/images/thumb_medium_sTBJOBJScE.jpg" width="100px">
</dd>
</dl>
<dl class="entity_photo_small">
<dt>Photo</dt>
<dd>
<img class="photo avatar" height="50px" src="http://bob.diaspora.ejemplo.com/uploads/images/thumb_small_sTBJOBJScE.jpg" width="50px">
</dd>
</dl>
<dl class="entity_searchable">
<dt>Searchable</dt>
<dd>
<span class="searchable">true</span>
</dd>
</dl>
</div>
</div>
</div>

Ubicación de una seed

<Link rel="http://joindiaspora.com/seed_location" type="text/html" href="http://bob.diaspora.ejemplo.com/"/>

«seed_location» es un enlace al pod de Bob.

GUID

<Link rel="http://joindiaspora.com/guid" type="text/html" href="((guid))"/>

Este es el GUID de Bob. Cuando un usuario crea una cuenta en un pod, el pod debe asignarle un GUID: una línea de caracteres aleatorios, de al menos 8 dígitos hexadecimales.

URL de Activity Stream

<Link rel="http://schemas.google.com/g/2010#updates-from" type="application/atom+xml" href="http://bob.diaspora.ejemplo.com/public/bob.atom"/>

Este es el feed atom de las publicaciones «públicas» de Bob. Los pods de Diaspora deben publicar el Activity Stream de los mensajes públicos, pero actualmente no se requiere que sea posible leer este Stream.[3]

Cabe señalar que este feed también podría estar disponible por medio del mecanismo PubSubHubbub.

Llave pública de Diaspora

<Link rel="diaspora-public-key" type="RSA" href="((base64-encoded representation of the rsa public key))"/>

Cuando se crea un usuario en un pod, el pod debe generar un par de llaves PGP. Esta llave se utiliza para firmar mensajes. Aquí se coloca la llave en el campo «href». El formato de la llave es el siguiente: se toma la representación ASCII-armored de la llave, y se codifica con Base-64. Entonces, la llave binaria real se «envuelve dos veces» con codificación Base-64. Remover el paquete exterior brinda una llave PKCS#1 DER-encoded, la cual comienza con el texto «----BEGIN RSA PUBLIC KEY----». Algunas plataformas podrían requerir la conversión de esta llave a un formato distinto, tal como PKCS#8, o «modulus/exponent».

Envío

Si tú (Alice) has decido compartir (Bob) en otro pod, entonces necesitarás enviar publicaciones como Salmon al usuario remoto. Tienes tres tareas:

  1. Construir tu mensaje.
  2. Construir la URL para la variable Salmon de Bob.
  3. Publicar el mensaje para Bob.

En este ejemplo, Alice ([email protected]) está tratando enviar un mensaje a Bob ([email protected]).

Construir el mensaje

En Diaspora, los mensajes se envían a los usuarios remotos encriptados. Esto ayuda a proteger la privacidad de tus mensjaes mientras están en transito, incluso si publicas el paquete al pod de Bob utilizando HTTP común, en vez de HTTP encriptado con SSL.[4]

Para soportar la semántica de encriptación, Diaspora de hecho extiende el protocolo Salmon magic-envelopes, para añadir un encabezado de encriptación.

Entonces, para poder construir el paquete, se necesita:

  1. Construir el encabezado empaquetado.
  2. Preparar la carga útil del mensaje.
  3. Construir un magic-envelope] Salmon.

Construir el encabezado de encriptación

Elegir una llave ASE y un vector de inicialización, adecuado para el cifrado aes-256-cbc. Nos referiremos a esto como «llave interna», y «vector de inicialización interna (iv)».

Construir el siguiente fragmento XML:
<decrypted_header>
  <iv>((base64-encoded inner iv))</iv>
  <aes_key>((base64-encoded inner key))</aes_key>
  <author>
    <name>Alice Exampleman</name>
    <uri>acct:[email protected]</uri>
  </author>
</decrypted_header>

Construir otra llave AES y vector de inicialización, apropiado para el cifrado aes-256. A esto lo llamaremos «llave externa» y «vector de inicialización externa (iv)».

Encriptar el fragmento XML decrypted_header, utilizando la «llave externa» y «iv externo» (usando el cifrado aes-256-cbc). Este blob encriptado se llamará «texto cifrado».

Construir el siguiente objeto JSON, que se conocerá como «paquete encriptado de llave externa aes»:

{
  "iv": ((base64-encoded AES outer iv)),
  "key": ((base64-encoded AES outer key))
}

Encriptar el «paquete encriptado de llave externa aes» con la llave RSA pública de Bob. Esto se conocerá como el «paquete encriptado de llave externa aes».

Construir el siguiente objeto JSON, que llamaremos «encabezado encriptado de objeto json»:

{
  "aes_key": ((base64-encoded encrypted outer aes key bundle)),
  "ciphertext": ((base64-encoded ciphertextm from above))
}  

Construir el fragmento XML: <encrypted_header>((base64-encoded encrypted header json object))</encrypted_header>

Guardar el fragmento encrypted_header para después; se añadirá al sobre Salmon para construir el Salmon extendido de Diaspora.

Recuerda la «llave aes interna» y «iv aes interno» para más tarde. Utilizarás esto para encriptar la carga útil del mensaje. Lo que publicarás para el usuario remoto será:

  • Notificaciones al comenzar a compartir con ellos.
  • Publicaciones que realices.
  • Comentarios que se hagan (por ti o por otros) en una de tus publicaciones.
  • Los «me gusta» que se haga (por ti o por otros) en una de tus publicaciones.
  • Conversaciones (cada hilo en la bandeja de entrada tiene un objeto que lo representa).
  • Mensajes (cada mensaje individual en una conversación).
  • Información de perfil.
  • Retracciones en las publicaciones.
  • Retracciones de comentarios/me gusta.

Sin importar el contenido de la carga útil del mensaje, este será el formato:

<XML>
  <post>((post-content))</post>
</XML>

El contenido de la publicación dependerá del tipo de mensaje que envíes. En general, la API está en flujo continuo, por lo que este documento podría estar desactualizado. La fuente autoritaria de información son los «modelos» de referencia en la implementación de Ruby on Rails.

Para preparar la carga útil del mensaje para su inclusión en el Salmon slap, harás lo siguiente:

  • Encriptar la carga útil del mensaje utilizando el cifrado aes-256-cbc, y la «llave de encriptación interna» y «iv de encriptación interna», los cuales elegiste antes.
  • Codificar con Base-64 la carga útil encriptada del mensaje.

Construir un Salmon magic-envelope

Por ahora, has reunido los siguientes componentes:

  • El encabezado de encriptación.
  • La carga útil preparada del mensaje.

Ahora deberás construir el magic-envelope Salmon que publicaremos a Bob, el destinatario. Se construye de esta manera:

<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns='http://www.w3.org/2005/Atom'>
  ((the encryption header))
  <me:env xmlns:me="http://salmon-protocol.org/ns/magic-env">
    <me:encoding>base64url</me:encoding>
    <me:alg>RSA-SHA256</me:alg>
    <me:data type="application/xml">((base64url-encoded prepared payload message))</me:data>
    <me:sig>((the RSA-SHA256 signature of the above data))</me:sig>
  </me:env>
</entry>

Cabe notar que el último paso en la preparación de la carga útil del mensaje fue codificar con Base-64. Esta línea debe codificarse con Base-64 nuevamente, para formar el elemento <me:data>. No obstante, esta ocasión, debes ser codificado utilizando la codificación base64url, que es ligeramente distinta. Así que tu carga útil de mensaje se empaquetará dos veces, con la codificación Base-64.

La firma (el elemento <me:sig>) se construye como se indica en la especificación de Magic Envelopes. Es decir, usando el algoritmo RSA-SHA256 para firmar la línea base con tú (la de Alice) llave RSA privada.

Para construir la línea base, se concatenan los siguientes elementos, separados con puntos (.):

  1. El contenido del campo <me:data>. Esto es la carga útil preparada del mensaje, codificada con base64-url. Recuerda que la carga útil original del mensaje se ha codificado ahora dos veces, con Base-64).[5]
  2. La codificación base64url del parámetro «data-type». En este caso application/atom se codifica con Base-64. Por tanto, la línea codificada con base64url es YXBwbGljYXRpb24veG1s.[6]
  3. La codificación base64url del parámetro «encoding», que es la línea literal base65url, codificada con Base-64. Por tanto, la línea codificada con base64url es YmFzZTY0dXJs.[6]
  4. La codificación base64url del parámetro «alg», que es la línea literal RSA-SHA256, codificado con Base-64. Por tanto, la línea codificada con base64url es UlNBLVNIQTI1Ng==.[6]

Firma la línea base con tu llave (la de Alice) RSA privada y codifica con base64url los resultados.

Esta es la forma final del Salmon slap, lista para la entrega.

Construir la URL de la variable Salmon de Bob

Para esto se debe hacer lo siguente:

  1. Obtener la ubicación del pod del usuario remoto, en este caso Bob.
  2. Obenter la GUID del usuario remoto (utilizando el proceso webfinger descrito antes).
  3. Construir <pod_url>/receive/users/<guid>. Esta es la variable Salmon de Bob.

Publicar el mensaje para Bob

Debes tomar el Salmon slap final, codificarlo doblemente, y publicar esta información para la variable Salmon de Bob, como Content-type: application/x-www-form-urlencoded:

xml=((double urlencoded salmon slap))  

Si recibes la respuesta HTTP 202 Created, o la respuesta 200 OK, tu Salmon slap ha sido aceptado.

Cabe destacar que esto difiere del protocolo Salmon estándar, el cual especifica que publicarás únicamente el Salmon slap sin codificación; es decir, sin xml=…

Recibir

Considera este caso: tú eres Bob, recibiendo un Salmon slap de Alice. En general, deberías seguir los pasos descritos en la sección «envío», al revés. Verificar el slap, como se indica en la sección 8 de las especificaciones de Salmon, y devolver la respuesta 202 Created si es un Salmon nuevo, o 200 OK si este Salmon actualiza a uno anterior. Si el slap falla la verificación, regresar 400 Bad Request.

Cabe notar que el slap enviado a Bob está firmado con la llave privada de Alice. Sin embargo, nada sobre el autor del slap es enviado en texto simple. Por lo tanto, tendrás que desencriptar y decodificar la carga útil del mensaje, para poder encontrar información acerca del autor del mismo. La carga útil del mensaje contendrá el ID de Diaspora del autor. Tendrás que obtener la llave pública desde su protocolo webfinger. Esta es la llave que usarás para verificar la firma.

Cuando se verifica la firma, observa que la referencia de implementación de Diaspora utiliza la librería OpenSSL de Ruby, la cual genera llaves RSA en el formato PKCS#1. Otro formato de llaves popular, es el nuevo formato PKC#8. Algunas herramientas podrían no ser interoperables. Por ejemplo, la herramienta de línea de comandos openssl tiene opciones para verificar firmas RSA, pero solo puede leerlas en formato PKCS#8. Para complicar las cosas aún más, no hay información de encabezado que diga cuál de estos formatos es el que se utilza en la llave, así que la herramienta openssl no puede devolver un error más informativo que «unable to load Public Key».[7]

Protocolos adicionales de Diaspora

Los pods de Diaspora podría ofrecer federación por medio de otros protocolos también. La implementación de referencia ofrece los siguientes protocolos adicionales:

  • ActivityStream de mensajes públicos.
  • Una API que permite a aplicaciones de terceros utilizar la información de tu pod a nombre de tus usuarios, utilizando auntenticación OAuth[8].

ActivityStream en mensajes públicos

La implementación de referencia de Diaspora expone una UI que permite a los usuarios marcar publicaciones como «públicas». Si Alice crea un mensaje que no está marcado cmo público, la publiación se enviará únicamente a las personas con quienes comparte Alice. No obstante, si Alice realiza una publicación que se marque como «pública», también será enviada a aquellas personas que comparten con ella, incluso si Alice no comparte con ellos.

Además, las publicaciones se añaden a un ActivityStream. La dirección de este feed se publica en la hcard del usuario, con:

<Link rel="http://schemas.google.com/g/2010#updates-from" type="application/atom+xml" href="https://joindiaspora.com/public/((username)).atom"/>

El feed debería también estar disponible en un servidor PubSubHubbub.[9]

API para aplicaciones de terceros

La implementación de referencia de Diaspora ofrece una API para desarrolladores de aplicaciones de terceros. Permite que aplicaciones de terceros utilicen la información de tu pod a nombre de tus usuarios. El acceso se controla con el protocolo OAuth.

Por ahora, solo hay una aplicación que utilice esta API, Cubbi.es. La API estará en flujo continuo, así que no se recomienda actualmente escribir nuevas aplicaciones, hasta que el protocolo se haya solidificado un poco más.

Notas

  1. Véanse las especificaciones del protocolo Webfinger para obtener más detalles.
  2. Mira las especificaciones de hcard para obtener información detallada sobre su sintaxis.
  3. Para obtener más información, lee las especificaciones de Activity Stream.
  4. Solo se garantiza que los mensajes se encripten en tránsito. Pueden ser desencriptados por el servidor de Bob, y almacenados en texto simple.
  5. En versiones previas se requería que esto se rellenara nuevamente con linefeeds. Esto ya no es necesario. Ahora esto es el contenido real del campo <me:data>, tal cual.
  6. 6,0 6,1 6,2 El carácter linefeed ya no se debería incluir.
  7. Para obtener más información sobre este problema, ve esta entrada de blog.
  8. El protocolo OAuth.
  9. Véanse las especificaciones de ActivityStreams y las especificaciones de PubSubHubbub.