OverviewFilesCommitsIssuesDocsReleasesPeers

ollama_semantic_function_calling / ollamatools-new / keet / node_modules / autobase / lib / encryption.js

108 lines · javascript ref:
const sodium = require('sodium-universal')
const Hypercore = require('hypercore')
const crypto = require('hypercore-crypto')
const c = require('compact-encoding')
const b4a = require('b4a')

const HypercoreEncryption = Hypercore.DefaultEncryption

const nonce = b4a.alloc(sodium.crypto_stream_NONCEBYTES)
const hash = nonce.subarray(0, sodium.crypto_generichash_BYTES_MIN)

const [NS_HASH_KEY] = crypto.namespace('autobase/encryption', 1)

class WriterEncryption {
  static PADDING = 8

  constructor (base, opts) {
    this.base = base

    this.classic = false
    this.keys = null
    this.hashKey = null
  }

  padding (context) {
    return context.manifest.version <= 1 ? HypercoreEncryption.PADDING : WriterEncryption.PADDING
  }

  _load (context) {
    this.keys = HypercoreEncryption.deriveKeys(this.base.encryptionKey, context.key)
    this.classic = context.manifest.version <= 1

    if (!this.classic) this.hashKey = crypto.hash([NS_HASH_KEY, this.keys.block])
  }

  // Padding is blockHash and key id
  _encodePadding (padding, block, keyId) {
    sodium.crypto_generichash(hash, block, this.hashKey)
    padding.set(hash.subarray(0, 8)) // copy first 8 bytes of hash

    c.uint64.encode({ start: 4, end: 8, buffer: padding }, keyId)
    hash.fill(0) // clear nonce buffer
  }

  encrypt (index, block, fork, context) {
    if (!this.keys) this._load(context)

    if (this.classic) {
      return HypercoreEncryption.encrypt(index, block, fork, this.keys.block, this.keys.blinding)
    }

    const padding = block.subarray(0, WriterEncryption.PADDING)
    block = block.subarray(WriterEncryption.PADDING)

    this._encodePadding(padding, block, 0)

    c.uint64.encode({ start: 0, end: 8, buffer: nonce }, index)

    // Blind block hash and key id
    encrypt(padding, nonce, this.keys.blinding)

    padding[0] = 1 // version in plaintext

    nonce.set(padding, 8, 16)

    // The combination of index, key id, fork id and block hash is very likely
    // to be unique for a given Hypercore and therefore our nonce is suitable
    encrypt(block, nonce, this.keys.block)
  }

  decrypt (index, block, context) {
    if (!this.keys) this._load(context)

    if (this.classic) {
      return HypercoreEncryption.decrypt(index, block, this.keys.block)
    }

    const padding = block.subarray(0, WriterEncryption.PADDING)
    block = block.subarray(WriterEncryption.PADDING)

    if (padding[0] === 0) return block // unencrypted

    c.uint64.encode({ start: 0, end: 8, buffer: nonce }, index)

    nonce.set(padding, 8, 16)

    // Decrypt the block using the full nonce
    decrypt(block, nonce, this.keys.block)
  }
}

module.exports = {
  WriterEncryption
}

function encrypt (block, nonce, key) {
  sodium.crypto_stream_xor(
    block,
    block,
    nonce,
    key
  )
}

function decrypt (block, nonce, key) {
  return encrypt(block, nonce, key) // symmetric
}