Introducing Cipher Page, an encryption-based communication platform. Encryption keys are managed on-chain, while larger data, including ciphertext is meant to be aliased to Interplanetary Filesystem (IPFS). I believe this work holds value within the Aleo Ecosystem, as it aims to raise awareness about privacy and enables users to embrace zero-knowledge concepts without the need to comprehend the intricate technical details. Instead, users can interact through a wallet and user interface. The frontend incorporates multiple examples, licensed under the copy-left (GPL) license, which any user can utilize as a foundation for building a React frontend. Additionally, the Aleo contract itself introduces novel concepts for implementing both public and private aspects of the application logic using mappings.
Copy program.example.json to program.json and fill in the values you wish to change. If the address is changed in program.json, the Makefile will need to be updated to reflect the new address as record.owner in many cases.
To test this Aleo program, run:
make test
This Makefile contains example commands for testing each transition. The program has a frontend at https://github.com/arosboro/newsletter-fe. While it is difficult to operate from the command line, it is recommended to use the frontend either locally or hosted at https://cipher.page to interact with the deployed newsletter_v0_1_0.aleo
contract.
Bytes24
: This struct contains 24u8
values that convert to 1 byte each. The entire value is frequently utilized as a BigInt nonce, for example.Bytes64
: This struct holds fouru128
values, each of which maps to 16 bytes. When combined, these values represent a value under 64 bytes.SharedIssue
: This struct holds the nonce and path (to IPFS) for the Group symmetric encrypted value which can be decrypted usingkey
andpath
.
owner
: This is the record holderop
: This is the record instantiating addressid
: This is a unique field value based on a BHP 256hash_to_field
algorithm running on thegroup_symmetric_key
member_sequence
: The op chooses members to invite, and each time increments the member sequence so the number of members invited is reflected by this field value.base
: Whether or not the record owner is the op. Base indicates it's an owned record not a derivative record (Meaning it's not being used to deliver as an accepted subscriber, but rather the Newsletter creator holds the record).revision
: True when the record has been received through an invitation but not initially created by the record holder.title
: Bytes64 the IPFS path to the cipher-text of the title.title_nonce
: Bytes24 the u8IntArray nonce of the cipher-text.template
: Bytes64 the IPFS path to the cipher-text of the template.template_nonce
: Bytes24 the u8IntArray nonce of the cipher-text.content
: Bytes64 the IPFS path to the cipher-text of the content.content_nonce
: Bytes24 the u8IntArray nonce of the cipher-text.group_symmetric_key
: Bytes64 the symmetric key for Group Encrypted Messaging.individual_private_key
: Bytes64 the individual private key paired to the mapping of the public key which indicates the user accepted the invite.
owner
: The owner eitherop
or the subscriberop
: The manager of the group.id
: This is a unique field value based on a BHP 256hash_to_field
algorithm running on thegroup_symmetric_key
. It serves to tie newsletters to subscriptions.member_sequence
: This field value pertains to the sequence the member used with the cantor's pairing function in the mapping assignment.member_secret_idx
: This field value identifies the combination of the id and member sequence provided by cantor's pairing function. It is the mapping key for themember_secrets
mapping.
newsletter_member_sequence
: (Field => Field) The newsletter id is indexed to determine how many members have been invited to the newsletter group.member_secrets
: (Field => SharedSecret) The cantor's pairing of member sequence of each member is mapped to Shared Public Key and Public Addressnewsletter_issue_sequence
: (Field => Field) Each time a member delivers an issue the sequence increments with respect to the key:newsleter.id
.newsletter_issues
: (Field => SharedIssue) Cantor's pairing with eachNewsletter.id
and associatednewsletter_issue_sequence
and is used to capture the path to each digest and nonce of every encrypted field for all subscribers.
Inputs:
title
: Bytes64 the IPFS path to the title.title_nonce
: Bytes24 the nonce from the sodium library.template
: Bytes64 the IPFS path to the template.template_nonce
: Bytes24 the nonce from the sodium librarycontent
: Bytes64 the IPFS path to the content.content_nonce
: Bytes24 the nonce from the sodium library.group_symmetric_key
: Bytes64 the key for group communication.individual_private_key
: Bytes64 the key for decrypting own messages.shared_public_key
: Bytes64 the key that other people can use to send messages.shared_recipient
: Bytes64 the address for other transitions and messages.
Outputs:
Newsletter
: Newsletter the record you created.
Finalize:
newsletter_member_sequence
: (Field => Field) The newsletter id is indexed to a default of 1field.member_secrets
: (Field => SharedSecret) The cantor's pairing ofNewsletter.id
andmember_sequence
of the first member is mapped to aSharedSecret
struct containingshared_public_key
andshared_recipient
.
Inputs:
newsletter
: Newsletter the record indicating you can invite members to this group.recipient
: address the user's address you want to send an invite to.
Outputs:
Newsletter
: Newsletter the record you created minted back to you to maintain your group key and private key.Newsletter
: Newsletter the record sent to the recipient.
Finalize:
newsletter_member_sequence
: (Field => Field) The newsletter id is indexed to the next member sequence.
Inputs:
newsletter
: Newsletter the record you received as a result of another user executing invite.secret
: Bytes64 your private key.shared_public_key
: Bytes64 the public key to match your secret.shared_recipient
: Bytes64 the address you signed up with.
Outputs:
Newsletter
: Newsletter the record you accepted minted back to you to maintain your group key and private key.Subscription
: Subscription the record for the operator to identify a subscriber.Subscription
: Subscription the record for the subscriber to manage their subscription.
Finalize:
member_secrets
: (Field => SharedSecret) The cantor's pairing of member sequence of the accepting member is mapped to aSharedSecret
struct containingshared_public_key
andshared_recipient
.
Inputs:
newsletter
: Newsletter a newsletter record you hold.title
: Bytes64 the IPFS path to the title you updated.title_nonce
: Bytes24 the U8IntArray of the nonce you used.content
: Bytes64 the IPFS path to the content you updated.content_nonce
: Bytes24 the U8IntArray of the nonce you used.issue_path
: Bytes64 The IPFS path of the digests for every member receiving an issue.issue_nonce
: Bytes64 The U8IntArray representation converted to bytes of the nonce using Group Symmetric key which encrypted issue path's contents.
Outputs:
Newsletter
: Newsletter the record you used to manage your issue delivery.
Finalize:
newsletter_issue_sequence
: (Field => Field) The newsletter id is indexed to the next issue sequence.newsletter_issues
: (Field => SharedIssue) The cantor's pairing of issue sequence of the accepting member is mapped toissue_path
andissue_nonce
of SharedIssue. The resource atissue_path
is encrypted using the Group Symmetric Key andissue_nonce
.
Inputs:
newsletter
: Newsletter a newsletter record you hold.title
: Bytes64 the IPFS path to the title you updated.title_nonce
: Bytes24 the U8IntArray of the nonce you used.template
: Bytes64 the IPFS path to the template you updated.template_nonce
: Bytes24 the U8IntArray of the nonce you used.content
: Bytes64 the IPFS path to the content you updated.content_nonce
: Bytes24 the U8IntArray of the nonce you used.
Outputs:
Newsletter
: Newsletter the record you provided new content for.
Inputs:
subscription
: The subscription you wish to unsubscribe from.
Outputs:
bool
true if the subscription was removed.
Finalize:
member_secrets
: (Field => SharedSecret) The cantor's pairing ofNewsletter.id
andmember_sequence
of the accepting member is removed (TheSubscription.member_secret_idx
value is used).
- Note: Unsubscribing leaves past content visible to the user in read only mode.
cantors_pairing
: Map two field values to a field value which will not intersect if there is sufficient entropy when choosingNewsletter.group_symetric_key
, this will result in a unique value for each newsletter id and member sequence combination.is_empty_bytes64
: Determine if a Bytes64 struct contains only0u128
values forb0
,b1
,b2
,b3
. Returnbool
.