symcrypto - Symmetric Cryptography

This module provides symmetric cryptography abstractions in the Charm cryptographic library, including authenticated encryption (AEAD) and message authentication.

Overview

Symmetric cryptography uses the same secret key for both encryption and decryption. This module provides high-level abstractions for symmetric encryption that are commonly used in hybrid encryption schemes, where an asymmetric scheme encrypts a session key that is then used for efficient bulk encryption.

Main Classes:

  • SymmetricCryptoAbstraction: Basic symmetric encryption using AES-CBC with PKCS7 padding

  • AuthenticatedCryptoAbstraction: Authenticated encryption providing both confidentiality and integrity (AEAD)

  • MessageAuthenticator: HMAC-based message authentication

How It Works:

  1. A symmetric key is derived (often from a group element via hashing)

  2. Messages are encrypted using AES in CBC mode with random IV

  3. For authenticated encryption, an HMAC is computed over the ciphertext

  4. The IV and ciphertext are encoded in JSON format for easy serialization

Security Properties

The symmetric encryption classes provide the following security guarantees:

Security Property

Description

IND-CPA

Indistinguishability under chosen-plaintext attack. Ciphertexts reveal nothing about which plaintext was encrypted (via random IV).

IND-CCA2

AuthenticatedCryptoAbstraction provides chosen-ciphertext security. Adversaries cannot create valid ciphertexts without the key.

INT-CTXT

Integrity of ciphertexts. Any modification to the ciphertext is detected during decryption (for AuthenticatedCryptoAbstraction).

AEAD

Authenticated Encryption with Associated Data. Supports binding additional context data to the ciphertext without encrypting it.

Underlying Primitives:

  • AES-128-CBC: Block cipher in CBC mode with random IV

  • HMAC-SHA256: Message authentication code for integrity

  • PKCS7: Padding scheme for block alignment

Typical Use Cases

  1. Hybrid Encryption

    Combine asymmetric encryption (for key transport) with symmetric encryption (for data). The asymmetric scheme encrypts a random session key, which is used with symcrypto for efficient bulk encryption.

    from charm.toolbox.pairinggroup import PairingGroup, GT, extract_key
    from charm.toolbox.symcrypto import AuthenticatedCryptoAbstraction
    
    group = PairingGroup('SS512')
    
    # Session key from group element (e.g., ABE decryption result)
    session_element = group.random(GT)
    sym_key = extract_key(session_element)
    
    # Encrypt large data with symmetric key
    cipher = AuthenticatedCryptoAbstraction(sym_key)
    ciphertext = cipher.encrypt(b"Large document contents...")
    
    # Decrypt
    plaintext = cipher.decrypt(ciphertext)
    
  2. Authenticated Channel

    After key agreement, use authenticated encryption to protect messages against both eavesdropping and tampering.

    from hashlib import sha256
    from charm.toolbox.symcrypto import AuthenticatedCryptoAbstraction
    
    # Derive key from shared secret
    shared_secret = b"key_from_DH_exchange"
    key = sha256(shared_secret).digest()
    
    cipher = AuthenticatedCryptoAbstraction(key)
    
    # Encrypt with associated data (e.g., message counter)
    ad = b"msg_id:12345"
    ct = cipher.encrypt("Secret message", associatedData=ad)
    
    # Decrypt (must provide same associated data)
    pt = cipher.decrypt(ct, associatedData=ad)
    
  3. Message Authentication

    Authenticate messages without encryption when confidentiality is not needed but integrity is required.

    from charm.toolbox.symcrypto import MessageAuthenticator
    from charm.toolbox.pairinggroup import PairingGroup, GT, extract_key
    
    group = PairingGroup('SS512')
    key = extract_key(group.random(GT))
    
    mac = MessageAuthenticator(key)
    authenticated_msg = mac.mac("Important announcement")
    
    # Verify integrity
    is_authentic = mac.verify(authenticated_msg)
    

Example Usage

Basic Authenticated Encryption:

from charm.toolbox.pairinggroup import PairingGroup, GT
from charm.core.math.pairing import hashPair as sha2
from charm.toolbox.symcrypto import AuthenticatedCryptoAbstraction

# Setup - derive key from group element
group = PairingGroup('SS512')
element = group.random(GT)
key = sha2(element)  # 32-byte key

