code, github, proofs

Intermedia Tokens

“Think of this type of NFT as a magic item from your Dungeon Master. You can put as many magic potions in your inventory as you want, but unless they were issued by the DM, they are worthless in your game. There’s no need to distribute your inventory list to every DM in every game in the world to prove your items are valuable.”

In April 2022, it was announced in ARTnews that the 2.9 million dollar NFT of Jack Dorsey’s first tweet had dropped 99% in value. The floor price of NFTs is down 84% since that peak in April and it was revealed that a Bored Ape NFT purchased by Justin Bieber for $1.3 million is now worth just south of $70k. Time will tell whether the NFT market has crashed, was always a scam, or will reemerge in some new and better form. However, given the ecological costs of minting, selling, and distributing NFTs, one could make a valid case that they just weren’t worth it to begin with. There’s money to be made, however, so some form of tokenized art probably isn’t disappearing any time soon. There’s also a valuable philosophical argument that digital authenticity is needed in the art world right now. Better solutions need to be examined.

This is my first draft attempt at addressing this problem. I’m not claiming it’s solved (yet), but it’s worth getting this idea down so it can be vetted and discussed. Think of this post as an unrefereed preprint. I hope this concept of an “intermedia token” circulates so it can be improved. Even if experts in the art and technology field find it totally unviable and ridiculous, it’s worth the effort.

What is an “Intermedia Token”?

An intermedia token is a non-fungible token except proof-of-ownership does not reside on a distributed ledger, it resides on the token itself. In fact, the spirit of this type of token is less focused on the financial obsession with proof-of-ownership and more with collaboration and provenance. An intermedia token is just a self-contained HTML file with JSON web tokens included in the document’s metadata. Everything in the <article> tag within the document body  is considered the actual content of the token.

To understand intermedia tokens, it’s helpful to first understand JWTs. JSON Web Tokens are an open, industry standard RFC 7519 method for securely representing claims between two parties. Often, these are used for authentication and client-side sessions, but here they are used as static representations of digital transfer.  A JWT is serialized json representing a series of claims. It consists of three json objects. A header, a payload, and a signature. The header is used to specify information about the JWT itself such as the algorithm, the payload is the actual content and this is where the provenance claims of the intermedia token will reside, and the signature will be a combination of the header and payload signed using public key encryption.

How to create an Intermedia Token

  1. Create some amount of content and place it within an <article> element within the body of the HTML document. An intermedia token is completely self-contained, so there should be no references to external resources. All images and media should be included as base64 data URLs and all CSS and JavaScript should be inline.
  2. Be sure to have public and private RSA keys to sign the content and specify subject and audience.
  3. Create a base64 encoding of the entire stringified <article> element, including the opening and closing tags.
  4. Sign the base64 encoded article with a private key
  5. Create a <meta> tag with a name=”jwt-block-n“, where n is the number of the block. The blockchain for each token is contained in the HTML file itself. Therefore, the genesis block is named “jwt-block-0”
  6. Create a JWT payload that contains information about the ownership. The following are the keys and suggested values for this payload.
    1. “iss” – The issuer of the token. Could be a domain, email, wallet address, or public key.
    2. “iat” – Issued at time. A Unix timestamp is recommended.
    3. “sender_name” – the sender’s name or a message they’d like to leave
    4. “sender_pk” – The sender’s public key
    5. “receiver_name” – Name of person receiving the token
    6. “receiver_pk” – Public key of the person the token is being given to
    7. “block” – The block number
    8.  “article_hash” – A SHA256 hash of the <article> element, the content of the token
  7. Sign the JWT with your RSA secret key used to also sign the message.
  8. Insert the completed JWT string into the <meta> tag content attribute for the block being generated

That’s it – that’s all there is to create a basic intermedia token. More options could be added, such as inclusion of automatic content creation, an optional topic <meta> tag, and more. However, the crucial step of creating a JWT with the proper payload and including it in the correctly specified <meta> tag block is all that is needed.

To verify an intermedia token:

  • Intermedia tokens are immutable, therefore each “article_hash” value in every block must be identical
  • An accurate chain of custody must be present from block to block. E.g. the “sender” for “jwt-block-6” must be the “receiver” for “jwt-block-5”

To transfer ownership of an intermedia token:

  1. Base64 encode the <article> element and its contents and verify it’s the same as the previous block’s “article_hash”. Add this as your “article_hash”.
  2. Add your recipient’s public key as the “receiver_pk” value.
  3. Add your public key as the “sender_pk value
  4. Increment the block number in the JWT payload
  5. Add the resulting JWT to a new meta tag with an incremented “jwt-block-n” name.

You are now free to participate in an alternate digital art economy! Is this perfect? Of course not. There is no distributed ledger, for example. Anyone can strip out your metadata, steal your content, and create their own onboard blockchain in the <meta> tags. However, they are also free to steal the art assets of your NFT and re-mint it on a the Ethereum network, too. This thought experiment and proof-of-concept is just that – experimental. It is about play, choice, and collaboration.

But for those groups using NFTs to grant users exclusive access to communities and events or Discord channels, this should work just fine. The exclusive community just mints a bunch of these tokens to themselves and then transfers ownership to whoever they want. Unless Block 0 is signed by the “exclusive community”, the NFT is invalid for this purpose.

Think of this type of NFT as a magic item from your Dungeon Master. You can put as many magic potions in your inventory as you want, but unless they were issued by the DM, they are worthless in your game. There’s no need to distribute your inventory list to every DM in every game in the world to prove your items are valuable.

The following is a python implementation of an Intermedia Token issuing script. It assumes you have a secret and public key in the same directory as the script. It utilizes the OpenAI API for content generation. Why not just make your own images and text instead of generating? You can! But “discovering” NFTs instead of “creating” NFTs is a different type of wonder. It also serves as a very small proof-of-work.

You can see the full proof-of-concept here. It includes a mechanism for minting, validating, and transferring these tokens, as well as a simple user interface.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#!/usr/bin/env python
# coding: utf-8
 
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
import jwt
import requests
import time
from yattag import Doc
import base64
import openai
import os
from dotenv import load_dotenv
from bs4 import BeautifulSoup
doc, tag, text = Doc().tagtext()
unix_timestamp = int(time.time())
 
load_dotenv()
openai.api_key = os.getenv('OPENAI_API_KEY')
sender_name = "Matthew Butler"
sender_public_key = os.getenv('INTERMEDIA_TOKEN_PUBLIC')
sender_secret_key = os.getenv('INTERMEDIA_TOKEN_PRIVATE')
receiver_name = "Matthew Butler"
print(len(sender_secret_key))
 
# just sending to myself for now
receiver_public_key = sender_public_key
print("minting...")
 
# the topic
with open('prompt.txt', 'r') as f:
    prompt = f.read()
prefix = "potion"
 
filename = prefix + "_intermedia_token_" + str(unix_timestamp)
 
 
def sha256(message):
    digest = SHA256.new()
    digest.update(message.encode('utf-8'))
    return digest.hexdigest()
 
 
def get_url_as_base64(url):
    return base64.b64encode(requests.get(url).content).decode('utf-8')
 
 
response = openai.Completion.create(
    engine="text-davinci-002",
    prompt=prompt,
    temperature=0.7,
    max_tokens=50,
    top_p=1,
    frequency_penalty=0,
    presence_penalty=0
)
 
dalle_response = openai.Image.create(
    prompt=prompt,
    n=1,
    size="256x256"
)
 
base64_image = get_url_as_base64(dalle_response['data'][0]['url'])
output = response.choices[0].text
 
# put attr in alphabetical order for beautiful soup
doc.asis('')
with tag('html', lang="en"):
    with tag('head'):
        with tag('title'):
            text(filename)
        doc.stag('meta', charset='utf-8')
        doc.stag('meta', content=prompt, name='topic')
        doc.stag('meta', content="%%replace%%", name='jwt-block-0')
    with tag('body'):
        with tag('article', id='token'):
            with tag('h4', id='text', style="width:256px;"):
                text(output)
            with tag('figure', id='image', style="margin:0px"):
                doc.stag('img', alt=prefix,
                         src="data:image/png;base64," + base64_image)
 
# need to make everything a beautiful soup object for hashing
docval = doc.getvalue()
soup = BeautifulSoup(docval, 'html.parser')
article = soup.find('article')
page = str(soup)
 
payload = {
    "iss": "intermedia_tokens_v1_1",
    "iat": unix_timestamp,
    "sender_name": sender_name,
    "sender_pk": sender_public_key,
    "receiver_name": receiver_name,
    "receiver_pk": receiver_public_key,
    "block": 0,
    "article_hash": sha256(str(article)),
}
 
encoded_jwt = jwt.encode(payload, sender_secret_key, algorithm="RS256")
page = page.replace("%%replace%%", encoded_jwt)
 
token = open(filename + ".html", "a")
token.write(page)
token.close()
print(filename + ".html")