Skip to content

Instantly share code, notes, and snippets.

@enkore
Last active May 20, 2017 16:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save enkore/dcb40bcea679bba55ba7192a9d106137 to your computer and use it in GitHub Desktop.
Save enkore/dcb40bcea679bba55ba7192a9d106137 to your computer and use it in GitHub Desktop.
What happens when you call key.encrypt(something) in Borg?

AESKeyBase

def encrypt(self, chunk):

data = self.compressor.compress(chunk)

CNONE: Allocate new string of size len(chunk) + 2, copy two type bytes, copy len(chunk) into it, point data at result.

self.nonce_manager.ensure_reservation(num_aes_blocks(len(data)))

...

self.enc_cipher.reset()

Set some fields in a C struct

data = b''.join((self.enc_cipher.iv[8:], self.enc_cipher.encrypt(data)))
self.enc_cipher.encrypt(data)
cdef unsigned char *out = <unsigned char *>malloc(inl+16)

Allocate output buffer of size len(data) + 16

... Encrypt data into output buffer

self.blocks += num_aes_blocks(ctl)
  1. Allocate temporary PyLong object from self.blocks
  2. Allocate temporary PyLong object from ctl
  3. Lookup of num_aes_blocks in module scope (at runtime)
  4. Runtime dispatch to one of four possible call paths of the looked up function
  5. Call num_aes_blocks
    1. Convert Python object argument into long
    2. Perform ADD, DIV
    3. Allocate return PyLong object
  6. Add PyLong objects together
  7. Convert resulting PyLong to long long
  8. Assign value to C struct
return out[:ctl]
  1. Allocate new Python bytes object of length ctl.
  2. Copy portion of output buffer into said object
  3. Return said object
self.enc_cipher.iv[8:]

.iv is a property of the AES class.

  1. return increment_iv(self.iv_orig[:16], self.blocks)
  2. Runtime lookup of increment_iv in module scope
  3. Allocate temporary PyBytes, copy iv_orig into it
  4. Allocate temporary PyLong w/ blocks
  5. Runtime dispatch to one of four call paths
  6. Call increment_iv
    1. Runtime lookup of bytes16_to_int in module scope
    2. Runtime dispatch to one of four possible call methods
    3. Call bytes16_to_int
      1. Runtime lookup of _2long in module scope
      2. Runtime lookup of the unpack_from attribute
      3. Runtime dispatch to ...
        1. In this instance this likely dispatches to a branch allocating a tuple object, among other things.
      4. Type checking of the result, unpacking into local PyObject*
      5. PyNumber_Lshift
      6. PyNumber_Add
      7. Return
    4. PyNumber_InPlaceAdd
    5. Runtime lookup of int_to_bytes16 in module scope
    6. Usual runtime dispatch
    7. Call int_to_bytes16
      1. Some PyNumber_arithmetic
      2. Lookup of _2long in module scope
      3. Lookup of pack attribute
      4. Runtime dispatch
      5. Return
  7. Create new bytes object of length 8, copy 8 bytes
  8. Create new bytes object of ciphertext length + 8
  9. Copy 8 bytes into there, copy ciphertext
assert (self.MAC is blake2b_256 and len(self.enc_hmac_key) == 128 or self.MAC is hmac_sha256 and len(self.enc_hmac_key) == 32)

Mostly runtime lookups of course

hmac = self.MAC(self.enc_hmac_key, data)

Dispatch to func from VM

blake2b_256(key, data)

  1. Allocate new PyBytes of 32 bytes (md)
  2. PyBytes_AsStringAndSize, acquires pointer to internal buffer of key
  3. Call blake2b_update_from_buffer twice (key, data)
    1. Acquire/release buffer (no copy)
    2. Do BLAKE2
  4. Return PyBytes from (1)
return b''.join((self.TYPE_STR, hmac, data))
  1. Allocate new bytes object of length 1 + len(hmac) + len(data)
  2. Copy one byte into it
  3. Copy hmac into it
  4. Copy data into it
  5. Return
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment