CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-sjcl

Stanford JavaScript Crypto Library providing comprehensive cryptographic operations including AES encryption, hash functions, key derivation, and elliptic curve cryptography.

Pending
Overview
Eval results
Files

bit-array-utilities.mddocs/

Bit Array Utilities

SJCL uses bit arrays (arrays of 32-bit integers) as its internal data representation for all cryptographic operations. The bit array utilities provide essential functions for manipulating, combining, and analyzing these data structures.

Capabilities

Basic Operations

Fundamental operations for working with bit arrays.

/**
 * Slice bit array in units of bits
 * @param {BitArray} a - Source bit array
 * @param {number} bstart - Starting bit position
 * @param {number} [bend] - Ending bit position (exclusive)
 * @returns {BitArray} Sliced bit array
 */
sjcl.bitArray.bitSlice(a, bstart, bend);

/**
 * Extract a number from bit array
 * @param {BitArray} a - Source bit array
 * @param {number} bstart - Starting bit position
 * @param {number} blength - Number of bits to extract
 * @returns {number} Extracted value as integer
 */
sjcl.bitArray.extract(a, bstart, blength);

/**
 * Concatenate two bit arrays
 * @param {BitArray} a1 - First bit array
 * @param {BitArray} a2 - Second bit array
 * @returns {BitArray} Concatenated bit array
 */
sjcl.bitArray.concat(a1, a2);

Usage Examples:

const sjcl = require('sjcl');

// Create test bit arrays
const array1 = sjcl.codec.hex.toBits('deadbeef');
const array2 = sjcl.codec.hex.toBits('cafebabe');

console.log('Array 1:', sjcl.codec.hex.fromBits(array1));
console.log('Array 2:', sjcl.codec.hex.fromBits(array2));

// Concatenate arrays
const combined = sjcl.bitArray.concat(array1, array2);
console.log('Combined:', sjcl.codec.hex.fromBits(combined)); // "deadbeefcafebabe"

// Slice bit array
const slice = sjcl.bitArray.bitSlice(combined, 16, 48); // Extract middle 32 bits
console.log('Slice:', sjcl.codec.hex.fromBits(slice));

// Extract specific bits
const extracted = sjcl.bitArray.extract(array1, 8, 8); // Extract bits 8-15
console.log('Extracted byte:', extracted.toString(16));

Length and Size Operations

Determine and manipulate bit array lengths.

/**
 * Find length of bit array in bits
 * @param {BitArray} a - Bit array to measure
 * @returns {number} Length in bits
 */
sjcl.bitArray.bitLength(a);

/**
 * Truncate bit array to specified bit length
 * @param {BitArray} a - Source bit array
 * @param {number} len - Desired length in bits
 * @returns {BitArray} Truncated bit array
 */
sjcl.bitArray.clamp(a, len);

Usage Examples:

const sjcl = require('sjcl');

// Create bit array from hex
const data = sjcl.codec.hex.toBits('deadbeefcafebabe');
console.log('Original length:', sjcl.bitArray.bitLength(data)); // 64 bits

// Clamp to different sizes
const clamped32 = sjcl.bitArray.clamp(data, 32);
console.log('Clamped to 32 bits:', sjcl.codec.hex.fromBits(clamped32)); // "deadbeef"

const clamped24 = sjcl.bitArray.clamp(data, 24);
console.log('Clamped to 24 bits:', sjcl.codec.hex.fromBits(clamped24)); // "deadbe"

// Extend with clamp (pads with zeros)
const extended = sjcl.bitArray.clamp(data, 96);
console.log('Extended to 96 bits:', sjcl.codec.hex.fromBits(extended));
console.log('Extended length:', sjcl.bitArray.bitLength(extended));

Partial Word Operations

Handle partial 32-bit words for precise bit manipulation.

/**
 * Make partial word for bit array
 * @param {number} len - Number of bits used (1-32)
 * @param {number} x - Word value
 * @param {number} [_end] - Endianness flag
 * @returns {number} Partial word with length encoding
 */
sjcl.bitArray.partial(len, x, _end);

/**
 * Get number of bits used by partial word
 * @param {number} x - Partial word
 * @returns {number} Number of bits used (1-32, or 32 if full word)
 */
sjcl.bitArray.getPartial(x);

Usage Examples:

const sjcl = require('sjcl');

// Create partial words
const partial8 = sjcl.bitArray.partial(8, 0xAB); // 8-bit partial word
const partial16 = sjcl.bitArray.partial(16, 0xCDEF); // 16-bit partial word

console.log('8-bit partial length:', sjcl.bitArray.getPartial(partial8)); // 8
console.log('16-bit partial length:', sjcl.bitArray.getPartial(partial16)); // 16

// Build array with partial word
const arrayWithPartial = [0xDEADBEEF, partial8];
console.log('Array length:', sjcl.bitArray.bitLength(arrayWithPartial)); // 40 bits

// Convert to hex to see the result
console.log('Hex:', sjcl.codec.hex.fromBits(arrayWithPartial));

Comparison Operations

Secure comparison functions for cryptographic applications.

/**
 * Compare two bit arrays for equality in constant time
 * @param {BitArray} a - First bit array
 * @param {BitArray} b - Second bit array
 * @returns {boolean} True if arrays are equal
 */
sjcl.bitArray.equal(a, b);

Usage Examples:

const sjcl = require('sjcl');

// Create identical arrays
const array1 = sjcl.codec.hex.toBits('deadbeef');
const array2 = sjcl.codec.hex.toBits('deadbeef');
const array3 = sjcl.codec.hex.toBits('cafebabe');

// Constant-time comparison (secure against timing attacks)
console.log('Arrays 1 and 2 equal:', sjcl.bitArray.equal(array1, array2)); // true
console.log('Arrays 1 and 3 equal:', sjcl.bitArray.equal(array1, array3)); // false

// Use in cryptographic verification
function verifyMAC(message, key, providedMAC) {
  const hmac = new sjcl.misc.hmac(key);
  const computedMAC = hmac.encrypt(message);
  
  // Secure comparison prevents timing attacks
  return sjcl.bitArray.equal(computedMAC, providedMAC);
}

// Example usage
const key = sjcl.random.randomWords(8);
const message = "Important message";
const hmac = new sjcl.misc.hmac(key);
const mac = hmac.encrypt(message);

const isValid = verifyMAC(message, key, mac);
console.log('MAC verification:', isValid);

Binary Operations

Low-level binary operations on bit arrays.

/**
 * XOR two 4-word bit arrays
 * @param {BitArray} a - First 4-word array
 * @param {BitArray} b - Second 4-word array
 * @returns {BitArray} XOR result as 4-word array
 */
sjcl.bitArray.i(a, b);

/**
 * Byteswap word array in place
 * @param {BitArray} a - Bit array to byteswap
 * @returns {BitArray} Same array (modified in place)
 */
sjcl.bitArray.byteswapM(a);

Usage Examples:

const sjcl = require('sjcl');

// XOR operation (works on 4-word arrays)
const a = sjcl.codec.hex.toBits('deadbeefcafebabe12345678');
const b = sjcl.codec.hex.toBits('1234567890abcdef87654321');

// Ensure both are exactly 4 words
const a4 = sjcl.bitArray.clamp(a, 128);
const b4 = sjcl.bitArray.clamp(b, 128);

const xorResult = sjcl.bitArray.i(a4, b4);
console.log('XOR result:', sjcl.codec.hex.fromBits(xorResult));

// Byteswap example
const original = sjcl.codec.hex.toBits('deadbeef');
console.log('Original:', sjcl.codec.hex.fromBits(original));

const swapped = sjcl.bitArray.byteswapM(original.slice()); // Clone first
console.log('Byteswapped:', sjcl.codec.hex.fromBits(swapped));

Advanced Operations

Bit Shifting and Rotation

Implement bit shifting operations using SJCL's internal functions:

const sjcl = require('sjcl');

// Left shift implementation
function leftShift(bits, positions) {
  const result = [];
  let carry = 0;
  
  for (let i = bits.length - 1; i >= 0; i--) {
    const word = bits[i];
    result[i] = ((word << positions) | carry) >>> 0;
    carry = word >>> (32 - positions);
  }
  
  if (carry !== 0) {
    result.unshift(carry);
  }
  
  return result;
}

// Right shift implementation
function rightShift(bits, positions) {
  const result = [];
  let carry = 0;
  
  for (let i = 0; i < bits.length; i++) {
    const word = bits[i];
    result[i] = (word >>> positions) | carry;
    carry = (word << (32 - positions)) >>> 0;
  }
  
  return result;
}

// Usage
const data = sjcl.codec.hex.toBits('deadbeef');
console.log('Original:', sjcl.codec.hex.fromBits(data));

const shifted = leftShift(data, 4);
console.log('Left shifted by 4:', sjcl.codec.hex.fromBits(shifted));

const rightShifted = rightShift(data, 4);
console.log('Right shifted by 4:', sjcl.codec.hex.fromBits(rightShifted));

Custom Bit Manipulation

Implement custom bit manipulation functions:

const sjcl = require('sjcl');

class BitArrayUtil {
  // Set specific bit to 1
  static setBit(bits, position) {
    const wordIndex = Math.floor(position / 32);
    const bitIndex = position % 32;
    const result = bits.slice(); // Clone array
    
    if (wordIndex < result.length) {
      result[wordIndex] |= (1 << (31 - bitIndex));
    }
    
    return result;
  }
  
  // Clear specific bit to 0
  static clearBit(bits, position) {
    const wordIndex = Math.floor(position / 32);
    const bitIndex = position % 32;
    const result = bits.slice();
    
    if (wordIndex < result.length) {
      result[wordIndex] &= ~(1 << (31 - bitIndex));
    }
    
    return result;
  }
  
  // Test if specific bit is set
  static testBit(bits, position) {
    const wordIndex = Math.floor(position / 32);
    const bitIndex = position % 32;
    
    if (wordIndex >= bits.length) return false;
    
    return (bits[wordIndex] & (1 << (31 - bitIndex))) !== 0;
  }
  
