Join our official collaboration hub!
AllPay is an open on-chain addressing protocol that works on the Allegory naming protocol. It allows senders to generate "stealth addresses" independently from the recipient's on-chain username & self-sovereign identity. This is very extensible and can be used in a variety of use-cases beyond payments. Its fundamental focus is user autonomy and trust-less security by eliminating the need for a third party, trusted or otherwise.
AllPay enables any user to send coin, optionally along with any type of message—the protocol is content-agnostic—to an AllPay-enabled recipient. The coins may be the main objective of the transaction, or merely incidental! This can be achieved independently by the sender and the receiver with complete privacy and security, and without the need for an off-chain service (trusted or otherwise) for relaying the message, generating addresses, or persisting any information off the blockchain. Hence, it works entirely within the confines of the senders' and receivers' wallets and the blockchain.
Any user in possession of an Allegory name can use their Allegory-enabled wallet to register for AllPay. The registration is committed on-chain in the form of OP_RETURN metadata of an Allegory transaction that extends the user's Allegory name. This metadata contains the following three keys & more:
- IdentityPubKey - for establishing an on-chain identity off which stealth addresses are generated.
- AuthEncryptPubKey - curve25519 based ECDH key exchange, for end to end encryption & perfect forward secrecy.
- SigningPubKey - ed25519 based detached signatures.
version: 1 name: - 97 - 112 action: owner-input: index: 1 owner-output: owner: index: 2 registrations: - service: AllPay mode: standard identity-pub-key: <pubKey> signing-pub-key: <pubKey> auth-encrypt-pub-key: <pubKey>
Ideally we would want the sender to be able to produce a nonce public key on behalf of the recipient trivially, for which the recipient is somehow able to derive its corresponding private key.
Essentially, we want two functions
g such that:
epPubKey = f(identityPublicKey) epPvtKey = g(identityPvtKey)
Incidentally, these primitives are the used in the BIP-0032 specification with the assistance of a shared "chain-code" parameter.
So our functions
g are equivalent to the below functions described in BIP-32 to compute a child extended key pair from the parent extended keys.
CKDpriv((kpar, cpar), i) → (ki, ci) CKDpub((Kpar, cpar), i) → (Ki, ci) Note: A constant i = 1 is used in our case.
Note that while this approach does leverage chain-code and some primitives introduced as a part of BIP-0032, it does not use the traditional 'hierarchical deterministic' approach of deriving PubKeys using incremental key indices and the ephemeral keys are purely random and non-deterministic. Thus, a sender who wishes to send coins or message(s) to an AllPay recipient has to look up the recipient's IdentityPubKey in the blockchain. They then use the IdentityPubKey to derive another nonce PubKey, which we refer to as the StealthPubKey.
The sender then drafts an AllPay transaction addressed to the StealthPubKey that carries coin and/or message(s). However, for the recipient to be able to derive the private key corresponding to the StealthPubKey (the StealthPvtKey), the chain-code has to be shared in private with the recipient. We use the "Public Key authenticated encryption" technology based on x25519-xsalsa20-poly1305 which is considered very secure and efficient. The transaction itself carries the chaincode and other params needed by the recipient to derive the private key and some other additional metadata to help identify and authenticate the sender and recipient to each other.
OP_FALSE OP_RETURN <AllPay-Protocol-ID/Version | 8 bytes> #(bytes for regular tx = "41 6c 6c 50 61 79 30 31") <Ephemeral-PublickKey | 32 bytes> <XOR-Encrypted-Ephemeral-PrivateKey | 32 byte> #(Ephemeral private key) XOR (Senders private key) <Sender-Authenticated-Encrypted-Metadata | variable bytes> #encrypted message for sender which contains - Recipient name <AuthEncrypt-Nonce | 24 bytes> #the nonce that will used by recipient and sender to decrypt their messages <Recipient-Authenticated-Encrypted-Metadata | variable bytes> #encrypted message for recipient which contains - Sender name <XOR-Encrypted-Sender-Signature | 64 bytes> #(64 byte detached signature (using ED25519 private key) of shared-secret-key concatenated with list of outpoints ) XOR (32 byte shared-secret-key concatenated to 64 bytes)
AllPay also supports a Mail extension where the focus is on transmitting a mail or a message to the intended recipient(s), end-to-end encrypted with perfect forward secrecy where not even the metadata of the mail is exposed. The format of the OP_RETURN output data of an AllPay Mail transaction is as follows:
**The sender generates a random 32 byte Shared Secret Key(SSK) as first step.
OP_FALSE OP_RETURN <AllPay-Protocol-ID/Version | 8 bytes> #(bytes for mail tx = "41 41 4d 61 69 6c 30 31") <Thread-ID | 4 bytes> #Randomly generated. Used to link replies to the same thread and reuse the same (SSK). Set to all zeroes to force fresh keys for subsequent mails in thread. <Ephemeral-PublickKey | 32 bytes> <XOR-Encrypted-Ephemeral-PrivateKey | 32 byte> #(Ephemeral private key) XOR (Senders private key) <XOR-Encrypted-Shared-SecretKey | 32 bytes> #(Ephemeral private key) XOR (SSK) <AuthEncrypt-Nonce | 24 bytes> #the common nonce that will used by all recipients to decrypt their individual messages <Recipient(1)-Authenticated-Encrypted-Metadata | variable bytes> #encrypted message for recipient '1' which contains (SSK) ... <Recipient(n)-Authenticated-Encrypted-Metadata | variable bytes> #encrypted message for recipient 'n' which contains (SSK) <Common-Encrypted-Metadata | variable bytes> #contains a JSON/CBOR encoded structure with - (i) Sender name - (ii) List of Recipient names - (iii) Subject - (iv) number of attachments (subsequent op_returns) **The entire message is encrypted with (SSK)** <XOR-Encrypted-Sender-Signature | 64 bytes> #(64 byte detached signature (using ED25519 private key) of shared-secret-key concatenated with list of outpoints ) XOR (32 byte shared-secret-key concatenated to 64 bytes)
On to the next important problem: since the public key and address of the recipient is generated independently (non-deterministic) by the sender, the recipient is not aware of which address to expect the inbound transaction at. This could be considered beyond the scope of the stealth address protocol, and this gap could be bridged by offline channels. Nevertheless, we chose to use the Bitcoin ledger itself for notifying the recipients of their inbound transactions. This requires an additional transaction carrying encrypted metadata privy only to the recipient – which, while revealing to the public chain analysis the fact that some transaction was sent to a recipient, does not reveal the senders' or recipients' identities or address, not even the transaction hash.
This marker transaction is then drafted by the sender, with an OP_RETURN containing the IdentityPubKey and TxID cipher-text of the AllPay Tx that carries the coin and/or message(s). This transaction need not contain other outputs addressed to the recipient. The recipient's AllPay-enabled wallet can then scan for such marker transactions and use them as pointers to discover AllPay Mail transactions addressed to themselves.
OP_FALSE OP_RETURN <AllPay-Protocol-ID/Version | 8 bytes> #(bytes for AllPay Meta tx = "41 50 4d 65 74 61 54 78") <Recipient-IdentityPubKey | 32 bytes> #recipient's registered IdentityPubKey <Sender Ephemeral PublickKey | 32 bytes> #Must be unique; do NOT repeat this key from actual AllPay tx <Recipient-AuthEncrypt-Nonce | 24 bytes> #the nonce that will used by recipient to decrypt messages <XOR-Encrypted-TxID | variable bytes> #(Txid of actual AllPay tx) XOR (Shared secret key)