cyberhoboing with dominic tarr

fairly tale cryptography 2: hashes

In “cinderella” a young woman living in unfortunate circumstances who’s situation suddenly changes, when a magical entity appears and gives her a make-over, allowing her to attend a fancy ball at where the local prince falls in love with her – but must hastily depart before the magic wears off… and looses a shoe, which happens to be made of glass.

The prince doesn’t write to the “missed connections” column, because it’s medieval times and Cinderella is most likely illiterate. Instead he sends out his people to test everyone and find the person whom the shoe fits.

In this story, the shoe behaves exactly like a hash. A hash is a small value that reflects the uniqueness of an file, But does not give you any other clues about the file.

A hash might usually looks something like this: ebd8581d6cea19558023b851fbdf599a5044b446 Just a seemingly random sequence of numbers and letters. There is nothing obviously indicative of a particular file in the output, yet if the file is changed by even one letter, it will have a completely different hash.

If you have that file (“Cinderella”) you can create that hash (a glass shoe that fits her) but you cannot go back the other way (you cannot create a Cinderella from the shoe!). Possessing the shoe also does not tell you where Cinderella is Possesing the shoe does not allow the prince to home in on her location - it does not give clues like “warmer” or “colder”

But what the shoe does do, is it allows the prince to know when he has found her. The prince can send someone out to take the shoe to every woman in the kingdom, and test if each one fits the shoe. A hash works exactly like this! If you know the hash, you can check whether any file produces the same hash. But if you only know the hash, and are looking for the file, it may be very hard to find that file.

What happens if the shoe fits someone else? In modern times, shoes are made in factories to standard sizes, but in medieval times, your shoes would have been handmade to fit your feet, and then worn in to your feet on top of that. If you imagine it from that perspective, it seems more reasonable that the shoe uniquely fits Cinderella, but indeed it is certainly possible that there is different person with the same feet as cinderella, it’s just extremely unlikely.

Again, this is exactly like a hash. The since the hash is often smaller than the file, there are obviously more possible files than hashes, so it’s entirely possible that there are two possible files that have the same hash, but there are already so many possible hashes (given 40+characters) that the chance of ever finding two files with the same hash is so low that it will basically never happen. Not before you and everyone you know is dead, or maybe not before the sun swallows the earth. Basically, with a well designed hash, we have much bigger things to worry about than hashes colliding, and if we are worried, we can just use a longer hash.

So there you go, hashes explained via a fairy tale you already know. Also see part 1: rumplestiltskin as bitcoin

Design Challenges of Decentralized Systems (at Data Terra Nemo)

pull streams

pull-streams are a very simple streaming primitive, but can do everything that streams need to do, from back pressure to propagating errors (which node streams do not support!)

You can use them to write any application or system you might use node for, but they are also great for simple realtime data processing - like Array.map but now you can easily do it on async – realtime – sequences of data. Things that would be cumbersome to do with node streams.

sources

The first pull stream you’ll meet is values, it creates a pull-stream from an array.

function values(array) {
  var i = 0
  return function (abort, cb) {
    if(abort) return cb(abort)
    return cb(i >= array.length ? true : null, array[i++])
  }
}

That’s it. values is a source pull-stream. A source stream is just an async function that you may call repeatedly. If you call with the first argument as true or an error, that tells the stream to abort, but we’ll come back to that later.

Otherwise, each time you call the stream, it calls back with the next item in the array. When all the items have been returned, the first argument will be true.

this is because err and end both are the terminal state of the stream: they both mean that nothing more is coming

sinks

data comes out of sources and goes into sinks. Where a source is a function you call repeatedly, a sink is a function you pass a source to.

Here is a simple sink that dumps a pull-stream to the console.

function log () {
  return function (read) {
    read(null, function next (end, data) {
      if(end == true) return
      else if(end) throw end //error
      console.log(data)
      read(null, next) //loop again.
    })
  }
}

log creates a function that accepts a source - i.e. a read function. it then calls that read function, and if it doesn’t give an “end” or “error” signal, then it reads again.

Other stream apis such as node.js streams have the concept of a “writable” stream. A writable is a passive object that accepts data, like a couch-potato watching the TV. They must actively use the TV remote to send an explicit signal to the TV when they want to stop or slow down. A pull-stream sink is a reader. it is like a book-worm reading a book - they are active and can read faster or slower. They don’t need to send an explicit signal to slow down, they just act slower.

you could combine these two streams like this:

log() (values([1,2,3]))

output:

1
2
3

Normally, we are used to reading code from left to right, so seeing log before values is a bit weird, when the data is coming out of values, but we’ll come back to that.

Since pull-streams use an async call, we get two way back pressure. The source can slow the stream down by calling back slower. And the sink can slow the stream down by waiting longer until they call read again.

Slowing down is imporant, because that is how you save resource. software performance is like loosing weight, not building muscles. To go faster you must do less. Streams are about doing less, and about not doing things you don’t need to do yet.

throughs/transforms

Often we want to transform data, we might split a file into lines, or parse objects out of json, or look for matches.

for that we need a stream which is both a source and a sink. here is a through stream that takes applies a function to the data in the stream.

function map (fn) {
  return function (read) {
    return function (abort, cb) {
      read(abort, function (end, data) {
        if(end) cb(end)
        else cb(null, fn(data))
      })
    }
  }
}

A through stream is just a sink stream that returns a source stream. You can think of it as transforming that stream.

if we put these streams to gether it would look like this.

var vs = values([2,4,6])
var ms = map(function (e) { return e*10 })
var ls = log()

ls(ms(vs))

output:

  20
  40
  60

A through stream is both a source and a sink but also neither. To the source, the through seems like a sink, but it’s really just proxying the sink behind it. To the sink, the through is a source, but it’s really just transforming the values that the source gives it. This means through streams are very cheap. They don’t need to add their own back pressure, but they allow the source/sink back pressure to flow through them.

Now, we don’t like reversing our thinking from left to right to right to left. lets fix that.

function pull() {
  var stream = arguments[0]
  //detect a sink (or through)
  if(stream.length === 1) {
    var args = [].slice.call(arguments)
    return function (read) {
      return pull.apply(null, [read].concat(args))
    }
  }
  for(var i = 1; i 0) read(abort, cb)
      else      read(true, cb)
    }
  }
}

take doesn’t change the incoming data, but after you have called it n times (n is decremented until it’s zero) and then on the next call it calls read with true as the first argument. If you check back at values, if the first argument is true, then it just calls back immediately.

map doesn’t need to handle this case specially, it just passes it through.

that means we can do things like:

pull(
  values([1,2,3,4,5,6,7]),
  map(function (e) { return e*e })
  take(4),
  log()
)

output:

1
4
9
16

when take decides to stop reading, it passes that on to the source end of the stream and they can stop too. If there was work they where gonna do, they wont need to do it now. This property is known as lazyness. pull-streams are very lazy.

Because lazyness means we don’t do any work until we know we’ll need to, we can commit to work that we know we’ll never do! like, we can have an infinite stream.

function random () {
  return function (abort, cb) {
    cb(abort, abort || Math.random())
  }
}

random creats and infinite stream of random values.

pull(random(), take(5), log())

the output will look like:

0.16838359273970127
0.10774739156477153
0.6401788892690092
0.24491786980070174
0.7873906309250742

Aborting an infinite stream is fun, but in real life there are lots of reasons for aborting a stream. maybe you are uploading a file, and the wifi goes down (now there is no where to send that file)

maybe you are running a server with many clients downloading files, if some of those clients break, you need to clean up after them otherwise you will get a resource leak.

Sometimes a stream needs to stop in the middle, maybe a parser has found an error.

expanding or shrinking streams

sometimes we have a stream of big chunks that we want to break into small chunks (say, pages into lines) this means few items become many. On the other hand, we may want to filter out lines that don’t match some pattern or property - here we turn many into few.

flatmap takes a function that returns an array, and outputs a stream of the items in returned arrays. (note, the array could have zero or more items!)

function flatmap (fn) {
  var queue = []
  return function (read) {
    return function again (abort, cb) {
      if(abort)        return read(abort, cb)
      if(queue.length) return cb(null, queue.shift())

      read(null, function (err, data) {
        if(err) return cb(err)
        queue = fn(data)
        again(null, cb) //cb or read again if queue is empty.
      })
    }
  }
}

This gives us expanding or contracting streams! The trick is to maintain a queue of the current state, and on the next read, read from that instead of asking the source. If fn returns an empty stream, the oppositie happens, the source is possibly consulted many times without passing an answer through to the sink.


