Skip to content

Crypto Module Documentation

Overview

The crypto module provides cryptographic functionality powered by libsodium, including hashing, encryption, digital signatures, and secure random number generation.

Import

swazi
tumia crypto

Constants

ConstantValueDescription
HASH_SHA256_BYTES32SHA-256 hash output size
HASH_SHA512_BYTES64SHA-512 hash output size
SECRETBOX_KEYBYTES32Secret key size for symmetric encryption
SECRETBOX_NONCEBYTES24Nonce size for symmetric encryption
SECRETBOX_MACBYTES16MAC (authentication tag) size
BOX_PUBLICKEYBYTES32Public key size for asymmetric encryption
BOX_SECRETKEYBYTES32Secret key size for asymmetric encryption
BOX_NONCEBYTES24Nonce size for asymmetric encryption
SIGN_PUBLICKEYBYTES32Public key size for signatures
SIGN_SECRETKEYBYTES64Secret key size for signatures
SIGN_BYTES64Signature size
KDF_OPSLIMIT_INTERACTIVE2Operations limit (interactive)
KDF_MEMLIMIT_INTERACTIVE67108864Memory limit (interactive, ~64MB)
KDF_OPSLIMIT_MODERATE3Operations limit (moderate)
KDF_MEMLIMIT_MODERATE268435456Memory limit (moderate, ~256MB)
KDF_OPSLIMIT_SENSITIVE4Operations limit (sensitive)
KDF_MEMLIMIT_SENSITIVE1073741824Memory limit (sensitive, ~1GB)

API Reference

1. Hashing

crypto.hash(algorithm, data)

Computes cryptographic hash of data.

ParameterTypeDescription
algorithmnenoHash algorithm: "sha256" or "sha512"
dataBuffer au nenoData to hash
ReturnsBufferHash digest

Usage:

swazi
tumia crypto

data message = "Hello, Swazi!"
data hash256 = crypto.hash("sha256", message)
data hash512 = crypto.hash("sha512", message)

chapisha hash256.toString("hex")

2. HMAC (Message Authentication)

crypto.hmac(algorithm, key, data)

Computes Hash-based Message Authentication Code.

ParameterTypeDescription
algorithmnenoHMAC algorithm: "sha256" or "sha512"
keyBuffer au nenoSecret key
dataBuffer au nenoData to authenticate
ReturnsBufferHMAC tag

Usage:

swazi
data key = "secret-key"
data message = "Important message"
data tag = crypto.hmac("sha256", key, message)

// Verify HMAC
data isValid = crypto.timingSafeEqual(tag, receivedTag)

3. Random Number Generation

crypto.randomBytes(length)

Generates cryptographically secure random bytes.

ParameterTypeDescription
lengthnambaNumber of bytes (max 1MB)
ReturnsBufferRandom bytes

Usage:

swazi
data randomKey = crypto.randomBytes(32)
data nonce = crypto.randomBytes(24)

crypto.randomInt(min, max)

Generates cryptographically secure random integer.

ParameterTypeDescription
minnambaMinimum value (inclusive)
maxnambaMaximum value (exclusive)
ReturnsnambaRandom integer

Usage:

swazi
data dice = crypto.randomInt(1, 7)  // 1-6
data pin = crypto.randomInt(1000, 10000)  // 4-digit PIN

crypto.randomUUID()

Generates RFC 4122 version 4 UUID.

ParameterTypeDescription
ReturnsnenoUUID string (e.g., "550e8400-e29b-41d4-a716-446655440000")

Usage:

swazi
data id = crypto.randomUUID()
chapisha id  // "a3f2b8c1-4d5e-4f6a-9b7c-8d9e0f1a2b3c"

4. Symmetric Encryption (Secret Box)

Uses XSalsa20-Poly1305 authenticated encryption.

crypto.secretbox.encrypt(key, nonce, data)

Encrypts data with symmetric key.

ParameterTypeDescription
keyBuffer32-byte secret key
nonceBuffer24-byte unique nonce
dataBuffer au nenoPlaintext to encrypt
ReturnsBufferCiphertext with MAC (plaintext.length + 16 bytes)

Usage:

swazi
// Generate key and nonce
data key = crypto.randomBytes(crypto.SECRETBOX_KEYBYTES)
data nonce = crypto.randomBytes(crypto.SECRETBOX_NONCEBYTES)

// Encrypt
data plaintext = "Top secret message"
data ciphertext = crypto.secretbox.encrypt(key, nonce, plaintext)

chapisha ciphertext.toString("hex")

crypto.secretbox.decrypt(key, nonce, ciphertext)

Decrypts data encrypted with secret box.

ParameterTypeDescription
keyBuffer32-byte secret key (same as encryption)
nonceBuffer24-byte nonce (same as encryption)
ciphertextBufferEncrypted data with MAC
ReturnsBufferDecrypted plaintext
ThrowsCryptoErrorIf authentication fails (wrong key/tampered data)

Usage:

swazi
jaribu {
    data decrypted = crypto.secretbox.decrypt(key, nonce, ciphertext)
    chapisha decrypted.toString("utf8")
} makosa err {
    chapisha "Decryption failed:", err.message
}

5. Asymmetric Encryption (Box)

Uses X25519-XSalsa20-Poly1305 for public-key encryption.

crypto.box.keypair()

Generates public/secret key pair.

ParameterTypeDescription
Returnsobject{ publicKey: Buffer, secretKey: Buffer }

Usage:

swazi
data alice = crypto.box.keypair()
data bob = crypto.box.keypair()

chapisha "Alice public:", alice.publicKey.toString("hex")

crypto.box.encrypt(theirPublicKey, mySecretKey, nonce, data)

Encrypts message for a recipient.

ParameterTypeDescription
theirPublicKeyBufferRecipient's 32-byte public key
mySecretKeyBufferSender's 32-byte secret key
nonceBuffer24-byte unique nonce
dataBuffer au nenoPlaintext to encrypt
ReturnsBufferEncrypted ciphertext with MAC

Usage:

swazi
data nonce = crypto.randomBytes(crypto.BOX_NONCEBYTES)
data message = "Secret message for Bob"

// Alice encrypts for Bob
data ciphertext = crypto.box.encrypt(
    bob.publicKey,
    alice.secretKey,
    nonce,
    message
)

crypto.box.decrypt(theirPublicKey, mySecretKey, nonce, ciphertext)

Decrypts message from a sender.

ParameterTypeDescription
theirPublicKeyBufferSender's 32-byte public key
mySecretKeyBufferRecipient's 32-byte secret key
nonceBuffer24-byte nonce (same as encryption)
ciphertextBufferEncrypted data
ReturnsBufferDecrypted plaintext
ThrowsCryptoErrorIf authentication fails

Usage:

swazi
// Bob decrypts Alice's message
data decrypted = crypto.box.decrypt(
    alice.publicKey,
    bob.secretKey,
    nonce,
    ciphertext
)

chapisha decrypted.toString("utf8")

6. Digital Signatures (Ed25519)

crypto.sign.keypair()

Generates signing key pair.

ParameterTypeDescription
Returnsobject{ publicKey: Buffer, secretKey: Buffer }

Usage:

swazi
data signer = crypto.sign.keypair()

crypto.sign.sign(secretKey, message)

Creates detached signature.

ParameterTypeDescription
secretKeyBuffer64-byte secret signing key
messageBuffer au nenoMessage to sign
ReturnsBuffer64-byte signature

Usage:

swazi
data message = "I agree to these terms"
data signature = crypto.sign.sign(signer.secretKey, message)

// Send message + signature + publicKey to recipient

crypto.sign.verify(publicKey, signature, message)

Verifies signature authenticity.

ParameterTypeDescription
publicKeyBuffer32-byte public verification key
signatureBuffer64-byte signature to verify
messageBuffer au nenoOriginal message
Returnsboolkweli if valid, sikweli if invalid

Usage:

swazi
data isValid = crypto.sign.verify(
    signer.publicKey,
    signature,
    message
)

kama isValid {
    chapisha "Signature verified!"
} vinginevyo {
    chapisha "Invalid signature!"
}

7. Key Derivation (Password Hashing)

crypto.kdf(password, salt, opsLimit, memLimit, keyLength)

Derives cryptographic key from password using Argon2.

ParameterTypeDescription
passwordBuffer au nenoUser password
saltBuffer16-byte unique salt
opsLimitnambaCPU operations limit (use constants)
memLimitnambaMemory limit in bytes (use constants)
keyLengthnambaDesired key length (1-1024 bytes)
ReturnsBufferDerived key

Usage:

swazi
// Interactive login (fast)
data salt = crypto.randomBytes(16)
data password = "user-password"

data key = crypto.kdf(
    password,
    salt,
    crypto.KDF_OPSLIMIT_INTERACTIVE,
    crypto.KDF_MEMLIMIT_INTERACTIVE,
    32
)

// Store salt + key hash for verification

Security Levels:

swazi
// INTERACTIVE: Fast, for frequent operations (login)
data fastKey = crypto.kdf(
    password, salt,
    crypto.KDF_OPSLIMIT_INTERACTIVE,
    crypto.KDF_MEMLIMIT_INTERACTIVE,
    32
)

// MODERATE: Balanced security/performance
data moderateKey = crypto.kdf(
    password, salt,
    crypto.KDF_OPSLIMIT_MODERATE,
    crypto.KDF_MEMLIMIT_MODERATE,
    32
)

// SENSITIVE: Maximum security (protect master keys)
data sensitiveKey = crypto.kdf(
    password, salt,
    crypto.KDF_OPSLIMIT_SENSITIVE,
    crypto.KDF_MEMLIMIT_SENSITIVE,
    32
)

8. Utility Functions

crypto.timingSafeEqual(a, b)

Constant-time comparison (prevents timing attacks).

ParameterTypeDescription
aBufferFirst buffer
bBufferSecond buffer
Returnsboolkweli if equal, sikweli otherwise

