Example #1
0
/**
 * returns $bytes bytes of random data as a hexadecimal string
 */
function common_random_hexstr($bytes)
{
    $str = common_random_rawstr($bytes);
    $hexstr = '';
    for ($i = 0; $i < $bytes; $i++) {
        $hexstr .= sprintf("%02x", ord($str[$i]));
    }
    return $hexstr;
}
 public function onStartMagicEnvelopeToXML(MagicEnvelope $magic_env, XMLStringer $xs, $flavour = null, Profile $target = null)
 {
     // Since Diaspora doesn't use a separate namespace for their "extended"
     // salmon slap, we'll have to resort to this workaround hack.
     if ($flavour !== 'diaspora') {
         return true;
     }
     // WARNING: This changes the $magic_env contents! Be aware of it.
     /**
      * https://wiki.diasporafoundation.org/Federation_protocol_overview
      * http://www.rubydoc.info/github/Raven24/diaspora-federation/master/DiasporaFederation/Salmon/EncryptedSlap
      *
      * Constructing the encryption header
      */
     // For some reason diaspora wants the salmon slap in a <diaspora> header.
     $xs->elementStart('diaspora', array('xmlns' => 'https://joindiaspora.com/protocol'));
     /**
      * Choose an AES key and initialization vector, suitable for the
      * aes-256-cbc cipher. I shall refer to this as the “inner key”
      * and the “inner initialization vector (iv)”.
      */
     $inner_key = new Crypt_AES(CRYPT_AES_MODE_CBC);
     $inner_key->setKeyLength(256);
     // set length to 256 bits (could be calculated, but let's be sure)
     $inner_key->setKey(common_random_rawstr(32));
     // 32 bytes from a (pseudo) random source
     $inner_key->setIV(common_random_rawstr(16));
     // 16 bytes is the block length
     /**
      * Construct the following XML snippet:
      *  <decrypted_header>
      *      <iv>((base64-encoded inner iv))</iv>
      *      <aes_key>((base64-encoded inner key))</aes_key>
      *      <author>
      *          <name>Alice Exampleman</name>
      *          <uri>acct:user@sender.example</uri>
      *      </author>
      *  </decrypted_header>
      */
     $decrypted_header = sprintf('<decrypted_header><iv>%1$s</iv><aes_key>%2$s</aes_key><author_id>%3$s</author_id></decrypted_header>', base64_encode($inner_key->iv), base64_encode($inner_key->key), $magic_env->getActor()->getAcctUri());
     /**
      * Construct another AES key and initialization vector suitable
      * for the aes-256-cbc cipher. I shall refer to this as the
      * “outer key” and the “outer initialization vector (iv)”.
      */
     $outer_key = new Crypt_AES(CRYPT_AES_MODE_CBC);
     $outer_key->setKeyLength(256);
     // set length to 256 bits (could be calculated, but let's be sure)
     $outer_key->setKey(common_random_rawstr(32));
     // 32 bytes from a (pseudo) random source
     $outer_key->setIV(common_random_rawstr(16));
     // 16 bytes is the block length
     /**
      * Encrypt your <decrypted_header> XML snippet using the “outer key”
      * and “outer iv” (using the aes-256-cbc cipher). This encrypted
      * blob shall be referred to as “the ciphertext”. 
      */
     $ciphertext = $outer_key->encrypt($decrypted_header);
     /**
      * Construct the following JSON object, which shall be referred to
      * as “the outer aes key bundle”:
      *  {
      *      "iv": ((base64-encoded AES outer iv)),
      *      "key": ((base64-encoded AES outer key))
      *  }
      */
     $outer_bundle = json_encode(array('iv' => base64_encode($outer_key->iv), 'key' => base64_encode($outer_key->key)));
     /**
      * Encrypt the “outer aes key bundle” with Bob’s RSA public key.
      * I shall refer to this as the “encrypted outer aes key bundle”.
      */
     common_debug('Diaspora creating "outer aes key bundle", will require magic-public-key');
     $key_fetcher = new MagicEnvelope();
     $remote_keys = $key_fetcher->getKeyPair($target, true);
     // actually just gets the public key
     $enc_outer = $remote_keys->publicKey->encrypt($outer_bundle);
     /**
      * Construct the following JSON object, which I shall refer to as
      * the “encrypted header json object”:
      *  {
      *      "aes_key": ((base64-encoded encrypted outer aes key bundle)),
      *      "ciphertext": ((base64-encoded ciphertextm from above))
      *  }
      */
     $enc_header = json_encode(array('aes_key' => base64_encode($enc_outer), 'ciphertext' => base64_encode($ciphertext)));
     /**
      * Construct the xml snippet:
      *  <encrypted_header>((base64-encoded encrypted header json object))</encrypted_header>
      */
     $xs->element('encrypted_header', null, base64_encode($enc_header));
     /**
      * In order to prepare the payload message for inclusion in your
      * salmon slap, you will:
      *
      * 1. Encrypt the payload message using the aes-256-cbc cipher and
      *      the “inner encryption key” and “inner encryption iv” you
      *      chose earlier.
      * 2. Base64-encode the encrypted payload message.
      */
     $payload = $inner_key->encrypt($magic_env->getData());
     //FIXME: This means we don't actually put an <atom:entry> in the payload,
     // since Diaspora has its own update method! Silly me. Read up on:
     // https://wiki.diasporafoundation.org/Federation_Message_Semantics
     $magic_env->signMessage(base64_encode($payload), 'application/xml');
     // Since we have to change the content of me:data we'll just write the
     // whole thing from scratch. We _could_ otherwise have just manipulated
     // that element and added the encrypted_header in the EndMagicEnvelopeToXML event.
     $xs->elementStart('me:env', array('xmlns:me' => MagicEnvelope::NS));
     $xs->element('me:data', array('type' => $magic_env->getDataType()), $magic_env->getData());
     $xs->element('me:encoding', null, $magic_env->getEncoding());
     $xs->element('me:alg', null, $magic_env->getSignatureAlgorithm());
     $xs->element('me:sig', null, $magic_env->getSignature());
     $xs->elementEnd('me:env');
     $xs->elementEnd('entry');
     return false;
 }