Notice that we have not installed any dependencies, or even imported any libraries. We have acomplished a lot, and without writing a function longer than 20 lines! I’m not pulling your leg, the code we have written is completely valid pull-streams, without bugs.

one small lie: log() will get a stack overflow if you use it on a long synchronous source stream, but will be fine with an async stream. use drain in the pull-stream module to get around this.

pull-streams are just a pattern. There are many modules that implement particular pull streams, but you can write a useful pull-stream without using them. many pull-stream modules don’t depend on other pull-streams, except maybe for testing.

That is how pull-streams work (except we havn’t gone into any detail about duplex stream - which are distinct from transform streams) but the best thing about pull-streams is that they are very easy to compose and it is very easy to write modules for and reuse those modules and so there are many pull-stream modules.

The most useful pull-streams are being curated into the pull-stream github org.

source for thes post as git repo

History Of Streams

The history of streams

contents

  • prehistory (streams before a stream api)
  • streams 1
  • summer of streams, userland vs core
  • streams2 (one step forward, 2 steps back)
  • streams3
  • conclusion

prehistory

in node.js’s early days back in 2010, the node.js website featured an example of a tcp echo server.

on feb 9th, 2010 the tcp stream’s events were named “receive” and “eof”. Shorly later, by feb 18th, 2010 those events had been renamed to their current “data” and “end”. There was not yet a Stream class, just an EventEmitter, but it was recognised that files and tcp sockets, etc were basically the same and a convention had developed.

This is the birth of node’s most important api, the Stream. But things were just beginning to take form.

Although it never appeared on the website, in [email protected] the sys module (now the util module) featured a pump method. by the time of the first node conference there was a base class for Stream and sys.pump(readStream, writeStream, callback) had become readStream.pipe(writeStream)

By [email protected] stream was now well cemented in the node.js api, but it wasn’t until pipe becomes chainable that it feels like the node.js streams as we know it.

Around this time the streams was beginning to get popular. When I discovered streams and realized they would be useful, I wrote event-stream which applied streams to a use similar to Functional Reactive Programming

I convinced substack and Max Ogden how useful streams were, and then they convinced everyone else.

Streams had been created by the core node developers, but they were then adopted by userspace - by people who maybe didn’t contribute much to node’s core, but did contribute greatly to node’s ecosystem.

These two groups had slightly different interpretations of what a stream was. Mainly, the core developers intended streams to only be applied to raw data. The core interpretation was that stream could be buffers or strings - but the userland interpretation was that a stream could be anything that is serializeable - anything that could become buffers or strings.

My argument was that you needed all the features of streams, passing items one at a time, possibly with an eventual end or error wether it was a sequence of buffers, bytes, strings or objects. Why not use the same api? If javascript was strictly typed, we never would have had this argument, but javascript didn’t care and the streams code at that time was agnostic - it worked just the same with objects as it did with strings.

problems with early node streams.

Streams were recognised as a powerful abstraction, but also difficult to use. One main difficulty was that data started flowing through streams as soon as you created them. Once you had a stream, had to pipe it somewhere before you did anything async.

This problem was exacerbated by the fact that the pause method was only advisory, it did not mean that data would stop flowing immediately.

There were also generally just too many things you had to get right for streams to work - not only did you have to implement write end and events 'data' 'end' and then there was the (still controversial) destroy methods and 'close' events.

I had written pause-stream to handle the second case, and more importanly through. Through represented about 11 months worth of myself using streams in userland, and I had finally figured out the pattern that was most general and easiest to use - from a userland persective (stream authors near core were more likely to implement streams that read from or wrote to some device - low level interfaces - but userland streams are mostly transformations)

I had called the module “through” because I figured that input, output… throughput. it was later pointed out by Isaac that “through” was a common word and “transform” stream was better name. In hindsight, I agree. through finally made creating streams easy, you just had to provide two functions, and all the edge cases were handled for you. through currently has 1601 dependant modules, and it’s heir, through2 (same api but uses streams2) has 5341 dependents. (check with npm-dependents)

Interestingly, I started this a few weeks before isaac began developing streams2 - streams2 addressed the problems in streams1, but made a much more radical departure from the api.

streams2 was paused until you called pipe, and was implemented around a read(n) function. read took an optional number of bytes, and would return a buffer with that many bytes in it. This was intended to be useful for implementing parsers, but to use it you had to have a handle on the stream directly, because pipe didn’t give you a way to specify a number of bytes.

streams2 was much more complicated than streams1, but worse, it was backwards compatible with streams1 (although streams1 was considered “legacy”)

streams1 was a readable 100 lines or so, and I read it many times, but by the time streams2 was ready to go into [email protected], the main class: Readable, was over 800 lines

During streams2 development the are-objects-data argument was settled with streams of objects officially sanctioned in the api (which would have otherwise excluded their possibility)

problems with streams2

All these changes in streams were causing their own problems, but one good move was developing streams2 in it’s own repository. This made it possible for those interested to depend on streams2 and test it out without having to use a fork of node.

Indeed, if you use through2 (and you very likey do use it, maybe because a module you use uses it) then you depend on that readable-stream repo. Although it’s built into node, the version in the repo is more cross version compatible. Modules that use readable-stream still work in old versions of node that did may not have as modern a version of streams!

Once the initial difficulties of piping new streams and getting friendlier apis for stream creation were surmounted, new problems emerged. There is a growing awareness that errors need to be propagated somehow. In this (open) issue, it’s shown that a naive http file server will have a file descriptor leak

To prevent that, you need to have a handle on both the source fs stream, and the destination stream that errored, not to mention a detailed understanding of how node streams function.

This is an open issue, and not even the iojs revolution could fix it. This would most surely be a breaking change, and so changing how core streams worked would be very difficult.

There is an approach in userland (that harkens back to sys.pump!)

There was one serious proposal to add error propagation to node streams. But the difficulty of providing backwards compatibilty made it untenable.

streams 3

The version of streams now in contemporary node@5 is considered streams3. it’s a significant refactor from streams2, but, is still backwards compatible. While it still supports streams2#read(n) that behavior is not activated until you trigger it by using it in a streams2 style. If you just use streams normally, via source.pipe(dest) then it works basically like streams1 streams3.pipe

If streams1 is classic streams and streams2 is new streams then streams3 is vintage streams. It’s old, but it’s new. But the sad thing, is that now it’s still too complex, because of features picked up during the streams2 detour.

stream 4? no.

If node streams teach us anything, it’s that it’s very difficult to develop something as fundamental as streams inside a “core” you can’t change core without breaking things, because things simply assume core and never declare what aspects of core they depend on. Hence a very strong incentive occurs to simply make core always be backwards compatible, and to focus only on performance improvements. This is still a pretty good thing, except sometimes decisions get inadvertently made that have negative implications, but that isn’t apparent until it’s too late. In this situation, a clean break is necessary - node.js itself is a great example of this. node created a IO performance improvement over previous dynamic languages (perl, ruby, python) because node was able to abandon threads and blocking, and write an entire stack from scratch.

But, usability problems with streams1 stopped us from seeing the need for error propagation until it was too late, and now node’s success has created it’s own obstacle for further improvement.

Streams are one of the best things about node, but they are still quite difficult to use, and they still have room for improvement. But that improvement cannot be made while they are a part of node.js core.

to be continued in part two.

Which JS crypto library should I use?

A friend recently asked me “which js crypto library should I use?” Since this is a question that many people have, I have answered with a blog post.

There are basically two schools of crypto library.

There is the openssl school - here you get a library that was built the same crypto as TSL, the most accessable one of these is node.js’s crypto module.

If you are using browserify, then it also works in the browser.

https://www.npmjs.com/package/crypto-browserify

This was a project that I started, but it was brought to completion by other contributors. It has a massive download count because it’s bundled in browserify - I have no idea how many people are actually using it, but probably lots.

That is probably what you should use if you are trying to be backwards compatible with a legacy system. But the problem is that all the standards in TLS have bugs, and you really need to understand those bugs to not use that in an insecure way. (hint: if the crypto standard has a 3 letter acronym, it has a bug you need to know about ;)

The other school is NACL. This is what I’d recommend if you are building something new, or just learning. NACL is carefully designed so that it’s primitves are simple and easy to use and don’t have the surprising bugs that openssl school librarys do. Where as TSL has a veritible buffet of options, NACL has only the best option in each category (signing, DH, hashing, authenticated encryption). This is better because you don’t have to risk not selecting the best thing. nacl was written by Daniel Bernstien (“djb”) who is widely revered among crypto hackers. Sometimes a little too revered - he is certainly an influential academic crypto expert - there are quite a few, but I suspect the reason he is so revered is that he actually produces code, and not just papers, making him a lot more useful to the hacker community.

nacl links: http://nacl.cr.yp.to/ there is a node binding, http://npm.im/sodium and in the browser you can use https://www.npmjs.com/package/libsodium-wrappers which has been compiled to javascript, and https://www.npmjs.com/package/tweetnacl which has been handwritten. I maintain https://npm.im/chloride which brings together all of these modules with the same api. You can use it to fallback to the small handwritten javascript version, or the large but fast js.

There is actually one more option: modern browsers now have built in crypto support - the WebCrypto api. https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto This is also a reasonable option for the openssl school. Here is probably the most contraversal part of the advice I’m gonna give here: I don’t really advise using this. It has a weird api, it uses promises for everything - but the only operation that is so slow it needs to be async is generating RSA keys, or using scrypt or pbkdf2 to strengthen user passwords. But worst, the political process of adding new browser apis is very slow and does not generally result in the best designs. Although WebCrypto is modern, it’s already stuck in legacy because it just exposes the openssl style primitives that browsers use to implement TLS. The one argument for this is that it’s faster than JS.

However, what I am more optimistic about is another developing browser technology - ASM.js and web assembly. This allows you to, end of the day, run C within JS efficiently. This will mean that you can just use nacl in the browser and it will be about as fast as webcrypto. But, there are a lot more interests who will benefit from C in JS (games etc) vs a tiny crypto community who is pushing for WebCrypto. My prediction is that the forces of C get us good browser crypto before the WebCrypto committee modernizes itself.

I’ve talked a lot about speed here - this may or may not be a problem for your application. Encrypting a few messages or verifying a few signatures is not gonna be slow enough to really be noticable - unless you have to do it to many messages in realtime. (exception: performance is critical for scrypt/pbkdf2)

That said and done, the most important question is: What is your motivation? security requires a different sort of thinking than ordinary web development. In short, it requires paranoia. But, a healthy paranoia tempered by a rational evaluation of the actual theats you system faces - more like a survialist, and less like a conspiracy theorist.

Choosing the right library is merely the first step - but understanding why to make that choice is the most important. To do this you find crypto experts and study why they make the decisions they do.

Of course, you can find this every where - and the blogs of things like https://whispersystems.org http://www.tor-project.org/ bitcoin etc are all worth studying, as well as classic papers, such as New Directions in Cryptography http://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.37.9720 (which is much more readable than you might expect! before this paper, cryptography is very different, so they had to explain it simply!)

Asymmetric Cryptography: Works Like Magic

It’s a common complaint that cryptography is too hard for regular people to understand - and that all our current cryptographically secure applications are designed for cyborgs and not humans. While the latter charge may well be correct, I argue that the former most certainly isn’t, because we have been teaching children the basic security principles behind asymmetric cryptography for probably thousands of years.

What am I talking about? A fairly tail called Rumplestiltskin, which is actually about bitcoin!

You probably heard this fairly tale as a child - but let me refresh your memory.

There is a miller, who drunkenly brags that is daughter can spin straw into gold.

probably, he was posting about his half baked cryptocurrency ideas on bitcointalk, and creating money “gold” from pointless work “spinning straw” sounds A LOT like bitcoin mining.

Anyway, the king is very impressed with his story.

the king is a venture capitalist?

And wants to see a demonstration, oh and if it doesn’t work he will cut off both their heads.

I have not heard about venture capitalists being quite this evil, but it seems some of them are into this medieval stuff

Of course, the miller and his daughter don’t actually have the ability to create gold by magic, so they are in big trouble! but just then a magic imp appears.

a hacker, who understands cryptography

The imp says he can spin straw into gold, but for a price: the daughter’s first born child.

in the modern version he wants her naked selfies

It’s a terrible deal, but the alternative is death, so they reluctantly accept. The imp spins straw into gold in 3 increasingly dramatic episodes.

The kind is satisified, and marries the daughter, making her queen.

their startup is aquired

One year later, the first child is born. The imp returns demanding his prize. Because they love their baby, the King and Queen pleads with the imp to get out of the deal. They offer him all their riches, but the imp is not interested! Desperately, they ask is there any other way? any at all? The imp replies, “Of course not! not unless you can guess my True Name”

the true name is actually his private key. If they can guess that, the hacker looses his magical power over them