Usage:

swazi
data storedHash = getStoredHash()
data computedHash = crypto.hash("sha256", inputPassword)

// NEVER use == for comparing secrets!
kama crypto.timingSafeEqual(storedHash, computedHash) {
    chapisha "Password correct"
}

crypto.memzero(buffer)

Securely wipes buffer contents.

ParameterTypeDescription
bufferBufferBuffer to clear
Returnsvoid-

Usage:

swazi
data secretKey = crypto.randomBytes(32)
// ... use key ...

// Securely erase from memory
crypto.memzero(secretKey)

crypto.uuidToBytes(uuid)

Converts UUID string to 16-byte buffer.

ParameterTypeDescription
uuidnenoUUID string with dashes
ReturnsBuffer16-byte buffer

Usage:

swazi
data uuid = "550e8400-e29b-41d4-a716-446655440000"
data bytes = crypto.uuidToBytes(uuid)
chapisha bytes.idadi  // 16

crypto.bytesToUUID(buffer)

Converts 16-byte buffer to UUID string.

ParameterTypeDescription
bufferBuffer16-byte buffer
ReturnsnenoFormatted UUID string

Usage:

swazi
data bytes = crypto.randomBytes(16)
data uuid = crypto.bytesToUUID(bytes)
chapisha uuid  // "a3f2b8c1-4d5e-4f6a-9b7c-8d9e0f1a2b3c"

Complete Examples

Secure Message System

swazi
tumia crypto

// Alice and Bob generate keypairs
data alice = crypto.box.keypair()
data bob = crypto.box.keypair()

// Alice sends encrypted message to Bob
kazi encryptMessage sender, recipient, message {
    data nonce = crypto.randomBytes(crypto.BOX_NONCEBYTES)
    data ciphertext = crypto.box.encrypt(
        recipient.publicKey,
        sender.secretKey,
        nonce,
        message
    )
    
    rudisha { nonce, ciphertext }
}

// Bob decrypts message from Alice
kazi decryptMessage sender, recipient, envelope {
    jaribu {
        data plaintext = crypto.box.decrypt(
            sender.publicKey,
            recipient.secretKey,
            envelope.nonce,
            envelope.ciphertext
        )
        rudisha plaintext.toString("utf8")
    } makosa err {
        chapisha "Decryption failed:", err.message
        rudisha sikweli
    }
}

// Usage
data envelope = encryptMessage(alice, bob, "Hello Bob!")
data message = decryptMessage(alice, bob, envelope)
chapisha message  // "Hello Bob!"

Password Storage

swazi
tumia crypto

kazi hashPassword password {
    data salt = crypto.randomBytes(16)
    data hash = crypto.kdf(
        password,
        salt,
        crypto.KDF_OPSLIMIT_MODERATE,
        crypto.KDF_MEMLIMIT_MODERATE,
        32
    )
    
    // Store salt + hash together
    rudisha {
        salt: salt.toString("hex"),
        hash: hash.toString("hex")
    }
}

kazi verifyPassword password, stored {
    data salt = Buffer.from(stored.salt, "hex")
    data expectedHash = Buffer.from(stored.hash, "hex")
    
    data computedHash = crypto.kdf(
        password,
        salt,
        crypto.KDF_OPSLIMIT_MODERATE,
        crypto.KDF_MEMLIMIT_MODERATE,
        32
    )
    
    rudisha crypto.timingSafeEqual(expectedHash, computedHash)
}

// Usage
data stored = hashPassword("my-secure-password")
data isValid = verifyPassword("my-secure-password", stored)
chapisha isValid  // kweli

File Signing

swazi
tumia crypto

data signer = crypto.sign.keypair()

kazi signFile content {
    data signature = crypto.sign.sign(signer.secretKey, content)
    
    rudisha {
        content,
        signature: signature.toString("hex"),
        publicKey: signer.publicKey.toString("hex")
    }
}

kazi verifyFile signedFile {
    data signature = Buffer.from(signedFile.signature, "hex")
    data publicKey = Buffer.from(signedFile.publicKey, "hex")
    
    rudisha crypto.sign.verify(
        publicKey,
        signature,
        signedFile.content
    )
}

// Usage
data file = signFile("Important document content")
data isAuthentic = verifyFile(file)
chapisha isAuthentic  // kweli

Error Handling

All crypto functions throw SwaziError with type "CryptoError" on failure:

swazi
jaribu {
    data result = crypto.secretbox.decrypt(wrongKey, nonce, ciphertext)
} makosa err {
    chapisha err.type      // "CryptoError"
    chapisha err.message   // "Decryption failed (authentication error)"
}

Security Best Practices

  1. Never reuse nonces with the same key
  2. Use timingSafeEqual() for comparing secrets
  3. Always generate keys with randomBytes(), never use passwords directly
  4. Call memzero() on sensitive buffers after use
  5. Use appropriate KDF limits for your use case
  6. Verify signatures before trusting data
  7. Store salts alongside password hashes