import { assign } from 'lodash' ; import { AtomId , Atom , atom } from '@casual-simulation/causal-trees' ; import { sign , verify , keypair , getHash } from '@casual-simulation/crypto' ; import stringify from '@casual-simulation/fast-json-stable-stringify' ; /**
 * The list of operation types.
 */ export enum AuxOpType { Root = 0 , Bot = 1 , Tag = 2 , Value = 3 , Delete = 4 , Insert = 5 , Certificate = 6 , Revocation = 7 , Signature = 8 , TagMask = 9 } /**
 * Defines a union of all the possible op types.
 */ export type AuxOp = BotOp | TagOp | ValueOp | InsertOp | DeleteOp | CertificateOp | SignatureOp | RevocationOp | TagMaskOp ; /**
 * Defines an interface for all the AUX atom values.
 */ export interface AuxOpBase { /**
     * The type of the operation.
     */ type : AuxOpType ; } /**
 * Defines an atom value that instructs the system to create a bot.
 */ export interface BotOp extends AuxOpBase { type : AuxOpType . Bot ; /**
     * Gets the ID of the bot.
     */ id : string ; } /**
 * Defines an atom value that instructs the system to create a tag on a bot.
 */ export interface TagOp extends AuxOpBase { type : AuxOpType . Tag ; /**
     * The name of the tag.
     */ name : string ; } /**
 * Defines an atom value that serves as the root for changes to the value of a tag.
 */ export interface ValueOp extends AuxOpBase { type : AuxOpType . Value ; /**
     * The initial value.
     */ value : any ; } /**
 * Defines an atom value that instructs the system to insert a set of text into a tag.
 * When inserting into a ValueOp this acts as inserting text into the value part of a tag.
 * When inserting into a TagOp this acts as inserting text into the name part of a tag.
 */ export interface InsertOp extends AuxOpBase { type : AuxOpType . Insert ; /**
     * The index that the text should be inserted into.
     * Note that this index refers to the previous insertion operation and
     * not the full text string.
     */ index : number ; /**
     * The text to insert.
     */ text : string ; } /**
 * Defines an atom value that instructs the system to delete an item.
 * If applied onto a bot, the bot will be deleted.
 * If applied to an insert operation, the specified substring will be deleted from that insertion's text.
 */ export interface DeleteOp extends AuxOpBase { type : AuxOpType . Delete ; /**
     * The start index of the substring to delete.
     * If not specified the entire parent element will be tombstoned.
     */ start ? : number ; /**
     * The end index of the substring to delete.
     */ end ? : number ; } /**
 * Defines an atom value that instructs the system to create a certificate. (See https://en.wikipedia.org/wiki/Public_key_certificate)
 * Certificates create a chain of trust (https://en.wikipedia.org/wiki/Chain_of_trust) that can be used to validate that specific tag values have been created by a particular certificate.
 */ export interface CertificateOp extends AuxOpBase { type : AuxOpType . Certificate ; /**
     * The keypair that is stored in the certificate.
     * Stores both the public and private keys in the format specified in
     * the crypto package.
     * The public key is unencrypted and available for anyone to use
     * while the private key is encrypted with a password.
     */ keypair : string ; /**
     * The signature that is applied to this certificate.
     * Must be from the atom's cause.
     */ signature : string ; } /**
 * Defines an atom value that represents a signature applied to a value.
 */ export interface SignatureOp extends AuxOpBase { type : AuxOpType . Signature ; /**
     * The ID of the atom that created this signature was created for.
     */ valueId : AtomId ; /**
     * The hash of the atom that this signature was created for.
     */ valueHash : string ; /**
     * The signature data that was created by the certificate.
     */ signature : string ; } /**
 * Defines an atom value that represents the revocation of a certificate.
 */ export interface RevocationOp extends AuxOpBase { type : AuxOpType . Revocation ; /**
     * The ID of the certificate that created this revocation.
     */ certId : AtomId ; /**
     * The hash of the certificate that created this revocation.
     */ certHash : string ; /**
     * The signature data that was created by the certificate.
     */ signature : string ; } /**
 * Defines an atom value that instructs the system to create a tag mask.
 */ export interface TagMaskOp extends AuxOpBase { type : AuxOpType . TagMask ; /**
     * The ID of the bot that this tag mask applies to.
     */ botId : string ; /**
     * The name of the tag.
     */ name : string ; } /**
 * Creates a bot atom op.
 */ export function bot ( id ) { return op < BotOp > ( AuxOpType . Bot , { id , } ) ; } /**
 * Creates a tag atom op.
 */ export function tag ( name ) { return op < TagOp > ( AuxOpType . Tag , { name , } ) ; } param value export function value ( value ) { return op < ValueOp > ( AuxOpType . Value , { value , } ) ; } param index param text export function insertOp ( index , text ) { return op < InsertOp > ( AuxOpType . Insert , { index , text , } ) ; } param index export function deleteOp ( start ? , end ? ) { return op < DeleteOp > ( AuxOpType . Delete , { start , end , } ) ; } param keypair param signature export function cert ( keypair , signature ? ) { return op < CertificateOp > ( AuxOpType . Certificate , { keypair , signature : signature || null , } ) ; } param valueId param valueHash param signature export function sig ( valueId , valueHash , signature ) { return op < SignatureOp > ( AuxOpType . Signature , { valueId , valueHash , signature , } ) ; } param certId param certHash param signature export function revocation ( certId , certHash , signature ) { return op < RevocationOp > ( AuxOpType . Revocation , { certId , certHash , signature , } ) ; } param botId param name export function tagMask ( botId , name ) { return op < TagMaskOp > ( AuxOpType . TagMask , { botId , name , } ) ; } export function op < T extends AuxOp > ( type , extra ) { return < T > assign ( { type : type , } , extra ) ; } param password export function selfSignedCert ( password ) { const keys = keypair ( password ) ; return signedCert ( null , password , keys ) ; } param signingCert param signingPassword param certKeypair export function signedCert ( signingCert , signingPassword , certKeypair ) { const bytes = certSigningBytes ( signingCert , certKeypair ) ; const sig = sign ( signingCert ? signingCert . value . keypair : certKeypair , signingPassword , bytes ) ; if ( ! sig ) { throw new Error ( 'Unable to sign the certificate.' ) ; } return cert ( certKeypair , sig ) ; } param signingCert param signedCert export function validateCertSignature ( signingCert , signedCert ) { const bytes = certSigningBytes ( signingCert , signedCert . value . keypair ) ; try { return verify ( signingCert ? signingCert . value . keypair : signedCert . value . keypair , signedCert . value . signature , bytes ) ; } catch ( err ) { return false ; } } param signingCert param signingPassword param value export function signedValue ( signingCert , signingPassword , value ) { const bytes = valueSigningBytes ( signingCert , value ) ; const signature = sign ( signingCert . value . keypair , signingPassword , bytes ) ; if ( ! signature ) { throw new Error ( 'Unable to sign the value.' ) ; } return sig ( value . id , value . hash , signature ) ; } param signingCert param signature export function validateSignedValue ( signingCert , signature , value ) { try { const bytes = valueSigningBytes ( signingCert , value ) ; return verify ( signingCert . value . keypair , signature . value . signature , bytes ) ; } catch ( err ) { return false ; } } param signingCert param signingPassword param atom export function signedRevocation ( signingCert , signingPassword , atom ) { const bytes = revocationSigningBytes ( signingCert , atom ) ; const signature = sign ( signingCert . value . keypair , signingPassword , bytes ) ; if ( ! signature ) { throw new Error ( 'Unable to sign the value.' ) ; } return revocation ( signingCert . id , signingCert . hash , signature ) ; } param signingCert param revocation export function validateRevocation ( signingCert , revocation , atom ) { try { const bytes = revocationSigningBytes ( signingCert , atom ) ; return verify ( signingCert . value . keypair , revocation . value . signature , bytes ) ; } catch ( err ) { return false ; } } function certSigningBytes ( signingCert , certKeypair ) { if ( ! signingCert ) { return signingBytes ( [ certKeypair ] ) ; } else { return signingBytes ( [ signingCert . hash , certKeypair ] ) ; } } function valueSigningBytes ( signingCert , value ) { return signingBytes ( [ signingCert . hash , value . hash ] ) ; } function revocationSigningBytes ( signingCert , value ) { return signingBytes ( [ signingCert . hash , value . hash ] ) ; } function signingBytes ( data ) { const json = stringify ( data ) ; const encoder = new TextEncoder ( ) ; return encoder . encode ( json ) ; } param botId param tag param value export function tagValueHash ( botId , tag , value ) { return getHash ( [ botId , tag , value ] ) ; }