  // Count number of set bits (Hamming weight)
  static popCount(bits) {
    let count = 0;
    
    for (const word of bits) {
      // Brian Kernighan's algorithm
      let w = word >>> 0; // Ensure unsigned
      while (w) {
        w &= w - 1;
        count++;
      }
    }
    
    return count;
  }
  
  // Reverse bits in array
  static reverse(bits) {
    const result = [];
    const totalBits = sjcl.bitArray.bitLength(bits);
    
    for (let i = 0; i < totalBits; i++) {
      const sourceBit = totalBits - 1 - i;
      if (BitArrayUtil.testBit(bits, sourceBit)) {
        result = BitArrayUtil.setBit(result, i);
      } else {
        result = BitArrayUtil.clearBit(result, i);
      }
    }
    
    return sjcl.bitArray.clamp(result, totalBits);
  }
}

// Usage examples
const data = sjcl.codec.hex.toBits('f0f0f0f0');
console.log('Original:', sjcl.codec.hex.fromBits(data));

// Set bit 4
const withBitSet = BitArrayUtil.setBit(data, 4);
console.log('Bit 4 set:', sjcl.codec.hex.fromBits(withBitSet));

// Test bits
console.log('Bit 0 set:', BitArrayUtil.testBit(data, 0)); // true (f starts with 1111)
console.log('Bit 4 set:', BitArrayUtil.testBit(data, 4)); // false (0 in f0f0)

// Count set bits
console.log('Set bits:', BitArrayUtil.popCount(data));

Data Validation

Validate bit array integrity and format:

const sjcl = require('sjcl');

class BitArrayValidator {
  // Check if array is valid bit array
  static isValid(bits) {
    if (!Array.isArray(bits)) return false;
    
    for (let i = 0; i < bits.length; i++) {
      const word = bits[i];
      
      // Check if it's a number
      if (typeof word !== 'number') return false;
      
      // Check if it's a valid 32-bit integer
      if (!Number.isInteger(word)) return false;
      
      // Check if it's in valid range
      if (word < 0 || word > 0xFFFFFFFF) return false;
      
      // Check partial word (last word might have length encoding)
      if (i === bits.length - 1) {
        const partialBits = sjcl.bitArray.getPartial(word);
        if (partialBits < 1 || partialBits > 32) return false;
      }
    }
    
    return true;
  }
  
  // Sanitize bit array
  static sanitize(bits) {
    if (!Array.isArray(bits)) return [];
    
    return bits.filter(word => {
      return typeof word === 'number' && 
             Number.isInteger(word) && 
             word >= 0 && 
             word <= 0xFFFFFFFF;
    });
  }
  
  // Get array statistics
  static getStats(bits) {
    return {
      isValid: BitArrayValidator.isValid(bits),
      wordCount: bits.length,
      bitLength: sjcl.bitArray.bitLength(bits),
      byteLength: Math.ceil(sjcl.bitArray.bitLength(bits) / 8),
      isEmpty: bits.length === 0,
      hasPartialWord: bits.length > 0 && sjcl.bitArray.getPartial(bits[bits.length - 1]) !== 32
    };
  }
}

// Usage
const testData = sjcl.codec.hex.toBits('deadbeef');
const stats = BitArrayValidator.getStats(testData);
console.log('Array stats:', stats);

// Test invalid data
const invalidData = [0xDEADBEEF, -1, 'invalid', 0x100000000];
console.log('Invalid data valid:', BitArrayValidator.isValid(invalidData)); // false

const sanitized = BitArrayValidator.sanitize(invalidData);
console.log('Sanitized:', sanitized); // [0xDEADBEEF]

Performance Considerations

  1. Memory Usage: Each 32-bit word uses 8 bytes in JavaScript
  2. Cloning: Use .slice() to clone arrays before modification
  3. Length Checks: Cache bit lengths for repeated operations
  4. Partial Words: Minimize partial word operations for better performance
  5. Native Operations: Use built-in methods when possible

Security Considerations

  1. Timing Attacks: Use sjcl.bitArray.equal() for secure comparisons
  2. Memory Clearing: JavaScript can't securely clear memory
  3. Constant Time: Most bit operations are NOT constant-time
  4. Data Validation: Always validate input bit arrays
  5. Side Channels: Be aware of potential information leakage

Common Patterns

  1. Data Conversion: Convert between formats via bit arrays
  2. Padding: Use clamp() to pad or truncate data
  3. Chunking: Use bitSlice() to process data in chunks
  4. Verification: Use equal() for cryptographic comparisons
  5. Concatenation: Use concat() to build composite data structures

Install with Tessl CLI

npx tessl i tessl/npm-sjcl

docs

big-number-arithmetic.md

bit-array-utilities.md

cipher-modes.md

data-encoding.md

elliptic-curve-cryptography.md

hash-functions.md

high-level-encryption.md

index.md

key-derivation.md

key-exchange.md

message-authentication.md

random-number-generation.md

symmetric-encryption.md

tile.json