# Create cipher
cipher = AuthenticatedCryptoAbstraction(key)

# Encrypt
plaintext = b"Hello, World!"
ciphertext = cipher.encrypt(plaintext)

# Decrypt
recovered = cipher.decrypt(ciphertext)
assert recovered == plaintext

With Associated Data (AEAD):

from hashlib import sha256
from charm.toolbox.symcrypto import AuthenticatedCryptoAbstraction

key = sha256(b'secret key').digest()
cipher = AuthenticatedCryptoAbstraction(key)

# Associated data is authenticated but not encrypted
header = b'\\x01\\x02\\x03\\x04'  # e.g., protocol header
ct = cipher.encrypt('Payload data', associatedData=header)

# Must provide correct associated data to decrypt
pt = cipher.decrypt(ct, associatedData=header)

# Wrong associated data causes verification failure
try:
    cipher.decrypt(ct, associatedData=b'wrong')
except ValueError as e:
    print("Tampered or wrong context!")

API Reference

class symcrypto.AuthenticatedCryptoAbstraction(key, alg=charm.core.crypto.cryptobase.AES, mode=charm.core.crypto.cryptobase.MODE_CBC)[source]

Bases: SymmetricCryptoAbstraction

Implements Authenticated Encryption with Associated Data (AEAD) abstraction. The associated data is optional, and this version is backwards compatible with the same class without the associated data option.

Examples

>>> from hashlib import sha256
>>> import charm.toolbox.symcrypto
>>> key = sha256(b'shameful secret key').digest()
>>> cipher = charm.toolbox.symcrypto.AuthenticatedCryptoAbstraction(key)
>>> ciphertext = cipher.encrypt('My age is 42.')
>>> cipher.decrypt(ciphertext)
b'My age is 42.'
>>> ciphertext2 = cipher.encrypt(b'My age is 42.')
>>> cipher.decrypt(ciphertext2)
b'My age is 42.'
>>> ad = b''
>>> ciphertextAssociatedData = cipher.encrypt('Some network PDU.', associatedData=ad)
>>> cipher.decrypt(ciphertextAssociatedData)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "./charm/toolbox/symcrypto.py", line 233, in decrypt
    raise ValueError("Invalid mac. Your data was tampered with or your key is wrong")
ValueError: Invalid mac. Your data was tampered with or your key is wrong
>>> cipher.decrypt(ciphertextAssociatedData, associatedData='wrong data')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "./charm/toolbox/symcrypto.py", line 233, in decrypt
    raise ValueError("Invalid mac. Your data was tampered with or your key is wrong")
ValueError: Invalid mac. Your data was tampered with or your key is wrong
>>> cipher.decrypt(ciphertextAssociatedData, associatedData=b'')
b'Some network PDU.'
>>>
decrypt(cipherText, associatedData='')[source]

Decrypts a ciphertext in AEAD mode (Authenticated Encryption with Associated Data) using the superclass symmetric encryption parameters. The MAC is computed with both the ciphertext and associated data (and other cryptosystem parameters), but the associated data is not encrypted, nor available within the ciphertext structure.

Parameters

ciphertextstr or byte str

The message to be decrypted.

associatedDatastr or byte str, optional

Associated data that will be MACed together with the ciphertext and algorithm. This associated text must be in plaintext.

Returns

byte str

The decrypted plaintext, if the ciphertext was successfuly authenticated. Raise exception otherwise.

Raises

ValueError

If the MAC is invalid.

Notes

The IV is included in the computation of the MAC. In fact, all cipher parameters are included: the encryption function returns a JSON object from a dictionary composed of the cipher parameters (e.g., algorithm, mode, IV), and the ciphertext. The MAC function uses the whole JSON object/string to compute the MAC, prepended with the HMAC algorithm + associatedData.

The MAC key is computed as sha2(b’Poor Mans Key Extractor” + key).

encrypt(msg, associatedData='')[source]

Encrypts a message in AEAD mode (Authenticated Encryption with Associated Data) using the superclass symmetric encryption parameters. The MAC is computed with both the ciphertext and associated data (and other cryptosystem parameters), but the associated data is not encrypted, nor saved within the ciphertext structure.

Parameters

msgstr or byte str

The message to be encrypted.

associatedDatastr or byte str, optional

Associated data that will be MACed together with the ciphertext and algorithm; the associated data will not be encrypted.

Returns

dict
Dictionary structure containing:
msg: {‘ALG’: symmetric cryptosystem.

‘MODE’: symmetric encryption mode. ‘IV’: the IV for the encryption algorithm. ‘CipherText’: the padded ciphertext (padding according to PKCS 7).

}

“alg”: The HMAC algorithm. “digest”: The MAC computed as MAC = HMAC(key, alg + associatedData + msg)

Notes

The IV is included in the computation of the MAC. In fact, all cipher parameters are included: the encryption function returns a JSON object from a dictionary composed of the cipher parameters (e.g., algorithm, mode, IV), and the ciphertext. The MAC function uses the whole JSON object/string to compute the MAC, prepended with the HMAC algorithm + associatedData.

The MAC key is computed as sha2(b’Poor Mans Key Extractor” + key).

class symcrypto.MessageAuthenticator(key, alg='HMAC_SHA2')[source]

Bases: object

Abstraction for constructing and verifying authenticated messages

A large number of the schemes can only encrypt group elements and do not provide an efficient mechanism for encoding byte in those elements. As such we don’t pick a symmetric key and encrypt it asymmetrically. Rather, we hash a random group element to get the symmetric key.

>>> from charm.toolbox.pairinggroup import PairingGroup,GT,extract_key
>>> groupObj = PairingGroup('SS512')
>>> key = groupObj.random(GT)
>>> m = MessageAuthenticator(extract_key(key))
>>> AuthenticatedMessage = m.mac('Hello World')
>>> m.verify(AuthenticatedMessage)
True
mac(msg, associatedData=b'')[source]

Authenticates (MAC) a message. The MAC is computed as: MAC = HMAC(key, algorithm + associatedData + message).

Parameters

msgstr or byte str

The message serving as input to the HMAC algorithm, in addition to the HMAC algorithm and associated data.

associatedDatastr or byte str, optional

Associated data that will be MACed together with the ciphertext and algorithm; the associated data will not be encrypted.

Returns

dict

Dictionary composed of the MAC algorithm, the MACed message (or ciphertext), and the digest computed by MACing HMAC_algorithm + associatedData + msg.

verify(msgAndDigest, associatedData=b'')[source]

Verifies whether the MAC digest from input ciphertext and digest matches the computed one over ciphertext and associated data.

Parameters

msgAndDigestdict

Dictionary composed of the MAC algorithm, the MACed message (or ciphertext), and the digest computed by MACing HMAC_algorithm + associatedData + msg. It is the format generated by the mac() function within this class.

associatedDatastr or byte str, optional

Associated data that will be MACed together with the ciphertext and algorithm; the associated data will not be encrypted.

Returns

bool

True if the digests match, False otherwise.

Raises

ValueError

If the HMAC algorithm is not supported.

class symcrypto.SymmetricCryptoAbstraction(key, alg=charm.core.crypto.cryptobase.AES, mode=charm.core.crypto.cryptobase.MODE_CBC)[source]

Bases: object

Abstraction for symmetric encryption and decryption of data. Ideally provide an INDCCA2 secure symmetric container for arbitrary data. Currently only supports primitives that JSON can encode and decode.

A large number of the schemes can only encrypt group elements and do not provide an efficient mechanism for encoding byte in those elements. As such we don’t pick a symmetric key and encrypt it asymmetrically. Rather, we hash a random group element to get the symmetric key.

>>> from charm.toolbox.pairinggroup import PairingGroup,GT,extract_key
>>> groupObj = PairingGroup('SS512')
>>> a = SymmetricCryptoAbstraction(extract_key(groupObj.random(GT)))
>>> ct = a.encrypt(b"Friendly Fire Isn't")
>>> a.decrypt(ct)
b"Friendly Fire Isn't"
decrypt(cipherText)[source]
encrypt(message)[source]

See Also

  • charm.toolbox.PKEnc - Public-key encryption (for hybrid schemes)

  • charm.toolbox.ABEnc - Attribute-based encryption using symcrypto

  • charm.toolbox.paddingschemes - Padding schemes (PKCS7, OAEP)

  • charm.toolbox.securerandom - Secure random number generation