“Okay I will try and guess your name” says the Queen. The imp just laughs! “you’ll never guess it!” “but I’ll give you three days to try!”

The imp skips off into the forrest, and the queen trys to think of his name for 3 days… but can’t figure it out.

The queen trys to brute force his private key. but there is not enough compute in the entire kingdom!

But then, the a messenger is travelling through the forrest, and he happens past a strange little man, dancing around a camp fire, singing:

ha ha ha!
te he he!
they’ll never guess my private key!
just three days! not enough to begin,
to guess my name is rumplestiltskin!

Being a messenger, he had a good memory for things he heard. When he arrived back at the castle, he mentioned the curious story to the queen.

the hacker had been careless with his private key

When the imp arrived in the morning, the queen greeted him by name. He was furious! He stamped his foot so hard the ground split open and then he fell into the gaping hole, never to be seen again. The king, queen, baby lived happily ever after, etc, etc.

they stole all his bitcoin


The simularities between this fairly tale and cryptography is uncanny. It has proof of work, it has private keys, it has an attempted brute force attack, and a successful (if accidental) end point attack. The essential point about your private key is captured successfully: the source of your magic is just a hard to guess secret, and that it’s easy to have a hard to guess name, but what gets you in the end is some work around when they steal your key some other way. This is the most important thing.

It’s not a talisman that can be physically protected, or an inate power you are born with - it’s just a name, but it must be an ungessable name, so the weirder the better.

“rumplestiltskin” is the german name for this story, which became wildly known in english after the brothers grim published their collection of folktales in the early 19th century, but according to wikipedia there are versions of this story throughout the europe, and the concept that knowing the true name of a magical creature give one power over it is common in mythology around the world.

How did the ancients come up with a children’s story that quite accurately (and amusingly) explains some of the important things about asymettric cryptography, and yet we moderns did not figure out the math that makes this possible this until the 1970’s?

Since the villian of the story is magical, really they have chosen any mechanism for the imps magic, why his name? Is this just a coincidence, or was there inspiration?

The astute reader has probably already guessed, but I think the simplest (and most fun) explaination is the best: extraterrestials with advanced cryptosystems visited earth during prehistory, and early humans didn’t really understand how their “magic” worked, but got the basic idea

To be continued in PART 2…

Social Networks and the Legal Hack

There have been a bit of fuss on my twitters about ello.co a hip new social network that is gonna change everything. They are gonna do things right, no ads, and not sell out their users.

They have a manifesto!

People are certainly trying it out, but the general vibe amongst my friends is that while they are ready for change, they are not yet convinced that we are gonna be treated better this time.

But here is an idea.

Burn their bridges to advertising as a business model, so that they couldn’t take advertising even if they wanted to.

How would you do that? Well, the simplest way would be with a terms of service! A terms of service is a contract with your users. Normally, terms of service are designed to protect the rights of the service provider. ToS are likely to include phrases such as “terms may change without notice”.

But it doesn’t have to be this way. The open source movement, for example, has performed a legal hack where by they used copyright law to undo copyrights. The right to prevent copying becomes the right for anyone to copy. Instead of reserving rights, open source gives them away!

An open source license might say that you are free to redistribute the code, or even to modify and sell it. It might say that the license must not be changed, or it might say that you can relicense the code.

This is not a thing that only opensource can do, services like ello.co and other social networks can also create a contract with their users that protects the users instead of the service.

just include a paragraph something like this

The user maintains ownership of the data they put into ello, including but not limited to content they post, replies to other users, and also metadata such as logs created by interacting with the ello user interface. Access to this material is granted to ello for the purposes of running the site, and to other users that have explicitly solicited that information by “following” in the user interface. Users have the right to refuse information “block” other users that have solicited their information. The data may not be passed on or sold to third parties. ello must honestly represent the information solicited by via the user interface, and must not inject spurious information that the user has not specifically solicited, this includes but is not limited to advertising. ello cannot change the terms of service without explicit consent of the users.

(Obviously get a real lawyer to write your ToS)

Something like that. It lays out clearly what ello is allowed to do with your data and metadata, and it also lays out information ello is to present to you - material created by users you have solicited content from (i.e. followed), and what not to show you, (i.e. advertising)

It may be too late for ello.co to do this now, since they may already have obligations to their investors that giving up a potential business model would conflict with.

But another social network could certainly do something like this. To my knowledge, such a legal hack has not been executed for a social network, so you might be the first!